Class to play tones, various sounds and music in MML format using a PWM channel.
PwmSound.cpp@1:67056b9df9ff, 2014-05-06 (annotated)
- 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?
User | Revision | Line number | New 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 |