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@0:d873d5d62d3b, 2015-02-05 (annotated)
- Committer:
- taylorza
- Date:
- Thu Feb 05 04:18:39 2015 +0000
- Revision:
- 0:d873d5d62d3b
- Child:
- 2:4f7c4255997a
Music Engine Library initial commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
taylorza | 0:d873d5d62d3b | 1 | /////////////////////////////////////////////////////////////////////////////// |
taylorza | 0:d873d5d62d3b | 2 | // Retro Music Engine |
taylorza | 0:d873d5d62d3b | 3 | // Author: Chris Taylor (taylorza) |
taylorza | 0:d873d5d62d3b | 4 | |
taylorza | 0:d873d5d62d3b | 5 | #include "MusicEngine.h" |
taylorza | 0:d873d5d62d3b | 6 | #include <cctype> |
taylorza | 0:d873d5d62d3b | 7 | |
taylorza | 0:d873d5d62d3b | 8 | const float MusicEngine::WHOLE_NOTE_DURATION = 1.0f; |
taylorza | 0:d873d5d62d3b | 9 | const float MusicEngine::QUARTER_NOTE_DURATION = MusicEngine::WHOLE_NOTE_DURATION / 4.0f; |
taylorza | 0:d873d5d62d3b | 10 | const float MusicEngine::QUARTER_NOTES_PER_MINUTE = 60.0f / MusicEngine::QUARTER_NOTE_DURATION; |
taylorza | 0:d873d5d62d3b | 11 | |
taylorza | 0:d873d5d62d3b | 12 | const float MusicEngine::DEFAULT_TIMING = 7.0f / 8.0f; |
taylorza | 0:d873d5d62d3b | 13 | const float MusicEngine::LEGATO_TIMING = 1.0f; |
taylorza | 0:d873d5d62d3b | 14 | const float MusicEngine::STACCATO_TIMING = 3.0f / 4.0f; |
taylorza | 0:d873d5d62d3b | 15 | |
taylorza | 0:d873d5d62d3b | 16 | const int MusicEngine::NOTE_REST = 0; |
taylorza | 0:d873d5d62d3b | 17 | const int MusicEngine::NOTE_C = 1; |
taylorza | 0:d873d5d62d3b | 18 | const int MusicEngine::NOTE_CS = 2; |
taylorza | 0:d873d5d62d3b | 19 | const int MusicEngine::NOTE_D = 3; |
taylorza | 0:d873d5d62d3b | 20 | const int MusicEngine::NOTE_DS = 4; |
taylorza | 0:d873d5d62d3b | 21 | const int MusicEngine::NOTE_E = 5; |
taylorza | 0:d873d5d62d3b | 22 | const int MusicEngine::NOTE_F = 6; |
taylorza | 0:d873d5d62d3b | 23 | const int MusicEngine::NOTE_FS = 7; |
taylorza | 0:d873d5d62d3b | 24 | const int MusicEngine::NOTE_G = 8; |
taylorza | 0:d873d5d62d3b | 25 | const int MusicEngine::NOTE_GS = 9; |
taylorza | 0:d873d5d62d3b | 26 | const int MusicEngine::NOTE_A = 10; |
taylorza | 0:d873d5d62d3b | 27 | const int MusicEngine::NOTE_AS = 11; |
taylorza | 0:d873d5d62d3b | 28 | const int MusicEngine::NOTE_B = 12; |
taylorza | 0:d873d5d62d3b | 29 | |
taylorza | 0:d873d5d62d3b | 30 | MusicEngine::MusicEngine(PinName pin) : |
taylorza | 0:d873d5d62d3b | 31 | _pwm(pin), |
taylorza | 0:d873d5d62d3b | 32 | _isPlaying(false) |
taylorza | 0:d873d5d62d3b | 33 | { |
taylorza | 0:d873d5d62d3b | 34 | _pwm = 0.0f; |
taylorza | 0:d873d5d62d3b | 35 | _pwm.period(0); |
taylorza | 0:d873d5d62d3b | 36 | } |
taylorza | 0:d873d5d62d3b | 37 | |
taylorza | 0:d873d5d62d3b | 38 | void MusicEngine::play(char *mml) |
taylorza | 0:d873d5d62d3b | 39 | { |
taylorza | 0:d873d5d62d3b | 40 | __disable_irq(); |
taylorza | 0:d873d5d62d3b | 41 | _isPlaying = false; |
taylorza | 0:d873d5d62d3b | 42 | _mml = mml; |
taylorza | 0:d873d5d62d3b | 43 | _mmlIndex = 0; |
taylorza | 0:d873d5d62d3b | 44 | _octave = 4; |
taylorza | 0:d873d5d62d3b | 45 | _duration = QUARTER_NOTE_DURATION; |
taylorza | 0:d873d5d62d3b | 46 | _durationRatio = DEFAULT_TIMING; |
taylorza | 0:d873d5d62d3b | 47 | _tempo = 120; |
taylorza | 0:d873d5d62d3b | 48 | _pwm.period(0); |
taylorza | 0:d873d5d62d3b | 49 | _pwm = 0.5f; |
taylorza | 0:d873d5d62d3b | 50 | _pause = 0; |
taylorza | 0:d873d5d62d3b | 51 | _isPlaying = true; |
taylorza | 0:d873d5d62d3b | 52 | __enable_irq(); |
taylorza | 0:d873d5d62d3b | 53 | |
taylorza | 0:d873d5d62d3b | 54 | MusicEngine::executeCommand(); |
taylorza | 0:d873d5d62d3b | 55 | } |
taylorza | 0:d873d5d62d3b | 56 | |
taylorza | 0:d873d5d62d3b | 57 | void MusicEngine::stop() |
taylorza | 0:d873d5d62d3b | 58 | { |
taylorza | 0:d873d5d62d3b | 59 | __disable_irq(); |
taylorza | 0:d873d5d62d3b | 60 | _isPlaying = false; |
taylorza | 0:d873d5d62d3b | 61 | __enable_irq(); |
taylorza | 0:d873d5d62d3b | 62 | } |
taylorza | 0:d873d5d62d3b | 63 | |
taylorza | 0:d873d5d62d3b | 64 | void MusicEngine::executeCommand() |
taylorza | 0:d873d5d62d3b | 65 | { |
taylorza | 0:d873d5d62d3b | 66 | if (_pause != 0) |
taylorza | 0:d873d5d62d3b | 67 | { |
taylorza | 0:d873d5d62d3b | 68 | _pwm.period(0); |
taylorza | 0:d873d5d62d3b | 69 | _pwm = 0.0f; |
taylorza | 0:d873d5d62d3b | 70 | _scheduler.attach(this, &MusicEngine::executeCommand, _pause); |
taylorza | 0:d873d5d62d3b | 71 | _pause = 0; |
taylorza | 0:d873d5d62d3b | 72 | } |
taylorza | 0:d873d5d62d3b | 73 | else |
taylorza | 0:d873d5d62d3b | 74 | { |
taylorza | 0:d873d5d62d3b | 75 | int freqIndex = -1; |
taylorza | 0:d873d5d62d3b | 76 | do |
taylorza | 0:d873d5d62d3b | 77 | { |
taylorza | 0:d873d5d62d3b | 78 | skipWhiteSpace(); |
taylorza | 0:d873d5d62d3b | 79 | switch(getChar()) |
taylorza | 0:d873d5d62d3b | 80 | { |
taylorza | 0:d873d5d62d3b | 81 | case 'a': freqIndex = NOTE_A; break; |
taylorza | 0:d873d5d62d3b | 82 | case 'b': freqIndex = NOTE_B; break; |
taylorza | 0:d873d5d62d3b | 83 | case 'c': freqIndex = NOTE_C; break; |
taylorza | 0:d873d5d62d3b | 84 | case 'd': freqIndex = NOTE_D; break; |
taylorza | 0:d873d5d62d3b | 85 | case 'e': freqIndex = NOTE_E; break; |
taylorza | 0:d873d5d62d3b | 86 | case 'f': freqIndex = NOTE_F; break; |
taylorza | 0:d873d5d62d3b | 87 | case 'g': freqIndex = NOTE_G; break; |
taylorza | 0:d873d5d62d3b | 88 | |
taylorza | 0:d873d5d62d3b | 89 | case 'p': |
taylorza | 0:d873d5d62d3b | 90 | case 'r': freqIndex = NOTE_REST; break; |
taylorza | 0:d873d5d62d3b | 91 | |
taylorza | 0:d873d5d62d3b | 92 | case 'l': if (isdigit(peekChar())) _duration = (float)WHOLE_NOTE_DURATION / getNumber(1, 64); break; |
taylorza | 0:d873d5d62d3b | 93 | case 'o': if (isdigit(peekChar())) _octave = getNumber(0, 7); break; |
taylorza | 0:d873d5d62d3b | 94 | case 't': if (isdigit(peekChar())) _tempo = getNumber(32, 255); break; |
taylorza | 0:d873d5d62d3b | 95 | case 'm': |
taylorza | 0:d873d5d62d3b | 96 | switch(getChar()) |
taylorza | 0:d873d5d62d3b | 97 | { |
taylorza | 0:d873d5d62d3b | 98 | case 'n': _durationRatio = DEFAULT_TIMING; break; |
taylorza | 0:d873d5d62d3b | 99 | case 'l': _durationRatio = LEGATO_TIMING; break; |
taylorza | 0:d873d5d62d3b | 100 | case 's': _durationRatio = STACCATO_TIMING; break; |
taylorza | 0:d873d5d62d3b | 101 | } |
taylorza | 0:d873d5d62d3b | 102 | break; |
taylorza | 0:d873d5d62d3b | 103 | |
taylorza | 0:d873d5d62d3b | 104 | case 'n': if (isdigit(peekChar())) freqIndex = getNumber(0, 96); break; |
taylorza | 0:d873d5d62d3b | 105 | case '<': --_octave; if (_octave < 0) _octave = 0; break; |
taylorza | 0:d873d5d62d3b | 106 | case '>': ++_octave; if (_octave > 7) _octave = 7; break; |
taylorza | 0:d873d5d62d3b | 107 | |
taylorza | 0:d873d5d62d3b | 108 | case '\0': |
taylorza | 0:d873d5d62d3b | 109 | _isPlaying = false; |
taylorza | 0:d873d5d62d3b | 110 | break; |
taylorza | 0:d873d5d62d3b | 111 | } |
taylorza | 0:d873d5d62d3b | 112 | |
taylorza | 0:d873d5d62d3b | 113 | if (!_isPlaying) |
taylorza | 0:d873d5d62d3b | 114 | { |
taylorza | 0:d873d5d62d3b | 115 | _pwm = 0.0; |
taylorza | 0:d873d5d62d3b | 116 | _pwm.period(0); |
taylorza | 0:d873d5d62d3b | 117 | _completionCallback.call(); |
taylorza | 0:d873d5d62d3b | 118 | return; |
taylorza | 0:d873d5d62d3b | 119 | } |
taylorza | 0:d873d5d62d3b | 120 | |
taylorza | 0:d873d5d62d3b | 121 | float durationRatio = _durationRatio; |
taylorza | 0:d873d5d62d3b | 122 | float duration = _duration; |
taylorza | 0:d873d5d62d3b | 123 | |
taylorza | 0:d873d5d62d3b | 124 | if (freqIndex != -1) |
taylorza | 0:d873d5d62d3b | 125 | { |
taylorza | 0:d873d5d62d3b | 126 | switch(getChar()) |
taylorza | 0:d873d5d62d3b | 127 | { |
taylorza | 0:d873d5d62d3b | 128 | case '+': |
taylorza | 0:d873d5d62d3b | 129 | case '#': ++freqIndex; break; |
taylorza | 0:d873d5d62d3b | 130 | |
taylorza | 0:d873d5d62d3b | 131 | case '-': --freqIndex; break; |
taylorza | 0:d873d5d62d3b | 132 | case '.': |
taylorza | 0:d873d5d62d3b | 133 | durationRatio = 3.0f / 2.0f; |
taylorza | 0:d873d5d62d3b | 134 | while(peekChar() == '.') |
taylorza | 0:d873d5d62d3b | 135 | { |
taylorza | 0:d873d5d62d3b | 136 | durationRatio *= 3.0f / 2.0f; |
taylorza | 0:d873d5d62d3b | 137 | getChar(); |
taylorza | 0:d873d5d62d3b | 138 | } |
taylorza | 0:d873d5d62d3b | 139 | break; |
taylorza | 0:d873d5d62d3b | 140 | default: |
taylorza | 0:d873d5d62d3b | 141 | rewind(); |
taylorza | 0:d873d5d62d3b | 142 | break; |
taylorza | 0:d873d5d62d3b | 143 | } |
taylorza | 0:d873d5d62d3b | 144 | |
taylorza | 0:d873d5d62d3b | 145 | if (isdigit(peekChar())) |
taylorza | 0:d873d5d62d3b | 146 | { |
taylorza | 0:d873d5d62d3b | 147 | duration = WHOLE_NOTE_DURATION / getNumber(1, 64); |
taylorza | 0:d873d5d62d3b | 148 | } |
taylorza | 0:d873d5d62d3b | 149 | |
taylorza | 0:d873d5d62d3b | 150 | if (freqIndex != NOTE_REST) |
taylorza | 0:d873d5d62d3b | 151 | { |
taylorza | 0:d873d5d62d3b | 152 | _pwm.period(PERIOD_TABLE[freqIndex + (_octave * 12)]); |
taylorza | 0:d873d5d62d3b | 153 | _pwm = 0.5; |
taylorza | 0:d873d5d62d3b | 154 | } |
taylorza | 0:d873d5d62d3b | 155 | |
taylorza | 0:d873d5d62d3b | 156 | duration *= (QUARTER_NOTES_PER_MINUTE / _tempo); |
taylorza | 0:d873d5d62d3b | 157 | _scheduler.attach(this, &MusicEngine::executeCommand, duration * durationRatio); |
taylorza | 0:d873d5d62d3b | 158 | _pause = duration * (1 - durationRatio); |
taylorza | 0:d873d5d62d3b | 159 | } |
taylorza | 0:d873d5d62d3b | 160 | } while (freqIndex == -1); |
taylorza | 0:d873d5d62d3b | 161 | } |
taylorza | 0:d873d5d62d3b | 162 | } |
taylorza | 0:d873d5d62d3b | 163 | |
taylorza | 0:d873d5d62d3b | 164 | int MusicEngine::getNumber(int min, int max) |
taylorza | 0:d873d5d62d3b | 165 | { |
taylorza | 0:d873d5d62d3b | 166 | char ch; |
taylorza | 0:d873d5d62d3b | 167 | int value = 0; |
taylorza | 0:d873d5d62d3b | 168 | while ((ch = getChar()) != 0) |
taylorza | 0:d873d5d62d3b | 169 | { |
taylorza | 0:d873d5d62d3b | 170 | if (!isdigit(ch)) |
taylorza | 0:d873d5d62d3b | 171 | { |
taylorza | 0:d873d5d62d3b | 172 | rewind(); |
taylorza | 0:d873d5d62d3b | 173 | break; |
taylorza | 0:d873d5d62d3b | 174 | } |
taylorza | 0:d873d5d62d3b | 175 | int digit = (int)ch - 48; |
taylorza | 0:d873d5d62d3b | 176 | value = (value * 10) + digit; |
taylorza | 0:d873d5d62d3b | 177 | } |
taylorza | 0:d873d5d62d3b | 178 | value = value < min ? min : value > max ? max : value; |
taylorza | 0:d873d5d62d3b | 179 | return value; |
taylorza | 0:d873d5d62d3b | 180 | } |
taylorza | 0:d873d5d62d3b | 181 | |
taylorza | 0:d873d5d62d3b | 182 | void MusicEngine::skipWhiteSpace() |
taylorza | 0:d873d5d62d3b | 183 | { |
taylorza | 0:d873d5d62d3b | 184 | while (isspace(peekChar())) getChar(); |
taylorza | 0:d873d5d62d3b | 185 | } |
taylorza | 0:d873d5d62d3b | 186 | |
taylorza | 0:d873d5d62d3b | 187 | char MusicEngine::getChar() |
taylorza | 0:d873d5d62d3b | 188 | { |
taylorza | 0:d873d5d62d3b | 189 | return tolower(_mml[_mmlIndex++]); |
taylorza | 0:d873d5d62d3b | 190 | } |
taylorza | 0:d873d5d62d3b | 191 | |
taylorza | 0:d873d5d62d3b | 192 | char MusicEngine::peekChar() |
taylorza | 0:d873d5d62d3b | 193 | { |
taylorza | 0:d873d5d62d3b | 194 | return tolower(_mml[_mmlIndex]); |
taylorza | 0:d873d5d62d3b | 195 | } |
taylorza | 0:d873d5d62d3b | 196 | |
taylorza | 0:d873d5d62d3b | 197 | void MusicEngine::rewind() |
taylorza | 0:d873d5d62d3b | 198 | { |
taylorza | 0:d873d5d62d3b | 199 | --_mmlIndex; |
taylorza | 0:d873d5d62d3b | 200 | } |
taylorza | 0:d873d5d62d3b | 201 | |
taylorza | 0:d873d5d62d3b | 202 | const float MusicEngine::PERIOD_TABLE[] = |
taylorza | 0:d873d5d62d3b | 203 | { |
taylorza | 0:d873d5d62d3b | 204 | 0, |
taylorza | 0:d873d5d62d3b | 205 | //1 2 3 4 5 6 7 8 9 10 11 12 |
taylorza | 0:d873d5d62d3b | 206 | //C C# D D# E F F# G G# A A# B |
taylorza | 0:d873d5d62d3b | 207 | 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 |
taylorza | 0:d873d5d62d3b | 208 | 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 |
taylorza | 0:d873d5d62d3b | 209 | 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 |
taylorza | 0:d873d5d62d3b | 210 | 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 |
taylorza | 0:d873d5d62d3b | 211 | 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 |
taylorza | 0:d873d5d62d3b | 212 | 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 |
taylorza | 0:d873d5d62d3b | 213 | 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 |
taylorza | 0:d873d5d62d3b | 214 | 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 |
taylorza | 0:d873d5d62d3b | 215 | }; |