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
Parent:
0:185bcd9f8e19
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 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 1:67056b9df9ff 9 * Class to play tones, various sounds, musical notes and simple tunes using
paulg 1:67056b9df9ff 10 * a PWM channel. 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 1:67056b9df9ff 40 * 2.00 06May14 PG Added play() etc to support MML music. Removed tune() etc.
paulg 0:185bcd9f8e19 41 *
paulg 0:185bcd9f8e19 42 ******************************************************************************/
paulg 0:185bcd9f8e19 43
paulg 0:185bcd9f8e19 44 #include "mbed.h"
paulg 0:185bcd9f8e19 45 #include "PwmSound.h"
paulg 1:67056b9df9ff 46 #include "FastOut.h"
paulg 0:185bcd9f8e19 47
paulg 1:67056b9df9ff 48 extern Serial pc; //for debugging, comment out if not needed
paulg 1:67056b9df9ff 49 FastOut<LED1> led1;
paulg 0:185bcd9f8e19 50
paulg 0:185bcd9f8e19 51 // Constructor
paulg 0:185bcd9f8e19 52
paulg 0:185bcd9f8e19 53 PwmSound::PwmSound(PinName pin) : _pin(pin) {
paulg 1:67056b9df9ff 54 _dutyCycle = 0.5;
paulg 1:67056b9df9ff 55 _pin = 0.0;
paulg 1:67056b9df9ff 56 _playing = false;
paulg 1:67056b9df9ff 57 }
paulg 0:185bcd9f8e19 58
paulg 0:185bcd9f8e19 59 // Play a tone on output pin
paulg 0:185bcd9f8e19 60 //
paulg 0:185bcd9f8e19 61 // Parameters:
paulg 0:185bcd9f8e19 62 // frequency - frequency of tone in Hz
paulg 0:185bcd9f8e19 63 // duration - duration of tone in seconds
paulg 0:185bcd9f8e19 64 // if duration = 0.0, tone continues in background until stopped
paulg 0:185bcd9f8e19 65 // Returns: nothing
paulg 1:67056b9df9ff 66 // Uses: current duty cycle
paulg 0:185bcd9f8e19 67
paulg 0:185bcd9f8e19 68 void PwmSound::tone(float frequency, float duration) {
paulg 0:185bcd9f8e19 69 _pin.period(1.0 / frequency);
paulg 1:67056b9df9ff 70 _pin = _dutyCycle;
paulg 0:185bcd9f8e19 71 if (duration == 0.0) {
paulg 0:185bcd9f8e19 72 _playing = true;
paulg 0:185bcd9f8e19 73 return;
paulg 0:185bcd9f8e19 74 }
paulg 0:185bcd9f8e19 75 wait(duration);
paulg 0:185bcd9f8e19 76 _pin = 0.0;
paulg 0:185bcd9f8e19 77 }
paulg 0:185bcd9f8e19 78
paulg 0:185bcd9f8e19 79 // Stop background tone or sound generation
paulg 0:185bcd9f8e19 80
paulg 0:185bcd9f8e19 81 void PwmSound::stop(void) {
paulg 0:185bcd9f8e19 82 _playing = false;
paulg 0:185bcd9f8e19 83 _pin = 0.0;
paulg 0:185bcd9f8e19 84 }
paulg 0:185bcd9f8e19 85
paulg 1:67056b9df9ff 86 // Set timbre (tonal quality)
paulg 0:185bcd9f8e19 87 //
paulg 0:185bcd9f8e19 88 // Parameters:
paulg 1:67056b9df9ff 89 // timbre - (1-4): sets PWM duty cycle to 12.5%, 25%, 37.5% or 50%
paulg 0:185bcd9f8e19 90 // Returns: nothing
paulg 0:185bcd9f8e19 91
paulg 1:67056b9df9ff 92 void PwmSound::timbre(int t) {
paulg 1:67056b9df9ff 93 if (t >= 1 && t <= 4) {
paulg 1:67056b9df9ff 94 _dutyCycle = t / 8.0;
paulg 1:67056b9df9ff 95 }
paulg 0:185bcd9f8e19 96 }
paulg 0:185bcd9f8e19 97 // Beeps of various types and other sounds
paulg 1:67056b9df9ff 98 // Note: All sounds below except phone permit continuous sound in background
paulg 1:67056b9df9ff 99 // To invoke this call the function with a zero parameter
paulg 1:67056b9df9ff 100 // Call stop() to end the sound
paulg 0:185bcd9f8e19 101 //
paulg 0:185bcd9f8e19 102 // Parameters:
paulg 1:67056b9df9ff 103 // n - number of cycles, 0 for continuous sound in background (not phone)
paulg 0:185bcd9f8e19 104 // Returns: nothing
paulg 0:185bcd9f8e19 105
paulg 0:185bcd9f8e19 106 void PwmSound::bip(int n) {
paulg 1:67056b9df9ff 107 if (n == 0) {
paulg 1:67056b9df9ff 108 _setup(1047.0, 0.1, 0.0, 0.03);
paulg 1:67056b9df9ff 109 return;
paulg 1:67056b9df9ff 110 }
paulg 0:185bcd9f8e19 111 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 112 tone(1047.0, 0.10);
paulg 0:185bcd9f8e19 113 wait(0.03);
paulg 0:185bcd9f8e19 114 }
paulg 0:185bcd9f8e19 115 }
paulg 0:185bcd9f8e19 116
paulg 1:67056b9df9ff 117 void PwmSound::bop(int n) {
paulg 1:67056b9df9ff 118 if (n == 0) {
paulg 1:67056b9df9ff 119 _setup(700.0, 0.1, 0.0, 0.03);
paulg 1:67056b9df9ff 120 return;
paulg 1:67056b9df9ff 121 }
paulg 1:67056b9df9ff 122 for (int i = 0; i < n; i++) {
paulg 1:67056b9df9ff 123 tone(700.0, 0.10);
paulg 1:67056b9df9ff 124 wait(0.03);
paulg 1:67056b9df9ff 125 }
paulg 1:67056b9df9ff 126 }
paulg 1:67056b9df9ff 127
paulg 0:185bcd9f8e19 128 void PwmSound::beep(int n) {
paulg 1:67056b9df9ff 129 if (n == 0) {
paulg 1:67056b9df9ff 130 _setup(969.0, 0.3, 0.0, 0.1);
paulg 1:67056b9df9ff 131 return;
paulg 1:67056b9df9ff 132 }
paulg 0:185bcd9f8e19 133 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 134 tone(969.0, 0.3);
paulg 0:185bcd9f8e19 135 wait(0.1);
paulg 0:185bcd9f8e19 136 }
paulg 0:185bcd9f8e19 137 }
paulg 0:185bcd9f8e19 138
paulg 0:185bcd9f8e19 139 void PwmSound::bleep(int n) {
paulg 1:67056b9df9ff 140 if (n == 0) {
paulg 1:67056b9df9ff 141 _setup(800.0, 0.4, 0.0, 0.1);
paulg 1:67056b9df9ff 142 return;
paulg 1:67056b9df9ff 143 }
paulg 0:185bcd9f8e19 144 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 145 tone(800.0, 0.4);
paulg 0:185bcd9f8e19 146 wait(0.1);
paulg 0:185bcd9f8e19 147 }
paulg 0:185bcd9f8e19 148 }
paulg 0:185bcd9f8e19 149
paulg 0:185bcd9f8e19 150 void PwmSound::buzz(int n) {
paulg 0:185bcd9f8e19 151 if (n == 0) {
paulg 1:67056b9df9ff 152 _setup(1900.0, 0.01, 300.0, 0.01);
paulg 0:185bcd9f8e19 153 return;
paulg 0:185bcd9f8e19 154 }
paulg 0:185bcd9f8e19 155 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 156 for (int j = 0; j < 20; j++) {
paulg 0:185bcd9f8e19 157 tone(1900.0, 0.01);
paulg 0:185bcd9f8e19 158 tone(300.0, 0.01);
paulg 0:185bcd9f8e19 159 }
paulg 0:185bcd9f8e19 160 }
paulg 0:185bcd9f8e19 161 }
paulg 0:185bcd9f8e19 162
paulg 0:185bcd9f8e19 163 void PwmSound::siren(int n) {
paulg 0:185bcd9f8e19 164 if (n == 0) {
paulg 1:67056b9df9ff 165 _setup(969.0, 0.5, 800.0, 0.5);
paulg 0:185bcd9f8e19 166 return;
paulg 0:185bcd9f8e19 167 }
paulg 0:185bcd9f8e19 168 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 169 tone(969.0, 0.5);
paulg 0:185bcd9f8e19 170 tone(800.0, 0.5);
paulg 0:185bcd9f8e19 171 }
paulg 0:185bcd9f8e19 172 }
paulg 0:185bcd9f8e19 173
paulg 0:185bcd9f8e19 174 void PwmSound::trill(int n) {
paulg 0:185bcd9f8e19 175 if (n == 0) {
paulg 1:67056b9df9ff 176 _setup(969.0, 0.05, 800.0, 0.05);
paulg 0:185bcd9f8e19 177 return;
paulg 0:185bcd9f8e19 178 }
paulg 0:185bcd9f8e19 179 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 180 if (i > 0) {
paulg 0:185bcd9f8e19 181 tone(800.0, 0.05); //make the trills sound continouus
paulg 0:185bcd9f8e19 182 }
paulg 0:185bcd9f8e19 183 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 184 tone(800.0, 0.05);
paulg 0:185bcd9f8e19 185 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 186 tone(800.0, 0.05);
paulg 0:185bcd9f8e19 187 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 188 tone(800.0, 0.05);
paulg 0:185bcd9f8e19 189 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 190 tone(800.0, 0.05);
paulg 0:185bcd9f8e19 191 tone(969.0, 0.05);
paulg 0:185bcd9f8e19 192 }
paulg 0:185bcd9f8e19 193 }
paulg 0:185bcd9f8e19 194
paulg 0:185bcd9f8e19 195 void PwmSound::phone(int n) {
paulg 0:185bcd9f8e19 196 for (int i = 0; i < n; i++) {
paulg 0:185bcd9f8e19 197 trill();
paulg 0:185bcd9f8e19 198 wait(0.10);
paulg 0:185bcd9f8e19 199 trill();
paulg 0:185bcd9f8e19 200 wait(0.7);
paulg 0:185bcd9f8e19 201 }
paulg 0:185bcd9f8e19 202 }
paulg 0:185bcd9f8e19 203
paulg 0:185bcd9f8e19 204 // Continuous sound setup and callback routines
paulg 1:67056b9df9ff 205 // _sustain() has been optimised for speed. On a 96MHz LPC1768 it takes 8.5us.
paulg 1:67056b9df9ff 206 // Non-optimised version with floating point freqency & duration took 11.4us.
paulg 1:67056b9df9ff 207 // Execution times measured with 'scope on LED1 pin.
paulg 0:185bcd9f8e19 208
paulg 1:67056b9df9ff 209 void PwmSound::_setup(float freq1, float dur1, float freq2, float dur2) {
paulg 1:67056b9df9ff 210 _period1 = (int) (1000000.0 / freq1);
paulg 1:67056b9df9ff 211 _period2 = (int) (1000000.0 / freq2);
paulg 1:67056b9df9ff 212 _dur1 = (unsigned int) (1000000.0 * dur1);
paulg 1:67056b9df9ff 213 _dur2 = (unsigned int) (1000000.0 * dur2);
paulg 1:67056b9df9ff 214 _phase = false;
paulg 1:67056b9df9ff 215 _sustainTmo.attach_us(this, &PwmSound::_sustain, _dur1);
paulg 1:67056b9df9ff 216 _pin.period_us(_period1); //start the sound
paulg 1:67056b9df9ff 217 _pin = _dutyCycle;
paulg 0:185bcd9f8e19 218 _playing = true;
paulg 0:185bcd9f8e19 219 }
paulg 0:185bcd9f8e19 220
paulg 0:185bcd9f8e19 221 void PwmSound::_sustain(void) {
paulg 1:67056b9df9ff 222 //led1 = 1;
paulg 0:185bcd9f8e19 223 if (_playing == false) {
paulg 1:67056b9df9ff 224 //kill pwm and no more callbacks
paulg 0:185bcd9f8e19 225 _pin = 0.0;
paulg 0:185bcd9f8e19 226 } else {
paulg 1:67056b9df9ff 227 _phase = !_phase;
paulg 1:67056b9df9ff 228 if (_phase) {
paulg 1:67056b9df9ff 229 _pin.period_us(_period2);
paulg 1:67056b9df9ff 230 _pin = _dutyCycle;
paulg 1:67056b9df9ff 231 _sustainTmo.attach_us(this, &PwmSound::_sustain, _dur2);
paulg 1:67056b9df9ff 232 } else {
paulg 1:67056b9df9ff 233 _pin.period_us(_period1);
paulg 1:67056b9df9ff 234 _pin = _dutyCycle;
paulg 1:67056b9df9ff 235 _sustainTmo.attach_us(this, &PwmSound::_sustain, _dur1);
paulg 1:67056b9df9ff 236 }
paulg 0:185bcd9f8e19 237 }
paulg 1:67056b9df9ff 238 //led1 = 0;
paulg 0:185bcd9f8e19 239 }
paulg 0:185bcd9f8e19 240
paulg 1:67056b9df9ff 241 // END of PwmSound.cpp