// --------------------------------------------------------------------------- // AUTHOR/LICENSE: // The following code was written by Antoine Beauchamp. For other authors, see AUTHORS file. // The code & updates for the library can be found at https://github.com/end2endzone/AnyRtttl // MIT License: http://www.opensource.org/licenses/mit-license.php // --------------------------------------------------------------------------- #include "Arduino.h" #include "anyrtttl.h" #include "binrtttl.h" /********************************************************* * RTTTL Library data *********************************************************/ namespace anyrtttl { const uint16_t notes[] = { NOTE_SILENT, NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4, NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6, NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7 }; #define isdigit(n) (n >= '0' && n <= '9') typedef uint16_t TONE_DURATION; static const byte NOTES_PER_OCTAVE = 12; const char * buffer = ""; ReadCharFuncPtr readCharFunc = &readChar; int bufferIndex = -32760; byte default_dur = 4; byte default_oct = 5; RTTTL_BPM bpm = 63; RTTTL_DURATION wholenote; byte pin = -1; unsigned long delayToNextNote = 0; //milliseconds before playing the next note bool playing = false; TONE_DURATION duration; byte noteOffset; RTTTL_OCTAVE_VALUE scale; int tmpNumber; const char * readNumber(const char * iBuffer, int & oValue, ReadCharFuncPtr iReadCharFunc) { oValue = 0; while(isdigit(iReadCharFunc(iBuffer))) { oValue = (oValue * 10) + (readCharFunc(buffer) - '0'); iBuffer++; } return iBuffer; } void serialPrint(const char * iBuffer, ReadCharFuncPtr iReadCharFunc) { char c = readCharFunc(iBuffer); while(c) { Serial.print(c); iBuffer++; c = readCharFunc(iBuffer); } } /**************************************************************************** * Custom functions ****************************************************************************/ ToneFuncPtr _tone = &tone; NoToneFuncPtr _noTone = &noTone; DelayFuncPtr _delay = &delay; MillisFuncPtr _millis = &millis; void setToneFunction(ToneFuncPtr iFunc) { _tone = iFunc; } void setNoToneFunction(NoToneFuncPtr iFunc) { _noTone = iFunc; } void setDelayFunction(DelayFuncPtr iFunc) { _delay = iFunc; } void setMillisFunction(MillisFuncPtr iFunc) { _millis = iFunc; } char readChar(const char * iBuffer) { return *iBuffer; } char readChar_P(const char * iBuffer) { return pgm_read_byte_near(iBuffer); } /**************************************************************************** * Blocking API ****************************************************************************/ namespace blocking { void play(byte iPin, const char * iBuffer, ReadCharFuncPtr iReadCharFunc) { // Absolutely no error checking in here default_dur = 4; default_oct = 6; bpm = 63; buffer = iBuffer; readCharFunc = iReadCharFunc; #ifdef ANY_RTTTL_DEBUG Serial.print("playing: "); serialPrint(buffer, readCharFunc); Serial.println(); #endif // format: d=N,o=N,b=NNN: // find the start (skip name, etc) while(readCharFunc(buffer) != ':') buffer++; // ignore name buffer++; // skip ':' // get default duration if(readCharFunc(buffer) == 'd') { buffer++; buffer++; // skip "d=" buffer = readNumber(buffer, tmpNumber, readCharFunc); if(tmpNumber > 0) default_dur = tmpNumber; buffer++; // skip comma } #ifdef ANY_RTTTL_INFO Serial.print("ddur: "); Serial.println(default_dur, 10); #endif // get default octave if(readCharFunc(buffer) == 'o') { buffer++; buffer++; // skip "o=" buffer = readNumber(buffer, tmpNumber, readCharFunc); if(tmpNumber >= 3 && tmpNumber <= 7) default_oct = tmpNumber; buffer++; // skip comma } #ifdef ANY_RTTTL_INFO Serial.print("doct: "); Serial.println(default_oct, 10); #endif // get BPM if(readCharFunc(buffer) == 'b') { buffer++; buffer++; // skip "b=" buffer = readNumber(buffer, tmpNumber, readCharFunc); bpm = tmpNumber; buffer++; // skip colon } #ifdef ANY_RTTTL_INFO Serial.print("bpm: "); Serial.println(bpm, 10); #endif // BPM usually expresses the number of quarter notes per minute wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole noteOffset (in milliseconds) #ifdef ANY_RTTTL_INFO Serial.print("wn: "); Serial.println(wholenote, 10); #endif // now begin note loop while(readCharFunc(buffer)) { // first, get note duration, if available buffer = readNumber(buffer, tmpNumber, readCharFunc); if(tmpNumber) duration = wholenote / tmpNumber; else duration = wholenote / default_dur; // we will need to check if we are a dotted noteOffset after // now get the note noteOffset = getNoteOffsetFromLetter(readCharFunc(buffer)); buffer++; // now, get optional '#' sharp if(readCharFunc(buffer) == '#') { noteOffset++; buffer++; } // now, get optional '.' dotted note if(readCharFunc(buffer) == '.') { duration += duration/2; buffer++; } // now, get scale if(isdigit(readCharFunc(buffer))) { scale = readCharFunc(buffer) - '0'; buffer++; } else { scale = default_oct; } if(readCharFunc(buffer) == ',') buffer++; // skip comma for next note (or we may be at the end) // now play the note if(noteOffset) { uint16_t frequency = notes[(scale - 4) * NOTES_PER_OCTAVE + noteOffset]; #ifdef ANY_RTTTL_INFO Serial.print("Playing: "); Serial.print(scale, 10); Serial.print(' '); Serial.print(noteOffset, 10); Serial.print(" ("); Serial.print(frequency, 10); Serial.print(") "); Serial.println(duration, 10); #endif _tone(iPin, frequency, duration); _delay(duration+1); _noTone(iPin); } else { #ifdef ANY_RTTTL_INFO Serial.print("Pausing: "); Serial.println(duration, 10); #endif _delay(duration); } } } void play(byte iPin, const char * iBuffer) { play(iPin, iBuffer, &readChar); } void play(byte iPin, const __FlashStringHelper* str) { play(iPin, (const char *)str, &readChar_P); } void playProgMem(byte iPin, const char * iBuffer) { play(iPin, iBuffer, &readChar_P); } void play_P(byte iPin, const char * iBuffer) { play(iPin, iBuffer, &readChar_P); } void play_P(byte iPin, const __FlashStringHelper* str) { play(iPin, (const char *)str, &readChar_P); } void play16Bits(int iPin, const unsigned char * iBuffer, int iNumNotes) { // Absolutely no error checking in here RTTTL_DEFAULT_VALUE_SECTION * defaultSection = (RTTTL_DEFAULT_VALUE_SECTION *)iBuffer; RTTTL_NOTE * notesBuffer = (RTTTL_NOTE *)iBuffer; bpm = defaultSection->bpm; #ifdef ANY_RTTTL_DEBUG Serial.print("numNotes="); Serial.println(iNumNotes); // format: d=N,o=N,b=NNN: Serial.print("d="); Serial.print(getNoteDurationFromIndex(defaultSection->durationIdx)); Serial.print(",o="); Serial.print(getNoteOctaveFromIndex(defaultSection->octaveIdx)); Serial.print(",b="); Serial.println(bpm); #endif // BPM usually expresses the number of quarter notes per minute wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole noteOffset (in milliseconds) // now begin note loop for(int i=0; i