c@116: /* c@116: Constant-Q library c@116: Copyright (c) 2013-2014 Queen Mary, University of London c@116: c@116: Permission is hereby granted, free of charge, to any person c@116: obtaining a copy of this software and associated documentation c@116: files (the "Software"), to deal in the Software without c@116: restriction, including without limitation the rights to use, copy, c@116: modify, merge, publish, distribute, sublicense, and/or sell copies c@116: of the Software, and to permit persons to whom the Software is c@116: furnished to do so, subject to the following conditions: c@116: c@116: The above copyright notice and this permission notice shall be c@116: included in all copies or substantial portions of the Software. c@116: c@116: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, c@116: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF c@116: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND c@116: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY c@116: CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF c@116: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION c@116: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. c@116: c@116: Except as contained in this notice, the names of the Centre for c@116: Digital Music; Queen Mary, University of London; and Chris Cannam c@116: shall not be used in advertising or otherwise to promote the sale, c@116: use or other dealings in this Software without prior written c@116: authorization. c@116: */ c@116: c@116: module test_frequency; c@116: c@116: mat = load may.matrix; c@116: vec = load may.vector; c@116: win = load may.signal.window; c@116: mm = load may.mathmisc; c@116: cm = load may.matrix.complex; c@116: syn = load may.stream.syntheticstream; c@116: plot = load may.plot; c@116: c@116: { compare } = load may.test; c@116: c@116: { cqt } = load cqt; c@116: c@116: // Test with a single windowed sinusoid, repeating at various frequencies c@116: c@116: sinTestStream sampleRate duration signalFreq = // duration is in samples c@116: (sin = syn.sinusoid sampleRate signalFreq; c@116: chunk = mat.getRow 0 (sin.read duration); c@116: syn.precalculatedMono sampleRate (win.windowed win.hann chunk)); c@116: c@116: // We want to make a CQ transform spanning more than one octave, but c@116: // not going all the way to fs/2 so we can test it also with c@116: // frequencies above and below its extents c@116: c@116: sampleRate = 100; c@116: c@116: // fs/2 = 50 so 10->40 gives us 2 octaves c@116: cqmin = 10; c@116: cqmax = 40; c@116: bpo = 4; // fairly arbitrary c@116: c@116: testFreqs = map (* 5) [ 0..10 ]; c@116: duration = sampleRate * 2; c@116: c@116: threshold = 0.08; c@116: c@116: streamBuilder = sinTestStream sampleRate duration; c@116: c@116: binForFreq f = c@116: mm.round (bpo * mm.log2 (f / cqmin)) - 1; c@116: c@116: report message matrix = c@116: (eprintln message; c@116: eprintln "matrix is:"; c@116: mat.eprint matrix); c@116: // chart = plot.plot [Grid matrix]; c@116: // sleep 100; c@116: // chart#dispose()); c@116: c@116: tests = mapIntoHash c@116: do f: "freq_\(f)" done c@116: do f: \( c@116: str = streamBuilder f; c@116: cq = cqt { maxFreq = cqmax, minFreq = cqmin, binsPerOctave = bpo } str; c@116: spec = cq.cqSpectrogram; c@116: rightSize = all id c@116: (map do s: c@116: compare (mat.size s) { c@116: rows = cq.kernel.binsPerOctave * cq.octaves, c@116: columns = cq.kernel.atomsPerFrame * mm.pow 2 (cq.octaves - 1) c@116: } c@116: done spec); c@116: m = mat.concatHorizontal spec; c@116: // println "binFrequencies = \(cq.kernel.binFrequencies)"; c@116: // println "binForFreq \(f) = \(binForFreq f)"; c@116: var colno = 0; c@116: success = all id c@116: (rightSize :: map do c: c@116: // The test passes for this column if: c@116: // c@116: // * the max bin is the expected one, or c@116: // c@116: // * the expected max is out of range entirely (but c@116: // we need to test _something_ in this case -- c@116: // what?), or c@116: // c@116: // * all bins are below a threshold, or c@116: // c@116: // * this is an odd column and the expected max is in c@116: // the lower octave c@116: // c@116: // We should also check that all values in the lower c@116: // octave are zero for odd columns. c@116: // c@116: expected = binForFreq f; c@116: good = c@116: (expected < 0 or expected >= vec.length c) or c@116: ((colno % 2 == 1) and expected < (vec.length c / 2)) or c@116: (vec.max c < threshold) or c@116: (vec.maxindex c == binForFreq f); c@116: if (not good) then c@116: 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@116: fi; c@116: colno := colno + 1; c@116: good; c@116: done (mat.asColumns m)); c@116: success; c@116: ) done c@116: testFreqs; c@116: c@116: tests is hash boolean>