
A MIDI piano synthesizer that implements the Karplus Strong physical modeling algorithm.
Dependencies: mbed USBDevice PinDetect
Audio/AudioEngine.cpp@22:b800e1766647, 2016-04-25 (annotated)
- Committer:
- asuszek
- Date:
- Mon Apr 25 23:58:15 2016 +0000
- Revision:
- 22:b800e1766647
- Parent:
- 18:26d93c5b9bb6
Fixed LED bug
Who changed what in which revision?
User | Revision | Line number | New 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 | 18:26d93c5b9bb6 | 58 | int AudioEngine::nextSynth(int direction) { |
asuszek | 18:26d93c5b9bb6 | 59 | int nextIndex; |
asuszek | 6:688698f814c0 | 60 | // Hand the command down to the synthesizers. |
asuszek | 6:688698f814c0 | 61 | for (int i = 0; i < C::MAX_POLYPHONY; i++) { |
asuszek | 18:26d93c5b9bb6 | 62 | nextIndex = synthesizers[i].nextSynth(direction); |
asuszek | 6:688698f814c0 | 63 | } |
asuszek | 18:26d93c5b9bb6 | 64 | return nextIndex; |
asuszek | 6:688698f814c0 | 65 | } |
asuszek | 6:688698f814c0 | 66 | |
asuszek | 6:688698f814c0 | 67 | void AudioEngine::outputAudio() { |
asuszek | 13:bb0ec927e458 | 68 | // The first thing we do is output the last calculated sample. |
asuszek | 13:bb0ec927e458 | 69 | // Map [-1, 1] range to [0, 1] range. |
asuszek | 13:bb0ec927e458 | 70 | audioOut = (outputSample + 1.0) / 2.0; |
asuszek | 13:bb0ec927e458 | 71 | |
asuszek | 13:bb0ec927e458 | 72 | // Now reset output and calculate the next sample. |
asuszek | 13:bb0ec927e458 | 73 | outputSample = 0.0; |
asuszek | 6:688698f814c0 | 74 | |
asuszek | 6:688698f814c0 | 75 | // Poll all of the synthesizers. |
asuszek | 6:688698f814c0 | 76 | for (int i = 0; i < C::MAX_POLYPHONY; i++) { |
asuszek | 6:688698f814c0 | 77 | if (synthesizers[i].isPlaying()) { |
asuszek | 13:bb0ec927e458 | 78 | // Get the next sample. |
asuszek | 13:bb0ec927e458 | 79 | outputSample += synthesizers[i].getSample(); |
asuszek | 6:688698f814c0 | 80 | } |
asuszek | 6:688698f814c0 | 81 | } |
asuszek | 6:688698f814c0 | 82 | |
asuszek | 6:688698f814c0 | 83 | // Scale the output by the number of voices. |
asuszek | 13:bb0ec927e458 | 84 | outputSample /= float(C::MAX_POLYPHONY); |
asuszek | 6:688698f814c0 | 85 | // Check that we do not clip. |
asuszek | 13:bb0ec927e458 | 86 | if (outputSample > 1.0) { |
asuszek | 13:bb0ec927e458 | 87 | outputSample = 1.0; |
asuszek | 6:688698f814c0 | 88 | } |
asuszek | 13:bb0ec927e458 | 89 | if (outputSample < -1.0) { |
asuszek | 13:bb0ec927e458 | 90 | outputSample = -1.0; |
asuszek | 6:688698f814c0 | 91 | } |
asuszek | 6:688698f814c0 | 92 | } |