diff --git a/ports/atmel-samd/peripherals b/ports/atmel-samd/peripherals index 4c0deecf889da..b89811f22a24a 160000 --- a/ports/atmel-samd/peripherals +++ b/ports/atmel-samd/peripherals @@ -1 +1 @@ -Subproject commit 4c0deecf889da0074c1dbc9a5e2d24cb7c7a31c6 +Subproject commit b89811f22a24ac350079ceaf0cdf0e62aa03f4f4 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 8dd4fc6b16525..38b41c619b969 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -322,7 +322,7 @@ SRC_SHARED_MODULE_ALL = \ audiomixer/Mixer.c \ audiomixer/MixerVoice.c \ audiomp3/__init__.c \ - audiomp3/MP3File.c \ + audiomp3/MP3Decoder.c \ bitbangio/I2C.c \ bitbangio/OneWire.c \ bitbangio/SPI.c \ diff --git a/shared-bindings/audiomp3/MP3File.c b/shared-bindings/audiomp3/MP3Decoder.c similarity index 79% rename from shared-bindings/audiomp3/MP3File.c rename to shared-bindings/audiomp3/MP3Decoder.c index f518bae4bb205..2240422127d1f 100644 --- a/shared-bindings/audiomp3/MP3File.c +++ b/shared-bindings/audiomp3/MP3Decoder.c @@ -30,18 +30,16 @@ #include "lib/utils/context_manager_helpers.h" #include "py/objproperty.h" #include "py/runtime.h" -#include "shared-bindings/audiomp3/MP3File.h" +#include "shared-bindings/audiomp3/MP3Decoder.h" #include "shared-bindings/util.h" #include "supervisor/shared/translate.h" //| .. currentmodule:: audiomp3 //| -//| :class:`MP3` -- Load a mp3 file for audio playback -//| ======================================================== +//| :class:`MP3Decoder` -- Load a mp3 file for audio playback +//| ========================================================= //| -//| A .mp3 file prepped for audio playback. Only mono and stereo files are supported. Samples must -//| be 8 bit unsigned or 16 bit signed. If a buffer is provided, it will be used instead of allocating -//| an internal buffer. +//| An object that decodes MP3 files for playback on an audio device. //| //| .. class:: MP3(file[, buffer]) //| @@ -63,7 +61,7 @@ //| speaker_enable.switch_to_output(value=True) //| //| data = open("cplay-16bit-16khz-64kbps.mp3", "rb") -//| mp3 = audiomp3.MP3File(data) +//| mp3 = audiomp3.MP3Decoder(data) //| a = audioio.AudioOut(board.A0) //| //| print("playing") @@ -129,6 +127,37 @@ STATIC mp_obj_t audiomp3_mp3file_obj___exit__(size_t n_args, const mp_obj_t *arg } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiomp3_mp3file___exit___obj, 4, 4, audiomp3_mp3file_obj___exit__); +//| .. attribute:: file +//| +//| File to play back. +//| +STATIC mp_obj_t audiomp3_mp3file_obj_get_file(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return self->file; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_file_obj, audiomp3_mp3file_obj_get_file); + +STATIC mp_obj_t audiomp3_mp3file_obj_set_file(mp_obj_t self_in, mp_obj_t file) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (!MP_OBJ_IS_TYPE(file, &mp_type_fileio)) { + mp_raise_TypeError(translate("file must be a file opened in byte mode")); + } + common_hal_audiomp3_mp3file_set_file(self, file); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiomp3_mp3file_set_file_obj, audiomp3_mp3file_obj_set_file); + +const mp_obj_property_t audiomp3_mp3file_file_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiomp3_mp3file_get_file_obj, + (mp_obj_t)&audiomp3_mp3file_set_file_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + + //| .. attribute:: sample_rate //| //| 32 bit value that dictates how quickly samples are loaded into the DAC @@ -193,6 +222,24 @@ const mp_obj_property_t audiomp3_mp3file_channel_count_obj = { (mp_obj_t)&mp_const_none_obj}, }; +//| .. attribute:: rms_level +//| +//| The RMS audio level of a recently played moment of audio. (read only) +//| +STATIC mp_obj_t audiomp3_mp3file_obj_get_rms_level(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_float(common_hal_audiomp3_mp3file_get_rms_level(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_rms_level_obj, audiomp3_mp3file_obj_get_rms_level); + +const mp_obj_property_t audiomp3_mp3file_rms_level_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiomp3_mp3file_get_rms_level_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + STATIC const mp_rom_map_elem_t audiomp3_mp3file_locals_dict_table[] = { // Methods @@ -201,9 +248,11 @@ STATIC const mp_rom_map_elem_t audiomp3_mp3file_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiomp3_mp3file___exit___obj) }, // Properties + { MP_ROM_QSTR(MP_QSTR_file), MP_ROM_PTR(&audiomp3_mp3file_file_obj) }, { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audiomp3_mp3file_sample_rate_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_rms_level), MP_ROM_PTR(&audiomp3_mp3file_rms_level_obj) }, }; STATIC MP_DEFINE_CONST_DICT(audiomp3_mp3file_locals_dict, audiomp3_mp3file_locals_dict_table); @@ -219,7 +268,7 @@ STATIC const audiosample_p_t audiomp3_mp3file_proto = { const mp_obj_type_t audiomp3_mp3file_type = { { &mp_type_type }, - .name = MP_QSTR_MP3File, + .name = MP_QSTR_MP3Decoder, .make_new = audiomp3_mp3file_make_new, .locals_dict = (mp_obj_dict_t*)&audiomp3_mp3file_locals_dict, .protocol = &audiomp3_mp3file_proto, diff --git a/shared-bindings/audiomp3/MP3File.h b/shared-bindings/audiomp3/MP3Decoder.h similarity index 90% rename from shared-bindings/audiomp3/MP3File.h rename to shared-bindings/audiomp3/MP3Decoder.h index adc13ea2c072e..36d525e938106 100644 --- a/shared-bindings/audiomp3/MP3File.h +++ b/shared-bindings/audiomp3/MP3Decoder.h @@ -31,18 +31,20 @@ #include "py/obj.h" #include "extmod/vfs_fat.h" -#include "shared-module/audiomp3/MP3File.h" +#include "shared-module/audiomp3/MP3Decoder.h" extern const mp_obj_type_t audiomp3_mp3file_type; void common_hal_audiomp3_mp3file_construct(audiomp3_mp3file_obj_t* self, pyb_file_obj_t* file, uint8_t *buffer, size_t buffer_size); +void common_hal_audiomp3_mp3file_set_file(audiomp3_mp3file_obj_t* self, pyb_file_obj_t* file); void common_hal_audiomp3_mp3file_deinit(audiomp3_mp3file_obj_t* self); bool common_hal_audiomp3_mp3file_deinited(audiomp3_mp3file_obj_t* self); uint32_t common_hal_audiomp3_mp3file_get_sample_rate(audiomp3_mp3file_obj_t* self); void common_hal_audiomp3_mp3file_set_sample_rate(audiomp3_mp3file_obj_t* self, uint32_t sample_rate); 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); +float common_hal_audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t* self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MP3FILE_H diff --git a/shared-bindings/audiomp3/__init__.c b/shared-bindings/audiomp3/__init__.c index 06f852ff1c1d9..fb2187669cd36 100644 --- a/shared-bindings/audiomp3/__init__.c +++ b/shared-bindings/audiomp3/__init__.c @@ -29,7 +29,7 @@ #include "py/obj.h" #include "py/runtime.h" -#include "shared-bindings/audiomp3/MP3File.h" +#include "shared-bindings/audiomp3/MP3Decoder.h" //| :mod:`audiomp3` --- Support for MP3-compressed audio files //| ========================================================== @@ -44,12 +44,12 @@ //| .. toctree:: //| :maxdepth: 3 //| -//| MP3File +//| MP3Decoder //| STATIC const mp_rom_map_elem_t audiomp3_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiomp3) }, - { MP_ROM_QSTR(MP_QSTR_MP3File), MP_ROM_PTR(&audiomp3_mp3file_type) }, + { MP_ROM_QSTR(MP_QSTR_MP3Decoder), MP_ROM_PTR(&audiomp3_mp3file_type) }, }; STATIC MP_DEFINE_CONST_DICT(audiomp3_module_globals, audiomp3_module_globals_table); diff --git a/shared-module/audiomp3/MP3File.c b/shared-module/audiomp3/MP3Decoder.c similarity index 86% rename from shared-module/audiomp3/MP3File.c rename to shared-module/audiomp3/MP3Decoder.c index 0aa4f24a66208..30357c6161e55 100644 --- a/shared-module/audiomp3/MP3File.c +++ b/shared-module/audiomp3/MP3Decoder.c @@ -25,18 +25,21 @@ * THE SOFTWARE. */ -#include "shared-bindings/audiomp3/MP3File.h" +#include "shared-bindings/audiomp3/MP3Decoder.h" #include #include +#include #include "py/mperrno.h" #include "py/runtime.h" -#include "shared-module/audiomp3/MP3File.h" +#include "shared-module/audiomp3/MP3Decoder.h" #include "supervisor/shared/translate.h" #include "lib/mp3/src/mp3common.h" +#define MAX_BUFFER_LEN (MAX_NSAMP * MAX_NGRAN * MAX_NCHAN * sizeof(int16_t)) + /** Fill the input buffer if it is less than half full. * * Returns true if the input buffer contains any useful data, @@ -143,7 +146,7 @@ STATIC bool mp3file_get_next_frame_info(audiomp3_mp3file_obj_t* self, MP3FrameIn do { err = MP3GetNextFrameInfo(self->decoder, fi, READ_PTR(self)); if (err == ERR_MP3_NONE) { - break; + break; } CONSUME(self, 1); mp3file_find_sync_word(self); @@ -165,7 +168,6 @@ void common_hal_audiomp3_mp3file_construct(audiomp3_mp3file_obj_t* self, // than the two 4kB output buffers, except that the alignment allows to // never allocate that extra frame buffer. - self->file = file; self->inbuf_length = 2048; self->inbuf_offset = self->inbuf_length; self->inbuf = m_malloc(self->inbuf_length, false); @@ -181,40 +183,56 @@ void common_hal_audiomp3_mp3file_construct(audiomp3_mp3file_obj_t* self, translate("Couldn't allocate decoder")); } - mp3file_find_sync_word(self); - MP3FrameInfo fi; - if(!mp3file_get_next_frame_info(self, &fi)) { - mp_raise_msg(&mp_type_RuntimeError, - translate("Failed to parse MP3 file")); - } - - self->sample_rate = fi.samprate; - self->channel_count = fi.nChans; - self->frame_buffer_size = fi.outputSamps*sizeof(int16_t); - if ((intptr_t)buffer & 1) { buffer += 1; buffer_size -= 1; } - if (buffer_size >= 2 * self->frame_buffer_size) { - self->len = buffer_size / 2 / self->frame_buffer_size * self->frame_buffer_size; + if (buffer_size >= 2 * MAX_BUFFER_LEN) { self->buffers[0] = (int16_t*)(void*)buffer; - self->buffers[1] = (int16_t*)(void*)buffer + self->len; + self->buffers[1] = (int16_t*)(void*)(buffer + MAX_BUFFER_LEN); } else { - self->len = 2 * self->frame_buffer_size; - self->buffers[0] = m_malloc(self->len, false); + self->buffers[0] = m_malloc(MAX_BUFFER_LEN, false); if (self->buffers[0] == NULL) { common_hal_audiomp3_mp3file_deinit(self); mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate first buffer")); } - self->buffers[1] = m_malloc(self->len, false); + self->buffers[1] = m_malloc(MAX_BUFFER_LEN, false); if (self->buffers[1] == NULL) { common_hal_audiomp3_mp3file_deinit(self); mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate second buffer")); } } + + common_hal_audiomp3_mp3file_set_file(self, file); +} + +void common_hal_audiomp3_mp3file_set_file(audiomp3_mp3file_obj_t* self, pyb_file_obj_t* file) { + self->file = file; + f_lseek(&self->file->fp, 0); + self->inbuf_offset = self->inbuf_length; + self->eof = 0; + self->other_channel = -1; + mp3file_update_inbuf(self); + mp3file_find_sync_word(self); + // It **SHOULD** not be necessary to do this; the buffer should be filled + // with fresh content before it is returned by get_buffer(). The fact that + // this is necessary to avoid a glitch at the start of playback of a second + // track using the same decoder object means there's still a bug in + // get_buffer() that I didn't understand. + memset(self->buffers[0], 0, MAX_BUFFER_LEN); + memset(self->buffers[1], 0, MAX_BUFFER_LEN); + MP3FrameInfo fi; + if(!mp3file_get_next_frame_info(self, &fi)) { + mp_raise_msg(&mp_type_RuntimeError, + translate("Failed to parse MP3 file")); + } + + self->sample_rate = fi.samprate; + self->channel_count = fi.nChans; + self->frame_buffer_size = fi.outputSamps*sizeof(int16_t); + self->len = 2 * self->frame_buffer_size; } void common_hal_audiomp3_mp3file_deinit(audiomp3_mp3file_obj_t* self) { @@ -280,7 +298,6 @@ audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t* channel = 0; } - *bufptr = (uint8_t*)(self->buffers[self->buffer_index] + channel); *buffer_length = self->frame_buffer_size; if (channel == self->other_channel) { @@ -289,11 +306,12 @@ audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t* return GET_BUFFER_MORE_DATA; } - self->other_channel = 1-channel; - self->other_buffer_index = self->buffer_index; self->buffer_index = !self->buffer_index; + self->other_channel = 1-channel; + self->other_buffer_index = self->buffer_index; int16_t *buffer = (int16_t *)(void *)self->buffers[self->buffer_index]; + *bufptr = (uint8_t*)buffer; mp3file_skip_id3v2(self); if (!mp3file_find_sync_word(self)) { @@ -322,3 +340,13 @@ void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t* self, bool si *spacing = 1; } } + +float common_hal_audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t* self) { + float sumsq = 0.f; + // Assumes no DC component to the audio. Is that a safe assumption? + int16_t *buffer = (int16_t *)(void *)self->buffers[self->buffer_index]; + for(size_t i=0; iframe_buffer_size / sizeof(int16_t); i++) { + sumsq += (float)buffer[i] * buffer[i]; + } + return sqrtf(sumsq) / (self->frame_buffer_size / sizeof(int16_t)); +} diff --git a/shared-module/audiomp3/MP3File.h b/shared-module/audiomp3/MP3Decoder.h similarity index 97% rename from shared-module/audiomp3/MP3File.h rename to shared-module/audiomp3/MP3Decoder.h index 9d99e8d1f04cc..9ee1d0949bbe4 100644 --- a/shared-module/audiomp3/MP3File.h +++ b/shared-module/audiomp3/MP3Decoder.h @@ -67,4 +67,6 @@ void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t* self, bool si bool* single_buffer, bool* samples_signed, uint32_t* max_buffer_length, uint8_t* spacing); +float audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t* self); + #endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MP3FILE_H pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy