Mercurial > hg > svapp
comparison audioio/IntegerTimeStretcher.cpp @ 27:2eb25a26390f
* Remove dsp directory. This is now the qm-dsp library used by
qm-vamp-plugins instead of being used in Sonic Visualiser directly.
* Remove plugins that have now become part of qm-vamp-plugins.
* Move time stretcher from dsp to audioio (this is the one DSP thing
we do need in SV)
author | Chris Cannam |
---|---|
date | Thu, 06 Apr 2006 12:12:41 +0000 |
parents | |
children | 4ed2448582cc |
comparison
equal
deleted
inserted
replaced
26:cc48a7189152 | 27:2eb25a26390f |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Sonic Visualiser | |
5 An audio file viewer and annotation editor. | |
6 Centre for Digital Music, Queen Mary, University of London. | |
7 This file copyright 2006 Chris Cannam. | |
8 | |
9 This program is free software; you can redistribute it and/or | |
10 modify it under the terms of the GNU General Public License as | |
11 published by the Free Software Foundation; either version 2 of the | |
12 License, or (at your option) any later version. See the file | |
13 COPYING included with this distribution for more information. | |
14 */ | |
15 | |
16 #include "IntegerTimeStretcher.h" | |
17 | |
18 #include <iostream> | |
19 #include <cassert> | |
20 | |
21 //#define DEBUG_INTEGER_TIME_STRETCHER 1 | |
22 | |
23 IntegerTimeStretcher::IntegerTimeStretcher(size_t ratio, | |
24 size_t maxProcessInputBlockSize, | |
25 size_t inputIncrement, | |
26 size_t windowSize, | |
27 WindowType windowType) : | |
28 m_ratio(ratio), | |
29 m_n1(inputIncrement), | |
30 m_n2(m_n1 * ratio), | |
31 m_wlen(std::max(windowSize, m_n2 * 2)), | |
32 m_inbuf(m_wlen), | |
33 m_outbuf(maxProcessInputBlockSize * ratio) | |
34 { | |
35 m_window = new Window<double>(windowType, m_wlen), | |
36 | |
37 m_time = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * m_wlen); | |
38 m_freq = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * m_wlen); | |
39 m_dbuf = (double *)fftw_malloc(sizeof(double) * m_wlen); | |
40 | |
41 m_plan = fftw_plan_dft_1d(m_wlen, m_time, m_freq, FFTW_FORWARD, FFTW_ESTIMATE); | |
42 m_iplan = fftw_plan_dft_c2r_1d(m_wlen, m_freq, m_dbuf, FFTW_ESTIMATE); | |
43 | |
44 m_mashbuf = new double[m_wlen]; | |
45 for (int i = 0; i < m_wlen; ++i) { | |
46 m_mashbuf[i] = 0.0; | |
47 } | |
48 } | |
49 | |
50 IntegerTimeStretcher::~IntegerTimeStretcher() | |
51 { | |
52 std::cerr << "IntegerTimeStretcher::~IntegerTimeStretcher" << std::endl; | |
53 | |
54 fftw_destroy_plan(m_plan); | |
55 fftw_destroy_plan(m_iplan); | |
56 | |
57 fftw_free(m_time); | |
58 fftw_free(m_freq); | |
59 fftw_free(m_dbuf); | |
60 | |
61 delete m_window; | |
62 delete m_mashbuf; | |
63 } | |
64 | |
65 size_t | |
66 IntegerTimeStretcher::getProcessingLatency() const | |
67 { | |
68 return getWindowSize() - getInputIncrement(); | |
69 } | |
70 | |
71 void | |
72 IntegerTimeStretcher::process(double *input, double *output, size_t samples) | |
73 { | |
74 // We need to add samples from input to our internal buffer. When | |
75 // we have m_windowSize samples in the buffer, we can process it, | |
76 // move the samples back by m_n1 and write the output onto our | |
77 // internal output buffer. If we have (samples * ratio) samples | |
78 // in that, we can write m_n2 of them back to output and return | |
79 // (otherwise we have to write zeroes). | |
80 | |
81 // When we process, we write m_wlen to our fixed output buffer | |
82 // (m_mashbuf). We then pull out the first m_n2 samples from that | |
83 // buffer, push them into the output ring buffer, and shift | |
84 // m_mashbuf left by that amount. | |
85 | |
86 // The processing latency is then m_wlen - m_n2. | |
87 | |
88 size_t consumed = 0; | |
89 | |
90 #ifdef DEBUG_INTEGER_TIME_STRETCHER | |
91 std::cerr << "IntegerTimeStretcher::process(" << samples << ", consumed = " << consumed << "), writable " << m_inbuf.getWriteSpace() <<", readable "<< m_outbuf.getReadSpace() << std::endl; | |
92 #endif | |
93 | |
94 while (consumed < samples) { | |
95 | |
96 size_t writable = m_inbuf.getWriteSpace(); | |
97 writable = std::min(writable, samples - consumed); | |
98 | |
99 if (writable == 0) { | |
100 //!!! then what? I don't think this should happen, but | |
101 std::cerr << "WARNING: IntegerTimeStretcher::process: writable == 0" << std::endl; | |
102 break; | |
103 } | |
104 | |
105 #ifdef DEBUG_INTEGER_TIME_STRETCHER | |
106 std::cerr << "writing " << writable << " from index " << consumed << " to inbuf, consumed will be " << consumed + writable << std::endl; | |
107 #endif | |
108 m_inbuf.write(input + consumed, writable); | |
109 consumed += writable; | |
110 | |
111 while (m_inbuf.getReadSpace() >= m_wlen && | |
112 m_outbuf.getWriteSpace() >= m_n2) { | |
113 | |
114 // We know we have at least m_wlen samples available | |
115 // in m_inbuf. We need to peek m_wlen of them for | |
116 // processing, and then read m_n1 to advance the read | |
117 // pointer. | |
118 | |
119 size_t got = m_inbuf.peek(m_dbuf, m_wlen); | |
120 assert(got == m_wlen); | |
121 | |
122 processBlock(m_dbuf, m_mashbuf); | |
123 | |
124 #ifdef DEBUG_INTEGER_TIME_STRETCHER | |
125 std::cerr << "writing first " << m_n2 << " from mashbuf, skipping " << m_n1 << " on inbuf " << std::endl; | |
126 #endif | |
127 m_inbuf.skip(m_n1); | |
128 m_outbuf.write(m_mashbuf, m_n2); | |
129 | |
130 for (size_t i = 0; i < m_wlen - m_n2; ++i) { | |
131 m_mashbuf[i] = m_mashbuf[i + m_n2]; | |
132 } | |
133 for (size_t i = m_wlen - m_n2; i < m_wlen; ++i) { | |
134 m_mashbuf[i] = 0.0f; | |
135 } | |
136 } | |
137 | |
138 // std::cerr << "WARNING: IntegerTimeStretcher::process: writespace not enough for output increment (" << m_outbuf.getWriteSpace() << " < " << m_n2 << ")" << std::endl; | |
139 // } | |
140 | |
141 #ifdef DEBUG_INTEGER_TIME_STRETCHER | |
142 std::cerr << "loop ended: inbuf read space " << m_inbuf.getReadSpace() << ", outbuf write space " << m_outbuf.getWriteSpace() << std::endl; | |
143 #endif | |
144 } | |
145 | |
146 if (m_outbuf.getReadSpace() < samples * m_ratio) { | |
147 std::cerr << "WARNING: IntegerTimeStretcher::process: not enough data (yet?) (" << m_outbuf.getReadSpace() << " < " << (samples * m_ratio) << ")" << std::endl; | |
148 size_t fill = samples * m_ratio - m_outbuf.getReadSpace(); | |
149 for (size_t i = 0; i < fill; ++i) { | |
150 output[i] = 0.0; | |
151 } | |
152 m_outbuf.read(output + fill, m_outbuf.getReadSpace()); | |
153 } else { | |
154 #ifdef DEBUG_INTEGER_TIME_STRETCHER | |
155 std::cerr << "enough data - writing " << samples * m_ratio << " from outbuf" << std::endl; | |
156 #endif | |
157 m_outbuf.read(output, samples * m_ratio); | |
158 } | |
159 | |
160 #ifdef DEBUG_INTEGER_TIME_STRETCHER | |
161 std::cerr << "IntegerTimeStretcher::process returning" << std::endl; | |
162 #endif | |
163 } | |
164 | |
165 void | |
166 IntegerTimeStretcher::processBlock(double *buf, double *out) | |
167 { | |
168 size_t i; | |
169 | |
170 // buf contains m_wlen samples; out contains enough space for | |
171 // m_wlen * ratio samples (we mix into out, rather than replacing) | |
172 | |
173 #ifdef DEBUG_INTEGER_TIME_STRETCHER | |
174 std::cerr << "IntegerTimeStretcher::processBlock" << std::endl; | |
175 #endif | |
176 | |
177 m_window->cut(buf); | |
178 | |
179 for (i = 0; i < m_wlen/2; ++i) { | |
180 double temp = buf[i]; | |
181 buf[i] = buf[i + m_wlen/2]; | |
182 buf[i + m_wlen/2] = temp; | |
183 } | |
184 | |
185 for (i = 0; i < m_wlen; ++i) { | |
186 m_time[i][0] = buf[i]; | |
187 m_time[i][1] = 0.0; | |
188 } | |
189 | |
190 fftw_execute(m_plan); // m_time -> m_freq | |
191 | |
192 for (i = 0; i < m_wlen; ++i) { | |
193 | |
194 double mag = sqrt(m_freq[i][0] * m_freq[i][0] + | |
195 m_freq[i][1] * m_freq[i][1]); | |
196 | |
197 double phase = atan2(m_freq[i][1], m_freq[i][0]); | |
198 | |
199 phase = phase * m_ratio; | |
200 | |
201 double real = mag * cos(phase); | |
202 double imag = mag * sin(phase); | |
203 m_freq[i][0] = real; | |
204 m_freq[i][1] = imag; | |
205 } | |
206 | |
207 fftw_execute(m_iplan); // m_freq -> in, inverse fft | |
208 | |
209 for (i = 0; i < m_wlen/2; ++i) { | |
210 double temp = buf[i] / m_wlen; | |
211 buf[i] = buf[i + m_wlen/2] / m_wlen; | |
212 buf[i + m_wlen/2] = temp; | |
213 } | |
214 | |
215 m_window->cut(buf); | |
216 | |
217 int div = m_wlen / m_n2; | |
218 if (div > 1) div /= 2; | |
219 for (i = 0; i < m_wlen; ++i) { | |
220 buf[i] /= div; | |
221 } | |
222 | |
223 for (i = 0; i < m_wlen; ++i) { | |
224 out[i] += buf[i]; | |
225 } | |
226 } |