Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: ClockControl PowerControl mbed
Revision 0:727737138ac5, committed 2014-11-09
- Comitter:
- kayekss
- Date:
- Sun Nov 09 08:00:33 2014 +0000
- Child:
- 1:5f0c89bffec1
- Commit message:
- 12-polyphonic "chiptune" MIDI synthesizer for mbed LPC1768 (Standalone version)
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Channels.cpp Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,60 @@
+#include "defs.h"
+#include "Channels.h"
+
+/** Constructor of class Channels */
+Channels::Channels() {
+}
+
+/** Destructor of class Channels */
+Channels::~Channels() {
+}
+
+/** Initialize all 16 channels */
+void Channels::initializeAll() {
+ for (uint8_t i = 0; i < 16; i++) {
+ this->volume[i] = 100;
+ this->expression[i] = 127;
+ this->pitchBend[i] = 0;
+ this->wave[i] = Wavetable::waveDefList[4];
+ }
+}
+
+/** Set volume to channel #i (i: zero origin) */
+void Channels::setVolume(uint8_t i, uint8_t vo) {
+ this->volume[i] = vo;
+}
+
+/** Set expression to channel #i (i: zero origin) */
+void Channels::setExpression(uint8_t i, uint8_t ex) {
+ this->expression[i] = ex;
+}
+
+/** Set pitch bend to channel #i (i: zero origin) */
+void Channels::setPitchBend(uint8_t i, int16_t pb) {
+ this->pitchBend[i] = pb;
+}
+
+/** Set wave parameters of channel #i (i: zero origin) */
+void Channels::setWave(uint8_t i, Wavetable::wave_t w) {
+ this->wave[i] = w;
+}
+
+/** Get volume of channel #i (i: zero origin) */
+uint8_t Channels::getVolume(uint8_t i) {
+ return this->volume[i];
+}
+
+/** Get expression of channel #i (i: zero origin) */
+uint8_t Channels::getExpression(uint8_t i) {
+ return this->expression[i];
+}
+
+/** Get pitch bend of channel #i (i: zero origin) */
+int16_t Channels::getPitchBend(uint8_t i) {
+ return this->pitchBend[i];
+}
+
+/** Get wave parameters of channel #i (i: zero origin) */
+Wavetable::wave_t Channels::getWave(uint8_t i) {
+ return this->wave[i];
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Channels.h Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,29 @@
+#ifndef CHANNELS_H_
+#define CHANNELS_H_
+
+#include <stdint.h>
+#include "Wavetable.h"
+
+class Channels {
+private:
+ uint8_t volume[16];
+ uint8_t expression[16];
+ int16_t pitchBend[16];
+ Wavetable::wave_t wave[16];
+
+public:
+ Channels();
+ ~Channels();
+ void initializeAll();
+ void setVolume(uint8_t, uint8_t);
+ void setExpression(uint8_t, uint8_t);
+ void setPitchBend(uint8_t, int16_t);
+ void setWave(uint8_t, Wavetable::wave_t);
+
+ uint8_t getVolume(uint8_t);
+ uint8_t getExpression(uint8_t);
+ int16_t getPitchBend(uint8_t);
+ Wavetable::wave_t getWave(uint8_t);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ClockControl.lib Sun Nov 09 08:00:33 2014 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/JST2011/code/ClockControl/#a6d100de3aee
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/GeminiCore.cpp Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,229 @@
+#include "mbed.h"
+#include "GeminiCore.h"
+
+uint16_t const GeminiCore::samplingRate = 32000u;
+
+/** Constructor of class GeminiCore */
+GeminiCore::GeminiCore(uint8_t numInstruments) {
+ this->numInstruments = numInstruments;
+
+ // Instantiate instrument list
+ this->instrumentList = new Instrument[numInstruments];
+ for (uint8_t i = 0; i < numInstruments; i++) {
+ this->instrumentList[i].enable();
+ this->instrumentList[i].setSamplingRate(GeminiCore::samplingRate);
+ this->instrumentList[i].setWave(Wavetable::waveDefList[4]);
+ }
+
+ // Allocate bytes to preserve previous samples
+ prevSample = new uint8_t[numInstruments];
+
+ // Initialize random seeds
+ this->x8 = 77u;
+ this->x16 = 5501u;
+}
+
+/** Destructor of class GeminiCore */
+GeminiCore::~GeminiCore() {
+ delete[] this->instrumentList;
+ delete[] this->prevSample;
+}
+
+/** Make a sample - should be called periodically depending on the sampling rate */
+uint16_t GeminiCore::makeSample() {
+ Instrument* iThInst;
+ Wavetable::wave_t iThWave;
+ int32_t sample;
+ int32_t iThSample;
+ uint16_t phasePrev;
+ uint32_t duration;
+ uint8_t decayRatio;
+
+ sample = 0;
+ for (uint8_t i = 0; i < numInstruments; i++) {
+ iThInst = &instrumentList[i];
+ iThWave = iThInst->getWave();
+
+ // Do nothing if the instrument is disabled or not sounding
+ if (!iThInst->isEnable() || iThInst->getFrequency() == 0) {
+ continue;
+ }
+
+ duration = iThInst->getDuration();
+ if (duration >> iThWave.decaySpeed >= 255 - iThWave.sustainLevel) {
+ decayRatio = iThWave.sustainLevel;
+ } else {
+ decayRatio = 256 - (duration >> iThWave.decaySpeed);
+ }
+
+ switch (iThWave.wavetype) {
+ case Wavetable::Noise:
+ phasePrev = iThInst->getPhase();
+ iThInst->advancePhase();
+
+ // If necessary, generate new random value
+ if ((iThInst->getPhase() >> 8) - (phasePrev >> 8) > 0) {
+ prevSample[i] = rand16() >> 8;
+ }
+ iThSample = prevSample[i];
+ break;
+ case Wavetable::LowPeriodNoise:
+ phasePrev = iThInst->getPhase();
+ iThInst->advancePhase();
+
+ // If necessary, generate new random value
+ if ((iThInst->getPhase() >> 8) - (phasePrev >> 8) > 0) {
+ prevSample[i] = rand8();
+ }
+ iThSample = prevSample[i];
+ break;
+ default:
+ iThInst->advancePhase();
+ iThSample = Wavetable::waveTableList[iThWave.wavetype][iThInst->getPhase() >> 8] - 128;
+ }
+ sample += iThSample * iThInst->getMasterVolume() * decayRatio;
+ }
+ sample >>= 10;
+ if (sample > 32767) sample = 32767;
+ if (sample < -32768) sample = -32768;
+ return (uint16_t) (sample + 32768);
+}
+
+/** Enable i-th Instrument */
+bool GeminiCore::enable(uint8_t i) {
+ Instrument* iThInst = NULL;
+
+ if (i >= numInstruments || instrumentList == NULL) {
+ return false;
+ }
+ iThInst = &instrumentList[i];
+ if (iThInst == NULL) {
+ return false;
+ }
+
+ iThInst->enable();
+ return true;
+}
+
+/** Disable i-th Instrument */
+bool GeminiCore::disable(uint8_t i) {
+ Instrument* iThInst = NULL;
+
+ if (i >= numInstruments || instrumentList == NULL) {
+ return false;
+ }
+ iThInst = &instrumentList[i];
+ if (iThInst == NULL) {
+ return false;
+ }
+
+ iThInst->disable();
+ return true;
+}
+
+/** Note on i-th Instrument */
+bool GeminiCore::noteOn(uint8_t i, uint16_t frequency, uint8_t velocity) {
+ Instrument* iThInst = NULL;
+
+ if (i >= numInstruments || instrumentList == NULL) {
+ return false;
+ }
+ iThInst = &instrumentList[i];
+ if (iThInst == NULL) {
+ return false;
+ }
+
+ iThInst->setFrequency(frequency); // @TODO pitch bend correction needed
+ iThInst->setVelocity(velocity);
+ return true;
+}
+
+/** Note off i-th Instrument */
+bool GeminiCore::noteOff(uint8_t i) {
+ Instrument* iThInst = NULL;
+
+ if (i >= numInstruments || instrumentList == NULL) {
+ return false;
+ }
+ iThInst = &instrumentList[i];
+ if (iThInst == NULL) {
+ return false;
+ }
+
+ iThInst->setFrequency(0);
+ return true;
+}
+
+/** Set volume (0..127) to i-th Instrument */
+bool GeminiCore::volume(uint8_t i, uint8_t vo) {
+ Instrument* iThInst = NULL;
+
+ if (i >= numInstruments || instrumentList == NULL) {
+ return false;
+ }
+ iThInst = &instrumentList[i];
+ if (iThInst == NULL) {
+ return false;
+ }
+
+ iThInst->setVolume(vo << 1); // 0..127 => 0..254
+ return true;
+}
+
+/** Set expression (0..127) to i-th Instrument */
+bool GeminiCore::expression(uint8_t i, uint8_t ex) {
+ Instrument* iThInst = NULL;
+
+ if (i >= numInstruments || instrumentList == NULL) {
+ return false;
+ }
+ iThInst = &instrumentList[i];
+ if (iThInst == NULL) {
+ return false;
+ }
+
+ iThInst->setExpression(ex << 1); // 0..127 => 0..254
+ return true;
+}
+
+bool GeminiCore::pitchBend(uint8_t i, int16_t pb) {
+ // @TODO
+ return true;
+}
+
+/** Set wave parameters to i-th Instrument */
+bool GeminiCore::setWave(uint8_t i, Wavetable::wave_t w) {
+ Instrument* iThInst = NULL;
+
+ if (i >= numInstruments || instrumentList == NULL) {
+ return false;
+ }
+ iThInst = &instrumentList[i];
+ if (iThInst == NULL) {
+ return false;
+ }
+
+ iThInst->setWave(w);
+ return true;
+}
+
+/** Get instrument list */
+Instrument* GeminiCore::getInstrumentList() {
+ return this->instrumentList;
+}
+
+/** Generate an 8-bit random number */
+uint8_t GeminiCore::rand8() {
+ x8 ^= x8 << 7;
+ x8 ^= x8 >> 5;
+ x8 ^= x8 << 3;
+ return x8;
+}
+
+/** Generate a 16-bit random number */
+uint16_t GeminiCore::rand16() {
+ x16 ^= x16 << 13;
+ x16 ^= x16 >> 9;
+ x16 ^= x16 << 7;
+ return x16;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/GeminiCore.h Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,38 @@
+#ifndef GEMINICORE_H_
+#define GEMINICORE_H_
+
+#include <stdint.h>
+#include "Instrument.h"
+
+class GeminiCore {
+private:
+ Instrument* instrumentList;
+ uint8_t numInstruments;
+ uint8_t* prevSample; // previous sample value (used for noise samples)
+ uint8_t x8; // 8-bit random seed
+ uint16_t x16; // 16-bit random seed
+
+public:
+ static uint16_t const samplingRate;
+
+ GeminiCore(uint8_t);
+ ~GeminiCore();
+ uint16_t makeSample();
+ bool enable(uint8_t);
+ bool disable(uint8_t);
+ bool noteOn(uint8_t, uint16_t, uint8_t);
+ bool noteOff(uint8_t);
+ bool volume(uint8_t, uint8_t);
+ bool expression(uint8_t, uint8_t);
+ bool pitchBend(uint8_t, int16_t);
+ bool setWave(uint8_t, Wavetable::wave_t);
+
+ Instrument* getInstrumentList();
+ uint32_t getPlaybackStartTime();
+
+private:
+ uint8_t rand8();
+ uint16_t rand16();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Instrument.cpp Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,157 @@
+#include <climits>
+#include "defs.h"
+#include "Instrument.h"
+
+/** Constructor of class Instrument */
+Instrument::Instrument() {
+ this->enableFlag = false;
+ this->soundingPosition = -1;
+ this->duration = 0;
+ this->frequency = 0;
+ this->volume = 100;
+ this->expression = 127;
+ this->velocity = 0;
+ this->calculatePhaseDelta();
+ this->calculateMasterVolume();
+}
+
+/** Destructor of class Instrument */
+Instrument::~Instrument() {
+}
+
+/** Enable instrument */
+void Instrument::enable() {
+ this->enableFlag = true;
+}
+
+/** Disable instrument */
+void Instrument::disable() {
+ this->enableFlag = false;
+}
+
+/** Set sampling rate */
+void Instrument::setSamplingRate(uint16_t fsamp) {
+ this->samplingRate = fsamp;
+}
+
+/** Advance wave phase by one sample */
+void Instrument::advancePhase() {
+ this->phase += this->phaseDelta;
+}
+
+/** Reset wave phase to zero */
+void Instrument::resetPhase() {
+ this->phase = 0;
+}
+
+/** Set wave parameters */
+void Instrument::setWave(Wavetable::wave_t w) {
+ this->wave = w;
+ this->phase = 0;
+}
+
+/** Set wave frequency */
+void Instrument::setFrequency(uint16_t f) {
+ this->frequency = f;
+ this->duration = 0; // Reset duration
+ calculatePhaseDelta(); // Recalculate phase delta
+}
+
+/** Set volume */
+void Instrument::setVolume(uint8_t vo) {
+ this->volume = vo;
+ calculateMasterVolume();
+}
+
+/** Set expression */
+void Instrument::setExpression(uint8_t ex) {
+ this->expression = ex;
+ calculateMasterVolume();
+}
+
+/** Set note velocity */
+void Instrument::setVelocity(uint8_t ve) {
+ this->velocity = ve;
+ calculateMasterVolume();
+}
+
+/** Get sampling rate */
+uint16_t Instrument::getSamplingRate() {
+ return this->samplingRate;
+}
+
+/** Get channel ID */
+uint8_t Instrument::getChannelId() {
+ return this->channelId;
+}
+
+/** Get enable flag */
+bool Instrument::isEnable() {
+ return this->enableFlag;
+}
+
+/** Get sounding note position */
+int16_t Instrument::getSoundingPosition() {
+ return this->soundingPosition;
+}
+
+/** Get sounding duration */
+uint32_t Instrument::getDuration() {
+ if (this->duration < UINT_MAX) {
+ this->duration++;
+ }
+ return this->duration;
+}
+
+/** Get wave parameters */
+Wavetable::wave_t Instrument::getWave() {
+ return this->wave;
+}
+
+/** Get wave frequency */
+uint16_t Instrument::getFrequency() {
+ return this->frequency;
+}
+
+/** Get master volume */
+uint8_t Instrument::getMasterVolume() {
+ return this->masterVolume;
+}
+
+/** Get volume */
+uint8_t Instrument::getVolume() {
+ return this->volume;
+}
+
+/** Get expression */
+uint8_t Instrument::getExpression() {
+ return this->expression;
+}
+
+/** Get note velocity */
+uint8_t Instrument::getVelocity() {
+ return this->velocity;
+}
+
+/** Get current phase */
+uint16_t Instrument::getPhase() {
+ return this->phase;
+}
+
+/** Get phase delta per sample */
+uint16_t Instrument::getPhaseDelta() {
+ return this->phaseDelta;
+}
+
+/** (Re)calculate phase delta per sample */
+void Instrument::calculatePhaseDelta() {
+ phase = 0x0000;
+ phaseDelta = (uint16_t) ((uint32_t) frequency * WAVETABLE_LENGTH * 256
+ / samplingRate);
+}
+
+/** Calculate master volume from product of its volume, expression, and velocity */
+void Instrument::calculateMasterVolume() {
+ this->masterVolume
+ = ((uint32_t) this->volume * this->expression * this->velocity) >> 16;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Instrument.h Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,57 @@
+#ifndef INSTRUMENT_H_
+#define INSTRUMENT_H_
+
+#include <stdint.h>
+#include "Wavetable.h"
+
+class Instrument {
+private:
+ uint16_t samplingRate; // sampling rate
+ uint8_t channelId; // channel ID
+ bool enableFlag; // enable flag (whether this instrument is enabled)
+ int32_t soundingPosition; // currently sounding note position
+ uint32_t duration; // sounding duration
+ Wavetable::wave_t wave; // wave parameters
+ uint16_t frequency; // wave frequency
+ uint8_t masterVolume; // + master volume
+ uint8_t volume; // |- volume
+ uint8_t expression; // |- expression (volume multiplier)
+ uint8_t velocity; // |- note velocity (volume multiplier)
+ uint16_t phase; // current phase (x256)
+ uint16_t phaseDelta; // phase delta per sample (x256)
+
+public:
+ Instrument();
+ ~Instrument();
+
+ void enable();
+ void disable();
+ void setSamplingRate(uint16_t);
+ void advancePhase();
+ void resetPhase();
+ void setWave(Wavetable::wave_t);
+ void setFrequency(uint16_t);
+ void setVolume(uint8_t);
+ void setExpression(uint8_t);
+ void setVelocity(uint8_t);
+
+ uint16_t getSamplingRate();
+ uint8_t getChannelId();
+ bool isEnable();
+ int16_t getSoundingPosition();
+ uint32_t getDuration();
+ Wavetable::wave_t getWave();
+ uint16_t getFrequency();
+ uint8_t getMasterVolume();
+ uint8_t getVolume();
+ uint8_t getExpression();
+ uint8_t getVelocity();
+ uint16_t getPhase();
+ uint16_t getPhaseDelta();
+
+private:
+ void calculatePhaseDelta();
+ void calculateMasterVolume();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PowerControl.lib Sun Nov 09 08:00:33 2014 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/JST2011/code/PowerControl/#d0fa2aeb02a4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/RingBuffer.h Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,114 @@
+#ifndef RINGBUFFER_H_
+#define RINGBUFFER_H_
+
+#include <stdint.h>
+
+template <class T>
+class RingBuffer {
+private:
+ uint32_t readPos;
+ uint32_t writePos;
+ uint32_t itemCount;
+ uint32_t length;
+ T* buffer;
+
+public:
+ RingBuffer(uint32_t);
+ ~RingBuffer();
+ bool isWritable();
+ bool isReadable();
+ bool write(T&);
+ T* read();
+ T* peek();
+ void flush();
+ int find(T);
+
+ uint32_t getItemCount();
+};
+
+template <class T>
+RingBuffer<T>::RingBuffer(uint32_t len) {
+ length = len;
+ buffer = new T[length];
+ readPos = 0;
+ writePos = 0;
+ itemCount = 0;
+}
+
+template <class T>
+RingBuffer<T>::~RingBuffer() {
+ delete[] buffer;
+}
+
+template <class T>
+bool RingBuffer<T>::isWritable() {
+ return itemCount < length;
+}
+
+template <class T>
+bool RingBuffer<T>::isReadable() {
+ return itemCount > 0;
+}
+
+template <class T>
+bool RingBuffer<T>::write(T& n) {
+ if (!isWritable()) {
+ return false;
+ }
+ buffer[writePos++] = n;
+ if (writePos == length) {
+ writePos = 0;
+ }
+ itemCount++;
+ return true;
+}
+
+template <class T>
+T* RingBuffer<T>::read() {
+ uint32_t readPosTemp = readPos;
+
+ if (!isReadable()) {
+ return NULL;
+ }
+ readPos++;
+ if (readPos == length) {
+ readPos = 0;
+ }
+ itemCount--;
+ return &buffer[readPosTemp];
+}
+
+template <class T>
+T* RingBuffer<T>::peek() {
+ if (!isReadable()) {
+ return NULL;
+ }
+ return &buffer[readPos];
+}
+
+template <class T>
+void RingBuffer<T>::flush() {
+ itemCount = 0;
+ readPos = writePos;
+}
+
+template <class T>
+int RingBuffer<T>::find(T key) {
+ uint32_t p = readPos;
+
+ for (uint32_t i = 0; i < itemCount; i++) {
+ if (buffer[p] == key) {
+ return i;
+ }
+ p++;
+ if (p == length) p = 0;
+ }
+ return -1;
+}
+
+template <class T>
+uint32_t RingBuffer<T>::getItemCount() {
+ return this->itemCount;
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Wavetable.cpp Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,466 @@
+#include "mbed.h"
+#include "Wavetable.h"
+
+const uint8_t* const Wavetable::waveTableList[] = {
+ Wavetable::sineTable,
+ Wavetable::squareTable,
+ Wavetable::pulse1_3Table,
+ Wavetable::pulse1_7Table,
+ Wavetable::sawtoothTable,
+ Wavetable::triangleTable,
+ Wavetable::coarseTriangleTable,
+ Wavetable::spikedSineTable,
+ NULL,
+ NULL
+};
+
+Wavetable::wave_t const Wavetable::waveDefList[128] = {
+ // 00h..07h Piano
+ { Wavetable::Sine, 8, 255 },
+ { Wavetable::Sine, 8, 128 },
+ { Wavetable::Sine, 8, 32 },
+ { Wavetable::Sine, 8, 8 },
+ { Wavetable::Sine, 7, 8 },
+ { Wavetable::Sine, 6, 8 },
+ { Wavetable::Sine, 5, 8 },
+ { Wavetable::Sine, 5, 0 },
+ // 08h..0Fh Chromatic Percussion
+ { Wavetable::Square, 8, 255 },
+ { Wavetable::Square, 8, 128 },
+ { Wavetable::Square, 8, 32 },
+ { Wavetable::Square, 8, 8 },
+ { Wavetable::Square, 7, 8 },
+ { Wavetable::Square, 6, 8 },
+ { Wavetable::Square, 5, 8 },
+ { Wavetable::Square, 5, 0 },
+ // 10h..17h Organ
+ { Wavetable::Pulse1_3, 8, 255 },
+ { Wavetable::Pulse1_3, 8, 128 },
+ { Wavetable::Pulse1_3, 8, 32 },
+ { Wavetable::Pulse1_3, 8, 8 },
+ { Wavetable::Pulse1_3, 7, 8 },
+ { Wavetable::Pulse1_3, 6, 8 },
+ { Wavetable::Pulse1_3, 5, 8 },
+ { Wavetable::Pulse1_3, 5, 0 },
+ // 18h..1Fh Guitar
+ { Wavetable::Pulse1_7, 8, 255 },
+ { Wavetable::Pulse1_7, 8, 128 },
+ { Wavetable::Pulse1_7, 8, 32 },
+ { Wavetable::Pulse1_7, 8, 8 },
+ { Wavetable::Pulse1_7, 7, 8 },
+ { Wavetable::Pulse1_7, 6, 8 },
+ { Wavetable::Pulse1_7, 5, 8 },
+ { Wavetable::Pulse1_7, 5, 0 },
+ // 20h..27h Bass
+ { Wavetable::Sawtooth, 8, 255 },
+ { Wavetable::Sawtooth, 8, 128 },
+ { Wavetable::Sawtooth, 8, 32 },
+ { Wavetable::Sawtooth, 8, 8 },
+ { Wavetable::Sawtooth, 7, 8 },
+ { Wavetable::Sawtooth, 6, 8 },
+ { Wavetable::Sawtooth, 5, 8 },
+ { Wavetable::Sawtooth, 5, 0 },
+ // 28h..2Fh Strings
+ { Wavetable::Triangle, 8, 255 },
+ { Wavetable::Triangle, 8, 128 },
+ { Wavetable::Triangle, 8, 32 },
+ { Wavetable::Triangle, 8, 8 },
+ { Wavetable::Triangle, 7, 8 },
+ { Wavetable::Triangle, 6, 8 },
+ { Wavetable::Triangle, 5, 8 },
+ { Wavetable::Triangle, 5, 0 },
+ // 30h..37h Emsemble
+ { Wavetable::CoarseTriangle, 8, 255 },
+ { Wavetable::CoarseTriangle, 8, 128 },
+ { Wavetable::CoarseTriangle, 8, 32 },
+ { Wavetable::CoarseTriangle, 8, 8 },
+ { Wavetable::CoarseTriangle, 7, 8 },
+ { Wavetable::CoarseTriangle, 6, 8 },
+ { Wavetable::CoarseTriangle, 5, 8 },
+ { Wavetable::CoarseTriangle, 5, 0 },
+ // 38h..3Fh Brass
+ { Wavetable::SpikedSine, 8, 255 },
+ { Wavetable::SpikedSine, 8, 128 },
+ { Wavetable::SpikedSine, 8, 32 },
+ { Wavetable::SpikedSine, 8, 8 },
+ { Wavetable::SpikedSine, 7, 8 },
+ { Wavetable::SpikedSine, 6, 8 },
+ { Wavetable::SpikedSine, 5, 8 },
+ { Wavetable::SpikedSine, 5, 0 },
+ // 40h..47h Reed
+ { Wavetable::Noise, 8, 255 },
+ { Wavetable::Noise, 8, 128 },
+ { Wavetable::Noise, 8, 32 },
+ { Wavetable::Noise, 8, 8 },
+ { Wavetable::Noise, 7, 8 },
+ { Wavetable::Noise, 6, 8 },
+ { Wavetable::Noise, 5, 8 },
+ { Wavetable::Noise, 5, 0 },
+ // 48h..4Fh Pipe
+ { Wavetable::LowPeriodNoise, 8, 255 },
+ { Wavetable::LowPeriodNoise, 8, 128 },
+ { Wavetable::LowPeriodNoise, 8, 32 },
+ { Wavetable::LowPeriodNoise, 8, 8 },
+ { Wavetable::LowPeriodNoise, 7, 8 },
+ { Wavetable::LowPeriodNoise, 6, 8 },
+ { Wavetable::LowPeriodNoise, 5, 8 },
+ { Wavetable::LowPeriodNoise, 5, 0 },
+ // 50h..57h Synth Lead
+ { Wavetable::Sine, 8, 255 },
+ { Wavetable::Sine, 8, 128 },
+ { Wavetable::Sine, 8, 32 },
+ { Wavetable::Sine, 8, 8 },
+ { Wavetable::Sine, 7, 8 },
+ { Wavetable::Sine, 6, 8 },
+ { Wavetable::Sine, 5, 8 },
+ { Wavetable::Sine, 5, 0 },
+ // 58h..5Fh Synth Pad
+ { Wavetable::Sine, 8, 255 },
+ { Wavetable::Sine, 8, 128 },
+ { Wavetable::Sine, 8, 32 },
+ { Wavetable::Sine, 8, 8 },
+ { Wavetable::Sine, 7, 8 },
+ { Wavetable::Sine, 6, 8 },
+ { Wavetable::Sine, 5, 8 },
+ { Wavetable::Sine, 5, 0 },
+ // 60h..67h Synth Effects
+ { Wavetable::Sine, 8, 255 },
+ { Wavetable::Sine, 8, 128 },
+ { Wavetable::Sine, 8, 32 },
+ { Wavetable::Sine, 8, 8 },
+ { Wavetable::Sine, 7, 8 },
+ { Wavetable::Sine, 6, 8 },
+ { Wavetable::Sine, 5, 8 },
+ { Wavetable::Sine, 5, 0 },
+ // 68h..6Fh Ethnic
+ { Wavetable::Sine, 8, 255 },
+ { Wavetable::Sine, 8, 128 },
+ { Wavetable::Sine, 8, 32 },
+ { Wavetable::Sine, 8, 8 },
+ { Wavetable::Sine, 7, 8 },
+ { Wavetable::Sine, 6, 8 },
+ { Wavetable::Sine, 5, 8 },
+ { Wavetable::Sine, 5, 0 },
+ // 70h..77h Percussive
+ { Wavetable::Sine, 8, 255 },
+ { Wavetable::Sine, 8, 128 },
+ { Wavetable::Sine, 8, 32 },
+ { Wavetable::Sine, 8, 8 },
+ { Wavetable::Sine, 7, 8 },
+ { Wavetable::Sine, 6, 8 },
+ { Wavetable::Sine, 5, 8 },
+ { Wavetable::Sine, 5, 0 },
+ // 78h..7Fh Sound Effects
+ { Wavetable::Sine, 8, 255 },
+ { Wavetable::Sine, 8, 128 },
+ { Wavetable::Sine, 8, 32 },
+ { Wavetable::Sine, 8, 8 },
+ { Wavetable::Sine, 7, 8 },
+ { Wavetable::Sine, 6, 8 },
+ { Wavetable::Sine, 5, 8 },
+ { Wavetable::Sine, 5, 0 },
+};
+
+uint8_t const Wavetable::sineTable[WAVETABLE_LENGTH] = {
+ 0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x95,
+ 0x98, 0x9b, 0x9e, 0xa1, 0xa4, 0xa7, 0xaa, 0xad,
+ 0xb0, 0xb3, 0xb6, 0xb9, 0xbb, 0xbe, 0xc1, 0xc3,
+ 0xc6, 0xc9, 0xcb, 0xce, 0xd0, 0xd2, 0xd5, 0xd7,
+ 0xd9, 0xdb, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe7,
+ 0xe9, 0xeb, 0xec, 0xee, 0xf0, 0xf1, 0xf2, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfb,
+ 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+
+ 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd,
+ 0xfc, 0xfb, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6,
+ 0xf5, 0xf4, 0xf2, 0xf1, 0xf0, 0xee, 0xec, 0xeb,
+ 0xe9, 0xe7, 0xe6, 0xe4, 0xe2, 0xe0, 0xde, 0xdb,
+ 0xd9, 0xd7, 0xd5, 0xd2, 0xd0, 0xce, 0xcb, 0xc9,
+ 0xc6, 0xc3, 0xc1, 0xbe, 0xbb, 0xb9, 0xb6, 0xb3,
+ 0xb0, 0xad, 0xaa, 0xa7, 0xa4, 0xa1, 0x9e, 0x9b,
+ 0x98, 0x95, 0x92, 0x8f, 0x8c, 0x89, 0x86, 0x83,
+
+ 0x7f, 0x7c, 0x79, 0x76, 0x73, 0x70, 0x6d, 0x6a,
+ 0x67, 0x64, 0x61, 0x5e, 0x5b, 0x58, 0x55, 0x52,
+ 0x4f, 0x4c, 0x49, 0x46, 0x44, 0x41, 0x3e, 0x3c,
+ 0x39, 0x36, 0x34, 0x31, 0x2f, 0x2d, 0x2a, 0x28,
+ 0x26, 0x24, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x18,
+ 0x16, 0x14, 0x13, 0x11, 0x0f, 0x0e, 0x0d, 0x0b,
+ 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x04,
+ 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x11, 0x13, 0x14,
+ 0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x24,
+ 0x26, 0x28, 0x2a, 0x2d, 0x2f, 0x31, 0x34, 0x36,
+ 0x39, 0x3c, 0x3e, 0x41, 0x44, 0x46, 0x49, 0x4c,
+ 0x4f, 0x52, 0x55, 0x58, 0x5b, 0x5e, 0x61, 0x64,
+ 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x76, 0x79, 0x7c
+};
+
+uint8_t const Wavetable::squareTable[WAVETABLE_LENGTH] = {
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
+};
+
+uint8_t const Wavetable::pulse1_3Table[WAVETABLE_LENGTH] = {
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
+};
+
+uint8_t const Wavetable::pulse1_7Table[WAVETABLE_LENGTH] = {
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
+};
+
+uint8_t const Wavetable::sawtoothTable[WAVETABLE_LENGTH] = {
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
+};
+
+uint8_t const Wavetable::triangleTable[WAVETABLE_LENGTH] = {
+ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e,
+ 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
+ 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae,
+ 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
+ 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce,
+ 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+ 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee,
+ 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
+
+ 0xff, 0xfe, 0xfc, 0xfa, 0xf8, 0xf6, 0xf4, 0xf2,
+ 0xf0, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2,
+ 0xe0, 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2,
+ 0xd0, 0xce, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2,
+ 0xc0, 0xbe, 0xbc, 0xba, 0xb8, 0xb6, 0xb4, 0xb2,
+ 0xb0, 0xae, 0xac, 0xaa, 0xa8, 0xa6, 0xa4, 0xa2,
+ 0xa0, 0x9e, 0x9c, 0x9a, 0x98, 0x96, 0x94, 0x92,
+ 0x90, 0x8e, 0x8c, 0x8a, 0x88, 0x86, 0x84, 0x82,
+
+ 0x80, 0x7e, 0x7c, 0x7a, 0x78, 0x76, 0x74, 0x72,
+ 0x70, 0x6e, 0x6c, 0x6a, 0x68, 0x66, 0x64, 0x62,
+ 0x60, 0x5e, 0x5c, 0x5a, 0x58, 0x56, 0x54, 0x52,
+ 0x50, 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x42,
+ 0x40, 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32,
+ 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22,
+ 0x20, 0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x14, 0x12,
+ 0x10, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02,
+
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e,
+ 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
+ 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e,
+ 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
+ 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e,
+ 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e
+};
+
+uint8_t const Wavetable::coarseTriangleTable[WAVETABLE_LENGTH] = {
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60
+};
+
+uint8_t const Wavetable::spikedSineTable[WAVETABLE_LENGTH] = {
+ 0xa0, 0xa2, 0xa4, 0xa7, 0xa9, 0xab, 0xae, 0xb0,
+ 0x72, 0x75, 0x77, 0x79, 0x7b, 0x7e, 0x80, 0x82,
+ 0xc4, 0xc6, 0xc9, 0xcb, 0xcd, 0xcf, 0xd1, 0xd3,
+ 0x95, 0x97, 0x99, 0x9b, 0x9c, 0x9e, 0xa0, 0xa2,
+ 0xe3, 0xe5, 0xe7, 0xe8, 0xea, 0xeb, 0xed, 0xee,
+ 0xaf, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, 0xfd,
+ 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
+ 0xbe, 0xbd, 0xbd, 0xbc, 0xbb, 0xbb, 0xba, 0xb9,
+ 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1,
+ 0xaf, 0xae, 0xad, 0xab, 0xaa, 0xa8, 0xa7, 0xa5,
+ 0xe3, 0xe2, 0xe0, 0xde, 0xdc, 0xdb, 0xd9, 0xd7,
+ 0x95, 0x93, 0x91, 0x8f, 0x8d, 0x8b, 0x89, 0x86,
+ 0xc4, 0xc2, 0xc0, 0xbe, 0xbb, 0xb9, 0xb7, 0xb5,
+ 0x72, 0x70, 0x6e, 0x6b, 0x69, 0x67, 0x64, 0x62,
+
+ 0x9f, 0x9d, 0x9b, 0x98, 0x96, 0x94, 0x91, 0x8f,
+ 0x4d, 0x4a, 0x48, 0x46, 0x44, 0x41, 0x3f, 0x3d,
+ 0x7b, 0x79, 0x76, 0x74, 0x72, 0x70, 0x6e, 0x6c,
+ 0x2a, 0x28, 0x26, 0x24, 0x23, 0x21, 0x1f, 0x1d,
+ 0x5c, 0x5a, 0x58, 0x57, 0x55, 0x54, 0x52, 0x51,
+ 0x10, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x47, 0x46, 0x45, 0x44, 0x44, 0x43, 0x42, 0x42,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41,
+ 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+ 0x10, 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a,
+ 0x5c, 0x5d, 0x5f, 0x61, 0x63, 0x64, 0x66, 0x68,
+ 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x39,
+ 0x7b, 0x7d, 0x7f, 0x81, 0x84, 0x86, 0x88, 0x8a,
+ 0x4d, 0x4f, 0x51, 0x54, 0x56, 0x58, 0x5b, 0x5d
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Wavetable.h Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,38 @@
+#ifndef WAVETABLE_H_
+#define WAVETABLE_H_
+
+#define WAVETABLE_LENGTH 256
+
+class Wavetable {
+public:
+ typedef enum {
+ Sine = 0,
+ Square = 1,
+ Pulse1_3 = 2,
+ Pulse1_7 = 3,
+ Sawtooth = 4,
+ Triangle = 5,
+ CoarseTriangle = 6,
+ SpikedSine = 7,
+ Noise = 8,
+ LowPeriodNoise = 9
+ } wavetype_t;
+ typedef struct {
+ wavetype_t wavetype;
+ uint8_t decaySpeed;
+ uint8_t sustainLevel;
+ } wave_t;
+
+ static const uint8_t* const waveTableList[];
+ static wave_t const waveDefList[128];
+ static uint8_t const sineTable[WAVETABLE_LENGTH];
+ static uint8_t const squareTable[WAVETABLE_LENGTH];
+ static uint8_t const pulse1_3Table[WAVETABLE_LENGTH];
+ static uint8_t const pulse1_7Table[WAVETABLE_LENGTH];
+ static uint8_t const sawtoothTable[WAVETABLE_LENGTH];
+ static uint8_t const triangleTable[WAVETABLE_LENGTH];
+ static uint8_t const coarseTriangleTable[WAVETABLE_LENGTH];
+ static uint8_t const spikedSineTable[WAVETABLE_LENGTH];
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/debug.cpp Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,61 @@
+#include <stdarg.h>
+#include "mbed.h"
+#include "defs.h"
+#include "RingBuffer.h"
+#include "debug.h"
+#include "note.h"
+
+extern Serial console;
+extern note_t noteSent[NUM_INSTRUMENT];
+extern RingBuffer<char> buffer;
+extern dumpmode_t dumpMode;
+
+void checkBuffer() {
+ size_t bufferCount = buffer.getItemCount();
+ size_t bufferCountToDisplay;
+ char* bufferPtr = NULL;
+
+ if (bufferCount) {
+ bufferPtr = buffer.peek();
+ bufferCountToDisplay = bufferCount > 16 ? 16 : bufferCount;
+
+ console.printf("* %u byte%c in buffer", bufferCount,
+ bufferCount == 1 ? '\0' : 's');
+ if (bufferCount > 16) {
+ console.printf(". The first 16 bytes:\r\n ");
+ } else {
+ console.printf(":\r\n ");
+ }
+ for (uint8_t i = 0; i < bufferCountToDisplay; i++) {
+ console.printf(" %02X", *bufferPtr++);
+ }
+ console.printf("h\r\n");
+ } else {
+ console.printf("* Buffer is empty.\r\n");
+ }
+}
+
+void dumpInstrumentList() {
+ char const channelLetter[] = {
+ '-', '1', '2', '3', '4', '5', '6', '7', '8',
+ '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G'
+ };
+
+ if (dumpMode == DUMP_INST_DETAIL) {
+ console.printf("\r\n");
+ for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
+ if (noteSent[i].channel) {
+ console.printf("#%2d ch=%2d n=%02Xh v=%02Xh ms=%lu\r\n",
+ i, noteSent[i].channel, noteSent[i].noteNumber,
+ noteSent[i].velocity, noteSent[i].noteOnMs);
+ } else {
+ console.printf("#%2d not in use\r\n", i);
+ }
+ }
+ } else if (dumpMode == DUMP_INST) {
+ for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
+ console.printf(" %c", channelLetter[noteSent[i].channel]);
+ }
+ console.printf("\r\n");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debug.h Sun Nov 09 08:00:33 2014 +0000 @@ -0,0 +1,7 @@ +#ifndef DEBUG_H_ +#define DEBUG_H_ + +void checkBuffer(); +void dumpInstrumentList(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/defs.h Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,19 @@
+#define BUFFER_LENGTH 2048
+#define SYSEX_BUFFER_LENGTH 128
+
+#define NUM_PERFORMER 1
+#define NUM_INSTRUMENT_IN_PERFORMER 16
+#define NUM_INSTRUMENT ((NUM_INSTRUMENT_IN_PERFORMER) * (NUM_PERFORMER))
+
+// Power down unused uC's peripherals and Ethernet PHY chip
+#define POWER_SAVE
+// Boost system clock to 120 MHz
+// (valid only when POWER_SAVE is also defined)
+#define CLOCKUP
+
+typedef enum {
+ DUMP_NOTHING,
+ DUMP_EVENTS,
+ DUMP_INST,
+ DUMP_INST_DETAIL
+} dumpmode_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/events.cpp Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,281 @@
+#include <climits>
+#include "mbed.h"
+#include "defs.h"
+#include "Channels.h"
+#include "GeminiCore.h"
+#include "Wavetable.h"
+#include "debug.h"
+#include "events.h"
+#include "frequency.h"
+#include "note.h"
+
+extern Serial console;
+extern volatile uint32_t __countMs;
+extern GeminiCore gemCore;
+extern Channels ch;
+extern note_t noteSent[NUM_INSTRUMENT];
+extern float const frequencyTable[128];
+extern dumpmode_t dumpMode;
+
+bool isImplementedDrumNoteNumber(uint8_t noteNumber) {
+ switch (noteNumber) {
+ case 0x23: // Acoustic Bass Drum
+ case 0x24: // Bass Drum 1
+ case 0x25: // Side Stick
+ case 0x26: // Acoustic Snare
+ case 0x28: // Electric Snare
+ case 0x29: // Low Floor Tom
+ case 0x2a: // Closed High-hat
+ case 0x2b: // High Floor Tom
+ case 0x2c: // Pedal High-hat
+ case 0x2d: // Low Tom
+ case 0x2e: // Open High-hat
+ case 0x2f: // Low-Mid Tom
+ case 0x30: // High-Mid Tom
+ case 0x31: // Crash Cymbal 1
+ case 0x32: // High Tom
+ case 0x39: // Crash Cymbal 2
+ return true;
+ default:
+ return false;
+ }
+}
+
+void dispatchNoteOff(char messageBytes[3]) {
+ uint16_t targetInst;
+ uint16_t oldest = 0xffff;
+ uint32_t oldestMs = UINT_MAX;
+ uint8_t channel = messageBytes[0] & 0x0f;
+ uint8_t noteNumber = messageBytes[1] & 0x7f;
+
+ // Skip if rhythm channel
+ if (channel == 0x09) {
+ return;
+ }
+
+ // Find an (oldest) Instrument sounding the specified note number
+ for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
+ if (noteSent[i].channel == channel + 1
+ && noteSent[i].noteNumber == noteNumber) {
+ if (noteSent[i].noteOnMs < oldestMs) {
+ oldest = i;
+ oldestMs = noteSent[i].noteOnMs;
+ }
+ }
+ }
+ if (oldest != 0xffff) {
+ noteSent[oldest].noteOnMs = 0;
+ noteSent[oldest].channel = 0;
+ noteSent[oldest].noteNumber = 0;
+ noteSent[oldest].velocity = 0;
+
+ // Send note off message to Performer
+ targetInst = oldest % NUM_INSTRUMENT_IN_PERFORMER;
+ gemCore.noteOff(targetInst);
+
+ if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
+ console.printf("%-12s", "Note off");
+ dumpInstrumentList();
+ }
+ } else {
+ if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
+ console.printf("* No such corresponding note: [ch=%2d n=%02Xh]\r\n",
+ channel + 1, noteNumber);
+ }
+ }
+}
+
+void dispatchNoteOn(char messageBytes[3]) {
+ uint32_t currentMs = __countMs;
+ uint32_t oldestMs = UINT_MAX;
+ uint16_t oldest = 0;
+ uint16_t targetInst;
+ uint8_t channel = messageBytes[0] & 0x0f;
+ uint8_t noteNumber = messageBytes[1] & 0x7f;
+ uint8_t velocity = messageBytes[2] & 0x7f;
+
+ // Skip if unimplemented note on rhythm channel
+ if (channel == 0x09 && !isImplementedDrumNoteNumber(noteNumber)) {
+ return;
+ }
+
+ // Find an available Instrument or the one with the oldest note
+ for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
+ if (noteSent[i].noteOnMs < oldestMs) {
+ oldest = i;
+ oldestMs = noteSent[i].noteOnMs;
+ }
+ }
+
+ if (noteSent[oldest].channel != 0) {
+ if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
+ console.printf("- Purged: #%2d [ch=%2d, n=%02Xh]\r\n",
+ oldest, noteSent[oldest].channel,
+ noteSent[oldest].noteNumber);
+ }
+ }
+
+ noteSent[oldest].noteOnMs = currentMs;
+ noteSent[oldest].channel = channel + 1;
+ noteSent[oldest].noteNumber = messageBytes[1];
+ noteSent[oldest].velocity = velocity & 0x70;
+
+ // Send note on message to Performer
+ targetInst = oldest % NUM_INSTRUMENT_IN_PERFORMER;
+ gemCore.volume(targetInst, ch.getVolume(channel));
+ gemCore.expression(targetInst, ch.getExpression(channel));
+ if (channel == 0x09) {
+ Wavetable::wave_t drumWave = { Wavetable::Noise, 4, 0 };
+ float drumFreq = 200;
+
+ switch (noteNumber) {
+ case 0x23: // Acoustic Bass Drum
+ case 0x24: // Bass Drum 1
+ drumWave.decaySpeed = 4;
+ drumFreq = 7;
+ break;
+ case 0x26: // Acoustic Snare
+ case 0x28: // Electric Snare
+ drumWave.decaySpeed = 4;
+ drumFreq = 35;
+ break;
+ case 0x2a: // Closed High-hat
+ case 0x2c: // Pedal High-hat
+ drumWave.decaySpeed = 3;
+ drumFreq = 200;
+ break;
+ case 0x2e: // Open High-hat
+ drumWave.decaySpeed = 5;
+ drumFreq = 400;
+ break;
+ case 0x31: // Crash Cymbal 1
+ case 0x39: // Crash Cymbal 2
+ drumWave.decaySpeed = 7;
+ drumFreq = 100;
+ break;
+ case 0x29: // Low Floor Tom
+ drumWave.wavetype = Wavetable::Triangle;
+ drumWave.decaySpeed = 5;
+ drumFreq = 120;
+ break;
+ case 0x2b: // High Floor Tom
+ drumWave.wavetype = Wavetable::Triangle;
+ drumWave.decaySpeed = 5;
+ drumFreq = 160;
+ break;
+ case 0x2d: // Low Tom
+ drumWave.wavetype = Wavetable::Triangle;
+ drumWave.decaySpeed = 5;
+ drumFreq = 190;
+ break;
+ case 0x2f: // Low-Mid Tom
+ drumWave.wavetype = Wavetable::Triangle;
+ drumWave.decaySpeed = 5;
+ drumFreq = 220;
+ break;
+ case 0x30: // High-Mid Tom
+ drumWave.wavetype = Wavetable::Triangle;
+ drumWave.decaySpeed = 5;
+ drumFreq = 250;
+ break;
+ case 0x32: // High Tom
+ drumWave.wavetype = Wavetable::Triangle;
+ drumWave.decaySpeed = 5;
+ drumFreq = 280;
+ break;
+ case 0x25: // Side Stick
+ drumWave.decaySpeed = 3;
+ drumFreq = 60;
+ break;
+ default:
+ break;
+ }
+ gemCore.setWave(targetInst, drumWave);
+ gemCore.noteOn(targetInst, drumFreq, velocity);
+ } else {
+ gemCore.setWave(targetInst, ch.getWave(channel));
+ gemCore.noteOn(targetInst, frequencyTable[noteNumber], velocity);
+ }
+
+ if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
+ console.printf("%-12s", "Note on");
+ dumpInstrumentList();
+ }
+}
+
+void sendControlChange(char messageBytes[3]) {
+ uint8_t channel = messageBytes[0] & 0x0f;
+ uint8_t controlNumber = messageBytes[1];
+ uint8_t value = messageBytes[2];
+
+ switch (controlNumber) {
+ case 0x07:
+ ch.setVolume(channel, value);
+ for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) {
+ if (noteSent[i].channel == channel + 1) {
+ gemCore.volume(i, value);
+ }
+ }
+ break;
+ case 0x0b:
+ ch.setExpression(channel, value);
+ for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) {
+ if (noteSent[i].channel == channel + 1) {
+ gemCore.expression(i, value);
+ }
+ }
+ break;
+ default:
+ // @TODO Other program change process goes here
+ break;
+ }
+}
+
+void sendProgramChange(char messageBytes[2]) {
+ uint8_t channel = messageBytes[0] & 0x0f;
+ uint8_t value = messageBytes[1] & 0x7f;
+ Wavetable::wave_t wave = Wavetable::waveDefList[value];
+
+ // Change channel wave type
+ ch.setWave(channel, wave);
+}
+
+void sendPitchBend(char messageBytes[3]) {
+ // @TODO
+}
+
+void allNoteOff(char messageBytes[3]) {
+ for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
+ if (noteSent[i].channel == (messageBytes[0] & 0x0f) + 1) {
+ noteSent[i].noteOnMs = 0;
+ noteSent[i].channel = 0;
+ noteSent[i].noteNumber = 0;
+ noteSent[i].velocity = 0;
+ gemCore.noteOff(i);
+ }
+ }
+
+ if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
+ console.printf("%-12s", "All note off");
+ dumpInstrumentList();
+ }
+}
+
+void sendSystemReset() {
+ for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
+ noteSent[i].noteOnMs = 0;
+ noteSent[i].channel = 0;
+ noteSent[i].noteNumber = 0;
+ noteSent[i].velocity = 0;
+ }
+
+ if (dumpMode == DUMP_INST || dumpMode == DUMP_INST_DETAIL) {
+ console.printf("%-12s", "System reset");
+ dumpInstrumentList();
+ }
+ for (uint8_t i = 0; i < NUM_INSTRUMENT; i++) {
+ gemCore.noteOff(i);
+ gemCore.volume(i, 100);
+ gemCore.expression(i, 127);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/events.h Sun Nov 09 08:00:33 2014 +0000 @@ -0,0 +1,13 @@ +#ifndef EVENTS_H_ +#define EVENTS_H_ + +void dispatchNoteOff(char messageBytes[3]); +void dispatchNoteOn(char messageBytes[3]); +void sendControlChange(char messageBytes[3]); +void sendProgramChange(char messageBytes[3]); +void sendPitchBend(char messageBytes[3]); +void sendSystemReset(); +void allNoteOff(char messageBytes[3]); +void dumpInstrumentList(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/frequency.h Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,135 @@
+#ifndef FREQUENCY_H_
+#define FREQUENCY_H_
+
+float const frequencyTable[128] = {
+ 8.175799, // 0 (C -1)
+ 8.661957, // 1 (Db-1)
+ 9.177024, // 2 (D -1)
+ 9.722718, // 3 (Eb-1)
+ 10.300861, // 4 (E -1)
+ 10.913382, // 5 (F -1)
+ 11.562326, // 6 (Gb-1)
+ 12.249857, // 7 (G -1)
+ 12.978272, // 8 (Ab-1)
+ 13.750000, // 9 (A -1)
+ 14.567618, // 10 (Bb-1)
+ 15.433853, // 11 (B -1)
+ 16.351598, // 12 (C 0)
+ 17.323914, // 13 (Db 0)
+ 18.354048, // 14 (D 0)
+ 19.445436, // 15 (Eb 0)
+ 20.601722, // 16 (E 0)
+ 21.826764, // 17 (F 0)
+ 23.124651, // 18 (Gb 0)
+ 24.499715, // 19 (G 0)
+ 25.956544, // 20 (Ab 0)
+ 27.500000, // 21 (A 0)
+ 29.135235, // 22 (Bb 0)
+ 30.867706, // 23 (B 0)
+ 32.703196, // 24 (C 1)
+ 34.647829, // 25 (Db 1)
+ 36.708096, // 26 (D 1)
+ 38.890873, // 27 (Eb 1)
+ 41.203445, // 28 (E 1)
+ 43.653529, // 29 (F 1)
+ 46.249303, // 30 (Gb 1)
+ 48.999429, // 31 (G 1)
+ 51.913087, // 32 (Ab 1)
+ 55.000000, // 33 (A 1)
+ 58.270470, // 34 (Bb 1)
+ 61.735413, // 35 (B 1)
+ 65.406391, // 36 (C 2)
+ 69.295658, // 37 (Db 2)
+ 73.416192, // 38 (D 2)
+ 77.781746, // 39 (Eb 2)
+ 82.406889, // 40 (E 2)
+ 87.307058, // 41 (F 2)
+ 92.498606, // 42 (Gb 2)
+ 97.998859, // 43 (G 2)
+ 103.826174, // 44 (Ab 2)
+ 110.000000, // 45 (A 2)
+ 116.540940, // 46 (Bb 2)
+ 123.470825, // 47 (B 2)
+ 130.812783, // 48 (C 3)
+ 138.591315, // 49 (Db 3)
+ 146.832384, // 50 (D 3)
+ 155.563492, // 51 (Eb 3)
+ 164.813778, // 52 (E 3)
+ 174.614116, // 53 (F 3)
+ 184.997211, // 54 (Gb 3)
+ 195.997718, // 55 (G 3)
+ 207.652349, // 56 (Ab 3)
+ 220.000000, // 57 (A 3)
+ 233.081881, // 58 (Bb 3)
+ 246.941651, // 59 (B 3)
+ 261.625565, // 60 (C 4)
+ 277.182631, // 61 (Db 4)
+ 293.664768, // 62 (D 4)
+ 311.126984, // 63 (Eb 4)
+ 329.627557, // 64 (E 4)
+ 349.228231, // 65 (F 4)
+ 369.994423, // 66 (Gb 4)
+ 391.995436, // 67 (G 4)
+ 415.304698, // 68 (Ab 4)
+ 440.000000, // 69 (A 4)
+ 466.163762, // 70 (Bb 4)
+ 493.883301, // 71 (B 4)
+ 523.251131, // 72 (C 5)
+ 554.365262, // 73 (Db 5)
+ 587.329536, // 74 (D 5)
+ 622.253967, // 75 (Eb 5)
+ 659.255114, // 76 (E 5)
+ 698.456463, // 77 (F 5)
+ 739.988845, // 78 (Gb 5)
+ 783.990872, // 79 (G 5)
+ 830.609395, // 80 (Ab 5)
+ 880.000000, // 81 (A 5)
+ 932.327523, // 82 (Bb 5)
+ 987.766603, // 83 (B 5)
+ 1046.502261, // 84 (C 6)
+ 1108.730524, // 85 (Db 6)
+ 1174.659072, // 86 (D 6)
+ 1244.507935, // 87 (Eb 6)
+ 1318.510228, // 88 (E 6)
+ 1396.912926, // 89 (F 6)
+ 1479.977691, // 90 (Gb 6)
+ 1567.981744, // 91 (G 6)
+ 1661.218790, // 92 (Ab 6)
+ 1760.000000, // 93 (A 6)
+ 1864.655046, // 94 (Bb 6)
+ 1975.533205, // 95 (B 6)
+ 2093.004522, // 96 (C 7)
+ 2217.461048, // 97 (Db 7)
+ 2349.318143, // 98 (D 7)
+ 2489.015870, // 99 (Eb 7)
+ 2637.020455, // 100 (E 7)
+ 2793.825851, // 101 (F 7)
+ 2959.955382, // 102 (Gb 7)
+ 3135.963488, // 103 (G 7)
+ 3322.437581, // 104 (Ab 7)
+ 3520.000000, // 105 (A 7)
+ 3729.310092, // 106 (Bb 7)
+ 3951.066410, // 107 (B 7)
+ 4186.009045, // 108 (C 8)
+ 4434.922096, // 109 (Db 8)
+ 4698.636287, // 110 (D 8)
+ 4978.031740, // 111 (Eb 8)
+ 5274.040911, // 112 (E 8)
+ 5587.651703, // 113 (F 8)
+ 5919.910763, // 114 (Gb 8)
+ 6271.926976, // 115 (G 8)
+ 6644.875161, // 116 (Ab 8)
+ 7040.000000, // 117 (A 8)
+ 7458.620184, // 118 (Bb 8)
+ 7902.132820, // 119 (B 8)
+ 8372.018090, // 120 (C 9)
+ 8869.844191, // 121 (Db 9)
+ 9397.272573, // 122 (D 9)
+ 9956.063479, // 123 (Eb 9)
+ 10548.081821, // 124 (E 9)
+ 11175.303406, // 125 (F 9)
+ 11839.821527, // 126 (Gb 9)
+ 12543.853951 // 127 (G 9)
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,222 @@
+#include "mbed.h"
+#include "ClockControl.h"
+#include "EthernetPowerControl.h"
+#include "PowerControl.h"
+#include "defs.h"
+#include "Channels.h"
+#include "GeminiCore.h"
+#include "RingBuffer.h"
+#include "debug.h"
+#include "events.h"
+#include "note.h"
+#include "parser.h"
+
+Serial pc(/*Tx*/ USBTX, /*Rx*/ USBRX);
+Serial midiIn(/*Tx*/ p28, /*Rx*/ p27); // @TODO final: p13, p14
+Serial console(/*Tx*/ p13, /*Rx*/ p14); // @TODO final: p28, p27
+DigitalIn swSerialSelect();
+DigitalIn swPanic(p15);
+BusOut led(/*Left*/ LED1, LED2, LED3, LED4 /*Right*/);
+BusOut ledBar(p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p29, p30);
+SPI spi(/*MOSI*/ p5, /*MISO*/ p6, /*SCK*/ p7);
+DigitalOut dacCs(p8);
+Ticker t1ms;
+Ticker tSample;
+volatile uint32_t __countMs;
+bool serSource;
+GeminiCore gemCore(NUM_INSTRUMENT_IN_PERFORMER);
+RingBuffer<char> buffer(BUFFER_LENGTH);
+Channels ch;
+note_t noteSent[NUM_INSTRUMENT];
+uint32_t receivedBytes;
+dumpmode_t dumpMode;
+
+void sampleOut() {
+ // soundOut.write(gemCore.makeSample() / 65536.0);
+ dacCs = 0;
+ spi.write(0x3000 | gemCore.makeSample() >> 4);
+ dacCs = 1;
+}
+
+void count1ms() {
+ __countMs++;
+}
+
+void readMidiIn() {
+ char c;
+
+ // Put a MIDI input byte into buffer if available
+ if ((serSource ? midiIn : pc).readable()) {
+ c = (serSource ? midiIn : pc).getc();
+ receivedBytes++;
+
+ // Discard if input byte is an active sensing message
+ if (c != 0xfe) {
+ buffer.write(c);
+ }
+ }
+}
+
+void setup() {
+#ifdef POWER_SAVE
+ // Power down Ethernet PHY chip
+ PHY_PowerDown();
+
+ // Power down unused peripherals
+ Peripheral_PowerDown(0x40067200);
+#endif
+
+#if defined(POWER_SAVE) && defined(CLOCKUP)
+ // Change clock speed to 120 MHz
+ setSystemFrequency(0x03, 0x01, 15, 1);
+#endif
+
+ // Enable pull-up at switch inputs
+ // swSerialSelect.mode(PullUp);
+ swPanic.mode(PullUp);
+ wait(0.2); // Wait a moment for stability
+
+ // Read serial selector switch
+ serSource = 1; // @TODO final: serSource = swSerialSelect;
+
+ // Open selected port
+ if (serSource) {
+ // Use MIDI port when high
+ midiIn.baud(38400); // @TODO final: midiIn.baud(31250);
+ midiIn.format(8, Serial::None, 1);
+ midiIn.attach(readMidiIn, Serial::RxIrq);
+ } else {
+ // Use serial MIDI when low
+ pc.baud(38400);
+ pc.format(8, Serial::None, 1);
+ pc.attach(readMidiIn, Serial::RxIrq);
+ }
+ receivedBytes = 0;
+
+ // Setup console
+ console.baud(115200);
+ console.format(8, Serial::None, 1);
+ dumpMode = DUMP_NOTHING;
+
+ // Setup SPI
+ spi.format(16, 0);
+ spi.frequency(12000000);
+ dacCs = 1;
+
+ // Initialize channels
+ ch.initializeAll();
+
+ // Initialize note status
+ for (uint16_t i = 0; i < NUM_INSTRUMENT; i++) {
+ noteSent[i].noteOnMs = 0;
+ noteSent[i].channel = 0;
+ noteSent[i].noteNumber = 0;
+ noteSent[i].velocity = 0;
+ }
+
+ // Start Timer
+ __countMs = 0;
+ t1ms.attach_us(&count1ms, 1000);
+
+ // Start playback & attach interrupt
+#if defined(POWER_SAVE) && defined(CLOCKUP)
+ tSample.attach_us(&sampleOut, 1250000 / GeminiCore::samplingRate);
+#else
+ tSample.attach_us(&sampleOut, 1000000 / GeminiCore::samplingRate);
+#endif
+}
+
+void consoleOperations(uint8_t c) {
+ switch (c) {
+ case 'b':
+ checkBuffer();
+ break;
+ case 'e':
+ if (dumpMode != DUMP_EVENTS) {
+ dumpMode = DUMP_EVENTS;
+ } else {
+ dumpMode = DUMP_NOTHING;
+ }
+ break;
+ case 'f':
+ buffer.flush();
+ console.printf("* Buffer flushed.\r\n");
+ break;
+ case 'i':
+ if (dumpMode == DUMP_INST) {
+ dumpMode = DUMP_INST_DETAIL;
+ } else if (dumpMode == DUMP_INST_DETAIL) {
+ dumpMode = DUMP_NOTHING;
+ } else {
+ dumpMode = DUMP_INST;
+ }
+ break;
+ case 'p':
+ // Panic
+ for (uint8_t i = 0; i < NUM_INSTRUMENT_IN_PERFORMER; i++) {
+ gemCore.noteOff(i);
+ noteSent[i].noteOnMs = 0;
+ noteSent[i].channel = 0;
+ noteSent[i].noteNumber = 0;
+ noteSent[i].velocity = 0;
+ }
+ break;
+ case 'r':
+ console.printf("* Received MIDI bytes: %u\r\n", receivedBytes);
+ break;
+ default: break;
+ }
+}
+
+void loop() {
+ static uint32_t lastPollSwitchMs = __countMs;
+ static bool prevstatePanic = 0;
+ bool statePanic;
+
+ // Serial console
+ if (console.readable()) {
+ consoleOperations(console.getc());
+ }
+
+ // Poll switches
+ if (__countMs > lastPollSwitchMs + 20) {
+ lastPollSwitchMs = __countMs;
+
+ statePanic = swPanic;
+
+ // Panic (all note off and flush buffer)
+ if (!statePanic && prevstatePanic) {
+ buffer.flush();
+ for (uint8_t i = 0; i < NUM_INSTRUMENT_IN_PERFORMER; i++) {
+ gemCore.noteOff(i);
+ noteSent[i].noteOnMs = 0;
+ noteSent[i].channel = 0;
+ noteSent[i].noteNumber = 0;
+ noteSent[i].velocity = 0;
+ }
+ }
+
+ // Preserve this time's switch states
+ prevstatePanic = statePanic;
+ }
+
+ // Parse MIDI messages
+ parseMessage(buffer);
+
+ // LEDs
+ uint8_t busyCount = 0;
+ for (uint8_t i = 0; i < NUM_INSTRUMENT_IN_PERFORMER; i++) {
+ if (noteSent[i].noteOnMs) {
+ busyCount++;
+ }
+ }
+ ledBar = (1 << busyCount) - 1;
+}
+
+int main() {
+ setup();
+
+ while (1) {
+ loop();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Sun Nov 09 08:00:33 2014 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/024bf7f99721 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/note.h Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,11 @@
+#ifndef NOTE_H_
+#define NOTE_H_
+
+typedef struct {
+ uint32_t noteOnMs;
+ uint8_t channel; // channel. 0: not in use, 1..16 is valid
+ uint8_t noteNumber;
+ uint8_t velocity;
+} note_t;
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/parser.cpp Sun Nov 09 08:00:33 2014 +0000
@@ -0,0 +1,236 @@
+#include <stdarg.h>
+#include "mbed.h"
+#include "defs.h"
+#include "RingBuffer.h"
+#include "debug.h"
+#include "events.h"
+#include "parser.h"
+
+extern Serial console;
+extern bool serSource;
+extern dumpmode_t dumpMode;
+char const sysExGmSystemOn[6] = { 0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7 };
+char const sysExGsReset[11] = { 0xf0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41, 0xf7 };
+char const sysExXgSystemOn[9] = { 0xf0, 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00, 0xf7 };
+
+void parseMessage(RingBuffer<char>& buffer) {
+ static char lastStatusByte;
+ char c;
+ char messageBytes[3];
+ char sysExBuffer[SYSEX_BUFFER_LENGTH];
+ bool runningStatus = false;
+
+ c = *buffer.peek();
+
+ // Running status
+ if (!(c & 0x80)) {
+ runningStatus = true;
+ // Restore previous status byte
+ c = lastStatusByte;
+ }
+
+ switch (c & 0xf0) {
+ case 0x80: // Note off
+ if (buffer.getItemCount() >= 3 - runningStatus) {
+ messageBytes[0] = runningStatus ? c : *buffer.read();
+ for (uint8_t i = 1; i < 3; i++) {
+ messageBytes[i] = *buffer.read();
+ }
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("%-19s %c%02X %02X %02Xh\r\n",
+ "Note off", runningStatus ? '*' : ' ',
+ messageBytes[0], messageBytes[1], messageBytes[2]);
+ }
+ dispatchNoteOff(messageBytes);
+ lastStatusByte = c;
+ }
+ break;
+ case 0x90: // Note on
+ if (buffer.getItemCount() >= 3 - runningStatus) {
+ messageBytes[0] = runningStatus ? c : *buffer.read();
+ for (uint8_t i = 1; i < 3; i++) {
+ messageBytes[i] = *buffer.read();
+ }
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("%-19s %c%02X %02X %02Xh\r\n",
+ "Note on", runningStatus ? '*' : ' ',
+ messageBytes[0], messageBytes[1], messageBytes[2]);
+ }
+ if (messageBytes[2] == 0x00) {
+ dispatchNoteOff(messageBytes);
+ } else {
+ dispatchNoteOn(messageBytes);
+ }
+ lastStatusByte = c;
+ }
+ break;
+ case 0xa0: // Polyphonic pressure
+ // Not supported
+ if (buffer.getItemCount() >= 3 - runningStatus) {
+ messageBytes[0] = runningStatus ? c : *buffer.read();
+ for (uint8_t i = 1; i < 3; i++) {
+ messageBytes[i] = *buffer.read();
+ }
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("%-19s %c%02X %02X %02Xh\r\n",
+ "Poly pressure", runningStatus ? '*' : ' ',
+ messageBytes[0], messageBytes[1], messageBytes[2]);
+ }
+ lastStatusByte = c;
+ }
+ break;
+ case 0xb0: // Control change
+ if (buffer.getItemCount() >= 3 - runningStatus) {
+ messageBytes[0] = runningStatus ? c : *buffer.read();
+ for (uint8_t i = 1; i < 3; i++) {
+ messageBytes[i] = *buffer.read();
+ }
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("%-19s %c%02X %02X %02Xh\r\n",
+ "Control change", runningStatus ? '*' : ' ',
+ messageBytes[0], messageBytes[1], messageBytes[2]);
+ }
+ switch (messageBytes[1]) {
+ case 0x78: // All sound off
+ case 0x7b: // All note off
+ allNoteOff(messageBytes);
+ break;
+ default:
+ sendControlChange(messageBytes);
+ break;
+ }
+ lastStatusByte = c;
+ }
+ break;
+ case 0xc0: // Program change
+ if (buffer.getItemCount() >= 2 - runningStatus) {
+ messageBytes[0] = runningStatus ? c : *buffer.read();
+ for (uint8_t i = 1; i < 2; i++) {
+ messageBytes[i] = *buffer.read();
+ }
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("%-19s %c%02X %02X __h\r\n",
+ "Program change", runningStatus ? '*' : ' ',
+ messageBytes[0], messageBytes[1]);
+ }
+ sendProgramChange(messageBytes);
+ lastStatusByte = c;
+ }
+ break;
+ case 0xd0: // Channel pressure
+ // Not supported
+ if (buffer.getItemCount() >= 2 - runningStatus) {
+ messageBytes[0] = runningStatus ? c : *buffer.read();
+ for (uint8_t i = 1; i < 2; i++) {
+ messageBytes[i] = *buffer.read();
+ }
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("%-19s %c%02X %02X __h\r\n",
+ "Ch pressure", runningStatus ? '*' : ' ',
+ messageBytes[0], messageBytes[1]);
+ }
+ lastStatusByte = c;
+ }
+ break;
+ case 0xe0: // Pitch bend
+ if (buffer.getItemCount() >= 3 - runningStatus) {
+ messageBytes[0] = runningStatus ? c : *buffer.read();
+ for (uint8_t i = 1; i < 3; i++) {
+ messageBytes[i] = *buffer.read();
+ }
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("%-19s %c%02X %02X %02Xh\r\n",
+ "Pitch bend", runningStatus ? '*' : ' ',
+ messageBytes[0], messageBytes[1], messageBytes[2]);
+ }
+ sendPitchBend(messageBytes);
+ lastStatusByte = c;
+ }
+ break;
+ case 0xf0:
+ switch (c) {
+ case 0xf0: // SysEx message
+ if (buffer.find(0xf7) == -1) {
+ break;
+ }
+
+ // Extract "F0 ** F7h" message block from buffer
+ extractSysExMessage(buffer, sysExBuffer);
+ if (strncmp(sysExBuffer, sysExGmSystemOn, 6) == 0) {
+ // Matches "GM System On" SysEx message
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("SysEx message: GM System On\r\n");
+ }
+ sendSystemReset();
+ } else if (strncmp(sysExBuffer, sysExGsReset, 11) == 0) {
+ // Matches "GS Reset" SysEx message
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("SysEx message: GS Reset\r\n");
+ }
+ sendSystemReset();
+ } else if (strncmp(sysExBuffer, sysExXgSystemOn, 9) == 0) {
+ // Matches "XG System On" SysEx message
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("SysEx message: XG System On\r\n");
+ }
+ sendSystemReset();
+ } else {
+ if (dumpMode == DUMP_EVENTS) {
+ console.printf("Unsupported SysEx message\r\n");
+ }
+ }
+ break;
+ case 0xf1: // MTC quarter frame
+ case 0xf3: // Song select
+ // Not supported
+ buffer.read();
+ buffer.read();
+ break;
+ case 0xf2: // Song position
+ // Not supported
+ buffer.read();
+ buffer.read();
+ buffer.read();
+ break;
+ case 0xf4: case 0xf5: case 0xf9: case 0xfd: // Undefined
+ case 0xf6: // Tune request
+ case 0xfa: // Start
+ case 0xfb: // Continue
+ case 0xfc: // Stop
+ buffer.read();
+ break;
+ case 0xfe: // Active sensing
+ buffer.read();
+ break;
+ case 0xff: // System reset
+ // Discard message (@todo)
+ buffer.read();
+ sendSystemReset();
+ }
+ }
+}
+
+uint32_t extractSysExMessage(RingBuffer<char>& buffer, char* msg) {
+ uint32_t extractedLength;
+ char* c = NULL;
+
+ // Check if the first byte matches SysEx start byte (0xf0)
+ c = buffer.read();
+ if (!c) return 0;
+ if (*c != 0xf0) {
+ return 0;
+ } else {
+ msg[0] = *c;
+ }
+
+ // Read buffer until reaching SysEx end byte (0xf7)
+ extractedLength = 1;
+ while (extractedLength < SYSEX_BUFFER_LENGTH) {
+ c = buffer.read();
+ if (!c) break;
+
+ msg[extractedLength++] = *c;
+ if (*c == 0xf7) return extractedLength;
+ }
+ return 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/parser.h Sun Nov 09 08:00:33 2014 +0000 @@ -0,0 +1,9 @@ +#ifndef PARSER_H_ +#define PARSER_H_ + +#include "RingBuffer.h" + +void parseMessage(RingBuffer<char>&); +uint32_t extractSysExMessage(RingBuffer<char>&, char*); + +#endif