diff --git a/LICENSE.txt b/LICENSE.txt index 3141ce7..43f68ea 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -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 */ diff --git a/README.md b/README.md index 4a25063..0a00707 100644 --- a/README.md +++ b/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 diff --git a/examples/ArduBreakout/ArduBreakout.ino b/examples/ArduBreakout/ArduBreakout.ino index 98b8a37..4dcd4ef 100644 --- a/examples/ArduBreakout/ArduBreakout.ino +++ b/examples/ArduBreakout/ArduBreakout.ino @@ -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(); +} + diff --git a/examples/BeepDemo/BeepDemo.ino b/examples/BeepDemo/BeepDemo.ino new file mode 100644 index 0000000..2b179f2 --- /dev/null +++ b/examples/BeepDemo/BeepDemo.ino @@ -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 +// There's no need to #include +// 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'; +} + diff --git a/keywords.txt b/keywords.txt index bec469e..a96a6ec 100644 --- a/keywords.txt +++ b/keywords.txt @@ -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 diff --git a/src/Arduboy2.h b/src/Arduboy2.h index a59c6a1..d4e9413 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -10,6 +10,7 @@ #include #include #include "Arduboy2Core.h" +#include "Arduboy2Beep.h" #include "Sprites.h" #include #include diff --git a/src/Arduboy2Beep.cpp b/src/Arduboy2Beep.cpp new file mode 100644 index 0000000..b1e86fd --- /dev/null +++ b/src/Arduboy2Beep.cpp @@ -0,0 +1,155 @@ +/** + * @file Arduboy2Beep.cpp + * \brief + * Classes to generate simple square wave tones on the Arduboy speaker pins. + */ + +#include +#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 diff --git a/src/Arduboy2Beep.h b/src/Arduboy2Beep.h new file mode 100644 index 0000000..8fd7fab --- /dev/null +++ b/src/Arduboy2Beep.h @@ -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 + * // There's no need to #include + * // 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 +