Tone library and RTTTL (RingTone Text Transfer Language) player

SwordfishUser.ToneLibraryAndRingtoneRTTTLPlayer History

Show minor edits - Show changes to output

Changed lines 388-391 from:


 

to:
!!!RTTTL example #2 - Using events
=code [=
// RTTTL example with tone events to control LEDs
program RTTTL_events_example

device = 18F26K22
clock = 16

// int osc and IO pin libraries
include "intosc.bas"
#option DIGITALIO_INIT = true      // automatically call setalldigital
#option DIGITALIO_SLEWRATE = 1      // 1=slow (we don't need fast edges)
include "setdigitalio.bas"

// tone library
// specify output pin
#option TONE_OUTPUT_PIN = PORTA.0
// wait flag setting (default = false)
#option TONE_WAIT = true
include "tone.bas"

// RTTTL library (uses tone library)
include "rtttl.bas"

// there are 8 leds... one for each note (C, D, E, F, G, A, B), and a spare
#option _LED_PORT = PORTC
#option _LED_PORT_TRIS = GetTris(_LED_PORT)

dim LED_PORT as _LED_PORT,
    LED_PORT_TRIS as _LED_PORT_TRIS,
    LED_C as LED_PORT.0,    // led 1
    LED_D as LED_PORT.1,    // led 2
    LED_E as LED_PORT.2,    // led 3
    LED_F as LED_PORT.3,    // led 4
    LED_G as LED_PORT.4,    // led 5
    LED_A as LED_PORT.5,    // led 6
    LED_B as LED_PORT.6,    // led 7
    LED_8 as LED_PORT.7      // led 8 (outside RTTTL range)

//------------
// tone events
//------------
// tone start event - turn on leds
// there are 12 notes in a scale, but there are only 8 leds
// combine the notes and sharps together into a single led (ie, C or C# -> LED_C, G or G# -> LED_G, etc)
// since RTTTL supports 4 scales (C4 to C7) combine all the scales into one
// so that a C played in any scale turns on LED_C
// for any notes outside the 4 scales covered by RTTTL it turns on LED_8
event tone_start_event()
    dim freq as word
   
    // get the current freq
    freq = tone.freq()

    // put all RTTTL supported notes into the same scale (scale 4)
    if (freq > NOTE_B6) then
        freq = freq >> 3            // divide by 8... scale 7 -> 4
    elseif (freq > NOTE_B5) then
        freq = freq >> 2            // divide by 4... scale 6 -> 4
    elseif (freq > NOTE_B4) then
        freq = freq >> 1            // divide by 2... scale 5 -> 4
    endif

    // determine which led to turn on
    select (freq)
        case > NOTE_B4      // above B7... lite spare LED
            LED_8 = 1
        case > NOTE_AS4    // B
            LED_B = 1
        case > NOTE_GS4    // A and A#
            LED_A = 1
        case > NOTE_FS4    // G and G#
            LED_G = 1                       
        case > NOTE_E4      // F and F#
            LED_F = 1
        case > NOTE_DS4    // E
            LED_E = 1
        case > NOTE_CS4    // D and D#
            LED_D = 1
        case > NOTE_B3      // C and C#
            LED_C = 1
        else
            LED_8 = 1      // below C4/unknown... lite spare LED
    end select                                                                   
end event

// tone stop event - turn off leds
event tone_stop_event()
    LED_PORT = 0        // turn off all leds
end event


// some example RTTTL tunes
public const IndianaJones = "IndianaJones:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6"

main:
    // set led pins as outputs and turn them off
    LED_PORT = 0
    LED_PORT_TRIS = 0

    // init tone library module
    tone.init()
    tone.setwait()              // set wait_flag true

    // set tone events
    tone.set_events(tone_start_event, tone_stop_event)
   
    // play tune
    rtttl.play(@IndianaJones)

    while (true)
    end while

end program
=]
Changed lines 4-6 from:
[[Attach:tone_v11.zip | Download Tone and RTTTL player libraries v1.1]]\\\

to:
[[Attach:tone_RTTTL_v11.zip | Download Tone and RTTTL player libraries v1.1]]\\\

Deleted lines 91-98:
// no longer have to call wait between notes...
tone.play(NOTE_A4, 500)    // play A for 500ms
tone.play(NOTE_G4, 500)    // play G for 500ms
=]


By default, Play() is non-blocking and will start a tone playing and immediately return while the tone

Deleted lines 227-229:

%lfloat text-align=center % http://www.sfcompiler.co.uk/wiki/uploads/icons/download-48x48.png
[[Attach:rtttl_v10.zip | Download RTTTL library v1.0]]\\\
Changed lines 4-6 from:
[[Attach:tone_v10.zip | Download Tone library v1.0]]\\\

to:
[[Attach:tone_v11.zip | Download Tone and RTTTL player libraries v1.1]]\\\

Added lines 20-23:
!!!V1.1 Update
- adds events that get called when a tone starts and stops playing (see tone_events.bas and RTTTL_events.bas for examples)\\
- consolidate into single download for both libraries
 
Added lines 79-98:

!!! Start and Stop Events

The Tone module defines two events: a Start event that's called when a tone begins playing, and a Stop event that gets called when a tone stops. You can set these to your user-defined event routines using 'set_events()'. Since the events are implemented as part of the tone library, they can also be defined when using the RTTTL player.
=code [=
tone.init()
tone.setwait()          // enable blocking mode

// set tone events
tone.set_events(tone_start_event, tone_stop_event)
=]


// no longer have to call wait between notes...
tone.play(NOTE_A4, 500)    // play A for 500ms
tone.play(NOTE_G4, 500)    // play G for 500ms
=]


By default, Play() is non-blocking and will start a tone playing and immediately return while the tone
Changed line 125 from:
const Imperial_March(20*2) as word =
to:
const Imperial_March() as word =
Changed line 49 from:
tone.play(@song)
to:
tone.play(song)
Added lines 3-6:
%lfloat text-align=center % http://www.sfcompiler.co.uk/wiki/uploads/icons/download-48x48.png
[[Attach:tone_v10.zip | Download Tone library v1.0]]\\\

Deleted line 19:
Changed lines 208-210 from:
=]

\\\
to:
=]\\\

Changed lines 213-214 from:
RTTTL is a text-based method for specifying sequences of notes, first developed by Nokia for cell phone ringtones. You can find many examples of ringtones on the web.
to:
%lfloat text-align=center % http://www.sfcompiler.co.uk/wiki/uploads/icons/download-48x48.png
[[Attach:rtttl_v10.zip | Download RTTTL library v1
.0]]\\\


RTTTL is a text-based method for specifying sequences of notes, first developed by Nokia for cell phone ringtones
. 
Changed line 220 from:
Since it uses the Tone library to generate the notes, using the player is simple... include RTTTL, define your tune, and play it!
to:
Since it uses the Tone library to generate the notes, using the player is simple... include RTTTL, define your tune, and play it! You can find many examples of RTTTL and ringtones on the web.
Changed lines 207-208 from:

to:
\\\
Added line 206:
Changed lines 10-11 from:
Also, unless the device has PPS you are limited by the fixed output pins of the device.
to:
Also, unless the device has PPS you are limited by the fixed output pins of the device available for PWM.
Changed lines 49-50 from:
!!! Blocking vs Non-blocking Operation
to:
!!! Blocking vs Non-blocking Mode
Changed lines 208-368 from:
TO BE CONTINUED...
to:
!!!RingTone Text Transfer Language (RTTTL) Player using Tone library

RTTTL is a text-based method for specifying sequences of notes, first developed by Nokia for cell phone ringtones. You can find many examples of ringtones on the web.

There is a description of the language in the RTTTL.bas module so I won't repeat it here.
Since it uses the Tone library to generate the notes, using the player is simple... include RTTTL, define your tune, and play it!

=code [=
public const HappyBday = "HappyBday:d=4,o=5,b=125:8d.,16d,e,d,g,2f#,8p, 8d.,16d,e,d,a,2g,8p, 8d.,16d,d6,b,g,f#,2e,8p,8c6.,16c6,b,g,a,2g"

rtttl.play(@HappyBday)
=]


!!!RTTTL library example program
=code [=
// RTTTL example (RingTone Text Transfer Language)
// uses tone module for generating notes
program RTTTL_example

device = 18F26K22
clock = 64

// int osc and IO pin libraries
include "intosc.bas"
#option DIGITALIO_INIT = true      // automatically call setalldigital
#option DIGITALIO_SLEWRATE = 1      // 1=slow (we don't need fast edges)
include "setdigitalio.bas"

// tone library
// specify output pin
#option TONE_OUTPUT_PIN = PORTC.2
include "tone.bas"

// RTTTL library (uses tone library)
include "rtttl.bas"

// some example RTTTL tunes
public const HappyBday = "HappyBday:d=4,o=5,b=125:8d.,16d,e,d,g,2f#,8p, 8d.,16d,e,d,a,2g,8p, 8d.,16d,d6,b,g,f#,2e,8p,8c6.,16c6,b,g,a,2g"
public const Simpsons = "The Simpsons:d=4,o=5,b=160:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p,8f#,8f#,8f#,8g,a#.,8c6,8c6,8c6,c6"
public const IndianaJones = "IndianaJones:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6"
public const TakeOnMe = "TakeOnMe:d=4,o=4,b=160:8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5,8f#5,8e5,8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5"
public const Entertainer = "Entertainer:d=4,o=5,b=140:8d,8d#,8e,c6,8e,c6,8e,2c.6,8c6,8d6,8d#6,8e6,8c6,8d6,e6,8b,d6,2c6,p,8d,8d#,8e,c6,8e,c6,8e,2c.6,8p,8a,8g,8f#,8a,8c6,e6,8d6,8c6,8a,2d6"
public const Muppets = "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c"
public const Xfiles = "Xfiles:d=4,o=5,b=125:e,b,a,b,d6,2b.,2p,e,b,a,b,e6,2b.,2p,g6,f#6,e6,d6,e6,2b.,2p,g6,f#6,e6,d6,f#6,2b.,2p,e,b,a,b,d6,2b.,2p,e,b,a,b,e6,2b.,2p,e6,2b."
public const LooneyTunes = "Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f"
public const TwentyCenFox = "20thCenFox:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p,8e,8g#,8b,1c#6,8f#,8a,8c#6,1e6,8a,8c#6,8e6,1e6,8b,8g#,8a,2b"
public const Bond = "Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6"
public const MASH = "MASH:d=8,o=5,b=140:4a,4g,f#,g,p,f#,p,g,p,f#,p,2e.,p,f#,e,4f#,e,f#,p,e,p,4d.,p,f#,4e,d,e,p,d,p,e,p,d,p,2c#.,p,d,c#,4d,c#,d,p,e,p,4f#,p,a,p,4b,a,b,p,a,p,b,p,2a.,4p,a,b,a,4b,a,b,p,2a.,a,4f#,a,b,p,d6,p,4e.6,d6,b,p,a,p,2b"
public const StarWars = "StarWars:d=4,o=5,b=35:32p,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#.6,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#6"
public const ImperialMarch = "ImperialMarch:d=4,o=5,b=100:e,e,e,8c,16p,16g,e,8c,16p,16g,e,p,b,b,b,8c6,16p,16g,d#,8c,16p,16g,e,8p"
public const GoodBadUgly = "GoodBad:d=4,o=5,b=56:32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,d#,8p,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,c#6,8p,32a#,32d#6,32a#,32d#6,8a#.,16f#.,32f.,32d#.,c#,8p,32a#,32d#6,32a#,32d#6,8a#.,16g#.,d#"
public const TopGun = "TopGun:d=4,o=4,b=31:32p,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,16f,d#,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,g#"
public const ATeam = "A-Team:d=8,o=5,b=125:4d#6,a#,2d#6,16p,g#,4a#,4d#.,p,16g,16a#,d#6,a#,f6,2d#6,16p,c#.6,16c6,16a#,g#.,2a#"
public const Flintstones = "Flinstones:d=4,o=5,b=40:32p,16f6,16a#,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,d6,16f6,16a#.,16a#6,32g6,16f6,16a#.,32f6,32f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,a#,16a6,16d.6,16a#6,32a6,32a6,32g6,32f#6,32a6,8g6,16g6,16c.6,32a6,32a6,32g6,32g6,32f6,32e6,32g6,8f6,16f6,16a#.,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#6,16c7,8a#.6"
public const Jeopardy = "Jeopardy:d=4,o=6,b=125:c,f,c,f5,c,f,2c,c,f,c,f,a.,8g,8f,8e,8d,8c#,c,f,c,f5,c,f,2c,f.,8d,c,a#5,a5,g5,f5,p,d#,g#,d#,g#5,d#,g#,2d#,d#,g#,d#,g#,c.7,8a#,8g#,8g,8f,8e,d#,g#,d#,g#5,d#,g#,2d#,g#.,8f,d#,c#,c,p,a#5,p,g#.5,d#,g#"
public const Gadget = "Gadget:d=16,o=5,b=50:32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,32d#,32f,32f#,32g#,a#,d#6,4d6,32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,8d#"
public const Smurfs = "Smurfs:d=32,o=5,b=200:4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8f#,p,8a#,p,4g#,4p,g#,p,a#,p,b,p,c6,p,4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8b,p,8f,p,4f#"
public const MahnaMahna = "MahnaMahna:d=16,o=6,b=125:c#,c.,b5,8a#.5,8f.,4g#,a#,g.,4d#,8p,c#,c.,b5,8a#.5,8f.,g#.,8a#.,4g,8p,c#,c.,b5,8a#.5,8f.,4g#,f,g.,8d#.,f,g.,8d#.,f,8g,8d#.,f,8g,d#,8c,a#5,8d#.,8d#.,4d#,8d#."
public const LeisureSuit = "LeisureSuit:d=16,o=6,b=56:f.5,f#.5,g.5,g#5,32a#5,f5,g#.5,a#.5,32f5,g#5,32a#5,g#5,8c#.,a#5,32c#,a5,a#.5,c#.,32a5,a#5,32c#,d#,8e,c#.,f.,f.,f.,f.,f,32e,d#,8d,a#.5,e,32f,e,32f,c#,d#.,c#"
public const MissionImp = "MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d"
public const Pacman = "Pacman:d=4,o=5,b=112:32b,32p,32b6,32p,32f#6,32p,32d#6,32p,32b6,32f#6,16p,16d#6,16p,32c6,32p,32c7,32p,32g6,32p,32e6,32p,32c7,32g6,16p,16e6,16p,32b,32p,32b6,32p,32f#6,32p,32d#6,32p,32b6,32f#6,16p,16d#6,16p,32d#6,32e6,32f6,32p,32f6,32f#6,32g6,32p,32g6,32g#6,32a6,32p,32b.6"
public const OffToSeeWiz = "OffToSeeWiz:d=32,o=5,b=45:16g#.,16c#6,f#,16f,f#,g#,8c#6,g#,a#,g#,f#,f,f#,d#,8c#.,c#,16c#,c#,16c#6,c#6,c6,c6,c6,16a#,a#,d#6,d#6,d#6,16d#6,c6,8g#"
public const MontyPython = "MontyPython:d=4,o=5,b=200:32p,f6,8e6,d6,8c#6,c6,8b,a#,8a,8g,8a,8a#,a,8g,2c6,8p,8c6,8a,8p,8a,8a,8g#,8a,8f6,8p,8c6,8c6,8p,8a,8a#,8p,8a#,8a#,8p,8c6,2d6,8p,8a#,8g,8p,8g,8g,8f#,8g,8e6,8p,8d6,8d6,8p,8a#,8a,8p,8a,8a,8p,8a#,2c6,8p,8c6"

const SONG_DELAY = 1000    // 1000ms delay between songs

main:
    // init tone library module
    tone.init()

    // play all the example RTTTL tunes
    rtttl.play(@HappyBday)
    delayms(SONG_DELAY)

    rtttl.play(@Simpsons)
    delayms(SONG_DELAY)

    rtttl.play(@IndianaJones)
    delayms(SONG_DELAY)

    rtttl.play(@TakeOnMe)
    delayms(SONG_DELAY)

    rtttl.play(@Entertainer)
    delayms(SONG_DELAY)

    rtttl.play(@Muppets)
    delayms(SONG_DELAY)

    rtttl.play(@Xfiles)
    delayms(SONG_DELAY)

    rtttl.play(@LooneyTunes)
    delayms(SONG_DELAY)

    rtttl.play(@TwentyCenFox)
    delayms(SONG_DELAY)

    rtttl.play(@Bond)
    delayms(SONG_DELAY)

    rtttl.play(@MASH)
    delayms(SONG_DELAY)

    rtttl.play(@StarWars)
    delayms(SONG_DELAY)

    rtttl.play(@ImperialMarch)
    delayms(SONG_DELAY)

    rtttl.play(@GoodBadUgly)
    delayms(SONG_DELAY)

    rtttl.play(@TopGun)
    delayms(SONG_DELAY)

    rtttl.play(@ATeam)
    delayms(SONG_DELAY)

    rtttl.play(@Flintstones)
    delayms(SONG_DELAY)

    rtttl.play(@Jeopardy)
    delayms(SONG_DELAY)

    rtttl.play(@Gadget)
    delayms(SONG_DELAY)

    rtttl.play(@Smurfs)
    delayms(SONG_DELAY)

    rtttl.play(@MahnaMahna)
    delayms(SONG_DELAY)

    rtttl.play(@LeisureSuit)
    delayms(SONG_DELAY)

    rtttl.play(@MissionImp)
    delayms(SONG_DELAY)

    rtttl.play(@Pacman)
    delayms(SONG_DELAY)

    rtttl.play(@OffToSeeWiz)
    delayms(SONG_DELAY)

    rtttl.play(@MontyPython)
    delayms(SONG_DELAY)

    // we're done
    tone.play(NOTE_A4, 2000)
    tone.wait

    while (true)
    end while

end program
=]

Changed line 17 from:
!!!! Basic operation
to:
!!! Basic Operation
Changed line 27 from:
There are several methods available for playing tones, the simplest of which is the 'Play(freq, duration)' routine, where 'freq' is in Hz and 'duration' in msecs. To stop playing a note use Stop() or wait for the duration to expire. If 'duration' is 0 (or is not specified) then the tone plays until Stop() is called.
to:
There are several methods available for playing tones, the simplest of which is the 'Play(freq, duration)' routine, where 'freq' is in Hz and 'duration' in msecs. The tones will automatically stop when the duration expires, or you can use Stop() to terminate it. If 'duration' is 0 (or is not specified) then the tone plays continuously until Stop() is called.
Changed line 37 from:
or just a portion of it. Another method allows you to build an array in ram at runtime and play that. For ease of use, constants are defined for the 88 notes found on a piano (ie NOTE_A4). A special NOTE_PAUSE allows to to put a delay in the sequence.
to:
or just a portion of it. Another method allows you to build an array in ram at runtime and play that. For ease of use, constants are defined for the 88 notes found on standard piano keys (ie NOTE_A4). A special NOTE_PAUSE allows to to insert a delay in the sequence.
Changed lines 49-50 from:
!!!! Blocking vs Non-blocking Operation
to:
!!! Blocking vs Non-blocking Operation
Added lines 73-205:
!!!Tone library example program
=code [=
// tone library example
program tone_example

device = 18F26K22
clock = 64

// int osc and IO pin libraries
include "intosc.bas"
#option DIGITALIO_INIT = true      // automatically call setalldigital
#option DIGITALIO_SLEWRATE = 1      // 1=slow (we don't need fast edges)
include "setdigitalio.bas"

// tone library options
// specify output pin
#option TONE_OUTPUT_PIN = PORTC.2
// wait flag setting (default = false)
'#option TONE_WAIT = true
include "tone.bas"

// star wars imperial march using individual tone play calls
sub Play_Imperial_March()
    tone.setwait()

    tone.play(NOTE_A4, 500) 
    tone.play(NOTE_A4, 500) 
    tone.play(NOTE_A4, 500) 
    tone.play(NOTE_F4, 350) 
    tone.play(NOTE_C5, 150) 
    tone.play(NOTE_A4, 500) 
    tone.play(NOTE_F4, 350) 
    tone.play(NOTE_C5, 150) 
    tone.play(NOTE_A4, 1000) 
    tone.play(NOTE_PAUSE, 250) 

    tone.play(NOTE_E5, 500) 
    tone.play(NOTE_E5, 500) 
    tone.play(NOTE_E5, 500) 
    tone.play(NOTE_F5, 350) 
    tone.play(NOTE_C5, 150) 
    tone.play(NOTE_GS4, 500) 
    tone.play(NOTE_F4, 350) 
    tone.play(NOTE_C5, 150) 
    tone.play(NOTE_A4, 1000) 
    tone.play(NOTE_PAUSE, 250) 
end sub

// const array of (freq, duration) note pairs
const Imperial_March(20*2) as word =
(
    NOTE_A4, 500, 
    NOTE_A4, 500, 
    NOTE_A4, 500, 
    NOTE_F4, 350, 
    NOTE_C5, 150, 
    NOTE_A4, 500, 
    NOTE_F4, 350, 
    NOTE_C5, 150, 
    NOTE_A4, 1000, 
    NOTE_PAUSE, 250, 

    NOTE_E5, 500, 
    NOTE_E5, 500, 
    NOTE_E5, 500, 
    NOTE_F5, 350, 
    NOTE_C5, 150, 
    NOTE_GS4, 500, 
    NOTE_F4, 350, 
    NOTE_C5, 150, 
    NOTE_A4, 1000, 
    NOTE_PAUSE, 250
)

dim ix as byte
dim note as word
dim num_notes as word

// note list in ram (freq, duration pairs)
dim note_list(64*2) as word

main:
    // init tone library module
    tone.init()
   
    // play a note until stop()
    tone.play(NOTE_A4)      // tone A (440) will play forever...
    delayms(1000)          // allow note to play for 1sec
    tone.stop()            // and now stop it

    // tone.play defaults to non-blocking mode...
    // it returns immediately and does not wait
    tone.setwait(false)        // this is the tone library default setting
    tone.play(NOTE_G4, 500)    // play G for 500ms
    tone.wait()                // wait for note to finish

    // set play to blocking mode...
    // it now returns when done and no need to call wait
    // (you can change the tone library default using '#option TONE_WAIT = true')
    tone.setwait()              // set wait_flag true
    tone.play(NOTE_A4, 100)    // play A for 100ms
   
    // play all 88 piano notes defined in the tone module for 250ms each
    for ix = 0 to bound(tone.notes)
        note = tone.notes(ix)      // get note from the list
        tone.play(note, 250)        // play note for 250ms
        delayms(10)                // short delay between notes
    next

    // play a tune using individual calls (long winded approach)
    Play_Imperial_March()

    // array functions
    // play a const array of notes
    tone.play(Imperial_March)

    // play first 9 notes from the const array
    tone.play(Imperial_March, 9)

    // play an array of notes in ram
    clear(note_list)
    note_list = Imperial_March                  // copy the const array to ram
    num_notes = NOTE_COUNT(Imperial_March)      // get number of notes defined in the const array
    tone.play(note_list, num_notes)            // play entire tune

    // play first 9 notes from the array in ram
    tone.play(note_list, 9)
   
    while (true)
    end while

end program
=]
Changed line 18 from:
Using the module is easy. Simply define your output pin using '#option TONE_OUTPUT_PIN', include tone.bas' and call the init() routine to set up the hardware.
to:
Using the module is easy. Simply define your output pin using '#option TONE_OUTPUT_PIN', include tone.bas and call the init() routine to set up the hardware.
Changed line 27 from:
There are several methods available for playing tones, the simplest of which is the 'Play(freq, duration)' routine
to:
There are several methods available for playing tones, the simplest of which is the 'Play(freq, duration)' routine, where 'freq' is in Hz and 'duration' in msecs. To stop playing a note use Stop() or wait for the duration to expire. If 'duration' is 0 (or is not specified) then the tone plays until Stop() is called.
Added lines 30-33:

tone.play(440)          // play note until stop
delayms(1000)          // do other stuff
tone.stop()            // stop playing tone
Changed lines 35-73 from:
 
to:

You can also specify a const array of notes (freq and duration word pairs) and play the entire sequence,
or just a portion of it. Another method allows you to build an array in ram at runtime and play that. For ease of use, constants are defined for the 88 notes found on a piano (ie NOTE_A4). A special NOTE_PAUSE allows to to put a delay in the sequence.
=code [=
const song() as word =      // an array of (freq, duration) note pairs
(
    NOTE_A4, 500, 
    NOTE_PAUSE, 250, 
    NOTE_F4, 350,
    NOTE_CS4, 850
}   
tone.play(@song)
=]

!!!! Blocking vs Non-blocking Operation

By default, Play() is non-blocking and will start a tone playing and immediately return while the tone plays (this replicates the original arduino library). You must use isPlaying() or Wait() to determine when a tone has finished before calling Play() again or the current tone will just stop.

=code [=
tone.play(NOTE_A4, 1000)    // play A (440Hz) for 1000ms
while tone.isPlaying()
  // wait for tone to finish...
end while

// or
tone.play(440, 1000)    // play A (440Hz) for 1000ms
tone.wait()            // wait for tone to finish
=]
 
The module allows you to change the mode to blocking so that Play() will automatically wait for tones to finish before returning. This makes it simpler if you're playing a sequence of tones. Blocking mode is enabled using the SetWait() routine, or can be set by specifying '#option TONE_WAIT = true' before including the module.

=code [=
tone.setwait()          // enable blocking mode
// no longer have to call wait between notes...
tone.play(NOTE_A4, 500)    // play A for 500ms
tone.play(NOTE_G4, 500)    // play G for 500ms
=]

Added lines 1-36:
(:title Tone library and RTTTL (RingTone Text Transfer Language) player:)

!!! Overview

The Tone library is an enhanced version of the arduino library, useful for generating square waves.\\
It uses a TMR + CCP peripheral, along with an interrupt to generate square waves on any user-defined pin.

Many times the PWM peripheral is used to generate waveforms. While this is convenient, PWM on the PIC18 is tied to the clock frequency and for most devices is limited in its low-frequency range. With a limit of a 1:16 clock prescaler, generating a 30Hz audio tone using PWM would limit you to a max cpu clock of < 1MHz!

Also, unless the device has PPS you are limited by the fixed output pins of the device.

This library uses a TMR and CCP peripheral in compare mode to generate the timing, with the output pin controlled by software using the CCP interrupt. This allows for generation of a wide range of tones at almost any clock setting. The code presented here is written for the 18FxxK22 using TMR1 and CCP1 but could easily be adapted for other devices/peripheral combinations.

Enhancements to the original arduino library include both blocking and non-blocking mode operation, and the ability to play an array of notes instead of just a single tone.


!!!! Basic operation
Using the module is easy. Simply define your output pin using '#option TONE_OUTPUT_PIN', include tone.bas' and call the init() routine to set up the hardware.
=code [=
// specify output pin
#option TONE_OUTPUT_PIN = PORTC.2
include "tone.bas"

tone.init()
=]

There are several methods available for playing tones, the simplest of which is the 'Play(freq, duration)' routine
=code [=
tone.play(440, 1000)    // play A (440Hz) for 1000ms
=]
 

TO BE CONTINUED...