audiomp3: add decoded_samples property

In my testing, there is no way to accurately know how far into a MP3 file
you're currently playing. You can use monotonic time, but that can have
drift versus the audio playback system, which may not be running at exactly
the expected sample rate.

To allow syncing animation with timestamps in a MP3 file, this presents a
new property, decoded_samples, that records the number of audio samples
sent out of the decoder. While this may not be a completely accurate time,
due to mixer delays, it's much better position that the monotonic clock
difference.

Implementation is keeping track of this value in the mp3file structure and
adding to it whenever data is sent out of the decoder. The property
implementation was a copy/paste from current properties in the audiomp3
files.
This commit is contained in:
Ben Combee 2022-01-01 23:28:43 -06:00
parent 6f8c9deda8
commit 98b0029a29
4 changed files with 30 additions and 0 deletions

View File

@ -232,6 +232,22 @@ const mp_obj_property_t audiomp3_mp3file_rms_level_obj = {
MP_ROM_NONE}, MP_ROM_NONE},
}; };
//| samples_decoded: int
//| """The number of audio samples decoded from the current file. (read only)"""
//|
STATIC mp_obj_t audiomp3_mp3file_obj_get_samples_decoded(mp_obj_t self_in) {
audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_audiomp3_mp3file_get_samples_decoded(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_samples_decoded_obj, audiomp3_mp3file_obj_get_samples_decoded);
const mp_obj_property_t audiomp3_mp3file_samples_decoded_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&audiomp3_mp3file_get_samples_decoded_obj,
MP_ROM_NONE,
MP_ROM_NONE},
};
STATIC const mp_rom_map_elem_t audiomp3_mp3file_locals_dict_table[] = { STATIC const mp_rom_map_elem_t audiomp3_mp3file_locals_dict_table[] = {
// Methods // Methods
@ -245,6 +261,7 @@ STATIC const mp_rom_map_elem_t audiomp3_mp3file_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_bits_per_sample), MP_ROM_PTR(&audiomp3_mp3file_bits_per_sample_obj) }, { MP_ROM_QSTR(MP_QSTR_bits_per_sample), MP_ROM_PTR(&audiomp3_mp3file_bits_per_sample_obj) },
{ MP_ROM_QSTR(MP_QSTR_channel_count), MP_ROM_PTR(&audiomp3_mp3file_channel_count_obj) }, { MP_ROM_QSTR(MP_QSTR_channel_count), MP_ROM_PTR(&audiomp3_mp3file_channel_count_obj) },
{ MP_ROM_QSTR(MP_QSTR_rms_level), MP_ROM_PTR(&audiomp3_mp3file_rms_level_obj) }, { MP_ROM_QSTR(MP_QSTR_rms_level), MP_ROM_PTR(&audiomp3_mp3file_rms_level_obj) },
{ MP_ROM_QSTR(MP_QSTR_samples_decoded), MP_ROM_PTR(&audiomp3_mp3file_samples_decoded_obj) },
}; };
STATIC MP_DEFINE_CONST_DICT(audiomp3_mp3file_locals_dict, audiomp3_mp3file_locals_dict_table); STATIC MP_DEFINE_CONST_DICT(audiomp3_mp3file_locals_dict, audiomp3_mp3file_locals_dict_table);

View File

@ -46,5 +46,6 @@ void common_hal_audiomp3_mp3file_set_sample_rate(audiomp3_mp3file_obj_t *self, u
uint8_t common_hal_audiomp3_mp3file_get_bits_per_sample(audiomp3_mp3file_obj_t *self); uint8_t common_hal_audiomp3_mp3file_get_bits_per_sample(audiomp3_mp3file_obj_t *self);
uint8_t common_hal_audiomp3_mp3file_get_channel_count(audiomp3_mp3file_obj_t *self); uint8_t common_hal_audiomp3_mp3file_get_channel_count(audiomp3_mp3file_obj_t *self);
float common_hal_audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t *self); float common_hal_audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t *self);
uint32_t common_hal_audiomp3_mp3file_get_samples_decoded(audiomp3_mp3file_obj_t *self);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MP3FILE_H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MP3FILE_H

View File

@ -258,6 +258,7 @@ void common_hal_audiomp3_mp3file_set_file(audiomp3_mp3file_obj_t *self, pyb_file
self->channel_count = fi.nChans; self->channel_count = fi.nChans;
self->frame_buffer_size = fi.outputSamps * sizeof(int16_t); self->frame_buffer_size = fi.outputSamps * sizeof(int16_t);
self->len = 2 * self->frame_buffer_size; self->len = 2 * self->frame_buffer_size;
self->samples_decoded = 0;
} }
void common_hal_audiomp3_mp3file_deinit(audiomp3_mp3file_obj_t *self) { void common_hal_audiomp3_mp3file_deinit(audiomp3_mp3file_obj_t *self) {
@ -267,6 +268,7 @@ void common_hal_audiomp3_mp3file_deinit(audiomp3_mp3file_obj_t *self) {
self->buffers[0] = NULL; self->buffers[0] = NULL;
self->buffers[1] = NULL; self->buffers[1] = NULL;
self->file = NULL; self->file = NULL;
self->samples_decoded = 0;
} }
bool common_hal_audiomp3_mp3file_deinited(audiomp3_mp3file_obj_t *self) { bool common_hal_audiomp3_mp3file_deinited(audiomp3_mp3file_obj_t *self) {
@ -327,6 +329,7 @@ audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t *
if (channel == self->other_channel) { if (channel == self->other_channel) {
*bufptr = (uint8_t *)(self->buffers[self->other_buffer_index] + channel); *bufptr = (uint8_t *)(self->buffers[self->other_buffer_index] + channel);
self->other_channel = -1; self->other_channel = -1;
self->samples_decoded += *buffer_length / sizeof(int16_t);
return GET_BUFFER_MORE_DATA; return GET_BUFFER_MORE_DATA;
} }
@ -359,6 +362,7 @@ audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t *
self); self);
} }
self->samples_decoded += *buffer_length / sizeof(int16_t);
return GET_BUFFER_MORE_DATA; return GET_BUFFER_MORE_DATA;
} }
@ -384,3 +388,7 @@ float common_hal_audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t *self) {
} }
return sqrtf(sumsq) / (self->frame_buffer_size / sizeof(int16_t)); return sqrtf(sumsq) / (self->frame_buffer_size / sizeof(int16_t));
} }
uint32_t common_hal_audiomp3_mp3file_get_samples_decoded(audiomp3_mp3file_obj_t *self) {
return self->samples_decoded;
}

View File

@ -54,6 +54,8 @@ typedef struct {
int8_t other_channel; int8_t other_channel;
int8_t other_buffer_index; int8_t other_buffer_index;
uint32_t samples_decoded;
} audiomp3_mp3file_obj_t; } audiomp3_mp3file_obj_t;
// These are not available from Python because it may be called in an interrupt. // These are not available from Python because it may be called in an interrupt.
@ -71,4 +73,6 @@ void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t *self, bool si
float audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t *self); float audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t *self);
uint32_t common_hal_audiomp3_mp3file_get_samples_decoded(audiomp3_mp3file_obj_t *self);
#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MP3FILE_H #endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MP3FILE_H