Class to play tones, various sounds and music in MML format using a PWM channel.
Diff: PwmSound.cpp
- Revision:
- 1:67056b9df9ff
- Parent:
- 0:185bcd9f8e19
diff -r 185bcd9f8e19 -r 67056b9df9ff PwmSound.cpp --- a/PwmSound.cpp Sun Mar 30 22:50:27 2014 +0000 +++ b/PwmSound.cpp Tue May 06 13:16:28 2014 +0000 @@ -6,8 +6,8 @@ * Version: see below * * Description: - * Class to play tones, various sounds and simple tunes using a PWM channel. - * Inspired by Jim Hamblem's Speaker class. Thanks Jim! + * Class to play tones, various sounds, musical notes and simple tunes using + * a PWM channel. Inspired by Jim Hamblem's Speaker class. Thanks Jim! * * Refer to the tutorial "Using a Speaker for Audio Output" in the Cookbook. * The mbed LPC1768 PWM pins will drive a small speaker without amplification. @@ -37,57 +37,37 @@ * Ver Date By Details * 0.00 25Mar14 PG File created. * 1.00 30Mar14 PG Initial release. + * 2.00 06May14 PG Added play() etc to support MML music. Removed tune() etc. * ******************************************************************************/ #include "mbed.h" #include "PwmSound.h" - -extern Serial pc; - -// Standard note pitches in Hz -// First entry is a dummy, real note numbers start at 1 -// Seven octaves, twelve notes per octave -// C, C#, D, D#, E, F, F#, G, G#, A, A#, B -// Middle C is element 37 (262Hz) +#include "FastOut.h" -int notePitches[1+84] = { - 1, //dummy - 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, - 65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123, - 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, - 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, - 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, - 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, - 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, -}; +extern Serial pc; //for debugging, comment out if not needed +FastOut<LED1> led1; // Constructor PwmSound::PwmSound(PinName pin) : _pin(pin) { - _duration = 0.25; - _volume = 1.0; - _tempo = 120; //120 beats/minute - _length = 4; //quarter note (crotchet) - _style = PwmSound::NORMAL; - _pin = 0.0; - _playing = false; - } + _dutyCycle = 0.5; + _pin = 0.0; + _playing = false; +} // Play a tone on output pin -// 2 versions for convenience when using default duration // // Parameters: // frequency - frequency of tone in Hz // duration - duration of tone in seconds // if duration = 0.0, tone continues in background until stopped -// volume - crude volume control (0.0-1.0), alters PWM duty cycle 0-50% // Returns: nothing -// Uses: current volume (duty cycle) +// Uses: current duty cycle void PwmSound::tone(float frequency, float duration) { _pin.period(1.0 / frequency); - _pin = _volume / 2.0; + _pin = _dutyCycle; if (duration == 0.0) { _playing = true; return; @@ -96,17 +76,6 @@ _pin = 0.0; } -void PwmSound::tone(float frequency) { - _pin.period(1.0 / frequency); - _pin = _volume / 2.0; - if (_duration == 0.0) { - _playing = true; - return; - } - wait(_duration); - _pin = 0.0; -} - // Stop background tone or sound generation void PwmSound::stop(void) { @@ -114,42 +83,53 @@ _pin = 0.0; } -// Set default duration -// -// Parameters: -// duration - duration of tone in seconds -// Returns: nothing - -void PwmSound::duration(float duration) { - _duration = duration; -} - -// Set default volume +// Set timbre (tonal quality) // // Parameters: -// volume - crude volume control (0.0 - 1.0), alters PWM duty cycle 0 - 50% +// timbre - (1-4): sets PWM duty cycle to 12.5%, 25%, 37.5% or 50% // Returns: nothing -void PwmSound::volume(float volume) { - _volume = volume; +void PwmSound::timbre(int t) { + if (t >= 1 && t <= 4) { + _dutyCycle = t / 8.0; + } } - // Beeps of various types and other sounds -// Note: buzz, siren and trill use a ticker and callback to -// support continuous sound in background +// Note: All sounds below except phone permit continuous sound in background +// To invoke this call the function with a zero parameter +// Call stop() to end the sound // // Parameters: -// n - number of cycles, 0 for continuous sound in background +// n - number of cycles, 0 for continuous sound in background (not phone) // Returns: nothing void PwmSound::bip(int n) { + if (n == 0) { + _setup(1047.0, 0.1, 0.0, 0.03); + return; + } for (int i = 0; i < n; i++) { tone(1047.0, 0.10); wait(0.03); } } +void PwmSound::bop(int n) { + if (n == 0) { + _setup(700.0, 0.1, 0.0, 0.03); + return; + } + for (int i = 0; i < n; i++) { + tone(700.0, 0.10); + wait(0.03); + } +} + void PwmSound::beep(int n) { + if (n == 0) { + _setup(969.0, 0.3, 0.0, 0.1); + return; + } for (int i = 0; i < n; i++) { tone(969.0, 0.3); wait(0.1); @@ -157,6 +137,10 @@ } void PwmSound::bleep(int n) { + if (n == 0) { + _setup(800.0, 0.4, 0.0, 0.1); + return; + } for (int i = 0; i < n; i++) { tone(800.0, 0.4); wait(0.1); @@ -165,7 +149,7 @@ void PwmSound::buzz(int n) { if (n == 0) { - _setup(1900.0, 300.0, 0.01); + _setup(1900.0, 0.01, 300.0, 0.01); return; } for (int i = 0; i < n; i++) { @@ -178,7 +162,7 @@ void PwmSound::siren(int n) { if (n == 0) { - _setup(969.0, 800.0, 0.5); + _setup(969.0, 0.5, 800.0, 0.5); return; } for (int i = 0; i < n; i++) { @@ -189,7 +173,7 @@ void PwmSound::trill(int n) { if (n == 0) { - _setup(969.0, 800.0, 0.05); + _setup(969.0, 0.05, 800.0, 0.05); return; } for (int i = 0; i < n; i++) { @@ -218,117 +202,40 @@ } // Continuous sound setup and callback routines +// _sustain() has been optimised for speed. On a 96MHz LPC1768 it takes 8.5us. +// Non-optimised version with floating point freqency & duration took 11.4us. +// Execution times measured with 'scope on LED1 pin. -void PwmSound::_setup(float freq1, float freq2, float duration) { - _freq1 = freq1; - _freq2 = freq2; - _beat = false; - _sustainTkr.attach(this, &PwmSound::_sustain, duration); - tone(freq1, 0.0); //start the sound +void PwmSound::_setup(float freq1, float dur1, float freq2, float dur2) { + _period1 = (int) (1000000.0 / freq1); + _period2 = (int) (1000000.0 / freq2); + _dur1 = (unsigned int) (1000000.0 * dur1); + _dur2 = (unsigned int) (1000000.0 * dur2); + _phase = false; + _sustainTmo.attach_us(this, &PwmSound::_sustain, _dur1); + _pin.period_us(_period1); //start the sound + _pin = _dutyCycle; _playing = true; } void PwmSound::_sustain(void) { + //led1 = 1; if (_playing == false) { - _sustainTkr.detach(); //detach callback to stop sound + //kill pwm and no more callbacks _pin = 0.0; } else { - _beat = !_beat; - tone( (_beat ? _freq2 : _freq1), 0.0); + _phase = !_phase; + if (_phase) { + _pin.period_us(_period2); + _pin = _dutyCycle; + _sustainTmo.attach_us(this, &PwmSound::_sustain, _dur2); + } else { + _pin.period_us(_period1); + _pin = _dutyCycle; + _sustainTmo.attach_us(this, &PwmSound::_sustain, _dur1); + } } -} - -// Play a musical note on output pin -// -// Parameters: -// number - 0 = rest, notes from 1 to 84, middle C (262Hz) = 37 -// length - duration of note (1-64), 1 = whole note (semibreve) -// 2 = half note (minim) -// 4 = quarter note (crotchet) = 1 beat -// 8 = eighth note (quaver) -// etc -// Returns: nothing -// Uses: current tempo, music style and volume (duty cycle) - -void PwmSound::note(int number, int length) { - int frequency; - float duration, play, rest; - - //pc.printf("<number %d, length %d, volume %f, ", number, length, volume); - if (number < 1 || number > 84) { //convert bad note to a rest - number = 0; - } - frequency = notePitches[number]; - - duration = 240.0 / (_tempo * length); - if (_style == PwmSound::STACCATO) { //staccato - play = duration * 0.75; - rest = duration * 0.25; - } else if (_style == PwmSound::LEGATO) { //legato - play = duration; - rest = 0; - } else { //normal - play = duration * 0.875; - rest = duration * 0.125; - } - //pc.printf("f %d, d %f, p %f, r %f>\n", frequency, duration, play, rest); - if (number > 0) { - _pin.period(1.0 / frequency); - _pin = _volume / 2.0; - } - wait(play); - _pin = 0.0; - wait(rest); + //led1 = 0; } -// Set default tempo -// -// Parameters: -// tempo - tempo in BPM -// Returns: nothing - -void PwmSound::tempo(int tempo) { - _tempo = tempo; -} - -// Set default music style -// -// Parameters: -// style - STAACATO, NORMAL or LEGATO -// Returns: nothing - -void PwmSound::style(MusicStyle style) { - _style = style; -} - -// Play a simple tune from note data in an array. -// -// Parameters: -// tune - pointer to char array containing tune data -// first entry is tempo -// second entry is music style (STACCATO, NORMAL, LEGATO) -// subsequent entries are note number/length pairs -// final entry must be 0, 0 -// Returns: nothing -// Uses: current volume - -void PwmSound::tune(unsigned char* tune) { - int t, number, length; - - t = *tune++; - if (t == 0) { - return; - } - tempo(t); - style( (MusicStyle) *tune++); - while (true) { - number = *tune++; - length = *tune++; - if (number == 0 && length == 0) { - break; - } - note(number, length); - } -} - -// END of PwmSound.cpp \ No newline at end of file +// END of PwmSound.cpp