A MIDI piano synthesizer that implements the Karplus Strong physical modeling algorithm.
Dependencies: mbed USBDevice PinDetect
Audio/KarplusStrong.cpp@22:b800e1766647, 2016-04-25 (annotated)
- 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?
User | Revision | Line number | New 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 | } |