[Core] Simplify audio_duration_to_ms() and audio_ms_to_duration(), reduce firmware size by a few bytes. (#21427)

This commit is contained in:
Pascal Getreuer 2023-07-07 08:47:16 -06:00 committed by GitHub
parent e648b84da3
commit a8a87a0922
Failed to generate hash of commit
6 changed files with 220 additions and 13 deletions

View file

@ -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;
}