Mercurial > hg > beaglert
view projects/oscillator_bank/render.cpp @ 0:8a575ba3ab52
Initial commit.
author | andrewm |
---|---|
date | Fri, 31 Oct 2014 19:10:17 +0100 |
parents | |
children | 06f93bef7dd2 |
line wrap: on
line source
/* * render.cpp * * Created on: Oct 24, 2014 * Author: parallels */ #include "../../include/RTAudio.h" #include "../../include/Utilities.h" #include <rtdk.h> #include <cstdlib> #include <cmath> #include <cstring> #include <time.h> const float kMinimumFrequency = 20.0f; const float kMaximumFrequency = 8000.0f; float *gWavetable; // Buffer holding the precalculated sine lookup table float *gPhases; // Buffer holding the phase of each oscillator float *gFrequencies; // Buffer holding the frequencies of each oscillator float *gAmplitudes; // Buffer holding the amplitudes of each oscillator float *gDFrequencies; // Buffer holding the derivatives of frequency float *gDAmplitudes; // Buffer holding the derivatives of amplitude float gAudioSampleRate; int gSampleCount; // Sample counter for indicating when to update frequencies float gNewMinFrequency; float gNewMaxFrequency; // Task for handling the update of the frequencies using the matrix AuxiliaryTask gFrequencyUpdateTask; // These settings are carried over from main.cpp // Setting global variables is an alternative approach // to passing a structure to userData in initialise_render() extern int gNumOscillators; extern int gWavetableLength; void recalculate_frequencies(); extern "C" { // Function prototype for ARM assembly implementation of oscillator bank void oscillator_bank_neon(int numAudioFrames, float *audioOut, int activePartialNum, int lookupTableSize, float *phases, float *frequencies, float *amplitudes, float *freqDerivatives, float *ampDerivatives, float *lookupTable); } // initialise_render() is called once before the audio rendering starts. // Use it to perform any initialisation and allocation which is dependent // on the period size or sample rate. // // userData holds an opaque pointer to a data structure that was passed // in from the call to initAudio(). // // Return true on success; returning false halts the program. bool initialise_render(int numChannels, int numMatrixFramesPerPeriod, int numAudioFramesPerPeriod, float matrixSampleRate, float audioSampleRate, void *userData) { srandom(time(NULL)); // Initialise the sine wavetable if(posix_memalign((void **)&gWavetable, 8, (gWavetableLength + 1) * sizeof(float))) { rt_printf("Error allocating wavetable\n"); return false; } for(int n = 0; n < gWavetableLength + 1; n++) gWavetable[n] = sinf(2.0 * M_PI * (float)n / (float)gWavetableLength); // Allocate the other buffers if(posix_memalign((void **)&gPhases, 16, gNumOscillators * sizeof(float))) { rt_printf("Error allocating phase buffer\n"); return false; } if(posix_memalign((void **)&gFrequencies, 16, gNumOscillators * sizeof(float))) { rt_printf("Error allocating frequency buffer\n"); return false; } if(posix_memalign((void **)&gAmplitudes, 16, gNumOscillators * sizeof(float))) { rt_printf("Error allocating amplitude buffer\n"); return false; } if(posix_memalign((void **)&gDFrequencies, 16, gNumOscillators * sizeof(float))) { rt_printf("Error allocating frequency derivative buffer\n"); return false; } if(posix_memalign((void **)&gDAmplitudes, 16, gNumOscillators * sizeof(float))) { rt_printf("Error allocating amplitude derivative buffer\n"); return false; } // Initialise buffer contents float freq = kMinimumFrequency; float increment = (kMaximumFrequency - kMinimumFrequency) / (float)gNumOscillators; for(int n = 0; n < gNumOscillators; n++) { gPhases[n] = 0.0; if(numMatrixFramesPerPeriod == 0) { // Random frequencies when used without matrix gFrequencies[n] = kMinimumFrequency + (kMaximumFrequency - kMinimumFrequency) * ((float)random() / (float)RAND_MAX); } else { // Constant spread of frequencies when used with matrix gFrequencies[n] = freq; freq += increment; } // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians gFrequencies[n] *= (float)gWavetableLength / audioSampleRate; gAmplitudes[n] = ((float)random() / (float)RAND_MAX) / (float)gNumOscillators; gDFrequencies[n] = gDAmplitudes[n] = 0.0; } // Initialise auxiliary tasks if((gFrequencyUpdateTask = createAuxiliaryTaskLoop(&recalculate_frequencies, 90, "beaglert-update-frequencies")) == 0) return false; gAudioSampleRate = audioSampleRate; gSampleCount = 0; return true; } // render() is called regularly at the highest priority by the audio engine. // Input and output are given from the audio hardware and the other // ADCs and DACs (if available). If only audio is available, numMatrixFrames // will be 0. void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut, uint16_t *matrixIn, uint16_t *matrixOut) { // Initialise buffer to 0 memset(audioOut, 0, 2 * numAudioFrames * sizeof(float)); // Render audio frames oscillator_bank_neon(numAudioFrames, audioOut, gNumOscillators, gWavetableLength, gPhases, gFrequencies, gAmplitudes, gDFrequencies, gDAmplitudes, gWavetable); if(numMatrixFrames != 0 && (gSampleCount += numAudioFrames) >= 128) { gSampleCount = 0; gNewMinFrequency = map(matrixIn[0], 0, MATRIX_MAX, 20.0f, 8000.0f); gNewMaxFrequency = map(matrixIn[1], 0, MATRIX_MAX, 20.0f, 8000.0f); // Make sure max >= min if(gNewMaxFrequency < gNewMinFrequency) { float temp = gNewMaxFrequency; gNewMaxFrequency = gNewMinFrequency; gNewMinFrequency = temp; } // Request that the lower-priority task run at next opportunity scheduleAuxiliaryTask(gFrequencyUpdateTask); } } // This is a lower-priority call to update the frequencies which will happen // periodically when the matrix is enabled. By placing it at a lower priority, // it has minimal effect on the audio performance but it will take longer to // complete if the system is under heavy audio load. void recalculate_frequencies() { float freq = gNewMinFrequency; float increment = (gNewMaxFrequency - gNewMinFrequency) / (float)gNumOscillators; for(int n = 0; n < gNumOscillators; n++) { // Update the frequencies to a regular spread, plus a small amount of randomness // to avoid weird phase effects float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; float newFreq = freq * randScale; // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians gFrequencies[n] = newFreq * (float)gWavetableLength / gAudioSampleRate; freq += increment; } } // cleanup_render() is called once at the end, after the audio has stopped. // Release any resources that were allocated in initialise_render(). void cleanup_render() { free(gWavetable); free(gPhases); free(gFrequencies); free(gAmplitudes); free(gDFrequencies); free(gDAmplitudes); }