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);
174 ring_buffer.
writeArray(
const_cast<uint8_t*
>(data), len);
176 if (!is_start && find_byte(data, len,
':') >= 0) {
177 LOGI(
"found ':' - resetting parser state by calling begin()");
200 std::function<
void(
float freqHz,
int durationMs,
int midiNote,
void* ref)>
216 Print* p_print =
nullptr;
217 int8_t m_tranpose_octaves = 0;
218 bool is_start =
true;
225 float m_msec_semi = 750;
226 void* reference =
nullptr;
227 std::function<void(
float,
int,
int,
void*)> noteCallback;
229 int find_byte(
const uint8_t* haystack,
size_t haystack_len, uint8_t needle) {
230 for (
size_t i = 0; i < haystack_len; i++) {
231 if (haystack[i] == needle) {
238 void play_note(
float freq,
int msec,
int midi = -1) {
240 LOGI(
"play_note: freq=%.2f Hz, msec=%d, midi=%d", freq, msec, midi);
241 if (noteCallback) noteCallback(freq, msec, midi, reference);
242 if (p_print ==
nullptr || p_generator ==
nullptr)
return;
246 int frames = (int)((uint64_t)info.sample_rate * (uint64_t)msec / 1000);
247 int frame_size = (info.channels * info.bits_per_sample) / 8;
248 int open = frames * frame_size;
249 uint8_t buffer[1024];
251 int toCopy = std::min(open, (
int)
sizeof(buffer));
253 p_print->write(buffer, toCopy);
259 char next_char(
bool convertToLower =
true) {
261 if (!ring_buffer.
read(c)) {
265 m_actual = convertToLower ? tolower(c) : c;
272 for (; m_actual !=
':' && m_actual !=
'\0'; next_char(
false)) {
275 if (!m_title.
isEmpty()) LOGI(
"title: %s", m_title.
c_str());
282 val += m_actual -
'0';
283 }
while (isdigit(next_char()));
288 void parse_defaults() {
291 while (
' ' == next_char());
293 if (isdigit(m_actual)) {
296 m_octave = parse_num();
297 LOGI(
"default octave: %d", m_octave);
300 m_duration = parse_num();
301 LOGI(
"default duration: %d", m_duration);
305 LOGI(
"default bpm: %d", m_bpm);
309 }
else if (isalpha(m_actual)) {
313 }
while (
':' != m_actual);
316 m_msec_semi = 240000.0 / m_bpm;
320 LOGI(
"msec per semi: %.2f", m_msec_semi);
323 float transpose(
float frequency, int8_t octaves) {
324 if (octaves == 0)
return frequency;
325 return frequency * powf(2.0f, octaves);
331 if (m_actual ==
':') next_char();
333 while (m_actual != 0) {
335 while (m_actual ==
' ' || m_actual ==
',') {
336 if (next_char() == 0)
return;
339 if (m_actual == 0)
break;
342 int duration = m_duration;
343 if (isdigit(m_actual)) {
344 duration = parse_num();
345 if (m_actual == 0)
break;
349 char name = m_actual;
350 if (name ==
'p' || name ==
'r') {
353 if (m_actual ==
'.') {
357 int msec = (int)((m_msec_semi / duration) * mult);
358 play_note(0.0f, msec, -1);
366 noteEnum = MusicalNotes::C;
369 noteEnum = MusicalNotes::D;
372 noteEnum = MusicalNotes::E;
375 noteEnum = MusicalNotes::F;
378 noteEnum = MusicalNotes::G;
381 noteEnum = MusicalNotes::A;
385 noteEnum = MusicalNotes::B;
395 if (next_char() == 0)
break;
398 int semitone = (int)noteEnum;
399 if (m_actual ==
'#') {
406 if (next_char() == 0)
break;
410 int octave = m_octave;
411 if (m_actual >=
'0' && m_actual <=
'9') {
412 octave = m_actual -
'0';
413 if (next_char() == 0) {
415 float freq = m_notes.
frequency(noteEnum, (uint8_t)octave);
416 freq = transpose(freq, m_tranpose_octaves);
417 int msec = (int)((m_msec_semi / duration) * 1.0);
419 play_note(freq, msec, midi);
425 double mult_duration = 1.0;
426 if (m_actual ==
'.') {
431 float freq = m_notes.
frequency(noteEnum, (uint8_t)octave);
432 freq = transpose(freq, m_tranpose_octaves);
433 int msec = (int)((m_msec_semi / duration) * mult_duration);
435 play_note(freq, msec, midi);