Krotos Modules 3
Loading...
Searching...
No Matches
PhaseVocoder.cpp
Go to the documentation of this file.
1namespace krotos
2{
3 PhaseVocoder::PhaseVocoder(int fftSize, int hopSize, int inputBufferSize, WindowType windowFunction)
4 : m_inputBufferSize(inputBufferSize), m_outputBufferSize(inputBufferSize), m_hopSizeAnalysis(hopSize),
5 m_fft(static_cast<int>(log2f(static_cast<float>(fftSize))))
6 {
7 m_fftSize = fftSize;
8 // set synthesis hopSize from timeStrechFactor
10
11 // set num channels to stereo.
13
14 // intialise the windowing functions
17
18 // generate windows
21 }
22
29
30 void PhaseVocoder::setNumChannels(int numChannels)
31 {
32 // Set the size for the two circular buffers
33 m_inputBuffer.resize(numChannels, std::vector<float>(m_inputBufferSize));
34 m_outputBuffer.resize(numChannels, std::vector<float>(m_outputBufferSize));
35
36 for (int i = 0; i < numChannels; i++)
37 {
38 // Start the write pointer ahead of the read pointer by at least window +
39 // hop, with some margin
41 }
42
43 // For Pitch and Time Streching
44 m_previousPhasesInput.resize(numChannels, std::vector<float>(m_fftSize));
45 m_previousPhasesOutput.resize(numChannels, std::vector<float>(m_fftSize));
46 m_magnitudesAnalysis.resize(numChannels, std::vector<float>(m_fftSize / 2 + 1));
47 m_magnitudesSynthesis.resize(numChannels, std::vector<float>(m_fftSize / 2 + 1));
48 m_binFrequenciesAnalysis.resize(numChannels, std::vector<float>(m_fftSize / 2 + 1));
49 m_binFrequenciesSynthesis.resize(numChannels, std::vector<float>(m_fftSize / 2 + 1));
50 }
51
52 void PhaseVocoder::setGeneralParam(float paramValue)
53 {
54 // Robotizer: param is the hopSize
56 {
57 // map from 0..1 to 64...fftSize
59 static_cast<int>(jmap<float>(paramValue, 0.f, 1.f, 64.f, static_cast<float>(m_fftSize)));
61
62 int newWinLength = m_hopSizeAnalysis * m_overlapFactor;
63 // clip to fftSize
64 newWinLength = (newWinLength > m_fftSize) ? m_fftSize : newWinLength;
65
66 // recaclulate window size to maintain COLA
69 }
70 // Pitch Shifter: param is the pitch ratio
72 {
73 // set hopSize to an appropriate value, TODO: Investigate more
76
77 // map from 0...1 to -2 ... 2
78 m_pitchShiftRatio = (jmap<float>(paramValue, 0.f, 1.f, -12.0f, 12.0f));
79
80 // convert to ratio
81 m_pitchShiftRatio = pow(2.0f, m_pitchShiftRatio / 12.0f);
82 }
84 {
85 // map from 0...1 to -2 ... 2
86 m_timeStretchRatio = jmap<float>(paramValue, 0.f, 1.f, 0.5f, 2.0f);
87 // change synthesis hopSize according to ratio
88 m_hopSizeSynthesis = static_cast<int>(float(m_hopSizeAnalysis) * m_timeStretchRatio);
89 }
90 }
91
92 float PhaseVocoder::process(float sampleIn, int numChannel)
93 {
94 m_numChannel = numChannel;
95
96 // store in inputBuffer and increment pointer, wrap around if necessary
99 {
101 }
102
103 // read output sample from outputBuffer and clear it
106
107 // scale output by overlap factor - TODO:: Investigate depening on window and
108 // ovp sampleOut *= m_windowGainCorrection;
109
110 // increment read pointer in output circular buffer and wrap around if
111 // necessary
114 {
116 }
117
118 // increment the hop counter and start new FFT if we have reached the hopSize
120 {
124
125 // Update the output buffer write pointer
128 }
129
130 return sampleOut;
131 }
132
133 void PhaseVocoder::processFrame(std::vector<float>& inBuffer, int inPointer, std::vector<float>& outBuffer,
134 int outPointer)
135 {
136 // linear buffer to hold unwrapped values for fft output
137 std::vector<float> unwrappedBuffer(2 * m_fftSize);
138
139 // copy input circular buffer into the linear buffer
140 for (int i = 0; i < m_fftSize; i++)
141 {
142 // unwrap using modulo operation
143 int circBufferIndex = (inPointer + i - m_fftSize + m_inputBufferSize) % m_inputBufferSize;
144 // copy and apply window
145 unwrappedBuffer[i] = inBuffer[circBufferIndex];
146 }
147
148 // caclulate input frame energy
149 float inEnergy = caclulateFrameRMS(unwrappedBuffer);
150
151 // apply analysis window
152 for (int i = 0; i < m_fftSize; i++)
153 {
154 // apply window
155 unwrappedBuffer[i] *= m_analysisWindow[i];
156 }
157
158 // perform fft
159 m_fft.performRealOnlyForwardTransform(unwrappedBuffer.data());
160
161 // do processing
162 switch (m_mode)
163 {
165 robotize(unwrappedBuffer);
166 break;
168 pitchShift(unwrappedBuffer);
169 break;
171 timeStrech(unwrappedBuffer);
172 break;
173 }
174
175 // perform ifft
176 m_fft.performRealOnlyInverseTransform(unwrappedBuffer.data());
177
178 // apply synthesis window
179 for (int i = 0; i < m_fftSize; i++)
180 {
181 // apply synthesis window
182 unwrappedBuffer[i] *= m_synthesisWindow[i];
183 }
184
185 // caclulate output frame energy
186 float outEnergy = caclulateFrameRMS(unwrappedBuffer);
187 // calculate gain
188 float gain = inEnergy / (outEnergy + 1e-12f);
189
190 // add the re trasnformed time domain signal to the output buffer
191 for (int i = 0; i < m_fftSize; i++)
192 {
193 // start from the write pointer
194 int circBufferIndex = (outPointer + i) % m_outputBufferSize;
195 // Overlap add and apply gain correction
196 outBuffer[circBufferIndex] += unwrappedBuffer[i] * gain;
197 }
198 }
199
200 void PhaseVocoder::detectFrequency(std::vector<float>& fftData)
201 {
202 // collect separately real and imaginary parts of the FFT
203 std::vector<float> realPart(m_fftSize / 2 + 1);
204 std::vector<float> imagPart(m_fftSize / 2 + 1);
205 int k = 0;
206 for (int i = 0; i <= m_fftSize / 2; i++)
207 {
208 realPart[i] = fftData[k];
209 imagPart[i] = fftData[k + 1];
210 k += 2;
211 }
212
213 // for each bin
214 for (int i = 0; i < m_fftSize / 2; i++)
215 {
216 // calculate magnitude and phase from Re and Imag
217 float mag = std::sqrtf(powf(realPart[i], 2.0f) + powf(imagPart[i], 2.0f));
218 float phase = std::atan2f(imagPart[i], realPart[i]);
219
220 // calculate phase difference between last hop and this one,
221 // gives us the direct frequency
222 float phaseDiff = phase - m_previousPhasesInput[m_numChannel][i];
223
224 // calculate bin frequency
225 float binFreq = 2.0f * float(M_PI) * float(i) / float(m_fftSize);
226
227 // TODO : WRAP TO PI!!!!!!
228 phaseDiff = wrapToPi(phaseDiff - binFreq * m_hopSizeAnalysis);
229
230 // calculate phase deviation in number of bins from the center freq
231 float binDeviation = phaseDiff * (float)m_fftSize / (float(m_hopSizeAnalysis) * 2.0f * float(M_PI));
232
233 // add original bin number to get the bin this partial belongs to
234 m_binFrequenciesAnalysis[m_numChannel][i] = (float)i + binDeviation;
235
236 // save magnitude for later
238
239 // update phase for next hop
241
242 // Find the bin with max magnitude to display in GUI
243 if (mag > m_maxBinValue)
244 {
245 m_maxBinValue = mag;
246 m_maxBinIndex = i;
247 }
248 }
249
251
252 // reconstruct fftData
253 k = 0;
254 for (int i = 0; i <= m_fftSize / 2; i++)
255 {
256 fftData[k] = realPart[i];
257 fftData[k + 1] = imagPart[i];
258 k += 2;
259 }
260
261 // DBG(m_frequencyDetected);
262 }
263
264 void PhaseVocoder::robotize(std::vector<float>& fftData)
265 {
266 // collect separately real and imaginary parts of the FFT
267 std::vector<float> realPart(m_fftSize / 2 + 1);
268 std::vector<float> imagPart(m_fftSize / 2 + 1);
269 int k = 0;
270 for (int i = 0; i <= m_fftSize / 2; i++)
271 {
272 realPart[i] = fftData[k];
273 imagPart[i] = fftData[k + 1];
274 k += 2;
275 }
276
277 // for each bin
278 for (int i = 0; i < m_fftSize / 2; i++)
279 {
280 // calculate magnitude and phase from Re and Imag (Rect to Polar)
281 float mag = std::sqrtf(powf(realPart[i], 2.0f) + powf(imagPart[i], 2.0f));
282 // set phase of all bins to 0
283 float phase = 0.0f;
284
285 // go back to real and imag (Polar to Rect)
286 realPart[i] = mag * std::cosf(phase);
287 imagPart[i] = mag * std::sinf(phase);
288 }
289
290 // reconstruct fftData
291 k = 0;
292 for (int i = 0; i <= m_fftSize / 2; i++)
293 {
294 fftData[k] = realPart[i];
295 fftData[k + 1] = imagPart[i];
296 k += 2;
297 }
298 }
299
300 void PhaseVocoder::pitchShift(std::vector<float>& fftData)
301 {
302 // collect separately real and imaginary parts of the FFT
303 std::vector<float> realPart(m_fftSize / 2 + 1);
304 std::vector<float> imagPart(m_fftSize / 2 + 1);
305 int k = 0;
306 for (int i = 0; i <= m_fftSize / 2; i++)
307 {
308 realPart[i] = fftData[k];
309 imagPart[i] = fftData[k + 1];
310 k += 2;
311 }
312
313 // for each bin
314 for (int i = 0; i < m_fftSize / 2; i++)
315 {
316 // calculate magnitude and phase from Re and Imag
317 float mag = std::sqrt(pow(realPart[i], 2.0f) + pow(imagPart[i], 2.0f));
318 float phase = std::atan2(imagPart[i], realPart[i]);
319
320 // calculate phase difference between last hop and this one,
321 // gives us the direct frequency
322 float phaseDiff = phase - m_previousPhasesInput[m_numChannel][i];
323
324 // calculate bin frequency
325 float binFreq = 2.0f * float(M_PI) * float(i) / float(m_fftSize);
326
327 // wrap to pi
328 phaseDiff = wrapToPi(phaseDiff - binFreq * m_hopSizeAnalysis);
329
330 // calculate phase deviation in number of bins from the center freq
331 float binDeviation = phaseDiff * (float)m_fftSize / (float(m_hopSizeAnalysis) * 2.0f * float(M_PI));
332
333 // add original bin number to get the bin this partial belongs to
334 m_binFrequenciesAnalysis[m_numChannel][i] = (float)i + binDeviation;
335
336 // save magnitude for later
338
339 // update phase for next hop
341 }
342
343 // zero out synthesis bins, ready for new data
344 // fill m_magnitudesSynthesis with zeros
346 // fill m_binFrequenciesSynthesis with zeros
348
349 // handle the pitch shift, storing frequencies to new bins
350 for (int i = 0; i <= m_fftSize / 2; i++)
351 {
352 // find nearest bin to shifted frequency
353 int newBin = static_cast<int>(floor(i * m_pitchShiftRatio + 0.5f));
354
355 // ignore shifted bins above Nyquist
356 if (newBin <= m_fftSize / 2)
357 {
361 }
362 }
363
364 // synthesise frequencies into new magnitude and phase values for FFT bins
365 for (int i = 0; i <= m_fftSize / 2; i++)
366 {
367 float mag = m_magnitudesSynthesis[m_numChannel][i];
368
369 // get the fractional offset from the bin center frequency
370 float binDeviation = m_binFrequenciesSynthesis[m_numChannel][i] - i;
371
372 // multiply to get back to a phase value
373 float phaseDiff = binDeviation * 2.0f * float(M_PI) * float(m_hopSizeAnalysis) / float(m_fftSize);
374
375 // add the expected phase increment based on the bin center frequency
376 float binCenterFreq = 2.0f * float(M_PI) * float(i) / float(m_fftSize);
377 phaseDiff += binCenterFreq * float(m_hopSizeAnalysis);
378
379 // advance the phase from the previous hop
380 float outPhase = wrapToPi(m_previousPhasesOutput[m_numChannel][i] + phaseDiff);
381
382 // go back to real and imag (Polar to Rect)
383 realPart[i] = mag * std::cos(outPhase);
384 imagPart[i] = mag * std::sin(outPhase);
385
386 // save phases for the next hop
388 }
389
390 // reconstruct fftData
391 k = 0;
392 for (int i = 0; i <= m_fftSize / 2; i++)
393 {
394 fftData[k] = realPart[i];
395 fftData[k + 1] = imagPart[i];
396 k += 2;
397 }
398 }
399
400 void PhaseVocoder::timeStrech(std::vector<float>& fftData)
401 {
402 // collect separately real and imaginary parts of the FFT
403 std::vector<float> realPart(m_fftSize / 2 + 1);
404 std::vector<float> imagPart(m_fftSize / 2 + 1);
405 int k = 0;
406 for (int i = 0; i <= m_fftSize / 2; i++)
407 {
408 realPart[i] = fftData[k];
409 imagPart[i] = fftData[k + 1];
410 k += 2;
411 }
412
413 // for each bin
414 for (int i = 0; i < m_fftSize / 2; i++)
415 {
416 // calculate magnitude and phase from Re and Imag (Rect to Polar)
417 float mag = std::sqrt(pow(realPart[i], 2.0f) + pow(imagPart[i], 2.0f));
418 // set phase of all bins to 0
419 float phase = std::atan2(imagPart[i], realPart[i]);
420
421 // go back to real and imag (Polar to Rect)
422 realPart[i] = mag * std::cos(phase);
423 imagPart[i] = mag * std::sin(phase);
424 }
425
426 // reconstruct fftData
427 k = 0;
428 for (int i = 0; i <= m_fftSize / 2; i++)
429 {
430 fftData[k] = realPart[i];
431 fftData[k + 1] = imagPart[i];
432 k += 2;
433 }
434 }
435
436 float PhaseVocoder::wrapToPi(float phaseIn)
437 {
438 if (phaseIn < -float(M_PI) || phaseIn > float(M_PI))
439 {
440 if (phaseIn >= 0.0f)
441 return fmod(phaseIn + float(M_PI), 2.0f * float(M_PI)) - float(M_PI);
442 else
443 return fmod(phaseIn - float(M_PI), -2.0f * float(M_PI)) + float(M_PI);
444 }
445 else
446 return phaseIn;
447 }
448
449 float PhaseVocoder::caclulateFrameRMS(std::vector<float> inputFrame)
450 {
451 float sumOfSquares = 0;
452
453 for (int i = 0; i < inputFrame.size(); i++)
454 {
455 sumOfSquares += inputFrame[i] * inputFrame[i];
456 }
457
458 float meanValue = sumOfSquares / (float)inputFrame.size();
459
460 float rmsValue = sqrtf(meanValue);
461
462 return rmsValue;
463 }
464
465} // namespace krotos
const int m_overlapFactor
Definition PhaseVocoder.h:119
int m_inputBufferSize
Definition PhaseVocoder.h:89
int m_hopSizeSynthesis
Definition PhaseVocoder.h:76
PhaseVocoder(int fftSize, int hopSize, int inputBufferSize, WindowType windowFunction)
Definition PhaseVocoder.cpp:3
void processFrame(std::vector< float > &inBuffer, int inPointer, std::vector< float > &outBuffer, int outPointer)
Definition PhaseVocoder.cpp:133
std::vector< float > m_analysisWindow
Definition PhaseVocoder.h:83
float process(float inputSample, int channelToUse)
Definition PhaseVocoder.cpp:92
std::vector< std::vector< float > > m_magnitudesSynthesis
Definition PhaseVocoder.h:107
float wrapToPi(float phaseIn)
Definition PhaseVocoder.cpp:436
std::vector< std::vector< float > > m_previousPhasesInput
Definition PhaseVocoder.h:105
std::vector< int > m_outputWritePointer
Definition PhaseVocoder.h:100
std::vector< float > m_synthesisWindow
Definition PhaseVocoder.h:84
int m_numChannel
Definition PhaseVocoder.h:120
void setNumChannels(int newValue)
Definition PhaseVocoder.cpp:30
@ Robotizer
Definition PhaseVocoder.h:22
@ PitchShifter
Definition PhaseVocoder.h:24
@ TimeStrecher
Definition PhaseVocoder.h:25
void robotize(std::vector< float > &fftData)
Definition PhaseVocoder.cpp:264
float m_pitchShiftRatio
Definition PhaseVocoder.h:114
float m_frequencyDetected
Definition PhaseVocoder.h:113
void pitchShift(std::vector< float > &fftData)
Definition PhaseVocoder.cpp:300
const int NUM_CHANNELS_MAX
Definition PhaseVocoder.h:117
std::vector< std::vector< float > > m_previousPhasesOutput
Definition PhaseVocoder.h:106
std::vector< std::vector< float > > m_magnitudesAnalysis
Definition PhaseVocoder.h:108
std::vector< int > m_outputReadPointer
Definition PhaseVocoder.h:102
std::vector< std::vector< float > > m_outputBuffer
Definition PhaseVocoder.h:98
void detectFrequency(std::vector< float > &fftData)
Definition PhaseVocoder.cpp:200
int m_fftSize
Definition PhaseVocoder.h:73
std::vector< int > m_inputWritePointer
Definition PhaseVocoder.h:92
std::vector< std::vector< float > > m_binFrequenciesSynthesis
Definition PhaseVocoder.h:110
int m_outputBufferSize
Definition PhaseVocoder.h:97
void setGeneralParam(float paramVaue)
Definition PhaseVocoder.cpp:52
float m_maxBinValue
Definition PhaseVocoder.h:112
int m_hopSizeAnalysis
Definition PhaseVocoder.h:75
int m_maxBinIndex
Definition PhaseVocoder.h:111
float m_timeStretchRatio
Definition PhaseVocoder.h:115
void setWindowFunction(WindowType windowType)
Definition PhaseVocoder.cpp:23
PhaseVocoderMode m_mode
Definition PhaseVocoder.h:71
dsp::FFT m_fft
Definition PhaseVocoder.h:81
float m_sampleRate
Definition PhaseVocoder.h:78
std::vector< int > m_hopCounter
Definition PhaseVocoder.h:94
void timeStrech(std::vector< float > &fftData)
Definition PhaseVocoder.cpp:400
WindowType m_windowType
Definition PhaseVocoder.h:85
float caclulateFrameRMS(std::vector< float > inputFrame)
Definition PhaseVocoder.cpp:449
std::vector< std::vector< float > > m_binFrequenciesAnalysis
Definition PhaseVocoder.h:109
std::vector< std::vector< float > > m_inputBuffer
Definition PhaseVocoder.h:90
std::vector< float > generateWindow(int sizeInSamples, WindowType windowType)
Definition WindowFunctions.cpp:12
Definition AirAbsorptionFilter.cpp:2
WindowType
Definition WindowFunctions.h:6
#define M_PI
Definition windowing.h:9