#include "audio.h"
#include "eeconfig.h"
#include "timer.h"
#include "wait.h"
#ifndef AUDIO_TONE_STACKSIZE
# define AUDIO_TONE_STACKSIZE 8
#endif
uint8_t active_tones = 0; musical_tone_t tones[AUDIO_TONE_STACKSIZE];
bool playing_melody = false; bool playing_note = false; bool state_changed = false;
float (*notes_pointer)[][2]; uint16_t notes_count; bool notes_repeat; uint16_t melody_current_note_duration = 0; uint8_t note_tempo = TEMPO_DEFAULT; uint16_t current_note = 0; bool note_resting = false; uint16_t last_timestamp = 0;
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
# ifndef AUDIO_MAX_SIMULTANEOUS_TONES
# define AUDIO_MAX_SIMULTANEOUS_TONES 3
# endif
uint16_t tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT;
uint8_t tone_multiplexing_index_shift = 0; #endif
extern uint8_t note_timbre;
extern bool glissando;
extern bool vibrato;
extern uint16_t voices_timer;
#ifndef STARTUP_SONG
# define STARTUP_SONG SONG(STARTUP_SOUND)
#endif
#ifndef AUDIO_ON_SONG
# define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND)
#endif
#ifndef AUDIO_OFF_SONG
# define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND)
#endif
float startup_song[][2] = STARTUP_SONG;
float audio_on_song[][2] = AUDIO_ON_SONG;
float audio_off_song[][2] = AUDIO_OFF_SONG;
static bool audio_initialized = false;
static bool audio_driver_stopped = true;
audio_config_t audio_config;
void audio_init() {
if (audio_initialized) {
return;
}
#ifdef EEPROM_ENABLE
if (!eeconfig_is_enabled()) {
eeconfig_init();
}
audio_config.raw = eeconfig_read_audio();
#else
audio_config.enable = true;
# ifdef AUDIO_CLICKY_ON
audio_config.clicky_enable = true;
# endif
#endif
for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) {
tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
}
if (!audio_initialized) {
audio_driver_initialize();
audio_initialized = true;
}
stop_all_notes();
}
void audio_startup(void) {
if (audio_config.enable) {
PLAY_SONG(startup_song);
}
last_timestamp = timer_read();
}
void audio_toggle(void) {
if (audio_config.enable) {
stop_all_notes();
}
audio_config.enable ^= 1;
eeconfig_update_audio(audio_config.raw);
if (audio_config.enable) {
audio_on_user();
}
}
void audio_on(void) {
audio_config.enable = 1;
eeconfig_update_audio(audio_config.raw);
audio_on_user();
PLAY_SONG(audio_on_song);
}
void audio_off(void) {
PLAY_SONG(audio_off_song);
wait_ms(100);
audio_stop_all();
audio_config.enable = 0;
eeconfig_update_audio(audio_config.raw);
}
bool audio_is_on(void) { return (audio_config.enable != 0); }
void audio_stop_all() {
if (audio_driver_stopped) {
return;
}
active_tones = 0;
audio_driver_stop();
playing_melody = false;
playing_note = false;
melody_current_note_duration = 0;
for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) {
tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
}
audio_driver_stopped = true;
}
void audio_stop_tone(float pitch) {
if (pitch < 0.0f) {
pitch = -1 * pitch;
}
if (playing_note) {
if (!audio_initialized) {
audio_init();
}
bool found = false;
for (int i = AUDIO_TONE_STACKSIZE - 1; i >= 0; i--) {
found = (tones[i].pitch == pitch);
if (found) {
tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
for (int j = i; (j < AUDIO_TONE_STACKSIZE - 1); j++) {
tones[j] = tones[j + 1];
tones[j + 1] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
}
break;
}
}
if (!found) {
return;
}
state_changed = true;
active_tones--;
if (active_tones < 0) active_tones = 0;
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
if (tone_multiplexing_index_shift >= active_tones) {
tone_multiplexing_index_shift = 0;
}
#endif
if (active_tones == 0) {
audio_driver_stop();
audio_driver_stopped = true;
playing_note = false;
}
}
}
void audio_play_note(float pitch, uint16_t duration) {
if (!audio_config.enable) {
return;
}
if (!audio_initialized) {
audio_init();
}
if (pitch < 0.0f) {
pitch = -1 * pitch;
}
bool found = false;
for (int i = active_tones - 1; i >= 0; i--) {
found = (tones[i].pitch == pitch);
if (found) {
for (int j = i; (j < active_tones - 1); j++) {
tones[j] = tones[j + 1];
tones[j + 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration};
}
return; }
}
active_tones++;
if (active_tones > AUDIO_TONE_STACKSIZE) {
active_tones = AUDIO_TONE_STACKSIZE;
for (int i = 0; i < active_tones - 1; i++) {
tones[i] = tones[i + 1];
}
}
state_changed = true;
playing_note = true;
tones[active_tones - 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration};
voices_timer = timer_read();
if (audio_driver_stopped) {
audio_driver_start();
audio_driver_stopped = false;
}
}
void audio_play_tone(float pitch) { audio_play_note(pitch, 0xffff); }
void audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat) {
if (!audio_config.enable) {
audio_stop_all();
return;
}
if (!audio_initialized) {
audio_init();
}
if (playing_note) audio_stop_all();
playing_melody = true;
note_resting = false;
notes_pointer = np;
notes_count = n_count;
notes_repeat = n_repeat;
current_note = 0;
audio_play_note((*notes_pointer)[current_note][0], audio_duration_to_ms((*notes_pointer)[current_note][1]));
last_timestamp = timer_read();
melody_current_note_duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
}
float click[2][2];
void audio_play_click(uint16_t delay, float pitch, uint16_t duration) {
uint16_t duration_tone = audio_ms_to_duration(duration);
uint16_t duration_delay = audio_ms_to_duration(delay);
if (delay <= 0.0f) {
click[0][0] = pitch;
click[0][1] = duration_tone;
click[1][0] = 0.0f;
click[1][1] = 0.0f;
audio_play_melody(&click, 1, false);
} else {
click[0][0] = 0.0f;
click[0][1] = duration_delay;
click[1][0] = pitch;
click[1][1] = duration_tone;
audio_play_melody(&click, 2, false);
}
}
bool audio_is_playing_note(void) { return playing_note; }
bool audio_is_playing_melody(void) { return playing_melody; }
uint8_t audio_get_number_of_active_tones(void) { return active_tones; }
float audio_get_frequency(uint8_t tone_index) {
if (tone_index >= active_tones) {
return 0.0f;
}
return tones[active_tones - tone_index - 1].pitch;
}
float audio_get_processed_frequency(uint8_t tone_index) {
if (tone_index >= active_tones) {
return 0.0f;
}
int8_t index = active_tones - tone_index - 1;
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
index = index - tone_multiplexing_index_shift;
if (index < 0) index += active_tones;
#endif
if (tones[index].pitch <= 0.0f) {
return 0.0f;
}
return voice_envelope(tones[index].pitch);
}
bool audio_update_state(void) {
if (!playing_note && !playing_melody) {
return false;
}
bool goto_next_note = false;
uint16_t current_time = timer_read();
if (playing_melody) {
goto_next_note = timer_elapsed(last_timestamp) >= melody_current_note_duration;
if (goto_next_note) {
uint16_t delta = timer_elapsed(last_timestamp) - melody_current_note_duration;
last_timestamp = current_time;
uint16_t previous_note = current_note;
current_note++;
voices_timer = timer_read();
if (current_note >= notes_count) {
if (notes_repeat) {
current_note = 0;
} else {
audio_stop_all();
return false;
}
}
if (!note_resting && (*notes_pointer)[previous_note][0] == (*notes_pointer)[current_note][0]) {
note_resting = true;
audio_play_note(0.0f, audio_duration_to_ms(2));
current_note = previous_note;
melody_current_note_duration = audio_duration_to_ms(2);
} else {
note_resting = false;
uint16_t duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
while (delta > duration && current_note < notes_count - 1) {
delta -= duration;
current_note++;
duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
}
if (delta < duration) {
duration -= delta;
} else {
duration = 1;
}
audio_play_note((*notes_pointer)[current_note][0], duration);
melody_current_note_duration = duration;
}
}
}
if (playing_note) {
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
tone_multiplexing_index_shift = (int)(current_time / tone_multiplexing_rate) % MIN(AUDIO_MAX_SIMULTANEOUS_TONES, active_tones);
goto_next_note = true;
#endif
if (vibrato || glissando) {
goto_next_note = true;
}
for (int i = 0; i < active_tones; i++) {
if ((tones[i].duration != 0xffff) && (tones[i].duration != 0) ) {
if (timer_elapsed(tones[i].time_started) >= tones[i].duration) {
audio_stop_tone(tones[i].pitch); }
}
}
}
if (state_changed) {
state_changed = false;
return true;
}
return goto_next_note;
}
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
void audio_set_tone_multiplexing_rate(uint16_t rate) { tone_multiplexing_rate = rate; }
void audio_enable_tone_multiplexing(void) { tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT; }
void audio_disable_tone_multiplexing(void) { tone_multiplexing_rate = 0; }
void audio_increase_tone_multiplexing_rate(uint16_t change) {
if ((0xffff - change) > tone_multiplexing_rate) {
tone_multiplexing_rate += change;
}
}
void audio_decrease_tone_multiplexing_rate(uint16_t change) {
if (change <= tone_multiplexing_rate) {
tone_multiplexing_rate -= change;
}
}
#endif
void audio_set_tempo(uint8_t tempo) {
if (tempo < 10) note_tempo = 10;
else
note_tempo = tempo;
}
void audio_increase_tempo(uint8_t tempo_change) {
if (tempo_change > 255 - note_tempo)
note_tempo = 255;
else
note_tempo += tempo_change;
}
void audio_decrease_tempo(uint8_t tempo_change) {
if (tempo_change >= note_tempo - 10)
note_tempo = 10;
else
note_tempo -= tempo_change;
}
uint16_t audio_duration_to_ms(uint16_t duration_bpm) {
#if defined(__AVR__)
return ((uint32_t)duration_bpm * 60 * 1000) / (64 * note_tempo);
#else
return ((float)duration_bpm * 60) / (64 * note_tempo) * 1000;
#endif
}
uint16_t audio_ms_to_duration(uint16_t duration_ms) {
#if defined(__AVR__)
return ((uint32_t)duration_ms * 64 * note_tempo) / 60 / 1000;
#else
return ((float)duration_ms * 64 * note_tempo) / 60 / 1000;
#endif
}