annotate yeti/test_frequency.yeti @ 68:b75c0eaaa6dd

Allow wrong max in alternate columns where expected max would be in lower octave (which is not present)
author Chris Cannam <c.cannam@qmul.ac.uk>
date Mon, 03 Mar 2014 08:32:45 +0000
parents d6dd6e1cc00e
children 27007f8302f4
rev   line source
c@65 1
c@65 2 module test_frequency;
c@65 3
c@65 4 mat = load may.matrix;
c@65 5 vec = load may.vector;
c@65 6 win = load may.signal.window;
c@65 7 mm = load may.mathmisc;
c@65 8 cm = load may.matrix.complex;
c@65 9 syn = load may.stream.syntheticstream;
c@67 10 plot = load may.plot;
c@65 11
c@65 12 { cqt } = load cqt;
c@65 13
c@65 14 // Test with a single windowed sinusoid, repeating at various frequencies
c@65 15
c@65 16 sinTestStream sampleRate duration signalFreq = // duration is in samples
c@65 17 (sin = syn.sinusoid sampleRate signalFreq;
c@65 18 chunk = mat.getRow 0 (sin.read duration);
c@65 19 syn.precalculatedMono sampleRate (win.windowed win.hann chunk));
c@65 20
c@65 21 // We want to make a CQ transform spanning more than one octave, but
c@65 22 // not going all the way to fs/2 so we can test it also with
c@65 23 // frequencies above and below its extents
c@65 24
c@65 25 sampleRate = 100;
c@65 26
c@65 27 // fs/2 = 50 so 10->40 gives us 2 octaves
c@65 28 cqmin = 10;
c@65 29 cqmax = 40;
c@65 30 bpo = 4; // fairly arbitrary
c@65 31
c@65 32 testFreqs = map (* 5) [ 0..10 ];
c@65 33 duration = sampleRate * 2;
c@65 34
c@68 35 threshold = 0.08;
c@67 36
c@65 37 streamBuilder = sinTestStream sampleRate duration;
c@65 38
c@65 39 binForFreq f =
c@65 40 mm.round (bpo * mm.log2 (f / cqmin)) - 1;
c@65 41
c@68 42 report message matrix =
c@68 43 (eprintln message;
c@68 44 eprintln "matrix is:";
c@68 45 mat.eprint matrix;
c@68 46 chart = plot.plot [Grid matrix];
c@68 47 sleep 100;
c@68 48 chart#dispose());
c@68 49
c@65 50 tests = mapIntoHash
c@65 51 do f: "freq_\(f)" done
c@65 52 do f: \(
c@65 53 str = streamBuilder f;
c@65 54 cq = cqt { maxFreq = cqmax, minFreq = cqmin, binsPerOctave = bpo } str;
c@65 55 m = mat.concatHorizontal (map cm.magnitudes cq.output);
c@65 56 // println "binFrequencies = \(cq.kernel.binFrequencies)";
c@65 57 // println "binForFreq \(f) = \(binForFreq f)";
c@67 58 var colno = 0;
c@65 59 success = all id
c@65 60 (map do c:
c@68 61 // The test passes for this column if:
c@68 62 //
c@68 63 // * the max bin is the expected one, or
c@68 64 //
c@68 65 // * the expected max is out of range entirely (but
c@68 66 // we need to test _something_ in this case --
c@68 67 // what?), or
c@68 68 //
c@68 69 // * all bins are below a threshold, or
c@68 70 //
c@68 71 // * this is an odd column and the expected max is in
c@68 72 // the lower octave
c@68 73 //
c@68 74 // We should also check that all values in the lower
c@68 75 // octave are zero for odd columns.
c@68 76 //
c@65 77 expected = binForFreq f;
c@65 78 good =
c@65 79 (expected < 0 or expected >= vec.length c) or
c@68 80 ((colno % 2 == 1) and expected < (vec.length c / 2)) or
c@67 81 (vec.max c < threshold) or
c@65 82 (vec.maxindex c == binForFreq f);
c@65 83 if (not good) then
c@68 84 report " * bad! maxindex \(vec.maxindex c) != expected \(binForFreq f) for freq \(f) in column \(colno) of \(mat.width m): column is \(vec.list c)" m;
c@65 85 fi;
c@67 86 colno := colno + 1;
c@65 87 good;
c@65 88 done (mat.asColumns m));
c@65 89 success;
c@65 90 ) done
c@65 91 testFreqs;
c@65 92
c@65 93 tests is hash<string, () -> boolean>