From 5deb4fd8f83301dca38656855be352de31fdd769 Mon Sep 17 00:00:00 2001 From: Ry Date: Wed, 25 May 2022 16:15:39 -0700 Subject: [PATCH] fox32: Add support for audio playback --- Cargo.lock | 303 ++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/audio.rs | 15 +++ src/bus.rs | 9 +- src/main.rs | 35 ++++++ 5 files changed, 356 insertions(+), 7 deletions(-) create mode 100644 src/audio.rs diff --git a/Cargo.lock b/Cargo.lock index 648a035..cc1783f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,28 @@ dependencies = [ "memchr", ] +[[package]] +name = "alsa" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b" +dependencies = [ + "alsa-sys", + "bitflags", + "libc", + "nix 0.23.1", +] + +[[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 = "ansi_term" version = "0.12.1" @@ -268,6 +290,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + [[package]] name = "cache-padded" version = "1.2.0" @@ -303,6 +331,12 @@ dependencies = [ "jobserver", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -378,6 +412,12 @@ dependencies = [ "vec_map", ] +[[package]] +name = "claxon" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" + [[package]] name = "cocoa" version = "0.24.0" @@ -425,6 +465,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "combine" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -522,6 +572,50 @@ dependencies = [ "objc", ] +[[package]] +name = "coreaudio-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" +dependencies = [ + "bitflags", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6" +dependencies = [ + "bindgen", +] + +[[package]] +name = "cpal" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116" +dependencies = [ + "alsa", + "core-foundation-sys 0.8.3", + "coreaudio-rs", + "jni", + "js-sys", + "lazy_static", + "libc", + "mach", + "ndk 0.6.0", + "ndk-glue 0.6.2", + "nix 0.23.1", + "oboe", + "parking_lot", + "stdweb", + "thiserror", + "web-sys", + "winapi", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -832,6 +926,7 @@ dependencies = [ "pixels", "rfd", "ringbuf", + "rodio", "vergen", "winit", "winit_input_helper", @@ -1197,6 +1292,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hound" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" + [[package]] name = "humantime" version = "2.1.0" @@ -1277,6 +1378,20 @@ dependencies = [ "web-sys", ] +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + [[package]] name = "jni-sys" version = "0.3.0" @@ -1344,6 +1459,17 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff" +[[package]] +name = "lewton" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" +dependencies = [ + "byteorder", + "ogg", + "tinyvec", +] + [[package]] name = "libc" version = "0.2.125" @@ -1402,6 +1528,15 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1461,6 +1596,26 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" +[[package]] +name = "minimp3" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985438f75febf74c392071a975a29641b420dd84431135a6e6db721de4b74372" +dependencies = [ + "minimp3-sys", + "slice-deque", + "thiserror", +] + +[[package]] +name = "minimp3-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e21c73734c69dc95696c9ed8926a2b393171d98b3f5f5935686a26a487ab9b90" +dependencies = [ + "cc", +] + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -1537,11 +1692,30 @@ checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" dependencies = [ "bitflags", "jni-sys", - "ndk-sys", + "ndk-sys 0.2.2", "num_enum", "thiserror", ] +[[package]] +name = "ndk" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys 0.3.0", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "ndk-glue" version = "0.5.0" @@ -1551,9 +1725,24 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk", + "ndk 0.5.0", "ndk-macro", - "ndk-sys", + "ndk-sys 0.2.2", +] + +[[package]] +name = "ndk-glue" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk 0.6.0", + "ndk-context", + "ndk-macro", + "ndk-sys 0.3.0", ] [[package]] @@ -1575,6 +1764,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" +[[package]] +name = "ndk-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +dependencies = [ + "jni-sys", +] + [[package]] name = "nix" version = "0.22.0" @@ -1621,6 +1819,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -1733,6 +1942,38 @@ dependencies = [ "objc", ] +[[package]] +name = "oboe" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1" +dependencies = [ + "jni", + "ndk 0.6.0", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd" +dependencies = [ + "cc", +] + +[[package]] +name = "ogg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" +dependencies = [ + "byteorder", +] + [[package]] name = "once_cell" version = "1.8.0" @@ -2098,6 +2339,19 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "rodio" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0939e9f626e6c6f1989adb6226a039c855ca483053f0ee7c98b90e41cf731e" +dependencies = [ + "claxon", + "cpal", + "hound", + "lewton", + "minimp3", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2119,6 +2373,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.0" @@ -2195,6 +2458,17 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +[[package]] +name = "slice-deque" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ef6ee280cdefba6d2d0b4b78a84a1c1a3f3a4cec98c2d4231c8bc225de0f25" +dependencies = [ + "libc", + "mach", + "winapi", +] + [[package]] name = "slotmap" version = "1.0.6" @@ -2264,6 +2538,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stdweb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" + [[package]] name = "strsim" version = "0.8.0" @@ -2493,6 +2773,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -2866,9 +3157,9 @@ dependencies = [ "libc", "log", "mio", - "ndk", - "ndk-glue", - "ndk-sys", + "ndk 0.5.0", + "ndk-glue 0.5.0", + "ndk-sys 0.2.2", "objc", "parking_lot", "percent-encoding", diff --git a/Cargo.toml b/Cargo.toml index 4fb25ab..acff4ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ log = "0.4" pixels = "0.9.0" rfd = "0.7.0" ringbuf = "0.2" +rodio = "0.15.0" winit = "0.26" winit_input_helper = "0.11" diff --git a/src/audio.rs b/src/audio.rs new file mode 100644 index 0000000..2d3dc9c --- /dev/null +++ b/src/audio.rs @@ -0,0 +1,15 @@ +// audio.rs + +pub struct Audio { + pub current_buffer_is_0: bool, + pub playing: bool, +} + +impl Audio { + pub fn new() -> Self { + Audio { + current_buffer_is_0: true, + playing: false, + } + } +} diff --git a/src/bus.rs b/src/bus.rs index d87da62..7812500 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,6 +1,6 @@ // bus.rs -use crate::{Memory, DiskController, Keyboard, Mouse, Overlay}; +use crate::{Memory, Audio, DiskController, Keyboard, Mouse, Overlay}; use std::sync::{Arc, Mutex}; use std::io::{Write, stdout}; @@ -10,6 +10,8 @@ pub struct Bus { pub disk_controller: DiskController, + pub audio: Arc>, + pub keyboard: Arc>, pub mouse: Arc>, @@ -165,6 +167,11 @@ impl Bus { _ => panic!("invalid mouse control port"), } } + 0x80000600 => { // audio port + let mut audio_lock = self.audio.lock().unwrap(); + audio_lock.playing = word != 0; + audio_lock.current_buffer_is_0 = true; + } 0x80001000..=0x80005003 => { // disk controller port let id = port as u8; let operation = (port & 0x0000F000) >> 8; diff --git a/src/main.rs b/src/main.rs index e790996..e340c2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ // main.rs pub mod memory; +pub mod audio; pub mod bus; pub mod cpu; pub mod keyboard; @@ -9,6 +10,7 @@ pub mod disk; pub mod runtime; pub mod wrapped; +use audio::Audio; use bus::Bus; use cpu::{Cpu, Interrupt}; use keyboard::Keyboard; @@ -23,6 +25,7 @@ use std::mem; use std::ops::Deref; use std::sync::{Arc, mpsc, Mutex}; use std::thread; +use std::time; use std::process::exit; use std::env; use std::fs::{File, read}; @@ -30,6 +33,7 @@ use std::fs::{File, read}; use image; use log::error; use pixels::{Pixels, SurfaceTexture}; +use rodio::{OutputStream, buffer::SamplesBuffer, Sink}; use winit::dpi::LogicalSize; use winit::event::{ElementState, Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; @@ -86,8 +90,11 @@ fn main() { let keyboard = Arc::new(Mutex::new(Keyboard::new())); let mouse = Arc::new(Mutex::new(Mouse::new())); + let audio = Arc::new(Mutex::new(Audio::new())); + let mut bus = Bus { memory: Box::new(MemoryStub()), + audio: audio.clone(), disk_controller: DiskController::new(), keyboard: keyboard.clone(), mouse: mouse.clone(), @@ -130,6 +137,7 @@ fn main() { runtime.halted_set(false); + let memory_audio = memory.clone(); let memory_cpu = memory.clone(); let memory_eventloop = memory.clone(); @@ -194,6 +202,33 @@ fn main() { } }).unwrap(); + let interrupt_sender_audio = interrupt_sender.clone(); + let builder = thread::Builder::new().name("audio".to_string()); + builder.spawn({ + move || { + let (_stream, stream_handle) = OutputStream::try_default().unwrap(); + let sink = Sink::try_new(&stream_handle).unwrap(); + loop { + // every 500 ms, play what is in the audio buffer and tell fox32 to swap them + let mut audio_lock = audio.lock().unwrap(); + if audio_lock.playing { + let current_buffer: Vec = if audio_lock.current_buffer_is_0 { + audio_lock.current_buffer_is_0 = false; + memory_audio.ram()[0x0212C000..0x0212C000+32768].to_vec().chunks_exact(2).map(|x| ((x[1] as i16) << 8) | x[0] as i16).collect() + } else { + audio_lock.current_buffer_is_0 = true; + memory_audio.ram()[0x0212C000+32768..0x0212C000+65536].to_vec().chunks_exact(2).map(|x| ((x[1] as i16) << 8) | x[0] as i16).collect() + }; + let buffer = SamplesBuffer::new(1, 22050, current_buffer); + sink.append(buffer); + drop(audio_lock); + interrupt_sender_audio.send(0xFE).unwrap(); // audio interrupt, swap audio buffers + thread::sleep(time::Duration::from_millis(500)); + } + } + } + }).unwrap(); + event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll;