diff --git a/Cargo.lock b/Cargo.lock index ae95496..762a6ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,12 +31,43 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" 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.6.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-activity" version = "0.5.2" @@ -176,6 +207,20 @@ dependencies = [ "libloading 0.7.4", ] +[[package]] +name = "asio-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb111d0b927c4b860b42a8bd869b94f5f973b0395ed5892f1ab2be2dc6831ea8" +dependencies = [ + "bindgen 0.69.5", + "cc", + "num-derive", + "num-traits", + "parse_cfg", + "walkdir", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -188,6 +233,47 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.87", + "which", +] + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.87", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -261,6 +347,12 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.9.0" @@ -336,6 +428,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-if" version = "1.0.0" @@ -354,6 +455,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[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.5", +] + [[package]] name = "clap" version = "4.5.21" @@ -394,6 +506,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +[[package]] +name = "claxon" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -510,6 +628,51 @@ 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 0.70.1", +] + +[[package]] +name = "cpal" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" +dependencies = [ + "alsa", + "asio-sys", + "core-foundation-sys", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", + "libc", + "mach2", + "ndk 0.8.0", + "ndk-context", + "num-traits", + "oboe", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.54.0", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -533,6 +696,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" @@ -560,6 +729,21 @@ 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 = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -661,7 +845,7 @@ dependencies = [ "vec_map", "wasm-bindgen", "web-sys", - "windows", + "windows 0.54.0", ] [[package]] @@ -675,6 +859,12 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "glow" version = "0.13.1" @@ -725,7 +915,7 @@ dependencies = [ "presser", "thiserror 1.0.69", "winapi", - "windows", + "windows 0.52.0", ] [[package]] @@ -797,6 +987,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + [[package]] name = "icrate" version = "0.0.4" @@ -854,6 +1059,24 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[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" @@ -911,6 +1134,29 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[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.164" @@ -1028,6 +1274,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" @@ -1114,6 +1366,27 @@ dependencies = [ "libc", ] +[[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 = "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.87", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1382,6 +1655,38 @@ 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 = "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.20.2" @@ -1429,6 +1734,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "parse_cfg" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905787a434a2c721408e7c9a252e85f3d93ca0f118a5283022636c0e05a7ea49" +dependencies = [ + "nom", +] + [[package]] name = "paste" version = "1.0.15" @@ -1513,6 +1827,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn 2.0.87", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -1594,12 +1918,55 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "renderdoc-sys" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "rodio" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6006a627c1a38d37f3d3a85c6575418cfe34a5392d60a686d0071e1c8d427acb" +dependencies = [ + "claxon", + "cpal", + "hound", + "lewton", + "symphonia", + "thiserror 1.0.69", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1813,18 +2180,69 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "svc16" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "clap", + "cpal", "gilrs", "pixels", + "rodio", "thiserror 2.0.3", "winit 0.29.15", "winit-input-map", "winit_input_helper", ] +[[package]] +name = "symphonia" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9" +dependencies = [ + "lazy_static", + "symphonia-bundle-mp3", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01c2aae70f0f1fb096b6f0ff112a930b1fb3626178fba3ae68b09dce71706d4" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-core" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-metadata" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", +] + [[package]] name = "syn" version = "1.0.109" @@ -1921,6 +2339,21 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml_datetime" version = "0.6.8" @@ -2384,6 +2817,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "wide" version = "0.7.30" @@ -2437,7 +2882,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", + "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", ] @@ -2450,6 +2905,25 @@ 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", + "windows-targets 0.52.6", +] + +[[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-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 9bceb05..f2d8de2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "svc16" -version = "0.5.0" +version = "0.6.0" edition = "2021" authors = ["Jan Neuendorf"] description = "An emulator for a simple virtual computer" @@ -9,9 +9,11 @@ license="MIT" [dependencies] anyhow = "1.0.93" clap = { version = "4.5.21", features = ["derive"] } +cpal = { version = "0.15.3", features = ["asio"] } gilrs = { version = "0.11.0"} # There seems to be some incompatibility with the latest crates.io version of pixels? pixels = { git = "https://github.com/parasyte/pixels.git", rev = "d4df286"} +rodio = "0.19.0" thiserror = "2.0.3" winit = "0.29.15" winit-input-map = "0.4.1" diff --git a/README.md b/README.md index ec5f017..8bdc635 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,13 @@ The instruction pointer represents an address in main memory. It starts as zero. The screen has a resolution of $256*256=2^{16}$ pixels. The color of each pixel is represented with 16-bits using `RGB565`. The coordinate $(x,y)$ of the screen maps to the index $256y+x$ in the screen-buffer. The coordinate $(0,0)$ is in the upper left-hand corner. Changes to the screen-buffer are not reflected on the screen until the system is synchronized. +### Sound + +The system can play a sample from a sound buffer. +There is only one channel, so if a new sample is dispatched any that are currently playing are stopped. +The audio is represented with a sampling rate of 16 kHz. Each sample being, once again a u16. +The amplitude of the signal is given as:`amplitude=(float(sample) - 32768.0) / 32768.0`. + ### Input
@@ -127,11 +134,13 @@ When the instruction pointer advances, it does so by four positions. | 8 | **Deref** | yes | `@arg2=@(@arg1+arg3)` | | 9 | **Ref** | yes | `@(@arg1+arg3)=@arg2` | | 10 | **Inst** | yes | `@arg1=inst_ptr` | -| 11 | **Print** | yes | Writes `color=@arg1` to `index=@arg2` of screen-buffer. | -| 12 | **Read** | yes | Copies `index=@arg1` of screen-buffer to `@arg2` | +| 11 | **Print** | yes | Writes `color=@arg1` to `index=@arg2` of buffer arg3 | +| 12 | **Read** | yes | Copies `index=@arg1` of buffer arg3 to `@arg2` | | 13 | **Band** | yes | `@arg3=@arg1&@arg2` | | 14 | **Xor** | yes | `@arg3=@arg1^@arg2` | -| 15 | **Sync** | yes | Puts `@arg1=position_code`, `@arg2=key_code` and synchronizes in that order | +| 15 | **Sync** | yes | Puts `@arg1=position_code`, `@arg2=key_code` and synchronizes in that order if @arg3, it plays @arg3 values of the sound buffer. | + +When an argument refers to the name of a buffer, it means the screen buffer if it is 0 and the sound buffer otherwise. ### Constructing a Program diff --git a/examples/sound_demo.svc16 b/examples/sound_demo.svc16 new file mode 100644 index 0000000..7f870e4 Binary files /dev/null and b/examples/sound_demo.svc16 differ diff --git a/specification/sketch.svg b/specification/sketch.svg index 45457f1..ae9f698 100644 --- a/specification/sketch.svg +++ b/specification/sketch.svg @@ -7,8 +7,31 @@ viewBox="0 0 121 84" version="1.1" id="svg1" + sodipodi:docname="sketch.svg" + inkscape:version="1.4 (e7c3feb100, 2024-10-09)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> + - - - - - - + + + Screen - - - - - - - - - - - - - - - - 16 - Screen-buffer - Sync - Input - 256px - 256px - Memory + dx="0" + dy="0">Screen + + d="m 41.595644,76.756889 h -1.584325 v -0.333375 l 0.568325,-0.574675 q 0.17145,-0.1778 0.276225,-0.295275 0.104775,-0.12065 0.1524,-0.2159 0.04763,-0.09843 0.04763,-0.20955 0,-0.136525 -0.0762,-0.2032 -0.07303,-0.06668 -0.200025,-0.06668 -0.130175,0 -0.254,0.06033 -0.123825,0.06032 -0.26035,0.17145 l -0.26035,-0.307975 q 0.09842,-0.08572 0.206375,-0.15875 0.111125,-0.07303 0.254,-0.117475 0.14605,-0.04762 0.34925,-0.04762 0.22225,0 0.381,0.08255 0.161925,0.07937 0.24765,0.219075 0.0889,0.136525 0.0889,0.31115 0,0.187325 -0.0762,0.3429 -0.07303,0.155575 -0.2159,0.307975 -0.1397,0.1524 -0.339725,0.33655 l -0.2921,0.27305 v 0.02222 h 0.987425 z" + style="font-weight:bold;font-family:'Fira Mono';-inkscape-font-specification:'Fira Mono Bold';fill:#000000;stroke:none;stroke-width:0.35" + id="path300" /> - Instruction-pointer - + d="m 46.313699,76.756889 h -0.479425 v -1.311275 q 0,-0.08255 0.0032,-0.2159 0.0063,-0.13335 0.0095,-0.23495 -0.01588,0.01905 -0.06985,0.06985 -0.0508,0.04762 -0.09525,0.08572 l -0.26035,0.20955 -0.231775,-0.288925 0.73025,-0.581025 h 0.3937 z" + style="font-weight:bold;font-family:'Fira Mono';-inkscape-font-specification:'Fira Mono Bold';fill:#000000;stroke:none;stroke-width:0.35" + id="path302" /> - - + d="m 46.92965,75.794864 q 0,-0.19685 0.02857,-0.38735 0.02858,-0.1905 0.09842,-0.358775 0.07303,-0.17145 0.200025,-0.301625 0.130175,-0.13335 0.327025,-0.206375 0.200025,-0.0762 0.4826,-0.0762 0.06668,0 0.155575,0.0063 0.0889,0.0032 0.149225,0.01587 v 0.384175 q -0.06032,-0.01588 -0.13335,-0.02223 -0.06985,-0.0095 -0.1397,-0.0095 -0.282575,0 -0.43815,0.0889 -0.1524,0.0889 -0.2159,0.250825 -0.0635,0.15875 -0.07303,0.3683 h 0.01905 q 0.0635,-0.111125 0.180975,-0.187325 0.12065,-0.0762 0.31115,-0.0762 0.29845,0 0.473075,0.187325 0.174625,0.187325 0.174625,0.530225 0,0.3683 -0.20955,0.57785 -0.206375,0.20955 -0.561975,0.20955 -0.231775,0 -0.4191,-0.104775 -0.187325,-0.10795 -0.29845,-0.327025 -0.111125,-0.22225 -0.111125,-0.561975 z m 0.81915,0.6096 q 0.1397,0 0.2286,-0.09525 0.0889,-0.09842 0.0889,-0.301625 0,-0.1651 -0.0762,-0.26035 -0.0762,-0.09525 -0.231775,-0.09525 -0.104775,0 -0.18415,0.04762 -0.07937,0.04445 -0.123825,0.117475 -0.04445,0.07303 -0.04445,0.149225 0,0.104775 0.0381,0.206375 0.0381,0.09842 0.1143,0.1651 0.07937,0.06668 0.1905,0.06668 z" + style="font-weight:bold;font-family:'Fira Mono';-inkscape-font-specification:'Fira Mono Bold';fill:#000000;stroke:none;stroke-width:0.35" + id="path304" /> + + + + + + + + + + + 16 + Screen-buffer + Sync + Input + 256px + 256px + Memory + + + Instruction-pointer + + + + + + Sound-buffer + diff --git a/src/engine.rs b/src/engine.rs index 6ef09c7..e4c0479 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -23,10 +23,12 @@ const SYNC: u16 = 15; pub struct Engine { memory: [u16; MEMSIZE], screen: [u16; MEMSIZE], + sound: [u16; MEMSIZE], instruction_pointer: u16, pos_code: u16, key_code: u16, sync_called: bool, + sound_code: u16, } #[derive(Debug, Error)] @@ -58,10 +60,12 @@ impl Engine { Self { memory, screen: [0; MEMSIZE], + sound: [0; MEMSIZE], instruction_pointer: 0, pos_code: 0, key_code: 0, sync_called: false, + sound_code: 0, } } pub fn wants_to_sync(&self) -> bool { @@ -76,10 +80,15 @@ impl Engine { pos_code: u16, key_code: u16, buffer: &mut [u16; MEMSIZE], - ) -> () { + ) -> Option> { self.set_input(pos_code, key_code); self.sync_called = false; *buffer = self.screen; + if self.sound_code == 0 { + return None; + } else { + return Some(self.sound[..self.sound_code as usize].to_vec()); + } } } impl Engine { @@ -92,9 +101,15 @@ impl Engine { fn get_screen(&self, index: u16) -> u16 { return self.screen[index as usize]; } + fn get_sound(&self, index: u16) -> u16 { + return self.sound[index as usize]; + } fn set_screen(&mut self, index: u16, value: u16) { self.screen[index as usize] = value; } + fn set_sound(&mut self, index: u16, value: u16) { + self.sound[index as usize] = value; + } pub fn read_instruction(&self) -> [u16; 4] { return [0, 1, 2, 3].map(|o| self.get(self.instruction_pointer.wrapping_add(o))); } @@ -168,11 +183,19 @@ impl Engine { self.advance_inst_ptr(); } PRINT => { - self.set_screen(self.get(arg2), self.get(arg1)); + if arg3 == 0 { + self.set_screen(self.get(arg2), self.get(arg1)); + } else { + self.set_sound(self.get(arg2), self.get(arg1)); + } self.advance_inst_ptr(); } READ => { - self.set(arg2, self.get_screen(self.get(arg1))); + if arg3 == 0 { + self.set(arg2, self.get_screen(self.get(arg1))); + } else { + self.set(arg2, self.get_sound(self.get(arg1))); + } self.advance_inst_ptr(); } BAND => { @@ -189,6 +212,7 @@ impl Engine { self.sync_called = true; self.set(arg1, self.pos_code); self.set(arg2, self.key_code); + self.sound_code = self.get(arg3); self.advance_inst_ptr(); } _ => return Err(EngineError::InvalidInstruction), diff --git a/src/main.rs b/src/main.rs index 6b26d4a..ffae1d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,8 @@ use cli::Cli; use engine::Engine; use gilrs::Gilrs; use pixels::{Pixels, SurfaceTexture}; +use rodio::{source::Source, Decoder, OutputStream, Sink}; +use std::any::Any; use std::time::{Duration, Instant}; use utils::*; use winit::dpi::LogicalSize; @@ -28,6 +30,8 @@ fn main() -> Result<()> { let event_loop = EventLoop::new()?; let mut input = WinitInputHelper::new(); let mut gamepad = build_gamepad_map(); + let (audio_strem, audio_player) = rodio::OutputStream::try_default()?; + let audio_sink = Sink::try_new(&audio_player)?; if cli.scaling < 1 { return Err(anyhow!("The minimal scaling factor is 1")); } @@ -67,12 +71,15 @@ fn main() -> Result<()> { paused = !paused; if paused { window.set_title("SVC16 (paused)"); + audio_sink.pause(); } else { window.set_title("SVC16"); + audio_sink.play(); } } if input.key_pressed_logical(Key::Character("r")) { engine = Engine::new(initial_state.clone()); + audio_sink.clear(); paused = false; } @@ -98,7 +105,14 @@ fn main() -> Result<()> { let engine_elapsed = engine_start.elapsed(); gamepad.update_with_gilrs(&mut girls); let (c1, c2) = get_input_code(&input, &gamepad, &pixels); - engine.perform_sync(c1, c2, &mut raw_buffer); + let sound_request = engine.perform_sync(c1, c2, &mut raw_buffer); + if let Some(audio) = sound_request { + dbg!(&audio); + let source = SoundFormat::new(audio); + audio_sink.clear(); + audio_sink.append(source); + audio_sink.play(); + } update_image_buffer(pixels.frame_mut(), &raw_buffer); let elapsed = start_time.elapsed(); diff --git a/src/utils.rs b/src/utils.rs index 6a0c631..daae309 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,7 @@ use crate::RES; use anyhow::Result; use pixels::Pixels; +use rodio::Source; use std::hash::Hash; use winit::{ event::MouseButton, @@ -129,3 +130,46 @@ pub fn handle_event_loop_error(handle: &EventLoopWindowTarget<()>, msg: impl AsR eprintln!("{}", msg.as_ref()); handle.exit(); } + +pub struct SoundFormat { + samples: Vec, + current: usize, +} + +impl SoundFormat { + pub fn new(vec: Vec) -> Self { + Self { + samples: vec, + current: 0, + } + } +} +impl Iterator for SoundFormat { + type Item = f32; + + fn next(&mut self) -> Option { + let val = self + .samples + .get(self.current) + .map(|v| (*v as f32 - 32768.0) / 32768.0); + self.current += 1; + val + } +} +impl Source for SoundFormat { + fn current_frame_len(&self) -> Option { + None + } + + fn channels(&self) -> u16 { + 1 + } + + fn sample_rate(&self) -> u32 { + 16000 + } + + fn total_duration(&self) -> Option { + None + } +}