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: andrewm@0: #include "../../include/RTAudio.h" andrewm@0: #include "../../include/Utilities.h" 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@0: // to passing a structure to userData in initialise_render() 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@0: // initialise_render() 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. andrewm@0: andrewm@0: bool initialise_render(int numChannels, int numMatrixFramesPerPeriod, andrewm@0: int numAudioFramesPerPeriod, float matrixSampleRate, andrewm@0: float audioSampleRate, void *userData) andrewm@0: { andrewm@0: srandom(time(NULL)); andrewm@0: 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@0: if(numMatrixFramesPerPeriod == 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@0: gFrequencies[n] *= (float)gWavetableLength / 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@0: // Initialise auxiliary tasks andrewm@0: if((gFrequencyUpdateTask = createAuxiliaryTaskLoop(&recalculate_frequencies, 90, "beaglert-update-frequencies")) == 0) andrewm@0: return false; andrewm@0: andrewm@0: gAudioSampleRate = 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: andrewm@0: void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut, andrewm@0: uint16_t *matrixIn, uint16_t *matrixOut) andrewm@0: { andrewm@0: // Initialise buffer to 0 andrewm@0: memset(audioOut, 0, 2 * numAudioFrames * sizeof(float)); andrewm@0: andrewm@0: // Render audio frames andrewm@0: oscillator_bank_neon(numAudioFrames, audioOut, andrewm@0: gNumOscillators, gWavetableLength, andrewm@0: gPhases, gFrequencies, gAmplitudes, andrewm@0: gDFrequencies, gDAmplitudes, andrewm@0: gWavetable); andrewm@0: andrewm@0: if(numMatrixFrames != 0 && (gSampleCount += numAudioFrames) >= 128) { andrewm@0: gSampleCount = 0; andrewm@0: gNewMinFrequency = map(matrixIn[0], 0, MATRIX_MAX, 20.0f, 8000.0f); andrewm@0: gNewMaxFrequency = map(matrixIn[1], 0, MATRIX_MAX, 20.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 andrewm@0: 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@0: // cleanup_render() is called once at the end, after the audio has stopped. andrewm@0: // Release any resources that were allocated in initialise_render(). andrewm@0: andrewm@0: void cleanup_render() 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: }