comparison src/CQSpectrogram.cpp @ 116:6deec2a51d13

Moving to standalone library layout
author Chris Cannam <c.cannam@qmul.ac.uk>
date Thu, 15 May 2014 12:04:00 +0100
parents
children 8996465e39fc
comparison
equal deleted inserted replaced
115:93be4aa255e5 116:6deec2a51d13
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 /*
3 Constant-Q library
4 Copyright (c) 2013-2014 Queen Mary, University of London
5
6 Permission is hereby granted, free of charge, to any person
7 obtaining a copy of this software and associated documentation
8 files (the "Software"), to deal in the Software without
9 restriction, including without limitation the rights to use, copy,
10 modify, merge, publish, distribute, sublicense, and/or sell copies
11 of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
22 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 Except as contained in this notice, the names of the Centre for
26 Digital Music; Queen Mary, University of London; and Chris Cannam
27 shall not be used in advertising or otherwise to promote the sale,
28 use or other dealings in this Software without prior written
29 authorization.
30 */
31
32 #include "CQSpectrogram.h"
33
34 #include <iostream>
35 #include <stdexcept>
36
37 using std::cerr;
38 using std::endl;
39
40 CQSpectrogram::CQSpectrogram(double sampleRate,
41 double minFreq, double maxFreq,
42 int binsPerOctave,
43 Interpolation interpolation) :
44 m_cq(sampleRate, minFreq, maxFreq, binsPerOctave),
45 m_interpolation(interpolation)
46 {
47 }
48
49 CQSpectrogram::~CQSpectrogram()
50 {
51 }
52
53 CQSpectrogram::RealBlock
54 CQSpectrogram::process(const RealSequence &td)
55 {
56 return postProcess(m_cq.process(td), false);
57 }
58
59 CQSpectrogram::RealBlock
60 CQSpectrogram::getRemainingOutput()
61 {
62 return postProcess(m_cq.getRemainingOutput(), true);
63 }
64
65 CQSpectrogram::RealBlock
66 CQSpectrogram::postProcess(const ComplexBlock &cq, bool insist)
67 {
68 int width = cq.size();
69
70 // convert to magnitudes
71 RealBlock spec;
72 for (int i = 0; i < width; ++i) {
73 int height = cq[i].size();
74 RealColumn col(height, 0);
75 for (int j = 0; j < height; ++j) {
76 col[j] = abs(cq[i][j]);
77 }
78 spec.push_back(col);
79 }
80
81 if (m_interpolation == InterpolateZeros) {
82 for (int i = 0; i < width; ++i) {
83 int sh = spec[i].size();
84 int fh = getTotalBins();
85 for (int j = sh; j < fh; ++j) {
86 spec[i].push_back(0);
87 }
88 }
89 return spec;
90 }
91
92 for (int i = 0; i < width; ++i) {
93 m_buffer.push_back(spec[i]);
94 }
95
96 if (m_interpolation == InterpolateHold) {
97 return fetchHold(insist);
98 } else {
99 return fetchLinear(insist);
100 }
101 }
102
103 CQSpectrogram::RealBlock
104 CQSpectrogram::fetchHold(bool)
105 {
106 RealBlock out;
107
108 int width = m_buffer.size();
109 int height = getTotalBins();
110
111 for (int i = 0; i < width; ++i) {
112
113 RealColumn col = m_buffer[i];
114
115 int thisHeight = col.size();
116 int prevHeight = m_prevColumn.size();
117
118 for (int j = thisHeight; j < height; ++j) {
119 if (j < prevHeight) {
120 col.push_back(m_prevColumn[j]);
121 } else {
122 col.push_back(0.0);
123 }
124 }
125
126 m_prevColumn = col;
127 out.push_back(col);
128 }
129
130 m_buffer.clear();
131
132 return out;
133 }
134
135 CQSpectrogram::RealBlock
136 CQSpectrogram::fetchLinear(bool insist)
137 {
138 RealBlock out;
139
140 //!!! This is surprisingly messy. I must be missing something.
141
142 // We can only return any data when we have at least one column
143 // that has the full height in the buffer, that is not the first
144 // column.
145 //
146 // If the first col has full height, and there is another one
147 // later that also does, then we can interpolate between those, up
148 // to but not including the second full height column. Then we
149 // drop and return the columns we interpolated, leaving the second
150 // full-height col as the first col in the buffer. And repeat as
151 // long as enough columns are available.
152 //
153 // If the first col does not have full height, then (so long as
154 // we're following the logic above) we must simply have not yet
155 // reached the first full-height column in the CQ output, and we
156 // can interpolate nothing.
157
158 int width = m_buffer.size();
159 int height = getTotalBins();
160
161 if (width == 0) return out;
162
163 int firstFullHeight = -1;
164 int secondFullHeight = -1;
165
166 for (int i = 0; i < width; ++i) {
167 if ((int)m_buffer[i].size() == height) {
168 if (firstFullHeight == -1) {
169 firstFullHeight = i;
170 } else if (secondFullHeight == -1) {
171 secondFullHeight = i;
172 break;
173 }
174 }
175 }
176
177 // cerr << "fetchLinear: firstFullHeight = " << firstFullHeight << ", secondFullHeight = " << secondFullHeight << endl;
178
179 if (firstFullHeight < 0) {
180 if (insist) {
181 return fetchHold(true);
182 } else {
183 return out;
184 }
185 } else if (firstFullHeight > 0) {
186 // can interpolate nothing, stash up to first full height & recurse
187 out = RealBlock(m_buffer.begin(), m_buffer.begin() + firstFullHeight);
188 m_buffer = RealBlock(m_buffer.begin() + firstFullHeight, m_buffer.end());
189 RealBlock more = fetchLinear(insist);
190 out.insert(out.end(), more.begin(), more.end());
191 return out;
192 } else if (secondFullHeight < 0) {
193 // firstFullHeight == 0, but there is no second full height --
194 // wait for it unless insist flag is set
195 if (insist) {
196 return fetchHold(true);
197 } else {
198 return out;
199 }
200 } else {
201 // firstFullHeight == 0 and secondFullHeight also valid. Can interpolate
202 out = linearInterpolated(m_buffer, 0, secondFullHeight);
203 m_buffer = RealBlock(m_buffer.begin() + secondFullHeight, m_buffer.end());
204 RealBlock more = fetchLinear(insist);
205 out.insert(out.end(), more.begin(), more.end());
206 return out;
207 }
208 }
209
210 CQSpectrogram::RealBlock
211 CQSpectrogram::linearInterpolated(const RealBlock &g, int x0, int x1)
212 {
213 // g must be a grid with full-height columns at x0 and x1
214
215 if (x0 >= x1) {
216 throw std::logic_error("x0 >= x1");
217 }
218 if (x1 >= (int)g.size()) {
219 throw std::logic_error("x1 >= g.size()");
220 }
221 if (g[x0].size() != g[x1].size()) {
222 throw std::logic_error("x0 and x1 are not the same height");
223 }
224
225 int height = g[x0].size();
226 int width = x1 - x0;
227
228 RealBlock out(g.begin() + x0, g.begin() + x1);
229
230 for (int y = 0; y < height; ++y) {
231
232 int spacing = width;
233 for (int i = 1; i < width; ++i) {
234 int thisHeight = g[x0 + i].size();
235 if (thisHeight > height) {
236 throw std::logic_error("First column not full-height");
237 }
238 if (thisHeight > y) {
239 spacing = i;
240 break;
241 }
242 }
243
244 if (spacing < 2) continue;
245
246 for (int i = 0; i + spacing <= width; i += spacing) {
247 for (int j = 1; j < spacing; ++j) {
248 double proportion = double(j)/double(spacing);
249 double interpolated =
250 g[x0 + i][y] * (1.0 - proportion) +
251 g[x0 + i + spacing][y] * proportion;
252 out[i + j].push_back(interpolated);
253 }
254 }
255 }
256
257 return out;
258 }
259
260
261