annotate projects/basic_FFT_phase_vocoder/render.cpp @ 269:ac8eb07afcf5

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