Dependencies: mbed USBDevice PinDetect
Diff: Audio/Synthesizer.cpp
- Revision:
- 9:1e012f67470c
- Child:
- 13:bb0ec927e458
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Audio/Synthesizer.cpp Wed Apr 13 19:46:28 2016 +0000 @@ -0,0 +1,250 @@ + +#include "Synthesizer.h" + +const float TWO_PI = 6.28318530717959; + +/** + * A function that acts as an individual synthesizer. + * It's job is to process the next sample at the current index + * based on the current state of the overall synthesizer. + * Output should be in the range of [-1.0, 1.0] and saved back into the buffer. + */ +typedef void (Synthesizer::*processorFunction)(); +const int numSynths = 4; + +// Array of pointers to all synthesizer functions. +processorFunction processors[] = { + &Synthesizer::processSine, + &Synthesizer::processTriangle, + &Synthesizer::processSquare, + &Synthesizer::processSaw, +}; + +int64_t Synthesizer::nextVoiceIndex = 0; + +Synthesizer::Synthesizer() { + bufferIndex = 0; + currentState = OFF; + nextBufferSize = -1; + voiceIndex = -1; + processorIndex = 0; +} + +void Synthesizer::midiNoteOn(int key, int velocity) { + float freq = 440.0 * std::pow(2.0, float(key - 69) / 12.0); + + // Make sure we are in bounds. + if (freq < C::MIN_FREQUENCY) { + #ifdef DEBUG + printf("Error: Note is below minimum frequency"); + #endif + return; + } + + currentKey = key; + int size = int((float(C::SAMPLE_RATE) / freq) + 0.5); + + if (currentState == OFF) { + // We can immediately play the new note. + bufferSize = size; + this->velocity = float(velocity) / 127.0; + + voiceIndex = Synthesizer::nextVoiceIndex; + ++Synthesizer::nextVoiceIndex; + fromDisabled = true; + currentState = FILL; + } else { + // Put the values in the queue for later. + nextBufferSize = size; + nextVelocity = float(velocity) / 127.0; + } +} + +void Synthesizer::midiNoteOff() { + // Begin release of current note. + currentState = RELEASE; + + // Clear the queue. + nextBufferSize = -1; +} + +float Synthesizer::getSample() { + if (currentState == FILL && fromDisabled) { + // The buffer may contain the release of the previous note so just return 0.0 + return 0.0; + } + + // Simply return the next sample. Do not increment. + return buffer[bufferIndex]; +} + +void Synthesizer::processBuffer() { + // Assert that we are playing. + if (currentState == OFF) { + #ifdef DEBUG + printf("Error: Attempting to process a disabled synthesizer."); + #endif + return; + } + + // Use the current processor to process the buffer. + (this->*processors[processorIndex])(); +} + +bool Synthesizer::isPlaying() { + return currentState != OFF; +} + +int64_t Synthesizer::getVoiceIndex() { + return voiceIndex; +} + +void Synthesizer::nextSynth(int direction) { + // Rotate to the next synth. + processorIndex += direction; + if (processorIndex >= numSynths) { + processorIndex -= numSynths; + } else if (processorIndex < 0) { + processorIndex += numSynths; + } + + // If we are playing, restart the buffer. + if (isPlaying()) { + fromDisabled = true; + bufferIndex = 0; + currentState = FILL; + } +} + +int Synthesizer::getCurrentKey() { + return currentKey; +} + +// Processor Functions. + +void Synthesizer::processSine() { + if (currentState == FILL) { + // Create the sine wave. + buffer[bufferIndex] = velocity * sin(TWO_PI * float(bufferIndex) / float(bufferSize)); + ++bufferIndex; + + // Check if we have reached the end of the buffer. + if (bufferIndex == bufferSize) { + bufferIndex = 0; + + // Check if there are new notes waiting in the queue. + if (nextBufferSize != -1) { + currentState = RELEASE; + } else { + currentState = SUSTAIN; + } + } + } else { + identityProcess(); + } +} + +void Synthesizer::processTriangle() { + if (currentState == FILL) { + // Create the triangle wave. + // Rotate pi/2 to remove phase offset. + int index = (bufferIndex + (bufferSize >> 2)) % bufferSize; + // Calculate linear function from -1 to 3 + int sample = -1.0 + (4.0 * float(index) / float(bufferSize)); + // Redirect second half of the line. + if (sample > 1.0) { + sample = -sample + 2.0; + } + buffer[bufferIndex] = velocity * sample; + ++bufferIndex; + + // Check if we have reached the end of the buffer. + if (bufferIndex == bufferSize) { + bufferIndex = 0; + + // Check if there are new notes waiting in the queue. + if (nextBufferSize != -1) { + currentState = RELEASE; + } else { + currentState = SUSTAIN; + } + } + } else { + identityProcess(); + } +} + +void Synthesizer::processSquare() { + if (currentState == FILL) { + // Create the square wave. + buffer[bufferIndex] = bufferIndex < (bufferSize >> 1) ? -velocity : velocity; + ++bufferIndex; + + // Check if we have reached the end of the buffer. + if (bufferIndex == bufferSize) { + bufferIndex = 0; + + // Check if there are new notes waiting in the queue. + if (nextBufferSize != -1) { + currentState = RELEASE; + } else { + currentState = SUSTAIN; + } + } + } else { + identityProcess(); + } +} + +void Synthesizer::processSaw() { + if (currentState == FILL) { + // Create the saw wave. + buffer[bufferIndex] = velocity * (1.0 - 2.0 * float(bufferIndex) / float(bufferSize)); + ++bufferIndex; + + // Check if we have reached the end of the buffer. + if (bufferIndex == bufferSize) { + bufferIndex = 0; + + // Check if there are new notes waiting in the queue. + if (nextBufferSize != -1) { + currentState = RELEASE; + } else { + currentState = SUSTAIN; + } + } + } else { + identityProcess(); + } +} + +void Synthesizer::identityProcess() { + if (currentState == RELEASE) { + // Attenuate the current buffer. + buffer[bufferIndex] *= -(float(bufferIndex) / float(bufferSize)) + 1.0; + } + + ++bufferIndex; + + // Check if we have reached the end of the buffer. + if (bufferIndex == bufferSize) { + bufferIndex = 0; + + // Check the queue to see if we have a new note waiting. + if (nextBufferSize != -1) { + if (currentState == SUSTAIN) { + currentState = RELEASE; + } else { + bufferSize = nextBufferSize; + velocity = nextVelocity; + nextBufferSize = -1; + fromDisabled = false; + currentState = FILL; + } + } else if (currentState == RELEASE) { + // Disable the synthesizer. + currentState = OFF; + voiceIndex = -1; + } + } +} \ No newline at end of file