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

Dependents:   PwmSoundTest

Committer:
paulg
Date:
Sun Mar 30 22:50:27 2014 +0000
Revision:
0:185bcd9f8e19
Child:
1:67056b9df9ff
Initial release.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
paulg 0:185bcd9f8e19 1 /******************************************************************************
paulg 0:185bcd9f8e19 2 * File: PwmSound.cpp
paulg 0:185bcd9f8e19 3 * Author: Paul Griffith
paulg 0:185bcd9f8e19 4 * Created: 25 Mar 2014
paulg 0:185bcd9f8e19 5 * Last Edit: see below
paulg 0:185bcd9f8e19 6 * Version: see below
paulg 0:185bcd9f8e19 7 *
paulg 0:185bcd9f8e19 8 * Description:
paulg 0:185bcd9f8e19 9 * Class to play tones, various sounds and simple tunes using a PWM channel.
paulg 0:185bcd9f8e19 10 * Inspired by Jim Hamblem's Speaker class. Thanks Jim!
paulg 0:185bcd9f8e19 11 *
paulg 0:185bcd9f8e19 12 * Refer to the tutorial "Using a Speaker for Audio Output" in the Cookbook.
paulg 0:185bcd9f8e19 13 * The mbed LPC1768 PWM pins will drive a small speaker without amplification.
paulg 0:185bcd9f8e19 14 * Connect speaker via a 220R resistor and 100uF 10V capacitor (+ve to mbed).
paulg 0:185bcd9f8e19 15 *
paulg 0:185bcd9f8e19 16 * Copyright (c) 2014 Paul Griffith, MIT License
paulg 0:185bcd9f8e19 17 *
paulg 0:185bcd9f8e19 18 * Permission is hereby granted, free of charge, to any person obtaining a copy
paulg 0:185bcd9f8e19 19 * of this software and associated documentation files (the "Software"), to
paulg 0:185bcd9f8e19 20 * deal in the Software without restriction, including without limitation the
paulg 0:185bcd9f8e19 21 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
paulg 0:185bcd9f8e19 22 * sell copies of the Software, and to permit persons to whom the Software is
paulg 0:185bcd9f8e19 23 * furnished to do so, subject to the following conditions:
paulg 0:185bcd9f8e19 24 *
paulg 0:185bcd9f8e19 25 * The above copyright notice and this permission notice shall be included in
paulg 0:185bcd9f8e19 26 * all copies or substantial portions of the Software.
paulg 0:185bcd9f8e19 27 *
paulg 0:185bcd9f8e19 28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
paulg 0:185bcd9f8e19 29 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
paulg 0:185bcd9f8e19 30 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
paulg 0:185bcd9f8e19 31 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
paulg 0:185bcd9f8e19 32 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
paulg 0:185bcd9f8e19 33 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
paulg 0:185bcd9f8e19 34 * IN THE SOFTWARE.
paulg 0:185bcd9f8e19 35 *
paulg 0:185bcd9f8e19 36 * Modifications:
paulg 0:185bcd9f8e19 37 * Ver Date By Details
paulg 0:185bcd9f8e19 38 * 0.00 25Mar14 PG File created.
paulg 0:185bcd9f8e19 39 * 1.00 30Mar14 PG Initial release.
paulg 0:185bcd9f8e19 40 *
paulg 0:185bcd9f8e19 41 ******************************************************************************/
paulg 0:185bcd9f8e19 42
paulg 0:185bcd9f8e19 43 #include "mbed.h"
paulg 0:185bcd9f8e19 44 #include "PwmSound.h"
paulg 0:185bcd9f8e19 45
paulg 0:185bcd9f8e19 46 extern Serial pc;
paulg 0:185bcd9f8e19 47
paulg 0:185bcd9f8e19 48 // Standard note pitches in Hz
paulg 0:185bcd9f8e19 49 // First entry is a dummy, real note numbers start at 1
paulg 0:185bcd9f8e19 50 // Seven octaves, twelve notes per octave
paulg 0:185bcd9f8e19 51 // C, C#, D, D#, E, F, F#, G, G#, A, A#, B
paulg 0:185bcd9f8e19 52 // Middle C is element 37 (262Hz)
paulg 0:185bcd9f8e19 53
paulg 0:185bcd9f8e19 54 int notePitches[1+84] = {
paulg 0:185bcd9f8e19 55 1, //dummy
paulg 0:185bcd9f8e19 56 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62,
paulg 0:185bcd9f8e19 57 65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123,
paulg 0:185bcd9f8e19 58 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
paulg 0:185bcd9f8e19 59 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
paulg 0:185bcd9f8e19 60 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
paulg 0:185bcd9f8e19 61 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
paulg 0:185bcd9f8e19 62 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
paulg 0:185bcd9f8e19 63 };
paulg 0:185bcd9f8e19 64
paulg 0:185bcd9f8e19 65 // Constructor
paulg 0:185bcd9f8e19 66
paulg 0:185bcd9f8e19 67 PwmSound::PwmSound(PinName pin) : _pin(pin) {
paulg 0:185bcd9f8e19 68 _duration = 0.25;
paulg 0:185bcd9f8e19 69 _volume = 1.0;
paulg 0:185bcd9f8e19 70 _tempo = 120; //120 beats/minute
paulg 0:185bcd9f8e19 71 _length = 4; //quarter note (crotchet)
paulg 0:185bcd9f8e19 72 _style = PwmSound::NORMAL;
paulg 0:185bcd9f8e19 73 _pin = 0.0;
paulg 0:185bcd9f8e19 74 _playing = false;
paulg 0:185bcd9f8e19 75 }
paulg 0:185bcd9f8e19 76
paulg 0:185bcd9f8e19 77 // Play a tone on output pin
paulg 0:185bcd9f8e19 78 // 2 versions for convenience when using default duration
paulg 0:185bcd9f8e19 79 //
paulg 0:185bcd9f8e19 80 // Parameters:
paulg 0:185bcd9f8e19 81 // frequency - frequency of tone in Hz
paulg 0:185bcd9f8e19 82 // duration - duration of tone in seconds
paulg 0:185bcd9f8e19 83 // if duration = 0.0, tone continues in background until stopped
paulg 0:185bcd9f8e19 84 // volume - crude volume control (0.0-1.0), alters PWM duty cycle 0-50%
paulg 0:185bcd9f8e19 85 // Returns: nothing
paulg 0:185bcd9f8e19 86 // Uses: current volume (duty cycle)
paulg 0:185bcd9f8e19 87
paulg 0:185bcd9f8e19 88 void PwmSound::tone(float frequency, float duration) {
paulg 0:185bcd9f8e19 89 _pin.period(1.0 / frequency);
paulg 0:185bcd9f8e19 90 _pin = _volume / 2.0;
paulg 0:185bcd9f8e19 91 if (duration == 0.0) {
paulg 0:185bcd9f8e19 92 _playing = true;
paulg 0:185bcd9f8e19 93 return;
paulg 0:185bcd9f8e19 94 }
paulg 0:185bcd9f8e19 95 wait(duration);
paulg 0:185bcd9f8e19 96 _pin = 0.0;
paulg 0:185bcd9f8e19 97 }
paulg 0:185bcd9f8e19 98
paulg 0:185bcd9f8e19 99 void PwmSound::tone(float frequency) {
paulg 0:185bcd9f8e19 100 _pin.period(1.0 / frequency);
paulg 0:185bcd9f8e19 101 _pin = _volume / 2.0;
paulg 0:185bcd9f8e19 102 if (_duration == 0.0) {
paulg 0:185bcd9f8e19 103 _playing = true;
paulg 0:185bcd9f8e19 104 return;
paulg 0:185bcd9f8e19 105 }
paulg 0:185bcd9f8e19 106 wait(_duration);
paulg 0:185bcd9f8e19 107 _pin = 0.0;
paulg 0:185bcd9f8e19 108 }
paulg 0:185bcd9f8e19 109
paulg 0:185bcd9f8e19 110 // Stop background tone or sound generation
paulg 0:185bcd9f8e19 111
paulg 0:185bcd9f8e19 112 void PwmSound::stop(void) {
paulg 0:185bcd9f8e19 113 _playing = false;
paulg 0:185bcd9f8e19 114 _pin = 0.0;
paulg 0:185bcd9f8e19 115 }
paulg 0:185bcd9f8e19 116
paulg 0:185bcd9f8e19 117 // Set default duration
paulg 0:185bcd9f8e19 118 //
paulg 0:185bcd9f8e19 119 // Parameters:
paulg 0:185bcd9f8e19 120 // duration - duration of tone in seconds
paulg 0:185bcd9f8e19 121 // Returns: nothing
paulg 0:185bcd9f8e19 122
paulg 0:185bcd9f8e19 123 void PwmSound::duration(float duration) {
paulg 0:185bcd9f8e19 124 _duration = duration;
paulg 0:185bcd9f8e19 125 }
paulg 0:185bcd9f8e19 126
paulg 0:185bcd9f8e19 127 // Set default volume
paulg 0:185bcd9f8e19 128 //
paulg 0:185bcd9f8e19 129 // Parameters:
paulg 0:185bcd9f8e19 130 // volume - crude volume control (0.0 - 1.0), alters PWM duty cycle 0 - 50%
paulg 0:185bcd9f8e19 131 // Returns: nothing
paulg 0:185bcd9f8e19 132
paulg 0:185bcd9f8e19 133 void PwmSound::volume(float volume) {
paulg 0:185bcd9f8e19 134 _volume = volume;
paulg 0:185bcd9f8e19 135 }
paulg 0:185bcd9f8e19 136
paulg 0:185bcd9f8e19 137 // Beeps of various types and other sounds
paulg 0:185bcd9f8e19 138 // Note: buzz, siren and trill use a ticker and callback to
paulg 0:185bcd9f8e19 139 // support continuous sound in background
paulg 0:185bcd9f8e19 140 //
paulg 0:185bcd9f8e19 141 // Parameters:
paulg 0:185bcd9f8e19 142 // n - number of cycles, 0 for continuous sound in background
paulg 0:185bcd9f8e19 143 // Returns: nothing
paulg 0:185bcd9f8e19 144
paulg 0:185bcd9f8e19 145 void PwmSound::bip(int n) {
paulg 0:185bcd9f8e19 146 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 147 tone(1047.0, 0.10);
paulg 0:185bcd9f8e19 148 wait(0.03);
paulg 0:185bcd9f8e19 149 }
paulg 0:185bcd9f8e19 150 }
paulg 0:185bcd9f8e19 151
paulg 0:185bcd9f8e19 152 void PwmSound::beep(int n) {
paulg 0:185bcd9f8e19 153 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 154 tone(969.0, 0.3);
paulg 0:185bcd9f8e19 155 wait(0.1);
paulg 0:185bcd9f8e19 156 }
paulg 0:185bcd9f8e19 157 }
paulg 0:185bcd9f8e19 158
paulg 0:185bcd9f8e19 159 void PwmSound::bleep(int n) {
paulg 0:185bcd9f8e19 160 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 161 tone(800.0, 0.4);
paulg 0:185bcd9f8e19 162 wait(0.1);
paulg 0:185bcd9f8e19 163 }
paulg 0:185bcd9f8e19 164 }
paulg 0:185bcd9f8e19 165
paulg 0:185bcd9f8e19 166 void PwmSound::buzz(int n) {
paulg 0:185bcd9f8e19 167 if (n == 0) {
paulg 0:185bcd9f8e19 168 _setup(1900.0, 300.0, 0.01);
paulg 0:185bcd9f8e19 169 return;
paulg 0:185bcd9f8e19 170 }
paulg 0:185bcd9f8e19 171 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 172 for (int j = 0; j < 20; j++) {
paulg 0:185bcd9f8e19 173 tone(1900.0, 0.01);
paulg 0:185bcd9f8e19 174 tone(300.0, 0.01);
paulg 0:185bcd9f8e19 175 }
paulg 0:185bcd9f8e19 176 }
paulg 0:185bcd9f8e19 177 }
paulg 0:185bcd9f8e19 178
paulg 0:185bcd9f8e19 179 void PwmSound::siren(int n) {
paulg 0:185bcd9f8e19 180 if (n == 0) {
paulg 0:185bcd9f8e19 181 _setup(969.0, 800.0, 0.5);
paulg 0:185bcd9f8e19 182 return;
paulg 0:185bcd9f8e19 183 }
paulg 0:185bcd9f8e19 184 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 185 tone(969.0, 0.5);
paulg 0:185bcd9f8e19 186 tone(800.0, 0.5);
paulg 0:185bcd9f8e19 187 }
paulg 0:185bcd9f8e19 188 }
paulg 0:185bcd9f8e19 189
paulg 0:185bcd9f8e19 190 void PwmSound::trill(int n) {
paulg 0:185bcd9f8e19 191 if (n == 0) {
paulg 0:185bcd9f8e19 192 _setup(969.0, 800.0, 0.05);
paulg 0:185bcd9f8e19 193 return;
paulg 0:185bcd9f8e19 194 }
paulg 0:185bcd9f8e19 195 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 196 if (i > 0) {
paulg 0:185bcd9f8e19 197 tone(800.0, 0.05); //make the trills sound continouus
paulg 0:185bcd9f8e19 198 }
paulg 0:185bcd9f8e19 199 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 200 tone(800.0, 0.05);
paulg 0:185bcd9f8e19 201 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 202 tone(800.0, 0.05);
paulg 0:185bcd9f8e19 203 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 204 tone(800.0, 0.05);
paulg 0:185bcd9f8e19 205 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 206 tone(800.0, 0.05);
paulg 0:185bcd9f8e19 207 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 208 }
paulg 0:185bcd9f8e19 209 }
paulg 0:185bcd9f8e19 210
paulg 0:185bcd9f8e19 211 void PwmSound::phone(int n) {
paulg 0:185bcd9f8e19 212 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 213 trill();
paulg 0:185bcd9f8e19 214 wait(0.10);
paulg 0:185bcd9f8e19 215 trill();
paulg 0:185bcd9f8e19 216 wait(0.7);
paulg 0:185bcd9f8e19 217 }
paulg 0:185bcd9f8e19 218 }
paulg 0:185bcd9f8e19 219
paulg 0:185bcd9f8e19 220 // Continuous sound setup and callback routines
paulg 0:185bcd9f8e19 221
paulg 0:185bcd9f8e19 222 void PwmSound::_setup(float freq1, float freq2, float duration) {
paulg 0:185bcd9f8e19 223 _freq1 = freq1;
paulg 0:185bcd9f8e19 224 _freq2 = freq2;
paulg 0:185bcd9f8e19 225 _beat = false;
paulg 0:185bcd9f8e19 226 _sustainTkr.attach(this, &PwmSound::_sustain, duration);
paulg 0:185bcd9f8e19 227 tone(freq1, 0.0); //start the sound
paulg 0:185bcd9f8e19 228 _playing = true;
paulg 0:185bcd9f8e19 229 }
paulg 0:185bcd9f8e19 230
paulg 0:185bcd9f8e19 231 void PwmSound::_sustain(void) {
paulg 0:185bcd9f8e19 232 if (_playing == false) {
paulg 0:185bcd9f8e19 233 _sustainTkr.detach(); //detach callback to stop sound
paulg 0:185bcd9f8e19 234 _pin = 0.0;
paulg 0:185bcd9f8e19 235 } else {
paulg 0:185bcd9f8e19 236 _beat = !_beat;
paulg 0:185bcd9f8e19 237 tone( (_beat ? _freq2 : _freq1), 0.0);
paulg 0:185bcd9f8e19 238 }
paulg 0:185bcd9f8e19 239 }
paulg 0:185bcd9f8e19 240
paulg 0:185bcd9f8e19 241 // Play a musical note on output pin
paulg 0:185bcd9f8e19 242 //
paulg 0:185bcd9f8e19 243 // Parameters:
paulg 0:185bcd9f8e19 244 // number - 0 = rest, notes from 1 to 84, middle C (262Hz) = 37
paulg 0:185bcd9f8e19 245 // length - duration of note (1-64), 1 = whole note (semibreve)
paulg 0:185bcd9f8e19 246 // 2 = half note (minim)
paulg 0:185bcd9f8e19 247 // 4 = quarter note (crotchet) = 1 beat
paulg 0:185bcd9f8e19 248 // 8 = eighth note (quaver)
paulg 0:185bcd9f8e19 249 // etc
paulg 0:185bcd9f8e19 250 // Returns: nothing
paulg 0:185bcd9f8e19 251 // Uses: current tempo, music style and volume (duty cycle)
paulg 0:185bcd9f8e19 252
paulg 0:185bcd9f8e19 253 void PwmSound::note(int number, int length) {
paulg 0:185bcd9f8e19 254 int frequency;
paulg 0:185bcd9f8e19 255 float duration, play, rest;
paulg 0:185bcd9f8e19 256
paulg 0:185bcd9f8e19 257 //pc.printf("<number %d, length %d, volume %f, ", number, length, volume);
paulg 0:185bcd9f8e19 258 if (number < 1 || number > 84) { //convert bad note to a rest
paulg 0:185bcd9f8e19 259 number = 0;
paulg 0:185bcd9f8e19 260 }
paulg 0:185bcd9f8e19 261 frequency = notePitches[number];
paulg 0:185bcd9f8e19 262
paulg 0:185bcd9f8e19 263 duration = 240.0 / (_tempo * length);
paulg 0:185bcd9f8e19 264 if (_style == PwmSound::STACCATO) { //staccato
paulg 0:185bcd9f8e19 265 play = duration * 0.75;
paulg 0:185bcd9f8e19 266 rest = duration * 0.25;
paulg 0:185bcd9f8e19 267 } else if (_style == PwmSound::LEGATO) { //legato
paulg 0:185bcd9f8e19 268 play = duration;
paulg 0:185bcd9f8e19 269 rest = 0;
paulg 0:185bcd9f8e19 270 } else { //normal
paulg 0:185bcd9f8e19 271 play = duration * 0.875;
paulg 0:185bcd9f8e19 272 rest = duration * 0.125;
paulg 0:185bcd9f8e19 273 }
paulg 0:185bcd9f8e19 274 //pc.printf("f %d, d %f, p %f, r %f>\n", frequency, duration, play, rest);
paulg 0:185bcd9f8e19 275 if (number > 0) {
paulg 0:185bcd9f8e19 276 _pin.period(1.0 / frequency);
paulg 0:185bcd9f8e19 277 _pin = _volume / 2.0;
paulg 0:185bcd9f8e19 278 }
paulg 0:185bcd9f8e19 279 wait(play);
paulg 0:185bcd9f8e19 280 _pin = 0.0;
paulg 0:185bcd9f8e19 281 wait(rest);
paulg 0:185bcd9f8e19 282 }
paulg 0:185bcd9f8e19 283
paulg 0:185bcd9f8e19 284 // Set default tempo
paulg 0:185bcd9f8e19 285 //
paulg 0:185bcd9f8e19 286 // Parameters:
paulg 0:185bcd9f8e19 287 // tempo - tempo in BPM
paulg 0:185bcd9f8e19 288 // Returns: nothing
paulg 0:185bcd9f8e19 289
paulg 0:185bcd9f8e19 290 void PwmSound::tempo(int tempo) {
paulg 0:185bcd9f8e19 291 _tempo = tempo;
paulg 0:185bcd9f8e19 292 }
paulg 0:185bcd9f8e19 293
paulg 0:185bcd9f8e19 294 // Set default music style
paulg 0:185bcd9f8e19 295 //
paulg 0:185bcd9f8e19 296 // Parameters:
paulg 0:185bcd9f8e19 297 // style - STAACATO, NORMAL or LEGATO
paulg 0:185bcd9f8e19 298 // Returns: nothing
paulg 0:185bcd9f8e19 299
paulg 0:185bcd9f8e19 300 void PwmSound::style(MusicStyle style) {
paulg 0:185bcd9f8e19 301 _style = style;
paulg 0:185bcd9f8e19 302 }
paulg 0:185bcd9f8e19 303
paulg 0:185bcd9f8e19 304 // Play a simple tune from note data in an array.
paulg 0:185bcd9f8e19 305 //
paulg 0:185bcd9f8e19 306 // Parameters:
paulg 0:185bcd9f8e19 307 // tune - pointer to char array containing tune data
paulg 0:185bcd9f8e19 308 // first entry is tempo
paulg 0:185bcd9f8e19 309 // second entry is music style (STACCATO, NORMAL, LEGATO)
paulg 0:185bcd9f8e19 310 // subsequent entries are note number/length pairs
paulg 0:185bcd9f8e19 311 // final entry must be 0, 0
paulg 0:185bcd9f8e19 312 // Returns: nothing
paulg 0:185bcd9f8e19 313 // Uses: current volume
paulg 0:185bcd9f8e19 314
paulg 0:185bcd9f8e19 315 void PwmSound::tune(unsigned char* tune) {
paulg 0:185bcd9f8e19 316 int t, number, length;
paulg 0:185bcd9f8e19 317
paulg 0:185bcd9f8e19 318 t = *tune++;
paulg 0:185bcd9f8e19 319 if (t == 0) {
paulg 0:185bcd9f8e19 320 return;
paulg 0:185bcd9f8e19 321 }
paulg 0:185bcd9f8e19 322 tempo(t);
paulg 0:185bcd9f8e19 323 style( (MusicStyle) *tune++);
paulg 0:185bcd9f8e19 324 while (true) {
paulg 0:185bcd9f8e19 325 number = *tune++;
paulg 0:185bcd9f8e19 326 length = *tune++;
paulg 0:185bcd9f8e19 327 if (number == 0 && length == 0) {
paulg 0:185bcd9f8e19 328 break;
paulg 0:185bcd9f8e19 329 }
paulg 0:185bcd9f8e19 330 note(number, length);
paulg 0:185bcd9f8e19 331 }
paulg 0:185bcd9f8e19 332 }
paulg 0:185bcd9f8e19 333
paulg 0:185bcd9f8e19 334 // END of PwmSound.cpp