c@116
|
1 /*
|
c@116
|
2 Constant-Q library
|
c@116
|
3 Copyright (c) 2013-2014 Queen Mary, University of London
|
c@116
|
4
|
c@116
|
5 Permission is hereby granted, free of charge, to any person
|
c@116
|
6 obtaining a copy of this software and associated documentation
|
c@116
|
7 files (the "Software"), to deal in the Software without
|
c@116
|
8 restriction, including without limitation the rights to use, copy,
|
c@116
|
9 modify, merge, publish, distribute, sublicense, and/or sell copies
|
c@116
|
10 of the Software, and to permit persons to whom the Software is
|
c@116
|
11 furnished to do so, subject to the following conditions:
|
c@116
|
12
|
c@116
|
13 The above copyright notice and this permission notice shall be
|
c@116
|
14 included in all copies or substantial portions of the Software.
|
c@116
|
15
|
c@116
|
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
c@116
|
17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
c@116
|
18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
c@116
|
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
c@116
|
20 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
c@116
|
21 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
c@116
|
22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
c@116
|
23
|
c@116
|
24 Except as contained in this notice, the names of the Centre for
|
c@116
|
25 Digital Music; Queen Mary, University of London; and Chris Cannam
|
c@116
|
26 shall not be used in advertising or otherwise to promote the sale,
|
c@116
|
27 use or other dealings in this Software without prior written
|
c@116
|
28 authorization.
|
c@116
|
29 */
|
c@116
|
30
|
c@116
|
31 module test_frequency;
|
c@116
|
32
|
c@116
|
33 mat = load may.matrix;
|
c@116
|
34 vec = load may.vector;
|
c@116
|
35 win = load may.signal.window;
|
c@116
|
36 mm = load may.mathmisc;
|
c@116
|
37 cm = load may.matrix.complex;
|
c@116
|
38 syn = load may.stream.syntheticstream;
|
c@116
|
39 plot = load may.plot;
|
c@116
|
40
|
c@116
|
41 { compare } = load may.test;
|
c@116
|
42
|
c@116
|
43 { cqt } = load cqt;
|
c@116
|
44
|
c@116
|
45 // Test with a single windowed sinusoid, repeating at various frequencies
|
c@116
|
46
|
c@116
|
47 sinTestStream sampleRate duration signalFreq = // duration is in samples
|
c@116
|
48 (sin = syn.sinusoid sampleRate signalFreq;
|
c@116
|
49 chunk = mat.getRow 0 (sin.read duration);
|
c@116
|
50 syn.precalculatedMono sampleRate (win.windowed win.hann chunk));
|
c@116
|
51
|
c@116
|
52 // We want to make a CQ transform spanning more than one octave, but
|
c@116
|
53 // not going all the way to fs/2 so we can test it also with
|
c@116
|
54 // frequencies above and below its extents
|
c@116
|
55
|
c@116
|
56 sampleRate = 100;
|
c@116
|
57
|
c@116
|
58 // fs/2 = 50 so 10->40 gives us 2 octaves
|
c@116
|
59 cqmin = 10;
|
c@116
|
60 cqmax = 40;
|
c@116
|
61 bpo = 4; // fairly arbitrary
|
c@116
|
62
|
c@116
|
63 testFreqs = map (* 5) [ 0..10 ];
|
c@116
|
64 duration = sampleRate * 2;
|
c@116
|
65
|
c@116
|
66 threshold = 0.08;
|
c@116
|
67
|
c@116
|
68 streamBuilder = sinTestStream sampleRate duration;
|
c@116
|
69
|
c@116
|
70 binForFreq f =
|
c@116
|
71 mm.round (bpo * mm.log2 (f / cqmin)) - 1;
|
c@116
|
72
|
c@116
|
73 report message matrix =
|
c@116
|
74 (eprintln message;
|
c@116
|
75 eprintln "matrix is:";
|
c@116
|
76 mat.eprint matrix);
|
c@116
|
77 // chart = plot.plot [Grid matrix];
|
c@116
|
78 // sleep 100;
|
c@116
|
79 // chart#dispose());
|
c@116
|
80
|
c@116
|
81 tests = mapIntoHash
|
c@116
|
82 do f: "freq_\(f)" done
|
c@116
|
83 do f: \(
|
c@116
|
84 str = streamBuilder f;
|
c@116
|
85 cq = cqt { maxFreq = cqmax, minFreq = cqmin, binsPerOctave = bpo } str;
|
c@116
|
86 spec = cq.cqSpectrogram;
|
c@116
|
87 rightSize = all id
|
c@116
|
88 (map do s:
|
c@116
|
89 compare (mat.size s) {
|
c@116
|
90 rows = cq.kernel.binsPerOctave * cq.octaves,
|
c@116
|
91 columns = cq.kernel.atomsPerFrame * mm.pow 2 (cq.octaves - 1)
|
c@116
|
92 }
|
c@116
|
93 done spec);
|
c@116
|
94 m = mat.concatHorizontal spec;
|
c@116
|
95 // println "binFrequencies = \(cq.kernel.binFrequencies)";
|
c@116
|
96 // println "binForFreq \(f) = \(binForFreq f)";
|
c@116
|
97 var colno = 0;
|
c@116
|
98 success = all id
|
c@116
|
99 (rightSize :: map do c:
|
c@116
|
100 // The test passes for this column if:
|
c@116
|
101 //
|
c@116
|
102 // * the max bin is the expected one, or
|
c@116
|
103 //
|
c@116
|
104 // * the expected max is out of range entirely (but
|
c@116
|
105 // we need to test _something_ in this case --
|
c@116
|
106 // what?), or
|
c@116
|
107 //
|
c@116
|
108 // * all bins are below a threshold, or
|
c@116
|
109 //
|
c@116
|
110 // * this is an odd column and the expected max is in
|
c@116
|
111 // the lower octave
|
c@116
|
112 //
|
c@116
|
113 // We should also check that all values in the lower
|
c@116
|
114 // octave are zero for odd columns.
|
c@116
|
115 //
|
c@116
|
116 expected = binForFreq f;
|
c@116
|
117 good =
|
c@116
|
118 (expected < 0 or expected >= vec.length c) or
|
c@116
|
119 ((colno % 2 == 1) and expected < (vec.length c / 2)) or
|
c@116
|
120 (vec.max c < threshold) or
|
c@116
|
121 (vec.maxindex c == binForFreq f);
|
c@116
|
122 if (not good) then
|
c@116
|
123 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
|
124 fi;
|
c@116
|
125 colno := colno + 1;
|
c@116
|
126 good;
|
c@116
|
127 done (mat.asColumns m));
|
c@116
|
128 success;
|
c@116
|
129 ) done
|
c@116
|
130 testFreqs;
|
c@116
|
131
|
c@116
|
132 tests is hash<string, () -> boolean>
|