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

Dependents:   PwmSoundTest

Files at this revision

API Documentation at this revision

Comitter:
paulg
Date:
Sun Mar 30 22:50:27 2014 +0000
Child:
1:67056b9df9ff
Commit message:
Initial release.

Changed in this revision

PwmSound.cpp Show annotated file Show diff for this revision Revisions of this file
PwmSound.h Show annotated file Show diff for this revision Revisions of this file
--- /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PwmSound.h	Sun Mar 30 22:50:27 2014 +0000
@@ -0,0 +1,92 @@
+/******************************************************************************
+ * File:      PwmSound.h
+ * Author:    Paul Griffith
+ * Created:   25 Mar 2014
+ * Last Edit: see below
+ * Version:   see below
+ *
+ * Description:
+ * Definitions for PwmSound class.
+ *
+ * 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
+ *
+ ******************************************************************************/
+
+#ifndef MBED_PWMSOUND_H
+#define MBED_PWMSOUND_H
+
+#include "mbed.h"
+
+class PwmSound {
+//private:
+
+public:
+    enum MusicStyle { STACCATO, NORMAL, LEGATO };
+
+    PwmSound(PinName pin);
+
+    void tone(float frequency, float duration);     //tones
+    void tone(float frequency);
+    void stop(void);
+    void duration(float duration);
+    void volume(float volume);
+
+    void bip(int n = 1);    //beeps and other sounds
+    void beep(int n = 1);
+    void bleep(int n = 1);
+    void buzz(int n = 1);
+    void siren(int n = 1);
+    void trill(int n = 1);
+    void phone(int n = 1);
+
+    void note(int number, int length);  //musical note
+    void tempo(int tempo);
+    void style(MusicStyle style);
+ 
+    void tune(unsigned char* t);    //play tune from data in array
+
+private:
+    PwmOut _pin;
+    float _duration;    //duration of tone in seconds
+    float _volume;      //crude volume 0.0-1.0 => 0-50% duty cycle
+    int _tempo;     //pace of music in beats per minute (32-255)
+                    //one beat equals one quarter note (ie a crotchet)
+    int _length;    //length of note (1-64), 1 = whole note, 4 = quarter etc
+    MusicStyle _style;
+
+    //the following support continuous two-tone sounds in background
+    void _setup(float freq1, float freq2, float duration);
+    void _sustain(void);    
+    Ticker _sustainTkr;
+    float _freq1;
+    float _freq2;
+    bool _beat;
+    bool _playing;
+};
+
+#endif
+
+// END of PwmSound.h
\ No newline at end of file