comparison projects/basic_FFT_phase_vocoder/render.cpp @ 250:aec268b5d1b4

Added phase vocoder example (optionally Midi controllable)
author Giulio Moro <giuliomoro@yahoo.it>
date Thu, 28 Apr 2016 19:30:12 +0100
parents
children cbf70fe3711b
comparison
equal deleted inserted replaced
249:f2d47df23c68 250:aec268b5d1b4
1 /*
2 * render.cpp
3 *
4 * Created on: Oct 24, 2014
5 * Author: parallels
6 */
7
8
9 #include <BeagleRT.h>
10 #include <rtdk.h>
11 #include <NE10.h> // NEON FFT library
12 #include <cmath>
13 #include "SampleData.h"
14 #include <Midi.h>
15
16 #define BUFFER_SIZE 16384
17
18 // TODO: your buffer and counter go here!
19 float gInputBuffer[BUFFER_SIZE];
20 int gInputBufferPointer = 0;
21 float gOutputBuffer[BUFFER_SIZE];
22 int gOutputBufferWritePointer = 0;
23 int gOutputBufferReadPointer = 0;
24 int gSampleCount = 0;
25
26 float *gWindowBuffer;
27
28 // -----------------------------------------------
29 // These variables used internally in the example:
30 int gFFTSize = 2048;
31 int gHopSize = 512;
32 int gPeriod = 512;
33 float gFFTScaleFactor = 0;
34
35 // FFT vars
36 ne10_fft_cpx_float32_t* timeDomainIn;
37 ne10_fft_cpx_float32_t* timeDomainOut;
38 ne10_fft_cpx_float32_t* frequencyDomain;
39 ne10_fft_cfg_float32_t cfg;
40
41 // Sample info
42 SampleData gSampleData; // User defined structure to get complex data from main
43 int gReadPtr = 0; // Position of last read sample from file
44
45 // Auxiliary task for calculating FFT
46 AuxiliaryTask gFFTTask;
47 int gFFTInputBufferPointer = 0;
48 int gFFTOutputBufferPointer = 0;
49
50 void process_fft_background();
51
52
53 int gEffect = 0; // change this here or with midi CC
54 enum{
55 kBypass,
56 kRobot,
57 kWhisper,
58 };
59
60 float gDryWet = 1; // mix between the unprocessed and processed sound
61 float gPlaybackLive = 0.5f; // mix between the file playback and the live audio input
62 float gGain = 1; // overall gain
63 Midi midi;
64 void midiCallback(MidiChannelMessage message, void* arg){
65 if(message.getType() == kmmNoteOn){
66 if(message.getDataByte(1) > 0){
67 int note = message.getDataByte(0);
68 float frequency = powf(2, (note-69)/12.f)*440;
69 gPeriod = (int)(44100 / frequency + 0.5);
70 printf("\nnote: %d, frequency: %f, hop: %d\n", note, frequency, gPeriod);
71 }
72 }
73
74 bool shouldPrint = false;
75 if(message.getType() == kmmControlChange){
76 float data = message.getDataByte(1) / 127.0f;
77 switch (message.getDataByte(0)){
78 case 2 :
79 gEffect = (int)(data * 2 + 0.5); // CC2 selects an effect between 0,1,2
80 break;
81 case 3 :
82 gPlaybackLive = data;
83 break;
84 case 4 :
85 gDryWet = data;
86 break;
87 case 5:
88 gGain = data*10;
89 break;
90 default:
91 shouldPrint = true;
92 }
93 }
94 if(shouldPrint){
95 message.prettyPrint();
96 }
97 }
98
99 // userData holds an opaque pointer to a data structure that was passed
100 // in from the call to initAudio().
101 //
102 // Return true on success; returning false halts the program.
103 bool setup(BeagleRTContext* context, void* userData)
104 {
105 midi.readFrom(0);
106 midi.setParserCallback(midiCallback);
107 // Retrieve a parameter passed in from the initAudio() call
108 gSampleData = *(SampleData *)userData;
109
110 gFFTScaleFactor = 1.0f / (float)gFFTSize;
111 gOutputBufferWritePointer += gHopSize;
112
113 timeDomainIn = (ne10_fft_cpx_float32_t*) NE10_MALLOC (gFFTSize * sizeof (ne10_fft_cpx_float32_t));
114 timeDomainOut = (ne10_fft_cpx_float32_t*) NE10_MALLOC (gFFTSize * sizeof (ne10_fft_cpx_float32_t));
115 frequencyDomain = (ne10_fft_cpx_float32_t*) NE10_MALLOC (gFFTSize * sizeof (ne10_fft_cpx_float32_t));
116 cfg = ne10_fft_alloc_c2c_float32 (gFFTSize);
117
118 memset(timeDomainOut, 0, gFFTSize * sizeof (ne10_fft_cpx_float32_t));
119 memset(gOutputBuffer, 0, BUFFER_SIZE * sizeof(float));
120
121 // Allocate the window buffer based on the FFT size
122 gWindowBuffer = (float *)malloc(gFFTSize * sizeof(float));
123 if(gWindowBuffer == 0)
124 return false;
125
126 // Calculate a Hann window
127 for(int n = 0; n < gFFTSize; n++) {
128 gWindowBuffer[n] = 0.5f * (1.0f - cosf(2.0 * M_PI * n / (float)(gFFTSize - 1)));
129 }
130
131 // Initialise auxiliary tasks
132 if((gFFTTask = BeagleRT_createAuxiliaryTask(&process_fft_background, 90, "fft-calculation")) == 0)
133 return false;
134 rt_printf("You are listening to an FFT phase-vocoder with overlap-and-add "
135 "Use Midi Control Change to control:\n"
136 "CC 2: effect type (bypass/robotization/whisperization)"
137 "CC 3: mix between recorded sample and live audio input"
138 "CC 4: mix between the unprocessed and processed sound"
139 "CC 5: gain"
140 );
141 return true;
142 }
143
144 // This function handles the FFT processing in this example once the buffer has
145 // been assembled.
146 void process_fft(float *inBuffer, int inWritePointer, float *outBuffer, int outWritePointer)
147 {
148 // Copy buffer into FFT input
149 int pointer = (inWritePointer - gFFTSize + BUFFER_SIZE) % BUFFER_SIZE;
150 for(int n = 0; n < gFFTSize; n++) {
151 timeDomainIn[n].r = (ne10_float32_t) inBuffer[pointer] * gWindowBuffer[n];
152 timeDomainIn[n].i = 0;
153
154 pointer++;
155 if(pointer >= BUFFER_SIZE)
156 pointer = 0;
157 }
158
159 // Run the FFT
160 ne10_fft_c2c_1d_float32_neon (frequencyDomain, timeDomainIn, cfg->twiddles, cfg->factors, gFFTSize, 0);
161
162 switch (gEffect){
163 case kRobot :
164 // Robotise the output
165 for(int n = 0; n < gFFTSize; n++) {
166 float amplitude = sqrtf(frequencyDomain[n].r * frequencyDomain[n].r + frequencyDomain[n].i * frequencyDomain[n].i);
167 frequencyDomain[n].r = amplitude;
168 frequencyDomain[n].i = 0;
169 }
170 break;
171 case kWhisper :
172 for(int n = 0; n < gFFTSize; n++) {
173 float amplitude = sqrtf(frequencyDomain[n].r * frequencyDomain[n].r + frequencyDomain[n].i * frequencyDomain[n].i);
174 float phase = rand()/(float)RAND_MAX * 2 * M_PI;
175 frequencyDomain[n].r = cosf(phase) * amplitude;
176 frequencyDomain[n].i = sinf(phase) * amplitude;
177 }
178 break;
179 case kBypass:
180 //bypass
181 break;
182 }
183
184 // Run the inverse FFT
185 ne10_fft_c2c_1d_float32_neon (timeDomainOut, frequencyDomain, cfg->twiddles, cfg->factors, gFFTSize, 1);
186 // Overlap-and-add timeDomainOut into the output buffer
187 pointer = outWritePointer;
188 for(int n = 0; n < gFFTSize; n++) {
189 outBuffer[pointer] += (timeDomainOut[n].r) * gFFTScaleFactor;
190 if(isnan(outBuffer[pointer]))
191 rt_printf("outBuffer OLA\n");
192 pointer++;
193 if(pointer >= BUFFER_SIZE)
194 pointer = 0;
195 }
196 }
197
198 // Function to process the FFT in a thread at lower priority
199 void process_fft_background() {
200 process_fft(gInputBuffer, gFFTInputBufferPointer, gOutputBuffer, gFFTOutputBufferPointer);
201 }
202
203 // render() is called regularly at the highest priority by the audio engine.
204 // Input and output are given from the audio hardware and the other
205 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
206 // will be 0.
207 void render(BeagleRTContext* context, void* userData)
208 {
209 float* audioIn = context->audioIn;
210 float* audioOut = context->audioOut;
211 int numAudioFrames = context->audioFrames;
212 int numAudioChannels = context->audioChannels;
213 // ------ this code internal to the demo; leave as is ----------------
214
215 // Prep the "input" to be the sound file played in a loop
216 for(int n = 0; n < numAudioFrames; n++) {
217 if(gReadPtr < gSampleData.sampleLen)
218 audioIn[2*n] = audioIn[2*n+1] = gSampleData.samples[gReadPtr]*(1-gPlaybackLive) +
219 gPlaybackLive*0.5f*(audioReadFrame(context,n,0)+audioReadFrame(context,n,1));
220 else
221 audioIn[2*n] = audioIn[2*n+1] = 0;
222 if(++gReadPtr >= gSampleData.sampleLen)
223 gReadPtr = 0;
224 }
225 // -------------------------------------------------------------------
226
227 for(int n = 0; n < numAudioFrames; n++) {
228 gInputBuffer[gInputBufferPointer] = ((audioIn[n*numAudioChannels] + audioIn[n*numAudioChannels+1]) * 0.5);
229
230 // Copy output buffer to output
231 for(int channel = 0; channel < numAudioChannels; channel++){
232 audioOut[n * numAudioChannels + channel] = gOutputBuffer[gOutputBufferReadPointer] * gGain * gDryWet + (1 - gDryWet) * audioIn[n * numAudioChannels + channel];
233 }
234
235 // Clear the output sample in the buffer so it is ready for the next overlap-add
236 gOutputBuffer[gOutputBufferReadPointer] = 0;
237 gOutputBufferReadPointer++;
238 if(gOutputBufferReadPointer >= BUFFER_SIZE)
239 gOutputBufferReadPointer = 0;
240 gOutputBufferWritePointer++;
241 if(gOutputBufferWritePointer >= BUFFER_SIZE)
242 gOutputBufferWritePointer = 0;
243
244 gInputBufferPointer++;
245 if(gInputBufferPointer >= BUFFER_SIZE)
246 gInputBufferPointer = 0;
247
248 gSampleCount++;
249 if(gSampleCount >= gHopSize) {
250 //process_fft(gInputBuffer, gInputBufferPointer, gOutputBuffer, gOutputBufferPointer);
251 gFFTInputBufferPointer = gInputBufferPointer;
252 gFFTOutputBufferPointer = gOutputBufferWritePointer;
253 BeagleRT_scheduleAuxiliaryTask(gFFTTask);
254
255 gSampleCount = 0;
256 }
257 }
258 gHopSize = gPeriod;
259 }
260
261 // cleanup_render() is called once at the end, after the audio has stopped.
262 // Release any resources that were allocated in initialise_render().
263
264 void cleanup(BeagleRTContext* context, void* userData)
265 {
266 NE10_FREE(timeDomainIn);
267 NE10_FREE(timeDomainOut);
268 NE10_FREE(frequencyDomain);
269 NE10_FREE(cfg);
270 free(gWindowBuffer);
271 }