diff --git a/Cargo.lock b/Cargo.lock index 664bede..4643eec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,7 +91,7 @@ dependencies = [ "accesskit_consumer", "hashbrown 0.15.5", "static_assertions", - "windows", + "windows 0.58.0", "windows-core 0.58.0", ] @@ -143,6 +143,28 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alsa" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d" +dependencies = [ + "alsa-sys", + "bitflags 2.11.1", + "cfg-if", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad7569085a265dd3f607ebecce7458eaab2132a84393534c95b18dcbc3f31e04" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "android-activity" version = "0.6.1" @@ -152,7 +174,7 @@ dependencies = [ "android-properties", "bitflags 2.11.1", "cc", - "jni", + "jni 0.22.4", "libc", "log", "ndk", @@ -447,6 +469,15 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2 0.6.4", +] + [[package]] name = "blocking" version = "1.6.2" @@ -556,6 +587,12 @@ dependencies = [ "shlex", ] +[[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" @@ -706,6 +743,50 @@ dependencies = [ "libc", ] +[[package]] +name = "coreaudio-rs" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16dd574a72a021b90c7656c474ea31d11a2f0366a8eff574186e761e0b9e3586" +dependencies = [ + "bitflags 2.11.1", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "cpal" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8942da362c0f0d895d7cac616263f2f9424edc5687364dfd1d25ef7eba506d7" +dependencies = [ + "alsa", + "coreaudio-rs", + "dasp_sample", + "jni 0.21.1", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "objc2 0.6.4", + "objc2-audio-toolbox", + "objc2-avf-audio", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.62.2", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -768,6 +849,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + [[package]] name = "derive_more" version = "0.99.20" @@ -1368,6 +1455,22 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + [[package]] name = "jni" version = "0.22.4" @@ -1608,6 +1711,15 @@ dependencies = [ "urid", ] +[[package]] +name = "mach2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea" +dependencies = [ + "libc", +] + [[package]] name = "memchr" version = "2.8.0" @@ -1703,6 +1815,17 @@ dependencies = [ "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.117", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1766,7 +1889,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.11.1", - "block2", + "block2 0.5.1", "libc", "objc2 0.5.2", "objc2-core-data", @@ -1787,6 +1910,31 @@ dependencies = [ "objc2-foundation 0.3.2", ] +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" +dependencies = [ + "bitflags 2.11.1", + "libc", + "objc2 0.6.4", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-avf-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a380031deed8e99db00065c45937da434ca987c034e13b87e4441f9e4090be" +dependencies = [ + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" @@ -1794,7 +1942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.11.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -1806,11 +1954,34 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "dispatch2", + "objc2 0.6.4", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -1818,7 +1989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.11.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -1830,7 +2001,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.11.1", + "block2 0.6.2", "dispatch2", + "libc", "objc2 0.6.4", ] @@ -1840,7 +2013,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", @@ -1852,7 +2025,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -1871,7 +2044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.11.1", - "block2", + "block2 0.5.1", "dispatch", "libc", "objc2 0.5.2", @@ -1884,6 +2057,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.1", + "block2 0.6.2", + "libc", "objc2 0.6.4", "objc2-core-foundation", ] @@ -1894,7 +2069,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -1907,7 +2082,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.11.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -1919,7 +2094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.11.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", @@ -1942,7 +2117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.11.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", @@ -1962,7 +2137,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -1974,7 +2149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.11.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -3008,6 +3183,9 @@ dependencies = [ name = "ui" version = "0.1.0" dependencies = [ + "cpal", + "crossbeam-channel", + "engine", "params", "vizia", ] @@ -3541,7 +3719,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core 0.58.0", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core 0.62.2", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", ] [[package]] @@ -3554,7 +3753,7 @@ dependencies = [ "windows-interface 0.58.0", "windows-result 0.2.0", "windows-strings 0.1.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3570,6 +3769,17 @@ dependencies = [ "windows-strings 0.5.1", ] +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -3620,13 +3830,23 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link", +] + [[package]] name = "windows-result" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3645,7 +3865,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result 0.2.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3657,13 +3877,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3672,7 +3901,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3684,34 +3913,76 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3724,24 +3995,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3758,7 +4053,7 @@ dependencies = [ "android-activity", "atomic-waker", "bitflags 2.11.1", - "block2", + "block2 0.5.1", "bytemuck", "calloop 0.13.0", "cfg_aliases", diff --git a/Cargo.toml b/Cargo.toml index 72ef720..df4f929 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ lv2 = "0.6.0" vizia = "0.3.0" atomic_float = "1.1.0" crossbeam-channel = "0.5.15" +cpal = "0.17.3" diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml index 9d0b384..37ea7eb 100644 --- a/crates/ui/Cargo.toml +++ b/crates/ui/Cargo.toml @@ -6,3 +6,6 @@ edition.workspace = true [dependencies] params = { workspace = true } vizia = { workspace = true } +engine = { workspace = true} +crossbeam-channel = { workspace = true } +cpal = { workspace = true } diff --git a/crates/ui/src/app.rs b/crates/ui/src/app.rs index 219e539..432cabb 100644 --- a/crates/ui/src/app.rs +++ b/crates/ui/src/app.rs @@ -1,3 +1,4 @@ +use crossbeam_channel::Sender; use params::{ParamId, ParamStore}; use vizia::prelude::*; @@ -6,6 +7,13 @@ use crate::widgets::piano::PianoKeyboard; pub const MOD_NSRC: usize = 19; pub const MOD_NDST: usize = 11; +#[derive(Clone, Copy, Debug)] +pub enum MidiMsg { + NoteOn(u8, u8), + NoteOff(u8), + AllNotesOff, +} + #[derive(Clone, Copy, Debug, PartialEq, Data)] pub enum Panel { Voice, @@ -41,10 +49,12 @@ pub struct AppData { pub active_env: usize, pub active_lfo: usize, pub fx_selected: usize, + #[lens(ignore)] + pub midi_tx: Sender, } impl AppData { - pub fn new(store: ParamStore) -> Self { + pub fn new(store: ParamStore, midi_tx: Sender) -> Self { let params = (0..ParamId::COUNT) .map(|i| store.get(unsafe { std::mem::transmute::(i) })) .collect(); @@ -63,6 +73,7 @@ impl AppData { active_env: 0, active_lfo: 0, fx_selected: 0, + midi_tx, } } } @@ -96,34 +107,45 @@ impl Model for AppData { self.params[*id as usize] = *val; self.store.set(*id, *val); } + AppEvent::SetPanel(p) => self.active_panel = *p, + AppEvent::SelectEnv(i) => self.active_env = *i, + AppEvent::SelectLfo(i) => self.active_lfo = *i, + AppEvent::SelectFx(i) => self.fx_selected = *i, + AppEvent::UpdateMetrics { voices, cpu } => { self.voice_count = *voices; self.cpu_load = *cpu; } - AppEvent::NoteOn(note, _) => { + + AppEvent::NoteOn(note, vel) => { if !self.held_notes.contains(note) { self.held_notes.push(*note); } + let _ = self.midi_tx.try_send(MidiMsg::NoteOn(*note, *vel)); } - AppEvent::NoteOff(note) => self.held_notes.retain(|n| n != note), + AppEvent::NoteOff(note) => { + self.held_notes.retain(|n| n != note); + let _ = self.midi_tx.try_send(MidiMsg::NoteOff(*note)); + } + AppEvent::OctaveUp => self.octave = (self.octave + 1).min(8), AppEvent::OctaveDown => self.octave = (self.octave - 1).max(0), + AppEvent::SetModDepth { src, dst, depth } => { let idx = src * MOD_NDST + dst; if idx < self.mod_depths.len() { self.mod_depths[idx] = depth.clamp(-1.0, 1.0); } } + AppEvent::ToggleFx(s) => { if *s < self.fx_enabled.len() { self.fx_enabled[*s] = !self.fx_enabled[*s]; } } - AppEvent::SelectEnv(i) => self.active_env = *i, - AppEvent::SelectLfo(i) => self.active_lfo = *i, - AppEvent::SelectFx(i) => self.fx_selected = *i, }); + event.map(|we: &WindowEvent, _| { crate::widgets::piano::handle_kbd(cx, we, self.octave); }); @@ -158,3 +180,14 @@ pub fn build_root(cx: &mut Context) { .width(Stretch(1.0)) .height(Stretch(1.0)); } + +fn tab_button(cx: &mut Context, p: Panel) { + Binding::new(cx, AppData::active_panel, move |cx, active_lens| { + let active = active_lens.get(cx) == p; + Label::new(cx, p.label()) + .class("top-tab") + .checked(active) + .on_press(move |cx| cx.emit(AppEvent::SetPanel(p))) + .height(Stretch(1.0)); + }); +} diff --git a/crates/ui/src/main.rs b/crates/ui/src/main.rs index 3873f2e..59bfb38 100644 --- a/crates/ui/src/main.rs +++ b/crates/ui/src/main.rs @@ -2,15 +2,28 @@ mod app; mod panels; mod widgets; +use std::sync::Arc; + +use app::{AppData, MidiMsg}; +use crossbeam_channel::bounded; +use engine::oscillator::WavetableBank; +use engine::synth::Synth; +use params::ParamStore; +use vizia::prelude::*; + fn main() { - use app::AppData; - use params::ParamStore; - use vizia::prelude::*; + let store = ParamStore::new(); + let bank = Arc::new(WavetableBank::new()); + + let (midi_tx, midi_rx) = bounded::(1024); + + let audio_store = store.clone(); + let _stream = build_audio_stream(audio_store, bank, midi_rx); let _ = Application::new(|cx| { cx.add_stylesheet(include_str!("theme.css")) .expect("theme.css"); - AppData::new(ParamStore::new()).build(cx); + AppData::new(store, midi_tx).build(cx); app::build_root(cx); cx.focus(); }) @@ -19,3 +32,92 @@ fn main() { .resizable(false) .run(); } + +fn build_audio_stream( + store: ParamStore, + bank: Arc, + midi_rx: crossbeam_channel::Receiver, +) -> cpal::Stream { + use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; + + let host = cpal::default_host(); + let device = host + .default_output_device() + .expect("no audio output device available"); + + let config = device + .default_output_config() + .expect("no default output config"); + + let sr = config.sample_rate() as f32; + + let synth = Synth::new(sr, store, bank); + + let host_bpm = 120.0f32; + + let stream = match config.sample_format() { + cpal::SampleFormat::F32 => { + build_stream_f32(&device, &config.into(), synth, midi_rx, host_bpm) + } + _fmt => { + let cfg: cpal::StreamConfig = config.into(); + build_stream_f32(&device, &cfg, synth, midi_rx, host_bpm) + } + }; + + stream.play().expect("failed to start audio stream"); + stream +} + +fn build_stream_f32( + device: &cpal::Device, + config: &cpal::StreamConfig, + mut synth: Synth, + midi_rx: crossbeam_channel::Receiver, + host_bpm: f32, +) -> cpal::Stream { + use cpal::traits::DeviceTrait; + + let channels = config.channels as usize; + + device + .build_output_stream( + config, + move |data: &mut [f32], _info: &cpal::OutputCallbackInfo| { + while let Ok(msg) = midi_rx.try_recv() { + match msg { + MidiMsg::NoteOn(note, vel) => synth.note_on(note, vel), + MidiMsg::NoteOff(note) => synth.note_off(note), + MidiMsg::AllNotesOff => { + for n in 0..128 { + synth.note_off(n); + } + } + } + } + + let frame_count = data.len() / channels; + for frame in 0..frame_count { + let (l, r) = synth.process(host_bpm); + let base = frame * channels; + match channels { + 1 => data[base] = (l + r) * 0.5, + 2 => { + data[base] = l; + data[base + 1] = r; + } + n => { + data[base] = l; + data[base + 1] = r; + for ch in 2..n { + data[base + ch] = 0.0; + } + } + } + } + }, + |err| eprintln!("audio stream error: {err}"), + None, + ) + .expect("failed to build output stream") +}