c@65: c@65: module test_frequency; c@65: c@65: mat = load may.matrix; c@65: vec = load may.vector; c@65: win = load may.signal.window; c@65: mm = load may.mathmisc; c@65: cm = load may.matrix.complex; c@65: syn = load may.stream.syntheticstream; c@65: c@65: { cqt } = load cqt; c@65: c@65: // Test with a single windowed sinusoid, repeating at various frequencies c@65: c@65: sinTestStream sampleRate duration signalFreq = // duration is in samples c@65: (sin = syn.sinusoid sampleRate signalFreq; c@65: chunk = mat.getRow 0 (sin.read duration); c@65: syn.precalculatedMono sampleRate (win.windowed win.hann chunk)); c@65: c@65: // We want to make a CQ transform spanning more than one octave, but c@65: // not going all the way to fs/2 so we can test it also with c@65: // frequencies above and below its extents c@65: c@65: sampleRate = 100; c@65: c@65: // fs/2 = 50 so 10->40 gives us 2 octaves c@65: cqmin = 10; c@65: cqmax = 40; c@65: bpo = 4; // fairly arbitrary c@65: c@65: testFreqs = map (* 5) [ 0..10 ]; c@65: duration = sampleRate * 2; c@65: c@65: streamBuilder = sinTestStream sampleRate duration; c@65: c@65: binForFreq f = c@65: mm.round (bpo * mm.log2 (f / cqmin)) - 1; c@65: c@65: tests = mapIntoHash c@65: do f: "freq_\(f)" done c@65: do f: \( c@65: str = streamBuilder f; c@65: cq = cqt { maxFreq = cqmax, minFreq = cqmin, binsPerOctave = bpo } str; c@65: m = mat.concatHorizontal (map cm.magnitudes cq.output); c@65: // println "binFrequencies = \(cq.kernel.binFrequencies)"; c@65: // println "binForFreq \(f) = \(binForFreq f)"; c@65: success = all id c@65: (map do c: c@65: // passes test if the correct max bin, or the expected max c@65: // is out of range, or if all bins are below a threshold c@65: expected = binForFreq f; c@65: good = c@65: (expected < 0 or expected >= vec.length c) or c@65: (vec.max c < 0.001) or c@65: (vec.maxindex c == binForFreq f); c@65: if (not good) then c@65: println " * bad! maxindex \(vec.maxindex c) != expected \(binForFreq f) for freq \(f) in column: \(vec.list c)"; c@65: println "matrix is:"; c@65: mat.print m; c@65: else c@65: print "✓"; c@65: fi; c@65: good; c@65: done (mat.asColumns m)); c@65: success; c@65: ) done c@65: testFreqs; c@65: c@65: tests is hash boolean>