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

Dependencies:   mbed USBDevice PinDetect

Committer:
asuszek
Date:
Sun Apr 17 21:59:03 2016 +0000
Revision:
15:aa5a4c350251
Parent:
13:bb0ec927e458
Child:
16:b25ca34a705f
Added character variation in the noise seed for a less artificial sound.

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 13:bb0ec927e458 6 KarplusStrong::KarplusStrong() {
asuszek 13:bb0ec927e458 7 initializeNoiseSeed();
asuszek 13:bb0ec927e458 8
asuszek 13:bb0ec927e458 9 // These values will be overwritten under normal control flow, but give them values in case of runtime errors.
asuszek 13:bb0ec927e458 10 lastOutput = 0.0;
asuszek 13:bb0ec927e458 11 filterCoefficient = 0.6;
asuszek 13:bb0ec927e458 12 nextFilterCoefficient = filterCoefficient;
asuszek 13:bb0ec927e458 13 }
asuszek 13:bb0ec927e458 14
asuszek 13:bb0ec927e458 15 void KarplusStrong::midiNoteOn(int key, float velocity) {
asuszek 13:bb0ec927e458 16 // Save the velocity for later.
asuszek 13:bb0ec927e458 17 this->velocity = velocity;
asuszek 13:bb0ec927e458 18
asuszek 13:bb0ec927e458 19 // Calculate the smoothing filter coefficient for this note.
asuszek 13:bb0ec927e458 20 float noteIndex = float(key - 24) / 52.0; // normalize the lower two octaves.
asuszek 13:bb0ec927e458 21 // Clip to 1.0
asuszek 13:bb0ec927e458 22 if (noteIndex > 1.0) {
asuszek 13:bb0ec927e458 23 noteIndex = 1.0;
asuszek 13:bb0ec927e458 24 }
asuszek 13:bb0ec927e458 25
asuszek 13:bb0ec927e458 26 nextFilterCoefficient = C::STRING_DAMPING // Initial damping.
asuszek 13:bb0ec927e458 27 + 0.5 * (1.0 - C::STRING_DAMPING) * sqrt(noteIndex) // Keyboard tracking
asuszek 13:bb0ec927e458 28 + C::STRING_DAMPING_VARIATION * (1.0 - C::STRING_DAMPING) * getRand(); // Random variation
asuszek 13:bb0ec927e458 29 }
asuszek 13:bb0ec927e458 30
asuszek 13:bb0ec927e458 31 float KarplusStrong::fillBuffer(const int bufferIndex) {
asuszek 13:bb0ec927e458 32 // Copy over the calculated filter coefficient.
asuszek 13:bb0ec927e458 33 filterCoefficient = nextFilterCoefficient;
asuszek 13:bb0ec927e458 34
asuszek 15:aa5a4c350251 35 // Get the noise sample from the static noise seed while making room for character variation.
asuszek 15:aa5a4c350251 36 float noiseSample = noiseSeed[bufferIndex] * (1.0 - C::CHARACTER_VARIATION);
asuszek 15:aa5a4c350251 37 // Add in some character randomness to make it more realistic.
asuszek 15:aa5a4c350251 38 noiseSample += getRandSample() * C::CHARACTER_VARIATION;
asuszek 15:aa5a4c350251 39 // Account for velocity.
asuszek 15:aa5a4c350251 40 noiseSample *= velocity;
asuszek 15:aa5a4c350251 41
asuszek 15:aa5a4c350251 42 lastOutput = noiseSample;
asuszek 15:aa5a4c350251 43 return noiseSample;
asuszek 13:bb0ec927e458 44 }
asuszek 13:bb0ec927e458 45
asuszek 13:bb0ec927e458 46 float KarplusStrong::processBuffer(const float inputSample) {
asuszek 13:bb0ec927e458 47 // Put the sample from the last period through the lowpass filter.
asuszek 13:bb0ec927e458 48 lastOutput = lowpassFilter(lastOutput, inputSample, filterCoefficient);
asuszek 13:bb0ec927e458 49 return lastOutput;
asuszek 13:bb0ec927e458 50 }
asuszek 13:bb0ec927e458 51
asuszek 13:bb0ec927e458 52 void KarplusStrong::initializeNoiseSeed() {
asuszek 13:bb0ec927e458 53 // Initialize the random function with a static seed.
asuszek 13:bb0ec927e458 54 srand(time(NULL));
asuszek 13:bb0ec927e458 55
asuszek 13:bb0ec927e458 56 int seedLength = (C::SAMPLE_RATE / C::MIN_FREQUENCY) + 1;
asuszek 13:bb0ec927e458 57 for (int i = 0; i < seedLength; i++) {
asuszek 13:bb0ec927e458 58 // Create a random number in [-1.0, 1.0]
asuszek 15:aa5a4c350251 59 noiseSeed[i] = getRandSample();
asuszek 13:bb0ec927e458 60 }
asuszek 13:bb0ec927e458 61 }
asuszek 13:bb0ec927e458 62
asuszek 13:bb0ec927e458 63 float KarplusStrong::lowpassFilter(const float output, const float input, const float smoothingFactor) {
asuszek 13:bb0ec927e458 64 // Simple single pole recursive IIR lowpass filter.
asuszek 13:bb0ec927e458 65 return input * smoothingFactor + output * (1.0 - smoothingFactor);
asuszek 13:bb0ec927e458 66 }