Description: A non-blocking Arduino Library to play infinite polyphonic notes with full control over volume, pitch, modulation, and . . . music :D
This library should work for the following boards:
Board | Stable Polyphony | Max Polyphony(Might be unstable) | Pin(CHA) | Pin(CHB) | Stereo Support |
---|---|---|---|---|---|
Arduino Uno, Nano, Pro Mini (boards with atmega328p) | 4 | 8 | 11 | 3 | yes |
Arduino Micro, Pro Micro (boards with atmega32u4) | 4 | 8 | 6 | N/A | no |
Arduino MEGA-2560 | 10 | 16~18 | 10 | 9 | yes |
Teensy 2.0 | 4 | 8 | A9 | N/A | no |
Teensy LC, 3.0+ (the Teensy 3rd generation) | 16 | 30* | 3 | 4 | YES!!! (you can output on more than 2 pins) |
ESP8266 (under construction) | 2 | ~8 | RX line(uses i2s) | N/A | No!!! 😡 |
begin
method. But the pin must be PWM. For example:
begin(6,SINE,ENVELOPE0,0) //output on pin 6
//or
begin(A9,SINE,ENVELOPE0,0) //output on pin A9
...
#elif defined(__arm__) && defined(TEENSYDUINO)
#define maxVOICES 16 //Teensy 3.++; Teensy boards can handle a whooping 30+ simultaneous voices(Teensy is a powerful 32-bit chip)
...
skipTo()
a favorite part in the song(The hardest to program, but it was worth it)overrideSustain
otherwise instrument will choose automatically.
SUSTAIN
REV_SUSTAIN
NONE
In this example, we will use an Arduino Uno.
You may be wondering how one can write songs for the Arduino without any knowledge of Music! Its actually quite interesting and fun. First you need to know the value of the notes in Music.
Triplets= 1/3
Now, remember these values. You will need them in the next step.
Now that you know the values of the basic notes in music, let's make some noise!
The Music file is stored in the PROGMEM
.
const char song[] PROGMEM = " name of song : settings : notes";
d
= The default duration. So any note(letter) without a number in front will automatically be assigned with the default durationd=1
which is a wholenoteo
= The default Octave.o=4
which is middle C on the Pianob
= the BPM(Beat per Minute) or the Tempo of the Songb=100
, which is normal tempo in musics
= The sharpss=aeb
, means all a's, e's, and b's in the song are sharpsf
= The Flatsf=aeb
, means all a's, e's, and b's in the song are flatsa,b,c,d,e,f,g
p
4f
is a quarter note.
for dotted notes
2.f
is a dotted half note_
right after the letter
b_
#
right after the letter
c#
b1
c#1
-
sign and then the number of octaves to be dropped after the Sharp/Flats/Letter
b-1
c#-2
,
to indicate the next note
b,c
means that the notes will be played one after the other(taking a breath)+
sign to indicate the next note is a slur with the current note
b+c
means that the notes will be played like a slur in music(without taking a breath)So using these format rules, we can make this ordered outline for the definition of a Note.
duration (optional)
period (optional)
letter (required)
sharp/flat (optional)
octave (optional)
,
or +
(required)
Let's use some examples to understand the format of the song file.
Assume const char name[] PROGMEM
Question: Lets try to play the letter C
name = "::c";
Question: Play the C major scale with wholenotes
name = "::c,d,e,f,g,a,b,c1";
Question: Play the C major scale with sixteenth notes
name = ":d=16:c,d,e,f,g,a,b,c1";
Question: Play the C Major with different durations
name = "::c,4d,2e,16f,g,8a,32b,c1";
Question: Play the C Major faster
name = ":b=160:c,d,e,f,g,a,b,c1";
Question: Play some dotted quarter notes
name = "::4.c,8.d,2.e";
Question: play a note every second
name = ":d=4,b=60:c,p";
//bpm=60 means a quarter note = 1 secondQuestion: Slur every three notes
name = "::a+b+c,c+d+e";
Question: Combine note with all possible options
name = "::4.a_-1";
Question: Play notes with whitespaces(whitespaces make it easier to write music since it's easier on the eyes)
name = ":: 12c , 12b , 12a , 4a , 4b , 4c , 2g , 2a , d";
//The library is user-friendly, it ignores spaces (but don't put spaces in between the definitions of the notes) So don't do this ":: 12 c # ,"
.You may create a MusicWithoutDelay object two different ways:
MusicWithoutDelay buzzer(const char *song); // where song is a const char song[] PROGMEM
or
MusicWithoutDelay buzzer;
In order to start our speaker, we need to call the begin()
function.
You can call it two ways.
begin(int pin, int waveForm, int envelope, int mod)
Where pin is the pin connected to the speaker. It must be CHA
or CHB
. For example: CHA is pin 11 on UNO, and CHB is pin 3 on UNO.
The waveForm can be SINE
, SQUARE
, TRIANGLE
, SAW
, RAMP
, or NOISE
TRIANGLE
is recommendedThe envelope can be ENVELOPE0
, ENVELOPE1
,ENVELOPE2
, or ENVELOPE3
.
ENVELOPE0
The mod can be any positive or negative number to create fancy musical effects. The further away you are from 0
, the fancier the song will sound.
0
.begin(int waveForm, int envelope, int mod)
Use this format for more than one instrument.
If you don't want to play a song but want to play a simple continuous sine wave, all you have to do is call:
buzzer.setFrequency(440.0); // A4 is 440 hz
However, if you don't know the frequency for a note, just call:
float freq = buzzer.getNoteAsFrequency(int Note);
Where Note
can be referenced by simply typing NOTE_A4
for A4, or NOTE_C4
for middle C.
It's been forever, but I finally mastered the art of Timers, and with it I figured out how to control Volume.
Fortunately, setting the volume is a piece of Cake:
buzzer.setVolume(50);// 0-127
The min volume is 0 and the max is 127.
So, you know how a piano's volume decreases overtime as soon as you play a note? Well, I've applied the same principle to my library. As a bonus, I've also figured out how to make music sound like it's being played backwards!
As you know, your RTTL files contains commas ,
and plus +
signs to seperate notes from each other; my library automatically adds sustain and removes it as your song plays. However, if you want the whole song to sound "backwards", you must Override the instrument!
buzzer.overrideSustain(true);
After you have overridden the instrument, you now have the ability to choose a sustain effect. If you choose to revert back to the original sustain, simply call buzzer.overrideSustain(false);
buzzer.setSustain(NONE); // monotone sound
buzzer.setSustain(SUSTAIN); // Normal sustain
buzzer.setSustain(REV_SUSTAIN); // Backwards effect
For the boards that have stereo support, all you have to do to set the instruments to different pins. For instance, this will allow sound to come at two pins:
buzzer.begin(CHA,SINE,ENVELOPE0,0);
buzzer2.begin(CHB,SINE,ENVELOPE0,0);
You can even combine buzzers on the same output:
buzzer.begin(CHA,SINE,ENVELOPE0,0); //Two Sine waves on both pins
buzzer2.begin(CHA,SINE,ENVELOPE0,0);
buzzer3.begin(CHB,SINE,ENVELOPE0,0);
buzzer4.begin(CHB,SINE,ENVELOPE0,0);
The Teensy(3rd generation) is really powerful; you can output on any PWM pin with barely any limitations.
For instance:
buzzer.begin(CHA,SINE,ENVELOPE0,0); //CHA is also pin 3
buzzer2.begin(CHB,SINE,ENVELOPE0,0); //CHB is pin 4
buzzer3.begin(6,SINE,ENVELOPE0,0); //pin 6
buzzer4.begin(A9,SINE,ENVELOPE0,0); //pin A9
buzzer5.begin(6,SINE,ENVELOPE0,0); //pin 6...again :D
etc...
begin(int pin, int waveForm, int envelope, int mod)//mode can be CHA or CHB
begin(int waveForm, int envelope, int mod)
--------------------------------------------------------
update() //update the instruments
play() // plays the song infinitely
play(int numOfTimes)//repeat a song a certain amount
pause(bool b) //pauses and resumes song
reverse(bool r) //reverse direction of song being
played
overrideSustain(bool v); // must be called in order to use setSustain()
newSong(const char p[] PROGMEM) //used to play a new song
skipTo(long index)//skips song to time suggested in milliseconds
mute(bool m) // mutes instrument
--------------------------------------------------------
setBPM(int tempo) //set speed of song
setOctave(int oct) //set octave
setWave(int waveShape) //set waveShape of instrument
setMod(int percent) //modulate instrument for cool effects
setSustain(int sustain) //change how a note sounds at the front or end(but u must call overrideSustain(true))
setFrequency(float freq);//stops song, and plays a single frequency
--------------------------------------------------------
getTotalTime() //gets totalTime of song in milliseconds
getCurrentTime() //gets currentTime of song in milliseconds
getBPM() //gets tempo of song
getOctave() //gets the song's octave
getName() //get name of song
getNoteAsFrequency(int n);//returns a Note as a frequency
--------------------------------------------------------
isRest() //returns true if song is playing a rest
isMuted() //returns true if muted
isStart() //returns true if song has reached the beginning
isEnd() //returns true if song has reached the end
isPaused() //returns true if song is paused
isNote() //returns true if song is playing a note
isBackwards() //returns true if song is playing backwards
isSustainOverrided() // returns true if the current instrument's sustain is overridden.
isSingleNote(); //returns true if instrument is playing the frequency you set from "setFrequency(float freq)"
//Use these constants for setting wave shapes
SINE
SQUARE
SAW
RAMP
TRIANGLE
NOISE
--------------------------------------------------------
//Use these constants for setting the speaker pin
CHA //pin 11 on Uno
CHB //pin 3 on Uno
--------------------------------------------------------
//Use these constants for setting the type of Envelope
ENVELOPE0
ENVELOPE1
ENVELOPE2
ENVELOPE3
--------------------------------------------------------
//Use these constants for setting the type of sustain for the instrument.
SUSTAIN // Default, like how a piano plays
REV_SUSTAIN // Sounds like music is played backwards
NONE // Boring, and classic Arduino monotone.