1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00
2012-05-25 15:04:49 +10:00

240 lines
6.3 KiB
C++

/*
* Copyright (C) 2012 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "Melody.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
/**
* \class Melody Melody.h <Melody.h>
* \brief Plays a melody on a digital output pin using tone().
*
* The following example plays a simple tone three times on digital pin 8:
*
* \code
* #include <Melody.h>
*
* int notes[] = {
* NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3,
* NOTE_REST, NOTE_B3, NOTE_C4, NOTE_REST
* };
* byte lengths[] = {4, 8, 8, 4, 4, 4, 4, 4, 2};
*
* Melody melody(8);
*
* void setup() {
* melody.setMelody(notes, lengths, sizeof(lengths));
* melody.setLoopCount(3);
* melody.play();
* }
*
* void loop() {
* melody.run();
* }
* \endcode
*
* The \c notes array contains the frequency of the notes to be played,
* with the special value \c NOTE_REST indicating a rest where no notes
* are playing. The \c lengths array contains the lengths of each of the
* notes; a value of 4 indicates a quarter note, a value of 8 indicates
* an eighth note, etc.
*
* The run() method must be called from the application's main
* <tt>loop()</tt> method to ensure that the melody advances from
* one note to the next. It will not block the application while
* notes are playing.
*
* The number of loops can also be specified with setLoopDuration() which
* sets a maximum amount of time that the melody will play before stopping.
* The following example plays the melody for no more than 60 seconds:
*
* \code
* void setup() {
* melody.setMelody(notes, lengths, sizeof(lengths));
* melody.setLoopDuration(60000UL);
* melody.play();
* }
* \endcode
*/
/**
* \brief Constructs a new melody playing object for \a pin.
*/
Melody::Melody(uint8_t pin)
: _pin(pin)
, playing(false)
, _loopCount(0)
, loopsLeft(0)
, notes(0)
, lengths(0)
, size(0)
, posn(0)
, duration(0)
, startNote(0)
{
}
/**
* \fn bool Melody::isPlaying() const
* \brief Returns true if the melody is currently playing; false if not.
*/
/**
* \fn int Melody::loopCount() const
* \brief Returns the number of times the melody should loop before stopping.
*
* The default value is zero, indicating that the melody will loop
* indefinitely.
*
* \sa setLoopCount(), setLoopDuration(), play()
*/
/**
* \fn void Melody::setLoopCount(int count)
* \brief Sets the number of times the melody should loop to \a count.
*
* If \a count is zero, then the melody will loop indefinitely.
*
* \sa loopCount(), setLoopDuration()
*/
/**
* \brief Sets the maximum number of loops to last no longer than \a ms milliseconds.
*
* This function must be called after the melody is specified with setMelody()
* as it uses the length of the melody and \a ms to determine the loopCount().
*
* \sa loopCount(), setLoopCount()
*/
void Melody::setLoopDuration(unsigned long ms)
{
unsigned long duration = 0;
for (unsigned int index = 0; index < size; ++index)
duration += (1000 / lengths[index]) * 13 / 10;
_loopCount = (int)(ms / duration);
if (!_loopCount)
_loopCount = 1; // Play the melody at least once.
}
/**
* \brief Starts playing the melody, or restarts it if already playing.
*
* \sa playOnce(), setMelody(), stop(), loopCount()
*/
void Melody::play()
{
stop();
if (size == 0)
return; // No melody to play.
loopsLeft = _loopCount;
posn = 0;
playing = true;
nextNote();
}
/**
* \brief Plays the melody once and then stops.
*
* \sa play(), stop()
*/
void Melody::playOnce()
{
stop();
if (size == 0)
return; // No melody to play.
loopsLeft = 1;
posn = 0;
playing = true;
nextNote();
}
/**
* \brief Stops playing the melody.
*
* \sa play()
*/
void Melody::stop()
{
if (!playing)
return;
playing = false;
noTone(_pin);
}
/**
* \brief Sets the melody to the \a size elements of \a notes and \a lengths.
*
* If a melody is currently playing, then this function will stop playback.
*
* The \a notes array contains the frequency of the notes to be played,
* with the special value \c NOTE_REST indicating a rest where no notes
* are playing. The \a lengths array contains the lengths of each of the
* notes; a value of 4 indicates a quarter note, a value of 8 indicates
* an eighth note, etc.
*
* \sa play()
*/
void Melody::setMelody(const int *notes, const uint8_t *lengths, unsigned int size)
{
stop();
this->notes = notes;
this->lengths = lengths;
this->size = size;
}
/**
* \brief Runs the melody control loop.
*
* This function must be called by the application's main <tt>loop()</tt>
* function to cause the melody to advance from note to note. It will not
* block the application while notes are playing.
*/
void Melody::run()
{
if (!playing)
return;
if ((millis() - startNote) >= duration) {
noTone(_pin);
nextNote();
}
}
void Melody::nextNote()
{
if (posn >= size) {
if (loopsLeft != 0 && --loopsLeft <= 0) {
stop();
return;
}
posn = 0;
}
duration = 1000 / lengths[posn];
if (notes[posn] != NOTE_REST)
tone(_pin, notes[posn], duration);
++posn;
duration = duration * 13 / 10; // i.e., duration * 1.3
startNote = millis();
}