robert@464: /* robert@464: ____ _____ _ _ robert@464: | __ )| ____| | / \ robert@464: | _ \| _| | | / _ \ robert@464: | |_) | |___| |___ / ___ \ robert@464: |____/|_____|_____/_/ \_\ robert@464: robert@464: The platform for ultra-low latency audio and sensor processing robert@464: robert@464: http://bela.io robert@464: robert@464: A project of the Augmented Instruments Laboratory within the robert@464: Centre for Digital Music at Queen Mary University of London. robert@464: http://www.eecs.qmul.ac.uk/~andrewm robert@464: robert@464: (c) 2016 Augmented Instruments Laboratory: Andrew McPherson, robert@464: Astrid Bin, Liam Donovan, Christian Heinrichs, Robert Jack, robert@464: Giulio Moro, Laurel Pardue, Victor Zappi. All rights reserved. robert@464: robert@464: The Bela software is distributed under the GNU Lesser General Public License robert@464: (LGPL 3.0), available here: https://www.gnu.org/licenses/lgpl-3.0.txt robert@464: */ robert@464: robert@464: robert@464: #include robert@464: #include robert@464: #include robert@464: #include robert@464: #include robert@464: #include robert@464: robert@464: const float kMinimumFrequency = 20.0f; robert@464: const float kMaximumFrequency = 8000.0f; robert@464: robert@464: float *gWavetable; // Buffer holding the precalculated sine lookup table robert@464: float *gPhases; // Buffer holding the phase of each oscillator robert@464: float *gFrequencies; // Buffer holding the frequencies of each oscillator robert@464: float *gAmplitudes; // Buffer holding the amplitudes of each oscillator robert@464: float *gDFrequencies; // Buffer holding the derivatives of frequency robert@464: float *gDAmplitudes; // Buffer holding the derivatives of amplitude robert@464: robert@464: float gAudioSampleRate; robert@464: int gSampleCount; // Sample counter for indicating when to update frequencies robert@464: float gNewMinFrequency; robert@464: float gNewMaxFrequency; robert@464: robert@464: // Task for handling the update of the frequencies using the matrix robert@464: AuxiliaryTask gFrequencyUpdateTask; robert@464: robert@464: // These settings are carried over from main.cpp robert@464: // Setting global variables is an alternative approach robert@464: // to passing a structure to userData in setup() robert@464: robert@464: extern int gNumOscillators; robert@464: extern int gWavetableLength; robert@464: robert@464: void recalculate_frequencies(); robert@464: robert@464: extern "C" { robert@464: // Function prototype for ARM assembly implementation of oscillator bank robert@464: void oscillator_bank_neon(int numAudioFrames, float *audioOut, robert@464: int activePartialNum, int lookupTableSize, robert@464: float *phases, float *frequencies, float *amplitudes, robert@464: float *freqDerivatives, float *ampDerivatives, robert@464: float *lookupTable); robert@464: } robert@464: robert@464: // setup() is called once before the audio rendering starts. robert@464: // Use it to perform any initialisation and allocation which is dependent robert@464: // on the period size or sample rate. robert@464: // robert@464: // userData holds an opaque pointer to a data structure that was passed robert@464: // in from the call to initAudio(). robert@464: // robert@464: // Return true on success; returning false halts the program. robert@464: bool setup(BelaContext *context, void *userData) robert@464: { robert@464: srandom(time(NULL)); robert@464: robert@464: if(context->audioChannels != 2) { robert@464: rt_printf("Error: this example needs stereo audio enabled\n"); robert@464: return false; robert@464: } robert@464: robert@464: // Initialise the sine wavetable robert@464: if(posix_memalign((void **)&gWavetable, 8, (gWavetableLength + 1) * sizeof(float))) { robert@464: rt_printf("Error allocating wavetable\n"); robert@464: return false; robert@464: } robert@464: for(int n = 0; n < gWavetableLength + 1; n++) robert@464: gWavetable[n] = sinf(2.0 * M_PI * (float)n / (float)gWavetableLength); robert@464: robert@464: // Allocate the other buffers robert@464: if(posix_memalign((void **)&gPhases, 16, gNumOscillators * sizeof(float))) { robert@464: rt_printf("Error allocating phase buffer\n"); robert@464: return false; robert@464: } robert@464: if(posix_memalign((void **)&gFrequencies, 16, gNumOscillators * sizeof(float))) { robert@464: rt_printf("Error allocating frequency buffer\n"); robert@464: return false; robert@464: } robert@464: if(posix_memalign((void **)&gAmplitudes, 16, gNumOscillators * sizeof(float))) { robert@464: rt_printf("Error allocating amplitude buffer\n"); robert@464: return false; robert@464: } robert@464: if(posix_memalign((void **)&gDFrequencies, 16, gNumOscillators * sizeof(float))) { robert@464: rt_printf("Error allocating frequency derivative buffer\n"); robert@464: return false; robert@464: } robert@464: if(posix_memalign((void **)&gDAmplitudes, 16, gNumOscillators * sizeof(float))) { robert@464: rt_printf("Error allocating amplitude derivative buffer\n"); robert@464: return false; robert@464: } robert@464: robert@464: // Initialise buffer contents robert@464: robert@464: float freq = kMinimumFrequency; robert@464: float increment = (kMaximumFrequency - kMinimumFrequency) / (float)gNumOscillators; robert@464: robert@464: for(int n = 0; n < gNumOscillators; n++) { robert@464: gPhases[n] = 0.0; robert@464: robert@464: if(context->analogFrames == 0) { robert@464: // Random frequencies when used without matrix robert@464: gFrequencies[n] = kMinimumFrequency + (kMaximumFrequency - kMinimumFrequency) * ((float)random() / (float)RAND_MAX); robert@464: } robert@464: else { robert@464: // Constant spread of frequencies when used with matrix robert@464: gFrequencies[n] = freq; robert@464: freq += increment; robert@464: } robert@464: robert@464: // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians robert@464: gFrequencies[n] *= (float)gWavetableLength / context->audioSampleRate; robert@464: gAmplitudes[n] = ((float)random() / (float)RAND_MAX) / (float)gNumOscillators; robert@464: gDFrequencies[n] = gDAmplitudes[n] = 0.0; robert@464: } robert@464: robert@464: increment = 0; robert@464: freq = 440.0; robert@464: robert@464: for(int n = 0; n < gNumOscillators; n++) { robert@464: // Update the frequencies to a regular spread, plus a small amount of randomness robert@464: // to avoid weird phase effects robert@464: float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; robert@464: float newFreq = freq * randScale; robert@464: robert@464: // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians robert@464: gFrequencies[n] = newFreq * (float)gWavetableLength / context->audioSampleRate; robert@464: robert@464: freq += increment; robert@464: } robert@464: robert@464: // Initialise auxiliary tasks robert@464: if((gFrequencyUpdateTask = Bela_createAuxiliaryTask(&recalculate_frequencies, 85, "bela-update-frequencies")) == 0) robert@464: return false; robert@464: robert@464: //for(int n = 0; n < gNumOscillators; n++) robert@464: // rt_printf("%f\n", gFrequencies[n]); robert@464: robert@464: gAudioSampleRate = context->audioSampleRate; robert@464: gSampleCount = 0; robert@464: robert@464: return true; robert@464: } robert@464: robert@464: // render() is called regularly at the highest priority by the audio engine. robert@464: // Input and output are given from the audio hardware and the other robert@464: // ADCs and DACs (if available). If only audio is available, numMatrixFrames robert@464: // will be 0. robert@464: robert@464: void render(BelaContext *context, void *userData) robert@464: { robert@464: // Initialise buffer to 0 robert@464: memset(context->audioOut, 0, 2 * context->audioFrames * sizeof(float)); robert@464: robert@464: // Render audio frames robert@464: oscillator_bank_neon(context->audioFrames, context->audioOut, robert@464: gNumOscillators, gWavetableLength, robert@464: gPhases, gFrequencies, gAmplitudes, robert@464: gDFrequencies, gDAmplitudes, robert@464: gWavetable); robert@464: robert@464: if(context->analogFrames != 0 && (gSampleCount += context->audioFrames) >= 128) { robert@464: gSampleCount = 0; robert@464: gNewMinFrequency = map(context->analogIn[0], 0, 1.0, 1000.0f, 8000.0f); robert@464: gNewMaxFrequency = map(context->analogIn[1], 0, 1.0, 1000.0f, 8000.0f); robert@464: robert@464: // Make sure max >= min robert@464: if(gNewMaxFrequency < gNewMinFrequency) { robert@464: float temp = gNewMaxFrequency; robert@464: gNewMaxFrequency = gNewMinFrequency; robert@464: gNewMinFrequency = temp; robert@464: } robert@464: robert@464: // Request that the lower-priority task run at next opportunity robert@464: //Bela_scheduleAuxiliaryTask(gFrequencyUpdateTask); robert@464: } robert@464: } robert@464: robert@464: // This is a lower-priority call to update the frequencies which will happen robert@464: // periodically when the matrix is enabled. By placing it at a lower priority, robert@464: // it has minimal effect on the audio performance but it will take longer to robert@464: // complete if the system is under heavy audio load. robert@464: robert@464: void recalculate_frequencies() robert@464: { robert@464: float freq = gNewMinFrequency; robert@464: float increment = (gNewMaxFrequency - gNewMinFrequency) / (float)gNumOscillators; robert@464: robert@464: for(int n = 0; n < gNumOscillators; n++) { robert@464: // Update the frequencies to a regular spread, plus a small amount of randomness robert@464: // to avoid weird phase effects robert@464: float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; robert@464: float newFreq = freq * randScale; robert@464: robert@464: // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians robert@464: gFrequencies[n] = newFreq * (float)gWavetableLength / gAudioSampleRate; robert@464: robert@464: freq += increment; robert@464: } robert@464: } robert@464: robert@464: robert@464: // cleanup() is called once at the end, after the audio has stopped. robert@464: // Release any resources that were allocated in setup(). robert@464: robert@464: void cleanup(BelaContext *context, void *userData) robert@464: { robert@464: free(gWavetable); robert@464: free(gPhases); robert@464: free(gFrequencies); robert@464: free(gAmplitudes); robert@464: free(gDFrequencies); robert@464: free(gDAmplitudes); robert@464: } robert@464: robert@464: robert@464: /** robert@500: \example oscillator-bank/render.cpp robert@464: robert@464: Oscillator Bank robert@464: ---------------------- robert@464: robert@464: These files demonstrate an oscillator bank implemented in assembly code robert@464: that is used as part of the d-box project. robert@464: */