allow userapps to change sample rate of audio

This commit is contained in:
2025-11-19 11:02:44 -07:00
parent cf8c59f021
commit 50341507cd
4 changed files with 62 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
use crate::Audio;
use core::sync::atomic::{AtomicBool, Ordering};
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use embassy_futures::{join::join, yield_now};
use embassy_rp::{
Peri,
@@ -23,9 +23,12 @@ const SILENCE: u8 = u8::MAX / 2;
pub static mut AUDIO_BUFFER: [u8; AUDIO_BUFFER_SAMPLES * 2] = [SILENCE; AUDIO_BUFFER_SAMPLES * 2];
static mut AUDIO_BUFFER_1: [u8; AUDIO_BUFFER_SAMPLES * 2] = [SILENCE; AUDIO_BUFFER_SAMPLES * 2];
// atomics for user applications to signal changes to audio buffers
pub static AUDIO_BUFFER_READY: AtomicBool = AtomicBool::new(true);
pub static AUDIO_BUFFER_WRITTEN: AtomicBool = AtomicBool::new(false);
pub static AUDIO_BUFFER_SAMPLE_RATE: AtomicU32 = AtomicU32::new(SAMPLE_RATE_HZ);
/// resets audio buffers after user applications are unloaded
pub fn clear_audio_buffers() {
unsafe {
AUDIO_BUFFER.fill(SILENCE);
@@ -41,8 +44,29 @@ pub async fn audio_handler(mut audio: Audio) {
let mut pwm_pio_right =
PioPwmAudio::new(audio.dma1, &mut audio.pio, audio.sm1, audio.right, &prg);
// enable sms at the same time to ensure they are synced
audio.pio.apply_sm_batch(|pio| {
pio.set_enable(&mut pwm_pio_right.sm, true);
pio.set_enable(&mut pwm_pio_left.sm, true);
});
let mut sample_rate = SAMPLE_RATE_HZ;
loop {
unsafe {
let new_sample_rate = AUDIO_BUFFER_SAMPLE_RATE.load(Ordering::Acquire);
if new_sample_rate != sample_rate {
sample_rate = new_sample_rate;
pwm_pio_left.reconfigure(sample_rate);
pwm_pio_right.reconfigure(sample_rate);
// restart sms at the same time to ensure they are synced
audio.pio.apply_sm_batch(|pio| {
pio.restart(&mut pwm_pio_right.sm);
pio.restart(&mut pwm_pio_left.sm);
});
}
if AUDIO_BUFFER_WRITTEN.load(Ordering::Acquire) {
write_samples(&mut pwm_pio_left, &mut pwm_pio_right, &AUDIO_BUFFER_1).await;
AUDIO_BUFFER_1.fill(SILENCE);
@@ -130,6 +154,11 @@ struct PioPwmAudio<'d, PIO: Instance, const SM: usize> {
}
impl<'d, PIO: Instance, const SM: usize> PioPwmAudio<'d, PIO, SM> {
fn get_sm_divider(sample_rate: u32) -> u32 {
let target_clock = (u8::MAX as u32 + 1) * sample_rate;
clk_sys_freq() / target_clock
}
fn new(
dma: Peri<'d, impl Channel>,
pio: &mut Common<'d, PIO>,
@@ -151,17 +180,18 @@ impl<'d, PIO: Instance, const SM: usize> PioPwmAudio<'d, PIO, SM> {
cfg.use_program(&prg.0, &[&pin]);
sm.set_config(&cfg);
let target_clock = (u8::MAX as u32 + 1) * SAMPLE_RATE_HZ;
let divider = (clk_sys_freq() / target_clock).to_fixed();
sm.set_clock_divider(divider);
sm.set_enable(true);
sm.set_clock_divider(Self::get_sm_divider(SAMPLE_RATE_HZ).to_fixed());
Self {
dma: dma.into(),
sm,
}
}
fn reconfigure(&mut self, sample_rate: u32) {
self.sm
.set_clock_divider(Self::get_sm_divider(sample_rate).to_fixed());
}
}
/// packs two u8 samples into 32bit word

View File

@@ -204,6 +204,9 @@ fn patch_syscalls(
SyscallTable::ReadFile => syscalls::read_file as usize,
SyscallTable::WriteFile => syscalls::write_file as usize,
SyscallTable::FileLen => syscalls::file_len as usize,
SyscallTable::ReconfigureAudioSampleRate => {
syscalls::reconfigure_audio_sample_rate as usize
}
SyscallTable::AudioBufferReady => syscalls::audio_buffer_ready as usize,
SyscallTable::SendAudioBuffer => syscalls::send_audio_buffer as usize,
};

View File

@@ -7,8 +7,8 @@ use embedded_sdmmc::LfnBuffer;
use heapless::spsc::Queue;
use userlib_sys::{
AUDIO_BUFFER_SAMPLES, Alloc, AudioBufferReady, CLayout, CPixel, Dealloc, DrawIter, FileLen,
GenRand, GetMs, ListDir, Print, ReadFile, RngRequest, SendAudioBuffer, SleepMs, WriteFile,
keyboard::*,
GenRand, GetMs, ListDir, Print, ReadFile, ReconfigureAudioSampleRate, RngRequest,
SendAudioBuffer, SleepMs, WriteFile, keyboard::*,
};
#[cfg(feature = "psram")]
@@ -18,7 +18,7 @@ use crate::heap::HEAP;
use core::alloc::GlobalAlloc;
use crate::{
audio::{AUDIO_BUFFER, AUDIO_BUFFER_READY, AUDIO_BUFFER_WRITTEN},
audio::{AUDIO_BUFFER, AUDIO_BUFFER_READY, AUDIO_BUFFER_SAMPLE_RATE, AUDIO_BUFFER_WRITTEN},
display::FRAMEBUFFER,
framebuffer::FB_PAUSED,
storage::{Dir, File, SDCARD},
@@ -333,6 +333,11 @@ pub extern "C" fn file_len(str: *const u8, len: usize) -> usize {
len as usize
}
const _: ReconfigureAudioSampleRate = reconfigure_audio_sample_rate;
pub extern "C" fn reconfigure_audio_sample_rate(sample_rate: u32) {
AUDIO_BUFFER_SAMPLE_RATE.store(sample_rate, Ordering::Release);
}
const _: AudioBufferReady = audio_buffer_ready;
pub extern "C" fn audio_buffer_ready() -> bool {
AUDIO_BUFFER_READY.load(Ordering::Acquire)

View File

@@ -12,7 +12,7 @@ use strum::{EnumCount, EnumIter};
pub type EntryFn = fn();
pub const SYS_CALL_TABLE_COUNT: usize = 14;
pub const SYS_CALL_TABLE_COUNT: usize = 15;
const _: () = assert!(SYS_CALL_TABLE_COUNT == SyscallTable::COUNT);
#[derive(Clone, Copy, EnumIter, EnumCount)]
@@ -30,8 +30,9 @@ pub enum SyscallTable {
ReadFile = 9,
WriteFile = 10,
FileLen = 11,
AudioBufferReady = 12,
SendAudioBuffer = 13,
ReconfigureAudioSampleRate = 12,
AudioBufferReady = 13,
SendAudioBuffer = 14,
}
#[unsafe(no_mangle)]
@@ -473,6 +474,17 @@ pub extern "C" fn file_len(str: *const u8, len: usize) -> usize {
}
}
pub type ReconfigureAudioSampleRate = extern "C" fn(sample_rate: u32);
#[allow(unused)]
pub fn reconfigure_audio_sample_rate(sample_rate: u32) {
unsafe {
let ptr = SYS_CALL_TABLE[SyscallTable::ReconfigureAudioSampleRate as usize];
let f: ReconfigureAudioSampleRate = core::mem::transmute(ptr);
f(sample_rate)
}
}
pub type AudioBufferReady = extern "C" fn() -> bool;
#[allow(unused)]