andrewm@0: /* andrewm@0: * render.cpp andrewm@0: * andrewm@0: * Created on: Oct 24, 2014 andrewm@0: * Author: parallels andrewm@0: */ andrewm@0: andrewm@0: giuliomoro@301: #include andrewm@56: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: andrewm@0: const float kMinimumFrequency = 20.0f; andrewm@0: const float kMaximumFrequency = 8000.0f; andrewm@0: andrewm@0: float *gWavetable; // Buffer holding the precalculated sine lookup table andrewm@0: float *gPhases; // Buffer holding the phase of each oscillator andrewm@0: float *gFrequencies; // Buffer holding the frequencies of each oscillator andrewm@0: float *gAmplitudes; // Buffer holding the amplitudes of each oscillator andrewm@0: float *gDFrequencies; // Buffer holding the derivatives of frequency andrewm@0: float *gDAmplitudes; // Buffer holding the derivatives of amplitude andrewm@0: andrewm@0: float gAudioSampleRate; andrewm@0: int gSampleCount; // Sample counter for indicating when to update frequencies andrewm@0: float gNewMinFrequency; andrewm@0: float gNewMaxFrequency; andrewm@0: andrewm@0: // Task for handling the update of the frequencies using the matrix andrewm@0: AuxiliaryTask gFrequencyUpdateTask; andrewm@0: andrewm@0: // These settings are carried over from main.cpp andrewm@0: // Setting global variables is an alternative approach andrewm@56: // to passing a structure to userData in setup() andrewm@0: andrewm@0: extern int gNumOscillators; andrewm@0: extern int gWavetableLength; andrewm@0: andrewm@0: void recalculate_frequencies(); andrewm@0: andrewm@0: extern "C" { andrewm@0: // Function prototype for ARM assembly implementation of oscillator bank andrewm@0: void oscillator_bank_neon(int numAudioFrames, float *audioOut, andrewm@0: int activePartialNum, int lookupTableSize, andrewm@0: float *phases, float *frequencies, float *amplitudes, andrewm@0: float *freqDerivatives, float *ampDerivatives, andrewm@0: float *lookupTable); andrewm@0: } andrewm@0: andrewm@56: // setup() is called once before the audio rendering starts. andrewm@0: // Use it to perform any initialisation and allocation which is dependent andrewm@0: // on the period size or sample rate. andrewm@0: // andrewm@0: // userData holds an opaque pointer to a data structure that was passed andrewm@0: // in from the call to initAudio(). andrewm@0: // andrewm@0: // Return true on success; returning false halts the program. giuliomoro@301: bool setup(BelaContext *context, void *userData) andrewm@0: { andrewm@0: srandom(time(NULL)); andrewm@0: andrewm@52: if(context->audioChannels != 2) { andrewm@14: rt_printf("Error: this example needs stereo audio enabled\n"); andrewm@14: return false; andrewm@14: } andrewm@14: andrewm@0: // Initialise the sine wavetable andrewm@0: if(posix_memalign((void **)&gWavetable, 8, (gWavetableLength + 1) * sizeof(float))) { andrewm@0: rt_printf("Error allocating wavetable\n"); andrewm@0: return false; andrewm@0: } andrewm@0: for(int n = 0; n < gWavetableLength + 1; n++) andrewm@0: gWavetable[n] = sinf(2.0 * M_PI * (float)n / (float)gWavetableLength); andrewm@0: andrewm@0: // Allocate the other buffers andrewm@0: if(posix_memalign((void **)&gPhases, 16, gNumOscillators * sizeof(float))) { andrewm@0: rt_printf("Error allocating phase buffer\n"); andrewm@0: return false; andrewm@0: } andrewm@0: if(posix_memalign((void **)&gFrequencies, 16, gNumOscillators * sizeof(float))) { andrewm@0: rt_printf("Error allocating frequency buffer\n"); andrewm@0: return false; andrewm@0: } andrewm@0: if(posix_memalign((void **)&gAmplitudes, 16, gNumOscillators * sizeof(float))) { andrewm@0: rt_printf("Error allocating amplitude buffer\n"); andrewm@0: return false; andrewm@0: } andrewm@0: if(posix_memalign((void **)&gDFrequencies, 16, gNumOscillators * sizeof(float))) { andrewm@0: rt_printf("Error allocating frequency derivative buffer\n"); andrewm@0: return false; andrewm@0: } andrewm@0: if(posix_memalign((void **)&gDAmplitudes, 16, gNumOscillators * sizeof(float))) { andrewm@0: rt_printf("Error allocating amplitude derivative buffer\n"); andrewm@0: return false; andrewm@0: } andrewm@0: andrewm@0: // Initialise buffer contents andrewm@0: andrewm@0: float freq = kMinimumFrequency; andrewm@0: float increment = (kMaximumFrequency - kMinimumFrequency) / (float)gNumOscillators; andrewm@0: andrewm@0: for(int n = 0; n < gNumOscillators; n++) { andrewm@0: gPhases[n] = 0.0; andrewm@0: andrewm@52: if(context->analogFrames == 0) { andrewm@0: // Random frequencies when used without matrix andrewm@0: gFrequencies[n] = kMinimumFrequency + (kMaximumFrequency - kMinimumFrequency) * ((float)random() / (float)RAND_MAX); andrewm@0: } andrewm@0: else { andrewm@0: // Constant spread of frequencies when used with matrix andrewm@0: gFrequencies[n] = freq; andrewm@0: freq += increment; andrewm@0: } andrewm@0: andrewm@0: // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians andrewm@52: gFrequencies[n] *= (float)gWavetableLength / context->audioSampleRate; andrewm@0: gAmplitudes[n] = ((float)random() / (float)RAND_MAX) / (float)gNumOscillators; andrewm@0: gDFrequencies[n] = gDAmplitudes[n] = 0.0; andrewm@0: } andrewm@0: andrewm@45: increment = 0; andrewm@45: freq = 440.0; andrewm@45: andrewm@45: for(int n = 0; n < gNumOscillators; n++) { andrewm@45: // Update the frequencies to a regular spread, plus a small amount of randomness andrewm@45: // to avoid weird phase effects andrewm@45: float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; andrewm@45: float newFreq = freq * randScale; andrewm@45: andrewm@45: // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians andrewm@52: gFrequencies[n] = newFreq * (float)gWavetableLength / context->audioSampleRate; andrewm@45: andrewm@45: freq += increment; andrewm@45: } andrewm@45: andrewm@0: // Initialise auxiliary tasks andrewm@303: if((gFrequencyUpdateTask = Bela_createAuxiliaryTask(&recalculate_frequencies, 85, "bela-update-frequencies")) == 0) andrewm@0: return false; andrewm@0: andrewm@52: //for(int n = 0; n < gNumOscillators; n++) andrewm@52: // rt_printf("%f\n", gFrequencies[n]); andrewm@45: andrewm@52: gAudioSampleRate = context->audioSampleRate; andrewm@0: gSampleCount = 0; andrewm@0: andrewm@0: return true; andrewm@0: } andrewm@0: andrewm@0: // render() is called regularly at the highest priority by the audio engine. andrewm@0: // Input and output are given from the audio hardware and the other andrewm@0: // ADCs and DACs (if available). If only audio is available, numMatrixFrames andrewm@0: // will be 0. andrewm@0: giuliomoro@301: void render(BelaContext *context, void *userData) andrewm@0: { andrewm@0: // Initialise buffer to 0 andrewm@52: memset(context->audioOut, 0, 2 * context->audioFrames * sizeof(float)); andrewm@0: andrewm@0: // Render audio frames andrewm@52: oscillator_bank_neon(context->audioFrames, context->audioOut, andrewm@0: gNumOscillators, gWavetableLength, andrewm@0: gPhases, gFrequencies, gAmplitudes, andrewm@0: gDFrequencies, gDAmplitudes, andrewm@0: gWavetable); andrewm@0: andrewm@52: if(context->analogFrames != 0 && (gSampleCount += context->audioFrames) >= 128) { andrewm@0: gSampleCount = 0; andrewm@52: gNewMinFrequency = map(context->analogIn[0], 0, 1.0, 1000.0f, 8000.0f); andrewm@52: gNewMaxFrequency = map(context->analogIn[1], 0, 1.0, 1000.0f, 8000.0f); andrewm@0: andrewm@0: // Make sure max >= min andrewm@0: if(gNewMaxFrequency < gNewMinFrequency) { andrewm@0: float temp = gNewMaxFrequency; andrewm@0: gNewMaxFrequency = gNewMinFrequency; andrewm@0: gNewMinFrequency = temp; andrewm@0: } andrewm@0: andrewm@0: // Request that the lower-priority task run at next opportunity giuliomoro@301: //Bela_scheduleAuxiliaryTask(gFrequencyUpdateTask); andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: // This is a lower-priority call to update the frequencies which will happen andrewm@0: // periodically when the matrix is enabled. By placing it at a lower priority, andrewm@0: // it has minimal effect on the audio performance but it will take longer to andrewm@0: // complete if the system is under heavy audio load. andrewm@0: andrewm@0: void recalculate_frequencies() andrewm@0: { andrewm@0: float freq = gNewMinFrequency; andrewm@0: float increment = (gNewMaxFrequency - gNewMinFrequency) / (float)gNumOscillators; andrewm@0: andrewm@0: for(int n = 0; n < gNumOscillators; n++) { andrewm@0: // Update the frequencies to a regular spread, plus a small amount of randomness andrewm@0: // to avoid weird phase effects andrewm@0: float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; andrewm@0: float newFreq = freq * randScale; andrewm@0: andrewm@0: // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians andrewm@0: gFrequencies[n] = newFreq * (float)gWavetableLength / gAudioSampleRate; andrewm@0: andrewm@0: freq += increment; andrewm@0: } andrewm@0: } andrewm@0: andrewm@0: andrewm@56: // cleanup() is called once at the end, after the audio has stopped. andrewm@56: // Release any resources that were allocated in setup(). andrewm@0: giuliomoro@301: void cleanup(BelaContext *context, void *userData) andrewm@0: { andrewm@0: free(gWavetable); andrewm@0: free(gPhases); andrewm@0: free(gFrequencies); andrewm@0: free(gAmplitudes); andrewm@0: free(gDFrequencies); andrewm@0: free(gDAmplitudes); andrewm@0: }