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
|
cannam@470
|
48 void test_sinusoid_12tET(double concertA, double sampleRate, int bpo)
|
c@451
|
49 {
|
cannam@470
|
50 int chromaMinPitch = 36;
|
cannam@470
|
51 int chromaMaxPitch = 108;
|
c@451
|
52
|
cannam@470
|
53 int probeMinPitch = 36;
|
cannam@470
|
54 int probeMaxPitch = 108;
|
cannam@470
|
55
|
c@451
|
56 ChromaConfig config {
|
c@451
|
57 sampleRate,
|
cannam@470
|
58 frequencyForPitch(chromaMinPitch, concertA),
|
cannam@470
|
59 frequencyForPitch(chromaMaxPitch, concertA),
|
c@451
|
60 bpo,
|
c@451
|
61 0.0054,
|
c@451
|
62 MathUtilities::NormaliseNone
|
c@451
|
63 };
|
c@451
|
64
|
c@451
|
65 Chromagram chroma(config);
|
cannam@470
|
66
|
cannam@470
|
67 int binsPerSemi = bpo / 12;
|
c@451
|
68
|
cannam@470
|
69 for (int midiPitch = probeMinPitch;
|
cannam@470
|
70 midiPitch < probeMaxPitch;
|
cannam@470
|
71 ++midiPitch) {
|
c@451
|
72
|
c@451
|
73 int blockSize = chroma.getFrameSize();
|
c@451
|
74
|
c@451
|
75 double frequency = frequencyForPitch(midiPitch, concertA);
|
cannam@470
|
76 int expectedPeakBin =
|
cannam@470
|
77 ((midiPitch - chromaMinPitch) * binsPerSemi) % bpo;
|
cannam@470
|
78 /*
|
c@451
|
79 cout << "midiPitch = " << midiPitch
|
c@451
|
80 << ", name = " << midiPitchName(midiPitch)
|
c@451
|
81 << ", frequency = " << frequency
|
c@451
|
82 << ", expected peak bin = "
|
c@451
|
83 << expectedPeakBin << endl;
|
cannam@470
|
84 */
|
c@451
|
85 vector<double> signal = generateSinusoid(frequency,
|
c@451
|
86 sampleRate,
|
c@451
|
87 blockSize);
|
cannam@467
|
88
|
c@451
|
89 double *output = chroma.process(signal.data());
|
c@451
|
90
|
c@451
|
91 int peakBin = -1;
|
c@451
|
92 double peakValue = 0.0;
|
c@451
|
93
|
c@451
|
94 for (int i = 0; i < bpo; ++i) {
|
c@451
|
95 if (i == 0 || output[i] > peakValue) {
|
c@451
|
96 peakValue = output[i];
|
c@451
|
97 peakBin = i;
|
c@451
|
98 }
|
c@451
|
99 }
|
cannam@470
|
100 /*
|
c@451
|
101 cout << "peak value = " << peakValue << " at bin " << peakBin << endl;
|
c@451
|
102 cout << "(neighbouring values are "
|
c@451
|
103 << (peakBin > 0 ? output[peakBin-1] : output[bpo-1])
|
c@451
|
104 << " and "
|
c@451
|
105 << (peakBin+1 < bpo ? output[peakBin+1] : output[0])
|
c@451
|
106 << ")" << endl;
|
cannam@463
|
107 if (peakBin != expectedPeakBin) {
|
cannam@463
|
108 cout << "NOTE: peak bin " << peakBin << " does not match expected " << expectedPeakBin << endl;
|
cannam@467
|
109 cout << "bin values are: ";
|
cannam@467
|
110 for (int i = 0; i < bpo; ++i) {
|
cannam@467
|
111 cout << i << ": " << output[i] << " ";
|
cannam@467
|
112 }
|
cannam@467
|
113 cout << endl;
|
cannam@463
|
114 }
|
cannam@470
|
115 */
|
cannam@470
|
116
|
cannam@470
|
117 BOOST_CHECK_EQUAL(peakBin, expectedPeakBin);
|
c@451
|
118 }
|
c@451
|
119 }
|
c@451
|
120
|
cannam@470
|
121 BOOST_AUTO_TEST_CASE(sinusoid_12tET_440_44100_36)
|
cannam@470
|
122 {
|
cannam@470
|
123 test_sinusoid_12tET(440.0, 44100.0, 36);
|
cannam@470
|
124 }
|
cannam@470
|
125
|
cannam@470
|
126 BOOST_AUTO_TEST_CASE(sinusoid_12tET_440_44100_60)
|
cannam@470
|
127 {
|
cannam@470
|
128 test_sinusoid_12tET(440.0, 44100.0, 60);
|
cannam@470
|
129 }
|
cannam@470
|
130
|
cannam@470
|
131 BOOST_AUTO_TEST_CASE(sinusoid_12tET_397_44100_60)
|
cannam@470
|
132 {
|
cannam@470
|
133 test_sinusoid_12tET(397.0, 44100.0, 60);
|
cannam@470
|
134 }
|
cannam@470
|
135
|
cannam@470
|
136 BOOST_AUTO_TEST_CASE(sinusoid_12tET_440_48000_60)
|
cannam@470
|
137 {
|
cannam@470
|
138 test_sinusoid_12tET(440.0, 48000.0, 60);
|
cannam@470
|
139 }
|
cannam@470
|
140
|
c@451
|
141 BOOST_AUTO_TEST_SUITE_END()
|