Class to play tones, various sounds and music in MML format using a PWM channel.

Dependents:   PwmSoundTest

Committer:
paulg
Date:
Tue May 06 13:16:28 2014 +0000
Revision:
1:67056b9df9ff
Added play() function to play music written in Music Macro Language (MML) format. Removed tune() as no longer required.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
paulg 1:67056b9df9ff 1 /******************************************************************************
paulg 1:67056b9df9ff 2 * File: play.cpp
paulg 1:67056b9df9ff 3 * Author: Paul Griffith
paulg 1:67056b9df9ff 4 * Created: 28 Mar 2014
paulg 1:67056b9df9ff 5 * Last Edit: see below
paulg 1:67056b9df9ff 6 * Version: see below
paulg 1:67056b9df9ff 7 *
paulg 1:67056b9df9ff 8 * Description:
paulg 1:67056b9df9ff 9 * Play melody from music data written in Music Macro Language (MML) format.
paulg 1:67056b9df9ff 10 * Part of PwmSound class.
paulg 1:67056b9df9ff 11 *
paulg 1:67056b9df9ff 12 * Copyright (c) 2014 Paul Griffith, MIT License
paulg 1:67056b9df9ff 13 *
paulg 1:67056b9df9ff 14 * Permission is hereby granted, free of charge, to any person obtaining a copy
paulg 1:67056b9df9ff 15 * of this software and associated documentation files (the "Software"), to
paulg 1:67056b9df9ff 16 * deal in the Software without restriction, including without limitation the
paulg 1:67056b9df9ff 17 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
paulg 1:67056b9df9ff 18 * sell copies of the Software, and to permit persons to whom the Software is
paulg 1:67056b9df9ff 19 * furnished to do so, subject to the following conditions:
paulg 1:67056b9df9ff 20 *
paulg 1:67056b9df9ff 21 * The above copyright notice and this permission notice shall be included in
paulg 1:67056b9df9ff 22 * all copies or substantial portions of the Software.
paulg 1:67056b9df9ff 23 *
paulg 1:67056b9df9ff 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
paulg 1:67056b9df9ff 25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
paulg 1:67056b9df9ff 26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
paulg 1:67056b9df9ff 27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
paulg 1:67056b9df9ff 28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
paulg 1:67056b9df9ff 29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
paulg 1:67056b9df9ff 30 * IN THE SOFTWARE.
paulg 1:67056b9df9ff 31 *
paulg 1:67056b9df9ff 32 * Modifications:
paulg 1:67056b9df9ff 33 * Ver Date By Details
paulg 1:67056b9df9ff 34 * 0.00 28Mar14 PG File created.
paulg 1:67056b9df9ff 35 * 1.00 06May14 PG Initial release.
paulg 1:67056b9df9ff 36 *
paulg 1:67056b9df9ff 37 ******************************************************************************/
paulg 1:67056b9df9ff 38 /*
paulg 1:67056b9df9ff 39 * Music Macro Language (MML) is a music description language used in sequencing
paulg 1:67056b9df9ff 40 * music on computer and video game systems. For further details refer to the
paulg 1:67056b9df9ff 41 * Wikipedia article of the same name.
paulg 1:67056b9df9ff 42 *
paulg 1:67056b9df9ff 43 * There are many dialects of MML. The one used here is essentially that of
paulg 1:67056b9df9ff 44 * the PLAY statement from Microsoft GW-BASIC. The original Microsoft
paulg 1:67056b9df9ff 45 * documentation is available online by following the References from the
paulg 1:67056b9df9ff 46 * Wikipedia GW-BASIC article. The MML data format is described below:
paulg 1:67056b9df9ff 47 *
paulg 1:67056b9df9ff 48 * A-G Play note using the letter names of the scale. The letter may be
paulg 1:67056b9df9ff 49 * followed by either a # or + for a sharp, or a - for a flat. Any note
paulg 1:67056b9df9ff 50 * letter followed by a #, + or - must refer to a black key on a piano.
paulg 1:67056b9df9ff 51 *
paulg 1:67056b9df9ff 52 * K Keyboard. Stops play() polling the PC keyboard during playback.
paulg 1:67056b9df9ff 53 * If K is not found, any PC keystroke will stop playback.
paulg 1:67056b9df9ff 54 *
paulg 1:67056b9df9ff 55 * Ln Sets the length of each note. n is a decimal number between 1 and 64
paulg 1:67056b9df9ff 56 * L1 is a whole note (semi-breve), L4 is a quarter note (crotchet) and
paulg 1:67056b9df9ff 57 * so on. The L value persists until the next L command is encountered.
paulg 1:67056b9df9ff 58 *
paulg 1:67056b9df9ff 59 * A length number may also follow a note letter name to change the length
paulg 1:67056b9df9ff 60 * for that note only. For example, D8 is equivalent to L8D.
paulg 1:67056b9df9ff 61 *
paulg 1:67056b9df9ff 62 * ML Music Legato. The note is played the for the full length of its
paulg 1:67056b9df9ff 63 * specified time. There is no rest between notes.
paulg 1:67056b9df9ff 64 *
paulg 1:67056b9df9ff 65 * MN Music Normal. The note is played for 7/8 of its specified time, and
paulg 1:67056b9df9ff 66 * 1/8 of the specified time is a rest between notes. This is the default.
paulg 1:67056b9df9ff 67 *
paulg 1:67056b9df9ff 68 * MS Music Staccato. The note is played for 3/4 of its specified time, and
paulg 1:67056b9df9ff 69 * 1/4 of the specified time is a rest between notes.
paulg 1:67056b9df9ff 70 *
paulg 1:67056b9df9ff 71 * Nn Play note. n is a decimal number between 0 and 84. n = 1 is the lowest
paulg 1:67056b9df9ff 72 * note and n = 84 is the highest note. n set to 0 indicates a rest. N can
paulg 1:67056b9df9ff 73 * be used as an alternative to defining a note by an octave and a letter.
paulg 1:67056b9df9ff 74 * For example, N37 = middle C.
paulg 1:67056b9df9ff 75 *
paulg 1:67056b9df9ff 76 * On Sets the current octave. n is a decimal number between 0 and 6. The
paulg 1:67056b9df9ff 77 * default octave is 4. Middle C starts octave 3, i.e. O3C = middle C.
paulg 1:67056b9df9ff 78 *
paulg 1:67056b9df9ff 79 * Note: The above convention differs from standard piano octave numbering
paulg 1:67056b9df9ff 80 * where middle C starts octave 4, i.e. O4C = middle C.
paulg 1:67056b9df9ff 81 *
paulg 1:67056b9df9ff 82 * Pn Pause, or rest, for a length defined by n. P works in the same way as
paulg 1:67056b9df9ff 83 * the L command above. For example, P2 = a half rest.
paulg 1:67056b9df9ff 84 *
paulg 1:67056b9df9ff 85 * Qn Sets the tonal quality (timbre). n is a decimal number between 1 and 4.
paulg 1:67056b9df9ff 86 * It sets the PWM duty cycle to n / 8, (i.e. 12.5%, 25%, 37.5% or 50%).
paulg 1:67056b9df9ff 87 * The default is 4 (50% duty cycle).
paulg 1:67056b9df9ff 88 *
paulg 1:67056b9df9ff 89 * Rn Rest, for a length defined by n. Alternative form of the P command.
paulg 1:67056b9df9ff 90 *
paulg 1:67056b9df9ff 91 * Tn Sets the tempo (the pace at which the music plays) in beats per minute.
paulg 1:67056b9df9ff 92 * n is a decimal number between 32 and 255. The default tempo is 120. One
paulg 1:67056b9df9ff 93 * beat corresponds to a quarter note (L4).
paulg 1:67056b9df9ff 94 *
paulg 1:67056b9df9ff 95 * . Dot. A dot can follow a letter note or a pause. It extends the duration
paulg 1:67056b9df9ff 96 * of the note or pause by half (to 150%). More than one dot may be used.
paulg 1:67056b9df9ff 97 *
paulg 1:67056b9df9ff 98 * Note: The Microsoft documentation states that multiple dots extend the
paulg 1:67056b9df9ff 99 * duration as follows:
paulg 1:67056b9df9ff 100 * 2 dots = 1.5 ^ 2 = 225%, 3 dots = 1.5 ^ 3 = 337.5%.
paulg 1:67056b9df9ff 101 * This differs from standard musical notation where multiple dots
paulg 1:67056b9df9ff 102 * provide successively smaller extensions as follows:
paulg 1:67056b9df9ff 103 * 2 dots = 1 + 1/2 + 1/4 = 175%.
paulg 1:67056b9df9ff 104 * 3 dots = 1 + 1/2 + 1/4 + 1/8 = 187.5%.
paulg 1:67056b9df9ff 105 *
paulg 1:67056b9df9ff 106 * The standard notation extensions are used here.
paulg 1:67056b9df9ff 107 *
paulg 1:67056b9df9ff 108 * > Play the following note in the next higher octave. For example,
paulg 1:67056b9df9ff 109 * O3C >D E is equivalent to O3C O4D O3E.
paulg 1:67056b9df9ff 110 *
paulg 1:67056b9df9ff 111 * < Play the following note in the next lower octave. For example,
paulg 1:67056b9df9ff 112 * O3C <D E is equivalent to O3C O2D O3E.
paulg 1:67056b9df9ff 113 *
paulg 1:67056b9df9ff 114 * Note: Some dialects of MML appear to treat < and > as persistent
paulg 1:67056b9df9ff 115 * commands that affect all following notes.
paulg 1:67056b9df9ff 116 *
paulg 1:67056b9df9ff 117 * Note: The Microsoft documentation does not say whether or not < and >
paulg 1:67056b9df9ff 118 * should act on notes specified by number, such as N37. In this
paulg 1:67056b9df9ff 119 * implementation it does, so >N37 = N49.
paulg 1:67056b9df9ff 120 *
paulg 1:67056b9df9ff 121 * : # Treat remainder of line as a comment. Note: line must end with '\n'.
paulg 1:67056b9df9ff 122 *
paulg 1:67056b9df9ff 123 * Note: Some dialects of MML use # to start a comment. We accept either.
paulg 1:67056b9df9ff 124 *
paulg 1:67056b9df9ff 125 * White space and line endings (CR, LF) are ignored (except in comments).
paulg 1:67056b9df9ff 126 *
paulg 1:67056b9df9ff 127 * Note: The play() function has a second parameter which supports some MML
paulg 1:67056b9df9ff 128 * dialect alternatives. Refer to the comments below for details.
paulg 1:67056b9df9ff 129 */
paulg 1:67056b9df9ff 130
paulg 1:67056b9df9ff 131 #include "mbed.h"
paulg 1:67056b9df9ff 132 #include "PwmSound.h"
paulg 1:67056b9df9ff 133 #include "ctype.h"
paulg 1:67056b9df9ff 134
paulg 1:67056b9df9ff 135 extern Serial pc; //for debug, comment out of not needed
paulg 1:67056b9df9ff 136
paulg 1:67056b9df9ff 137 // Standard note pitches in Hz
paulg 1:67056b9df9ff 138 // From Wikipedia: http://en.wikipedia.org/wiki/Scientific_pitch_notation
paulg 1:67056b9df9ff 139 // First entry is a dummy, real note numbers start at 1
paulg 1:67056b9df9ff 140 // Seven octaves, twelve notes per octave
paulg 1:67056b9df9ff 141 // C, C#, D, D#, E, F, F#, G, G#, A, A#, B
paulg 1:67056b9df9ff 142 // Middle C (261.63Hz) is element 37
paulg 1:67056b9df9ff 143
paulg 1:67056b9df9ff 144 float notePitches[1+84] = {
paulg 1:67056b9df9ff 145 1.0, //dummy
paulg 1:67056b9df9ff 146 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, //first octave
paulg 1:67056b9df9ff 147 46.249, 48.999, 51.913, 55.000, 58.270, 61.735,
paulg 1:67056b9df9ff 148 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, //second octave
paulg 1:67056b9df9ff 149 92.499, 97.999, 103.83, 110.00, 116.54, 123.47,
paulg 1:67056b9df9ff 150 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, //third octave
paulg 1:67056b9df9ff 151 185.00, 196.00, 207.65, 220.00, 233.08, 246.94,
paulg 1:67056b9df9ff 152 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, //fourth octave
paulg 1:67056b9df9ff 153 369.99, 392.00, 415.30, 440.00, 466.16, 493.88,
paulg 1:67056b9df9ff 154 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, //fifth octave
paulg 1:67056b9df9ff 155 739.99, 783.99, 830.61, 880.00, 932.33, 987.77,
paulg 1:67056b9df9ff 156 1046.5, 1108.7, 1174.7, 1244.5, 1318.5, 1396.9, //sixth octave
paulg 1:67056b9df9ff 157 1480.0, 1568.0, 1661.2, 1760.0, 1864.7, 1975.5,
paulg 1:67056b9df9ff 158 2093.0, 2217.5, 2349.3, 2489.0, 2637.0, 2793.8, //seventh octave
paulg 1:67056b9df9ff 159 2960.0, 3136.0, 3322.4, 3520.0, 3729.3, 3951.1,
paulg 1:67056b9df9ff 160 };
paulg 1:67056b9df9ff 161
paulg 1:67056b9df9ff 162 // Note numbers within octave for notes A - G (white keys on piano)
paulg 1:67056b9df9ff 163
paulg 1:67056b9df9ff 164 int notes[7] = { 10, 12, 1, 3, 5, 6, 8 }; //C is first note = 1
paulg 1:67056b9df9ff 165
paulg 1:67056b9df9ff 166 // Allowable note modifiers for notes A - G
paulg 1:67056b9df9ff 167
paulg 1:67056b9df9ff 168 int flats[7] = { -1, -1, 0, -1, -1, 0, -1 }; //not C or F
paulg 1:67056b9df9ff 169 int sharps[7] = {1, 0, 1, 1, 0, 1, 1 }; //not B or E
paulg 1:67056b9df9ff 170
paulg 1:67056b9df9ff 171 // Play a melody from music data written in MML format.
paulg 1:67056b9df9ff 172 //
paulg 1:67056b9df9ff 173 // Parameters:
paulg 1:67056b9df9ff 174 // m - pointer to string containing music data
paulg 1:67056b9df9ff 175 // options (default 0) - support for different dialects of MML
paulg 1:67056b9df9ff 176 // bit 0 (1) - standard octave numbering
paulg 1:67056b9df9ff 177 // 0: octaves range from 0 to 6, middle C starts octave 3
paulg 1:67056b9df9ff 178 // 1: octaves range from 1 to 7, middle C starts octave 4
paulg 1:67056b9df9ff 179 // bit 1 (2) - stickyShift
paulg 1:67056b9df9ff 180 // 0: < and > act on next note only
paulg 1:67056b9df9ff 181 // 1: < and > act on all following notes
paulg 1:67056b9df9ff 182 // bit 2 (4) - longDots
paulg 1:67056b9df9ff 183 // 0: standard dot extensions (i.e. 150%, 175%, 187.5%)
paulg 1:67056b9df9ff 184 // 1: GW-BASIC dot extensions (i.e. 150%, 225%, 337.5%)
paulg 1:67056b9df9ff 185 // Returns: 0 if no error in input, otherwise position of offending character
paulg 1:67056b9df9ff 186
paulg 1:67056b9df9ff 187 int PwmSound::play(char* m, int options) {
paulg 1:67056b9df9ff 188 bool run = true, kbdPoll = true;
paulg 1:67056b9df9ff 189 char c, c1;
paulg 1:67056b9df9ff 190 int n, n1, n2;
paulg 1:67056b9df9ff 191
paulg 1:67056b9df9ff 192 bool stdOctNum = (options & 1) ? true : false; //options bits
paulg 1:67056b9df9ff 193 bool stickyShift = (options & 2) ? true : false;
paulg 1:67056b9df9ff 194 bool longDots = (options & 4) ? true : false;
paulg 1:67056b9df9ff 195
paulg 1:67056b9df9ff 196 _octave = 4; //set defaults
paulg 1:67056b9df9ff 197 _shift = 0;
paulg 1:67056b9df9ff 198 _tempo = 120;
paulg 1:67056b9df9ff 199 _length = 4;
paulg 1:67056b9df9ff 200 _1dot = 1.5;
paulg 1:67056b9df9ff 201 _2dots = (longDots == true) ? 2.25 : 1.75;
paulg 1:67056b9df9ff 202 _3dots = (longDots == true) ? 3.375 : 1.875;
paulg 1:67056b9df9ff 203 _style = 7;
paulg 1:67056b9df9ff 204 _dutyCycle = 0.5;
paulg 1:67056b9df9ff 205 _mp = m;
paulg 1:67056b9df9ff 206 _haveNext = false;
paulg 1:67056b9df9ff 207 //pc.putc('[');
paulg 1:67056b9df9ff 208 while (run) {
paulg 1:67056b9df9ff 209 if (kbdPoll && pc.readable()) {
paulg 1:67056b9df9ff 210 pc.getc();
paulg 1:67056b9df9ff 211 break;
paulg 1:67056b9df9ff 212 }
paulg 1:67056b9df9ff 213 c = _getChar(); //read next char in input stream
paulg 1:67056b9df9ff 214 //pc.putc(c);
paulg 1:67056b9df9ff 215 switch (c) {
paulg 1:67056b9df9ff 216 case 'A': //specify note by letter (and modifier)
paulg 1:67056b9df9ff 217 case 'B':
paulg 1:67056b9df9ff 218 case 'C':
paulg 1:67056b9df9ff 219 case 'D':
paulg 1:67056b9df9ff 220 case 'E':
paulg 1:67056b9df9ff 221 case 'F':
paulg 1:67056b9df9ff 222 case 'G':
paulg 1:67056b9df9ff 223 if (stdOctNum) {
paulg 1:67056b9df9ff 224 n = ((_octave - 1) * 12) + notes[c - 'A'];
paulg 1:67056b9df9ff 225 } else {
paulg 1:67056b9df9ff 226 n = (_octave * 12) + notes[c - 'A'];
paulg 1:67056b9df9ff 227 }
paulg 1:67056b9df9ff 228 c1 = _nextChar(); //optional modifier
paulg 1:67056b9df9ff 229 if (c1 == '-' || c1 == '+' || c1 == '#') {
paulg 1:67056b9df9ff 230 c1 = _getChar();
paulg 1:67056b9df9ff 231 n += (c1 == '-') ? flats[c - 'A'] : sharps[c - 'A'];
paulg 1:67056b9df9ff 232 }
paulg 1:67056b9df9ff 233 n += _shift * 12;
paulg 1:67056b9df9ff 234 if (!stickyShift) {
paulg 1:67056b9df9ff 235 _shift = 0;
paulg 1:67056b9df9ff 236 }
paulg 1:67056b9df9ff 237 n1 = _getNumber(); //optional length number
paulg 1:67056b9df9ff 238 n2 = _getDots(); //optional dots
paulg 1:67056b9df9ff 239 if (n1 == 0) {
paulg 1:67056b9df9ff 240 _note(n, _length, n2);
paulg 1:67056b9df9ff 241 } else {
paulg 1:67056b9df9ff 242 _note(n, n1, n2);
paulg 1:67056b9df9ff 243 }
paulg 1:67056b9df9ff 244 break;
paulg 1:67056b9df9ff 245
paulg 1:67056b9df9ff 246 case 'K':
paulg 1:67056b9df9ff 247 kbdPoll = false;
paulg 1:67056b9df9ff 248 break;
paulg 1:67056b9df9ff 249
paulg 1:67056b9df9ff 250 case 'L': //set note length
paulg 1:67056b9df9ff 251 n = _getNumber();
paulg 1:67056b9df9ff 252 if (n >= 1 && n <= 64) {
paulg 1:67056b9df9ff 253 _length = n;
paulg 1:67056b9df9ff 254 }
paulg 1:67056b9df9ff 255 break;
paulg 1:67056b9df9ff 256
paulg 1:67056b9df9ff 257 case 'M': //set music style (proportion of note length played)
paulg 1:67056b9df9ff 258 switch (_getChar() ) {
paulg 1:67056b9df9ff 259 case 'L': //legato
paulg 1:67056b9df9ff 260 _style = 8;
paulg 1:67056b9df9ff 261 break;
paulg 1:67056b9df9ff 262
paulg 1:67056b9df9ff 263 case 'N': //normal
paulg 1:67056b9df9ff 264 _style = 7;
paulg 1:67056b9df9ff 265 break;
paulg 1:67056b9df9ff 266
paulg 1:67056b9df9ff 267 case 'S': //staccato
paulg 1:67056b9df9ff 268 _style = 6;
paulg 1:67056b9df9ff 269 break;
paulg 1:67056b9df9ff 270 }
paulg 1:67056b9df9ff 271 break;
paulg 1:67056b9df9ff 272
paulg 1:67056b9df9ff 273 case 'N': //specify note by number
paulg 1:67056b9df9ff 274 n = _getNumber();
paulg 1:67056b9df9ff 275 n += _shift * 12; //not really sure about this
paulg 1:67056b9df9ff 276 if (!stickyShift) {
paulg 1:67056b9df9ff 277 _shift = 0;
paulg 1:67056b9df9ff 278 }
paulg 1:67056b9df9ff 279 _note(n, _length);
paulg 1:67056b9df9ff 280 break;
paulg 1:67056b9df9ff 281
paulg 1:67056b9df9ff 282 case 'O': //set octave
paulg 1:67056b9df9ff 283 n = _getNumber();
paulg 1:67056b9df9ff 284 if (stdOctNum) {
paulg 1:67056b9df9ff 285 if (n >= 1 && n <= 7) {
paulg 1:67056b9df9ff 286 _octave = n;
paulg 1:67056b9df9ff 287 }
paulg 1:67056b9df9ff 288 } else {
paulg 1:67056b9df9ff 289 if (n >= 0 && n <= 6) {
paulg 1:67056b9df9ff 290 _octave = n;
paulg 1:67056b9df9ff 291 }
paulg 1:67056b9df9ff 292 }
paulg 1:67056b9df9ff 293 break;
paulg 1:67056b9df9ff 294
paulg 1:67056b9df9ff 295 case 'P': //pause or rest
paulg 1:67056b9df9ff 296 case 'R':
paulg 1:67056b9df9ff 297 n1 = _getNumber(); //optional length number
paulg 1:67056b9df9ff 298 n2 = _getDots(); //optional dots
paulg 1:67056b9df9ff 299 if (n1 == 0) {
paulg 1:67056b9df9ff 300 _note(0, _length, n2);
paulg 1:67056b9df9ff 301 } else {
paulg 1:67056b9df9ff 302 _note(0, n1, n2);
paulg 1:67056b9df9ff 303 }
paulg 1:67056b9df9ff 304 break;
paulg 1:67056b9df9ff 305
paulg 1:67056b9df9ff 306 case 'Q': //set timbre
paulg 1:67056b9df9ff 307 n = _getNumber();
paulg 1:67056b9df9ff 308 if (n >= 1 && n <= 4) {
paulg 1:67056b9df9ff 309 _dutyCycle = n / 8.0;
paulg 1:67056b9df9ff 310 }
paulg 1:67056b9df9ff 311 break;
paulg 1:67056b9df9ff 312
paulg 1:67056b9df9ff 313 case 'T': //set tempo
paulg 1:67056b9df9ff 314 n = _getNumber();
paulg 1:67056b9df9ff 315 if ( n>= 32 && n <= 255) {
paulg 1:67056b9df9ff 316 _tempo = n;
paulg 1:67056b9df9ff 317 }
paulg 1:67056b9df9ff 318 break;
paulg 1:67056b9df9ff 319
paulg 1:67056b9df9ff 320 case '<': //move down an octave
paulg 1:67056b9df9ff 321 _shift--;
paulg 1:67056b9df9ff 322 break;
paulg 1:67056b9df9ff 323
paulg 1:67056b9df9ff 324 case '>': //move up an octave
paulg 1:67056b9df9ff 325 _shift++;
paulg 1:67056b9df9ff 326 break;
paulg 1:67056b9df9ff 327
paulg 1:67056b9df9ff 328 case ':': //comment to end of line
paulg 1:67056b9df9ff 329 case '#':
paulg 1:67056b9df9ff 330 while (_getChar() != '\n') ;
paulg 1:67056b9df9ff 331 break;
paulg 1:67056b9df9ff 332
paulg 1:67056b9df9ff 333 case ' ': //skip over white space and line endings
paulg 1:67056b9df9ff 334 case '\t':
paulg 1:67056b9df9ff 335 case '\r':
paulg 1:67056b9df9ff 336 case '\n':
paulg 1:67056b9df9ff 337 break;
paulg 1:67056b9df9ff 338
paulg 1:67056b9df9ff 339 case '\0': //end of string
paulg 1:67056b9df9ff 340 run = false;
paulg 1:67056b9df9ff 341 break;
paulg 1:67056b9df9ff 342
paulg 1:67056b9df9ff 343 default: //abort on invalid characters
paulg 1:67056b9df9ff 344 _pin = 0.0;
paulg 1:67056b9df9ff 345 return(int (_mp - m) ); //return position of error
paulg 1:67056b9df9ff 346 }
paulg 1:67056b9df9ff 347 } //end of while
paulg 1:67056b9df9ff 348 //pc.putc(']');
paulg 1:67056b9df9ff 349 _pin = 0.0;
paulg 1:67056b9df9ff 350 return 0;
paulg 1:67056b9df9ff 351 }
paulg 1:67056b9df9ff 352
paulg 1:67056b9df9ff 353 // Play a musical note on output pin
paulg 1:67056b9df9ff 354 //
paulg 1:67056b9df9ff 355 // Parameters:
paulg 1:67056b9df9ff 356 // number - 0 = rest, notes from 1 to 84, middle C (262Hz) = 37
paulg 1:67056b9df9ff 357 // length - duration of note (1-64): 1 = whole note (semibreve)
paulg 1:67056b9df9ff 358 // 2 = half note (minim)
paulg 1:67056b9df9ff 359 // 4 = quarter note (crotchet) = 1 beat
paulg 1:67056b9df9ff 360 // 8 = eighth note (quaver)
paulg 1:67056b9df9ff 361 // etc
paulg 1:67056b9df9ff 362 // dots - length extension (0-3, default 0)
paulg 1:67056b9df9ff 363 // Returns: nothing
paulg 1:67056b9df9ff 364
paulg 1:67056b9df9ff 365 void PwmSound::_note(int number, int length, int dots) {
paulg 1:67056b9df9ff 366 float duration, play, rest;
paulg 1:67056b9df9ff 367
paulg 1:67056b9df9ff 368 if (number < 1 || number > 84) { //convert bad note to a rest
paulg 1:67056b9df9ff 369 number = 0;
paulg 1:67056b9df9ff 370 }
paulg 1:67056b9df9ff 371
paulg 1:67056b9df9ff 372 duration = 240.0 / (_tempo * length);
paulg 1:67056b9df9ff 373 if (dots == 1) {
paulg 1:67056b9df9ff 374 duration *= _1dot;
paulg 1:67056b9df9ff 375 } else if (dots == 2) {
paulg 1:67056b9df9ff 376 duration *= _2dots;
paulg 1:67056b9df9ff 377 } else if (dots == 3) {
paulg 1:67056b9df9ff 378 duration *= _3dots;
paulg 1:67056b9df9ff 379 }
paulg 1:67056b9df9ff 380 play = duration * _style / 8.0;
paulg 1:67056b9df9ff 381 rest = duration * (8 - _style) / 8.0;
paulg 1:67056b9df9ff 382
paulg 1:67056b9df9ff 383 if (number > 0) {
paulg 1:67056b9df9ff 384 _pin.period(1.0 / notePitches[number]);
paulg 1:67056b9df9ff 385 _pin = _dutyCycle;
paulg 1:67056b9df9ff 386 }
paulg 1:67056b9df9ff 387 wait(play);
paulg 1:67056b9df9ff 388 _pin = 0.0;
paulg 1:67056b9df9ff 389 wait(rest);
paulg 1:67056b9df9ff 390 }
paulg 1:67056b9df9ff 391
paulg 1:67056b9df9ff 392 // Read next character in input string
paulg 1:67056b9df9ff 393 //
paulg 1:67056b9df9ff 394 // Parameters: none
paulg 1:67056b9df9ff 395 // Returns: next character
paulg 1:67056b9df9ff 396
paulg 1:67056b9df9ff 397 char PwmSound::_getChar(void) {
paulg 1:67056b9df9ff 398 if (_haveNext) {
paulg 1:67056b9df9ff 399 _haveNext = false;
paulg 1:67056b9df9ff 400 return _nextCh;
paulg 1:67056b9df9ff 401 } else {
paulg 1:67056b9df9ff 402 return *_mp++;
paulg 1:67056b9df9ff 403 }
paulg 1:67056b9df9ff 404 }
paulg 1:67056b9df9ff 405
paulg 1:67056b9df9ff 406 // Examine next character in input string without consuming it
paulg 1:67056b9df9ff 407 //
paulg 1:67056b9df9ff 408 // Parameters: none
paulg 1:67056b9df9ff 409 // Returns: next character
paulg 1:67056b9df9ff 410
paulg 1:67056b9df9ff 411 char PwmSound::_nextChar(void) {
paulg 1:67056b9df9ff 412 if (!_haveNext) {
paulg 1:67056b9df9ff 413 _nextCh = *_mp++;
paulg 1:67056b9df9ff 414 _haveNext = true;
paulg 1:67056b9df9ff 415 }
paulg 1:67056b9df9ff 416 return _nextCh;
paulg 1:67056b9df9ff 417 }
paulg 1:67056b9df9ff 418
paulg 1:67056b9df9ff 419 // Read a variable length number from input string
paulg 1:67056b9df9ff 420 //
paulg 1:67056b9df9ff 421 // Parameters: none
paulg 1:67056b9df9ff 422 // Returns: number
paulg 1:67056b9df9ff 423
paulg 1:67056b9df9ff 424 int PwmSound::_getNumber(void) {
paulg 1:67056b9df9ff 425 int n = 0;
paulg 1:67056b9df9ff 426
paulg 1:67056b9df9ff 427 while (isdigit(_nextChar()) ) {
paulg 1:67056b9df9ff 428 n *= 10;
paulg 1:67056b9df9ff 429 n += _getChar() - '0';
paulg 1:67056b9df9ff 430 }
paulg 1:67056b9df9ff 431 return n;
paulg 1:67056b9df9ff 432 }
paulg 1:67056b9df9ff 433
paulg 1:67056b9df9ff 434 // Read variable number of dots from input string
paulg 1:67056b9df9ff 435 //
paulg 1:67056b9df9ff 436 // Parameters: none
paulg 1:67056b9df9ff 437 // Returns: number of dots
paulg 1:67056b9df9ff 438
paulg 1:67056b9df9ff 439 int PwmSound::_getDots(void) {
paulg 1:67056b9df9ff 440 int n = 0;
paulg 1:67056b9df9ff 441
paulg 1:67056b9df9ff 442 while (_nextChar() == '.') {
paulg 1:67056b9df9ff 443 _getChar();
paulg 1:67056b9df9ff 444 n++;
paulg 1:67056b9df9ff 445 }
paulg 1:67056b9df9ff 446 return n;
paulg 1:67056b9df9ff 447 }
paulg 1:67056b9df9ff 448
paulg 1:67056b9df9ff 449 // END of play.cpp