Files
tonberry-pico/software/modules/audiocore/audiocore.c
Matthias Blankertz 7f8282315e
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 5m29s
Check code formatting / Check-C-Format (push) Successful in 9s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 4s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Restructure sources
The python and C modules that are supposed to be built into the firmware
image (i.e. those that are in manifest.py or in USER_C_MODULES) have
been moved to the software/modules directory.

The software/src directory should now only contain python scripts and
other files that should be installed to the Picos flash filesystem. The
idea is that these should be those scripts that implement the
application behaviour, as these are the ones that a user who does not
want to build the whole firmware themself wants to modify.
2025-04-01 22:05:30 +02:00

118 lines
3.2 KiB
C

// SPDX-License-Identifier: MIT
// Copyright (c) 2024-2025 Matthias Blankertz <matthias@blankertz.org>
#include "audiocore.h"
#include "i2s.h"
#include "mp3.h"
#include "py/mperrno.h"
_Static_assert(MP3_FRAME_SIZE == I2S_DMA_BUF_SIZE, "i2s buffer size must match MP3 frame size");
void __time_critical_func(volume_adjust)(int16_t *buf, size_t samples, uint16_t scalef)
{
for (size_t pos = 0; pos < samples; ++pos) {
buf[pos] = ((int32_t)buf[pos] * scalef) >> 15;
}
}
static void __time_critical_func(send_fifo_response)(uint32_t data)
{
multicore_fifo_push_blocking(AUDIOCORE_FIFO_DATA_FLAG | data);
}
#ifndef UNIT_TESTING
static void __time_critical_func(send_consume_notify)(void)
{
if (multicore_fifo_wready())
sio_hw->fifo_wr = 0;
}
#else
// Don't do optimization with raw HW access in unit test environment
static void send_consume_notify(void)
{
if (multicore_fifo_wready())
multicore_fifo_push_blocking(0);
}
#endif
void __time_critical_func(core1_main)(void)
{
uint32_t ret = 0;
bool running = true, playing = false;
if (!i2s_init(shared_context.out_pin, shared_context.sideset_base)) {
ret = MP_EIO;
goto out;
}
if (!mp3_init()) {
ret = MP_ENOMEM;
goto out_i2s;
}
send_fifo_response(0);
uint32_t current_volume = AUDIOCORE_MAX_VOLUME >> 4;
bool flushing = false;
while (running) {
uint32_t cmd;
uint32_t *buf;
if (multicore_fifo_rvalid()) {
cmd = multicore_fifo_pop_blocking();
switch (cmd) {
case AUDIOCORE_CMD_SHUTDOWN:
running = false;
break;
case AUDIOCORE_CMD_SET_VOLUME: {
const uint32_t new_volume = multicore_fifo_pop_blocking();
if (new_volume > AUDIOCORE_MAX_VOLUME) {
send_fifo_response(1);
} else {
current_volume = new_volume;
send_fifo_response(0);
}
} break;
case AUDIOCORE_CMD_FLUSH:
if (playing) {
flushing = true;
} else {
send_fifo_response(0);
}
break;
default:
break;
}
}
if ((buf = i2s_next_buf()) != NULL) {
unsigned samplerate;
// decode one frame
if (mp3_decode(buf, &samplerate)) {
if (!playing) {
i2s_play(samplerate);
playing = true;
}
volume_adjust((int16_t *)buf, MP3_FRAME_SIZE * 2, current_volume);
i2s_commit_buf(buf);
send_consume_notify();
continue;
}
/* mp3_decode returned false: not enough data in buffer */
if (flushing) {
mp3_reset();
i2s_stop();
playing = false;
flushing = false;
send_fifo_response(0);
} else {
send_consume_notify();
}
}
__wfe();
}
mp3_deinit();
out_i2s:
i2s_deinit();
out:
send_fifo_response(ret);
}