4#include "AudioTools/CoreAudio/AudioBasic/Str.h"
5#include "AudioTools/CoreAudio/AudioStreams.h"
6#include "AudioTools/CoreAudio/MusicalNotes.h"
96template <
class T =
int16_t>
116 using AudioOutput::begin;
146 LOGD(
"RTTTLOutput::begin");
171 size_t write(
const uint8_t* data,
size_t len)
override {
172 LOGD(
"write: %d", len);
175 ring_buffer.
writeArray(
const_cast<uint8_t*
>(data), len);
177 if (!is_start && find_byte(data, len,
':') >= 0) {
178 LOGI(
"found ':' - resetting parser state by calling begin()");
201 std::function<
void(
float freqHz,
int durationMs,
int midiNote,
void* ref)>
214 void setRamp(uint8_t upPercent = 20, uint8_t downPercent = 30) {
215 m_ramp_upPercent = upPercent;
216 m_ramp_downPercent = downPercent;
223 Print* p_print =
nullptr;
224 int8_t m_tranpose_octaves = 0;
225 bool is_start =
true;
232 float m_msec_semi = 750;
233 void* reference =
nullptr;
234 uint8_t m_ramp_upPercent = 20;
235 uint8_t m_ramp_downPercent = 30;
236 std::function<void(
float,
int,
int,
void*)> noteCallback;
238 int find_byte(
const uint8_t* haystack,
size_t haystack_len, uint8_t needle) {
239 for (
size_t i = 0; i < haystack_len; i++) {
240 if (haystack[i] == needle) {
247 void play_note(
float freq,
int msec,
int midi = -1) {
249 LOGI(
"play_note: freq=%.2f Hz, msec=%d, midi=%d", freq, msec, midi);
250 if (noteCallback) noteCallback(freq, msec, midi, reference);
251 if (p_print ==
nullptr || p_generator ==
nullptr)
return;
256 p_generator->setPlayTime(msec, m_ramp_upPercent, m_ramp_downPercent);
258 uint8_t buffer[1024];
259 size_t len = p_generator->
readBytes(buffer, 1024);
261 p_print->write(buffer, len);
262 len = p_generator->
readBytes(buffer, 1024);
267 char next_char(
bool convertToLower =
true) {
269 if (!ring_buffer.
read(c)) {
273 m_actual = convertToLower ? tolower(c) : c;
280 for (; m_actual !=
':' && m_actual !=
'\0'; next_char(
false)) {
283 if (!m_title.
isEmpty()) LOGI(
"title: %s", m_title.
c_str());
290 val += m_actual -
'0';
291 }
while (isdigit(next_char()));
296 void parse_defaults() {
299 while (
' ' == next_char());
301 if (isdigit(m_actual)) {
304 m_octave = parse_num();
305 LOGI(
"default octave: %d", m_octave);
308 m_duration = parse_num();
309 LOGI(
"default duration: %d", m_duration);
313 LOGI(
"default bpm: %d", m_bpm);
317 }
else if (isalpha(m_actual)) {
321 }
while (
':' != m_actual);
324 m_msec_semi = 240000.0 / m_bpm;
328 LOGI(
"msec per semi: %.2f", m_msec_semi);
331 float transpose(
float frequency, int8_t octaves) {
332 if (octaves == 0)
return frequency;
333 return frequency * powf(2.0f, octaves);
338 if (m_actual ==
':') next_char();
340 if (m_actual == 0 && ring_buffer.
available() > 0) next_char();
342 bool last_note_is_valid =
false;
343 float last_mult_duration = 1.0;
345 int last_duration = 0;
348 while (m_actual != 0) {
350 while (m_actual ==
' ' || m_actual ==
',') {
351 if (next_char() == 0)
return;
354 if (m_actual == 0)
break;
357 int duration = m_duration;
358 if (isdigit(m_actual)) {
359 duration = parse_num();
360 if (m_actual == 0)
break;
362 last_duration = duration;
365 char name = m_actual;
366 if (name ==
'p' || name ==
'r') {
369 if (m_actual ==
'.') {
373 int msec = (int)((m_msec_semi / duration) * mult);
374 play_note(0.0f, msec, -1);
375 last_note_is_valid =
false;
383 noteEnum = MusicalNotes::C;
386 noteEnum = MusicalNotes::D;
389 noteEnum = MusicalNotes::E;
392 noteEnum = MusicalNotes::F;
395 noteEnum = MusicalNotes::G;
398 noteEnum = MusicalNotes::A;
402 noteEnum = MusicalNotes::B;
406 last_note_is_valid =
false;
409 last_noteEnum = noteEnum;
413 if (next_char() == 0) {
414 last_note_is_valid =
true;
415 last_octave = m_octave;
416 last_mult_duration = 1.0;
421 int semitone = (int)noteEnum;
422 if (m_actual ==
'#') {
429 if (next_char() == 0) {
430 last_note_is_valid =
true;
431 last_octave = m_octave;
432 last_mult_duration = 1.0;
438 int octave = m_octave;
439 if (m_actual >=
'0' && m_actual <=
'9') {
440 octave = m_actual -
'0';
441 if (next_char() == 0) {
442 last_note_is_valid =
true;
443 last_octave = octave;
444 last_mult_duration = 1.0;
450 float mult_duration = 1.0;
451 if (m_actual ==
'.') {
453 if (next_char() == 0) {
454 last_note_is_valid =
true;
455 last_octave = octave;
456 last_mult_duration = mult_duration;
461 float freq = m_notes.
frequency(noteEnum, (uint8_t)octave);
462 freq = transpose(freq, m_tranpose_octaves);
463 int msec = (int)((m_msec_semi / duration) * mult_duration);
465 play_note(freq, msec, midi);
466 last_note_is_valid =
false;
470 if (last_note_is_valid) {
471 float freq = m_notes.
frequency(last_noteEnum, (uint8_t)last_octave);
472 freq = transpose(freq, m_tranpose_octaves);
473 int msec = (int)((m_msec_semi / last_duration) * last_mult_duration);
475 play_note(freq, msec, midi);