3 Commits

Author SHA1 Message Date
b20439e1d7 fix readme 2025-11-19 11:12:37 -07:00
50341507cd allow userapps to change sample rate of audio 2025-11-19 11:02:44 -07:00
cf8c59f021 cargo clippy 2025-11-19 10:31:34 -07:00
5 changed files with 64 additions and 14 deletions

View File

@@ -14,7 +14,7 @@ Current focus is on exanding applications and porting software, finding bugs in
- **`userlib_sys/`** C FFI bindings for kernel syscall
- **`userlib/`** Rust wrapper on top of `userlib_sys`
- **`picolib/`** Built with ```just newlib```, and provides libc symbols when linking with C libraries
- **`user-apps/`** Collection of userspace programs (gif player, wav player, calculator, snake, etc.)
- **`user_apps/`** Collection of userspace programs (gif player, wav player, calculator, snake, etc.)
## Features

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)]
@@ -217,7 +218,7 @@ pub mod keyboard {
1 => KeyState::Pressed,
2 => KeyState::Hold,
3 => KeyState::Released,
0 | _ => KeyState::Idle,
_ => KeyState::Idle,
}
}
}
@@ -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)]