Austin Suszek / Mbed 2 deprecated MIDISynthesizer

Dependencies:   mbed USBDevice PinDetect

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers KarplusStrong.cpp Source File

KarplusStrong.cpp

00001 
00002 #include "KarplusStrong.h"
00003 
00004 static float noiseSeed[(C::SAMPLE_RATE / C::MIN_FREQUENCY) + 1];
00005 
00006 // Calculate pluck damping constant variables.
00007 const float pluckDampingMin = 0.1;
00008 const float pluckDampingMax = 0.9;
00009 const float pluckDampingVariationMax = C::PLUCK_DAMPING + C::PLUCK_DAMPING_VARIATION * (pluckDampingMax - C::PLUCK_DAMPING);
00010 const float KarplusStrong::pluckDampingVariationMin = C::PLUCK_DAMPING - 
00011         C::PLUCK_DAMPING_VARIATION * (C::PLUCK_DAMPING - pluckDampingMin);
00012 const float KarplusStrong::pluckDampingVariationDifference = pluckDampingVariationMax - pluckDampingVariationMin;
00013 
00014 KarplusStrong::KarplusStrong() {
00015     initializeNoiseSeed();
00016     
00017     // These values will be overwritten under normal control flow, but give them values in case of runtime errors.
00018     lastOutput = 0.0;
00019     filterCoefficient = 0.6;
00020     nextFilterCoefficient = filterCoefficient;
00021     pluckDampingCoefficient = 0.5;
00022 }
00023 
00024 void KarplusStrong::midiNoteOn(int key, float velocity) {    
00025     // Calculate the smoothing filter coefficient for this note.
00026     float noteIndex = float(key - 24) / 48.0; // normalize the lower two octaves.
00027     // Clip to 1.0
00028     if (noteIndex > 1.0) {
00029         noteIndex = 1.0;
00030     }
00031     
00032     // The volume is always pretty quiet, even at full velocity, so use velocity as a filter cutoff instead.
00033     float stringDamping = C::STRING_DAMPING_MIN + velocity * (C::STRING_DAMPING_MAX - C::STRING_DAMPING_MIN);
00034     
00035     nextFilterCoefficient = stringDamping // Initial damping.
00036             + 0.5 * (1.0 - stringDamping) * sqrt(noteIndex) // Keyboard tracking
00037             + C::STRING_DAMPING_VARIATION * (1.0 - stringDamping) * getRand(); // Random variation
00038             
00039     // Calculate the pluck damping for this note.
00040     pluckDampingCoefficient = pluckDampingVariationMin + getRand() * pluckDampingVariationDifference;
00041 }
00042 
00043 float KarplusStrong::fillBuffer(const int bufferIndex) {
00044     // Copy over the calculated filter coefficient.
00045     filterCoefficient = nextFilterCoefficient;
00046     
00047     // Get the noise sample from the static noise seed while making room for character variation.
00048     float noiseSample = noiseSeed[bufferIndex] * (1.0 - C::CHARACTER_VARIATION);
00049     // Add in some character randomness to make it more realistic.
00050     noiseSample += getRandSample() * C::CHARACTER_VARIATION;
00051     
00052     // Filter using the pluck damping coefficient to control sprectral content.
00053     noiseSample = lowpassFilter(lastOutput, noiseSample, pluckDampingCoefficient);
00054     return processBuffer(noiseSample);
00055 }
00056 
00057 float KarplusStrong::processBuffer(const float inputSample) {
00058     // Put the sample from the last period through the lowpass filter.
00059     lastOutput = lowpassFilter(lastOutput, inputSample, filterCoefficient);
00060     return lastOutput;
00061 }
00062 
00063 void KarplusStrong::initializeNoiseSeed() {
00064     // Initialize the random function with a static seed.
00065     srand(time(NULL));
00066 
00067     int seedLength = (C::SAMPLE_RATE / C::MIN_FREQUENCY) + 1;
00068     for (int i = 0; i < seedLength; i++) {
00069         // Create a random number in [-1.0, 1.0]
00070         noiseSeed[i] = getRandSample();   
00071     } 
00072 }
00073 
00074 float KarplusStrong::lowpassFilter(const float output, const float input, const float smoothingFactor) {
00075     // Simple single pole recursive IIR lowpass filter.
00076     return input * smoothingFactor + output * (1.0 - smoothingFactor);   
00077 }