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,
|
c@451
|
33 int sampleRate,
|
c@451
|
34 int length)
|
c@451
|
35 {
|
c@451
|
36 vector<double> buffer;
|
c@451
|
37 buffer.reserve(length);
|
c@451
|
38 for (int i = 0; i < length; ++i) {
|
c@451
|
39 buffer.push_back(sin(i * M_PI * 2.0 * frequency / sampleRate));
|
c@451
|
40 }
|
c@451
|
41 return buffer;
|
c@451
|
42 }
|
c@451
|
43
|
c@451
|
44 double frequencyForPitch(int midiPitch, double concertA)
|
c@451
|
45 {
|
c@451
|
46 return concertA * pow(2.0, (midiPitch - 69.0) / 12.0);
|
c@451
|
47 }
|
c@451
|
48
|
c@451
|
49 BOOST_AUTO_TEST_CASE(sinusoid_12tET)
|
c@451
|
50 {
|
c@451
|
51 double concertA = 440.0;
|
c@451
|
52 int sampleRate = 44100;
|
c@451
|
53 int bpo = 60;
|
c@451
|
54
|
c@451
|
55 ChromaConfig config {
|
c@451
|
56 sampleRate,
|
c@451
|
57 frequencyForPitch(36, concertA),
|
c@451
|
58 frequencyForPitch(108, concertA),
|
c@451
|
59 bpo,
|
c@451
|
60 0.0054,
|
c@451
|
61 MathUtilities::NormaliseNone
|
c@451
|
62 };
|
c@451
|
63
|
c@451
|
64 Chromagram chroma(config);
|
c@451
|
65
|
c@451
|
66 for (int midiPitch = 48; midiPitch < 96; ++midiPitch) {
|
c@451
|
67
|
c@451
|
68 cout << endl;
|
c@451
|
69
|
c@451
|
70 int blockSize = chroma.getFrameSize();
|
c@451
|
71 int hopSize = chroma.getHopSize();
|
c@451
|
72 cerr << "blockSize = " << blockSize
|
c@451
|
73 << ", hopSize = " << hopSize << endl;
|
c@451
|
74
|
c@451
|
75 double frequency = frequencyForPitch(midiPitch, concertA);
|
c@451
|
76 int expectedPeakBin = ((midiPitch - 36) * 5) % bpo;
|
c@451
|
77
|
c@451
|
78 cout << "midiPitch = " << midiPitch
|
c@451
|
79 << ", name = " << midiPitchName(midiPitch)
|
c@451
|
80 << ", frequency = " << frequency
|
c@451
|
81 << ", expected peak bin = "
|
c@451
|
82 << expectedPeakBin << endl;
|
c@451
|
83
|
c@451
|
84 vector<double> signal = generateSinusoid(frequency,
|
c@451
|
85 sampleRate,
|
c@451
|
86 blockSize);
|
c@451
|
87
|
c@451
|
88 double *output = chroma.process(signal.data());
|
c@451
|
89
|
c@451
|
90 int peakBin = -1;
|
c@451
|
91 double peakValue = 0.0;
|
c@451
|
92
|
c@451
|
93 for (int i = 0; i < bpo; ++i) {
|
c@451
|
94 if (i == 0 || output[i] > peakValue) {
|
c@451
|
95 peakValue = output[i];
|
c@451
|
96 peakBin = i;
|
c@451
|
97 }
|
c@451
|
98 }
|
c@451
|
99
|
c@451
|
100 cout << "peak value = " << peakValue << " at bin " << peakBin << endl;
|
c@451
|
101 cout << "(neighbouring values are "
|
c@451
|
102 << (peakBin > 0 ? output[peakBin-1] : output[bpo-1])
|
c@451
|
103 << " and "
|
c@451
|
104 << (peakBin+1 < bpo ? output[peakBin+1] : output[0])
|
c@451
|
105 << ")" << endl;
|
c@451
|
106
|
c@451
|
107 BOOST_CHECK_EQUAL(peakBin, expectedPeakBin);
|
c@451
|
108 }
|
c@451
|
109 }
|
c@451
|
110
|
c@451
|
111 BOOST_AUTO_TEST_SUITE_END()
|