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 buffer.reserve(length);
|
c@451
|
38 for (int i = 0; i < length; ++i) {
|
cannam@453
|
39 buffer.push_back(sin((i * M_PI * 2.0 * frequency) / sampleRate));
|
c@451
|
40 }
|
cannam@453
|
41
|
c@451
|
42 return buffer;
|
c@451
|
43 }
|
c@451
|
44
|
c@451
|
45 double frequencyForPitch(int midiPitch, double concertA)
|
c@451
|
46 {
|
c@451
|
47 return concertA * pow(2.0, (midiPitch - 69.0) / 12.0);
|
c@451
|
48 }
|
c@451
|
49
|
c@451
|
50 BOOST_AUTO_TEST_CASE(sinusoid_12tET)
|
c@451
|
51 {
|
c@451
|
52 double concertA = 440.0;
|
cannam@465
|
53 double sampleRate = 44100;
|
c@451
|
54 int bpo = 60;
|
c@451
|
55
|
c@451
|
56 ChromaConfig config {
|
c@451
|
57 sampleRate,
|
c@451
|
58 frequencyForPitch(36, concertA),
|
c@451
|
59 frequencyForPitch(108, 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);
|
c@451
|
66
|
cannam@453
|
67 for (int midiPitch = 36; midiPitch < 108; ++midiPitch) {
|
c@451
|
68
|
c@451
|
69 cout << endl;
|
c@451
|
70
|
c@451
|
71 int blockSize = chroma.getFrameSize();
|
c@451
|
72 int hopSize = chroma.getHopSize();
|
c@451
|
73 cerr << "blockSize = " << blockSize
|
c@451
|
74 << ", hopSize = " << hopSize << endl;
|
c@451
|
75
|
c@451
|
76 double frequency = frequencyForPitch(midiPitch, concertA);
|
c@451
|
77 int expectedPeakBin = ((midiPitch - 36) * 5) % bpo;
|
c@451
|
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;
|
c@451
|
84
|
c@451
|
85 vector<double> signal = generateSinusoid(frequency,
|
c@451
|
86 sampleRate,
|
c@451
|
87 blockSize);
|
cannam@453
|
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 }
|
c@451
|
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;
|
c@451
|
107
|
cannam@463
|
108 if (peakBin != expectedPeakBin) {
|
cannam@463
|
109 cout << "NOTE: peak bin " << peakBin << " does not match expected " << expectedPeakBin << endl;
|
cannam@463
|
110 }
|
c@451
|
111 }
|
c@451
|
112 }
|
c@451
|
113
|
c@451
|
114 BOOST_AUTO_TEST_SUITE_END()
|