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

Dependencies:   mbed USBDevice PinDetect

Audio/AudioEngine.cpp

Committer:
asuszek
Date:
2016-04-25
Revision:
22:b800e1766647
Parent:
18:26d93c5b9bb6

File content as of revision 22:b800e1766647:

/* 
 * The interface for all audio input and output.
 * This class acts as a container for individual voices.
 * It takes in MIDI events and handles processing and output.
 *
 * @author Austin Suszek
 */
 
#include "AudioEngine.h"

#if USE_PWM
PwmOut audioOut(p21);
#else
AnalogOut audioOut(p18);
#endif
 
AudioEngine::AudioEngine() {
    // Initialize the synthesizers.
    for (int i = 0; i < C::MAX_POLYPHONY; i++) {
        synthesizers[i] = Synthesizer();   
    }
    
    outputSample = 0.0;
    
    #if USE_PWM
    audioOut.period(1.0/200000.0);
    #endif
    
    // Set up the sampler.
    samplePeriod.attach(this, &AudioEngine::outputAudio, 1.0 / float(C::SAMPLE_RATE));
}

void AudioEngine::midiNoteOn(const int key, const int velocity) {
    // Find the firt disabled voice or the one that has been playing longest.
    int64_t min  = synthesizers[0].getVoiceIndex();
    int minIndex = 0;
    for (int i = 1; i < C::MAX_POLYPHONY; i++) {
        int64_t voiceIndex = synthesizers[i].getVoiceIndex();
        if (voiceIndex < min) {
            min = voiceIndex;
            minIndex = i;
        }   
    }
    
    // Send the note to the minimum voice.
    synthesizers[minIndex].midiNoteOn(key, velocity);
}

void AudioEngine::midiNoteOff(const int key) {
    for (int i = 0; i < C::MAX_POLYPHONY; i++) {
        if (synthesizers[i].isPlaying() && synthesizers[i].getCurrentKey() == key) {
            synthesizers[i].midiNoteOff();
            break;
        }
    }
}

int AudioEngine::nextSynth(int direction) {
    int nextIndex;
    // Hand the command down to the synthesizers.
    for (int i = 0; i < C::MAX_POLYPHONY; i++) {
        nextIndex = synthesizers[i].nextSynth(direction);   
    }
    return nextIndex;
}

void AudioEngine::outputAudio() {
    // The first thing we do is output the last calculated sample.
    // Map [-1, 1] range to [0, 1] range.
    audioOut = (outputSample + 1.0) / 2.0;
    
    // Now reset output and calculate the next sample.
    outputSample = 0.0;
    
    // Poll all of the synthesizers.
    for (int i = 0; i < C::MAX_POLYPHONY; i++) {
        if (synthesizers[i].isPlaying()) {
            // Get the next sample.
            outputSample += synthesizers[i].getSample();   
        } 
    }
    
    // Scale the output by the number of voices.
    outputSample /= float(C::MAX_POLYPHONY);
    // Check that we do not clip.
    if (outputSample > 1.0) {
        outputSample = 1.0;
    }
    if (outputSample < -1.0) {
        outputSample = -1.0;
    }
}