Music Engine plays Music Macro Language compositions in the background
Dependents: RETRO_BallsAndPaddle RETRO_BallAndHoles MusicBoxForFathersDay USBSec_mbed-os_dev
Music Engine is a simply library to execute Music Macro Language sequences asynchronously. Learn more about MML on wikipedia http://en.wikipedia.org/wiki/Music_Macro_Language
The following sample plays a simple tune
#include "mbed.h" #include "MusicEngine.h" // Play music on MCU Pin 0.18 // The pin should support PWM MusicEngine music(P0_18); main() { music.play("T224L8O5CL16>C<P16GP16L8EL16P16>C<GP16L8E.L16P16L8C#L16>C#<P16G#P16L8FL16P16>C#<G#P16L8F.L16P16L8CL16>C<P16GP16L8EL16P16>C<GP16L8E.L16P16D#EFP16FF#GP16GG#AP16L8>C<P8L4>C"); while(1) { } }
MusicEngine.cpp
- Committer:
- taylorza
- Date:
- 2015-02-06
- Revision:
- 2:4f7c4255997a
- Parent:
- 0:d873d5d62d3b
File content as of revision 2:4f7c4255997a:
/////////////////////////////////////////////////////////////////////////////// // Retro Music Engine // Author: Chris Taylor (taylorza) #include "MusicEngine.h" #include <cctype> const float MusicEngine::WHOLE_NOTE_DURATION = 1.0f; const float MusicEngine::QUARTER_NOTE_DURATION = MusicEngine::WHOLE_NOTE_DURATION / 4.0f; const float MusicEngine::QUARTER_NOTES_PER_MINUTE = 60.0f / MusicEngine::QUARTER_NOTE_DURATION; const float MusicEngine::DEFAULT_TIMING = 7.0f / 8.0f; const float MusicEngine::LEGATO_TIMING = 1.0f; const float MusicEngine::STACCATO_TIMING = 3.0f / 4.0f; const int MusicEngine::NOTE_REST = 0; const int MusicEngine::NOTE_C = 1; const int MusicEngine::NOTE_CS = 2; const int MusicEngine::NOTE_D = 3; const int MusicEngine::NOTE_DS = 4; const int MusicEngine::NOTE_E = 5; const int MusicEngine::NOTE_F = 6; const int MusicEngine::NOTE_FS = 7; const int MusicEngine::NOTE_G = 8; const int MusicEngine::NOTE_GS = 9; const int MusicEngine::NOTE_A = 10; const int MusicEngine::NOTE_AS = 11; const int MusicEngine::NOTE_B = 12; MusicEngine::MusicEngine(PinName pin) : _pwm(pin), _isPlaying(false) { _pwm.period_ms(1); _pwm.write(0.0); } void MusicEngine::play(char *mml) { __disable_irq(); _isPlaying = false; _mml = mml; _mmlIndex = 0; _octave = 4; _duration = QUARTER_NOTE_DURATION; _durationRatio = DEFAULT_TIMING; _tempo = 120; _pwm.period(0); _pwm = 0.5f; _pause = 0; _isPlaying = true; __enable_irq(); MusicEngine::executeCommand(); } void MusicEngine::stop() { __disable_irq(); _isPlaying = false; __enable_irq(); } void MusicEngine::executeCommand() { if (_pause != 0) { _pwm.period(0); _pwm = 0.0f; _scheduler.attach(this, &MusicEngine::executeCommand, _pause); _pause = 0; } else { int freqIndex = -1; do { skipWhiteSpace(); switch(getChar()) { case 'a': freqIndex = NOTE_A; break; case 'b': freqIndex = NOTE_B; break; case 'c': freqIndex = NOTE_C; break; case 'd': freqIndex = NOTE_D; break; case 'e': freqIndex = NOTE_E; break; case 'f': freqIndex = NOTE_F; break; case 'g': freqIndex = NOTE_G; break; case 'p': case 'r': freqIndex = NOTE_REST; break; case 'l': if (isdigit(peekChar())) _duration = (float)WHOLE_NOTE_DURATION / getNumber(1, 64); break; case 'o': if (isdigit(peekChar())) _octave = getNumber(0, 7); break; case 't': if (isdigit(peekChar())) _tempo = getNumber(32, 255); break; case 'm': switch(getChar()) { case 'n': _durationRatio = DEFAULT_TIMING; break; case 'l': _durationRatio = LEGATO_TIMING; break; case 's': _durationRatio = STACCATO_TIMING; break; } break; case 'n': if (isdigit(peekChar())) freqIndex = getNumber(0, 96); break; case '<': --_octave; if (_octave < 0) _octave = 0; break; case '>': ++_octave; if (_octave > 7) _octave = 7; break; case '\0': _isPlaying = false; break; } if (!_isPlaying) { _pwm.period_ms(1); _pwm.write(0.0); _completionCallback.call(); return; } float durationRatio = _durationRatio; float duration = _duration; if (freqIndex != -1) { switch(getChar()) { case '+': case '#': ++freqIndex; break; case '-': --freqIndex; break; case '.': durationRatio = 3.0f / 2.0f; while(peekChar() == '.') { durationRatio *= 3.0f / 2.0f; getChar(); } break; default: rewind(); break; } if (isdigit(peekChar())) { duration = WHOLE_NOTE_DURATION / getNumber(1, 64); } if (freqIndex != NOTE_REST) { _pwm.period(PERIOD_TABLE[freqIndex + (_octave * 12)]); _pwm = 0.5; } duration *= (QUARTER_NOTES_PER_MINUTE / _tempo); _scheduler.attach(this, &MusicEngine::executeCommand, duration * durationRatio); _pause = duration * (1 - durationRatio); } } while (freqIndex == -1); } } int MusicEngine::getNumber(int min, int max) { char ch; int value = 0; while ((ch = getChar()) != 0) { if (!isdigit(ch)) { rewind(); break; } int digit = (int)ch - 48; value = (value * 10) + digit; } value = value < min ? min : value > max ? max : value; return value; } void MusicEngine::skipWhiteSpace() { while (isspace(peekChar())) getChar(); } char MusicEngine::getChar() { return tolower(_mml[_mmlIndex++]); } char MusicEngine::peekChar() { return tolower(_mml[_mmlIndex]); } void MusicEngine::rewind() { --_mmlIndex; } const float MusicEngine::PERIOD_TABLE[] = { 0, //1 2 3 4 5 6 7 8 9 10 11 12 //C C# D D# E F F# G G# A A# B 1.0f / 16.35f, 1.0f / 17.32f, 1.0f / 18.35f, 1.0f / 19.45f, 1.0f / 20.60f, 1.0f / 21.83f, 1.0f / 23.12f, 1.0f / 24.50f, 1.0f / 25.96f, 1.0f / 27.50f, 1.0f / 29.14f, 1.0f / 30.87f, // Octave 0 1.0f / 32.70f, 1.0f / 34.65f, 1.0f / 36.71f, 1.0f / 38.89f, 1.0f / 41.20f, 1.0f / 43.65f, 1.0f / 46.25f, 1.0f / 49.00f, 1.0f / 51.91f, 1.0f / 55.00f, 1.0f / 58.27f, 1.0f / 61.74f, // Octave 1 1.0f / 65.41f, 1.0f / 69.30f, 1.0f / 73.42f, 1.0f / 77.78f, 1.0f / 82.41f, 1.0f / 87.31f, 1.0f / 92.50f, 1.0f / 98.00f, 1.0f / 103.83f, 1.0f / 110.00f, 1.0f / 116.54f, 1.0f / 123.47f, // Octave 2 1.0f / 130.81f, 1.0f / 138.59f, 1.0f / 146.83f, 1.0f / 155.56f, 1.0f / 164.81f, 1.0f / 174.62f, 1.0f / 185.00f, 1.0f / 196.00f, 1.0f / 207.65f, 1.0f / 220.00f, 1.0f / 233.08f, 1.0f / 246.94f, // Octave 3 1.0f / 261.63f, 1.0f / 277.18f, 1.0f / 293.67f, 1.0f / 311.13f, 1.0f / 329.63f, 1.0f / 349.23f, 1.0f / 370.00f, 1.0f / 392.00f, 1.0f / 415.31f, 1.0f / 440.00f, 1.0f / 466.17f, 1.0f / 493.89f, // Octave 4 1.0f / 523.25f, 1.0f / 554.37f, 1.0f / 587.33f, 1.0f / 622.26f, 1.0f / 659.26f, 1.0f / 698.46f, 1.0f / 739.99f, 1.0f / 783.99f, 1.0f / 830.61f, 1.0f / 880.00f, 1.0f / 932.33f, 1.0f / 987.77f, // Octave 5 1.0f / 1046.51f, 1.0f / 1108.74f, 1.0f / 1174.67f, 1.0f / 1244.51f, 1.0f / 1318.52f, 1.0f / 1396.92f, 1.0f / 1479.99f, 1.0f / 1567.99f, 1.0f / 1661.23f, 1.0f / 1760.01f, 1.0f / 1864.66f, 1.0f / 1975.54f, // Octave 6 1.0f / 2093.02f, 1.0f / 2217.47f, 1.0f / 2349.33f, 1.0f / 2489.03f, 1.0f / 2637.03f, 1.0f / 2793.84f, 1.0f / 2959.97f, 1.0f / 3135.98f, 1.0f / 3322.45f, 1.0f / 3520.02f, 1.0f / 3729.33f, 1.0f / 3951.09f, // Octave 7 };