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

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 6:688698f814c0 1 /*
asuszek 6:688698f814c0 2 * The interface for all audio input and output.
asuszek 6:688698f814c0 3 * This class acts as a container for individual voices.
asuszek 6:688698f814c0 4 * It takes in MIDI events and handles processing and output.
asuszek 6:688698f814c0 5 *
asuszek 6:688698f814c0 6 * @author Austin Suszek
asuszek 6:688698f814c0 7 */
asuszek 6:688698f814c0 8
asuszek 6:688698f814c0 9 #include "AudioEngine.h"
asuszek 6:688698f814c0 10
asuszek 6:688698f814c0 11 #if USE_PWM
asuszek 6:688698f814c0 12 PwmOut audioOut(p21);
asuszek 6:688698f814c0 13 #else
asuszek 6:688698f814c0 14 AnalogOut audioOut(p18);
asuszek 6:688698f814c0 15 #endif
asuszek 6:688698f814c0 16
asuszek 6:688698f814c0 17 AudioEngine::AudioEngine() {
asuszek 6:688698f814c0 18 // Initialize the synthesizers.
asuszek 6:688698f814c0 19 for (int i = 0; i < C::MAX_POLYPHONY; i++) {
asuszek 6:688698f814c0 20 synthesizers[i] = Synthesizer();
asuszek 6:688698f814c0 21 }
asuszek 6:688698f814c0 22
asuszek 13:bb0ec927e458 23 outputSample = 0.0;
asuszek 13:bb0ec927e458 24
asuszek 6:688698f814c0 25 #if USE_PWM
asuszek 6:688698f814c0 26 audioOut.period(1.0/200000.0);
asuszek 6:688698f814c0 27 #endif
asuszek 6:688698f814c0 28
asuszek 6:688698f814c0 29 // Set up the sampler.
asuszek 6:688698f814c0 30 samplePeriod.attach(this, &AudioEngine::outputAudio, 1.0 / float(C::SAMPLE_RATE));
asuszek 6:688698f814c0 31 }
asuszek 6:688698f814c0 32
asuszek 6:688698f814c0 33 void AudioEngine::midiNoteOn(const int key, const int velocity) {
asuszek 6:688698f814c0 34 // Find the firt disabled voice or the one that has been playing longest.
asuszek 6:688698f814c0 35 int64_t min = synthesizers[0].getVoiceIndex();
asuszek 6:688698f814c0 36 int minIndex = 0;
asuszek 6:688698f814c0 37 for (int i = 1; i < C::MAX_POLYPHONY; i++) {
asuszek 6:688698f814c0 38 int64_t voiceIndex = synthesizers[i].getVoiceIndex();
asuszek 6:688698f814c0 39 if (voiceIndex < min) {
asuszek 6:688698f814c0 40 min = voiceIndex;
asuszek 6:688698f814c0 41 minIndex = i;
asuszek 6:688698f814c0 42 }
asuszek 6:688698f814c0 43 }
asuszek 6:688698f814c0 44
asuszek 6:688698f814c0 45 // Send the note to the minimum voice.
asuszek 6:688698f814c0 46 synthesizers[minIndex].midiNoteOn(key, velocity);
asuszek 6:688698f814c0 47 }
asuszek 6:688698f814c0 48
asuszek 6:688698f814c0 49 void AudioEngine::midiNoteOff(const int key) {
asuszek 6:688698f814c0 50 for (int i = 0; i < C::MAX_POLYPHONY; i++) {
asuszek 6:688698f814c0 51 if (synthesizers[i].isPlaying() && synthesizers[i].getCurrentKey() == key) {
asuszek 6:688698f814c0 52 synthesizers[i].midiNoteOff();
asuszek 6:688698f814c0 53 break;
asuszek 6:688698f814c0 54 }
asuszek 6:688698f814c0 55 }
asuszek 6:688698f814c0 56 }
asuszek 6:688698f814c0 57
asuszek 6:688698f814c0 58 void AudioEngine::nextSynth(int direction) {
asuszek 6:688698f814c0 59 // Hand the command down to the synthesizers.
asuszek 6:688698f814c0 60 for (int i = 0; i < C::MAX_POLYPHONY; i++) {
asuszek 6:688698f814c0 61 synthesizers[i].nextSynth(direction);
asuszek 6:688698f814c0 62 }
asuszek 6:688698f814c0 63 }
asuszek 6:688698f814c0 64
asuszek 6:688698f814c0 65 void AudioEngine::outputAudio() {
asuszek 13:bb0ec927e458 66 // The first thing we do is output the last calculated sample.
asuszek 13:bb0ec927e458 67 // Map [-1, 1] range to [0, 1] range.
asuszek 13:bb0ec927e458 68 audioOut = (outputSample + 1.0) / 2.0;
asuszek 13:bb0ec927e458 69
asuszek 13:bb0ec927e458 70 // Now reset output and calculate the next sample.
asuszek 13:bb0ec927e458 71 outputSample = 0.0;
asuszek 6:688698f814c0 72
asuszek 6:688698f814c0 73 // Poll all of the synthesizers.
asuszek 6:688698f814c0 74 for (int i = 0; i < C::MAX_POLYPHONY; i++) {
asuszek 6:688698f814c0 75 if (synthesizers[i].isPlaying()) {
asuszek 13:bb0ec927e458 76 // Get the next sample.
asuszek 13:bb0ec927e458 77 outputSample += synthesizers[i].getSample();
asuszek 6:688698f814c0 78 }
asuszek 6:688698f814c0 79 }
asuszek 6:688698f814c0 80
asuszek 6:688698f814c0 81 // Scale the output by the number of voices.
asuszek 13:bb0ec927e458 82 outputSample /= float(C::MAX_POLYPHONY);
asuszek 6:688698f814c0 83 // Check that we do not clip.
asuszek 13:bb0ec927e458 84 if (outputSample > 1.0) {
asuszek 13:bb0ec927e458 85 outputSample = 1.0;
asuszek 6:688698f814c0 86 }
asuszek 13:bb0ec927e458 87 if (outputSample < -1.0) {
asuszek 13:bb0ec927e458 88 outputSample = -1.0;
asuszek 6:688698f814c0 89 }
asuszek 6:688698f814c0 90 }