Add sound effects to calls (#2673)

Mikayla Maki created

This PR adds joined, leaving, mute, and unmute sound effects to Zed. 

Release Notes:

- Added joined, leaving, mute, and unmute sound effects (preview-only)

Change summary

Cargo.lock                 | 478 +++++++++++++++++++++++++++++++++++++--
Cargo.toml                 |   1 
assets/sounds/joined.wav   |   0 
assets/sounds/leave.wav    |   0 
assets/sounds/mute.wav     |   0 
assets/sounds/unmute.wav   |   0 
crates/audio/Cargo.toml    |  23 +
crates/audio/src/assets.rs |  44 +++
crates/audio/src/audio.rs  |  63 +++++
crates/call/Cargo.toml     |   1 
crates/call/src/room.rs    |  95 +++++--
crates/collab/Cargo.toml   |   1 
crates/collab/src/tests.rs |   1 
crates/zed/Cargo.toml      |   1 
crates/zed/src/assets.rs   |   1 
crates/zed/src/main.rs     |   2 
crates/zed/src/zed.rs      |   1 
17 files changed, 657 insertions(+), 55 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -177,6 +177,28 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
 
+[[package]]
+name = "alsa"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44"
+dependencies = [
+ "alsa-sys",
+ "bitflags",
+ "libc",
+ "nix",
+]
+
+[[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 = "ambient-authority"
 version = "0.0.1"
@@ -590,6 +612,19 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
+[[package]]
+name = "audio"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "collections",
+ "gpui",
+ "log",
+ "parking_lot 0.11.2",
+ "rodio",
+ "util",
+]
+
 [[package]]
 name = "auto_update"
 version = "0.1.0"
@@ -756,6 +791,26 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "bindgen"
+version = "0.64.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "peeking_take_while",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "bindgen"
 version = "0.65.1"
@@ -857,7 +912,7 @@ checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7"
 dependencies = [
  "borsh-derive-internal",
  "borsh-schema-derive-internal",
- "proc-macro-crate",
+ "proc-macro-crate 0.1.5",
  "proc-macro2",
  "syn 1.0.109",
 ]
@@ -986,6 +1041,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "async-broadcast",
+ "audio",
  "client",
  "collections",
  "fs",
@@ -1082,6 +1138,12 @@ dependencies = [
  "jobserver",
 ]
 
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
 [[package]]
 name = "cexpr"
 version = "0.6.0"
@@ -1155,7 +1217,7 @@ dependencies = [
  "bitflags",
  "clap_derive 3.2.25",
  "clap_lex 0.2.4",
- "indexmap",
+ "indexmap 1.9.3",
  "once_cell",
  "strsim",
  "termcolor",
@@ -1226,6 +1288,12 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
 
+[[package]]
+name = "claxon"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688"
+
 [[package]]
 name = "cli"
 version = "0.1.0"
@@ -1337,6 +1405,7 @@ version = "0.15.0"
 dependencies = [
  "anyhow",
  "async-tungstenite",
+ "audio",
  "axum",
  "axum-extra",
  "base64 0.13.1",
@@ -1444,6 +1513,16 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
+[[package]]
+name = "combine"
+version = "4.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
+dependencies = [
+ "bytes 1.4.0",
+ "memchr",
+]
+
 [[package]]
 name = "command_palette"
 version = "0.1.0"
@@ -1540,11 +1619,17 @@ name = "core-foundation"
 version = "0.9.3"
 source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85"
 dependencies = [
- "core-foundation-sys",
+ "core-foundation-sys 0.8.3",
  "libc",
  "uuid 0.5.1",
 ]
 
+[[package]]
+name = "core-foundation-sys"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
+
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.3"
@@ -1594,6 +1679,51 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "coreaudio-rs"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb17e2d1795b1996419648915df94bc7103c28f7b48062d7acf4652fc371b2ff"
+dependencies = [
+ "bitflags",
+ "core-foundation-sys 0.6.2",
+ "coreaudio-sys",
+]
+
+[[package]]
+name = "coreaudio-sys"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f034b2258e6c4ade2f73bf87b21047567fb913ee9550837c2316d139b0262b24"
+dependencies = [
+ "bindgen 0.64.0",
+]
+
+[[package]]
+name = "cpal"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c"
+dependencies = [
+ "alsa",
+ "core-foundation-sys 0.8.3",
+ "coreaudio-rs",
+ "dasp_sample",
+ "jni 0.19.0",
+ "js-sys",
+ "libc",
+ "mach2",
+ "ndk",
+ "ndk-context",
+ "oboe",
+ "once_cell",
+ "parking_lot 0.12.1",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows 0.46.0",
+]
+
 [[package]]
 name = "cpp_demangle"
 version = "0.3.5"
@@ -1924,6 +2054,12 @@ dependencies = [
  "parking_lot_core 0.9.7",
 ]
 
+[[package]]
+name = "dasp_sample"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
+
 [[package]]
 name = "data-url"
 version = "0.1.1"
@@ -2233,6 +2369,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "equivalent"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
+
 [[package]]
 name = "erased-serde"
 version = "0.3.25"
@@ -2793,7 +2935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
 dependencies = [
  "fallible-iterator",
- "indexmap",
+ "indexmap 1.9.3",
  "stable_deref_trait",
 ]
 
@@ -2889,7 +3031,7 @@ dependencies = [
  "anyhow",
  "async-task",
  "backtrace",
- "bindgen",
+ "bindgen 0.65.1",
  "block",
  "cc",
  "cocoa",
@@ -2961,7 +3103,7 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http",
- "indexmap",
+ "indexmap 1.9.3",
  "slab",
  "tokio",
  "tokio-util 0.7.8",
@@ -2995,6 +3137,12 @@ dependencies = [
  "ahash 0.8.3",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+
 [[package]]
 name = "hashlink"
 version = "0.8.1"
@@ -3105,6 +3253,12 @@ dependencies = [
  "digest 0.10.6",
 ]
 
+[[package]]
+name = "hound"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1"
+
 [[package]]
 name = "http"
 version = "0.2.9"
@@ -3213,11 +3367,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
 dependencies = [
  "android_system_properties",
- "core-foundation-sys",
+ "core-foundation-sys 0.8.3",
  "iana-time-zone-haiku",
  "js-sys",
  "wasm-bindgen",
- "windows",
+ "windows 0.48.0",
 ]
 
 [[package]]
@@ -3287,6 +3441,16 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "indexmap"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.0",
+]
+
 [[package]]
 name = "indoc"
 version = "1.0.9"
@@ -3459,6 +3623,40 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "jni"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
+dependencies = [
+ "cesu8",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "jni"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c"
+dependencies = [
+ "cesu8",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
 [[package]]
 name = "jobserver"
 version = "0.1.26"
@@ -3661,6 +3859,17 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
 
+[[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.144"
@@ -3893,6 +4102,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "mach2"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "malloc_buf"
 version = "0.0.6"
@@ -3949,7 +4167,7 @@ name = "media"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "bindgen",
+ "bindgen 0.65.1",
  "block",
  "bytes 1.4.0",
  "core-foundation",
@@ -4207,6 +4425,35 @@ dependencies = [
  "tempfile",
 ]
 
+[[package]]
+name = "ndk"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
+dependencies = [
+ "bitflags",
+ "jni-sys",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.4.1+23.1.7779620"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3"
+dependencies = [
+ "jni-sys",
+]
+
 [[package]]
 name = "net2"
 version = "0.2.38"
@@ -4315,6 +4562,17 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "num-derive"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "num-integer"
 version = "0.1.45"
@@ -4367,6 +4625,27 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "num_enum"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
+dependencies = [
+ "proc-macro-crate 1.3.1",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "nvim-rs"
 version = "0.5.0"
@@ -4409,7 +4688,7 @@ checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
 dependencies = [
  "crc32fast",
  "hashbrown 0.11.2",
- "indexmap",
+ "indexmap 1.9.3",
  "memchr",
 ]
 
@@ -4422,6 +4701,38 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "oboe"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0"
+dependencies = [
+ "jni 0.20.0",
+ "ndk",
+ "ndk-context",
+ "num-derive",
+ "num-traits",
+ "oboe-sys",
+]
+
+[[package]]
+name = "oboe-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f44155e7fb718d3cfddcf70690b2b51ac4412f347cd9e4fbe511abe9cd7b5f2"
+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.17.1"
@@ -4711,7 +5022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
 dependencies = [
  "fixedbitset",
- "indexmap",
+ "indexmap 1.9.3",
 ]
 
 [[package]]
@@ -4788,7 +5099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590"
 dependencies = [
  "base64 0.21.0",
- "indexmap",
+ "indexmap 1.9.3",
  "line-wrap",
  "quick-xml",
  "serde",
@@ -4921,6 +5232,16 @@ dependencies = [
  "toml",
 ]
 
+[[package]]
+name = "proc-macro-crate"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+dependencies = [
+ "once_cell",
+ "toml_edit",
+]
+
 [[package]]
 name = "proc-macro-error"
 version = "1.0.4"
@@ -5333,6 +5654,12 @@ dependencies = [
  "rand_core 0.5.1",
 ]
 
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+
 [[package]]
 name = "rayon"
 version = "1.7.0"
@@ -5616,6 +5943,19 @@ dependencies = [
  "rmp",
 ]
 
+[[package]]
+name = "rodio"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdf1d4dea18dff2e9eb6dca123724f8b60ef44ad74a9ad283cdfe025df7e73fa"
+dependencies = [
+ "claxon",
+ "cpal",
+ "hound",
+ "lewton",
+ "symphonia",
+]
+
 [[package]]
 name = "rope"
 version = "0.1.0"
@@ -6117,7 +6457,7 @@ checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
 dependencies = [
  "bitflags",
  "core-foundation",
- "core-foundation-sys",
+ "core-foundation-sys 0.8.3",
  "libc",
  "security-framework-sys",
 ]
@@ -6128,7 +6468,7 @@ version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
 dependencies = [
- "core-foundation-sys",
+ "core-foundation-sys 0.8.3",
  "libc",
 ]
 
@@ -6202,7 +6542,7 @@ version = "1.0.96"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
 dependencies = [
- "indexmap",
+ "indexmap 1.9.3",
  "itoa 1.0.6",
  "ryu",
  "serde",
@@ -6214,7 +6554,7 @@ version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7d7b9ce5b0a63c6269b9623ed828b39259545a6ec0d8a35d6135ad6af6232add"
 dependencies = [
- "indexmap",
+ "indexmap 1.9.3",
  "itoa 0.4.8",
  "ryu",
  "serde",
@@ -6249,7 +6589,7 @@ version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
 dependencies = [
- "indexmap",
+ "indexmap 1.9.3",
  "ryu",
  "serde",
  "yaml-rust",
@@ -6623,7 +6963,7 @@ dependencies = [
  "hex",
  "hkdf",
  "hmac 0.12.1",
- "indexmap",
+ "indexmap 1.9.3",
  "itoa 1.0.6",
  "libc",
  "libsqlite3-sys",
@@ -6774,6 +7114,56 @@ dependencies = [
  "siphasher",
 ]
 
+[[package]]
+name = "symphonia"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62e48dba70095f265fdb269b99619b95d04c89e619538138383e63310b14d941"
+dependencies = [
+ "lazy_static",
+ "symphonia-bundle-mp3",
+ "symphonia-core",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "symphonia-bundle-mp3"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f31d7fece546f1e6973011a9eceae948133bbd18fd3d52f6073b1e38ae6368a"
+dependencies = [
+ "bitflags",
+ "lazy_static",
+ "log",
+ "symphonia-core",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "symphonia-core"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c73eb88fee79705268cc7b742c7bc93a7b76e092ab751d0833866970754142"
+dependencies = [
+ "arrayvec 0.7.2",
+ "bitflags",
+ "bytemuck",
+ "lazy_static",
+ "log",
+]
+
+[[package]]
+name = "symphonia-metadata"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89c3e1937e31d0e068bbe829f66b2f2bfaa28d056365279e0ef897172c3320c0"
+dependencies = [
+ "encoding_rs",
+ "lazy_static",
+ "log",
+ "symphonia-core",
+]
+
 [[package]]
 name = "syn"
 version = "1.0.109"
@@ -6819,7 +7209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a902e9050fca0a5d6877550b769abd2bd1ce8c04634b941dbe2809735e1a1e33"
 dependencies = [
  "cfg-if 1.0.0",
- "core-foundation-sys",
+ "core-foundation-sys 0.8.3",
  "libc",
  "ntapi 0.4.1",
  "once_cell",
@@ -6988,7 +7378,7 @@ dependencies = [
  "anyhow",
  "fs",
  "gpui",
- "indexmap",
+ "indexmap 1.9.3",
  "parking_lot 0.11.2",
  "schemars",
  "serde",
@@ -7294,6 +7684,23 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "toml_datetime"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+
+[[package]]
+name = "toml_edit"
+version = "0.19.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7"
+dependencies = [
+ "indexmap 2.0.0",
+ "toml_datetime",
+ "winnow",
+]
+
 [[package]]
 name = "tonic"
 version = "0.6.2"
@@ -7333,7 +7740,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
 dependencies = [
  "futures-core",
  "futures-util",
- "indexmap",
+ "indexmap 1.9.3",
  "pin-project",
  "pin-project-lite 0.2.9",
  "rand 0.8.5",
@@ -8190,7 +8597,7 @@ version = "0.85.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "570460c58b21e9150d2df0eaaedbb7816c34bcec009ae0dcc976e40ba81463e7"
 dependencies = [
- "indexmap",
+ "indexmap 1.9.3",
 ]
 
 [[package]]
@@ -8204,7 +8611,7 @@ dependencies = [
  "backtrace",
  "bincode",
  "cfg-if 1.0.0",
- "indexmap",
+ "indexmap 1.9.3",
  "lazy_static",
  "libc",
  "log",
@@ -8278,7 +8685,7 @@ dependencies = [
  "anyhow",
  "cranelift-entity",
  "gimli 0.26.2",
- "indexmap",
+ "indexmap 1.9.3",
  "log",
  "more-asserts",
  "object 0.28.4",
@@ -8348,7 +8755,7 @@ dependencies = [
  "backtrace",
  "cc",
  "cfg-if 1.0.0",
- "indexmap",
+ "indexmap 1.9.3",
  "libc",
  "log",
  "mach",
@@ -8604,6 +9011,15 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
 [[package]]
 name = "windows"
 version = "0.48.0"
@@ -8760,6 +9176,15 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
 
+[[package]]
+name = "winnow"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "winreg"
 version = "0.10.1"
@@ -8920,6 +9345,7 @@ dependencies = [
  "async-recursion 0.3.2",
  "async-tar",
  "async-trait",
+ "audio",
  "auto_update",
  "backtrace",
  "breadcrumbs",
@@ -8949,7 +9375,7 @@ dependencies = [
  "gpui",
  "ignore",
  "image",
- "indexmap",
+ "indexmap 1.9.3",
  "install_cli",
  "isahc",
  "journal",

Cargo.toml 🔗

@@ -2,6 +2,7 @@
 members = [
     "crates/activity_indicator",
     "crates/ai",
+    "crates/audio",
     "crates/auto_update",
     "crates/breadcrumbs",
     "crates/call",

crates/audio/Cargo.toml 🔗

@@ -0,0 +1,23 @@
+[package]
+name = "audio"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/audio.rs"
+doctest = false
+
+[dependencies]
+gpui = { path = "../gpui" }
+collections = { path = "../collections" }
+util = { path = "../util" }
+
+rodio = "0.17.1"
+
+log.workspace = true
+
+anyhow.workspace = true
+parking_lot.workspace = true
+
+[dev-dependencies]

crates/audio/src/assets.rs 🔗

@@ -0,0 +1,44 @@
+use std::{io::Cursor, sync::Arc};
+
+use anyhow::Result;
+use collections::HashMap;
+use gpui::{AppContext, AssetSource};
+use rodio::{
+    source::{Buffered, SamplesConverter},
+    Decoder, Source,
+};
+
+type Sound = Buffered<SamplesConverter<Decoder<Cursor<Vec<u8>>>, f32>>;
+
+pub struct SoundRegistry {
+    cache: Arc<parking_lot::Mutex<HashMap<String, Sound>>>,
+    assets: Box<dyn AssetSource>,
+}
+
+impl SoundRegistry {
+    pub fn new(source: impl AssetSource) -> Arc<Self> {
+        Arc::new(Self {
+            cache: Default::default(),
+            assets: Box::new(source),
+        })
+    }
+
+    pub fn global(cx: &AppContext) -> Arc<Self> {
+        cx.global::<Arc<Self>>().clone()
+    }
+
+    pub fn get(&self, name: &str) -> Result<impl Source<Item = f32>> {
+        if let Some(wav) = self.cache.lock().get(name) {
+            return Ok(wav.clone());
+        }
+
+        let path = format!("sounds/{}.wav", name);
+        let bytes = self.assets.load(&path)?.into_owned();
+        let cursor = Cursor::new(bytes);
+        let source = Decoder::new(cursor)?.convert_samples::<f32>().buffered();
+
+        self.cache.lock().insert(name.to_string(), source.clone());
+
+        Ok(source)
+    }
+}

crates/audio/src/audio.rs 🔗

@@ -0,0 +1,63 @@
+use assets::SoundRegistry;
+use gpui::{AppContext, AssetSource};
+use rodio::{OutputStream, OutputStreamHandle};
+use util::ResultExt;
+
+mod assets;
+
+pub fn init(source: impl AssetSource, cx: &mut AppContext) {
+    cx.set_global(SoundRegistry::new(source));
+    cx.set_global(Audio::new());
+}
+
+pub enum Sound {
+    Joined,
+    Leave,
+    Mute,
+    Unmute,
+}
+
+impl Sound {
+    fn file(&self) -> &'static str {
+        match self {
+            Self::Joined => "joined",
+            Self::Leave => "leave",
+            Self::Mute => "mute",
+            Self::Unmute => "unmute",
+        }
+    }
+}
+
+pub struct Audio {
+    _output_stream: Option<OutputStream>,
+    output_handle: Option<OutputStreamHandle>,
+}
+
+impl Audio {
+    pub fn new() -> Self {
+        let (_output_stream, output_handle) = OutputStream::try_default().log_err().unzip();
+
+        Self {
+            _output_stream,
+            output_handle,
+        }
+    }
+
+    pub fn play_sound(sound: Sound, cx: &AppContext) {
+        if !cx.has_global::<Self>() {
+            return;
+        }
+
+        let this = cx.global::<Self>();
+
+        let Some(output_handle) = this.output_handle.as_ref() else {
+            return;
+        };
+
+        let Some(source) = SoundRegistry::global(cx).get(sound.file()).log_err() else {
+        return;
+    };
+
+        output_handle.play_raw(source).log_err();
+    }
+}

crates/call/Cargo.toml 🔗

@@ -19,6 +19,7 @@ test-support = [
 ]
 
 [dependencies]
+audio = { path = "../audio" }
 client = { path = "../client" }
 collections = { path = "../collections" }
 gpui = { path = "../gpui" }

crates/call/src/room.rs 🔗

@@ -3,6 +3,7 @@ use crate::{
     IncomingCall,
 };
 use anyhow::{anyhow, Result};
+use audio::{Audio, Sound};
 use client::{
     proto::{self, PeerId},
     Client, TypedEnvelope, User, UserStore,
@@ -151,6 +152,7 @@ impl Room {
             let connect = room.connect(&connection_info.server_url, &connection_info.token);
             cx.spawn(|this, mut cx| async move {
                 connect.await?;
+
                 this.update(&mut cx, |this, cx| this.share_microphone(cx))
                     .await?;
 
@@ -176,6 +178,8 @@ impl Room {
         let maintain_connection =
             cx.spawn_weak(|this, cx| Self::maintain_connection(this, client.clone(), cx).log_err());
 
+        Audio::play_sound(Sound::Joined, cx);
+
         Self {
             id,
             live_kit: live_kit_room,
@@ -265,6 +269,7 @@ impl Room {
                 room.apply_room_update(room_proto, cx)?;
                 anyhow::Ok(())
             })?;
+
             Ok(room)
         })
     }
@@ -306,6 +311,8 @@ impl Room {
             }
         }
 
+        Audio::play_sound(Sound::Leave, cx);
+
         self.status = RoomStatus::Offline;
         self.remote_participants.clear();
         self.pending_participants.clear();
@@ -656,6 +663,8 @@ impl Room {
                                 },
                             );
 
+                            Audio::play_sound(Sound::Joined, cx);
+
                             if let Some(live_kit) = this.live_kit.as_ref() {
                                 let video_tracks =
                                     live_kit.room.remote_video_tracks(&user.id.to_string());
@@ -922,6 +931,7 @@ impl Room {
         cx.spawn(|this, mut cx| async move {
             let project =
                 Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
+
             this.update(&mut cx, |this, cx| {
                 this.joined_projects.retain(|project| {
                     if let Some(project) = project.upgrade(cx) {
@@ -1227,38 +1237,20 @@ impl Room {
                 })
         })
     }
-    fn set_mute(
-        live_kit: &mut LiveKitRoom,
-        should_mute: bool,
-        cx: &mut ModelContext<Self>,
-    ) -> Result<Task<Result<()>>> {
-        if !should_mute {
-            // clear user muting state.
-            live_kit.muted_by_user = false;
-        }
-        match &mut live_kit.microphone_track {
-            LocalTrack::None => Err(anyhow!("microphone was not shared")),
-            LocalTrack::Pending { muted, .. } => {
-                *muted = should_mute;
-                cx.notify();
-                Ok(Task::Ready(Some(Ok(()))))
-            }
-            LocalTrack::Published {
-                track_publication,
-                muted,
-            } => {
-                *muted = should_mute;
-                cx.notify();
-                Ok(cx.background().spawn(track_publication.set_mute(*muted)))
-            }
-        }
-    }
+
     pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> {
         let should_mute = !self.is_muted();
         if let Some(live_kit) = self.live_kit.as_mut() {
-            let ret = Self::set_mute(live_kit, should_mute, cx);
+            let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?;
             live_kit.muted_by_user = should_mute;
-            ret
+
+            if old_muted == true && live_kit.deafened == true {
+                if let Some(task) = self.toggle_deafen(cx).ok() {
+                    task.detach();
+                }
+            }
+
+            Ok(ret_task)
         } else {
             Err(anyhow!("LiveKit not started"))
         }
@@ -1274,7 +1266,7 @@ impl Room {
             // When deafening, mute user's mic as well.
             // When undeafening, unmute user's mic unless it was manually muted prior to deafening.
             if live_kit.deafened || !live_kit.muted_by_user {
-                mute_task = Some(Self::set_mute(live_kit, live_kit.deafened, cx)?);
+                mute_task = Some(live_kit.set_mute(live_kit.deafened, cx)?.0);
             };
             for participant in self.remote_participants.values() {
                 for track in live_kit
@@ -1347,6 +1339,51 @@ struct LiveKitRoom {
     _maintain_tracks: [Task<()>; 2],
 }
 
+impl LiveKitRoom {
+    fn set_mute(
+        self: &mut LiveKitRoom,
+        should_mute: bool,
+        cx: &mut ModelContext<Room>,
+    ) -> Result<(Task<Result<()>>, bool)> {
+        if !should_mute {
+            // clear user muting state.
+            self.muted_by_user = false;
+        }
+
+        let (result, old_muted) = match &mut self.microphone_track {
+            LocalTrack::None => Err(anyhow!("microphone was not shared")),
+            LocalTrack::Pending { muted, .. } => {
+                let old_muted = *muted;
+                *muted = should_mute;
+                cx.notify();
+                Ok((Task::Ready(Some(Ok(()))), old_muted))
+            }
+            LocalTrack::Published {
+                track_publication,
+                muted,
+            } => {
+                let old_muted = *muted;
+                *muted = should_mute;
+                cx.notify();
+                Ok((
+                    cx.background().spawn(track_publication.set_mute(*muted)),
+                    old_muted,
+                ))
+            }
+        }?;
+
+        if old_muted != should_mute {
+            if should_mute {
+                Audio::play_sound(Sound::Mute, cx);
+            } else {
+                Audio::play_sound(Sound::Unmute, cx);
+            }
+        }
+
+        Ok((result, old_muted))
+    }
+}
+
 enum LocalTrack {
     None,
     Pending {

crates/collab/Cargo.toml 🔗

@@ -14,6 +14,7 @@ name = "seed"
 required-features = ["seed-support"]
 
 [dependencies]
+audio = { path = "../audio" }
 collections = { path = "../collections" }
 live_kit_server = { path = "../live_kit_server" }
 rpc = { path = "../rpc" }

crates/collab/src/tests.rs 🔗

@@ -203,6 +203,7 @@ impl TestServer {
             language::init(cx);
             editor::init_settings(cx);
             workspace::init(app_state.clone(), cx);
+            audio::init((), cx);
             call::init(client.clone(), user_store.clone(), cx);
         });
 

crates/zed/Cargo.toml 🔗

@@ -16,6 +16,7 @@ name = "Zed"
 path = "src/main.rs"
 
 [dependencies]
+audio = { path = "../audio" }
 activity_indicator = { path = "../activity_indicator" }
 auto_update = { path = "../auto_update" }
 breadcrumbs = { path = "../breadcrumbs" }

crates/zed/src/assets.rs 🔗

@@ -7,6 +7,7 @@ use rust_embed::RustEmbed;
 #[include = "fonts/**/*"]
 #[include = "icons/**/*"]
 #[include = "themes/**/*"]
+#[include = "sounds/**/*"]
 #[include = "*.md"]
 #[exclude = "*.DS_Store"]
 pub struct Assets;

crates/zed/src/main.rs 🔗

@@ -180,6 +180,8 @@ fn main() {
             background_actions,
         });
         cx.set_global(Arc::downgrade(&app_state));
+
+        audio::init(Assets, cx);
         auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx);
 
         workspace::init(app_state.clone(), cx);

crates/zed/src/zed.rs 🔗

@@ -2160,6 +2160,7 @@ mod tests {
             state.initialize_workspace = initialize_workspace;
             state.build_window_options = build_window_options;
             theme::init((), cx);
+            audio::init((), cx);
             call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
             workspace::init(app_state.clone(), cx);
             Project::init_settings(cx);