A MIDI piano synthesizer that implements the Karplus Strong physical modeling algorithm.

Dependencies:   mbed USBDevice PinDetect

Committer:
asuszek
Date:
Mon Apr 25 23:58:15 2016 +0000
Revision:
22:b800e1766647
Parent:
18:26d93c5b9bb6
Fixed LED bug

Who changed what in which revision?

UserRevisionLine numberNew contents of line
asuszek 9:1e012f67470c 1
asuszek 9:1e012f67470c 2 #include "Synthesizer.h"
asuszek 9:1e012f67470c 3
asuszek 9:1e012f67470c 4 const float TWO_PI = 6.28318530717959;
asuszek 9:1e012f67470c 5
asuszek 9:1e012f67470c 6 /**
asuszek 9:1e012f67470c 7 * A function that acts as an individual synthesizer.
asuszek 9:1e012f67470c 8 * It's job is to process the next sample at the current index
asuszek 9:1e012f67470c 9 * based on the current state of the overall synthesizer.
asuszek 9:1e012f67470c 10 * Output should be in the range of [-1.0, 1.0] and saved back into the buffer.
asuszek 9:1e012f67470c 11 */
asuszek 9:1e012f67470c 12 typedef void (Synthesizer::*processorFunction)();
asuszek 13:bb0ec927e458 13 const int numSynths = 5;
asuszek 9:1e012f67470c 14
asuszek 9:1e012f67470c 15 // Array of pointers to all synthesizer functions.
asuszek 9:1e012f67470c 16 processorFunction processors[] = {
asuszek 13:bb0ec927e458 17 &Synthesizer::processKarplusStrong,
asuszek 9:1e012f67470c 18 &Synthesizer::processSine,
asuszek 9:1e012f67470c 19 &Synthesizer::processTriangle,
asuszek 9:1e012f67470c 20 &Synthesizer::processSquare,
asuszek 9:1e012f67470c 21 &Synthesizer::processSaw,
asuszek 9:1e012f67470c 22 };
asuszek 9:1e012f67470c 23
asuszek 9:1e012f67470c 24 int64_t Synthesizer::nextVoiceIndex = 0;
asuszek 9:1e012f67470c 25
asuszek 9:1e012f67470c 26 Synthesizer::Synthesizer() {
asuszek 9:1e012f67470c 27 bufferIndex = 0;
asuszek 9:1e012f67470c 28 currentState = OFF;
asuszek 9:1e012f67470c 29 nextBufferSize = -1;
asuszek 9:1e012f67470c 30 voiceIndex = -1;
asuszek 9:1e012f67470c 31 processorIndex = 0;
asuszek 9:1e012f67470c 32 }
asuszek 9:1e012f67470c 33
asuszek 9:1e012f67470c 34 void Synthesizer::midiNoteOn(int key, int velocity) {
asuszek 9:1e012f67470c 35 float freq = 440.0 * std::pow(2.0, float(key - 69) / 12.0);
asuszek 13:bb0ec927e458 36 float velocityFloat = float(velocity) / 127.0;
asuszek 9:1e012f67470c 37
asuszek 9:1e012f67470c 38 // Make sure we are in bounds.
asuszek 9:1e012f67470c 39 if (freq < C::MIN_FREQUENCY) {
asuszek 9:1e012f67470c 40 #ifdef DEBUG
asuszek 9:1e012f67470c 41 printf("Error: Note is below minimum frequency");
asuszek 9:1e012f67470c 42 #endif
asuszek 9:1e012f67470c 43 return;
asuszek 9:1e012f67470c 44 }
asuszek 9:1e012f67470c 45
asuszek 9:1e012f67470c 46 currentKey = key;
asuszek 9:1e012f67470c 47 int size = int((float(C::SAMPLE_RATE) / freq) + 0.5);
asuszek 9:1e012f67470c 48
asuszek 9:1e012f67470c 49 if (currentState == OFF) {
asuszek 9:1e012f67470c 50 // We can immediately play the new note.
asuszek 9:1e012f67470c 51 bufferSize = size;
asuszek 13:bb0ec927e458 52 this->velocity = velocityFloat;
asuszek 9:1e012f67470c 53
asuszek 9:1e012f67470c 54 voiceIndex = Synthesizer::nextVoiceIndex;
asuszek 9:1e012f67470c 55 ++Synthesizer::nextVoiceIndex;
asuszek 9:1e012f67470c 56 fromDisabled = true;
asuszek 9:1e012f67470c 57 currentState = FILL;
asuszek 9:1e012f67470c 58 } else {
asuszek 9:1e012f67470c 59 // Put the values in the queue for later.
asuszek 9:1e012f67470c 60 nextBufferSize = size;
asuszek 13:bb0ec927e458 61 nextVelocity = velocityFloat;
asuszek 9:1e012f67470c 62 }
asuszek 13:bb0ec927e458 63
asuszek 13:bb0ec927e458 64 // Signal the Karplus Strong processor to calculate a new note.
asuszek 13:bb0ec927e458 65 karplusStrong.midiNoteOn(key, velocityFloat);
asuszek 9:1e012f67470c 66 }
asuszek 9:1e012f67470c 67
asuszek 9:1e012f67470c 68 void Synthesizer::midiNoteOff() {
asuszek 9:1e012f67470c 69 // Begin release of current note.
asuszek 9:1e012f67470c 70 currentState = RELEASE;
asuszek 9:1e012f67470c 71
asuszek 9:1e012f67470c 72 // Clear the queue.
asuszek 9:1e012f67470c 73 nextBufferSize = -1;
asuszek 9:1e012f67470c 74 }
asuszek 9:1e012f67470c 75
asuszek 9:1e012f67470c 76 float Synthesizer::getSample() {
asuszek 13:bb0ec927e458 77 float outputSample;
asuszek 9:1e012f67470c 78 if (currentState == FILL && fromDisabled) {
asuszek 9:1e012f67470c 79 // The buffer may contain the release of the previous note so just return 0.0
asuszek 13:bb0ec927e458 80 outputSample = 0.0;
asuszek 13:bb0ec927e458 81 } else {
asuszek 13:bb0ec927e458 82 // Simply return the next sample. Do not increment.
asuszek 13:bb0ec927e458 83 outputSample = buffer[bufferIndex];
asuszek 9:1e012f67470c 84 }
asuszek 9:1e012f67470c 85
asuszek 13:bb0ec927e458 86 // Process the next period.
asuszek 9:1e012f67470c 87 // Use the current processor to process the buffer.
asuszek 9:1e012f67470c 88 (this->*processors[processorIndex])();
asuszek 13:bb0ec927e458 89
asuszek 13:bb0ec927e458 90 // Return the sample.
asuszek 13:bb0ec927e458 91 return outputSample;
asuszek 9:1e012f67470c 92 }
asuszek 9:1e012f67470c 93
asuszek 9:1e012f67470c 94 bool Synthesizer::isPlaying() {
asuszek 9:1e012f67470c 95 return currentState != OFF;
asuszek 9:1e012f67470c 96 }
asuszek 9:1e012f67470c 97
asuszek 9:1e012f67470c 98 int64_t Synthesizer::getVoiceIndex() {
asuszek 9:1e012f67470c 99 return voiceIndex;
asuszek 9:1e012f67470c 100 }
asuszek 9:1e012f67470c 101
asuszek 18:26d93c5b9bb6 102 int Synthesizer::nextSynth(int direction) {
asuszek 9:1e012f67470c 103 // Rotate to the next synth.
asuszek 9:1e012f67470c 104 processorIndex += direction;
asuszek 9:1e012f67470c 105 if (processorIndex >= numSynths) {
asuszek 9:1e012f67470c 106 processorIndex -= numSynths;
asuszek 9:1e012f67470c 107 } else if (processorIndex < 0) {
asuszek 9:1e012f67470c 108 processorIndex += numSynths;
asuszek 9:1e012f67470c 109 }
asuszek 9:1e012f67470c 110
asuszek 9:1e012f67470c 111 // If we are playing, restart the buffer.
asuszek 9:1e012f67470c 112 if (isPlaying()) {
asuszek 9:1e012f67470c 113 fromDisabled = true;
asuszek 9:1e012f67470c 114 bufferIndex = 0;
asuszek 9:1e012f67470c 115 currentState = FILL;
asuszek 9:1e012f67470c 116 }
asuszek 18:26d93c5b9bb6 117
asuszek 18:26d93c5b9bb6 118 return processorIndex;
asuszek 9:1e012f67470c 119 }
asuszek 9:1e012f67470c 120
asuszek 9:1e012f67470c 121 int Synthesizer::getCurrentKey() {
asuszek 9:1e012f67470c 122 return currentKey;
asuszek 9:1e012f67470c 123 }
asuszek 9:1e012f67470c 124
asuszek 9:1e012f67470c 125 // Processor Functions.
asuszek 9:1e012f67470c 126
asuszek 13:bb0ec927e458 127 void Synthesizer::processKarplusStrong() {
asuszek 13:bb0ec927e458 128 if (currentState == FILL) {
asuszek 13:bb0ec927e458 129 buffer[bufferIndex] = karplusStrong.fillBuffer(bufferIndex);
asuszek 13:bb0ec927e458 130 ++bufferIndex;
asuszek 13:bb0ec927e458 131
asuszek 13:bb0ec927e458 132 checkFillBounds();
asuszek 13:bb0ec927e458 133 } else {
asuszek 13:bb0ec927e458 134 // Process for sustain or release.
asuszek 13:bb0ec927e458 135 float inputSample = buffer[bufferIndex];
asuszek 13:bb0ec927e458 136 buffer[bufferIndex] = karplusStrong.processBuffer(inputSample);
asuszek 13:bb0ec927e458 137 // Check transition logic.
asuszek 13:bb0ec927e458 138 identityProcess();
asuszek 13:bb0ec927e458 139 }
asuszek 13:bb0ec927e458 140 }
asuszek 13:bb0ec927e458 141
asuszek 9:1e012f67470c 142 void Synthesizer::processSine() {
asuszek 9:1e012f67470c 143 if (currentState == FILL) {
asuszek 9:1e012f67470c 144 // Create the sine wave.
asuszek 9:1e012f67470c 145 buffer[bufferIndex] = velocity * sin(TWO_PI * float(bufferIndex) / float(bufferSize));
asuszek 9:1e012f67470c 146 ++bufferIndex;
asuszek 9:1e012f67470c 147
asuszek 13:bb0ec927e458 148 checkFillBounds();
asuszek 9:1e012f67470c 149 } else {
asuszek 9:1e012f67470c 150 identityProcess();
asuszek 9:1e012f67470c 151 }
asuszek 9:1e012f67470c 152 }
asuszek 9:1e012f67470c 153
asuszek 9:1e012f67470c 154 void Synthesizer::processTriangle() {
asuszek 9:1e012f67470c 155 if (currentState == FILL) {
asuszek 9:1e012f67470c 156 // Create the triangle wave.
asuszek 9:1e012f67470c 157 // Rotate pi/2 to remove phase offset.
asuszek 9:1e012f67470c 158 int index = (bufferIndex + (bufferSize >> 2)) % bufferSize;
asuszek 9:1e012f67470c 159 // Calculate linear function from -1 to 3
asuszek 9:1e012f67470c 160 int sample = -1.0 + (4.0 * float(index) / float(bufferSize));
asuszek 9:1e012f67470c 161 // Redirect second half of the line.
asuszek 9:1e012f67470c 162 if (sample > 1.0) {
asuszek 9:1e012f67470c 163 sample = -sample + 2.0;
asuszek 9:1e012f67470c 164 }
asuszek 9:1e012f67470c 165 buffer[bufferIndex] = velocity * sample;
asuszek 9:1e012f67470c 166 ++bufferIndex;
asuszek 9:1e012f67470c 167
asuszek 13:bb0ec927e458 168 checkFillBounds();
asuszek 9:1e012f67470c 169 } else {
asuszek 9:1e012f67470c 170 identityProcess();
asuszek 9:1e012f67470c 171 }
asuszek 9:1e012f67470c 172 }
asuszek 9:1e012f67470c 173
asuszek 9:1e012f67470c 174 void Synthesizer::processSquare() {
asuszek 9:1e012f67470c 175 if (currentState == FILL) {
asuszek 9:1e012f67470c 176 // Create the square wave.
asuszek 9:1e012f67470c 177 buffer[bufferIndex] = bufferIndex < (bufferSize >> 1) ? -velocity : velocity;
asuszek 9:1e012f67470c 178 ++bufferIndex;
asuszek 9:1e012f67470c 179
asuszek 13:bb0ec927e458 180 checkFillBounds();
asuszek 9:1e012f67470c 181 } else {
asuszek 9:1e012f67470c 182 identityProcess();
asuszek 9:1e012f67470c 183 }
asuszek 9:1e012f67470c 184 }
asuszek 9:1e012f67470c 185
asuszek 9:1e012f67470c 186 void Synthesizer::processSaw() {
asuszek 9:1e012f67470c 187 if (currentState == FILL) {
asuszek 9:1e012f67470c 188 // Create the saw wave.
asuszek 9:1e012f67470c 189 buffer[bufferIndex] = velocity * (1.0 - 2.0 * float(bufferIndex) / float(bufferSize));
asuszek 9:1e012f67470c 190 ++bufferIndex;
asuszek 9:1e012f67470c 191
asuszek 13:bb0ec927e458 192 checkFillBounds();
asuszek 9:1e012f67470c 193 } else {
asuszek 9:1e012f67470c 194 identityProcess();
asuszek 9:1e012f67470c 195 }
asuszek 9:1e012f67470c 196 }
asuszek 9:1e012f67470c 197
asuszek 9:1e012f67470c 198 void Synthesizer::identityProcess() {
asuszek 9:1e012f67470c 199 if (currentState == RELEASE) {
asuszek 9:1e012f67470c 200 // Attenuate the current buffer.
asuszek 9:1e012f67470c 201 buffer[bufferIndex] *= -(float(bufferIndex) / float(bufferSize)) + 1.0;
asuszek 9:1e012f67470c 202 }
asuszek 9:1e012f67470c 203
asuszek 9:1e012f67470c 204 ++bufferIndex;
asuszek 9:1e012f67470c 205
asuszek 9:1e012f67470c 206 // Check if we have reached the end of the buffer.
asuszek 9:1e012f67470c 207 if (bufferIndex == bufferSize) {
asuszek 9:1e012f67470c 208 bufferIndex = 0;
asuszek 9:1e012f67470c 209
asuszek 9:1e012f67470c 210 // Check the queue to see if we have a new note waiting.
asuszek 9:1e012f67470c 211 if (nextBufferSize != -1) {
asuszek 9:1e012f67470c 212 if (currentState == SUSTAIN) {
asuszek 9:1e012f67470c 213 currentState = RELEASE;
asuszek 9:1e012f67470c 214 } else {
asuszek 9:1e012f67470c 215 bufferSize = nextBufferSize;
asuszek 9:1e012f67470c 216 velocity = nextVelocity;
asuszek 9:1e012f67470c 217 nextBufferSize = -1;
asuszek 9:1e012f67470c 218 fromDisabled = false;
asuszek 9:1e012f67470c 219 currentState = FILL;
asuszek 9:1e012f67470c 220 }
asuszek 9:1e012f67470c 221 } else if (currentState == RELEASE) {
asuszek 9:1e012f67470c 222 // Disable the synthesizer.
asuszek 9:1e012f67470c 223 currentState = OFF;
asuszek 9:1e012f67470c 224 voiceIndex = -1;
asuszek 9:1e012f67470c 225 }
asuszek 9:1e012f67470c 226 }
asuszek 13:bb0ec927e458 227 }
asuszek 13:bb0ec927e458 228
asuszek 13:bb0ec927e458 229 void Synthesizer::checkFillBounds() {
asuszek 13:bb0ec927e458 230 // Check if we have reached the end of the buffer.
asuszek 13:bb0ec927e458 231 if (bufferIndex == bufferSize) {
asuszek 13:bb0ec927e458 232 bufferIndex = 0;
asuszek 13:bb0ec927e458 233
asuszek 13:bb0ec927e458 234 // Check if there are new notes waiting in the queue.
asuszek 13:bb0ec927e458 235 if (nextBufferSize != -1) {
asuszek 13:bb0ec927e458 236 currentState = RELEASE;
asuszek 13:bb0ec927e458 237 } else {
asuszek 13:bb0ec927e458 238 currentState = SUSTAIN;
asuszek 13:bb0ec927e458 239 }
asuszek 13:bb0ec927e458 240 }
asuszek 9:1e012f67470c 241 }