Mercurial > hg > constant-q-cpp
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 |