diff --git a/CHANGES b/CHANGES index 483c17e..31d3a6a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +Changes for 2.7.0 + +* New feature: Built-in octave shift support via `anyrtttl::setOctaveShift()`. Shifts all note frequencies up or down by N octaves before calling `tone()`. Useful for passive buzzers with a higher resonant frequency sweet spot. +* New feature: example `OctaveShift` demonstrating octave shift usage. + + Changes for 2.6.0 * Fixed issue #33 - Incorrect dotted note reading. diff --git a/README.md b/README.md index 1265e99..b2e209e 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ AnyRtttl is a feature rich arduino library for playing [RTTTL](http://www.end2en * Supports highly compressed RTTTL binary format. See [Play16Bits](examples\Play16Bits\Play16Bits.ino) or [Play10Bits](examples\Play10Bits\Play10Bits.ino) examples. * Supports names longer than the 10 character limit. * Supports dotted notes in format `[][][.]` (Nokia's specification) or the alternate format `[][.][]` (Nokia's Simpsons example). +* Built-in octave shift to move all note frequencies up or down, ideal for passive buzzers. See [OctaveShift](examples/OctaveShift/OctaveShift.ino) example. ## Status ## @@ -313,6 +314,32 @@ void setup() +## Octave Shift ## + +Passive buzzers often have a resonant frequency sweet spot well above the standard RTTTL note range (typically 200-800Hz). The octave shift feature lets you shift all note frequencies up or down by N octaves without modifying the RTTTL data or writing a custom `tone()` wrapper. + +Use `anyrtttl::setOctaveShift(N)` to shift all notes up by N octaves (or down if N is negative). The default value is 0 (no shift). This applies to the global context used by the legacy APIs. + +For multi-context usage, use `anyrtttl::setOctaveShift(context, N)` to set the shift on a specific context. + +```cpp +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + // Shift all notes up by 2 octaves (frequency * 4) + // Great for passive buzzers that sound best above 2000Hz + anyrtttl::setOctaveShift(2); +} + +void loop() { + anyrtttl::blocking::play(BUZZER_PIN, melody); +} +``` + +See [OctaveShift](examples/OctaveShift/OctaveShift.ino) example for a complete demo. + + + ## Binary RTTTL / Compatibility with custom RTTTL formats ## AnyRtttl can be configured for playing your custom format. AnyRtttl can use a custom function for decoding such a custom format. This allows the library to be compatible with any custom RTTTL formats that can be decoded as legacy RTTTL. @@ -431,6 +458,7 @@ More AnyRtttl examples are also available: * [NonBlockingProgramMemoryRtttl](examples/NonBlockingProgramMemoryRtttl/NonBlockingProgramMemoryRtttl.ino) * [NonBlockingRtttl](examples/NonBlockingRtttl/NonBlockingRtttl.ino) * [NonBlockingStopBeforeEnd](examples/NonBlockingStopBeforeEnd/NonBlockingStopBeforeEnd.ino) +* [OctaveShift](examples/OctaveShift/OctaveShift.ino) * [Play10Bits](examples/Play10Bits/Play10Bits.ino) * [Play16Bits](examples/Play16Bits/Play16Bits.ino) * [PlaySerialRtttl](examples/PlaySerialRtttl/PlaySerialRtttl.ino) diff --git a/examples/OctaveShift/OctaveShift.ino b/examples/OctaveShift/OctaveShift.ino new file mode 100644 index 0000000..741b542 --- /dev/null +++ b/examples/OctaveShift/OctaveShift.ino @@ -0,0 +1,47 @@ +// --------------------------------------------------------------------------- +// OctaveShift example for AnyRtttl +// +// Demonstrates the octave shift feature. Plays a melody at normal pitch, +// then plays it again shifted up by 2 octaves — useful for passive buzzers +// whose sweet spot is at higher frequencies (2500Hz+). +// --------------------------------------------------------------------------- + +#include +#include +#include + +// Define the BUZZER_PIN for current board +#if defined(ESP32) +#define BUZZER_PIN 25 +#elif defined(ESP8266) +#define BUZZER_PIN 2 +#else +#define BUZZER_PIN 9 +#endif + +const char * mario = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p"; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); + + // --- Play at normal pitch (default octaveShift = 0) --- + Serial.println("Playing at normal pitch..."); + anyrtttl::blocking::play(BUZZER_PIN, mario); + + delay(1000); + + // --- Shift up 2 octaves for passive buzzer --- + Serial.println("Playing shifted up 2 octaves..."); + anyrtttl::setOctaveShift(2); + anyrtttl::blocking::play(BUZZER_PIN, mario); + + // Reset shift back to normal + anyrtttl::setOctaveShift(0); +} + +void loop() { + // nothing to do +} diff --git a/src/anyrtttl.cpp b/src/anyrtttl.cpp index cdab64a..8682a51 100644 --- a/src/anyrtttl.cpp +++ b/src/anyrtttl.cpp @@ -135,6 +135,10 @@ void setMillisFunction(MillisFuncPtr iFunc) { _millis = iFunc; } +void setOctaveShift(int8_t iOctaveShift) { + gGlobalContext.octaveShift = iOctaveShift; +} + char readCharMem(const char * iBuffer) { char output = *iBuffer; return output; @@ -195,8 +199,10 @@ void begin(rtttl_context_t & c, byte iPin, const char * iBuffer, GetCharFuncPtr return; } - // init context + // init context, preserving octaveShift set by user + int8_t savedOctaveShift = c.octaveShift; initContext(c); + c.octaveShift = savedOctaveShift; //init values c.pin = iPin; @@ -422,6 +428,10 @@ void nextNote(rtttl_context_t & c) #endif uint16_t frequency = gNotes[(c.scale - 4) * NOTES_PER_OCTAVE + c.noteOffset]; + if (c.octaveShift > 0) + frequency <<= c.octaveShift; + else if (c.octaveShift < 0) + frequency >>= (-c.octaveShift); _tone(c.pin, frequency, c.duration); c.nextNoteMs = _millis() + (c.duration+1); @@ -552,6 +562,7 @@ void initContext(rtttl_context_t & c) { c.nextNoteMs = 0; c.playing = false; c.noteOffset = 0; + c.octaveShift = 0; } }; //anyrtttl namespace diff --git a/src/anyrtttl.h b/src/anyrtttl.h index 187789d..bbc5ac2 100644 --- a/src/anyrtttl.h +++ b/src/anyrtttl.h @@ -47,6 +47,7 @@ typedef struct rtttl_context_t { unsigned long nextNoteMs; // timestamp in milliseconds of end of note (start of next). bool playing; byte noteOffset; + int8_t octaveShift; // shift all note frequencies up (positive) or down (negative) by N octaves. } rtttl_context_t; /**************************************************************************** @@ -109,6 +110,26 @@ void setNoToneFunction(NoToneFuncPtr iFunc); ****************************************************************************/ void setMillisFunction(MillisFuncPtr iFunc); +/**************************************************************************** + * Description: + * Sets the octave shift applied to all note frequencies. + * A positive value shifts notes up (e.g. 2 shifts up by 2 octaves), + * a negative value shifts notes down. Default is 0 (no shift). + * This sets the shift on the global context used by legacy APIs. + * Parameters: + * iOctaveShift: Number of octaves to shift. Positive = up, negative = down. + ****************************************************************************/ +void setOctaveShift(int8_t iOctaveShift); + +/**************************************************************************** + * Description: + * Sets the octave shift on a specific context. + * Parameters: + * c: The context to modify. + * iOctaveShift: Number of octaves to shift. Positive = up, negative = down. + ****************************************************************************/ +inline void setOctaveShift(rtttl_context_t & c, int8_t iOctaveShift) { c.octaveShift = iOctaveShift; } + /**************************************************************************** * Description: * Read the first byte of a buffer stored in RAM.