diff --git a/Cargo.lock b/Cargo.lock index 52343f4904..aa4f6a9b76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,10 +21,21 @@ dependencies = [ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "alga" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "alsa-sys" -version = "0.1.1" -source = "git+https://github.com/desttinghim/cpal?rev=e7c086d0afc368a888ad133c3b1d928b16986130#e7c086d0afc368a888ad133c3b1d928b16986130" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -311,16 +322,6 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "cgmath" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "chashmap" version = "2.2.2" @@ -378,7 +379,7 @@ dependencies = [ [[package]] name = "claxon" -version = "0.3.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -535,9 +536,9 @@ dependencies = [ [[package]] name = "cpal" version = "0.8.2" -source = "git+https://github.com/desttinghim/cpal?rev=e7c086d0afc368a888ad133c3b1d928b16986130#e7c086d0afc368a888ad133c3b1d928b16986130" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "alsa-sys 0.1.1 (git+https://github.com/desttinghim/cpal?rev=e7c086d0afc368a888ad133c3b1d928b16986130)", + "alsa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "coreaudio-rs 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1023,6 +1024,14 @@ dependencies = [ "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "getrandom" version = "0.1.11" @@ -1512,6 +1521,11 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "line_drawing" version = "0.7.0" @@ -1596,6 +1610,14 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "matrixmultiply" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -1722,6 +1744,22 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nalgebra" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "alga 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "matrixmultiply 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "net2" version = "0.2.33" @@ -2439,6 +2477,11 @@ dependencies = [ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rawpointer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rayon" version = "1.1.0" @@ -2524,16 +2567,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rodio" -version = "0.8.1" -source = "git+https://github.com/desttinghim/rodio.git?rev=dd93f905c1afefaac03c496a666ecab27d3e391b#dd93f905c1afefaac03c496a666ecab27d3e391b" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cgmath 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "claxon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cpal 0.8.2 (git+https://github.com/desttinghim/cpal?rev=e7c086d0afc368a888ad133c3b1d928b16986130)", + "claxon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cpal 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "hound 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lewton 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", "minimp3 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3000,6 +3043,11 @@ name = "tuple_utils" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ucd-util" version = "0.1.5" @@ -3181,7 +3229,7 @@ dependencies = [ "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "portpicker 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rodio 0.8.1 (git+https://github.com/desttinghim/rodio.git?rev=dd93f905c1afefaac03c496a666ecab27d3e391b)", + "rodio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3419,7 +3467,8 @@ dependencies = [ "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum alsa-sys 0.1.1 (git+https://github.com/desttinghim/cpal?rev=e7c086d0afc368a888ad133c3b1d928b16986130)" = "" +"checksum alga 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d708cb68c7106ed1844de68f50f0157a7788c2909a6926fad5a87546ef6a4ff8" +"checksum alsa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b0edcbbf9ef68f15ae1b620f722180b82a98b6f0628d30baa6b8d2a5abc87d58" "checksum andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" "checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -3455,13 +3504,12 @@ dependencies = [ "checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" -"checksum cgmath 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "87f025a17ad3f30d49015c787903976d5f9cd6115ece1eb7f4d6ffe06b8c4080" "checksum chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff41a3c2c1e39921b9003de14bf0439c7b63a9039637c291e1a64925d8ddfa45" "checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" "checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a" "checksum clang-sys 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e414af9726e1d11660801e73ccc7fb81803fb5f49e5903a25b348b2b3b480d2e" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" -"checksum claxon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "35193597ff846c905e135b66b7a88876a8b684d269a24fa0f6086988fc2197c8" +"checksum claxon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f86c952727a495bda7abaf09bafdee1a939194dd793d9a8e26281df55ac43b00" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cmake 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "3c84c596dcf125d6781f58e3f4254677ec2a6d8aa56e8501ac277100990b3229" "checksum cocoa 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0c23085dde1ef4429df6e5896b89356d35cdd321fb43afe3e378d010bb5adc6" @@ -3479,7 +3527,7 @@ dependencies = [ "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" "checksum coreaudio-rs 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491" "checksum coreaudio-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "78fdbabf58d5b1f461e31b94a571c109284f384cec619a3d96e66ec55b4de82b" -"checksum cpal 0.8.2 (git+https://github.com/desttinghim/cpal?rev=e7c086d0afc368a888ad133c3b1d928b16986130)" = "" +"checksum cpal 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d58ae1ed6536b1b233f5e3aeb6997a046ddb4d05e3f61701b58a92eb254a829e" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7408247b1b87f480890f28b670c5f8d9a8a4274833433fe74dc0dfd46d33650" "checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" @@ -3532,6 +3580,7 @@ dependencies = [ "checksum gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2d2199eba47ebcb9977ce28179649bdd59305ef465c4e6f9b65aaa41c24e6b5" "checksum gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df6a3b73e04fafc07f5ebc083f1096a773412e627828e1103a55e921f81187d8" "checksum gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3162ff940526ddff71bf1f630facee6b5e05d282d125ba0c4c803842819b80c3" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fc344b02d3868feb131e8b5fe2b9b0a1cc42942679af493061fc13b853243872" "checksum gfx 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "619e38a31e275efaf92c6a94f977db8aac396e3cb6998c176cfde32ce3239b69" "checksum gfx_core 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e1127b02a9d4fcc880091d8a0f4419efd598de4f1649edcd005c76e5792176f" @@ -3583,6 +3632,7 @@ dependencies = [ "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fd38073de8f7965d0c17d30546d4bb6da311ab428d1c7a3fc71dff7f9d4979b9" "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +"checksum libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" "checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" @@ -3594,6 +3644,7 @@ dependencies = [ "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" "checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +"checksum matrixmultiply 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfed72d871629daa12b25af198f110e8095d7650f5f4c61c5bac28364604f9b" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" @@ -3608,6 +3659,7 @@ dependencies = [ "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" "checksum msgbox 0.1.1 (git+https://github.com/bekker/msgbox-rs.git)" = "" +"checksum nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aaa9fddbc34c8c35dd2108515587b8ce0cab396f17977b8c738568e4edb521a2" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" @@ -3685,6 +3737,7 @@ dependencies = [ "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019" "checksum rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" "checksum rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" @@ -3694,7 +3747,7 @@ dependencies = [ "checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" -"checksum rodio 0.8.1 (git+https://github.com/desttinghim/rodio.git?rev=dd93f905c1afefaac03c496a666ecab27d3e391b)" = "" +"checksum rodio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d0f961b254e66d147a7b550c78b01308934c97d807a34b417fd0f5a0a0f3a2d" "checksum ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ece421e0c4129b90e4a35b6f625e472e96c552136f5093a2f4fa2bbb75a62d5" "checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" @@ -3749,6 +3802,7 @@ dependencies = [ "checksum tiff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b7c2cfc4742bd8a32f2e614339dd8ce30dbcf676bb262bd63a2327bc5df57d" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tuple_utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbfecd7bb8f0a3e96b3b31c46af2677a55a588767c0091f484601424fcb20e7e" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" diff --git a/assets/voxygen/audio/footsteps/stepdirt_1.wav b/assets/voxygen/audio/footsteps/stepdirt_1.wav new file mode 100644 index 0000000000..3fe1a8485c Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepdirt_1.wav differ diff --git a/assets/voxygen/audio/footsteps/stepdirt_2.wav b/assets/voxygen/audio/footsteps/stepdirt_2.wav new file mode 100644 index 0000000000..335318564a Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepdirt_2.wav differ diff --git a/assets/voxygen/audio/footsteps/stepdirt_3.wav b/assets/voxygen/audio/footsteps/stepdirt_3.wav new file mode 100644 index 0000000000..1540ae2c92 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepdirt_3.wav differ diff --git a/assets/voxygen/audio/footsteps/stepdirt_4.wav b/assets/voxygen/audio/footsteps/stepdirt_4.wav new file mode 100644 index 0000000000..d88c17a358 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepdirt_4.wav differ diff --git a/assets/voxygen/audio/footsteps/stepdirt_5.wav b/assets/voxygen/audio/footsteps/stepdirt_5.wav new file mode 100644 index 0000000000..c71c375cb9 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepdirt_5.wav differ diff --git a/assets/voxygen/audio/footsteps/stepdirt_6.wav b/assets/voxygen/audio/footsteps/stepdirt_6.wav new file mode 100644 index 0000000000..aed47a7eb0 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepdirt_6.wav differ diff --git a/assets/voxygen/audio/footsteps/stepdirt_7.wav b/assets/voxygen/audio/footsteps/stepdirt_7.wav new file mode 100644 index 0000000000..7210624b48 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepdirt_7.wav differ diff --git a/assets/voxygen/audio/footsteps/stepdirt_8.wav b/assets/voxygen/audio/footsteps/stepdirt_8.wav new file mode 100644 index 0000000000..6e5a358615 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepdirt_8.wav differ diff --git a/assets/voxygen/audio/footsteps/stepsnow_1.wav b/assets/voxygen/audio/footsteps/stepsnow_1.wav new file mode 100644 index 0000000000..f6eaf8693c Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepsnow_1.wav differ diff --git a/assets/voxygen/audio/footsteps/stepsnow_2.wav b/assets/voxygen/audio/footsteps/stepsnow_2.wav new file mode 100644 index 0000000000..ed69cebfbb Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepsnow_2.wav differ diff --git a/assets/voxygen/audio/footsteps/stepstone_1.wav b/assets/voxygen/audio/footsteps/stepstone_1.wav new file mode 100644 index 0000000000..ca4de9dfcc Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepstone_1.wav differ diff --git a/assets/voxygen/audio/footsteps/stepstone_2.wav b/assets/voxygen/audio/footsteps/stepstone_2.wav new file mode 100644 index 0000000000..af0aa5f780 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepstone_2.wav differ diff --git a/assets/voxygen/audio/footsteps/stepstone_3.wav b/assets/voxygen/audio/footsteps/stepstone_3.wav new file mode 100644 index 0000000000..08328a7588 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepstone_3.wav differ diff --git a/assets/voxygen/audio/footsteps/stepstone_4.wav b/assets/voxygen/audio/footsteps/stepstone_4.wav new file mode 100644 index 0000000000..5362d93137 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepstone_4.wav differ diff --git a/assets/voxygen/audio/footsteps/stepstone_5.wav b/assets/voxygen/audio/footsteps/stepstone_5.wav new file mode 100644 index 0000000000..db6a92a652 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepstone_5.wav differ diff --git a/assets/voxygen/audio/footsteps/stepstone_6.wav b/assets/voxygen/audio/footsteps/stepstone_6.wav new file mode 100644 index 0000000000..d436b94694 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepstone_6.wav differ diff --git a/assets/voxygen/audio/footsteps/stepstone_7.wav b/assets/voxygen/audio/footsteps/stepstone_7.wav new file mode 100644 index 0000000000..0af921a7de Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepstone_7.wav differ diff --git a/assets/voxygen/audio/footsteps/stepstone_8.wav b/assets/voxygen/audio/footsteps/stepstone_8.wav new file mode 100644 index 0000000000..78fdf40e0f Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepstone_8.wav differ diff --git a/assets/voxygen/audio/footsteps/stepwater_1.wav b/assets/voxygen/audio/footsteps/stepwater_1.wav new file mode 100644 index 0000000000..584dc599a4 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepwater_1.wav differ diff --git a/assets/voxygen/audio/footsteps/stepwater_2.wav b/assets/voxygen/audio/footsteps/stepwater_2.wav new file mode 100644 index 0000000000..526f613eac Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepwater_2.wav differ diff --git a/assets/voxygen/audio/footsteps/stepwood_1.wav b/assets/voxygen/audio/footsteps/stepwood_1.wav new file mode 100644 index 0000000000..a81439012c Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepwood_1.wav differ diff --git a/assets/voxygen/audio/footsteps/stepwood_2.wav b/assets/voxygen/audio/footsteps/stepwood_2.wav new file mode 100644 index 0000000000..9dae7e0189 Binary files /dev/null and b/assets/voxygen/audio/footsteps/stepwood_2.wav differ diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 555a6181c4..324c4ab18d 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -54,7 +54,7 @@ num = "0.2.0" backtrace = "0.3.33" rand = "0.7.0" frustum_query = "0.1.2" -rodio = { git = "https://github.com/desttinghim/rodio.git", rev = "dd93f905c1afefaac03c496a666ecab27d3e391b" } +rodio = "0.9.0" crossbeam = "0.7.2" heaptrack = "0.3.0" hashbrown = { version = "0.5.0", features = ["serde", "nightly"] } diff --git a/voxygen/src/audio/base.rs b/voxygen/src/audio/base.rs deleted file mode 100644 index 72365e6063..0000000000 --- a/voxygen/src/audio/base.rs +++ /dev/null @@ -1,424 +0,0 @@ -use crate::settings::{AudioSettings, Settings}; -use common::assets::read_dir; -use crossbeam::{ - atomic::AtomicCell, - channel::{unbounded, Sender}, - queue::SegQueue, - sync::ShardedLock, -}; -use parking_lot::{Condvar, Mutex}; -use rodio::{Decoder, Device, Sink}; -use std::fs::File; -use std::io::BufReader; -use std::sync::Arc; -use std::thread; - -trait AudioConfig { - fn set_volume(&mut self, volume: f32); - fn set_device(&mut self, name: String); -} - -trait MonoMode { - fn set_mono(tx: Sender) -> Self; -} - -trait StereoMode { - fn set_stereo(tx: Sender) -> Self; -} - -trait DebugMode { - fn set_no_audio(tx: Sender) -> Self; -} - -#[derive(Debug)] -pub enum SinkError { - SinkNotMatch, -} - -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum Genre { - Bgm, - Sfx, - None, -} - -#[derive(Debug)] -pub enum AudioPlayerMsg { - AudioPlay, - AudioStop, - AudioTime(f64), -} - -#[derive(Debug)] -pub enum Action { - Load(String), - Stop, - AdjustVolume(f32), - ChangeDevice(String), -} - -#[derive(Clone)] -struct EventLoop { - condvar: Arc<(Mutex, Condvar)>, - queue: Arc>, - playing: Arc>, -} - -impl EventLoop { - fn new() -> Self { - Self { - condvar: Arc::new((Mutex::new(false), Condvar::new())), - queue: Arc::new(SegQueue::new()), - playing: Arc::new(ShardedLock::new(false)), - } - } -} - -/// The `AudioPlayer` handles exactly what player does by playing audio from emitter. -pub struct AudioPlayer { - event_loop: EventLoop, - paused: AtomicCell, - tx: Sender, -} - -impl AudioPlayer { - pub(crate) fn new(genre: Genre, tx: Sender) -> Self { - match genre { - Genre::Bgm => AudioPlayer::set_mono(tx), - Genre::Sfx => unimplemented!(), - Genre::None => AudioPlayer::set_no_audio(tx), - } - } - - pub(crate) fn load(&self, path: &str) { - self.emit(Action::Load(path.to_string())); - self.set_playing(true); - } - - // pub(crate) fn pause(&mut self) { - // self.paused.store(true); - // self.send(AudioPlayerMsg::AudioStop); - // self.set_playing(false); - // } - - pub(crate) fn resume(&mut self) { - self.paused.store(false); - self.send(AudioPlayerMsg::AudioPlay); - self.set_playing(true); - } - - // pub(crate) fn stop(&mut self) { - // self.paused.store(false); - // self.send(AudioPlayerMsg::AudioStop); - // self.emit(Action::Stop); - // self.set_playing(false); - // } - - pub(crate) fn is_paused(&self) -> bool { - self.paused.load() - } - - pub(crate) fn set_volume(&self, value: f32) { - self.emit(Action::AdjustVolume(value)); - } - - pub(crate) fn set_device(&self, device: &str) { - self.emit(Action::ChangeDevice(device.to_string())); - } - - fn emit(&self, action: Action) { - self.event_loop.queue.push(action); - } - - fn send(&mut self, msg: AudioPlayerMsg) { - send_msg(&mut self.tx, msg); - } - - fn set_playing(&self, playing: bool) { - *self.event_loop.playing.write().unwrap() = playing; - let &(ref lock, ref condvar) = &*self.event_loop.condvar; - let mut started = lock.lock(); - *started = playing; - if playing { - condvar.notify_one(); - } - } -} - -impl MonoMode for AudioPlayer { - /// Playing audio until a receive operation appears on the other side. - fn set_mono(tx: Sender) -> Self { - let event_loop = EventLoop::new(); - - { - //let mut tx = tx.clone(); - let event_loop = event_loop.clone(); - let condition = event_loop.condvar.clone(); - - thread::spawn(move || { - let block = || { - let (ref lock, ref condvar) = *condition; - let mut started = lock.lock(); - *started = false; - while !*started { - condvar.wait(&mut started); - } - }; - let mut playback = MonoEmitter::new(&Settings::load().audio); - - // Start the thread if set_playing(true). - loop { - if let Ok(action) = event_loop.queue.pop() { - match action { - Action::Load(path) => { - if playback.stream.empty() { - playback.play_from(&path); - //send_msg(&mut tx, AudioPlayerMsg::AudioPlay); - } - } - Action::Stop => playback.stream.stop(), - Action::AdjustVolume(value) => playback.set_volume(value), - Action::ChangeDevice(device) => playback.set_device(device), - } - } else { - block(); - } - } - }); - } - - Self { - event_loop, - paused: AtomicCell::new(false), - tx, - } - } -} - -impl DebugMode for AudioPlayer { - /// Don't load `rodio` for `no-audio` feature. - fn set_no_audio(tx: Sender) -> Self { - Self { - event_loop: EventLoop::new(), - paused: AtomicCell::new(true), - tx, - } - } -} - -/// TODO: Implement treeview and modellist widgets for GUI design. -pub struct Jukebox { - genre: AtomicCell, - pub(crate) player: AudioPlayer, -} - -impl Jukebox { - pub(crate) fn new(genre: Genre) -> Self { - let (tx, _rx) = unbounded(); - Self { - genre: AtomicCell::new(genre), - player: AudioPlayer::new(genre, tx), - } - } - - // TODO: The `update` function should associate with `conrod` to visualise the audio playlist - // and settings. - // pub(crate) fn update(&mut self, _msg: AudioPlayerMsg) { - // unimplemented!() - // } - - /// Display the current genre. - pub(crate) fn get_genre(&self) -> Genre { - self.genre.load() - } -} - -struct MonoEmitter { - device: Device, - stream: Sink, -} - -// struct StereoEmitter { -// device: Device, -// stream: SpatialSink, -// } - -impl MonoEmitter { - fn new(settings: &AudioSettings) -> Self { - let device = match &settings.audio_device { - Some(dev) => rodio::output_devices() - .find(|x| &x.name() == dev) - .or_else(rodio::default_output_device) - .expect("No Audio devices found!"), - None => rodio::default_output_device().expect("No Audio devices found!"), - }; - let sink = Sink::new(&device); - sink.set_volume(settings.music_volume); - - Self { - device, - stream: sink, - } - } - - // /// Returns the name of the current audio device. - // /// Does not return rodio Device struct in case our audio backend changes. - // fn get_device(&self) -> String { - // self.device.name() - // } - - fn play_from(&mut self, path: &str) { - let bufreader = BufReader::new(File::open(path).unwrap()); - let src = Decoder::new(bufreader).unwrap(); - self.stream.append(src); - } -} - -impl AudioConfig for MonoEmitter { - fn set_volume(&mut self, volume: f32) { - self.stream.set_volume(volume.min(1.0).max(0.0)) - } - - /// Sets the current audio device from a string. - /// Does not use the rodio Device struct in case that detail changes. - /// If the string is an invalid audio device, then no change is made. - fn set_device(&mut self, name: String) { - if let Some(dev) = rodio::output_devices().find(|x| x.name() == name) { - self.device = dev; - self.stream = Sink::new(&self.device); - } - } -} - -// impl StereoEmitter { -// fn new(settings: &AudioSettings) -> Self { -// let device = match &settings.audio_device { -// Some(dev) => rodio::output_devices() -// .find(|x| &x.name() == dev) -// .or_else(rodio::default_output_device) -// .expect("No Audio devices found!"), -// None => rodio::default_output_device().expect("No Audio devices found!"), -// }; -// let sink = SpatialSink::new( -// &device.device, -// [0.0, 0.0, 0.0], -// [1.0, 0.0, 0.0], -// [-1.0, 0.0, 0.0], -// ); -// sink.set_volume(settings.music_volume); - -// Self { -// device, -// stream: sink, -// } -// } - -// fn play_from(&mut self, path: &str) { -// let bufreader = load_from_path(path).unwrap(); -// let src = Decoder::new(bufreader).unwrap(); -// self.stream.append(src); -// } -// } - -// impl AudioConfig for StereoEmitter { -// fn set_volume(&mut self, volume: f32) { -// self.stream.set_volume(volume.min(1.0).max(0.0)) -// } - -// /// Sets the current audio device from a string. -// /// Does not use the rodio Device struct in case that detail changes. -// /// If the string is an invalid audio device, then no change is made. -// fn set_device(&mut self, name: String) { -// if let Some(dev) = rodio::output_devices().find(|x| x.name() == name) { -// self.device = dev; -// self.stream = SpatialSink::new( -// &self.device, -// [0.0, 0.0, 0.0], -// [1.0, 0.0, 0.0], -// [-1.0, 0.0, 0.0], -// ); -// } -// } -// } - -/// Returns the default audio device. -/// Does not return rodio Device struct in case our audio backend changes. -pub(crate) fn get_default_device() -> String { - rodio::default_output_device() - .expect("No audio output devices detected.") - .name() -} - -/// Load the audio file directory selected by genre. -pub(crate) fn load_soundtracks(genre: &Genre) -> Vec { - match *genre { - Genre::Bgm => { - let assets = read_dir("voxygen.audio.soundtrack").unwrap(); - let soundtracks = assets - .filter_map(|entry| { - entry.ok().map(|f| { - let path = f.path(); - path.to_string_lossy().into_owned() - }) - }) - .collect::>(); - - soundtracks - } - Genre::Sfx => { - let assets = read_dir("voxygen.audio.soundtrack").unwrap(); - let soundtracks = assets - //.filter_map(|entry| { - // entry.ok().and_then(|f| { - // f.path() - // .file_name() - // .and_then(|n| n.to_str().map(|s| String::from(s))) - // }) - //}) - //.collect::>(); - .filter_map(|entry| { - entry.ok().map(|f| { - let path = f.path(); - (*path.into_os_string().to_string_lossy()).to_owned() - }) - }) - .collect::>(); - - soundtracks - } - Genre::None => { - let empty_list = Vec::new(); - empty_list - } - } -} - -pub(crate) fn select_random_music(genre: &Genre) -> String { - let soundtracks = load_soundtracks(genre); - let index = rand::random::() % soundtracks.len(); - soundtracks[index].clone() -} - -/// Returns a vec of the audio devices available. -/// Does not return rodio Device struct in case our audio backend changes. -pub(crate) fn list_devices() -> Vec { - list_devices_raw().iter().map(|x| x.name()).collect() -} - -/// Returns vec of devices -fn list_devices_raw() -> Vec { - rodio::output_devices().collect() -} - -fn send_msg(tx: &mut Sender, msg: AudioPlayerMsg) { - tx.send(msg) - .expect("Failed on attempting to send a message into audio channel."); -} - -#[test] -fn test_load_soundtracks() { - use crate::audio::base::{load_soundtracks, Genre}; - for entry in load_soundtracks(&Genre::Bgm).iter() { - println!("{}", entry) - } -} diff --git a/voxygen/src/audio/channel.rs b/voxygen/src/audio/channel.rs new file mode 100644 index 0000000000..d9a341324c --- /dev/null +++ b/voxygen/src/audio/channel.rs @@ -0,0 +1,137 @@ +use crate::audio::fader::Fader; +use rodio::{Decoder, Device, Sample, Source, SpatialSink}; +use std::fs::File; +use std::io::BufReader; +use vek::*; + +#[derive(PartialEq, Clone, Copy)] +pub enum AudioType { + Sfx, + Music, + None, +} + +#[derive(PartialEq, Clone, Copy)] +enum ChannelState { + // Init, + // ToPlay, + // Loading, + Playing, + Stopping, + Stopped, +} + +pub struct Channel { + id: usize, + sink: SpatialSink, + audio_type: AudioType, + state: ChannelState, + fader: Fader, + pub pos: Vec3, +} + +// TODO: Implement asynchronous loading +impl Channel { + /// Create an empty channel for future use + pub fn new(device: &Device) -> Self { + Self { + id: 0, + sink: SpatialSink::new(device, [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [-1.0, 0.0, 0.0]), + audio_type: AudioType::None, + state: ChannelState::Stopped, + fader: Fader::fade_in(0.0), + pos: Vec3::zero(), + } + } + + pub fn music(id: usize, device: &Device, bufr: BufReader) -> Self { + let sink = SpatialSink::new(device, [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [-1.0, 0.0, 0.0]); + let sound = Decoder::new(bufr).unwrap(); + + sink.append(sound); + + Self { + id, + sink, + audio_type: AudioType::Music, + state: ChannelState::Playing, + fader: Fader::fade_in(0.0), + pos: Vec3::zero(), + } + } + + pub fn sfx(id: usize, sink: SpatialSink, pos: Vec3) -> Self { + Self { + id, + sink, + audio_type: AudioType::Sfx, + state: ChannelState::Playing, + fader: Fader::fade_in(0.0), + pos, + } + } + + pub fn play(&mut self, source: S) + where + S: Source + Send + 'static, + S::Item: Sample, + S::Item: Send, + ::Item: std::fmt::Debug, + { + self.state = ChannelState::Playing; + self.sink.append(source); + } + + pub fn is_done(&self) -> bool { + self.sink.empty() || self.state == ChannelState::Stopped + } + + pub fn stop(&mut self, fader: Fader) { + self.state = ChannelState::Stopping; + self.fader = fader; + } + + pub fn get_id(&self) -> usize { + self.id + } + + pub fn set_id(&mut self, new_id: usize) { + self.id = new_id; + } + + pub fn get_audio_type(&self) -> AudioType { + self.audio_type + } + + pub fn set_volume(&mut self, volume: f32) { + self.sink.set_volume(volume); + } + + pub fn set_emitter_position(&mut self, pos: [f32; 3]) { + self.sink.set_emitter_position(pos); + } + + pub fn set_left_ear_position(&mut self, pos: [f32; 3]) { + self.sink.set_left_ear_position(pos); + } + + pub fn set_right_ear_position(&mut self, pos: [f32; 3]) { + self.sink.set_right_ear_position(pos); + } + + pub fn update(&mut self, dt: f32) { + match self.state { + // ChannelState::Init | ChannelState::ToPlay | ChannelState::Loading => {} + ChannelState::Playing => {} + ChannelState::Stopping => { + self.fader.update(dt); + self.sink.set_volume(self.fader.get_volume()); + + if self.fader.is_finished() { + self.state = ChannelState::Stopped; + } + } + ChannelState::Stopped => {} + } + } +} diff --git a/voxygen/src/audio/fader.rs b/voxygen/src/audio/fader.rs new file mode 100644 index 0000000000..5adf960c5a --- /dev/null +++ b/voxygen/src/audio/fader.rs @@ -0,0 +1,66 @@ +#[derive(PartialEq, Clone, Copy)] +pub struct Fader { + length: f32, + running_time: f32, + volume_from: f32, + volume_to: f32, + is_running: bool, +} + +fn lerp(t: f32, a: f32, b: f32) -> f32 { + (1.0 - t) * a + t * b +} + +impl Fader { + pub fn fade(time: f32, volume_from: f32, volume_to: f32) -> Self { + Self { + length: time, + running_time: 0.0, + volume_from, + volume_to, + is_running: true, + } + } + + pub fn fade_in(time: f32) -> Self { + Self { + length: time, + running_time: 0.0, + volume_from: 0.0, + volume_to: 1.0, + is_running: true, + } + } + + pub fn fade_out(time: f32) -> Self { + Self { + length: time, + running_time: 0.0, + volume_from: 1.0, + volume_to: 0.0, + is_running: true, + } + } + + pub fn update(&mut self, dt: f32) { + if self.is_running { + self.running_time = self.running_time + dt; + if self.running_time >= self.length { + self.running_time = self.length; + self.is_running = false; + } + } + } + + pub fn get_volume(&self) -> f32 { + lerp( + self.running_time / self.length, + self.volume_from, + self.volume_to, + ) + } + + pub fn is_finished(&self) -> bool { + self.running_time >= self.length || !self.is_running + } +} diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index 7de8428049..e723889319 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -1,41 +1,242 @@ -pub mod base; -use base::{Genre, Jukebox}; +pub mod channel; +pub mod fader; +pub mod soundcache; +use channel::{AudioType, Channel}; +use fader::Fader; +use soundcache::SoundCache; + +use common::assets; +use rodio::{Decoder, Device}; +use vek::*; + +const FALLOFF: f32 = 0.13; pub struct AudioFrontend { - pub(crate) model: Jukebox, - pub(crate) default_device: String, - pub(crate) device_list: Vec, + pub device: String, + pub device_list: Vec, + audio_device: Option, + sound_cache: SoundCache, + + channels: Vec, + next_channel_id: usize, + + sfx_volume: f32, + music_volume: f32, + + listener_pos: Vec3, + listener_ori: Vec3, + + listener_ear_left: Vec3, + listener_ear_right: Vec3, } impl AudioFrontend { - pub(crate) fn new() -> Self { + /// Construct with given device + pub fn new(device: String, channel_num: usize) -> Self { + let mut channels = Vec::with_capacity(channel_num); + let audio_device = get_device_raw(&device); + if let Some(audio_device) = &audio_device { + for _i in 0..channel_num { + channels.push(Channel::new(&audio_device)); + } + } Self { - model: Jukebox::new(Genre::Bgm), - default_device: base::get_default_device(), - device_list: base::list_devices(), + device: device.clone(), + device_list: list_devices(), + audio_device, + sound_cache: SoundCache::new(), + channels: channels, + next_channel_id: 1, + sfx_volume: 1.0, + music_volume: 1.0, + listener_pos: Vec3::zero(), + listener_ori: Vec3::zero(), + listener_ear_left: Vec3::zero(), + listener_ear_right: Vec3::zero(), } } - /// Play audio. - pub(crate) fn play(&mut self) { - let path = base::select_random_music(&Genre::Bgm); - - match self.model.player.is_paused() { - true => match self.model.get_genre() { - Genre::Bgm => self.model.player.resume(), - Genre::Sfx => unimplemented!(), // TODO: add support for sound effects. - Genre::None => (), - }, - false => self.model.player.load(&path), + /// Construct in `no-audio` mode for debugging + pub fn no_audio() -> Self { + Self { + device: "none".to_string(), + device_list: list_devices(), + audio_device: None, + sound_cache: SoundCache::new(), + channels: Vec::new(), + next_channel_id: 1, + sfx_volume: 1.0, + music_volume: 1.0, + listener_pos: Vec3::zero(), + listener_ori: Vec3::zero(), + listener_ear_left: Vec3::zero(), + listener_ear_right: Vec3::zero(), } } - /// Construct in `no-audio` mode for debugging. - pub(crate) fn no_audio() -> Self { - Self { - model: Jukebox::new(Genre::None), - default_device: "None".to_owned(), - device_list: Vec::new(), + /// Maintain audio + pub fn maintain(&mut self, dt: f32) { + for channel in self.channels.iter_mut() { + channel.update(dt); } } + + pub fn get_channel(&mut self) -> Option<&mut Channel> { + self.channels.iter_mut().find(|c| c.is_done()) + } + + /// Play specfied sound file. + ///```ignore + ///audio.play_sound("voxygen.audio.sfx.step"); + ///``` + pub fn play_sound(&mut self, sound: &str, pos: Vec3) -> usize { + let id = self.next_channel_id; + self.next_channel_id += 1; + + if let Some(_) = &self.audio_device { + let calc_pos = ((pos - self.listener_pos) * FALLOFF).into_array(); + + let sound = self.sound_cache.load_sound(sound); + + let left_ear = self.listener_ear_left.into_array(); + let right_ear = self.listener_ear_right.into_array(); + + if let Some(channel) = self.get_channel() { + channel.set_id(id); + channel.set_emitter_position(calc_pos); + channel.set_left_ear_position(left_ear); + channel.set_right_ear_position(right_ear); + channel.play(sound); + } else { + log::warn!("No available channels!"); + } + } + + id + } + + pub fn set_listener_pos(&mut self, pos: &Vec3, ori: &Vec3) { + self.listener_pos = pos.clone(); + self.listener_ori = ori.normalized(); + + let up = Vec3::new(0.0, 0.0, 1.0); + + let pos_left = up.cross(self.listener_ori.clone()).normalized(); + let pos_right = self.listener_ori.cross(up.clone()).normalized(); + + self.listener_ear_left = pos_left; + self.listener_ear_right = pos_right; + + for channel in self.channels.iter_mut() { + if channel.get_audio_type() == AudioType::Sfx { + channel.set_emitter_position( + ((channel.pos - self.listener_pos) * FALLOFF).into_array(), + ); + channel.set_left_ear_position(pos_left.into_array()); + channel.set_right_ear_position(pos_right.into_array()); + } + } + } + + pub fn play_music(&mut self, sound: &str) -> usize { + let id = self.next_channel_id; + self.next_channel_id += 1; + + if let Some(_) = &self.audio_device { + let file = assets::load_file(&sound, &["ogg"]).unwrap(); + let sound = Decoder::new(file).unwrap(); + + if let Some(channel) = self.get_channel() { + channel.set_id(id); + channel.play(sound); + } + } + + id + } + + pub fn stop_channel(&mut self, channel_id: usize, fader: Fader) { + let index = self.channels.iter().position(|c| c.get_id() == channel_id); + if let Some(index) = index { + self.channels[index].stop(fader); + } + } + + pub fn get_sfx_volume(&self) -> f32 { + self.sfx_volume + } + + pub fn get_music_volume(&self) -> f32 { + self.music_volume + } + + pub fn set_sfx_volume(&mut self, volume: f32) { + self.sfx_volume = volume; + + for channel in self.channels.iter_mut() { + if channel.get_audio_type() == AudioType::Sfx { + channel.set_volume(volume); + } + } + } + + pub fn set_music_volume(&mut self, volume: f32) { + self.music_volume = volume; + + for channel in self.channels.iter_mut() { + if channel.get_audio_type() == AudioType::Music { + channel.set_volume(volume); + } + } + } + + // TODO: figure out how badly this will break things when it is called + pub fn set_device(&mut self, name: String) { + self.device = name.clone(); + self.audio_device = get_device_raw(&name); + } +} + +pub fn select_random_music() -> String { + let soundtracks = load_soundtracks(); + let index = rand::random::() % soundtracks.len(); + soundtracks[index].clone() +} + +/// Returns the default audio device. +/// Does not return rodio Device struct in case our audio backend changes. +pub fn get_default_device() -> String { + rodio::default_output_device() + .expect("No audio output devices detected.") + .name() +} + +/// Load the audio file directory selected by genre. +pub fn load_soundtracks() -> Vec { + let assets = assets::read_dir("voxygen.audio.soundtrack").unwrap(); + let soundtracks = assets + .filter_map(|entry| { + entry.ok().map(|f| { + let path = f.path(); + path.to_string_lossy().into_owned() + }) + }) + .collect::>(); + + soundtracks +} + +/// Returns a vec of the audio devices available. +/// Does not return rodio Device struct in case our audio backend changes. +pub fn list_devices() -> Vec { + list_devices_raw().iter().map(|x| x.name()).collect() +} + +/// Returns vec of devices +fn list_devices_raw() -> Vec { + rodio::output_devices().collect() +} + +fn get_device_raw(device: &str) -> Option { + rodio::output_devices().find(|d| d.name() == device) } diff --git a/voxygen/src/audio/soundcache.rs b/voxygen/src/audio/soundcache.rs new file mode 100644 index 0000000000..4af0ce9a9a --- /dev/null +++ b/voxygen/src/audio/soundcache.rs @@ -0,0 +1,53 @@ +use common::assets; +use hashbrown::HashMap; +use rodio; +use std::convert::AsRef; +use std::io; +use std::io::Read; +use std::sync::Arc; + +// Implementation of sound taken from this github issue: +// https://github.com/RustAudio/rodio/issues/141 +pub struct Sound(Arc>); + +impl AsRef<[u8]> for Sound { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Sound { + pub fn load(filename: &str) -> Result { + let mut file = assets::load_file(filename, &["wav"])?; + let mut buf = Vec::new(); + file.read_to_end(&mut buf)?; + Ok(Sound(Arc::new(buf))) + } + + pub fn cursor(&self) -> io::Cursor { + io::Cursor::new(Sound(self.0.clone())) + } + + pub fn decoder(&self) -> rodio::Decoder> { + rodio::Decoder::new(self.cursor()).unwrap() + } +} + +pub struct SoundCache { + sounds: HashMap, +} + +impl SoundCache { + pub fn new() -> Self { + Self { + sounds: HashMap::new(), + } + } + + pub fn load_sound(&mut self, name: &str) -> rodio::Decoder> { + self.sounds + .entry(name.to_string()) + .or_insert(Sound::load(name).unwrap()) + .decoder() + } +} diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index fbb64e05b2..682eb3dfd9 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -2,7 +2,6 @@ use super::{ img_ids::Imgs, BarNumbers, CrosshairType, Fonts, ShortcutNumbers, Show, XpBar, TEXT_COLOR, }; use crate::{ - audio::base::Genre, ui::{ImageSlider, ScaleMode, ToggleButton}, GlobalState, }; @@ -1251,39 +1250,26 @@ impl<'a> Widget for SettingsWindow<'a> { } // Audio Device Selector -------------------------------------------- - match self.global_state.audio.model.get_genre() { - Genre::Bgm => { - let device = &self.global_state.audio.default_device; - let device_list = &self.global_state.audio.device_list; - Text::new("Volume") - .down_from(state.ids.audio_volume_slider, 10.0) - .font_size(14) - .font_id(self.fonts.opensans) - .color(TEXT_COLOR) - .set(state.ids.audio_device_text, ui); + let device = &self.global_state.audio.device; + let device_list = &self.global_state.audio.device_list; + Text::new("Volume") + .down_from(state.ids.audio_volume_slider, 10.0) + .font_size(14) + .font_id(self.fonts.opensans) + .color(TEXT_COLOR) + .set(state.ids.audio_device_text, ui); - // Get which device is currently selected - let selected = device_list.iter().position(|x| x.contains(device)); + // Get which device is currently selected + let selected = device_list.iter().position(|x| x.contains(device)); - if let Some(clicked) = DropDownList::new(&device_list, selected) - .w_h(400.0, 22.0) - .down_from(state.ids.audio_device_text, 10.0) - .label_font_id(self.fonts.opensans) - .set(state.ids.audio_device_list, ui) - { - let new_val = device_list[clicked].clone(); - events.push(Event::ChangeAudioDevice(new_val)); - } - } - Genre::Sfx => unimplemented!(), - Genre::None => { - Text::new("Volume") - .down_from(state.ids.audio_volume_slider, 10.0) - .font_size(14) - .font_id(self.fonts.opensans) - .color(TEXT_COLOR) - .set(state.ids.audio_device_text, ui); - } + if let Some(clicked) = DropDownList::new(&device_list, selected) + .w_h(400.0, 22.0) + .down_from(state.ids.audio_device_text, 10.0) + .label_font_id(self.fonts.opensans) + .set(state.ids.audio_device_list, ui) + { + let new_val = device_list[clicked].clone(); + events.push(Event::ChangeAudioDevice(new_val)); } } diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 4afeda5723..a69960ca3b 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -31,10 +31,7 @@ pub mod window; // Reexports pub use crate::error::Error; -use crate::{ - audio::base::Genre, audio::AudioFrontend, menu::main::MainMenuState, settings::Settings, - window::Window, -}; +use crate::{audio::AudioFrontend, menu::main::MainMenuState, settings::Settings, window::Window}; use heaptrack::track_mem; use log::{self, debug, error, info}; @@ -58,9 +55,8 @@ impl GlobalState { self.window.needs_refresh_resize(); } - pub fn maintain(&mut self) { - // TODO: Maintain both `Bgm` and `Sfx` audio threads. - self.audio.play(); + pub fn maintain(&mut self, dt: f32) { + self.audio.maintain(dt); } } @@ -108,6 +104,22 @@ fn main() { if let Err(err) = settings.save_to_file() { panic!("Failed to save settings: {:?}", err); } + let audio_device = match &settings.audio.audio_device { + Some(d) => d.to_string(), + None => audio::get_default_device(), + }; + let audio = if settings.audio.audio_on { + AudioFrontend::new(audio_device, 16) + } else { + AudioFrontend::no_audio() + }; + + let mut global_state = GlobalState { + audio, + window: Window::new(&settings).expect("Failed to create window!"), + settings, + }; + let settings = &global_state.settings; // Initialize logging. let term_log_level = std::env::var_os("VOXYGEN_LOG") @@ -186,39 +198,6 @@ fn main() { default_hook(panic_info); })); - // Set up the global state. - let audio = if settings.audio.audio_on { - AudioFrontend::new() - } else { - AudioFrontend::no_audio() - }; - - let mut global_state = GlobalState { - audio, - window: Window::new(&settings).expect("Failed to create window!"), - settings, - }; - - // Initialize discord. (lazy_static initalise lazily...) - #[cfg(feature = "discord")] - { - match DISCORD_INSTANCE.lock() { - Ok(_disc) => { - //great - } - Err(e) => log::error!("Couldn't init discord: {}", e), - } - } - - match global_state.audio.model.get_genre() { - Genre::Bgm => { - global_state.settings.audio.audio_device = - Some(crate::audio::base::get_default_device()) - } - Genre::Sfx => unimplemented!(), - Genre::None => global_state.settings.audio.audio_device = None, - } - // Set up the initial play state. let mut states: Vec> = vec![Box::new(MainMenuState::new(&mut global_state))]; states diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 5ddfb528ec..ac5010747f 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -78,7 +78,7 @@ impl PlayState for CharSelectionState { } // Maintain global state. - global_state.maintain(); + global_state.maintain(clock.get_last_delta().as_secs_f32()); // Maintain the scene. self.scene.maintain( diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index eb2ce26018..6b8bbca182 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -76,7 +76,7 @@ impl PlayState for MainMenuState { } // Maintain global_state - global_state.maintain(); + global_state.maintain(clock.get_last_delta().as_secs_f32()); // Maintain the UI. for event in self.main_menu_ui.maintain(global_state) { diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 96518f516b..424345d204 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -1,13 +1,16 @@ pub mod camera; pub mod figure; +pub mod sound; pub mod terrain; use self::{ camera::{Camera, CameraMode}, figure::FigureMgr, + sound::SoundMgr, terrain::Terrain, }; use crate::{ + audio::AudioFrontend, render::{ create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals, PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline, @@ -46,6 +49,7 @@ pub struct Scene { loaded_distance: f32, figure_mgr: FigureMgr, + sound_mgr: SoundMgr, } impl Scene { @@ -71,6 +75,7 @@ impl Scene { terrain: Terrain::new(renderer), loaded_distance: 0.0, figure_mgr: FigureMgr::new(), + sound_mgr: SoundMgr::new(), } } @@ -115,7 +120,12 @@ impl Scene { } /// Maintain data such as GPU constant buffers, models, etc. To be called once per tick. - pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { + pub fn maintain( + &mut self, + renderer: &mut Renderer, + audio: &mut AudioFrontend, + client: &Client, + ) { // Get player position. let player_pos = client .state() @@ -219,6 +229,9 @@ impl Scene { // Remove unused figures. self.figure_mgr.clean(client.get_tick()); + + // Maintain audio + self.sound_mgr.maintain(audio, client); } /// Render the scene using the provided `Renderer`. diff --git a/voxygen/src/scene/sound.rs b/voxygen/src/scene/sound.rs new file mode 100644 index 0000000000..96776bdf24 --- /dev/null +++ b/voxygen/src/scene/sound.rs @@ -0,0 +1,67 @@ +use crate::audio::AudioFrontend; +use client::Client; +use common::comp::{Body, CharacterState, MovementState::*, Ori, Pos}; +use hashbrown::HashMap; +use specs::{Entity as EcsEntity, Join}; +use std::time::Instant; +use vek::*; + +pub struct AnimState { + last_step_sound: Instant, +} + +pub struct SoundMgr { + character_states: HashMap, +} + +impl SoundMgr { + pub fn new() -> Self { + Self { + character_states: HashMap::new(), + } + } + + pub fn maintain(&mut self, audio: &mut AudioFrontend, client: &Client) { + let ecs = client.state().ecs(); + // Get player position. + let player_pos = ecs + .read_storage::() + .get(client.entity()) + .map_or(Vec3::zero(), |pos| pos.0); + + let player_ori = ecs + .read_storage::() + .get(client.entity()) + .map_or(Vec3::zero(), |pos| pos.0); + + audio.set_listener_pos(&player_pos, &player_ori); + + for (entity, pos, body, character) in ( + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + ) + .join() + { + if let (Body::Humanoid(_), Some(character)) = (body, character) { + let state = self + .character_states + .entry(entity) + .or_insert_with(|| AnimState { + last_step_sound: Instant::now(), + }); + + if character.movement == Run && state.last_step_sound.elapsed().as_secs_f64() > 0.25 + { + let rand_step = (rand::random::() % 7) + 1; + audio.play_sound( + &format!("voxygen.audio.footsteps.stepdirt_{}", rand_step), + pos.0, + ); + state.last_step_sound = Instant::now(); + } + } + } + } +} diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 7555b8efc7..9710e831ab 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -271,7 +271,7 @@ impl PlayState for SessionState { } // Maintain global state. - global_state.maintain(); + global_state.maintain(clock.get_last_delta().as_secs_f32()); // Extract HUD events ensuring the client borrow gets dropped. let hud_events = self.hud.maintain( @@ -357,13 +357,13 @@ impl PlayState for SessionState { } HudEvent::AdjustVolume(volume) => { - global_state.audio.model.player.set_volume(volume); + global_state.audio.set_music_volume(volume); global_state.settings.audio.music_volume = volume; global_state.settings.save_to_file_warn(); } HudEvent::ChangeAudioDevice(name) => { - global_state.audio.model.player.set_device(&name.clone()); + global_state.audio.set_device(name.clone()); global_state.settings.audio.audio_device = Some(name); global_state.settings.save_to_file_warn(); @@ -388,8 +388,11 @@ impl PlayState for SessionState { } // Maintain the scene. - self.scene - .maintain(global_state.window.renderer_mut(), &self.client.borrow()); + self.scene.maintain( + global_state.window.renderer_mut(), + &mut global_state.audio, + &self.client.borrow(), + ); // Render the session. self.render(global_state.window.renderer_mut());