Austin Suszek / Mbed 2 deprecated MIDISynthesizer

Dependencies:   mbed USBDevice PinDetect

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Synthesizer.cpp Source File

Synthesizer.cpp

00001 
00002 #include "Synthesizer.h"
00003 
00004 const float TWO_PI = 6.28318530717959;
00005 
00006 /**
00007  * A function that acts as an individual synthesizer.
00008  * It's job is to process the next sample at the current index
00009  *   based on the current state of the overall synthesizer.
00010  * Output should be in the range of [-1.0, 1.0] and saved back into the buffer.
00011  */
00012 typedef void (Synthesizer::*processorFunction)();
00013 const int numSynths = 5;
00014 
00015 // Array of pointers to all synthesizer functions.
00016 processorFunction processors[] = {
00017         &Synthesizer::processKarplusStrong,
00018         &Synthesizer::processSine,
00019         &Synthesizer::processTriangle,
00020         &Synthesizer::processSquare,
00021         &Synthesizer::processSaw,
00022 };
00023 
00024 int64_t Synthesizer::nextVoiceIndex = 0;
00025 
00026 Synthesizer::Synthesizer() {
00027     bufferIndex = 0;
00028     currentState = OFF;
00029     nextBufferSize = -1;
00030     voiceIndex = -1;
00031     processorIndex = 0;
00032 }
00033 
00034 void Synthesizer::midiNoteOn(int key, int velocity) {
00035     float freq = 440.0 * std::pow(2.0, float(key - 69) / 12.0);
00036     float velocityFloat = float(velocity) / 127.0;
00037     
00038     // Make sure we are in bounds.
00039     if (freq < C::MIN_FREQUENCY) {
00040         #ifdef DEBUG
00041         printf("Error: Note is below minimum frequency");
00042         #endif
00043         return;   
00044     }
00045     
00046     currentKey = key;
00047     int size = int((float(C::SAMPLE_RATE) / freq) + 0.5);
00048     
00049     if (currentState == OFF) {
00050         // We can immediately play the new note.
00051         bufferSize = size;
00052         this->velocity = velocityFloat;
00053         
00054         voiceIndex = Synthesizer::nextVoiceIndex;
00055         ++Synthesizer::nextVoiceIndex;
00056         fromDisabled = true;
00057         currentState = FILL;
00058     } else {
00059          // Put the values in the queue for later.
00060          nextBufferSize = size;  
00061          nextVelocity = velocityFloat;
00062     } 
00063     
00064     // Signal the Karplus Strong processor to calculate a new note.
00065     karplusStrong.midiNoteOn(key, velocityFloat);
00066 }
00067 
00068 void Synthesizer::midiNoteOff() {
00069     // Begin release of current note.
00070     currentState = RELEASE;
00071     
00072     // Clear the queue.
00073     nextBufferSize = -1; 
00074 }
00075 
00076 float Synthesizer::getSample() {
00077     float outputSample;
00078     if (currentState == FILL && fromDisabled) {
00079         // The buffer may contain the release of the previous note so just return 0.0
00080         outputSample = 0.0; 
00081     } else {
00082         // Simply return the next sample. Do not increment.
00083         outputSample = buffer[bufferIndex]; 
00084     }
00085     
00086     // Process the next period.
00087     // Use the current processor to process the buffer.
00088     (this->*processors[processorIndex])();
00089     
00090     // Return the sample.
00091     return outputSample;
00092 }
00093 
00094 bool Synthesizer::isPlaying() {
00095     return currentState != OFF;
00096 }
00097 
00098 int64_t Synthesizer::getVoiceIndex() {
00099     return voiceIndex;   
00100 }
00101 
00102 int Synthesizer::nextSynth(int direction) {
00103     // Rotate to the next synth.
00104     processorIndex += direction;
00105     if (processorIndex >= numSynths) {
00106         processorIndex -= numSynths;
00107     } else if (processorIndex < 0) {
00108         processorIndex += numSynths;
00109     }
00110     
00111     // If we are playing, restart the buffer.
00112     if (isPlaying()) {
00113         fromDisabled = true;
00114         bufferIndex = 0;
00115         currentState = FILL;  
00116     }
00117     
00118     return processorIndex;
00119 }
00120 
00121 int Synthesizer::getCurrentKey() {
00122     return currentKey;   
00123 }
00124 
00125 // Processor Functions.
00126 
00127 void Synthesizer::processKarplusStrong() {
00128     if (currentState == FILL) {
00129         buffer[bufferIndex] = karplusStrong.fillBuffer(bufferIndex);
00130         ++bufferIndex;
00131         
00132         checkFillBounds();
00133     } else {
00134        // Process for sustain or release.
00135        float inputSample = buffer[bufferIndex];
00136        buffer[bufferIndex] = karplusStrong.processBuffer(inputSample);
00137        // Check transition logic.
00138        identityProcess(); 
00139     }  
00140 }
00141 
00142 void Synthesizer::processSine() {
00143     if (currentState == FILL) {
00144         // Create the sine wave.
00145         buffer[bufferIndex] = velocity * sin(TWO_PI * float(bufferIndex) / float(bufferSize));
00146         ++bufferIndex;
00147         
00148         checkFillBounds();
00149     } else {
00150         identityProcess();    
00151     }
00152 }
00153 
00154 void Synthesizer::processTriangle() {
00155     if (currentState == FILL) {
00156         // Create the triangle wave.
00157         // Rotate pi/2 to remove phase offset.
00158         int index = (bufferIndex + (bufferSize >> 2)) % bufferSize;
00159         // Calculate linear function from -1 to 3
00160         int sample = -1.0 + (4.0 * float(index) / float(bufferSize));
00161         // Redirect second half of the line.
00162         if (sample > 1.0) {
00163             sample = -sample + 2.0;   
00164         }
00165         buffer[bufferIndex] = velocity * sample;
00166         ++bufferIndex;
00167         
00168         checkFillBounds();
00169     } else {
00170         identityProcess();    
00171     }
00172 }
00173 
00174 void Synthesizer::processSquare() {
00175     if (currentState == FILL) {
00176         // Create the square wave.
00177         buffer[bufferIndex] = bufferIndex < (bufferSize >> 1) ? -velocity : velocity;
00178         ++bufferIndex;
00179         
00180         checkFillBounds();
00181     } else {
00182         identityProcess();    
00183     }
00184 }
00185 
00186 void Synthesizer::processSaw() {
00187     if (currentState == FILL) {
00188         // Create the saw wave.
00189         buffer[bufferIndex] = velocity * (1.0 - 2.0 * float(bufferIndex) / float(bufferSize));
00190         ++bufferIndex;
00191         
00192         checkFillBounds();
00193     } else {
00194         identityProcess();    
00195     }
00196 }
00197 
00198 void Synthesizer::identityProcess() {
00199     if (currentState == RELEASE) {
00200         // Attenuate the current buffer.
00201         buffer[bufferIndex] *= -(float(bufferIndex) / float(bufferSize)) + 1.0;
00202     }
00203     
00204     ++bufferIndex;
00205         
00206     // Check if we have reached the end of the buffer.
00207     if (bufferIndex == bufferSize) {
00208         bufferIndex = 0;
00209         
00210         // Check the queue to see if we have a new note waiting.
00211         if (nextBufferSize != -1) {
00212             if (currentState == SUSTAIN) {
00213                 currentState = RELEASE;   
00214             } else {
00215                 bufferSize = nextBufferSize;
00216                 velocity = nextVelocity;
00217                 nextBufferSize = -1;
00218                 fromDisabled = false;
00219                 currentState = FILL;
00220             }
00221         } else if (currentState == RELEASE) {
00222             // Disable the synthesizer.
00223             currentState = OFF;
00224             voiceIndex = -1;
00225         }
00226     }
00227 }
00228 
00229 void Synthesizer::checkFillBounds() {
00230     // Check if we have reached the end of the buffer.
00231     if (bufferIndex == bufferSize) {
00232         bufferIndex = 0;
00233         
00234         // Check if there are new notes waiting in the queue.
00235         if (nextBufferSize != -1) {
00236             currentState = RELEASE;
00237         } else {
00238             currentState = SUSTAIN;
00239         }   
00240     }    
00241 }