Dependencies:   mbed USBDevice PinDetect

Committer:
asuszek
Date:
Sun Apr 17 21:35:23 2016 +0000
Revision:
13:bb0ec927e458
Parent:
9:1e012f67470c
Child:
18:26d93c5b9bb6
Implemented basic Karplus Strong. The sample rate had to be halved in order to process the algorithm for each sample without crashing.

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 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 13:bb0ec927e458 125 void Synthesizer::processKarplusStrong() {
asuszek 13:bb0ec927e458 126 if (currentState == FILL) {
asuszek 13:bb0ec927e458 127 buffer[bufferIndex] = karplusStrong.fillBuffer(bufferIndex);
asuszek 13:bb0ec927e458 128 ++bufferIndex;
asuszek 13:bb0ec927e458 129
asuszek 13:bb0ec927e458 130 checkFillBounds();
asuszek 13:bb0ec927e458 131 } else {
asuszek 13:bb0ec927e458 132 // Process for sustain or release.
asuszek 13:bb0ec927e458 133 float inputSample = buffer[bufferIndex];
asuszek 13:bb0ec927e458 134 buffer[bufferIndex] = karplusStrong.processBuffer(inputSample);
asuszek 13:bb0ec927e458 135 // Check transition logic.
asuszek 13:bb0ec927e458 136 identityProcess();
asuszek 13:bb0ec927e458 137 }
asuszek 13:bb0ec927e458 138 }
asuszek 13:bb0ec927e458 139
asuszek 9:1e012f67470c 140 void Synthesizer::processSine() {
asuszek 9:1e012f67470c 141 if (currentState == FILL) {
asuszek 9:1e012f67470c 142 // Create the sine wave.
asuszek 9:1e012f67470c 143 buffer[bufferIndex] = velocity * sin(TWO_PI * float(bufferIndex) / float(bufferSize));
asuszek 9:1e012f67470c 144 ++bufferIndex;
asuszek 9:1e012f67470c 145
asuszek 13:bb0ec927e458 146 checkFillBounds();
asuszek 9:1e012f67470c 147 } else {
asuszek 9:1e012f67470c 148 identityProcess();
asuszek 9:1e012f67470c 149 }
asuszek 9:1e012f67470c 150 }
asuszek 9:1e012f67470c 151
asuszek 9:1e012f67470c 152 void Synthesizer::processTriangle() {
asuszek 9:1e012f67470c 153 if (currentState == FILL) {
asuszek 9:1e012f67470c 154 // Create the triangle wave.
asuszek 9:1e012f67470c 155 // Rotate pi/2 to remove phase offset.
asuszek 9:1e012f67470c 156 int index = (bufferIndex + (bufferSize >> 2)) % bufferSize;
asuszek 9:1e012f67470c 157 // Calculate linear function from -1 to 3
asuszek 9:1e012f67470c 158 int sample = -1.0 + (4.0 * float(index) / float(bufferSize));
asuszek 9:1e012f67470c 159 // Redirect second half of the line.
asuszek 9:1e012f67470c 160 if (sample > 1.0) {
asuszek 9:1e012f67470c 161 sample = -sample + 2.0;
asuszek 9:1e012f67470c 162 }
asuszek 9:1e012f67470c 163 buffer[bufferIndex] = velocity * sample;
asuszek 9:1e012f67470c 164 ++bufferIndex;
asuszek 9:1e012f67470c 165
asuszek 13:bb0ec927e458 166 checkFillBounds();
asuszek 9:1e012f67470c 167 } else {
asuszek 9:1e012f67470c 168 identityProcess();
asuszek 9:1e012f67470c 169 }
asuszek 9:1e012f67470c 170 }
asuszek 9:1e012f67470c 171
asuszek 9:1e012f67470c 172 void Synthesizer::processSquare() {
asuszek 9:1e012f67470c 173 if (currentState == FILL) {
asuszek 9:1e012f67470c 174 // Create the square wave.
asuszek 9:1e012f67470c 175 buffer[bufferIndex] = bufferIndex < (bufferSize >> 1) ? -velocity : velocity;
asuszek 9:1e012f67470c 176 ++bufferIndex;
asuszek 9:1e012f67470c 177
asuszek 13:bb0ec927e458 178 checkFillBounds();
asuszek 9:1e012f67470c 179 } else {
asuszek 9:1e012f67470c 180 identityProcess();
asuszek 9:1e012f67470c 181 }
asuszek 9:1e012f67470c 182 }
asuszek 9:1e012f67470c 183
asuszek 9:1e012f67470c 184 void Synthesizer::processSaw() {
asuszek 9:1e012f67470c 185 if (currentState == FILL) {
asuszek 9:1e012f67470c 186 // Create the saw wave.
asuszek 9:1e012f67470c 187 buffer[bufferIndex] = velocity * (1.0 - 2.0 * float(bufferIndex) / float(bufferSize));
asuszek 9:1e012f67470c 188 ++bufferIndex;
asuszek 9:1e012f67470c 189
asuszek 13:bb0ec927e458 190 checkFillBounds();
asuszek 9:1e012f67470c 191 } else {
asuszek 9:1e012f67470c 192 identityProcess();
asuszek 9:1e012f67470c 193 }
asuszek 9:1e012f67470c 194 }
asuszek 9:1e012f67470c 195
asuszek 9:1e012f67470c 196 void Synthesizer::identityProcess() {
asuszek 9:1e012f67470c 197 if (currentState == RELEASE) {
asuszek 9:1e012f67470c 198 // Attenuate the current buffer.
asuszek 9:1e012f67470c 199 buffer[bufferIndex] *= -(float(bufferIndex) / float(bufferSize)) + 1.0;
asuszek 9:1e012f67470c 200 }
asuszek 9:1e012f67470c 201
asuszek 9:1e012f67470c 202 ++bufferIndex;
asuszek 9:1e012f67470c 203
asuszek 9:1e012f67470c 204 // Check if we have reached the end of the buffer.
asuszek 9:1e012f67470c 205 if (bufferIndex == bufferSize) {
asuszek 9:1e012f67470c 206 bufferIndex = 0;
asuszek 9:1e012f67470c 207
asuszek 9:1e012f67470c 208 // Check the queue to see if we have a new note waiting.
asuszek 9:1e012f67470c 209 if (nextBufferSize != -1) {
asuszek 9:1e012f67470c 210 if (currentState == SUSTAIN) {
asuszek 9:1e012f67470c 211 currentState = RELEASE;
asuszek 9:1e012f67470c 212 } else {
asuszek 9:1e012f67470c 213 bufferSize = nextBufferSize;
asuszek 9:1e012f67470c 214 velocity = nextVelocity;
asuszek 9:1e012f67470c 215 nextBufferSize = -1;
asuszek 9:1e012f67470c 216 fromDisabled = false;
asuszek 9:1e012f67470c 217 currentState = FILL;
asuszek 9:1e012f67470c 218 }
asuszek 9:1e012f67470c 219 } else if (currentState == RELEASE) {
asuszek 9:1e012f67470c 220 // Disable the synthesizer.
asuszek 9:1e012f67470c 221 currentState = OFF;
asuszek 9:1e012f67470c 222 voiceIndex = -1;
asuszek 9:1e012f67470c 223 }
asuszek 9:1e012f67470c 224 }
asuszek 13:bb0ec927e458 225 }
asuszek 13:bb0ec927e458 226
asuszek 13:bb0ec927e458 227 void Synthesizer::checkFillBounds() {
asuszek 13:bb0ec927e458 228 // Check if we have reached the end of the buffer.
asuszek 13:bb0ec927e458 229 if (bufferIndex == bufferSize) {
asuszek 13:bb0ec927e458 230 bufferIndex = 0;
asuszek 13:bb0ec927e458 231
asuszek 13:bb0ec927e458 232 // Check if there are new notes waiting in the queue.
asuszek 13:bb0ec927e458 233 if (nextBufferSize != -1) {
asuszek 13:bb0ec927e458 234 currentState = RELEASE;
asuszek 13:bb0ec927e458 235 } else {
asuszek 13:bb0ec927e458 236 currentState = SUSTAIN;
asuszek 13:bb0ec927e458 237 }
asuszek 13:bb0ec927e458 238 }
asuszek 9:1e012f67470c 239 }