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@170
|
44 int highestOctave = m_params.lowestOctave + m_params.octaves - 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@170
|
53 int bps = m_params.bpo / 12;
|
c@170
|
54 m_maxFrequency = midiPitchLimitFreq /
|
c@170
|
55 pow(2, (1.0 + floor(bps/2)) / m_params.bpo);
|
c@170
|
56
|
c@170
|
57 // Min frequency is frequency of midiPitchLimit lowered by the
|
c@170
|
58 // appropriate number of octaves.
|
c@170
|
59 m_minFrequency = midiPitchLimitFreq /
|
c@170
|
60 pow(2, m_params.octaves + 1);
|
c@170
|
61
|
c@170
|
62 CQParameters p
|
c@170
|
63 (params.sampleRate, m_minFrequency, m_maxFrequency, params.bpo);
|
c@170
|
64
|
c@170
|
65 m_cq = new CQSpectrogram(p, CQSpectrogram::InterpolateLinear);
|
c@170
|
66 }
|
c@170
|
67
|
c@170
|
68 Chromagram::~Chromagram()
|
c@170
|
69 {
|
c@170
|
70 delete m_cq;
|
c@170
|
71 }
|
c@170
|
72
|
c@170
|
73 bool
|
c@170
|
74 Chromagram::isValid() const
|
c@170
|
75 {
|
c@170
|
76 return m_cq->isValid();
|
c@170
|
77 }
|
c@170
|
78
|
c@170
|
79 int
|
c@170
|
80 Chromagram::getColumnHop() const
|
c@170
|
81 {
|
c@170
|
82 return m_cq->getColumnHop();
|
c@170
|
83 }
|
c@170
|
84
|
c@170
|
85 int
|
c@170
|
86 Chromagram::getLatency() const
|
c@170
|
87 {
|
c@170
|
88 return m_cq->getLatency();
|
c@170
|
89 }
|
c@170
|
90
|
c@170
|
91 string
|
c@170
|
92 Chromagram::getBinName(int bin) const
|
c@170
|
93 {
|
c@170
|
94 static const char *names[] = {
|
c@170
|
95 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
|
c@170
|
96 };
|
c@170
|
97
|
c@170
|
98 float freq = m_cq->getBinFrequency(m_params.bpo - bin - 1);
|
c@170
|
99 int note = Pitch::getPitchForFrequency(freq, 0, m_params.tuningFrequency);
|
c@170
|
100 float nearestFreq =
|
c@170
|
101 Pitch::getFrequencyForPitch(note, 0, m_params.tuningFrequency);
|
c@170
|
102
|
c@170
|
103 char name[40];
|
c@170
|
104 sprintf(name, "%d", bin);
|
c@170
|
105 if (fabs(freq - nearestFreq) < 0.01) {
|
c@170
|
106 return (name + std::string(" ") + names[note % 12]);
|
c@170
|
107 } else {
|
c@170
|
108 return (name);
|
c@170
|
109 }
|
c@170
|
110 }
|
c@170
|
111
|
c@170
|
112 CQBase::RealBlock
|
c@170
|
113 Chromagram::process(const CQBase::RealSequence &data)
|
c@170
|
114 {
|
c@170
|
115 return convert(m_cq->process(data));
|
c@170
|
116 }
|
c@170
|
117
|
c@170
|
118 CQBase::RealBlock
|
c@170
|
119 Chromagram::getRemainingOutput()
|
c@170
|
120 {
|
c@170
|
121 return convert(m_cq->getRemainingOutput());
|
c@170
|
122 }
|
c@170
|
123
|
c@170
|
124 CQBase::RealBlock
|
c@170
|
125 Chromagram::convert(const CQBase::RealBlock &cqout)
|
c@170
|
126 {
|
c@170
|
127 CQBase::RealBlock chroma;
|
c@170
|
128
|
c@170
|
129 int width = cqout.size();
|
c@170
|
130
|
c@170
|
131 for (int i = 0; i < width; ++i) {
|
c@170
|
132
|
c@170
|
133 CQBase::RealSequence column(m_params.bpo, 0.);
|
c@170
|
134
|
c@170
|
135 // fold and invert to put low frequencies at the start
|
c@170
|
136
|
c@170
|
137 int thisHeight = cqout[i].size();
|
c@170
|
138
|
c@170
|
139 for (int j = 0; j < thisHeight; ++j) {
|
c@170
|
140 column[m_params.bpo - (j % m_params.bpo) - 1] += cqout[i][j];
|
c@170
|
141 }
|
c@170
|
142
|
c@170
|
143 chroma.push_back(column);
|
c@170
|
144 }
|
c@170
|
145
|
c@170
|
146 return chroma;
|
c@170
|
147 }
|
c@170
|
148
|