Introduction
The following article is about yet another Arduino library that I have written. AnyRtttl is a feature rich arduino library for playing RTTTL melodies. The library offers much more interesting features than relying on the widely available void play_rtttl(char *p)
function. The AnyRtttl is a feature rich library which supports all best RTTTL features.
Skip to the download section for quick download.
Purpose
See this post for a quick recall of the RTTTL format.
Note that AnyRtttl library is a port from my NonBlockingRtttl arduino library which is now deprecated since development has stopped.
After publishing my NonBlockingRtttl arduino library, I started using the library in more complex projects which was requiring other libraries. I quickly ran into the hell of library dependencies and library conflicts. I realized that I needed more features that could help me prototype faster.
Other libraries available which allows you to “play” a melody in RTTTL format suffer the same issue: they are based on blocking APIs or the RTTTL data is not optimized for space.
AnyRtttl is different since it packs multiple RTTTL related features in a single library. It supports blocking & non-blocking API which makes it suitable to be used by more advanced algorithm. For instance, when using the non-blocking API, the melody can be stopped when a button is pressed. The library is also compatible with external Tone libraries and it supports highly compressed RTTTL binary formats.
Library features
Here is a list of all library features:
- Really small increase in memory & code footprint compared to the usual blocking algorithm.
- Blocking & Non-Blocking modes available.
- Support custom
tone()
,noTone()
,delay()
andmillis()
functions. - Compatible with external Tone libraries.
- Supports highly compressed RTTTL binary format.
- Supports RTTTL melodies stored in Program Memory (
PROGMEM
). - Play two monolithic melodies on two different pins using 2 piezo buzzer with the help of an external Tone library.
Non-Blocking
Most of the code that can “play” a melody on internet are all build the same way: sequential calls to tone()
and delay()
functions using hardcoded values. This type of implementation might be good for robots but not for realtime application or projects that needs to monitor pins while the song is playing.
With AnyRtttl non-blocking mode, your program can read/write IOs pins while playing and react on changes. Implementing a “stop” or “next song” push button is a breeze!
External Tone or Timer #0 libraries
The AnyRtttl library is also flexible by allowing you to use the build-in arduino tone()
and noTone()
functions or an implementation from any external library which makes it compatible with any Tone library in the market.
The library also supports custom delay()
and millis()
functions. If a projects requires modification to the microcontroller’s build-in Timer #0, the millis()
function may be impacted and behave incorrectly. To maximize compatibility, one can supply a custom function which behaves like the original to prevent altering playback.
Binary RTTTL
The AnyRtttl library also supports playing RTTTL data which is stored as binary data instead of text. This is actually a custom implementation of the RTTTL format. Using these format, one can achieve storing an highly compressed RTTTL melody which saves memory.
See the Binary RTTTL format definition section for details.
Usage
Define ANY_RTTTL_INFO to enable the debugging of the library state on the serial port.
Use ANY_RTTTL_VERSION to get the current version of the library.
Non-blocking mode
Call anyrtttl::begin()
to setup the AnyRtttl library in non-blocking mode. Then call anyrtttl::play()
to update the library’s state and play notes as required.
Use anyrtttl::done()
or anyrtttl::isPlaying()
to know if the library is done playing the given song.
Anytime one can call anyrtttl::stop()
to stop playing the current song.
Demo
The following demo show how to use the library in non-blocking mode:
(download
AnyRtttl v2.0 Non-Blocking example (987 downloads)
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <anyrtttl.h> #include <binrtttl.h> #include <pitches.h> //project's contants #define BUZZER_PIN 8 const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; const char * arkanoid = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; const char * mario = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; byte songIndex = 0; //which song to play when the previous one finishes void setup() { pinMode(BUZZER_PIN, OUTPUT); Serial.begin(115200); Serial.println(); } void loop() { if ( !anyrtttl::nonblocking::isPlaying() ) { if (songIndex == 0) { anyrtttl::nonblocking::begin(BUZZER_PIN, mario); songIndex++; //ready for next song //play for 5 sec then stop. //note: this is a blocking code section //use to demonstrate the use of stop() unsigned long start = millis(); while( millis() - start < 5000 ) { anyrtttl::nonblocking::play(); } anyrtttl::nonblocking::stop(); } else if (songIndex == 1) { anyrtttl::nonblocking::begin(BUZZER_PIN, arkanoid); songIndex++; //ready for next song } else if (songIndex == 2) { anyrtttl::nonblocking::begin(BUZZER_PIN, tetris); songIndex++; //ready for next song } } else { anyrtttl::nonblocking::play(); } } |
Play 16 bits per note RTTTL
Note that this mode requires that an RTTTL melody be already converted to 16-bits per note binary format.
Use the anyrtttl::blocking::play16Bits()
API for playing an RTTTL melody encoded as 16 bits per note.
Demo
The following demo show how to use the library with 16-bits per note binary RTTTL:
(download
AnyRtttl v2.0 Play 16 bits example (914 downloads)
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <anyrtttl.h> #include <binrtttl.h> #include <pitches.h> //project's contants #define BUZZER_PIN 8 //RTTTL 16 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a //Compatible with AnyRtttl library v2.0 //The code & updates for the AnyRtttl library can be found on http://end2endzone.com const unsigned char tetris16[] = {0x0A, 0x14, 0x12, 0x02, 0x33, 0x01, 0x03, 0x02, 0x0B, 0x02, 0x14, 0x02, 0x0C, 0x02, 0x03, 0x02, 0x33, 0x01, 0x2A, 0x01, 0x2B, 0x01, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x29, 0x01, 0x3B, 0x01, 0x0A, 0x02, 0x1B, 0x02, 0x2A, 0x02, 0x23, 0x02, 0x1B, 0x02, 0x12, 0x02, 0x13, 0x02, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x2A, 0x01}; const int tetris16_length = 42; void setup() { pinMode(BUZZER_PIN, OUTPUT); Serial.begin(115200); Serial.println(); } void loop() { anyrtttl::blocking::play16Bits(BUZZER_PIN, tetris16, tetris16_length); while(true) { } } |
Play 10 bits per note RTTTL
Note that this mode requires that an RTTTL melody be already converted to 10-bits per note binary format.
Create a function that will be used by AnyRtttl library to read bits as required. The signature of the library must look like this:
uint16_t function_name(uint8_t numBits)
.
Note that this demo uses the arduino BitReader library to extract bits from the RTTTL binary buffer. The implementation of readNextBits()
function delegates the job to the BitReader’s read()
method.
In the setup()
function, setup the external library that is used for reading bits: bitreader.setBuffer(tetris10);
Use the anyrtttl::blocking::play10Bits()
API for playing an RTTTL melody encoded as 10 bits per note. The 3rd argument of the function requires a pointer to the function extracting bits: &function_name
.
Demo
The following demo show how to use the library with 10-bits per note binary RTTTL:
(download
AnyRtttl v2.0 Play 10 bits example (882 downloads)
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include <anyrtttl.h> #include <binrtttl.h> #include <pitches.h> #include <bitreader.h> //project's contants #define BUZZER_PIN 8 //RTTTL 10 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a //Compatible with AnyRtttl library v2.0 //The code & updates for the AnyRtttl library can be found on http://end2endzone.com const unsigned char tetris10[] = {0x0A, 0x14, 0x12, 0xCE, 0x34, 0xE0, 0x82, 0x14, 0x32, 0x38, 0xE0, 0x4C, 0x2A, 0xAD, 0x34, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA5, 0xB4, 0x93, 0x82, 0x1B, 0xAA, 0x38, 0xE2, 0x86, 0x12, 0x4E, 0x38, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA9, 0x04}; const int tetris10_length = 42; //bit reader support #ifndef USE_BITADDRESS_READ_WRITE BitReader bitreader; #else BitAddress bitreader; #endif uint16_t readNextBits(uint8_t numBits) { uint16_t bits = 0; bitreader.read(numBits, &bits); return bits; } void setup() { pinMode(BUZZER_PIN, OUTPUT); bitreader.setBuffer(tetris10); Serial.begin(115200); Serial.println(); } void loop() { anyrtttl::blocking::play10Bits(BUZZER_PIN, tetris10_length, &readNextBits); while(true) { } } |
Custom Tone function (a.k.a. RTTTL 2 code)
This demo shows how custom functions can be used by the AnyRtttl library. This example shows how to convert an RTTTL melody to arduino code.
First define replacement functions like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void serialTone(byte pin, uint16_t frequency, uint32_t duration) { Serial.print("tone("); Serial.print(pin); Serial.print(","); Serial.print(frequency); Serial.print(","); Serial.print(duration); Serial.println(");"); } void serialNoTone(byte pin) { Serial.print("noTone("); Serial.print(pin); Serial.println(");"); } void serialDelay(uint32_t duration) { Serial.print("delay("); Serial.print(duration); Serial.println(");"); } |
Each new functions prints the function call & arguments to the serial port.
In the setup()
function, setup the AnyRtttl library to use the new functions:
1 2 3 4 |
//Use custom functions anyrtttl::setToneFunction(&serialTone); anyrtttl::setNoToneFunction(&serialNoTone); anyrtttl::setDelayFunction(&serialDelay); |
Use the anyrtttl::blocking::play()
API for “playing” an RTTTL melody and monitor the output of the serial port to see the actual arduino code generated by the library.
Demo
The following demo show how to use the library with custom functions:
(download
AnyRtttl v2.0 RTTTL 2 code example (875 downloads)
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#include <anyrtttl.h> #include <binrtttl.h> #include <pitches.h> //project's contants #define BUZZER_PIN 8 const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; //******************************************************************************************************************* // The following replacement functions prints the function call & parameters to the serial port. //******************************************************************************************************************* void serialTone(byte pin, uint16_t frequency, uint32_t duration) { Serial.print("tone("); Serial.print(pin); Serial.print(","); Serial.print(frequency); Serial.print(","); Serial.print(duration); Serial.println(");"); } void serialNoTone(byte pin) { Serial.print("noTone("); Serial.print(pin); Serial.println(");"); } void serialDelay(uint32_t duration) { Serial.print("delay("); Serial.print(duration); Serial.println(");"); } void setup() { pinMode(BUZZER_PIN, OUTPUT); Serial.begin(115200); Serial.println(); //Use custom functions anyrtttl::setToneFunction(&serialTone); anyrtttl::setNoToneFunction(&serialNoTone); anyrtttl::setDelayFunction(&serialDelay); } void loop() { anyrtttl::blocking::play(BUZZER_PIN, tetris); while(true) { } } |
Binary RTTTL format definition
The following section defines the field order and size (in bits) required for encoding / decoding of each melody as binary RTTTL.
Note that all fields definition are defined in LSB to MSB order.
The first 16 bits stores the RTTTL default section which is defined as the following:
- Default duration index, 3 bits, with values within [0, 7] range, matches the index used for
getNoteDurationFromIndex()
API. - Default octave index, 2 bits, with values within [0, 3] range, matches the index used for
getNoteOctaveFromIndex()
API. - Beats per minutes (BPM), 10 bits, with values within [1, 900].
- Padding, 1 bit
Next is each note’s of the melody. Each note is encoded as 10 bits (or 16 bits) per note and is defined as the following:
- Duration index, 3 bits, with values within [0, 7] range, matches the index used for
getNoteDurationFromIndex()
API. - Note letter index, 3 bits, with values within [0, 7] range, matches the index used for
getNoteLetterFromIndex()
API. - Pound, 1 bit, defines if the note is pounded or not.
- Dotted, 1 bit, defines if the note is dotted or not.
- Octave index, 2 bits, with values within [0, 3] range, matches the index used for
getNoteOctaveFromIndex()
API. - Padding, 6 bits, optional. See sections below.
The last field of a note (defined as Padding) is an optional 6 bits field. The AnyRtttl library supports both 10 bits per note and 16 bits per note definitions. Use the appropriate API for playing both format.
10 bits per note (no padding)
Each RTTTL note is encoded into 10 bits which is the minimum size of a note. This storage method is the best compression method for storing RTTTL melodies and reduces the usage of the dynamic memory to the minimum.
However, since all notes are not aligned on multiple of 8 bits, addressing each note by an offset is impossible which makes the playback harder. Each notes must be deserialized one after the other. An additional library is required for deserializing a note from a buffer using blocks of 10 bits which may increase the program storage space footprint.
An external arduino library must also be used to allow the AnyRtttl library to consume bits as needed. The arduino BitReader library may be used for handling bit deserialization but any library that can extract a given number of bits from a buffer would work.
16 bits per note (with padding)
Each RTTTL note is encoded into 16 bits which is much better than the average 3.28 bytes per note text format. This storage method is optimum for storing RTTTL melodies and reduces the usage of the dynamic memory without increasing to much program storage space.
All notes are aligned on 16 bits. Addressing each note by an offset allows an easy playback. Only the first 10 bits of each 16 bits block is used. The value of the padding field is undefined.
Playback
The following AnyRtttl blocking APIs are available for playing both format:
- 10 bits per note:
play10Bits()
. - 16 bits per note:
play16Bits()
.
License
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License (LGPL-3.0) for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
DISCLAIMER:
This software is furnished “as is”, without technical support, and with no warranty, express or implied, as to its usefulness for any purpose.
Download
You can download the AnyRtttl library by clicking on the following link:
Download “AnyRtttl v2.1.229 arduino library” AnyRtttl-v2.1.229.zip – Downloaded 834 times – 54 KB