Files
nes-emu/src/audio.rs
Matthew Pomes b9a30c286a
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 10s
Partial audio implementation
2026-03-14 21:19:16 -05:00

95 lines
2.8 KiB
Rust

use std::{
sync::{
atomic::AtomicBool, mpsc::{channel, Sender, TryRecvError}, Arc
},
time::Instant,
};
use cpal::{
Device, FrameCount, Host, SampleFormat, Stream,
traits::{DeviceTrait, HostTrait, StreamTrait},
};
use ringbuf::{
HeapRb, SharedRb,
traits::{Consumer, Observer, Producer, Split},
wrap::caching::Caching,
};
type SampleTx = Caching<Arc<HeapRb<u8>>, true, false>;
// TODO: Audio should be run through a low-pass filter,
// a high-pass filter, as well as some kind of envelope
// around pause events
pub struct Audio {
_host: Host,
_device: Device,
_stream: Stream,
rb: SampleTx,
last: usize,
max: usize,
paused: Arc<AtomicBool>,
}
impl Audio {
pub fn init() -> Self {
const BUFFER_SIZE: usize = 1 << 10;
let host = cpal::default_host();
let device = host.default_output_device().unwrap();
// let mut configs = device.supported_output_configs().unwrap();
// dbg!(configs.find(|c| c.sample_format() == SampleFormat::U8));
// let v = dbg!(configs.next().unwrap().buffer_size());
// let mut t = 0;
let (prod, mut cons) = SharedRb::new(BUFFER_SIZE * 1024 * 1024).split();
let paused = Arc::new(AtomicBool::new(true));
let paused_inner = Arc::clone(&paused);
let stream = device
.build_output_stream(
&cpal::StreamConfig {
channels: 1,
sample_rate: 60 * 3723,
buffer_size: cpal::BufferSize::Fixed(BUFFER_SIZE as FrameCount),
},
move |a: &mut [u8], _b| {
if !paused_inner.load(std::sync::atomic::Ordering::Acquire) {
let taken = cons.pop_slice(a);
a[taken..].fill(128);
}
},
|e| eprintln!("Audio: {e}"),
None,
)
.unwrap();
stream.play().unwrap();
Self {
_host: host,
_device: device,
_stream: stream,
rb: prod,
paused,
last: 0,
max: 0,
}
}
pub fn pause(&mut self) {
self.paused.store(true, std::sync::atomic::Ordering::Release);
}
pub fn submit(&mut self, samples: &[u8]) {
let start = self.rb.occupied_len();
self.max = self.max.max(self.last - start);
println!("Buffer size: {:07}, Max: {:07}", start, self.max);
println!(
"Adding: {:07}, Played: {:07}",
samples.len(),
self.last - start
);
self.rb.push_slice(samples);
self.last = self.rb.occupied_len();
if self.last > 9000 {
self.paused.store(false, std::sync::atomic::Ordering::Release);
}
}
}