1 Commits

Author SHA1 Message Date
93e9aea368 wip: async flush
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m43s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 11s
2025-12-27 15:46:07 +01:00
3 changed files with 56 additions and 7 deletions

View File

@@ -2,7 +2,8 @@
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org> # Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
import _audiocore import _audiocore
from asyncio import ThreadSafeFlag import asyncio
from asyncio import Lock, ThreadSafeFlag
from utils import get_pin_index from utils import get_pin_index
@@ -11,19 +12,34 @@ class Audiocore:
# PIO requires sideset pins to be adjacent # 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 assert get_pin_index(lrclk) == get_pin_index(dclk)+1 or get_pin_index(lrclk) == get_pin_index(dclk)-1
self.notify = ThreadSafeFlag() self.notify = ThreadSafeFlag()
self.audiocore_lock = Lock()
self._audiocore = _audiocore.Audiocore(din, dclk, lrclk, self._interrupt) self._audiocore = _audiocore.Audiocore(din, dclk, lrclk, self._interrupt)
def deinit(self): def deinit(self):
assert not self.audiocore_lock.locked()
self._audiocore.deinit() self._audiocore.deinit()
def _interrupt(self, _): def _interrupt(self, _):
self.notify.set() self.notify.set()
def flush(self): def flush(self):
assert not self.audiocore_lock.locked()
self._audiocore.flush() 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): def set_volume(self, volume):
self._audiocore.set_volume(volume) asyncio.create_task(self.async_set_volume(volume))
def put(self, buffer, blocking=False): def put(self, buffer, blocking=False):
pos = 0 pos = 0

View File

@@ -5,6 +5,8 @@
// Include MicroPython API. // Include MicroPython API.
#include "py/mperrno.h" #include "py/mperrno.h"
#include "py/obj.h"
#include "py/persistentcode.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "shared/runtime/mpirq.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) * 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. * 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. * 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); multicore_fifo_push_blocking(AUDIOCORE_CMD_FLUSH);
wake_core1(); wake_core1();
get_fifo_read_value_blocking(self); if (mp_obj_is_true(blocking))
get_fifo_read_value_blocking(self);
return mp_const_none; 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) * 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_deinit), MP_ROM_PTR(&audiocore_deinit_obj)},
{MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&audiocore_put_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_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)}, {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); static MP_DEFINE_CONST_DICT(audiocore_locals_dict, audiocore_locals_dict_table);

View File

@@ -74,7 +74,7 @@ class MP3Player:
# Call onPlaybackDone after flush # Call onPlaybackDone after flush
send_done = True send_done = True
finally: finally:
self.audiocore.flush() await self.audiocore.async_flush()
if send_done: if send_done:
# Only call onPlaybackDone if exit due to end of stream # Only call onPlaybackDone if exit due to end of stream
# Use timer with time 0 to call callback "immediately" but from a different task # Use timer with time 0 to call callback "immediately" but from a different task