From a8a87a0922a33d38ed80165ee62f9dc35bd08a3d Mon Sep 17 00:00:00 2001 From: Pascal Getreuer <50221757+getreuer@users.noreply.github.com> Date: Fri, 7 Jul 2023 08:47:16 -0600 Subject: [Core] Simplify audio_duration_to_ms() and audio_ms_to_duration(), reduce firmware size by a few bytes. (#21427) --- quantum/audio/audio.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'quantum/audio') diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c index 2570ad9cd1..0300483a93 100644 --- a/quantum/audio/audio.c +++ b/quantum/audio/audio.c @@ -547,20 +547,39 @@ void audio_decrease_tempo(uint8_t tempo_change) { note_tempo -= tempo_change; } -// TODO in the int-math version are some bugs; songs sometimes abruptly end - maybe an issue with the timer/system-tick wrapping around? +/** + * Converts from units of 1/64ths of a beat to milliseconds. + * + * Round-off error is at most 1 millisecond. + * + * Conversion will never overflow for duration_bpm <= 699, provided that + * note_tempo is at least 10. This is quite a long duration, over ten beats. + * + * Beware that for duration_bpm > 699, the result may overflow uint16_t range + * when duration_bpm is large compared to note_tempo: + * + * duration_bpm * 60 * 1000 / (64 * note_tempo) > UINT16_MAX + * + * duration_bpm > (2 * 65535 / 1875) * note_tempo + * = 69.904 * note_tempo. + */ uint16_t audio_duration_to_ms(uint16_t duration_bpm) { -#if defined(__AVR__) - // doing int-math saves us some bytes in the overall firmware size, but the intermediate result is less accurate before being cast to/returned as uint - return ((uint32_t)duration_bpm * 60 * 1000) / (64 * note_tempo); - // NOTE: beware of uint16_t overflows when note_tempo is low and/or the duration is long -#else - return ((float)duration_bpm * 60) / (64 * note_tempo) * 1000; -#endif + return ((uint32_t)duration_bpm * 1875) / ((uint_fast16_t)note_tempo * 2); } + +/** + * Converts from units of milliseconds to 1/64ths of a beat. + * + * Round-off error is at most 1/64th of a beat. + * + * This conversion never overflows: since duration_ms <= UINT16_MAX = 65535 + * and note_tempo <= 255, the result is always in uint16_t range: + * + * duration_ms * 64 * note_tempo / 60 / 1000 + * <= 65535 * 2 * 255 / 1875 + * = 17825.52 + * <= UINT16_MAX. + */ 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 + return ((uint32_t)duration_ms * 2 * note_tempo) / 1875; } -- cgit v1.2.3