comparison constant-q-cpp/src/CQSpectrogram.cpp @ 366:5d0a2ebb4d17

Bring dependent libraries in to repo
author Chris Cannam
date Fri, 24 Jun 2016 14:47:45 +0100
parents
children
comparison
equal deleted inserted replaced
365:112766f4c34b 366:5d0a2ebb4d17
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 //#define DEBUG_CQSPECTROGRAM 1
41
42 CQSpectrogram::CQSpectrogram(CQParameters params,
43 Interpolation interpolation) :
44 m_cq(params),
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 #ifdef DEBUG_CQSPECTROGRAM
77 if (isnan(cq[i][j].real())) {
78 cerr << "WARNING: NaN in real at (" << i << "," << j << ")" << endl;
79 }
80 if (isnan(cq[i][j].imag())) {
81 cerr << "WARNING: NaN in imag at (" << i << "," << j << ")" << endl;
82 }
83 #endif
84 col[j] = abs(cq[i][j]);
85 }
86 spec.push_back(col);
87 }
88
89 if (m_interpolation == InterpolateZeros) {
90 for (int i = 0; i < width; ++i) {
91 int sh = spec[i].size();
92 int fh = getTotalBins();
93 for (int j = sh; j < fh; ++j) {
94 spec[i].push_back(0);
95 }
96 }
97 return spec;
98 }
99
100 for (int i = 0; i < width; ++i) {
101 m_buffer.push_back(spec[i]);
102 }
103
104 if (m_interpolation == InterpolateHold) {
105 return fetchHold(insist);
106 } else {
107 return fetchLinear(insist);
108 }
109 }
110
111 CQSpectrogram::RealBlock
112 CQSpectrogram::fetchHold(bool)
113 {
114 RealBlock out;
115
116 int width = m_buffer.size();
117 int height = getTotalBins();
118
119 for (int i = 0; i < width; ++i) {
120
121 RealColumn col = m_buffer[i];
122
123 int thisHeight = col.size();
124 int prevHeight = m_prevColumn.size();
125
126 for (int j = thisHeight; j < height; ++j) {
127 if (j < prevHeight) {
128 col.push_back(m_prevColumn[j]);
129 } else {
130 col.push_back(0.0);
131 }
132 }
133
134 m_prevColumn = col;
135 out.push_back(col);
136 }
137
138 m_buffer.clear();
139
140 return out;
141 }
142
143 CQSpectrogram::RealBlock
144 CQSpectrogram::fetchLinear(bool insist)
145 {
146 RealBlock out;
147
148 //!!! This is surprisingly messy. I must be missing something.
149
150 // We can only return any data when we have at least one column
151 // that has the full height in the buffer, that is not the first
152 // column.
153 //
154 // If the first col has full height, and there is another one
155 // later that also does, then we can interpolate between those, up
156 // to but not including the second full height column. Then we
157 // drop and return the columns we interpolated, leaving the second
158 // full-height col as the first col in the buffer. And repeat as
159 // long as enough columns are available.
160 //
161 // If the first col does not have full height, then (so long as
162 // we're following the logic above) we must simply have not yet
163 // reached the first full-height column in the CQ output, and we
164 // can interpolate nothing.
165
166 int width = m_buffer.size();
167 int height = getTotalBins();
168
169 if (width == 0) return out;
170
171 int firstFullHeight = -1;
172 int secondFullHeight = -1;
173
174 for (int i = 0; i < width; ++i) {
175 if ((int)m_buffer[i].size() == height) {
176 if (firstFullHeight == -1) {
177 firstFullHeight = i;
178 } else if (secondFullHeight == -1) {
179 secondFullHeight = i;
180 break;
181 }
182 }
183 }
184
185 // cerr << "fetchLinear: firstFullHeight = " << firstFullHeight << ", secondFullHeight = " << secondFullHeight << endl;
186
187 if (firstFullHeight < 0) {
188 if (insist) {
189 return fetchHold(true);
190 } else {
191 return out;
192 }
193 } else if (firstFullHeight > 0) {
194 // can interpolate nothing, stash up to first full height & recurse
195 out = RealBlock(m_buffer.begin(), m_buffer.begin() + firstFullHeight);
196 m_buffer = RealBlock(m_buffer.begin() + firstFullHeight, m_buffer.end());
197 RealBlock more = fetchLinear(insist);
198 out.insert(out.end(), more.begin(), more.end());
199 return out;
200 } else if (secondFullHeight < 0) {
201 // firstFullHeight == 0, but there is no second full height --
202 // wait for it unless insist flag is set
203 if (insist) {
204 return fetchHold(true);
205 } else {
206 return out;
207 }
208 } else {
209 // firstFullHeight == 0 and secondFullHeight also valid. Can interpolate
210 out = linearInterpolated(m_buffer, 0, secondFullHeight);
211 m_buffer = RealBlock(m_buffer.begin() + secondFullHeight, m_buffer.end());
212 RealBlock more = fetchLinear(insist);
213 out.insert(out.end(), more.begin(), more.end());
214 return out;
215 }
216 }
217
218 CQSpectrogram::RealBlock
219 CQSpectrogram::linearInterpolated(const RealBlock &g, int x0, int x1)
220 {
221 // g must be a grid with full-height columns at x0 and x1
222
223 if (x0 >= x1) {
224 throw std::logic_error("x0 >= x1");
225 }
226 if (x1 >= (int)g.size()) {
227 throw std::logic_error("x1 >= g.size()");
228 }
229 if (g[x0].size() != g[x1].size()) {
230 throw std::logic_error("x0 and x1 are not the same height");
231 }
232
233 int height = g[x0].size();
234 int width = x1 - x0;
235
236 RealBlock out(g.begin() + x0, g.begin() + x1);
237
238 for (int y = 0; y < height; ++y) {
239
240 int spacing = width;
241 for (int i = 1; i < width; ++i) {
242 int thisHeight = g[x0 + i].size();
243 if (thisHeight > height) {
244 throw std::logic_error("First column not full-height");
245 }
246 if (thisHeight > y) {
247 spacing = i;
248 break;
249 }
250 }
251
252 if (spacing < 2) continue;
253
254 for (int i = 0; i + spacing <= width; i += spacing) {
255 for (int j = 1; j < spacing; ++j) {
256 double proportion = double(j)/double(spacing);
257 double interpolated =
258 g[x0 + i][y] * (1.0 - proportion) +
259 g[x0 + i + spacing][y] * proportion;
260 out[i + j].push_back(interpolated);
261 }
262 }
263 }
264
265 return out;
266 }
267
268
269