mirror of
https://github.com/LegitCamper/picocalc-os-rs.git
synced 2025-12-27 07:45:28 +00:00
WIP
This commit is contained in:
19
Cargo.lock
generated
19
Cargo.lock
generated
@@ -828,6 +828,14 @@ dependencies = [
|
|||||||
"embedded-io-async",
|
"embedded-io-async",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-audio"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/LegitCamper/embedded-audio#087784644d810b94dd659a03dbed4795dfb0bd24"
|
||||||
|
dependencies = [
|
||||||
|
"heapless",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-graphics"
|
name = "embedded-graphics"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
@@ -1343,6 +1351,7 @@ dependencies = [
|
|||||||
"goblin",
|
"goblin",
|
||||||
"heapless",
|
"heapless",
|
||||||
"kolibri-embedded-gui",
|
"kolibri-embedded-gui",
|
||||||
|
"micromath",
|
||||||
"num_enum 0.7.4",
|
"num_enum 0.7.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"panic-probe",
|
"panic-probe",
|
||||||
@@ -2516,6 +2525,16 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wav_player"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"abi",
|
||||||
|
"embedded-audio",
|
||||||
|
"embedded-graphics",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ members = [
|
|||||||
"user-apps/calculator",
|
"user-apps/calculator",
|
||||||
"user-apps/snake",
|
"user-apps/snake",
|
||||||
"user-apps/gallery",
|
"user-apps/gallery",
|
||||||
|
"user-apps/wav_player",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
pub use abi_sys::{
|
||||||
|
AUDIO_BUFFER_LEN, audio_buffer_ready, file_len, get_key, list_dir, lock_display, print,
|
||||||
|
read_file, send_audio_buffer, sleep,
|
||||||
|
};
|
||||||
use abi_sys::{RngRequest, draw_iter, gen_rand};
|
use abi_sys::{RngRequest, draw_iter, gen_rand};
|
||||||
pub use abi_sys::{file_len, get_key, list_dir, lock_display, print, read_file, sleep};
|
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
pub use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers};
|
pub use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers};
|
||||||
use talc::*;
|
use talc::*;
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ pub enum CallAbiTable {
|
|||||||
ListDir = 6,
|
ListDir = 6,
|
||||||
ReadFile = 7,
|
ReadFile = 7,
|
||||||
FileLen = 8,
|
FileLen = 8,
|
||||||
|
AudioBufferReady = 9,
|
||||||
|
SendAudioBuffer = 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PrintAbi = extern "C" fn(ptr: *const u8, len: usize);
|
pub type PrintAbi = extern "C" fn(ptr: *const u8, len: usize);
|
||||||
@@ -145,3 +147,27 @@ pub fn file_len(file: &str) -> usize {
|
|||||||
f(file.as_ptr(), file.len())
|
f(file.as_ptr(), file.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type AudioBufferReady = extern "C" fn() -> bool;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn audio_buffer_ready() -> bool {
|
||||||
|
unsafe {
|
||||||
|
let ptr = CALL_ABI_TABLE[CallAbiTable::AudioBufferReady as usize];
|
||||||
|
let f: AudioBufferReady = core::mem::transmute(ptr);
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const AUDIO_BUFFER_LEN: usize = 1024;
|
||||||
|
|
||||||
|
pub type SendAudioBuffer = extern "C" fn(ptr: *const u8, len: usize);
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn send_audio_buffer(buf: &[u8; AUDIO_BUFFER_LEN]) {
|
||||||
|
unsafe {
|
||||||
|
let ptr = CALL_ABI_TABLE[CallAbiTable::SendAudioBuffer as usize];
|
||||||
|
let f: SendAudioBuffer = core::mem::transmute(ptr);
|
||||||
|
f(buf.as_ptr(), buf.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
1
justfile
1
justfile
@@ -13,3 +13,4 @@ userapps:
|
|||||||
just userapp calculator
|
just userapp calculator
|
||||||
just userapp snake
|
just userapp snake
|
||||||
just userapp gallery
|
just userapp gallery
|
||||||
|
just userapp wav_player
|
||||||
|
|||||||
@@ -94,3 +94,4 @@ bumpalo = "3.19.0"
|
|||||||
|
|
||||||
shared = { path = "../shared" }
|
shared = { path = "../shared" }
|
||||||
abi_sys = { path = "../abi_sys" }
|
abi_sys = { path = "../abi_sys" }
|
||||||
|
micromath = "2.1.0"
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
use abi_sys::{
|
use abi_sys::{
|
||||||
DrawIterAbi, FileLen, GenRand, GetKeyAbi, ListDir, LockDisplay, Modifiers, PrintAbi, ReadFile,
|
AUDIO_BUFFER_LEN, AudioBufferReady, DrawIterAbi, FileLen, GenRand, GetKeyAbi, ListDir,
|
||||||
RngRequest, SleepAbi,
|
LockDisplay, Modifiers, PrintAbi, ReadFile, RngRequest, SendAudioBuffer, SleepAbi,
|
||||||
};
|
};
|
||||||
use alloc::{string::ToString, vec::Vec};
|
use alloc::{string::ToString, vec::Vec};
|
||||||
use core::sync::atomic::Ordering;
|
use core::sync::atomic::Ordering;
|
||||||
use embassy_rp::clocks::{RoscRng, clk_sys_freq};
|
use embassy_rp::clocks::{RoscRng, clk_sys_freq};
|
||||||
use embedded_graphics::{Pixel, draw_target::DrawTarget, pixelcolor::Rgb565};
|
use embedded_graphics::{Pixel, draw_target::DrawTarget, pixelcolor::Rgb565};
|
||||||
use embedded_sdmmc::{DirEntry, LfnBuffer, ShortFileName};
|
use embedded_sdmmc::{DirEntry, LfnBuffer};
|
||||||
use heapless::spsc::Queue;
|
use heapless::spsc::Queue;
|
||||||
use shared::keyboard::KeyEvent;
|
use shared::keyboard::KeyEvent;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
audio::{AUDIO_BUFFER, AUDIO_BUFFER_READY},
|
||||||
display::{FB_PAUSED, FRAMEBUFFER},
|
display::{FB_PAUSED, FRAMEBUFFER},
|
||||||
storage::{Dir, File, SDCARD},
|
storage::{Dir, File, SDCARD},
|
||||||
};
|
};
|
||||||
@@ -181,7 +182,9 @@ pub extern "C" fn read_file(
|
|||||||
if !file.is_empty() {
|
if !file.is_empty() {
|
||||||
sd.access_root_dir(|root| {
|
sd.access_root_dir(|root| {
|
||||||
if let Ok(result) = recurse_file(&root, &file[1..], |file| {
|
if let Ok(result) = recurse_file(&root, &file[1..], |file| {
|
||||||
file.seek_from_start(start_from as u32).unwrap();
|
if file.offset() as usize != start_from {
|
||||||
|
file.seek_from_start(start_from as u32).unwrap();
|
||||||
|
}
|
||||||
file.read(&mut buf).unwrap()
|
file.read(&mut buf).unwrap()
|
||||||
}) {
|
}) {
|
||||||
read = result
|
read = result
|
||||||
@@ -210,3 +213,21 @@ pub extern "C" fn file_len(str: *const u8, len: usize) -> usize {
|
|||||||
}
|
}
|
||||||
len as usize
|
len as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _: AudioBufferReady = audio_buffer_ready;
|
||||||
|
pub extern "C" fn audio_buffer_ready() -> bool {
|
||||||
|
AUDIO_BUFFER_READY.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: SendAudioBuffer = send_audio_buffer;
|
||||||
|
pub extern "C" fn send_audio_buffer(ptr: *const u8, len: usize) {
|
||||||
|
// SAFETY: caller guarantees `ptr` is valid for `len` bytes
|
||||||
|
let buf = unsafe { core::slice::from_raw_parts(ptr, len) };
|
||||||
|
|
||||||
|
while !AUDIO_BUFFER_READY.load(Ordering::Acquire) {}
|
||||||
|
|
||||||
|
if buf.len() == AUDIO_BUFFER_LEN {
|
||||||
|
unsafe { AUDIO_BUFFER.copy_from_slice(buf) };
|
||||||
|
AUDIO_BUFFER_READY.store(false, Ordering::Release)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
52
kernel/src/audio.rs
Normal file
52
kernel/src/audio.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use core::{
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
use embassy_rp::{
|
||||||
|
Peri,
|
||||||
|
pio::Pio,
|
||||||
|
pio_programs::pwm::{PioPwm, PioPwmProgram},
|
||||||
|
pwm::{Config, Pwm, SetDutyCycle},
|
||||||
|
};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
|
||||||
|
use crate::{Audio, Irqs};
|
||||||
|
|
||||||
|
const AUDIO_BUFFER_LEN: usize = 1024;
|
||||||
|
const _: () = assert!(AUDIO_BUFFER_LEN == abi_sys::AUDIO_BUFFER_LEN);
|
||||||
|
pub static mut AUDIO_BUFFER: [u8; AUDIO_BUFFER_LEN] = [0; AUDIO_BUFFER_LEN];
|
||||||
|
static mut AUDIO_BUFFER_1: [u8; AUDIO_BUFFER_LEN] = [0; AUDIO_BUFFER_LEN];
|
||||||
|
|
||||||
|
pub static AUDIO_BUFFER_READY: AtomicBool = AtomicBool::new(true);
|
||||||
|
|
||||||
|
pub const SAMPLE_RATE_HZ: u32 = 22_050;
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn audio_handler(audio: Audio) {
|
||||||
|
let var_name = Pio::new(audio.pio_left, Irqs);
|
||||||
|
let Pio {
|
||||||
|
mut common, sm0, ..
|
||||||
|
} = var_name;
|
||||||
|
|
||||||
|
let prg = PioPwmProgram::new(&mut common);
|
||||||
|
let mut pwm_pio = PioPwm::new(&mut common, sm0, audio.left, &prg);
|
||||||
|
|
||||||
|
let period = Duration::from_nanos(1_000_000_000 / SAMPLE_RATE_HZ as u64);
|
||||||
|
pwm_pio.set_period(period);
|
||||||
|
|
||||||
|
pwm_pio.start();
|
||||||
|
|
||||||
|
let sample_interval = 1_000_000 / SAMPLE_RATE_HZ as u64; // in µs ≈ 45 µs
|
||||||
|
|
||||||
|
loop {
|
||||||
|
for &sample in unsafe { &AUDIO_BUFFER }.iter() {
|
||||||
|
let period_ns = period.as_nanos() as u32;
|
||||||
|
let duty_ns = period_ns * (sample as u32) / 255;
|
||||||
|
pwm_pio.write(Duration::from_nanos(duty_ns as u64));
|
||||||
|
Timer::after_micros(sample_interval).await; // sample interval = 1 / sample rate
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { core::mem::swap(&mut AUDIO_BUFFER, &mut AUDIO_BUFFER_1) };
|
||||||
|
AUDIO_BUFFER_READY.store(true, Ordering::Release)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -204,6 +204,8 @@ fn patch_abi(
|
|||||||
CallAbiTable::ListDir => abi::list_dir as usize,
|
CallAbiTable::ListDir => abi::list_dir as usize,
|
||||||
CallAbiTable::ReadFile => abi::read_file as usize,
|
CallAbiTable::ReadFile => abi::read_file as usize,
|
||||||
CallAbiTable::FileLen => abi::file_len as usize,
|
CallAbiTable::FileLen => abi::file_len as usize,
|
||||||
|
CallAbiTable::AudioBufferReady => abi::audio_buffer_ready as usize,
|
||||||
|
CallAbiTable::SendAudioBuffer => abi::send_audio_buffer as usize,
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
table_base.add(idx as usize).write(ptr);
|
table_base.add(idx as usize).write(ptr);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
mod abi;
|
mod abi;
|
||||||
|
mod audio;
|
||||||
mod display;
|
mod display;
|
||||||
mod elf;
|
mod elf;
|
||||||
mod framebuffer;
|
mod framebuffer;
|
||||||
@@ -19,6 +20,7 @@ mod utils;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abi::KEY_CACHE,
|
abi::KEY_CACHE,
|
||||||
|
audio::audio_handler,
|
||||||
display::{FRAMEBUFFER, display_handler, init_display},
|
display::{FRAMEBUFFER, display_handler, init_display},
|
||||||
peripherals::{
|
peripherals::{
|
||||||
conf_peripherals,
|
conf_peripherals,
|
||||||
@@ -48,8 +50,10 @@ use embassy_rp::{
|
|||||||
multicore::{Stack, spawn_core1},
|
multicore::{Stack, spawn_core1},
|
||||||
peripherals::{
|
peripherals::{
|
||||||
DMA_CH0, DMA_CH1, I2C1, PIN_6, PIN_7, PIN_10, PIN_11, PIN_12, PIN_13, PIN_14, PIN_15,
|
DMA_CH0, DMA_CH1, I2C1, PIN_6, PIN_7, PIN_10, PIN_11, PIN_12, PIN_13, PIN_14, PIN_15,
|
||||||
PIN_16, PIN_17, PIN_18, PIN_19, PIN_22, SPI0, SPI1, USB,
|
PIN_16, PIN_17, PIN_18, PIN_19, PIN_22, PIN_26, PIN_27, PIO0, PIO1, PWM_SLICE5, SPI0, SPI1,
|
||||||
|
USB,
|
||||||
},
|
},
|
||||||
|
pio,
|
||||||
spi::{self, Spi},
|
spi::{self, Spi},
|
||||||
usb as embassy_rp_usb,
|
usb as embassy_rp_usb,
|
||||||
};
|
};
|
||||||
@@ -65,6 +69,7 @@ use talc::*;
|
|||||||
embassy_rp::bind_interrupts!(struct Irqs {
|
embassy_rp::bind_interrupts!(struct Irqs {
|
||||||
I2C1_IRQ => i2c::InterruptHandler<I2C1>;
|
I2C1_IRQ => i2c::InterruptHandler<I2C1>;
|
||||||
USBCTRL_IRQ => embassy_rp_usb::InterruptHandler<USB>;
|
USBCTRL_IRQ => embassy_rp_usb::InterruptHandler<USB>;
|
||||||
|
PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
|
||||||
});
|
});
|
||||||
|
|
||||||
static mut CORE1_STACK: Stack<16384> = Stack::new();
|
static mut CORE1_STACK: Stack<16384> = Stack::new();
|
||||||
@@ -105,6 +110,12 @@ async fn main(_spawner: Spawner) {
|
|||||||
data: p.PIN_14,
|
data: p.PIN_14,
|
||||||
reset: p.PIN_15,
|
reset: p.PIN_15,
|
||||||
};
|
};
|
||||||
|
let audio = Audio {
|
||||||
|
pio_left: p.PIO0,
|
||||||
|
pio_right: p.PIO1,
|
||||||
|
left: p.PIN_26,
|
||||||
|
right: p.PIN_27,
|
||||||
|
};
|
||||||
let sd = Sd {
|
let sd = Sd {
|
||||||
spi: p.SPI0,
|
spi: p.SPI0,
|
||||||
clk: p.PIN_18,
|
clk: p.PIN_18,
|
||||||
@@ -119,7 +130,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
data: p.PIN_6,
|
data: p.PIN_6,
|
||||||
};
|
};
|
||||||
let executor0 = EXECUTOR0.init(Executor::new());
|
let executor0 = EXECUTOR0.init(Executor::new());
|
||||||
executor0.run(|spawner| unwrap!(spawner.spawn(kernel_task(spawner, display, sd, mcu, p.USB))));
|
executor0.run(|spawner| {
|
||||||
|
unwrap!(spawner.spawn(kernel_task(spawner, display, audio, sd, mcu, p.USB)))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// One-slot channel to pass EntryFn from core1
|
// One-slot channel to pass EntryFn from core1
|
||||||
@@ -168,6 +181,12 @@ struct Display {
|
|||||||
data: Peri<'static, PIN_14>,
|
data: Peri<'static, PIN_14>,
|
||||||
reset: Peri<'static, PIN_15>,
|
reset: Peri<'static, PIN_15>,
|
||||||
}
|
}
|
||||||
|
struct Audio {
|
||||||
|
pio_left: Peri<'static, PIO0>,
|
||||||
|
pio_right: Peri<'static, PIO1>,
|
||||||
|
left: Peri<'static, PIN_26>,
|
||||||
|
right: Peri<'static, PIN_27>,
|
||||||
|
}
|
||||||
struct Sd {
|
struct Sd {
|
||||||
spi: Peri<'static, SPI0>,
|
spi: Peri<'static, SPI0>,
|
||||||
clk: Peri<'static, PIN_18>,
|
clk: Peri<'static, PIN_18>,
|
||||||
@@ -225,6 +244,7 @@ async fn setup_sd(sd: Sd) {
|
|||||||
async fn kernel_task(
|
async fn kernel_task(
|
||||||
spawner: Spawner,
|
spawner: Spawner,
|
||||||
display: Display,
|
display: Display,
|
||||||
|
audio: Audio,
|
||||||
sd: Sd,
|
sd: Sd,
|
||||||
mcu: Mcu,
|
mcu: Mcu,
|
||||||
usb: Peri<'static, USB>,
|
usb: Peri<'static, USB>,
|
||||||
@@ -234,6 +254,8 @@ async fn kernel_task(
|
|||||||
setup_display(display, spawner).await;
|
setup_display(display, spawner).await;
|
||||||
setup_sd(sd).await;
|
setup_sd(sd).await;
|
||||||
|
|
||||||
|
spawner.spawn(audio_handler(audio)).unwrap();
|
||||||
|
|
||||||
let _usb = embassy_rp_usb::Driver::new(usb, Irqs);
|
let _usb = embassy_rp_usb::Driver::new(usb, Irqs);
|
||||||
// spawner.spawn(usb_handler(usb)).unwrap();
|
// spawner.spawn(usb_handler(usb)).unwrap();
|
||||||
|
|
||||||
|
|||||||
10
user-apps/wav_player/Cargo.toml
Normal file
10
user-apps/wav_player/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "wav_player"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
abi = { path = "../../abi" }
|
||||||
|
embedded-graphics = "0.8.1"
|
||||||
|
rand = { version = "0.9.0", default-features = false }
|
||||||
|
embedded-audio = { git = "https://github.com/LegitCamper/embedded-audio" }
|
||||||
28
user-apps/wav_player/build.rs
Normal file
28
user-apps/wav_player/build.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//! This build script copies the `memory.x` file from the crate root into
|
||||||
|
//! a directory where the linker can always find it at build time.
|
||||||
|
//! For many projects this is optional, as the linker always searches the
|
||||||
|
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||||
|
//! are using a workspace or have a more complicated build setup, this
|
||||||
|
//! build script becomes required. Additionally, by requesting that
|
||||||
|
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||||
|
//! updating `memory.x` ensures a rebuild of the application with the
|
||||||
|
//! new memory settings.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put `memory.x` in our output directory and ensure it's
|
||||||
|
// on the linker search path.
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("memory.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("../memory.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tmemory.x");
|
||||||
|
}
|
||||||
92
user-apps/wav_player/src/main.rs
Normal file
92
user-apps/wav_player/src/main.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
use abi::{
|
||||||
|
AUDIO_BUFFER_LEN, KeyCode, KeyState, Rng, audio_buffer_ready,
|
||||||
|
display::{Display, SCREEN_HEIGHT, SCREEN_WIDTH},
|
||||||
|
file_len, get_key, lock_display, print, read_file, send_audio_buffer, sleep,
|
||||||
|
};
|
||||||
|
use alloc::{format, string::String};
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use embedded_audio::{AudioFile, PlatformFile, PlatformFileError, wav::Wav};
|
||||||
|
use embedded_graphics::{pixelcolor::Rgb565, prelude::RgbColor};
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
print(&format!(
|
||||||
|
"user panic: {} @ {:?}",
|
||||||
|
info.message(),
|
||||||
|
info.location(),
|
||||||
|
));
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "Rust" fn _start() {
|
||||||
|
main()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
print("Starting Wav player app");
|
||||||
|
let mut display = Display;
|
||||||
|
|
||||||
|
let mut buf = [0_u8; AUDIO_BUFFER_LEN];
|
||||||
|
|
||||||
|
let file = File::new(String::from("/music/test.wav"));
|
||||||
|
let mut wav = Wav::new(file).unwrap();
|
||||||
|
loop {
|
||||||
|
if audio_buffer_ready() {
|
||||||
|
if wav.is_eof() {
|
||||||
|
wav.restart().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
wav.read(&mut buf).unwrap();
|
||||||
|
send_audio_buffer(&buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct File {
|
||||||
|
current_pos: usize,
|
||||||
|
file: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
fn new(file: String) -> Self {
|
||||||
|
Self {
|
||||||
|
current_pos: 0,
|
||||||
|
file,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformFile for File {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, PlatformFileError> {
|
||||||
|
let read = read_file(&self.file, self.current_pos, buf);
|
||||||
|
Ok(read)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seek_from_current(&mut self, offset: i64) -> Result<(), PlatformFileError> {
|
||||||
|
if offset.is_positive() {
|
||||||
|
self.current_pos += offset as usize;
|
||||||
|
} else {
|
||||||
|
self.current_pos -= offset as usize;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seek_from_start(&mut self, offset: usize) -> Result<(), PlatformFileError> {
|
||||||
|
self.current_pos = offset;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seek_from_end(&mut self, offset: usize) -> Result<(), PlatformFileError> {
|
||||||
|
self.current_pos = self.length() - offset;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn length(&mut self) -> usize {
|
||||||
|
file_len(&self.file)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user