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:
|
||||
|
||||
Arduboy2 library:
|
||||
Copyright (c) 2016-2017, Scott Allen
|
||||
Copyright (c) 2016-2018, Scott Allen
|
||||
All rights reserved.
|
||||
|
||||
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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Placed in the public domain:
|
||||
|
||||
BeepDemo example sketch:
|
||||
By Scott Allen
|
||||
|
||||
===============================================================================
|
||||
\endverbatim
|
||||
*/
|
||||
|
|
10
README.md
10
README.md
|
@ -129,11 +129,19 @@ Arduboy2 arduboy;
|
|||
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
|
||||
|
||||
#### 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
|
||||
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
#define EE_FILE 2
|
||||
|
||||
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 ROWS = 4; //Rows of bricks
|
||||
int dx = -1; //Initial movement of ball
|
||||
|
@ -57,7 +59,8 @@ byte tick;
|
|||
void setup()
|
||||
{
|
||||
arduboy.begin();
|
||||
arduboy.setFrameRate(40);
|
||||
beep.begin();
|
||||
arduboy.setFrameRate(FRAME_RATE);
|
||||
arduboy.initRandomSeed();
|
||||
}
|
||||
|
||||
|
@ -67,6 +70,9 @@ void loop()
|
|||
if (!(arduboy.nextFrame()))
|
||||
return;
|
||||
|
||||
// Handle the timing and stopping of tones
|
||||
beep.timer();
|
||||
|
||||
//Title screen loop switches from title screen
|
||||
//and high scores until FIRE is pressed
|
||||
while (!start)
|
||||
|
@ -190,7 +196,7 @@ void moveBall()
|
|||
yb=60;
|
||||
released = false;
|
||||
lives--;
|
||||
playTone(175, 250);
|
||||
playToneTimed(175, 500);
|
||||
if (random(0, 2) == 0)
|
||||
{
|
||||
dx = 1;
|
||||
|
@ -347,7 +353,7 @@ void drawGameOver()
|
|||
arduboy.print("Score: ");
|
||||
arduboy.print(score);
|
||||
arduboy.display();
|
||||
delay(4000);
|
||||
arduboy.delayShort(4000);
|
||||
}
|
||||
|
||||
void pause()
|
||||
|
@ -359,7 +365,7 @@ void pause()
|
|||
arduboy.display();
|
||||
while (paused)
|
||||
{
|
||||
delay(150);
|
||||
arduboy.delayShort(150);
|
||||
//Unpause if FIRE is pressed
|
||||
pad2 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
|
||||
if (pad2 == true && oldpad2 == false && released)
|
||||
|
@ -410,7 +416,7 @@ boolean pollFireButton(int n)
|
|||
{
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
delay(15);
|
||||
arduboy.delayShort(15);
|
||||
pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
|
||||
if(pad == true && oldpad == false)
|
||||
{
|
||||
|
@ -548,14 +554,14 @@ void enterInitials()
|
|||
}
|
||||
arduboy.drawLine(56, 28, 88, 28, 0);
|
||||
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 (index > 0)
|
||||
{
|
||||
index--;
|
||||
playTone(1046, 250);
|
||||
playToneTimed(1046, 80);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,14 +570,14 @@ void enterInitials()
|
|||
if (index < 2)
|
||||
{
|
||||
index++;
|
||||
playTone(1046, 250);
|
||||
playToneTimed(1046, 80);
|
||||
}
|
||||
}
|
||||
|
||||
if (arduboy.pressed(DOWN_BUTTON))
|
||||
if (arduboy.pressed(UP_BUTTON))
|
||||
{
|
||||
initials[index]++;
|
||||
playTone(523, 250);
|
||||
playToneTimed(523, 80);
|
||||
// A-Z 0-9 :-? !-/ ' '
|
||||
if (initials[index] == '0')
|
||||
{
|
||||
|
@ -591,10 +597,10 @@ void enterInitials()
|
|||
}
|
||||
}
|
||||
|
||||
if (arduboy.pressed(UP_BUTTON))
|
||||
if (arduboy.pressed(DOWN_BUTTON))
|
||||
{
|
||||
initials[index]--;
|
||||
playTone(523, 250);
|
||||
playToneTimed(523, 80);
|
||||
if (initials[index] == ' ') {
|
||||
initials[index] = '?';
|
||||
}
|
||||
|
@ -611,12 +617,11 @@ void enterInitials()
|
|||
|
||||
if (arduboy.pressed(A_BUTTON))
|
||||
{
|
||||
playToneTimed(1046, 80);
|
||||
if (index < 2)
|
||||
{
|
||||
index++;
|
||||
playTone(1046, 250);
|
||||
} else {
|
||||
playTone(1046, 250);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -693,12 +698,19 @@ void enterHighScore(byte file)
|
|||
}
|
||||
}
|
||||
|
||||
// Wrap the Arduino tone() function so that the pin doesn't have to be
|
||||
// specified each time. Also, don't play if audio is set to off.
|
||||
void playTone(unsigned int frequency, unsigned long duration)
|
||||
// Play a tone at the specified frequency for the specified duration.
|
||||
void playTone(unsigned int frequency, unsigned int duration)
|
||||
{
|
||||
if (arduboy.audio.enabled() == true)
|
||||
{
|
||||
tone(PIN_SPEAKER_1, frequency, duration);
|
||||
}
|
||||
beep.tone(beep.freq(frequency), duration / (1000 / FRAME_RATE));
|
||||
}
|
||||
|
||||
// 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
|
||||
Arduboy2Base KEYWORD1
|
||||
BeepPin1 KEYWORD1
|
||||
BeepPin2 KEYWORD1
|
||||
Sprites KEYWORD1
|
||||
|
||||
#######################################
|
||||
|
@ -98,6 +100,12 @@ writeShowUnitNameFlag KEYWORD2
|
|||
writeUnitID KEYWORD2
|
||||
writeUnitName KEYWORD2
|
||||
|
||||
# Arduboy2Beep classes
|
||||
freq KEYWORD2
|
||||
noTone KEYWORD2
|
||||
timer KEYWORD2
|
||||
tone KEYWORD2
|
||||
|
||||
# Sprites class
|
||||
drawErase KEYWORD2
|
||||
drawExternalMask KEYWORD2
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <Arduino.h>
|
||||
#include <EEPROM.h>
|
||||
#include "Arduboy2Core.h"
|
||||
#include "Arduboy2Beep.h"
|
||||
#include "Sprites.h"
|
||||
#include <Print.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