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:
20:bf675ba2c454
Fixed LED bug

Who changed what in which revision?

UserRevisionLine numberNew contents of line
asuszek 13:bb0ec927e458 1
asuszek 13:bb0ec927e458 2 #include "KarplusStrong.h"
asuszek 13:bb0ec927e458 3
asuszek 13:bb0ec927e458 4 static float noiseSeed[(C::SAMPLE_RATE / C::MIN_FREQUENCY) + 1];
asuszek 13:bb0ec927e458 5
asuszek 16:b25ca34a705f 6 // Calculate pluck damping constant variables.
asuszek 16:b25ca34a705f 7 const float pluckDampingMin = 0.1;
asuszek 16:b25ca34a705f 8 const float pluckDampingMax = 0.9;
asuszek 20:bf675ba2c454 9 const float pluckDampingVariationMax = C::PLUCK_DAMPING + C::PLUCK_DAMPING_VARIATION * (pluckDampingMax - C::PLUCK_DAMPING);
asuszek 16:b25ca34a705f 10 const float KarplusStrong::pluckDampingVariationMin = C::PLUCK_DAMPING -
asuszek 16:b25ca34a705f 11 C::PLUCK_DAMPING_VARIATION * (C::PLUCK_DAMPING - pluckDampingMin);
asuszek 16:b25ca34a705f 12 const float KarplusStrong::pluckDampingVariationDifference = pluckDampingVariationMax - pluckDampingVariationMin;
asuszek 16:b25ca34a705f 13
asuszek 13:bb0ec927e458 14 KarplusStrong::KarplusStrong() {
asuszek 13:bb0ec927e458 15 initializeNoiseSeed();
asuszek 13:bb0ec927e458 16
asuszek 13:bb0ec927e458 17 // These values will be overwritten under normal control flow, but give them values in case of runtime errors.
asuszek 13:bb0ec927e458 18 lastOutput = 0.0;
asuszek 13:bb0ec927e458 19 filterCoefficient = 0.6;
asuszek 13:bb0ec927e458 20 nextFilterCoefficient = filterCoefficient;
asuszek 16:b25ca34a705f 21 pluckDampingCoefficient = 0.5;
asuszek 13:bb0ec927e458 22 }
asuszek 13:bb0ec927e458 23
asuszek 19:894c31ee9ad4 24 void KarplusStrong::midiNoteOn(int key, float velocity) {
asuszek 13:bb0ec927e458 25 // Calculate the smoothing filter coefficient for this note.
asuszek 16:b25ca34a705f 26 float noteIndex = float(key - 24) / 48.0; // normalize the lower two octaves.
asuszek 13:bb0ec927e458 27 // Clip to 1.0
asuszek 13:bb0ec927e458 28 if (noteIndex > 1.0) {
asuszek 13:bb0ec927e458 29 noteIndex = 1.0;
asuszek 13:bb0ec927e458 30 }
asuszek 13:bb0ec927e458 31
asuszek 19:894c31ee9ad4 32 // The volume is always pretty quiet, even at full velocity, so use velocity as a filter cutoff instead.
asuszek 19:894c31ee9ad4 33 float stringDamping = C::STRING_DAMPING_MIN + velocity * (C::STRING_DAMPING_MAX - C::STRING_DAMPING_MIN);
asuszek 19:894c31ee9ad4 34
asuszek 19:894c31ee9ad4 35 nextFilterCoefficient = stringDamping // Initial damping.
asuszek 19:894c31ee9ad4 36 + 0.5 * (1.0 - stringDamping) * sqrt(noteIndex) // Keyboard tracking
asuszek 19:894c31ee9ad4 37 + C::STRING_DAMPING_VARIATION * (1.0 - stringDamping) * getRand(); // Random variation
asuszek 16:b25ca34a705f 38
asuszek 16:b25ca34a705f 39 // Calculate the pluck damping for this note.
asuszek 16:b25ca34a705f 40 pluckDampingCoefficient = pluckDampingVariationMin + getRand() * pluckDampingVariationDifference;
asuszek 13:bb0ec927e458 41 }
asuszek 13:bb0ec927e458 42
asuszek 13:bb0ec927e458 43 float KarplusStrong::fillBuffer(const int bufferIndex) {
asuszek 13:bb0ec927e458 44 // Copy over the calculated filter coefficient.
asuszek 13:bb0ec927e458 45 filterCoefficient = nextFilterCoefficient;
asuszek 13:bb0ec927e458 46
asuszek 15:aa5a4c350251 47 // Get the noise sample from the static noise seed while making room for character variation.
asuszek 15:aa5a4c350251 48 float noiseSample = noiseSeed[bufferIndex] * (1.0 - C::CHARACTER_VARIATION);
asuszek 15:aa5a4c350251 49 // Add in some character randomness to make it more realistic.
asuszek 15:aa5a4c350251 50 noiseSample += getRandSample() * C::CHARACTER_VARIATION;
asuszek 15:aa5a4c350251 51
asuszek 16:b25ca34a705f 52 // Filter using the pluck damping coefficient to control sprectral content.
asuszek 16:b25ca34a705f 53 noiseSample = lowpassFilter(lastOutput, noiseSample, pluckDampingCoefficient);
asuszek 16:b25ca34a705f 54 return processBuffer(noiseSample);
asuszek 13:bb0ec927e458 55 }
asuszek 13:bb0ec927e458 56
asuszek 13:bb0ec927e458 57 float KarplusStrong::processBuffer(const float inputSample) {
asuszek 13:bb0ec927e458 58 // Put the sample from the last period through the lowpass filter.
asuszek 13:bb0ec927e458 59 lastOutput = lowpassFilter(lastOutput, inputSample, filterCoefficient);
asuszek 13:bb0ec927e458 60 return lastOutput;
asuszek 13:bb0ec927e458 61 }
asuszek 13:bb0ec927e458 62
asuszek 13:bb0ec927e458 63 void KarplusStrong::initializeNoiseSeed() {
asuszek 13:bb0ec927e458 64 // Initialize the random function with a static seed.
asuszek 13:bb0ec927e458 65 srand(time(NULL));
asuszek 13:bb0ec927e458 66
asuszek 13:bb0ec927e458 67 int seedLength = (C::SAMPLE_RATE / C::MIN_FREQUENCY) + 1;
asuszek 13:bb0ec927e458 68 for (int i = 0; i < seedLength; i++) {
asuszek 13:bb0ec927e458 69 // Create a random number in [-1.0, 1.0]
asuszek 15:aa5a4c350251 70 noiseSeed[i] = getRandSample();
asuszek 13:bb0ec927e458 71 }
asuszek 13:bb0ec927e458 72 }
asuszek 13:bb0ec927e458 73
asuszek 13:bb0ec927e458 74 float KarplusStrong::lowpassFilter(const float output, const float input, const float smoothingFactor) {
asuszek 13:bb0ec927e458 75 // Simple single pole recursive IIR lowpass filter.
asuszek 13:bb0ec927e458 76 return input * smoothingFactor + output * (1.0 - smoothingFactor);
asuszek 13:bb0ec927e458 77 }