c@170
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@170
|
2 /*
|
c@170
|
3 Constant-Q library
|
c@170
|
4 Copyright (c) 2013-2015 Queen Mary, University of London
|
c@170
|
5
|
c@170
|
6 Permission is hereby granted, free of charge, to any person
|
c@170
|
7 obtaining a copy of this software and associated documentation
|
c@170
|
8 files (the "Software"), to deal in the Software without
|
c@170
|
9 restriction, including without limitation the rights to use, copy,
|
c@170
|
10 modify, merge, publish, distribute, sublicense, and/or sell copies
|
c@170
|
11 of the Software, and to permit persons to whom the Software is
|
c@170
|
12 furnished to do so, subject to the following conditions:
|
c@170
|
13
|
c@170
|
14 The above copyright notice and this permission notice shall be
|
c@170
|
15 included in all copies or substantial portions of the Software.
|
c@170
|
16
|
c@170
|
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
c@170
|
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
c@170
|
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
c@170
|
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
c@170
|
21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
c@170
|
22 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
c@170
|
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
c@170
|
24
|
c@170
|
25 Except as contained in this notice, the names of the Centre for
|
c@170
|
26 Digital Music; Queen Mary, University of London; and Chris Cannam
|
c@170
|
27 shall not be used in advertising or otherwise to promote the sale,
|
c@170
|
28 use or other dealings in this Software without prior written
|
c@170
|
29 authorization.
|
c@170
|
30 */
|
c@170
|
31
|
c@170
|
32 #include "Chromagram.h"
|
c@170
|
33 #include "CQSpectrogram.h"
|
c@170
|
34 #include "Pitch.h"
|
c@170
|
35
|
c@170
|
36 #include <cstdio>
|
c@170
|
37
|
c@170
|
38 using namespace std;
|
c@170
|
39
|
c@170
|
40 Chromagram::Chromagram(Parameters params) :
|
c@170
|
41 m_params(params),
|
c@170
|
42 m_cq(0)
|
c@170
|
43 {
|
c@171
|
44 int highestOctave = m_params.lowestOctave + m_params.octaveCount - 1;
|
c@170
|
45
|
c@170
|
46 int midiPitchLimit = (1 + highestOctave) * 12 + 12; // C just beyond top
|
c@170
|
47 double midiPitchLimitFreq = Pitch::getFrequencyForPitch
|
c@170
|
48 (midiPitchLimit, 0, m_params.tuningFrequency);
|
c@170
|
49
|
c@170
|
50 // Max frequency is frequency of the MIDI pitch just beyond the
|
c@170
|
51 // top octave range (midiPitchLimit) minus one bin, then minus
|
c@170
|
52 // floor(bins per semitone / 2)
|
c@171
|
53 int bps = m_params.binsPerOctave / 12;
|
c@170
|
54 m_maxFrequency = midiPitchLimitFreq /
|
c@180
|
55 pow(2.0, (1.0 + floor(bps/2)) / m_params.binsPerOctave);
|
c@170
|
56
|
c@170
|
57 // Min frequency is frequency of midiPitchLimit lowered by the
|
c@171
|
58 // appropriate number of octaveCount.
|
c@170
|
59 m_minFrequency = midiPitchLimitFreq /
|
c@180
|
60 pow(2.0, m_params.octaveCount + 1);
|
c@170
|
61
|
c@170
|
62 CQParameters p
|
c@171
|
63 (params.sampleRate, m_minFrequency, m_maxFrequency, params.binsPerOctave);
|
c@171
|
64
|
c@171
|
65 p.q = params.q;
|
c@171
|
66 p.atomHopFactor = params.atomHopFactor;
|
c@171
|
67 p.threshold = params.threshold;
|
c@171
|
68 p.window = params.window;
|
c@170
|
69
|
c@170
|
70 m_cq = new CQSpectrogram(p, CQSpectrogram::InterpolateLinear);
|
c@170
|
71 }
|
c@170
|
72
|
c@170
|
73 Chromagram::~Chromagram()
|
c@170
|
74 {
|
c@170
|
75 delete m_cq;
|
c@170
|
76 }
|
c@170
|
77
|
c@170
|
78 bool
|
c@170
|
79 Chromagram::isValid() const
|
c@170
|
80 {
|
c@170
|
81 return m_cq->isValid();
|
c@170
|
82 }
|
c@170
|
83
|
c@170
|
84 int
|
c@170
|
85 Chromagram::getColumnHop() const
|
c@170
|
86 {
|
c@170
|
87 return m_cq->getColumnHop();
|
c@170
|
88 }
|
c@170
|
89
|
c@170
|
90 int
|
c@170
|
91 Chromagram::getLatency() const
|
c@170
|
92 {
|
c@170
|
93 return m_cq->getLatency();
|
c@170
|
94 }
|
c@170
|
95
|
c@170
|
96 string
|
c@170
|
97 Chromagram::getBinName(int bin) const
|
c@170
|
98 {
|
c@170
|
99 static const char *names[] = {
|
c@170
|
100 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
|
c@170
|
101 };
|
c@170
|
102
|
c@171
|
103 float freq = m_cq->getBinFrequency(m_params.binsPerOctave - bin - 1);
|
c@170
|
104 int note = Pitch::getPitchForFrequency(freq, 0, m_params.tuningFrequency);
|
c@170
|
105 float nearestFreq =
|
c@170
|
106 Pitch::getFrequencyForPitch(note, 0, m_params.tuningFrequency);
|
c@170
|
107
|
c@170
|
108 char name[40];
|
c@170
|
109 sprintf(name, "%d", bin);
|
c@170
|
110 if (fabs(freq - nearestFreq) < 0.01) {
|
c@170
|
111 return (name + std::string(" ") + names[note % 12]);
|
c@170
|
112 } else {
|
c@170
|
113 return (name);
|
c@170
|
114 }
|
c@170
|
115 }
|
c@170
|
116
|
c@170
|
117 CQBase::RealBlock
|
c@170
|
118 Chromagram::process(const CQBase::RealSequence &data)
|
c@170
|
119 {
|
c@170
|
120 return convert(m_cq->process(data));
|
c@170
|
121 }
|
c@170
|
122
|
c@170
|
123 CQBase::RealBlock
|
c@170
|
124 Chromagram::getRemainingOutput()
|
c@170
|
125 {
|
c@170
|
126 return convert(m_cq->getRemainingOutput());
|
c@170
|
127 }
|
c@170
|
128
|
c@170
|
129 CQBase::RealBlock
|
c@170
|
130 Chromagram::convert(const CQBase::RealBlock &cqout)
|
c@170
|
131 {
|
c@170
|
132 CQBase::RealBlock chroma;
|
c@170
|
133
|
c@170
|
134 int width = cqout.size();
|
c@170
|
135
|
c@170
|
136 for (int i = 0; i < width; ++i) {
|
c@170
|
137
|
c@171
|
138 CQBase::RealSequence column(m_params.binsPerOctave, 0.);
|
c@170
|
139
|
c@170
|
140 // fold and invert to put low frequencies at the start
|
c@170
|
141
|
c@170
|
142 int thisHeight = cqout[i].size();
|
c@170
|
143
|
c@170
|
144 for (int j = 0; j < thisHeight; ++j) {
|
c@171
|
145 column[m_params.binsPerOctave - (j % m_params.binsPerOctave) - 1]
|
c@171
|
146 += cqout[i][j];
|
c@170
|
147 }
|
c@170
|
148
|
c@170
|
149 chroma.push_back(column);
|
c@170
|
150 }
|
c@170
|
151
|
c@170
|
152 return chroma;
|
c@170
|
153 }
|
c@170
|
154
|