Dependencies:   mbed USBDevice PinDetect

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