annotate tests/TestChromagram.cpp @ 468:a72d98f8baa3

Revise mechanism for extending chromagram to round number of octaves - do it only in the chromagram itself, so that we can still create deviant constant-Q spectrograms if desired
author Chris Cannam <cannam@all-day-breakfast.com>
date Thu, 30 May 2019 11:35:35 +0100
parents 1db23b9a8da4
children dd132354ea02
rev   line source
c@451 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@451 2
c@451 3 #include "dsp/chromagram/Chromagram.h"
c@451 4
c@451 5 #include <iostream>
c@451 6
c@451 7 #include <cmath>
c@451 8
c@451 9 #define BOOST_TEST_DYN_LINK
c@451 10 #define BOOST_TEST_MAIN
c@451 11
c@451 12 #include <boost/test/unit_test.hpp>
c@451 13
c@451 14 BOOST_AUTO_TEST_SUITE(TestChromagram)
c@451 15
c@451 16 using std::cout;
c@451 17 using std::endl;
c@451 18 using std::string;
c@451 19 using std::vector;
c@451 20
c@451 21 string midiPitchName(int midiPitch)
c@451 22 {
c@451 23 static string names[] = {
c@451 24 "C", "C#", "D", "D#",
c@451 25 "E", "F", "F#", "G",
c@451 26 "G#", "A", "A#", "B"
c@451 27 };
c@451 28
c@451 29 return names[midiPitch % 12];
c@451 30 }
c@451 31
c@451 32 vector<double> generateSinusoid(double frequency,
cannam@465 33 double sampleRate,
c@451 34 int length)
c@451 35 {
c@451 36 vector<double> buffer;
c@451 37 for (int i = 0; i < length; ++i) {
cannam@453 38 buffer.push_back(sin((i * M_PI * 2.0 * frequency) / sampleRate));
c@451 39 }
c@451 40 return buffer;
c@451 41 }
c@451 42
c@451 43 double frequencyForPitch(int midiPitch, double concertA)
c@451 44 {
c@451 45 return concertA * pow(2.0, (midiPitch - 69.0) / 12.0);
c@451 46 }
c@451 47
c@451 48 BOOST_AUTO_TEST_CASE(sinusoid_12tET)
c@451 49 {
c@451 50 double concertA = 440.0;
cannam@467 51 double sampleRate = 44100.0;
c@451 52 int bpo = 60;
c@451 53
c@451 54 ChromaConfig config {
c@451 55 sampleRate,
c@451 56 frequencyForPitch(36, concertA),
c@451 57 frequencyForPitch(108, concertA),
c@451 58 bpo,
c@451 59 0.0054,
c@451 60 MathUtilities::NormaliseNone
c@451 61 };
c@451 62
c@451 63 Chromagram chroma(config);
c@451 64
cannam@453 65 for (int midiPitch = 36; midiPitch < 108; ++midiPitch) {
c@451 66
c@451 67 cout << endl;
c@451 68
c@451 69 int blockSize = chroma.getFrameSize();
c@451 70 int hopSize = chroma.getHopSize();
c@451 71 cerr << "blockSize = " << blockSize
c@451 72 << ", hopSize = " << hopSize << endl;
c@451 73
c@451 74 double frequency = frequencyForPitch(midiPitch, concertA);
c@451 75 int expectedPeakBin = ((midiPitch - 36) * 5) % bpo;
c@451 76
c@451 77 cout << "midiPitch = " << midiPitch
c@451 78 << ", name = " << midiPitchName(midiPitch)
c@451 79 << ", frequency = " << frequency
c@451 80 << ", expected peak bin = "
c@451 81 << expectedPeakBin << endl;
c@451 82
c@451 83 vector<double> signal = generateSinusoid(frequency,
c@451 84 sampleRate,
c@451 85 blockSize);
cannam@467 86
c@451 87 double *output = chroma.process(signal.data());
c@451 88
c@451 89 int peakBin = -1;
c@451 90 double peakValue = 0.0;
c@451 91
c@451 92 for (int i = 0; i < bpo; ++i) {
c@451 93 if (i == 0 || output[i] > peakValue) {
c@451 94 peakValue = output[i];
c@451 95 peakBin = i;
c@451 96 }
c@451 97 }
c@451 98
c@451 99 cout << "peak value = " << peakValue << " at bin " << peakBin << endl;
c@451 100 cout << "(neighbouring values are "
c@451 101 << (peakBin > 0 ? output[peakBin-1] : output[bpo-1])
c@451 102 << " and "
c@451 103 << (peakBin+1 < bpo ? output[peakBin+1] : output[0])
c@451 104 << ")" << endl;
c@451 105
cannam@463 106 if (peakBin != expectedPeakBin) {
cannam@463 107 cout << "NOTE: peak bin " << peakBin << " does not match expected " << expectedPeakBin << endl;
cannam@467 108 cout << "bin values are: ";
cannam@467 109 for (int i = 0; i < bpo; ++i) {
cannam@467 110 cout << i << ": " << output[i] << " ";
cannam@467 111 }
cannam@467 112 cout << endl;
cannam@463 113 }
c@451 114 }
c@451 115 }
c@451 116
c@451 117 BOOST_AUTO_TEST_SUITE_END()