Class to play tones, various sounds and music in MML format using a PWM channel.
Diff: PwmSound.cpp
- Revision:
- 0:185bcd9f8e19
- Child:
- 1:67056b9df9ff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PwmSound.cpp Sun Mar 30 22:50:27 2014 +0000 @@ -0,0 +1,334 @@ +/****************************************************************************** + * File: PwmSound.cpp + * Author: Paul Griffith + * Created: 25 Mar 2014 + * Last Edit: see below + * 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! + * + * 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. + * Connect speaker via a 220R resistor and 100uF 10V capacitor (+ve to mbed). + * + * Copyright (c) 2014 Paul Griffith, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Modifications: + * Ver Date By Details + * 0.00 25Mar14 PG File created. + * 1.00 30Mar14 PG Initial release. + * + ******************************************************************************/ + +#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) + +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, +}; + +// 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; + } + +// 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) + +void PwmSound::tone(float frequency, float duration) { + _pin.period(1.0 / frequency); + _pin = _volume / 2.0; + if (duration == 0.0) { + _playing = true; + return; + } + wait(duration); + _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) { + _playing = false; + _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 +// +// Parameters: +// volume - crude volume control (0.0 - 1.0), alters PWM duty cycle 0 - 50% +// Returns: nothing + +void PwmSound::volume(float volume) { + _volume = volume; +} + +// Beeps of various types and other sounds +// Note: buzz, siren and trill use a ticker and callback to +// support continuous sound in background +// +// Parameters: +// n - number of cycles, 0 for continuous sound in background +// Returns: nothing + +void PwmSound::bip(int n) { + for (int i = 0; i < n; i++) { + tone(1047.0, 0.10); + wait(0.03); + } +} + +void PwmSound::beep(int n) { + for (int i = 0; i < n; i++) { + tone(969.0, 0.3); + wait(0.1); + } +} + +void PwmSound::bleep(int n) { + for (int i = 0; i < n; i++) { + tone(800.0, 0.4); + wait(0.1); + } +} + +void PwmSound::buzz(int n) { + if (n == 0) { + _setup(1900.0, 300.0, 0.01); + return; + } + for (int i = 0; i < n; i++) { + for (int j = 0; j < 20; j++) { + tone(1900.0, 0.01); + tone(300.0, 0.01); + } + } +} + +void PwmSound::siren(int n) { + if (n == 0) { + _setup(969.0, 800.0, 0.5); + return; + } + for (int i = 0; i < n; i++) { + tone(969.0, 0.5); + tone(800.0, 0.5); + } +} + +void PwmSound::trill(int n) { + if (n == 0) { + _setup(969.0, 800.0, 0.05); + return; + } + for (int i = 0; i < n; i++) { + if (i > 0) { + tone(800.0, 0.05); //make the trills sound continouus + } + tone(969.0, 0.05); + tone(800.0, 0.05); + tone(969.0, 0.05); + tone(800.0, 0.05); + tone(969.0, 0.05); + tone(800.0, 0.05); + tone(969.0, 0.05); + tone(800.0, 0.05); + tone(969.0, 0.05); + } +} + +void PwmSound::phone(int n) { + for (int i = 0; i < n; i++) { + trill(); + wait(0.10); + trill(); + wait(0.7); + } +} + +// Continuous sound setup and callback routines + +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 + _playing = true; +} + +void PwmSound::_sustain(void) { + if (_playing == false) { + _sustainTkr.detach(); //detach callback to stop sound + _pin = 0.0; + } else { + _beat = !_beat; + tone( (_beat ? _freq2 : _freq1), 0.0); + } +} + +// 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); +} + +// 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