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

Dependencies:   mbed USBDevice PinDetect

Committer:
asuszek
Date:
Wed Apr 13 19:46:28 2016 +0000
Revision:
9:1e012f67470c
Child:
13:bb0ec927e458
Moved Audio into it's own folder

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 9:1e012f67470c 13 const int numSynths = 4;
asuszek 9:1e012f67470c 14
asuszek 9:1e012f67470c 15 // Array of pointers to all synthesizer functions.
asuszek 9:1e012f67470c 16 processorFunction processors[] = {
asuszek 9:1e012f67470c 17 &Synthesizer::processSine,
asuszek 9:1e012f67470c 18 &Synthesizer::processTriangle,
asuszek 9:1e012f67470c 19 &Synthesizer::processSquare,
asuszek 9:1e012f67470c 20 &Synthesizer::processSaw,
asuszek 9:1e012f67470c 21 };
asuszek 9:1e012f67470c 22
asuszek 9:1e012f67470c 23 int64_t Synthesizer::nextVoiceIndex = 0;
asuszek 9:1e012f67470c 24
asuszek 9:1e012f67470c 25 Synthesizer::Synthesizer() {
asuszek 9:1e012f67470c 26 bufferIndex = 0;
asuszek 9:1e012f67470c 27 currentState = OFF;
asuszek 9:1e012f67470c 28 nextBufferSize = -1;
asuszek 9:1e012f67470c 29 voiceIndex = -1;
asuszek 9:1e012f67470c 30 processorIndex = 0;
asuszek 9:1e012f67470c 31 }
asuszek 9:1e012f67470c 32
asuszek 9:1e012f67470c 33 void Synthesizer::midiNoteOn(int key, int velocity) {
asuszek 9:1e012f67470c 34 float freq = 440.0 * std::pow(2.0, float(key - 69) / 12.0);
asuszek 9:1e012f67470c 35
asuszek 9:1e012f67470c 36 // Make sure we are in bounds.
asuszek 9:1e012f67470c 37 if (freq < C::MIN_FREQUENCY) {
asuszek 9:1e012f67470c 38 #ifdef DEBUG
asuszek 9:1e012f67470c 39 printf("Error: Note is below minimum frequency");
asuszek 9:1e012f67470c 40 #endif
asuszek 9:1e012f67470c 41 return;
asuszek 9:1e012f67470c 42 }
asuszek 9:1e012f67470c 43
asuszek 9:1e012f67470c 44 currentKey = key;
asuszek 9:1e012f67470c 45 int size = int((float(C::SAMPLE_RATE) / freq) + 0.5);
asuszek 9:1e012f67470c 46
asuszek 9:1e012f67470c 47 if (currentState == OFF) {
asuszek 9:1e012f67470c 48 // We can immediately play the new note.
asuszek 9:1e012f67470c 49 bufferSize = size;
asuszek 9:1e012f67470c 50 this->velocity = float(velocity) / 127.0;
asuszek 9:1e012f67470c 51
asuszek 9:1e012f67470c 52 voiceIndex = Synthesizer::nextVoiceIndex;
asuszek 9:1e012f67470c 53 ++Synthesizer::nextVoiceIndex;
asuszek 9:1e012f67470c 54 fromDisabled = true;
asuszek 9:1e012f67470c 55 currentState = FILL;
asuszek 9:1e012f67470c 56 } else {
asuszek 9:1e012f67470c 57 // Put the values in the queue for later.
asuszek 9:1e012f67470c 58 nextBufferSize = size;
asuszek 9:1e012f67470c 59 nextVelocity = float(velocity) / 127.0;
asuszek 9:1e012f67470c 60 }
asuszek 9:1e012f67470c 61 }
asuszek 9:1e012f67470c 62
asuszek 9:1e012f67470c 63 void Synthesizer::midiNoteOff() {
asuszek 9:1e012f67470c 64 // Begin release of current note.
asuszek 9:1e012f67470c 65 currentState = RELEASE;
asuszek 9:1e012f67470c 66
asuszek 9:1e012f67470c 67 // Clear the queue.
asuszek 9:1e012f67470c 68 nextBufferSize = -1;
asuszek 9:1e012f67470c 69 }
asuszek 9:1e012f67470c 70
asuszek 9:1e012f67470c 71 float Synthesizer::getSample() {
asuszek 9:1e012f67470c 72 if (currentState == FILL && fromDisabled) {
asuszek 9:1e012f67470c 73 // The buffer may contain the release of the previous note so just return 0.0
asuszek 9:1e012f67470c 74 return 0.0;
asuszek 9:1e012f67470c 75 }
asuszek 9:1e012f67470c 76
asuszek 9:1e012f67470c 77 // Simply return the next sample. Do not increment.
asuszek 9:1e012f67470c 78 return buffer[bufferIndex];
asuszek 9:1e012f67470c 79 }
asuszek 9:1e012f67470c 80
asuszek 9:1e012f67470c 81 void Synthesizer::processBuffer() {
asuszek 9:1e012f67470c 82 // Assert that we are playing.
asuszek 9:1e012f67470c 83 if (currentState == OFF) {
asuszek 9:1e012f67470c 84 #ifdef DEBUG
asuszek 9:1e012f67470c 85 printf("Error: Attempting to process a disabled synthesizer.");
asuszek 9:1e012f67470c 86 #endif
asuszek 9:1e012f67470c 87 return;
asuszek 9:1e012f67470c 88 }
asuszek 9:1e012f67470c 89
asuszek 9:1e012f67470c 90 // Use the current processor to process the buffer.
asuszek 9:1e012f67470c 91 (this->*processors[processorIndex])();
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 9:1e012f67470c 102 void 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 9:1e012f67470c 117 }
asuszek 9:1e012f67470c 118
asuszek 9:1e012f67470c 119 int Synthesizer::getCurrentKey() {
asuszek 9:1e012f67470c 120 return currentKey;
asuszek 9:1e012f67470c 121 }
asuszek 9:1e012f67470c 122
asuszek 9:1e012f67470c 123 // Processor Functions.
asuszek 9:1e012f67470c 124
asuszek 9:1e012f67470c 125 void Synthesizer::processSine() {
asuszek 9:1e012f67470c 126 if (currentState == FILL) {
asuszek 9:1e012f67470c 127 // Create the sine wave.
asuszek 9:1e012f67470c 128 buffer[bufferIndex] = velocity * sin(TWO_PI * float(bufferIndex) / float(bufferSize));
asuszek 9:1e012f67470c 129 ++bufferIndex;
asuszek 9:1e012f67470c 130
asuszek 9:1e012f67470c 131 // Check if we have reached the end of the buffer.
asuszek 9:1e012f67470c 132 if (bufferIndex == bufferSize) {
asuszek 9:1e012f67470c 133 bufferIndex = 0;
asuszek 9:1e012f67470c 134
asuszek 9:1e012f67470c 135 // Check if there are new notes waiting in the queue.
asuszek 9:1e012f67470c 136 if (nextBufferSize != -1) {
asuszek 9:1e012f67470c 137 currentState = RELEASE;
asuszek 9:1e012f67470c 138 } else {
asuszek 9:1e012f67470c 139 currentState = SUSTAIN;
asuszek 9:1e012f67470c 140 }
asuszek 9:1e012f67470c 141 }
asuszek 9:1e012f67470c 142 } else {
asuszek 9:1e012f67470c 143 identityProcess();
asuszek 9:1e012f67470c 144 }
asuszek 9:1e012f67470c 145 }
asuszek 9:1e012f67470c 146
asuszek 9:1e012f67470c 147 void Synthesizer::processTriangle() {
asuszek 9:1e012f67470c 148 if (currentState == FILL) {
asuszek 9:1e012f67470c 149 // Create the triangle wave.
asuszek 9:1e012f67470c 150 // Rotate pi/2 to remove phase offset.
asuszek 9:1e012f67470c 151 int index = (bufferIndex + (bufferSize >> 2)) % bufferSize;
asuszek 9:1e012f67470c 152 // Calculate linear function from -1 to 3
asuszek 9:1e012f67470c 153 int sample = -1.0 + (4.0 * float(index) / float(bufferSize));
asuszek 9:1e012f67470c 154 // Redirect second half of the line.
asuszek 9:1e012f67470c 155 if (sample > 1.0) {
asuszek 9:1e012f67470c 156 sample = -sample + 2.0;
asuszek 9:1e012f67470c 157 }
asuszek 9:1e012f67470c 158 buffer[bufferIndex] = velocity * sample;
asuszek 9:1e012f67470c 159 ++bufferIndex;
asuszek 9:1e012f67470c 160
asuszek 9:1e012f67470c 161 // Check if we have reached the end of the buffer.
asuszek 9:1e012f67470c 162 if (bufferIndex == bufferSize) {
asuszek 9:1e012f67470c 163 bufferIndex = 0;
asuszek 9:1e012f67470c 164
asuszek 9:1e012f67470c 165 // Check if there are new notes waiting in the queue.
asuszek 9:1e012f67470c 166 if (nextBufferSize != -1) {
asuszek 9:1e012f67470c 167 currentState = RELEASE;
asuszek 9:1e012f67470c 168 } else {
asuszek 9:1e012f67470c 169 currentState = SUSTAIN;
asuszek 9:1e012f67470c 170 }
asuszek 9:1e012f67470c 171 }
asuszek 9:1e012f67470c 172 } else {
asuszek 9:1e012f67470c 173 identityProcess();
asuszek 9:1e012f67470c 174 }
asuszek 9:1e012f67470c 175 }
asuszek 9:1e012f67470c 176
asuszek 9:1e012f67470c 177 void Synthesizer::processSquare() {
asuszek 9:1e012f67470c 178 if (currentState == FILL) {
asuszek 9:1e012f67470c 179 // Create the square wave.
asuszek 9:1e012f67470c 180 buffer[bufferIndex] = bufferIndex < (bufferSize >> 1) ? -velocity : velocity;
asuszek 9:1e012f67470c 181 ++bufferIndex;
asuszek 9:1e012f67470c 182
asuszek 9:1e012f67470c 183 // Check if we have reached the end of the buffer.
asuszek 9:1e012f67470c 184 if (bufferIndex == bufferSize) {
asuszek 9:1e012f67470c 185 bufferIndex = 0;
asuszek 9:1e012f67470c 186
asuszek 9:1e012f67470c 187 // Check if there are new notes waiting in the queue.
asuszek 9:1e012f67470c 188 if (nextBufferSize != -1) {
asuszek 9:1e012f67470c 189 currentState = RELEASE;
asuszek 9:1e012f67470c 190 } else {
asuszek 9:1e012f67470c 191 currentState = SUSTAIN;
asuszek 9:1e012f67470c 192 }
asuszek 9:1e012f67470c 193 }
asuszek 9:1e012f67470c 194 } else {
asuszek 9:1e012f67470c 195 identityProcess();
asuszek 9:1e012f67470c 196 }
asuszek 9:1e012f67470c 197 }
asuszek 9:1e012f67470c 198
asuszek 9:1e012f67470c 199 void Synthesizer::processSaw() {
asuszek 9:1e012f67470c 200 if (currentState == FILL) {
asuszek 9:1e012f67470c 201 // Create the saw wave.
asuszek 9:1e012f67470c 202 buffer[bufferIndex] = velocity * (1.0 - 2.0 * float(bufferIndex) / float(bufferSize));
asuszek 9:1e012f67470c 203 ++bufferIndex;
asuszek 9:1e012f67470c 204
asuszek 9:1e012f67470c 205 // Check if we have reached the end of the buffer.
asuszek 9:1e012f67470c 206 if (bufferIndex == bufferSize) {
asuszek 9:1e012f67470c 207 bufferIndex = 0;
asuszek 9:1e012f67470c 208
asuszek 9:1e012f67470c 209 // Check if there are new notes waiting in the queue.
asuszek 9:1e012f67470c 210 if (nextBufferSize != -1) {
asuszek 9:1e012f67470c 211 currentState = RELEASE;
asuszek 9:1e012f67470c 212 } else {
asuszek 9:1e012f67470c 213 currentState = SUSTAIN;
asuszek 9:1e012f67470c 214 }
asuszek 9:1e012f67470c 215 }
asuszek 9:1e012f67470c 216 } else {
asuszek 9:1e012f67470c 217 identityProcess();
asuszek 9:1e012f67470c 218 }
asuszek 9:1e012f67470c 219 }
asuszek 9:1e012f67470c 220
asuszek 9:1e012f67470c 221 void Synthesizer::identityProcess() {
asuszek 9:1e012f67470c 222 if (currentState == RELEASE) {
asuszek 9:1e012f67470c 223 // Attenuate the current buffer.
asuszek 9:1e012f67470c 224 buffer[bufferIndex] *= -(float(bufferIndex) / float(bufferSize)) + 1.0;
asuszek 9:1e012f67470c 225 }
asuszek 9:1e012f67470c 226
asuszek 9:1e012f67470c 227 ++bufferIndex;
asuszek 9:1e012f67470c 228
asuszek 9:1e012f67470c 229 // Check if we have reached the end of the buffer.
asuszek 9:1e012f67470c 230 if (bufferIndex == bufferSize) {
asuszek 9:1e012f67470c 231 bufferIndex = 0;
asuszek 9:1e012f67470c 232
asuszek 9:1e012f67470c 233 // Check the queue to see if we have a new note waiting.
asuszek 9:1e012f67470c 234 if (nextBufferSize != -1) {
asuszek 9:1e012f67470c 235 if (currentState == SUSTAIN) {
asuszek 9:1e012f67470c 236 currentState = RELEASE;
asuszek 9:1e012f67470c 237 } else {
asuszek 9:1e012f67470c 238 bufferSize = nextBufferSize;
asuszek 9:1e012f67470c 239 velocity = nextVelocity;
asuszek 9:1e012f67470c 240 nextBufferSize = -1;
asuszek 9:1e012f67470c 241 fromDisabled = false;
asuszek 9:1e012f67470c 242 currentState = FILL;
asuszek 9:1e012f67470c 243 }
asuszek 9:1e012f67470c 244 } else if (currentState == RELEASE) {
asuszek 9:1e012f67470c 245 // Disable the synthesizer.
asuszek 9:1e012f67470c 246 currentState = OFF;
asuszek 9:1e012f67470c 247 voiceIndex = -1;
asuszek 9:1e012f67470c 248 }
asuszek 9:1e012f67470c 249 }
asuszek 9:1e012f67470c 250 }