c@451: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@451: c@451: #include "dsp/chromagram/Chromagram.h" c@451: c@451: #include c@451: c@451: #include c@451: c@451: #define BOOST_TEST_DYN_LINK c@451: #define BOOST_TEST_MAIN c@451: c@451: #include c@451: c@451: BOOST_AUTO_TEST_SUITE(TestChromagram) c@451: c@451: using std::cout; c@451: using std::endl; c@451: using std::string; c@451: using std::vector; c@451: c@451: string midiPitchName(int midiPitch) c@451: { c@451: static string names[] = { c@451: "C", "C#", "D", "D#", c@451: "E", "F", "F#", "G", c@451: "G#", "A", "A#", "B" c@451: }; c@451: c@451: return names[midiPitch % 12]; c@451: } c@451: c@451: vector generateSinusoid(double frequency, cannam@465: double sampleRate, c@451: int length) c@451: { c@451: vector buffer; c@451: for (int i = 0; i < length; ++i) { cannam@453: buffer.push_back(sin((i * M_PI * 2.0 * frequency) / sampleRate)); c@451: } c@451: return buffer; c@451: } c@451: c@451: double frequencyForPitch(int midiPitch, double concertA) c@451: { c@451: return concertA * pow(2.0, (midiPitch - 69.0) / 12.0); c@451: } c@451: cannam@470: void test_sinusoid_12tET(double concertA, double sampleRate, int bpo) c@451: { cannam@470: int chromaMinPitch = 36; cannam@470: int chromaMaxPitch = 108; c@451: cannam@470: int probeMinPitch = 36; cannam@470: int probeMaxPitch = 108; cannam@470: c@451: ChromaConfig config { c@451: sampleRate, cannam@470: frequencyForPitch(chromaMinPitch, concertA), cannam@470: frequencyForPitch(chromaMaxPitch, concertA), c@451: bpo, c@451: 0.0054, c@451: MathUtilities::NormaliseNone c@451: }; c@451: c@451: Chromagram chroma(config); cannam@470: cannam@470: int binsPerSemi = bpo / 12; c@451: cannam@470: for (int midiPitch = probeMinPitch; cannam@470: midiPitch < probeMaxPitch; cannam@470: ++midiPitch) { c@451: c@451: int blockSize = chroma.getFrameSize(); c@451: c@451: double frequency = frequencyForPitch(midiPitch, concertA); cannam@470: int expectedPeakBin = cannam@470: ((midiPitch - chromaMinPitch) * binsPerSemi) % bpo; cannam@470: /* c@451: cout << "midiPitch = " << midiPitch c@451: << ", name = " << midiPitchName(midiPitch) c@451: << ", frequency = " << frequency c@451: << ", expected peak bin = " c@451: << expectedPeakBin << endl; cannam@470: */ c@451: vector signal = generateSinusoid(frequency, c@451: sampleRate, c@451: blockSize); cannam@467: c@451: double *output = chroma.process(signal.data()); c@451: c@451: int peakBin = -1; c@451: double peakValue = 0.0; c@451: c@451: for (int i = 0; i < bpo; ++i) { c@451: if (i == 0 || output[i] > peakValue) { c@451: peakValue = output[i]; c@451: peakBin = i; c@451: } c@451: } cannam@470: /* c@451: cout << "peak value = " << peakValue << " at bin " << peakBin << endl; c@451: cout << "(neighbouring values are " c@451: << (peakBin > 0 ? output[peakBin-1] : output[bpo-1]) c@451: << " and " c@451: << (peakBin+1 < bpo ? output[peakBin+1] : output[0]) c@451: << ")" << endl; cannam@463: if (peakBin != expectedPeakBin) { cannam@463: cout << "NOTE: peak bin " << peakBin << " does not match expected " << expectedPeakBin << endl; cannam@467: cout << "bin values are: "; cannam@467: for (int i = 0; i < bpo; ++i) { cannam@467: cout << i << ": " << output[i] << " "; cannam@467: } cannam@467: cout << endl; cannam@463: } cannam@470: */ cannam@470: cannam@470: BOOST_CHECK_EQUAL(peakBin, expectedPeakBin); c@451: } c@451: } c@451: cannam@470: BOOST_AUTO_TEST_CASE(sinusoid_12tET_440_44100_36) cannam@470: { cannam@470: test_sinusoid_12tET(440.0, 44100.0, 36); cannam@470: } cannam@470: cannam@470: BOOST_AUTO_TEST_CASE(sinusoid_12tET_440_44100_60) cannam@470: { cannam@470: test_sinusoid_12tET(440.0, 44100.0, 60); cannam@470: } cannam@470: cannam@470: BOOST_AUTO_TEST_CASE(sinusoid_12tET_397_44100_60) cannam@470: { cannam@470: test_sinusoid_12tET(397.0, 44100.0, 60); cannam@470: } cannam@470: cannam@470: BOOST_AUTO_TEST_CASE(sinusoid_12tET_440_48000_60) cannam@470: { cannam@470: test_sinusoid_12tET(440.0, 48000.0, 60); cannam@470: } cannam@470: c@451: BOOST_AUTO_TEST_SUITE_END()