diff --git a/software/modules/audiocore/audiocore.py b/software/modules/audiocore/audiocore.py index eeb3354..ea7dd11 100644 --- a/software/modules/audiocore/audiocore.py +++ b/software/modules/audiocore/audiocore.py @@ -2,7 +2,8 @@ # Copyright (c) 2025 Matthias Blankertz import _audiocore -from asyncio import ThreadSafeFlag +import asyncio +from asyncio import Lock, ThreadSafeFlag from utils import get_pin_index @@ -11,19 +12,34 @@ class Audiocore: # PIO requires sideset pins to be adjacent assert get_pin_index(lrclk) == get_pin_index(dclk)+1 or get_pin_index(lrclk) == get_pin_index(dclk)-1 self.notify = ThreadSafeFlag() + self.audiocore_lock = Lock() self._audiocore = _audiocore.Audiocore(din, dclk, lrclk, self._interrupt) def deinit(self): + assert not self.audiocore_lock.locked() self._audiocore.deinit() def _interrupt(self, _): self.notify.set() def flush(self): + assert not self.audiocore_lock.locked() self._audiocore.flush() + async def async_flush(self): + async with self.audiocore_lock: + self._audiocore.flush(False) + while True: + if self._audiocore.get_async_result() is not None: + return + await self.notify.wait() + + async def async_set_volume(self, volume): + async with self.audiocore_lock: + self._audiocore.set_volume(volume) + def set_volume(self, volume): - self._audiocore.set_volume(volume) + asyncio.create_task(self.async_set_volume(volume)) def put(self, buffer, blocking=False): pos = 0 diff --git a/software/modules/audiocore/module.c b/software/modules/audiocore/module.c index c2cdbcb..881c65d 100644 --- a/software/modules/audiocore/module.c +++ b/software/modules/audiocore/module.c @@ -5,6 +5,8 @@ // Include MicroPython API. #include "py/mperrno.h" +#include "py/obj.h" +#include "py/persistentcode.h" #include "py/runtime.h" #include "shared/runtime/mpirq.h" @@ -61,6 +63,20 @@ static uint32_t get_fifo_read_value_blocking(struct audiocore_obj *obj) } } +static bool get_fifo_read_value(struct audiocore_obj *obj, uint32_t *result) +{ + const long flags = save_and_disable_interrupts(); + const uint32_t value = obj->fifo_read_value; + obj->fifo_read_value = 0; + if (value & AUDIOCORE_FIFO_DATA_FLAG) { + restore_interrupts(flags); + *result = value & ~AUDIOCORE_FIFO_DATA_FLAG; + return true; + } + restore_interrupts(flags); + return false; +} + /* * audiocore.Context.deinit(self) * @@ -125,15 +141,31 @@ static MP_DEFINE_CONST_FUN_OBJ_2(audiocore_put_obj, audiocore_put); * Tells the audiocore to stop playback as soon as all MP3 frames still in the buffer have been decoded. * This function blocks until playback has ended. */ -static mp_obj_t audiocore_flush(mp_obj_t self_in) +static mp_obj_t audiocore_flush(size_t n_args, const mp_obj_t *args) { - struct audiocore_obj *self = MP_OBJ_TO_PTR(self_in); + struct audiocore_obj *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_t blocking = MP_ROM_TRUE; + if (n_args == 2) { + blocking = args[1]; + } multicore_fifo_push_blocking(AUDIOCORE_CMD_FLUSH); wake_core1(); - get_fifo_read_value_blocking(self); + if (mp_obj_is_true(blocking)) + get_fifo_read_value_blocking(self); return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_1(audiocore_flush_obj, audiocore_flush); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiocore_flush_obj, 1, 2, audiocore_flush); + +static mp_obj_t audiocore_get_async_result(mp_obj_t self_in) +{ + uint32_t result; + struct audiocore_obj *self = MP_OBJ_TO_PTR(self_in); + if (get_fifo_read_value(self, &result)) { + return mp_obj_new_int(result); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(audiocore_get_async_result_obj, audiocore_get_async_result); /* * audiocore.set_volume(self, volume) @@ -240,6 +272,7 @@ static const mp_rom_map_elem_t audiocore_locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiocore_deinit_obj)}, {MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&audiocore_put_obj)}, {MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&audiocore_flush_obj)}, + {MP_ROM_QSTR(MP_QSTR_get_async_result), MP_ROM_PTR(&audiocore_get_async_result_obj)}, {MP_ROM_QSTR(MP_QSTR_set_volume), MP_ROM_PTR(&audiocore_set_volume_obj)}, }; static MP_DEFINE_CONST_DICT(audiocore_locals_dict, audiocore_locals_dict_table); diff --git a/software/src/mp3player.py b/software/src/mp3player.py index f62e156..1682fd7 100644 --- a/software/src/mp3player.py +++ b/software/src/mp3player.py @@ -74,7 +74,7 @@ class MP3Player: # Call onPlaybackDone after flush send_done = True finally: - self.audiocore.flush() + await self.audiocore.async_flush() if send_done: # Only call onPlaybackDone if exit due to end of stream # Use timer with time 0 to call callback "immediately" but from a different task