view 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
line wrap: on
line source

module test_frequency;

mat = load may.matrix;
vec = load may.vector;
win = load may.signal.window;
mm = load may.mathmisc;
cm = load may.matrix.complex;
syn = load may.stream.syntheticstream;
plot = load may.plot;

{ cqt } = load cqt;

// Test with a single windowed sinusoid, repeating at various frequencies

sinTestStream sampleRate duration signalFreq = // duration is in samples
   (sin = syn.sinusoid sampleRate signalFreq;
    chunk = mat.getRow 0 (sin.read duration);
    syn.precalculatedMono sampleRate (win.windowed win.hann chunk));

// We want to make a CQ transform spanning more than one octave, but
// not going all the way to fs/2 so we can test it also with
// frequencies above and below its extents

sampleRate = 100;

// fs/2 = 50 so 10->40 gives us 2 octaves
cqmin = 10;
cqmax = 40;
bpo = 4; // fairly arbitrary

testFreqs = map (* 5) [ 0..10 ];
duration = sampleRate * 2;

threshold = 0.08;

streamBuilder = sinTestStream sampleRate duration;

binForFreq f =
    mm.round (bpo * mm.log2 (f / cqmin)) - 1;

report message matrix =
   (eprintln message;
    eprintln "matrix is:";
    mat.eprint matrix;
    chart = plot.plot [Grid matrix];
    sleep 100;
    chart#dispose());

tests = mapIntoHash
    do f: "freq_\(f)" done
    do f: \(
        str = streamBuilder f;
        cq = cqt { maxFreq = cqmax, minFreq = cqmin, binsPerOctave = bpo } str;
        m = mat.concatHorizontal (map cm.magnitudes cq.output);
//    println "binFrequencies = \(cq.kernel.binFrequencies)";
//    println "binForFreq \(f) = \(binForFreq f)";
        var colno = 0;
        success = all id
           (map do c:
                // The test passes for this column if:
                //
                //  * the max bin is the expected one, or
                //
                //  * the expected max is out of range entirely (but
                //  we need to test _something_ in this case --
                //  what?), or
                //
                //  * all bins are below a threshold, or
                //
                //  * this is an odd column and the expected max is in
                //  the lower octave
                //
                // We should also check that all values in the lower
                // octave are zero for odd columns.
                //
                expected = binForFreq f;
                good =
                   (expected < 0 or expected >= vec.length c) or
                   ((colno % 2 == 1) and expected < (vec.length c / 2)) or
                   (vec.max c < threshold) or
                   (vec.maxindex c == binForFreq f);
                if (not good) then
                    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;
                fi;
                colno := colno + 1;
                good;
            done (mat.asColumns m));
        success;
    ) done
    testFreqs;

tests is hash<string, () -> boolean>