/**
 * @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