Audacity 3.2.0
SpectrumTransformer.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5SpectrumTransformer.cpp
6
7Edward Hui
8
9**********************************************************************/
10
11#include "SpectrumTransformer.h"
12
13#include <algorithm>
14#include "FFT.h"
15
17 eWindowFunctions inWindowType,
18 eWindowFunctions outWindowType,
19 size_t windowSize, unsigned stepsPerWindow,
20 bool leadingPadding, bool trailingPadding )
21: mWindowSize{ windowSize }
22, mSpectrumSize{ 1 + mWindowSize / 2 }
23, mStepsPerWindow{ stepsPerWindow }
24, mStepSize{ mWindowSize / mStepsPerWindow }
25, mLeadingPadding{ leadingPadding }
26, mTrailingPadding{ trailingPadding }
27, hFFT{ GetFFT(mWindowSize) }
28, mFFTBuffer( mWindowSize )
29, mInWaveBuffer( mWindowSize )
30, mOutOverlapBuffer( mWindowSize )
31, mNeedsOutput{ needsOutput }
32{
33 // Check preconditions
34
35 // Powers of 2 only!
36 wxASSERT(mWindowSize > 0 &&
37 0 == (mWindowSize & (mWindowSize - 1)));
38
39 wxASSERT(mWindowSize % mStepsPerWindow == 0);
40
41 wxASSERT(!(inWindowType == eWinFuncRectangular && outWindowType == eWinFuncRectangular));
42
43 // To do: check that inWindowType, outWindowType, and mStepsPerWindow
44 // are compatible for correct overlap-add reconstruction.
45
46 // Create windows as needed
47 if (inWindowType != eWinFuncRectangular) {
48 mInWindow.resize(mWindowSize);
49 std::fill(mInWindow.begin(), mInWindow.end(), 1.0f);
50 NewWindowFunc(inWindowType, mWindowSize, false, mInWindow.data());
51 }
52 if (outWindowType != eWinFuncRectangular) {
53 mOutWindow.resize(mWindowSize);
54 std::fill(mOutWindow.begin(), mOutWindow.end(), 1.0f);
55 NewWindowFunc(outWindowType, mWindowSize, false, mOutWindow.data());
56 }
57
58 // Must scale one or the other window so overlap-add
59 // comes out right
60 double denom = 0;
61 for (size_t ii = 0; ii < mWindowSize; ii += mStepSize) {
62 denom +=
63 (mInWindow.empty() ? 1.0 : mInWindow[ii])
64 *
65 (mOutWindow.empty() ? 1.0 : mOutWindow[ii]);
66 }
67 // It is ASSUMED that you have chosen window types and
68 // steps per window, so that this sum denom would be the
69 // same, starting the march anywhere from 0 to mStepSize - 1.
70 // Else, your overlap-add won't be right, and the transformer
71 // might not be an identity even when you do nothing to the
72 // spectra.
73
74 float *pWindow = 0;
75 if (!mInWindow.empty())
76 pWindow = mInWindow.data();
77 else if (!mOutWindow.empty())
78 pWindow = mOutWindow.data();
79 else
80 // Can only happen if both window types were rectangular
81 wxASSERT(false);
82 for (size_t ii = 0; ii < mWindowSize; ++ii)
83 *pWindow++ /= denom;
84}
85
86auto SpectrumTransformer::NewWindow(size_t windowSize)
87 -> std::unique_ptr<Window>
88{
89 return std::make_unique<Window>(windowSize);
90}
91
93{
94 return true;
95}
96
98{
99 return true;
100}
101
102bool SpectrumTransformer::Start(size_t queueLength)
103{
104 // Prepare clean queue
105 ResizeQueue(queueLength);
106 for (auto &pWindow : mQueue)
107 pWindow->Zero();
108
109 // invoke derived method
110 if (!DoStart())
111 return false;
112
113 // Clean input and output buffers
114 {
115 float *pFill;
116 pFill = mInWaveBuffer.data();
117 std::fill(pFill, pFill + mWindowSize, 0.0f);
118 pFill = mOutOverlapBuffer.data();
119 std::fill(pFill, pFill + mWindowSize, 0.0f);
120 }
121
122 if (!mLeadingPadding)
123 {
124 // We do not want leading zero padded windows
125 mInWavePos = 0;
126 mOutStepCount = -static_cast<int>(queueLength - 1);
127 }
128 else
129 {
130 // So that the queue gets primed with some windows,
131 // zero-padded in front, the first having mStepSize
132 // samples of wave data:
134 // This starts negative, to count up until the queue fills:
135 mOutStepCount = -static_cast<int>(queueLength - 1)
136 // ... and then must pass over the padded windows,
137 // before the first full window:
138 - static_cast<int>(mStepsPerWindow - 1);
139 }
140
141 mInSampleCount = 0;
142
143 return true;
144}
145
147 const float *buffer, size_t len )
148{
149 if (buffer)
150 mInSampleCount += len;
151 bool success = true;
152 while (success && len &&
153 mOutStepCount * static_cast<int>(mStepSize) < mInSampleCount) {
154 auto avail = std::min(len, mWindowSize - mInWavePos);
155 if (buffer)
156 memmove(&mInWaveBuffer[mInWavePos], buffer, avail * sizeof(float));
157 else
158 memset(&mInWaveBuffer[mInWavePos], 0, avail * sizeof(float));
159 if (buffer)
160 buffer += avail;
161 len -= avail;
162 mInWavePos += avail;
163
164 if (mInWavePos == mWindowSize) {
166
167 // invoke derived method
168 if ( (success = processor(*this)), success )
169 OutputStep();
170
173
174 // Shift input.
175 memmove(mInWaveBuffer.data(), &mInWaveBuffer[mStepSize],
176 (mWindowSize - mStepSize) * sizeof(float));
178 }
179 }
180
181 return success;
182}
183
184void SpectrumTransformer::ResizeQueue(size_t queueLength)
185{
186 int oldLen = mQueue.size();
187 mQueue.resize(queueLength);
188 for (size_t ii = oldLen; ii < queueLength; ++ii)
189 // invoke derived method to get a queue element
190 // with appropriate extra fields
192}
193
195{
196 // Transform samples to frequency domain, windowed as needed
197 {
198 auto pFFTBuffer = mFFTBuffer.data(), pInWaveBuffer = mInWaveBuffer.data();
199 if (mInWindow.size() > 0) {
200 auto pInWindow = mInWindow.data();
201 for (size_t ii = 0; ii < mWindowSize; ++ii)
202 *pFFTBuffer++ = *pInWaveBuffer++ * *pInWindow++;
203 }
204 else
205 memmove(pFFTBuffer, pInWaveBuffer, mWindowSize * sizeof(float));
206 }
207 RealFFTf(mFFTBuffer.data(), hFFT.get());
208
209 auto &record = Nth(0);
210
211 // Store real and imaginary parts for later inverse FFT
212 {
213 float *pReal = &record.mRealFFTs[1];
214 float *pImag = &record.mImagFFTs[1];
215 int *pBitReversed = &hFFT->BitReversed[1];
216 const auto last = mSpectrumSize - 1;
217 for (size_t ii = 1; ii < last; ++ii) {
218 const int kk = *pBitReversed++;
219 *pReal++ = mFFTBuffer[kk];
220 *pImag++ = mFFTBuffer[kk + 1];
221 }
222 // DC and Fs/2 bins need to be handled specially
223 const float dc = mFFTBuffer[0];
224 record.mRealFFTs[0] = dc;
225
226 const float nyquist = mFFTBuffer[1];
227 record.mImagFFTs[0] = nyquist; // For Fs/2, not really imaginary
228 }
229}
230
232{
233 std::rotate(mQueue.begin(), mQueue.end() - 1, mQueue.end());
234}
235
237{
238 bool bLoopSuccess = true;
239 if (mTrailingPadding) {
240 // Keep flushing empty input buffers through the history
241 // windows until we've output exactly as many samples as
242 // were input.
243 // Well, not exactly, but not more than one step-size of extra samples
244 // at the end.
245
246 while (bLoopSuccess &&
247 mOutStepCount * static_cast<int>(mStepSize) < mInSampleCount)
248 bLoopSuccess = ProcessSamples(processor, nullptr, mStepSize);
249 }
250
251 if (bLoopSuccess)
252 // invoke derived method
253 bLoopSuccess = DoFinish();
254
255 return bLoopSuccess;
256}
257
259{
260 auto allocSize = mQueue.size();
261 auto size = mOutStepCount + allocSize;
262 if (mLeadingPadding)
263 size += mStepsPerWindow - 1;
264
265 if (size < allocSize)
266 return size.as_size_t();
267 else
268 return allocSize;
269}
270
271// Formerly part of EffectNoiseReduction::Worker::ReduceNoise()
273{
274 if (!mNeedsOutput)
275 return;
276 if (QueueIsFull()) {
277 const auto last = mSpectrumSize - 1;
278 Window &record = **mQueue.rbegin();
279
280 const float *pReal = &record.mRealFFTs[1];
281 const float *pImag = &record.mImagFFTs[1];
282 float *pBuffer = &mFFTBuffer[2];
283 auto nn = mSpectrumSize - 2;
284 for (; nn--;) {
285 *pBuffer++ = *pReal++;
286 *pBuffer++ = *pImag++;
287 }
288 mFFTBuffer[0] = record.mRealFFTs[0];
289 // The Fs/2 component is stored as the imaginary part of the DC component
290 mFFTBuffer[1] = record.mImagFFTs[0];
291
292 // Invert the FFT into the output buffer
293 InverseRealFFTf(mFFTBuffer.data(), hFFT.get());
294
295 // Overlap-add
296 if (mOutWindow.size() > 0) {
297 auto pOut = mOutOverlapBuffer.data();
298 auto pWindow = mOutWindow.data();
299 auto pBitReversed = &hFFT->BitReversed[0];
300 for (size_t jj = 0; jj < last; ++jj) {
301 auto kk = *pBitReversed++;
302 *pOut++ += mFFTBuffer[kk] * (*pWindow++);
303 *pOut++ += mFFTBuffer[kk + 1] * (*pWindow++);
304 }
305 }
306 else {
307 auto pOut = mOutOverlapBuffer.data();
308 auto pBitReversed = &hFFT->BitReversed[0];
309 for (size_t jj = 0; jj < last; ++jj) {
310 auto kk = *pBitReversed++;
311 *pOut++ += mFFTBuffer[kk];
312 *pOut++ += mFFTBuffer[kk + 1];
313 }
314 }
315 auto buffer = mOutOverlapBuffer.data();
316 if (mOutStepCount >= 0) {
317 // Output the first portion of the overlap buffer, they're done
318 DoOutput(buffer, mStepSize);
319 }
320 // Shift the remainder over.
321 memmove(buffer, buffer + mStepSize, sizeof(float)*(mWindowSize - mStepSize));
322 std::fill(buffer + mWindowSize - mStepSize, buffer + mWindowSize, 0.0f);
323 }
324}
325
327{
328 if (mLeadingPadding)
329 return (mOutStepCount >= -static_cast<int>(mStepsPerWindow - 1));
330 else
331 return (mOutStepCount >= 0);
332}
333
335
int min(int a, int b)
void NewWindowFunc(int whichFunction, size_t NumSamplesIn, bool extraSample, float *in)
Definition: FFT.cpp:368
eWindowFunctions
Definition: FFT.h:110
@ eWinFuncRectangular
Definition: FFT.h:111
void RealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:161
void InverseRealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:263
HFFT GetFFT(size_t fftlen)
Definition: RealFFTf.cpp:104
sampleCount mOutStepCount
sometimes negative
FloatVector mFFTBuffer
These have size mWindowSize:
std::function< bool(SpectrumTransformer &) > WindowProcessor
Type of function that transforms windows in the queue.
bool Finish(const WindowProcessor &processor)
void ResizeQueue(size_t queueLength)
virtual bool DoStart()
Called before any calls to ProcessWindow.
virtual void DoOutput(const float *outBuffer, size_t mStepSize)=0
Called within ProcessSamples if output was requested.
const unsigned mStepsPerWindow
bool ProcessSamples(const WindowProcessor &processor, const float *buffer, size_t len)
Call multiple times.
virtual std::unique_ptr< Window > NewWindow(size_t windowSize)
Allocates a window to place in the queue.
Window & Nth(int n)
Access the queue, so you can inspect and modify any window in it.
std::vector< std::unique_ptr< Window > > mQueue
virtual ~SpectrumTransformer()
virtual bool DoFinish()
Called after the last call to ProcessWindow().
SpectrumTransformer(bool needsOutput, eWindowFunctions inWindowType, eWindowFunctions outWindowType, size_t windowSize, unsigned stepsPerWindow, bool leadingPadding, bool trailingPadding)
FloatVector mInWindow
These have size mWindowSize, or 0 for rectangular window:
size_t CurrentQueueSize() const
How many windows in the queue have been filled?
bool Start(size_t queueLength)
Call once before a sequence of calls to ProcessSamples; Invokes DoStart.
void rotate(const float *oldPhase, const float *newPhase, std::complex< float > *dst, int32_t n)
Definition: VectorOps.h:140
Derive this class to add information to the queue.
FloatVector mRealFFTs
index zero holds the dc coefficient, which has no imaginary part
FloatVector mImagFFTs
index zero holds the nyquist frequency coefficient, actually real