diff --git a/Cargo.lock b/Cargo.lock index 2700d23..3e93d36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ version = "0.1.0" dependencies = [ "clap", "clap_derive", + "cpal", "pixels", "rand", "tao", @@ -42,6 +43,28 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alsa" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +dependencies = [ + "alsa-sys", + "bitflags 2.8.0", + "cfg-if", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -145,6 +168,24 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.8.0", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.98", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -233,6 +274,8 @@ version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -242,6 +285,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-expr" version = "0.15.8" @@ -264,6 +316,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.6", +] + [[package]] name = "clap" version = "4.5.28" @@ -452,6 +515,49 @@ dependencies = [ "libc", ] +[[package]] +name = "coreaudio-rs" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +dependencies = [ + "bitflags 1.3.2", + "core-foundation-sys", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce857aa0b77d77287acc1ac3e37a05a8c95a2af3647d23b15f263bdaeb7562b" +dependencies = [ + "bindgen", +] + +[[package]] +name = "cpal" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" +dependencies = [ + "alsa", + "core-foundation-sys", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", + "libc", + "mach2", + "ndk 0.8.0", + "ndk-context", + "oboe", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.54.0", +] + [[package]] name = "crossbeam-channel" version = "0.5.14" @@ -478,6 +584,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + [[package]] name = "dispatch" version = "0.2.0" @@ -524,6 +636,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" @@ -826,6 +944,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "glow" version = "0.13.1" @@ -1166,6 +1290,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "jni" version = "0.21.1" @@ -1188,6 +1321,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -1269,6 +1411,15 @@ version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1317,6 +1468,12 @@ dependencies = [ "paste", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "naga" version = "0.19.2" @@ -1337,6 +1494,20 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.8.0", + "jni-sys", + "log", + "ndk-sys 0.5.0+25.2.9519653", + "num_enum", + "thiserror", +] + [[package]] name = "ndk" version = "0.9.0" @@ -1376,6 +1547,16 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1386,6 +1567,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1435,6 +1627,29 @@ dependencies = [ "cc", ] +[[package]] +name = "oboe" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" +dependencies = [ + "jni", + "ndk 0.8.0", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" +dependencies = [ + "cc", +] + [[package]] name = "once_cell" version = "1.20.3" @@ -1943,7 +2158,7 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk", + "ndk 0.9.0", "ndk-context", "ndk-sys 0.6.0+11769913", "objc", @@ -2476,6 +2691,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.58.0" @@ -2495,6 +2720,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.58.0" @@ -2503,7 +2738,7 @@ checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ "windows-implement", "windows-interface", - "windows-result", + "windows-result 0.2.0", "windows-strings", "windows-targets 0.52.6", ] @@ -2530,6 +2765,15 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -2545,7 +2789,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] diff --git a/Cargo.toml b/Cargo.toml index f72f638..6ccd586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ pixels = "0.15.0" tao = "0.31.1" tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } -rand = "0.9.0" \ No newline at end of file +rand = "0.9.0" +cpal = "0.15.3" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 35b2078..2321c9d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,10 +14,14 @@ use tao::{ use tao::dpi::LogicalSize; use tracing::{info, error}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use rand::prelude::*; use tao::keyboard::Key; use std::time::{Duration, Instant}; +use cpal::{SampleFormat, SampleRate, Stream, SupportedStreamConfig}; +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use rand::seq::index::sample; +use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; const MODERNSHIFT: bool = false; const OLDJUMP: bool = true; @@ -30,6 +34,81 @@ struct Args { file: PathBuf, } +struct AudioPlayer { + stream: Stream, +} + +struct Oscilator { + sample_index: f32, + volume: f32, +} + +impl AudioPlayer { + fn make(rx: Receiver) -> Self { + let host = cpal::default_host(); + let device = host + .default_output_device() + .expect("no output device available"); + + let config = device.supported_output_configs().expect("error while querying config").find(|config| config.sample_format() == SampleFormat::F32); + if config.is_none() {panic!("no supported config found");} + let config = config.unwrap(); + if config.min_sample_rate() > SampleRate(48000) || config.max_sample_rate() < SampleRate(48000) { + //("{:?},{:?}", config.min_sample_rate(), config.max_sample_rate()); + panic!("sample rate out of range"); + } + + let format = SupportedStreamConfig::new( + config.channels(), + SampleRate(48000), + config.buffer_size().clone(), + SampleFormat::F32 + ); + + //let time_at_start = Instant::now(); + let mut oscilator = Oscilator{sample_index: 0.0, volume: 0.0}; + let stream = device.build_output_stream( + &format.config(), + move |data: &mut [f32], _: &cpal::OutputCallbackInfo| { + //let time_since_start = Instant::now().duration_since(time_at_start).as_secs_f32(); + if let Ok(vol) = rx.try_recv() { + oscilator.volume = vol as f32; + } + process_frame(data, config.channels().into(), &mut oscilator); + }, + (|err| error!("Error while creating output stream, {}", err)), + None, + ); + + Self { + stream: stream.expect("no stream available"), + } + + } + + fn run(&mut self) { + self.stream.play().unwrap(); + } +} + +fn process_frame(output: &mut [f32], channels: usize, oscilator: &mut Oscilator) { + for frame in output.chunks_mut(channels) { + let value = oscilator.tick() * oscilator.volume; + + for sample in frame.iter_mut() { + *sample = value; + } + } +} + +impl Oscilator { + fn tick(&mut self) -> f32 { + self.sample_index = (self.sample_index + 1.0) % 48000.0; + let two_pi = 2.0 * std::f32::consts::PI; + (self.sample_index * 440.0 * two_pi/48000.0).sin() + } +} + #[derive(Copy, Clone)] struct Chip8 { display: [u64; 32], @@ -78,9 +157,12 @@ impl Chip8 { self } - fn draw(&mut self, frame: &mut [u8], dimensions: (u32, u32)) -> Self { + fn draw(&mut self, frame: &mut [u8], dimensions: (u32, u32), tx: &SyncSender) -> Self { if self.sound_timer != 0 { self.sound_timer -= 1; + tx.send(0.5).unwrap(); + } else { + tx.send(0.0).unwrap(); } if self.delay_timer != 0 { self.delay_timer -= 1; @@ -219,10 +301,10 @@ impl Chip8 { 0x05 => { let r1 = self.registers[opcodes[1] as usize]; let r2 = self.registers[opcodes[2] as usize]; - info!("{},{}", r1 , r2); + //info!("{},{}", r1 , r2); let no_underflow = (r1 >= r2); if no_underflow { - info!("{}",r1 - r2); + //info!("{}",r1 - r2); self.registers[opcodes[1] as usize] = r1 - r2; } else { self.registers[opcodes[1] as usize] = 255 - ((r2 - r1) - 1); @@ -493,6 +575,7 @@ fn main() { let args = Args::parse(); let mut chip8 = Chip8::new().insert_font().load_file(args.file); + let (tx, rx) = sync_channel(1); println!("ram: {:?}", chip8.ram); @@ -501,7 +584,7 @@ fn main() { let window = { let window = WindowBuilder::new() .with_title("Chip8 Emulator") .with_inner_size(LogicalSize::new(320, 240)) - .with_min_inner_size(LogicalSize::new(320, 240)) + .with_min_inner_size(LogicalSize::new(600, 300)) .build(&event_loop) .unwrap(); Arc::new(window) @@ -513,8 +596,12 @@ fn main() { Pixels::new(window_size.width, window_size.height, surface_texture).unwrap() }; + let mut audio = AudioPlayer::make(rx); + + audio.run(); + let test: u64 = 1; - info!("{}", get_bit(test, 0)); + //info!("{}", get_bit(test, 0)); let mut start = Instant::now(); event_loop.run(move |event, _, control_flow| { match event { @@ -599,14 +686,14 @@ fn main() { window.request_redraw(); } Event::RedrawRequested(_) => { - chip8.draw(pixels.frame_mut(), (window.inner_size().width, window.inner_size().height)); + chip8.draw(pixels.frame_mut(), (window.inner_size().width, window.inner_size().height), &tx); if let Err(err) = pixels.render() { error!("pixels.render"); *control_flow = ControlFlow::Exit; } - let duration = start.elapsed(); - info!("{:?}", duration); - start = Instant::now(); + //let duration = start.elapsed(); + //info!("{:?}", duration); + //start = Instant::now(); } _ => {} }