c@92
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@92
|
2
|
c@92
|
3 /*
|
c@92
|
4 QM Vamp Plugin Set
|
c@92
|
5
|
c@92
|
6 Centre for Digital Music, Queen Mary, University of London.
|
c@135
|
7
|
c@135
|
8 This program is free software; you can redistribute it and/or
|
c@135
|
9 modify it under the terms of the GNU General Public License as
|
c@135
|
10 published by the Free Software Foundation; either version 2 of the
|
c@135
|
11 License, or (at your option) any later version. See the file
|
c@135
|
12 COPYING included with this distribution for more information.
|
c@92
|
13 */
|
c@92
|
14
|
c@92
|
15 #ifndef _ADAPTIVE_SPECTROGRAM_H_
|
c@92
|
16 #define _ADAPTIVE_SPECTROGRAM_H_
|
c@92
|
17
|
c@92
|
18 #include <vamp-sdk/Plugin.h>
|
c@92
|
19 #include <cmath>
|
c@92
|
20 #include <vector>
|
c@92
|
21
|
c@108
|
22 #include <dsp/transforms/FFT.h>
|
c@107
|
23 #include <base/Window.h>
|
c@105
|
24
|
c@156
|
25 #include <thread/Thread.h>
|
c@156
|
26 #include <thread/AsynchronousTask.h>
|
c@156
|
27 #include <thread/BlockAllocator.h>
|
c@156
|
28
|
c@156
|
29 class Decimator;
|
c@104
|
30
|
c@92
|
31 class AdaptiveSpectrogram : public Vamp::Plugin
|
c@92
|
32 {
|
c@92
|
33 public:
|
c@92
|
34 AdaptiveSpectrogram(float inputSampleRate);
|
c@92
|
35 virtual ~AdaptiveSpectrogram();
|
c@92
|
36
|
c@92
|
37 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
|
c@92
|
38 void reset();
|
c@92
|
39
|
c@92
|
40 InputDomain getInputDomain() const { return TimeDomain; }
|
c@92
|
41
|
c@92
|
42 std::string getIdentifier() const;
|
c@92
|
43 std::string getName() const;
|
c@92
|
44 std::string getDescription() const;
|
c@92
|
45 std::string getMaker() const;
|
c@92
|
46 int getPluginVersion() const;
|
c@92
|
47 std::string getCopyright() const;
|
c@92
|
48
|
c@92
|
49 size_t getPreferredStepSize() const;
|
c@92
|
50 size_t getPreferredBlockSize() const;
|
c@92
|
51
|
c@92
|
52 ParameterList getParameterDescriptors() const;
|
c@92
|
53 float getParameter(std::string id) const;
|
c@92
|
54 void setParameter(std::string id, float value);
|
c@92
|
55
|
c@92
|
56 OutputList getOutputDescriptors() const;
|
c@92
|
57
|
c@92
|
58 FeatureSet process(const float *const *inputBuffers,
|
c@92
|
59 Vamp::RealTime timestamp);
|
c@92
|
60
|
c@92
|
61 FeatureSet getRemainingFeatures();
|
c@92
|
62
|
c@92
|
63 protected:
|
c@92
|
64 int m_w;
|
c@92
|
65 int m_n;
|
c@114
|
66 bool m_coarse;
|
c@109
|
67 bool m_threaded;
|
c@156
|
68 int m_decFactor;
|
c@156
|
69 float *m_buffer;
|
c@156
|
70 int m_buflen;
|
c@156
|
71 Decimator *m_decimator;
|
c@92
|
72
|
c@100
|
73 struct Spectrogram
|
c@100
|
74 {
|
c@100
|
75 int resolution;
|
c@100
|
76 int width;
|
c@100
|
77 double **data;
|
c@100
|
78
|
c@100
|
79 Spectrogram(int r, int w) :
|
c@100
|
80 resolution(r), width(w) {
|
c@100
|
81 data = new double *[width];
|
c@100
|
82 for (int i = 0; i < width; ++i) data[i] = new double[resolution];
|
c@100
|
83 }
|
c@100
|
84
|
c@100
|
85 ~Spectrogram() {
|
c@100
|
86 for (int i = 0; i < width; ++i) delete[] data[i];
|
c@100
|
87 delete[] data;
|
c@100
|
88 }
|
c@100
|
89 };
|
c@100
|
90
|
c@100
|
91 struct Spectrograms
|
c@100
|
92 {
|
c@100
|
93 int minres;
|
c@100
|
94 int maxres;
|
c@100
|
95 int n;
|
c@100
|
96 Spectrogram **spectrograms;
|
c@100
|
97
|
c@100
|
98 Spectrograms(int mn, int mx, int widthofmax) :
|
c@100
|
99 minres(mn), maxres(mx) {
|
c@100
|
100 n = log2(maxres/minres) + 1;
|
c@100
|
101 spectrograms = new Spectrogram *[n];
|
c@100
|
102 int r = mn;
|
c@100
|
103 for (int i = 0; i < n; ++i) {
|
c@100
|
104 spectrograms[i] = new Spectrogram(r, widthofmax * (mx / r));
|
c@100
|
105 r = r * 2;
|
c@100
|
106 }
|
c@100
|
107 }
|
c@100
|
108 ~Spectrograms() {
|
c@100
|
109 for (int i = 0; i < n; ++i) {
|
c@100
|
110 delete spectrograms[i];
|
c@100
|
111 }
|
c@100
|
112 delete[] spectrograms;
|
c@100
|
113 }
|
c@100
|
114 };
|
c@100
|
115
|
c@100
|
116 struct Cutting
|
c@100
|
117 {
|
c@100
|
118 enum Cut { Horizontal, Vertical, Finished };
|
c@100
|
119 Cut cut;
|
c@100
|
120 Cutting *first;
|
c@100
|
121 Cutting *second;
|
c@100
|
122 double cost;
|
c@100
|
123 double value;
|
c@110
|
124 BlockAllocator *allocator;
|
c@100
|
125
|
c@100
|
126 ~Cutting() {
|
c@110
|
127 if (first) first->erase();
|
c@110
|
128 if (second) second->erase();
|
c@110
|
129 }
|
c@110
|
130
|
c@110
|
131 void erase() {
|
c@110
|
132 if (allocator) {
|
c@110
|
133 if (first) first->erase();
|
c@110
|
134 if (second) second->erase();
|
c@110
|
135 allocator->deallocate(this);
|
c@110
|
136 } else {
|
c@110
|
137 delete this;
|
c@110
|
138 }
|
c@100
|
139 }
|
c@100
|
140 };
|
c@100
|
141
|
c@105
|
142 class FFTThread : public AsynchronousTask
|
c@104
|
143 {
|
c@104
|
144 public:
|
c@107
|
145 FFTThread(int w) :
|
c@107
|
146 m_window(HanningWindow, w) {
|
c@106
|
147 m_w = w;
|
c@106
|
148 m_fft = new FFTReal(m_w);
|
c@106
|
149 m_rin = new double[m_w];
|
c@106
|
150 m_rout = new double[m_w];
|
c@106
|
151 m_iout = new double[m_w];
|
c@106
|
152 }
|
c@106
|
153 ~FFTThread() {
|
c@106
|
154 delete[] m_rin;
|
c@106
|
155 delete[] m_rout;
|
c@106
|
156 delete[] m_iout;
|
c@106
|
157 delete m_fft;
|
c@106
|
158 }
|
c@106
|
159
|
c@106
|
160 int getW() const { return m_w; }
|
c@105
|
161
|
c@109
|
162 void startCalculation(const float *timeDomain, Spectrograms &s,
|
c@109
|
163 int res, int maxwidth) {
|
c@109
|
164 setParameters(timeDomain, s, res, maxwidth);
|
c@105
|
165 startTask();
|
c@105
|
166 }
|
c@105
|
167
|
c@105
|
168 void await() {
|
c@105
|
169 awaitTask();
|
c@105
|
170 }
|
c@105
|
171
|
c@109
|
172 void setParameters(const float *timeDomain, Spectrograms &s,
|
c@109
|
173 int res, int maxwidth) {
|
c@109
|
174 m_in = timeDomain;
|
c@109
|
175 m_s = &s;
|
c@109
|
176 m_res = res;
|
c@109
|
177 m_maxwid = maxwidth;
|
c@109
|
178 }
|
c@109
|
179
|
c@105
|
180 void performTask() {
|
c@105
|
181 for (int i = 0; i < m_maxwid / m_w; ++i) {
|
c@105
|
182 int origin = m_maxwid/4 - m_w/4; // for 50% overlap
|
c@105
|
183 for (int j = 0; j < m_w; ++j) {
|
c@109
|
184 m_rin[j] = m_in[origin + i * m_w/2 + j];
|
c@105
|
185 }
|
c@107
|
186 m_window.cut(m_rin);
|
c@153
|
187 m_fft->forward(m_rin, m_rout, m_iout);
|
c@105
|
188 for (int j = 0; j < m_w/2; ++j) {
|
c@105
|
189 int k = j+1; // include Nyquist but not DC
|
c@106
|
190 double mag = sqrt(m_rout[k] * m_rout[k] +
|
c@106
|
191 m_iout[k] * m_iout[k]);
|
c@105
|
192 double scaled = mag / (m_w/2);
|
c@105
|
193 m_s->spectrograms[m_res]->data[i][j] = scaled;
|
c@105
|
194 }
|
c@105
|
195 }
|
c@105
|
196 }
|
c@105
|
197
|
c@105
|
198 private:
|
c@107
|
199 Window<double> m_window;
|
c@106
|
200 FFTReal *m_fft;
|
c@105
|
201 const float *m_in;
|
c@106
|
202 double *m_rin;
|
c@106
|
203 double *m_rout;
|
c@106
|
204 double *m_iout;
|
c@105
|
205 Spectrograms *m_s;
|
c@105
|
206 int m_res;
|
c@105
|
207 int m_w;
|
c@105
|
208 int m_maxwid;
|
c@105
|
209 };
|
c@105
|
210
|
c@106
|
211 typedef std::map<int, FFTThread *> FFTMap;
|
c@106
|
212 FFTMap m_fftThreads;
|
c@105
|
213
|
c@105
|
214 class CutThread : public AsynchronousTask
|
c@105
|
215 {
|
c@105
|
216 public:
|
c@110
|
217 CutThread(const AdaptiveSpectrogram *as) : m_as(as), m_result(0) {
|
c@110
|
218 m_allocator = new BlockAllocator(sizeof(Cutting));
|
c@110
|
219 }
|
c@110
|
220 ~CutThread() {
|
c@110
|
221 delete m_allocator;
|
c@110
|
222 }
|
c@105
|
223
|
c@104
|
224 void cut(const Spectrograms &s, int res, int x, int y, int h) {
|
c@104
|
225 m_s = &s;
|
c@104
|
226 m_res = res;
|
c@104
|
227 m_x = x;
|
c@104
|
228 m_y = y;
|
c@104
|
229 m_h = h;
|
c@105
|
230 startTask();
|
c@104
|
231 }
|
c@104
|
232
|
c@104
|
233 Cutting *get() {
|
c@105
|
234 awaitTask();
|
c@105
|
235 return m_result;
|
c@104
|
236 }
|
c@104
|
237
|
c@104
|
238 protected:
|
c@105
|
239 void performTask() {
|
c@110
|
240 m_result = m_as->cut(*m_s, m_res, m_x, m_y, m_h, m_allocator);
|
c@104
|
241 }
|
c@104
|
242
|
c@105
|
243 private:
|
c@104
|
244 const AdaptiveSpectrogram *m_as;
|
c@110
|
245 BlockAllocator *m_allocator;
|
c@104
|
246 const Spectrograms *m_s;
|
c@104
|
247 int m_res;
|
c@104
|
248 int m_x;
|
c@104
|
249 int m_y;
|
c@104
|
250 int m_h;
|
c@104
|
251 Cutting *m_result;
|
c@104
|
252 };
|
c@105
|
253
|
c@109
|
254 mutable std::vector<CutThread *> m_cutThreads;
|
c@109
|
255 mutable bool m_threadsInUse;
|
c@104
|
256
|
c@110
|
257 inline double xlogx(double x) const {
|
c@104
|
258 if (x == 0.0) return 0.0;
|
c@104
|
259 else return x * log(x);
|
c@104
|
260 }
|
c@104
|
261
|
c@110
|
262 inline double cost(const Spectrogram &s, int x, int y) const {
|
c@100
|
263 return xlogx(s.data[x][y]);
|
c@100
|
264 }
|
c@100
|
265
|
c@110
|
266 inline double value(const Spectrogram &s, int x, int y) const {
|
c@100
|
267 return s.data[x][y];
|
c@100
|
268 }
|
c@100
|
269
|
c@114
|
270 inline double normalize(double vcost, double venergy) const {
|
c@114
|
271 return (vcost + (venergy * log(venergy))) / venergy;
|
c@114
|
272 }
|
c@114
|
273
|
c@114
|
274 inline bool isResolutionWanted(const Spectrograms &s, int res) const {
|
c@114
|
275 if (!m_coarse) return true;
|
c@114
|
276 if (res == s.minres || res == s.maxres) return true;
|
c@114
|
277 int n = 0;
|
c@114
|
278 for (int r = res; r > s.minres; r >>= 1) ++n;
|
c@114
|
279 return ((n & 0x1) == 0);
|
c@114
|
280 }
|
c@114
|
281
|
c@110
|
282 Cutting *cut(const Spectrograms &, int res, int x, int y, int h,
|
c@110
|
283 BlockAllocator *allocator) const;
|
c@100
|
284
|
c@104
|
285 void getSubCuts(const Spectrograms &, int res, int x, int y, int h,
|
c@114
|
286 Cutting **top, Cutting **bottom,
|
c@114
|
287 Cutting **left, Cutting **right,
|
c@113
|
288 BlockAllocator *allocator) const;
|
c@100
|
289
|
c@104
|
290 void printCutting(Cutting *, std::string) const;
|
c@104
|
291
|
c@104
|
292 void assemble(const Spectrograms &, const Cutting *,
|
c@104
|
293 std::vector<std::vector<float> > &,
|
c@104
|
294 int x, int y, int w, int h) const;
|
c@156
|
295 };
|
c@92
|
296
|
c@92
|
297
|
c@92
|
298 #endif
|