mirror of https://github.com/MLXXXp/Arduboy2.git
Add Beep classes for playing simple tones
Also added a BeepDemo example sketch and modified the ArduBreakout example sketch to use the BeepPin1 class instead of Arduino tone().
This commit is contained in:
parent
460e768ea9
commit
53ea8188d5
|
@ -8,7 +8,7 @@ Software License Agreements
|
||||||
Licensed under the BSD 3-clause license:
|
Licensed under the BSD 3-clause license:
|
||||||
|
|
||||||
Arduboy2 library:
|
Arduboy2 library:
|
||||||
Copyright (c) 2016-2017, Scott Allen
|
Copyright (c) 2016-2018, Scott Allen
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
The Arduboy2 library was forked from the Arduboy library:
|
The Arduboy2 library was forked from the Arduboy library:
|
||||||
|
@ -134,6 +134,12 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
Lesser General Public License for more details.
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Placed in the public domain:
|
||||||
|
|
||||||
|
BeepDemo example sketch:
|
||||||
|
By Scott Allen
|
||||||
|
|
||||||
===============================================================================
|
===============================================================================
|
||||||
\endverbatim
|
\endverbatim
|
||||||
*/
|
*/
|
||||||
|
|
10
README.md
10
README.md
|
@ -129,11 +129,19 @@ Arduboy2 arduboy;
|
||||||
arduboy.audio.off();
|
arduboy.audio.off();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Simple tone generation
|
||||||
|
|
||||||
|
The *BeepPin1* and *BeepPin2* classes are available to generate simple square wave tones using speaker pin 1 and speaker pin 2 respectively. These classes are documented in file *Arduboy2Beep.h*. Also, *BeepDemo* is included as one of the example sketches, which demonstrates basic use.
|
||||||
|
|
||||||
|
NOTE: These functions will not work with a DevKit Arduboy because the speaker pins used cannot be directly controlled by a timer/counter. "Dummy" functions are provided so a sketch will compile and work properly but no sound will be produced.
|
||||||
|
|
||||||
### Ways to make more code space available to sketches
|
### Ways to make more code space available to sketches
|
||||||
|
|
||||||
#### Sound effects and music
|
#### Sound effects and music
|
||||||
|
|
||||||
If you want your sketch to have sound, then using the *ArduboyTones* library will be more code efficient than using *ArduboyPlaytune* or most other sound libraries compatible with the Arduboy. *ArduboyTones* even produces less code than the [Arduino built in *tone()* function](https://www.arduino.cc/en/Reference/Tone). You'll have to decide on the appropriate library or functions you use to generate sound, based on the features required and how much memory you want it to use.
|
If all you want is to play single tones, using the built in *BeepPin1* or *BeepPin2* classes will be very efficient.
|
||||||
|
|
||||||
|
If you want to be able to play sequences of tones or background music, using the [*ArduboyTones*](https://github.com/MLXXXp/ArduboyTones) library will be more code efficient than using [*ArduboyPlaytune*](https://github.com/Arduboy/ArduboyPlayTune) or most other sound libraries compatible with the Arduboy. *ArduboyTones* even produces less code than the [Arduino built in *tone()* function](https://www.arduino.cc/en/Reference/Tone). You'll have to decide on the appropriate library or functions you use to generate sound, based on the features required and how much memory you want it to use.
|
||||||
|
|
||||||
#### Remove the text functions
|
#### Remove the text functions
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
#define EE_FILE 2
|
#define EE_FILE 2
|
||||||
|
|
||||||
Arduboy2 arduboy;
|
Arduboy2 arduboy;
|
||||||
|
BeepPin1 beep;
|
||||||
|
|
||||||
|
const unsigned int FRAME_RATE = 40; // Frame rate in frames per second
|
||||||
const unsigned int COLUMNS = 13; //Columns of bricks
|
const unsigned int COLUMNS = 13; //Columns of bricks
|
||||||
const unsigned int ROWS = 4; //Rows of bricks
|
const unsigned int ROWS = 4; //Rows of bricks
|
||||||
int dx = -1; //Initial movement of ball
|
int dx = -1; //Initial movement of ball
|
||||||
|
@ -57,7 +59,8 @@ byte tick;
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
arduboy.begin();
|
arduboy.begin();
|
||||||
arduboy.setFrameRate(40);
|
beep.begin();
|
||||||
|
arduboy.setFrameRate(FRAME_RATE);
|
||||||
arduboy.initRandomSeed();
|
arduboy.initRandomSeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +70,9 @@ void loop()
|
||||||
if (!(arduboy.nextFrame()))
|
if (!(arduboy.nextFrame()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Handle the timing and stopping of tones
|
||||||
|
beep.timer();
|
||||||
|
|
||||||
//Title screen loop switches from title screen
|
//Title screen loop switches from title screen
|
||||||
//and high scores until FIRE is pressed
|
//and high scores until FIRE is pressed
|
||||||
while (!start)
|
while (!start)
|
||||||
|
@ -190,7 +196,7 @@ void moveBall()
|
||||||
yb=60;
|
yb=60;
|
||||||
released = false;
|
released = false;
|
||||||
lives--;
|
lives--;
|
||||||
playTone(175, 250);
|
playToneTimed(175, 500);
|
||||||
if (random(0, 2) == 0)
|
if (random(0, 2) == 0)
|
||||||
{
|
{
|
||||||
dx = 1;
|
dx = 1;
|
||||||
|
@ -347,7 +353,7 @@ void drawGameOver()
|
||||||
arduboy.print("Score: ");
|
arduboy.print("Score: ");
|
||||||
arduboy.print(score);
|
arduboy.print(score);
|
||||||
arduboy.display();
|
arduboy.display();
|
||||||
delay(4000);
|
arduboy.delayShort(4000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pause()
|
void pause()
|
||||||
|
@ -359,7 +365,7 @@ void pause()
|
||||||
arduboy.display();
|
arduboy.display();
|
||||||
while (paused)
|
while (paused)
|
||||||
{
|
{
|
||||||
delay(150);
|
arduboy.delayShort(150);
|
||||||
//Unpause if FIRE is pressed
|
//Unpause if FIRE is pressed
|
||||||
pad2 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
|
pad2 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
|
||||||
if (pad2 == true && oldpad2 == false && released)
|
if (pad2 == true && oldpad2 == false && released)
|
||||||
|
@ -410,7 +416,7 @@ boolean pollFireButton(int n)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < n; i++)
|
for(int i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
delay(15);
|
arduboy.delayShort(15);
|
||||||
pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
|
pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
|
||||||
if(pad == true && oldpad == false)
|
if(pad == true && oldpad == false)
|
||||||
{
|
{
|
||||||
|
@ -548,14 +554,14 @@ void enterInitials()
|
||||||
}
|
}
|
||||||
arduboy.drawLine(56, 28, 88, 28, 0);
|
arduboy.drawLine(56, 28, 88, 28, 0);
|
||||||
arduboy.drawLine(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
|
arduboy.drawLine(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
|
||||||
delay(150);
|
arduboy.delayShort(70);
|
||||||
|
|
||||||
if (arduboy.pressed(LEFT_BUTTON) || arduboy.pressed(B_BUTTON))
|
if (arduboy.pressed(LEFT_BUTTON) || arduboy.pressed(B_BUTTON))
|
||||||
{
|
{
|
||||||
if (index > 0)
|
if (index > 0)
|
||||||
{
|
{
|
||||||
index--;
|
index--;
|
||||||
playTone(1046, 250);
|
playToneTimed(1046, 80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,14 +570,14 @@ void enterInitials()
|
||||||
if (index < 2)
|
if (index < 2)
|
||||||
{
|
{
|
||||||
index++;
|
index++;
|
||||||
playTone(1046, 250);
|
playToneTimed(1046, 80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arduboy.pressed(DOWN_BUTTON))
|
if (arduboy.pressed(UP_BUTTON))
|
||||||
{
|
{
|
||||||
initials[index]++;
|
initials[index]++;
|
||||||
playTone(523, 250);
|
playToneTimed(523, 80);
|
||||||
// A-Z 0-9 :-? !-/ ' '
|
// A-Z 0-9 :-? !-/ ' '
|
||||||
if (initials[index] == '0')
|
if (initials[index] == '0')
|
||||||
{
|
{
|
||||||
|
@ -591,10 +597,10 @@ void enterInitials()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arduboy.pressed(UP_BUTTON))
|
if (arduboy.pressed(DOWN_BUTTON))
|
||||||
{
|
{
|
||||||
initials[index]--;
|
initials[index]--;
|
||||||
playTone(523, 250);
|
playToneTimed(523, 80);
|
||||||
if (initials[index] == ' ') {
|
if (initials[index] == ' ') {
|
||||||
initials[index] = '?';
|
initials[index] = '?';
|
||||||
}
|
}
|
||||||
|
@ -611,12 +617,11 @@ void enterInitials()
|
||||||
|
|
||||||
if (arduboy.pressed(A_BUTTON))
|
if (arduboy.pressed(A_BUTTON))
|
||||||
{
|
{
|
||||||
|
playToneTimed(1046, 80);
|
||||||
if (index < 2)
|
if (index < 2)
|
||||||
{
|
{
|
||||||
index++;
|
index++;
|
||||||
playTone(1046, 250);
|
|
||||||
} else {
|
} else {
|
||||||
playTone(1046, 250);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -693,12 +698,19 @@ void enterHighScore(byte file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the Arduino tone() function so that the pin doesn't have to be
|
// Play a tone at the specified frequency for the specified duration.
|
||||||
// specified each time. Also, don't play if audio is set to off.
|
void playTone(unsigned int frequency, unsigned int duration)
|
||||||
void playTone(unsigned int frequency, unsigned long duration)
|
|
||||||
{
|
{
|
||||||
if (arduboy.audio.enabled() == true)
|
beep.tone(beep.freq(frequency), duration / (1000 / FRAME_RATE));
|
||||||
{
|
|
||||||
tone(PIN_SPEAKER_1, frequency, duration);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Play a tone at the specified frequency for the specified duration using
|
||||||
|
// a delay to time the tone.
|
||||||
|
// Used when beep.timer() isn't being called.
|
||||||
|
void playToneTimed(unsigned int frequency, unsigned int duration)
|
||||||
|
{
|
||||||
|
beep.tone(beep.freq(frequency));
|
||||||
|
arduboy.delayShort(duration);
|
||||||
|
beep.noTone();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
This sketch provides an example of using the Arduboy2 library's BeepPin1 class
|
||||||
|
to play simple tones.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
To the extent possible under law, Scott Allen has waived all copyright and
|
||||||
|
related or neighboring rights to this BeepDemo program.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Comments are only provided for code dealing with tone generation or control.
|
||||||
|
|
||||||
|
#include <Arduboy2.h>
|
||||||
|
// There's no need to #include <Arduboy2Beep.h>
|
||||||
|
// It will be included in Arduboy2.h
|
||||||
|
|
||||||
|
Arduboy2 arduboy;
|
||||||
|
|
||||||
|
BeepPin1 beep; // Create a class instance for speaker pin 1
|
||||||
|
//BeepPin2 beep; // For speaker pin 2, use this line instead of the line above
|
||||||
|
|
||||||
|
int objectX = 0;
|
||||||
|
|
||||||
|
char displayText[60];
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
arduboy.begin();
|
||||||
|
arduboy.setFrameRate(25);
|
||||||
|
|
||||||
|
beep.begin(); // Set up the hardware for playing tones
|
||||||
|
|
||||||
|
commandText("none - Press buttons");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (!arduboy.nextFrame()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The timer() function is called once per frame, so duration values will be
|
||||||
|
// the number of frames that the tone plays for.
|
||||||
|
// At 25 frames per second each frame will be 40ms.
|
||||||
|
beep.timer(); // handle tone duration
|
||||||
|
|
||||||
|
arduboy.pollButtons();
|
||||||
|
|
||||||
|
if (arduboy.justPressed(LEFT_BUTTON)) {
|
||||||
|
// Play a 523.251Hz tone (piano note C5) for 5 frames (200ms at 25 FPS)
|
||||||
|
// beep.freq(523.251) is used to convert 523.251Hz to the required count
|
||||||
|
beep.tone(beep.freq(523.251), 5);
|
||||||
|
|
||||||
|
commandText("beep.tone(\n beep.freq(523.251),\n 5)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arduboy.justPressed(UP_BUTTON)) {
|
||||||
|
// Play a 587.330Hz tone (piano note D5) for 15 frames (600ms at 25 FPS)
|
||||||
|
beep.tone(beep.freq(587.330), 15);
|
||||||
|
|
||||||
|
commandText("beep.tone(\n beep.freq(587.330),\n 15)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arduboy.justPressed(RIGHT_BUTTON)) {
|
||||||
|
// Play a 659.255Hz tone (piano note E5) for 50 frames (2s at 25 FPS)
|
||||||
|
beep.tone(beep.freq(659.255), 50);
|
||||||
|
|
||||||
|
commandText("beep.tone(\n beep.freq(659.255),\n 50)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arduboy.justPressed(DOWN_BUTTON)) {
|
||||||
|
// Play a 698.456Hz tone (piano note F5) until stopped
|
||||||
|
// or replaced by another tone
|
||||||
|
beep.tone(beep.freq(698.456));
|
||||||
|
|
||||||
|
commandText("beep.tone(\n beep.freq(698.456))");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arduboy.justPressed(A_BUTTON)) {
|
||||||
|
// For short tones with a duration less than a frame time,
|
||||||
|
// or when timer() isn't being used, such as in a menu,
|
||||||
|
// a continuous tone can be played and then stopped after a delay
|
||||||
|
// but note that no other work will be done during the delay.
|
||||||
|
beep.tone(beep.freq(1000)); // Play a 1000Hz tone until stopped
|
||||||
|
arduboy.delayShort(30); // Delay for 30ms
|
||||||
|
beep.noTone(); // Stop the tone
|
||||||
|
|
||||||
|
commandText("beep.tone(\n beep.freq(1000))\n(delay 30ms)\nbeep.noTone()");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arduboy.justPressed(B_BUTTON)) {
|
||||||
|
beep.noTone(); // Stop the tone if one is playing
|
||||||
|
|
||||||
|
commandText("beep.noTone()");
|
||||||
|
}
|
||||||
|
|
||||||
|
arduboy.println(F("Last command:"));
|
||||||
|
arduboy.print(displayText);
|
||||||
|
|
||||||
|
// The Arduboy2 class's audio subclass controls sound muting.
|
||||||
|
// For this sketch, mute or unmute can be set using the "System Control"
|
||||||
|
// start up feature. (Hold B while powering up then, while still holding B,
|
||||||
|
// press UP to enable sound or DOWN for mute.)
|
||||||
|
if (!arduboy.audio.enabled()) {
|
||||||
|
arduboy.setCursor(22, 40);
|
||||||
|
arduboy.print(F("Sound is MUTED"));
|
||||||
|
}
|
||||||
|
|
||||||
|
arduboy.setCursor(0, 48);
|
||||||
|
// The "duration" variable can be tested for non-zero to determine if a
|
||||||
|
// timed tone is currently playing.
|
||||||
|
if (beep.duration != 0) {
|
||||||
|
arduboy.print(F("A tone is playing"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
arduboy.print(F("Continuous tone or\nno tone playing"));
|
||||||
|
}
|
||||||
|
|
||||||
|
arduboy.drawRect(objectX, 40, 6, 6);
|
||||||
|
if (++objectX == WIDTH - 6) {
|
||||||
|
objectX = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
arduboy.display(CLEAR_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void commandText(const char* text) {
|
||||||
|
strncpy(displayText, text, sizeof displayText);
|
||||||
|
displayText[sizeof displayText - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
Arduboy2 KEYWORD1
|
Arduboy2 KEYWORD1
|
||||||
Arduboy2Base KEYWORD1
|
Arduboy2Base KEYWORD1
|
||||||
|
BeepPin1 KEYWORD1
|
||||||
|
BeepPin2 KEYWORD1
|
||||||
Sprites KEYWORD1
|
Sprites KEYWORD1
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
|
@ -98,6 +100,12 @@ writeShowUnitNameFlag KEYWORD2
|
||||||
writeUnitID KEYWORD2
|
writeUnitID KEYWORD2
|
||||||
writeUnitName KEYWORD2
|
writeUnitName KEYWORD2
|
||||||
|
|
||||||
|
# Arduboy2Beep classes
|
||||||
|
freq KEYWORD2
|
||||||
|
noTone KEYWORD2
|
||||||
|
timer KEYWORD2
|
||||||
|
tone KEYWORD2
|
||||||
|
|
||||||
# Sprites class
|
# Sprites class
|
||||||
drawErase KEYWORD2
|
drawErase KEYWORD2
|
||||||
drawExternalMask KEYWORD2
|
drawExternalMask KEYWORD2
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
#include "Arduboy2Core.h"
|
#include "Arduboy2Core.h"
|
||||||
|
#include "Arduboy2Beep.h"
|
||||||
#include "Sprites.h"
|
#include "Sprites.h"
|
||||||
#include <Print.h>
|
#include <Print.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
/**
|
||||||
|
* @file Arduboy2Beep.cpp
|
||||||
|
* \brief
|
||||||
|
* Classes to generate simple square wave tones on the Arduboy speaker pins.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Arduboy2Beep.h"
|
||||||
|
|
||||||
|
#ifndef AB_DEVKIT
|
||||||
|
|
||||||
|
// Speaker pin 1, Timer 3A, Port C bit 6, Arduino pin 5
|
||||||
|
|
||||||
|
uint8_t BeepPin1::duration = 0;
|
||||||
|
|
||||||
|
void BeepPin1::begin()
|
||||||
|
{
|
||||||
|
TCCR3A = 0;
|
||||||
|
TCCR3B = (bit(WGM32) | bit(CS31)); // CTC mode. Divide by 8 clock prescale
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin1::tone(uint16_t count)
|
||||||
|
{
|
||||||
|
tone(count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin1::tone(uint16_t count, uint8_t dur)
|
||||||
|
{
|
||||||
|
duration = dur;
|
||||||
|
TCCR3A = bit(COM3A0); // set toggle on compare mode (which connects the pin)
|
||||||
|
OCR3A = count; // load the count (16 bits), which determines the frequency
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin1::timer()
|
||||||
|
{
|
||||||
|
if (duration && (--duration == 0)) {
|
||||||
|
TCCR3A = 0; // set normal mode (which disconnects the pin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin1::noTone()
|
||||||
|
{
|
||||||
|
duration = 0;
|
||||||
|
TCCR3A = 0; // set normal mode (which disconnects the pin)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Speaker pin 2, Timer 4A, Port C bit 7, Arduino pin 13
|
||||||
|
|
||||||
|
uint8_t BeepPin2::duration = 0;
|
||||||
|
|
||||||
|
void BeepPin2::begin()
|
||||||
|
{
|
||||||
|
TCCR4A = 0; // normal mode. Disable PWM
|
||||||
|
TCCR4B = bit(CS43); // divide by 128 clock prescale
|
||||||
|
TCCR4D = 0; // normal mode
|
||||||
|
TC4H = 0; // toggle pin at count = 0
|
||||||
|
OCR4A = 0; // "
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin2::tone(uint16_t count)
|
||||||
|
{
|
||||||
|
tone(count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin2::tone(uint16_t count, uint8_t dur)
|
||||||
|
{
|
||||||
|
duration = dur;
|
||||||
|
TCCR4A = bit(COM4A0); // set toggle on compare mode (which connects the pin)
|
||||||
|
TC4H = highByte(count); // load the count (10 bits),
|
||||||
|
OCR4C = lowByte(count); // which determines the frequency
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin2::timer()
|
||||||
|
{
|
||||||
|
if (duration && (--duration == 0)) {
|
||||||
|
TCCR4A = 0; // set normal mode (which disconnects the pin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin2::noTone()
|
||||||
|
{
|
||||||
|
duration = 0;
|
||||||
|
TCCR4A = 0; // set normal mode (which disconnects the pin)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else /* AB_DEVKIT */
|
||||||
|
|
||||||
|
// *** The pins used for the speaker on the DevKit cannot be directly
|
||||||
|
// controlled by a timer/counter. The following "dummy" functions will
|
||||||
|
// compile and operate properly but no sound will be produced
|
||||||
|
|
||||||
|
uint8_t BeepPin1::duration = 0;
|
||||||
|
|
||||||
|
void BeepPin1::begin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin1::tone(uint16_t count)
|
||||||
|
{
|
||||||
|
tone(count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin1::tone(uint16_t count, uint8_t dur)
|
||||||
|
{
|
||||||
|
(void) count; // parameter not used
|
||||||
|
|
||||||
|
duration = dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin1::timer()
|
||||||
|
{
|
||||||
|
if (duration) {
|
||||||
|
--duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin1::noTone()
|
||||||
|
{
|
||||||
|
duration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t BeepPin2::duration = 0;
|
||||||
|
|
||||||
|
void BeepPin2::begin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin2::tone(uint16_t count)
|
||||||
|
{
|
||||||
|
tone(count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin2::tone(uint16_t count, uint8_t dur)
|
||||||
|
{
|
||||||
|
(void) count; // parameter not used
|
||||||
|
|
||||||
|
duration = dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin2::timer()
|
||||||
|
{
|
||||||
|
if (duration) {
|
||||||
|
--duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeepPin2::noTone()
|
||||||
|
{
|
||||||
|
duration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,362 @@
|
||||||
|
/**
|
||||||
|
* @file Arduboy2Beep.h
|
||||||
|
* \brief
|
||||||
|
* Classes to generate simple square wave tones on the Arduboy speaker pins.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARDUBOY2_BEEP_H
|
||||||
|
#define ARDUBOY2_BEEP_H
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Play simple square wave tones using speaker pin 1.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* Class `BeepPin2` provides identical functions for playing tones on speaker
|
||||||
|
* pin 2. Both classes can be used in the same sketch to allow playing
|
||||||
|
* two tones at the same time. To do this, the `begin()` and `timer()`
|
||||||
|
* functions of both classes must be used.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This class can be used to play square wave tones on speaker pin 1.
|
||||||
|
* The functions are designed to produce very small and efficient code.
|
||||||
|
*
|
||||||
|
* A tone can be set to play for a given duration, or continuously until
|
||||||
|
* stopped or replaced by a new tone. No interrupts are used. A tone is
|
||||||
|
* generated by a hardware timer/counter directly toggling the pin,
|
||||||
|
* so once started, no CPU cycles are used to actually play the tone.
|
||||||
|
* The program continues to run while a tone is playing. However, a small
|
||||||
|
* amount of code is required to time and stop a tone after a given duration.
|
||||||
|
*
|
||||||
|
* Tone frequencies can range from 15.26Hz to 1000000Hz.
|
||||||
|
*
|
||||||
|
* Although there's no specific code to handle mute control, the
|
||||||
|
* `Arduboy2Audio` class will work since it has code to mute sound by setting
|
||||||
|
* the speaker pins to input mode and unmute by setting the pins as outputs.
|
||||||
|
* The `BeepPin1` class doesn't interfere with this operation.
|
||||||
|
*
|
||||||
|
* In order to avoid needing to use interrupts, the duration of tones is timed
|
||||||
|
* by calling the `timer()` function continuously at a fixed interval.
|
||||||
|
* The duration of a tone is given by specifying the number of times `timer()`
|
||||||
|
* will be called before stopping the tone.
|
||||||
|
*
|
||||||
|
* For sketches that use `Arduboy2::nextFrame()`, or some other method to
|
||||||
|
* generate frames at a fixed rate, `timer()` can be called once per frame.
|
||||||
|
* Tone durations will then be given as the number of frames to play the tone
|
||||||
|
* for. For example, with a rate of 60 frames per second a duration of 30
|
||||||
|
* would be used to play a tone for half a second.
|
||||||
|
*
|
||||||
|
* The variable named `#duration` is the counter that times the duration of a
|
||||||
|
* tone. A sketch can determine if a tone is currently playing by testing if
|
||||||
|
* the `#duration` variable is non-zero (assuming it's a timed tone, not a
|
||||||
|
* continuous tone).
|
||||||
|
*
|
||||||
|
* To keep the code small and efficient, the frequency of a tone is specified
|
||||||
|
* by the actual count value to be loaded into to timer/counter peripheral.
|
||||||
|
* The frequency will be determined by the count provided and the clock rate
|
||||||
|
* of the timer/counter. In order to allow a tone's frequency to be specified
|
||||||
|
* in hertz (cycles per second) the `freq()` helper function is provided,
|
||||||
|
* which converts a given frequency to the required count value.
|
||||||
|
*
|
||||||
|
* NOTE that it is intended that `freq()` only be called with constant values.
|
||||||
|
* If `freq()` is called with a variable, code to perform floating point math
|
||||||
|
* will be included in the sketch, which will likely greatly increase the
|
||||||
|
* sketch's code size unless the sketch also uses floating point math for
|
||||||
|
* other purposes.
|
||||||
|
*
|
||||||
|
* The formulas for frequency/count conversion are:
|
||||||
|
*
|
||||||
|
* count=(1000000/frequency)-1
|
||||||
|
* frequency=1000000/(count+1)
|
||||||
|
*
|
||||||
|
* Counts must be between 0 and 65535.
|
||||||
|
*
|
||||||
|
* All members of the class are static, so it's not necessary to create an
|
||||||
|
* instance of the class in order to use it. However, creating an instance
|
||||||
|
* doesn't produce any more code and it may make the source code smaller and
|
||||||
|
* make it easier to switch to the `BeepPin2` class if it becomes necessary.
|
||||||
|
*
|
||||||
|
* The following is a basic example sketch, which will generate a tone when
|
||||||
|
* a button is pressed.
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* #include <Arduboy2.h>
|
||||||
|
* // There's no need to #include <Arduboy2Beep.h>
|
||||||
|
* // It will be included in Arduboy2.h
|
||||||
|
*
|
||||||
|
* Arduboy2 arduboy;
|
||||||
|
* BeepPin1 beep; // class instance for speaker pin 1
|
||||||
|
*
|
||||||
|
* void setup() {
|
||||||
|
* arduboy.begin();
|
||||||
|
* arduboy.setFrameRate(50);
|
||||||
|
* beep.begin(); // set up the hardware for playing tones
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void loop() {
|
||||||
|
* if (!arduboy.nextFrame()) {
|
||||||
|
* return;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* beep.timer(); // handle tone duration
|
||||||
|
*
|
||||||
|
* arduboy.pollButtons();
|
||||||
|
*
|
||||||
|
* if (arduboy.justPressed(A_BUTTON)) {
|
||||||
|
* // play a 1000Hz tone for 100 frames (2 seconds at 50 FPS)
|
||||||
|
* // beep.freq(1000) is used to convert 1000Hz to the required count
|
||||||
|
* beep.tone(beep.freq(1000), 100);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* These functions, and the equivalents in class `BeepPin2`, will not work with
|
||||||
|
* a DevKit Arduboy because the speaker pins used cannot be directly controlled
|
||||||
|
* by a timer/counter. "Dummy" functions are provided so a sketch will compile
|
||||||
|
* and work properly but no sound will be produced.
|
||||||
|
*
|
||||||
|
* \see BeepPin2
|
||||||
|
*/
|
||||||
|
class BeepPin1
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* The counter used by the `timer()` function to time the duration of a tone.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This variable is set by the `dur` parameter of the `tone()` function.
|
||||||
|
* It is then decremented each time the `timer()` function is called, if its
|
||||||
|
* value isn't 0. When `timer()` decrements it to 0, a tone that is playing
|
||||||
|
* will be stopped.
|
||||||
|
*
|
||||||
|
* A sketch can determine if a tone is currently playing by testing if
|
||||||
|
* this variable is non-zero (assuming it's a timed tone, not a continuous
|
||||||
|
* tone).
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* \code
|
||||||
|
* beep.tone(beep.freq(1000), 15);
|
||||||
|
* while (beep.duration != 0) { } // wait for the tone to stop playing
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* It can also be manipulated directly by the sketch, although this should
|
||||||
|
* seldom be necessary.
|
||||||
|
*/
|
||||||
|
static uint8_t duration;
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Set up the hardware.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Prepare the hardware for playing tones.
|
||||||
|
* This function must be called (usually in `setup()`) before using any of
|
||||||
|
* the other functions in this class.
|
||||||
|
*/
|
||||||
|
static void begin();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Play a tone continually, until replaced by a new tone or stopped.
|
||||||
|
*
|
||||||
|
* \param count The count to be loaded into the timer/counter to play
|
||||||
|
* the desired frequency.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* A tone is played indefinitely, until replaced by another tone or stopped
|
||||||
|
* using `noTone()`.
|
||||||
|
*
|
||||||
|
* The tone's frequency is determined by the specified count, which is loaded
|
||||||
|
* into the timer/counter that generates the tone. A desired frequency can be
|
||||||
|
* converted into the required count value using the `freq()` function.
|
||||||
|
*
|
||||||
|
* \see freq() timer() noTone()
|
||||||
|
*/
|
||||||
|
static void tone(uint16_t count);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Play a tone for a given duration.
|
||||||
|
*
|
||||||
|
* \param count The count to be loaded into the timer/counter to play
|
||||||
|
* the desired frequency.
|
||||||
|
* \param dur The duration of the tone, used by `timer()`.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* A tone is played for the specified duration, or until replaced by another
|
||||||
|
* tone or stopped using `noTone()`.
|
||||||
|
*
|
||||||
|
* The tone's frequency is determined by the specified count, which is loaded
|
||||||
|
* into the timer/counter that generates the tone. A desired frequency can be
|
||||||
|
* converted into the required count value using the `freq()` function.
|
||||||
|
*
|
||||||
|
* The duration value is the number of times the `timer()` function must be
|
||||||
|
* called before the tone is stopped.
|
||||||
|
*
|
||||||
|
* \see freq() timer() noTone()
|
||||||
|
*/
|
||||||
|
static void tone(uint16_t count, uint8_t dur);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Handle the duration that a tone plays for.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This function must be called at a constant interval, which would normally
|
||||||
|
* be once per frame, in order to stop a tone after the desired tone duration
|
||||||
|
* has elapsed.
|
||||||
|
*
|
||||||
|
* If the value of the `duration` variable is not 0, it will be decremented.
|
||||||
|
* When the `duration` variable is decremented to 0, a playing tone will be
|
||||||
|
* stopped.
|
||||||
|
*/
|
||||||
|
static void timer();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Stop a tone that is playing.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* If a tone is playing it will be stopped. It's safe to call this function
|
||||||
|
* even if a tone isn't currently playing.
|
||||||
|
*
|
||||||
|
* \see tone()
|
||||||
|
*/
|
||||||
|
static void noTone();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Convert a frequency to the required count.
|
||||||
|
*
|
||||||
|
* \param hz The frequency, in hertz (cycles per second), to be converted
|
||||||
|
* to a count.
|
||||||
|
*
|
||||||
|
* \return The required count to be loaded into the timer/counter for the
|
||||||
|
* given frequency.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This helper function will convert a desired tone frequency to the closest
|
||||||
|
* value required by the `tone()` function's `count` parameter.
|
||||||
|
* The calculated count is rounded up or down to the nearest integer,
|
||||||
|
* if necessary.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* \code
|
||||||
|
* beep.tone(beep.freq(440)); // play a 440Hz tone until stopped or replaced
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* It is intended that `freq()` only be called with constant values.
|
||||||
|
* If `freq()` is called with a variable, code to perform floating point math
|
||||||
|
* will be included in the sketch, which will likely greatly increase the
|
||||||
|
* sketch's code size unless the sketch also uses floating point math for
|
||||||
|
* other purposes.
|
||||||
|
*/
|
||||||
|
static constexpr uint16_t freq(const float hz)
|
||||||
|
{
|
||||||
|
return (uint16_t) (((F_CPU / 8 / 2) + (hz / 2)) / hz) - 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Play simple square wave tones using speaker pin 2.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This class contains the same functions as class `BeepPin1` except they use
|
||||||
|
* speaker pin 2 instead of speaker pin 1.
|
||||||
|
*
|
||||||
|
* Using `BeepPin1` is more desirable, as it uses a 16 bit Timer, which can
|
||||||
|
* produce a greater frequency range and resolution than the 10 bit Timer
|
||||||
|
* used by `BeepPin2`. However, if the sketch also includes other sound
|
||||||
|
* generating code that uses speaker pin 1, `BeepPin2` can be used to avoid
|
||||||
|
* conflict.
|
||||||
|
*
|
||||||
|
* Tone frequencies on speaker pin 2 can range from 61.04Hz to 15625Hz using
|
||||||
|
* allowed counts from 3 to 1023.
|
||||||
|
*
|
||||||
|
* The formulas for frequency/count conversion are:
|
||||||
|
*
|
||||||
|
* count=(62500/frequency)-1
|
||||||
|
* frequency=62500/(count+1)
|
||||||
|
*
|
||||||
|
* See the documentation for `BeepPin1` for more details.
|
||||||
|
*
|
||||||
|
* \see BeepPin1
|
||||||
|
*/
|
||||||
|
class BeepPin2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* The counter used by the `timer()` function to time the duration of a tone
|
||||||
|
* played on speaker pin 2.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* For details see `BeepPin1::duration`.
|
||||||
|
*/
|
||||||
|
static uint8_t duration;
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Set up the hardware for playing tones using speaker pin 2.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* For details see `BeepPin1::begin()`.
|
||||||
|
*/
|
||||||
|
static void begin();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Play a tone on speaker pin 2 continually, until replaced by a new tone
|
||||||
|
* or stopped.
|
||||||
|
*
|
||||||
|
* \param count The count to be loaded into the timer/counter to play
|
||||||
|
* the desired frequency.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* For details see `BeepPin1::tone(uint16_t)`.
|
||||||
|
*/
|
||||||
|
static void tone(uint16_t count);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Play a tone on speaker pin 2 for a given duration.
|
||||||
|
*
|
||||||
|
* \param count The count to be loaded into the timer/counter to play
|
||||||
|
* the desired frequency.
|
||||||
|
* \param dur The duration of the tone, used by `timer()`.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* For details see `BeepPin1::tone(uint16_t, uint8_t)`.
|
||||||
|
*/
|
||||||
|
static void tone(uint16_t count, uint8_t dur);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Handle the duration that a tone on speaker pin 2 plays for.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* For details see `BeepPin1::timer()`.
|
||||||
|
*/
|
||||||
|
static void timer();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Stop a tone that is playing on speaker pin 2.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* For details see `BeepPin1::noTone()`.
|
||||||
|
*/
|
||||||
|
static void noTone();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Convert a frequency to the required count for speaker pin 2.
|
||||||
|
*
|
||||||
|
* \param hz The frequency, in hertz (cycles per second), to be converted
|
||||||
|
* to a count.
|
||||||
|
*
|
||||||
|
* \return The required count to be loaded into the timer/counter for the
|
||||||
|
* given frequency.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* For details see `BeepPin1::freq()`.
|
||||||
|
*/
|
||||||
|
static constexpr uint16_t freq(const float hz)
|
||||||
|
{
|
||||||
|
return (uint16_t) (((F_CPU / 128 / 2) + (hz / 2)) / hz) - 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue