From 19b62211d9f5999f8d74298f78097e8aeb0e3009 Mon Sep 17 00:00:00 2001 From: Benjamin Carlsson Date: Sat, 5 Sep 2026 19:01:51 +0000 Subject: [PATCH] Add opt-in local audio devices to the voice helper (#43079) ## What changed - Add `openDevices` and `setAudioControls` to the helper protocol. Open the default microphone and speaker after runtime initialization and transport negotiation, initially muted and suppressed. - Use CPAL on macOS, GNU Linux, and MSVC Windows, with native audio dependencies confined to `codex-voice-host`. Add ALSA development dependencies to Linux CI. - Pack callback samples into preallocated, bounded queues and service devices every 5 ms. Reject incompatible callback configurations, invalidate queued audio on control transitions, and reject capture backlog after unmute using device timestamps. - Handle recoverable stream underruns and overruns without terminating the helper; terminate on device failure, active audio queue overflow, or stale queued audio. Device opening only establishes local device readiness. Capture and rendered audio references are drained locally; encoding, decoding, and peer audio are not connected by this change. ## Testing Add unit tests for mute boundaries, stale playback invalidation, sample bounds, partial-block packing and timestamps, callback size and timing limits, startup silence, queue overflow, and recoverable stream errors. GitOrigin-RevId: 108aea3d411ef6c12908fe5fb67aaf9cddf4475f --- .../rust-ci-full-nextest-platform.yml | 4 +- .github/workflows/rust-ci-full.yml | 2 +- MODULE.bazel.lock | 15 + codex-rs/Cargo.lock | 187 +++++++++- codex-rs/realtime-webrtc/src/lib.rs | 1 + codex-rs/realtime-webrtc/src/protocol.rs | 11 + codex-rs/voice-host/Cargo.toml | 4 + codex-rs/voice-host/README.md | 36 +- codex-rs/voice-host/src/device_buffers.rs | 194 +++++++++++ .../voice-host/src/device_buffers_tests.rs | 212 ++++++++++++ codex-rs/voice-host/src/devices.rs | 327 ++++++++++++++++++ codex-rs/voice-host/src/devices_tests.rs | 271 +++++++++++++++ .../voice-host/src/devices_unavailable.rs | 21 ++ codex-rs/voice-host/src/main.rs | 42 ++- 14 files changed, 1318 insertions(+), 9 deletions(-) create mode 100644 codex-rs/voice-host/src/device_buffers.rs create mode 100644 codex-rs/voice-host/src/device_buffers_tests.rs create mode 100644 codex-rs/voice-host/src/devices.rs create mode 100644 codex-rs/voice-host/src/devices_tests.rs create mode 100644 codex-rs/voice-host/src/devices_unavailable.rs diff --git a/.github/workflows/rust-ci-full-nextest-platform.yml b/.github/workflows/rust-ci-full-nextest-platform.yml index b8540e858a..474535267a 100644 --- a/.github/workflows/rust-ci-full-nextest-platform.yml +++ b/.github/workflows/rust-ci-full-nextest-platform.yml @@ -82,7 +82,7 @@ jobs: set -euo pipefail if command -v apt-get >/dev/null 2>&1; then sudo apt-get update -y - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential pkg-config libcap-dev bubblewrap + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential pkg-config libcap-dev libasound2-dev bubblewrap fi - uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -297,7 +297,7 @@ jobs: set -euo pipefail if command -v apt-get >/dev/null 2>&1; then sudo apt-get update -y - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev bubblewrap + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev libasound2-dev bubblewrap fi - uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 diff --git a/.github/workflows/rust-ci-full.yml b/.github/workflows/rust-ci-full.yml index 7be29ba5a0..0ad68df35d 100644 --- a/.github/workflows/rust-ci-full.yml +++ b/.github/workflows/rust-ci-full.yml @@ -256,7 +256,7 @@ jobs: set -euo pipefail if command -v apt-get >/dev/null 2>&1; then sudo apt-get update -y - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential pkg-config libcap-dev + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential pkg-config libcap-dev libasound2-dev fi - uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 with: diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 4cb44e82d7..b8550eb260 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -679,6 +679,8 @@ "allocative_0.3.6": "{\"dependencies\":[{\"name\":\"allocative_derive\",\"req\":\"=0.3.6\"},{\"name\":\"anyhow\",\"optional\":true,\"req\":\"^1.0.65\"},{\"name\":\"bumpalo\",\"optional\":true,\"req\":\"^3.11.1\"},{\"name\":\"compact_str\",\"optional\":true,\"req\":\"^0.9\"},{\"name\":\"ctor\",\"req\":\"^1.0.5\"},{\"name\":\"dashmap\",\"optional\":true,\"req\":\"^6.1.0\"},{\"name\":\"either\",\"optional\":true,\"req\":\"^1.8\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.24\"},{\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.16.1\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.2.6\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"inferno\",\"req\":\"^0.11.11\"},{\"name\":\"num-bigint\",\"optional\":true,\"req\":\"^0.4.3\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.21.4\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.11.2\"},{\"name\":\"prost-types\",\"optional\":true,\"req\":\"^0.14.3\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.48\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.12\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.10.0\"},{\"name\":\"sorted_vector_map\",\"optional\":true,\"req\":\"^0.2\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.5\"},{\"name\":\"triomphe\",\"optional\":true,\"req\":\"^0.1.8\"}],\"features\":{\"anyhow\":[\"dep:anyhow\"],\"bumpalo\":[\"dep:bumpalo\"],\"compact_str\":[\"dep:compact_str\"],\"dashmap\":[\"dep:dashmap\"],\"default\":[],\"either\":[\"dep:either\"],\"futures\":[\"dep:futures\"],\"hashbrown\":[\"dep:hashbrown\"],\"indexmap\":[\"dep:indexmap\"],\"num-bigint\":[\"dep:num-bigint\"],\"once_cell\":[\"dep:once_cell\"],\"parking_lot\":[\"dep:parking_lot\"],\"prost-types\":[\"dep:prost-types\"],\"serde_json\":[\"dep:serde_json\"],\"slab\":[\"dep:slab\"],\"smallvec\":[\"dep:smallvec\"],\"sorted_vector_map\":[\"dep:sorted_vector_map\"],\"tokio\":[\"dep:tokio\"],\"triomphe\":[\"dep:triomphe\"]}}", "allocative_derive_0.3.6": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.106\"},{\"name\":\"quote\",\"req\":\"^1.0.45\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.117\"}],\"features\":{}}", "allocator-api2_0.2.21": "{\"dependencies\":[{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"fresh-rust\":[],\"nightly\":[],\"std\":[\"alloc\"]}}", + "alsa-sys_0.4.0": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.72\"},{\"name\":\"libc\",\"req\":\"^0.2.178\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.7\"}],\"features\":{\"use-bindgen\":[\"bindgen\"]}}", + "alsa_0.11.0": "{\"dependencies\":[{\"name\":\"alsa-sys\",\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"bitflags\",\"req\":\"^2.4.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"libc\",\"req\":\"^0.2.178\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "android_system_properties_0.1.5": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.126\"}],\"features\":{}}", "annotate-snippets_0.9.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"difference\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"toml\",\"req\":\"^0.5\"},{\"name\":\"unicode-width\",\"req\":\"^0.1\"},{\"name\":\"yansi-term\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"yansi-term\",\"req\":\"^0.1\"}],\"features\":{\"color\":[\"yansi-term\"],\"default\":[]}}", "ansi-to-tui_8.0.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"eyre\",\"req\":\"^0.6\"},{\"name\":\"nom\",\"req\":\"^8\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"default_features\":false,\"name\":\"ratatui-core\",\"req\":\"^0.1.0\"},{\"name\":\"simdutf8\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"const_generics\"],\"name\":\"smallvec\",\"req\":\"^1\"},{\"name\":\"thiserror\",\"req\":\"^2\"}],\"features\":{\"default\":[\"zero-copy\",\"simd\"],\"simd\":[\"dep:simdutf8\"],\"zero-copy\":[]}}", @@ -870,6 +872,8 @@ "core-foundation_0.10.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"core-foundation-sys\",\"req\":\"^0.8\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"default\":[\"link\"],\"link\":[\"core-foundation-sys/link\"],\"mac_os_10_7_support\":[\"core-foundation-sys/mac_os_10_7_support\"],\"mac_os_10_8_features\":[\"core-foundation-sys/mac_os_10_8_features\"],\"with-uuid\":[\"dep:uuid\"]}}", "core-foundation_0.9.4": "{\"dependencies\":[{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^0.5\"}],\"features\":{\"default\":[\"link\"],\"link\":[\"core-foundation-sys/link\"],\"mac_os_10_7_support\":[\"core-foundation-sys/mac_os_10_7_support\"],\"mac_os_10_8_features\":[\"core-foundation-sys/mac_os_10_8_features\"],\"with-chrono\":[\"chrono\"],\"with-uuid\":[\"uuid\"]}}", "core_maths_0.1.1": "{\"dependencies\":[{\"name\":\"libm\",\"req\":\"^0.2\"}],\"features\":{}}", + "coreaudio-rs_0.14.2": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.10\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"bitflags\",\"libc\",\"objc2-core-foundation\",\"objc2-core-audio\",\"objc2-core-audio-types\",\"AUComponent\",\"AudioCodec\",\"AudioComponent\",\"AudioFormat\",\"AudioOutputUnit\",\"AudioUnitProperties\",\"AudioServices\",\"AudioSession\"],\"name\":\"objc2-audio-toolbox\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"objc2-avf-audio\",\"req\":\"^0.3.2\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"tvos\\\", target_os = \\\"visionos\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"objc2-core-audio-types\",\"AudioHardware\",\"AudioHardwareDeprecated\"],\"name\":\"objc2-core-audio\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\",\"bitflags\",\"CoreAudioBaseTypes\"],\"name\":\"objc2-core-audio-types\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\",\"CFString\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3\"}],\"features\":{\"audio_toolbox\":[\"dep:objc2-audio-toolbox\",\"dep:objc2-core-foundation\",\"dep:objc2-core-audio\",\"dep:objc2-core-audio-types\"],\"audio_unit\":[\"audio_toolbox\"],\"core_audio\":[\"dep:objc2-core-audio\",\"dep:objc2-core-audio-types\"],\"core_midi\":[],\"default\":[\"audio_toolbox\",\"core_audio\"],\"open_al\":[]}}", + "cpal_0.18.2": "{\"dependencies\":[{\"name\":\"alsa\",\"req\":\"^0.11\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\"))\"},{\"name\":\"alsa-sys\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\"))\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"asio-sys\",\"optional\":true,\"req\":\"^0.4.0\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"default_features\":false,\"name\":\"audio_thread_priority\",\"optional\":true,\"req\":\"^0.35\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\"))\"},{\"default_features\":false,\"name\":\"audio_thread_priority\",\"optional\":true,\"req\":\"^0.35\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"default_features\":false,\"name\":\"audio_thread_priority\",\"optional\":true,\"req\":\"^0.35\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"block2\",\"req\":\"^0.6\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"tvos\\\", target_os = \\\"visionos\\\"))\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\">=4.6, <5\"},{\"default_features\":false,\"features\":[\"core_audio\",\"audio_toolbox\"],\"name\":\"coreaudio-rs\",\"req\":\"^0.14.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"name\":\"dasp_sample\",\"req\":\"^0.11\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\"))\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"hound\",\"req\":\"^3.5\"},{\"name\":\"jack\",\"optional\":true,\"req\":\"^0.13.5\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\"))\"},{\"name\":\"jack\",\"optional\":true,\"req\":\"^0.13.5\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"name\":\"jack\",\"optional\":true,\"req\":\"^0.13.5\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"jni\",\"req\":\"^0.22\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"js-sys\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\"))\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"mach2\",\"req\":\"^0.6\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"audio\",\"api-level-26\"],\"name\":\"ndk\",\"req\":\"^0.9\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"ndk-context\",\"req\":\"^0.1\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"num-derive\",\"req\":\"^0.4\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"num-traits\",\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"objc2\",\"req\":\"^0.6\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"AUComponent\",\"AudioUnitProperties\"],\"name\":\"objc2-audio-toolbox\",\"req\":\"^0.3\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"AVAudioSession\",\"AVAudioSessionTypes\"],\"name\":\"objc2-avf-audio\",\"req\":\"^0.3\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"tvos\\\", target_os = \\\"visionos\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"AudioHardware\",\"AudioHardwareDeprecated\",\"objc2\",\"objc2-foundation\"],\"name\":\"objc2-core-audio\",\"req\":\"^0.3\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"CoreAudioBaseTypes\"],\"name\":\"objc2-core-audio-types\",\"req\":\"^0.3\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"name\":\"objc2-core-foundation\",\"req\":\"^0.3.1\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"NSArray\",\"NSString\",\"NSValue\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"features\":[\"block2\",\"NSDictionary\",\"NSNotification\",\"NSOperation\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"tvos\\\", target_os = \\\"visionos\\\"))\"},{\"features\":[\"v0_3_53\"],\"name\":\"pipewire\",\"optional\":true,\"req\":\"^0.10\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\"))\"},{\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(not(target_has_atomic = \\\"64\\\"))\"},{\"name\":\"pulseaudio\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\"))\"},{\"kind\":\"dev\",\"name\":\"ringbuf\",\"req\":\"^0.4\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"wasm-bindgen-futures\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"AudioContext\",\"AudioContextOptions\",\"AudioBuffer\",\"AudioBufferSourceNode\",\"AudioNode\",\"AudioDestinationNode\",\"Window\",\"AudioContextState\"],\"name\":\"web-sys\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"Win32_Media\",\"Win32_Media_Audio\",\"Win32_Foundation\",\"Win32_Devices_Properties\",\"Win32_Media_KernelStreaming\",\"Win32_System_Com_StructuredStorage\",\"Win32_System_Threading\",\"Win32_System_Performance\",\"Win32_Security\",\"Win32_System_SystemServices\",\"Win32_System_Variant\",\"Win32_Media_Multimedia\",\"Win32_UI_Shell_PropertiesSystem\"],\"name\":\"windows\",\"req\":\"^0.62\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"windows-core\",\"req\":\"^0.62\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"asio\":[\"dep:asio-sys\",\"dep:num-traits\"],\"audioworklet\":[\"dep:futures-channel\",\"dep:futures-util\",\"wasm-bindgen\",\"web-sys/Blob\",\"web-sys/BlobPropertyBag\",\"web-sys/Url\",\"web-sys/AudioWorklet\",\"web-sys/AudioWorkletNode\",\"web-sys/AudioWorkletNodeOptions\"],\"custom\":[],\"default\":[],\"jack\":[\"dep:jack\"],\"pipewire\":[\"dep:pipewire\"],\"pulseaudio\":[\"dep:pulseaudio\",\"dep:futures\",\"dep:portable-atomic\"],\"realtime\":[\"dep:audio_thread_priority\",\"dep:alsa-sys\"],\"realtime-dbus\":[\"realtime\",\"audio_thread_priority/with_dbus\"],\"wasm-bindgen\":[\"dep:wasm-bindgen\",\"dep:wasm-bindgen-futures\",\"dep:futures-channel\",\"dep:futures-util\"]}}", "cpufeatures_0.2.17": "{\"dependencies\":[{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"aarch64-linux-android\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_os = \\\"linux\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_vendor = \\\"apple\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(target_arch = \\\"loongarch64\\\", target_os = \\\"linux\\\"))\"}],\"features\":{}}", "cpufeatures_0.3.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_os = \\\"android\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_os = \\\"linux\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_vendor = \\\"apple\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(target_arch = \\\"loongarch64\\\", target_os = \\\"linux\\\"))\"}],\"features\":{}}", "crc-catalog_2.4.0": "{\"dependencies\":[],\"features\":{}}", @@ -913,6 +917,7 @@ "darling_macro_0.23.0": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.23.0\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{}}", "darling_macro_0.24.0": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.24.0\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^3.0.0\"}],\"features\":{}}", "dashmap_6.1.0": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.3.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"crossbeam-utils\",\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"raw\"],\"name\":\"hashbrown\",\"req\":\"^0.14.0\"},{\"name\":\"lock_api\",\"req\":\"^0.4.10\"},{\"name\":\"once_cell\",\"req\":\"^1.18.0\"},{\"name\":\"parking_lot_core\",\"req\":\"^0.9.8\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.7.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.188\"},{\"default_features\":false,\"name\":\"typesize\",\"optional\":true,\"req\":\"^0.1.8\"}],\"features\":{\"inline\":[\"hashbrown/inline-more\"],\"raw-api\":[],\"typesize\":[\"dep:typesize\"]}}", + "dasp_sample_0.11.0": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "data-encoding_2.10.0": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "dbus-secret-service_4.1.0": "{\"dependencies\":[{\"name\":\"aes\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"std\"],\"name\":\"block-padding\",\"optional\":true,\"req\":\"^0.3\"},{\"features\":[\"block-padding\",\"alloc\"],\"name\":\"cbc\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"dbus\",\"req\":\"^0.9\"},{\"name\":\"fastrand\",\"optional\":true,\"req\":\"^2.3\"},{\"name\":\"hkdf\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"num\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.55\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"features\":[\"derive\"],\"name\":\"zeroize\",\"req\":\"^1.8\"}],\"features\":{\"crypto-openssl\":[\"dep:fastrand\",\"dep:num\",\"dep:once_cell\",\"dep:openssl\"],\"crypto-rust\":[\"dep:aes\",\"dep:block-padding\",\"dep:cbc\",\"dep:fastrand\",\"dep:hkdf\",\"dep:num\",\"dep:once_cell\",\"dep:sha2\"],\"vendored\":[\"dbus/vendored\",\"openssl?/vendored\"]}}", "dbus_0.9.10": "{\"dependencies\":[{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"futures-executor\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"libc\",\"req\":\"^0.2.66\"},{\"name\":\"libdbus-sys\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"},{\"features\":[\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\"^0.59.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"futures\":[\"futures-util\",\"futures-channel\"],\"no-string-validation\":[],\"stdfd\":[],\"vendored\":[\"libdbus-sys/vendored\"]}}", @@ -1261,6 +1266,7 @@ "jiff_0.2.24": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.81\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.38\"},{\"kind\":\"dev\",\"name\":\"chrono-tz\",\"req\":\"^0.10.0\"},{\"kind\":\"dev\",\"name\":\"hifitime\",\"req\":\"^3.9.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"humantime\",\"req\":\"^2.1.0\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.39.0\"},{\"name\":\"jiff-static\",\"req\":\"=0.2.24\",\"target\":\"cfg(any())\"},{\"name\":\"jiff-static\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"jiff-tzdb\",\"optional\":true,\"req\":\"^0.1.6\"},{\"name\":\"jiff-tzdb-platform\",\"optional\":true,\"req\":\"^0.1.3\",\"target\":\"cfg(any(windows, target_family = \\\"wasm\\\"))\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.50\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.21\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.21\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"req\":\"^1.10.0\",\"target\":\"cfg(not(target_has_atomic = \\\"ptr\\\"))\"},{\"default_features\":false,\"name\":\"portable-atomic-util\",\"req\":\"^0.2.4\",\"target\":\"cfg(not(target_has_atomic = \\\"ptr\\\"))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.203\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.221\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.117\"},{\"kind\":\"dev\",\"name\":\"serde_yaml\",\"req\":\"^0.9.34\"},{\"kind\":\"dev\",\"name\":\"tabwriter\",\"req\":\"^1.4.0\"},{\"features\":[\"local-offset\",\"macros\",\"parsing\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.36\"},{\"kind\":\"dev\",\"name\":\"time-tz\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"tzfile\",\"req\":\"^0.1.3\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5.0\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.70\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"Win32_Foundation\",\"Win32_System_Time\"],\"name\":\"windows-sys\",\"optional\":true,\"req\":\">=0.52.0, <=0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"alloc\":[\"serde_core?/alloc\",\"portable-atomic-util/alloc\"],\"default\":[\"std\",\"tz-system\",\"tz-fat\",\"tzdb-bundle-platform\",\"tzdb-zoneinfo\",\"tzdb-concatenated\",\"perf-inline\"],\"js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"],\"logging\":[\"dep:log\"],\"perf-inline\":[],\"serde\":[\"dep:serde_core\"],\"static\":[\"static-tz\",\"jiff-static?/tzdb\"],\"static-tz\":[\"dep:jiff-static\"],\"std\":[\"alloc\",\"log?/std\",\"serde_core?/std\"],\"tz-fat\":[\"jiff-static?/tz-fat\"],\"tz-system\":[\"std\",\"dep:windows-sys\"],\"tzdb-bundle-always\":[\"dep:jiff-tzdb\",\"alloc\"],\"tzdb-bundle-platform\":[\"dep:jiff-tzdb-platform\",\"alloc\"],\"tzdb-concatenated\":[\"std\"],\"tzdb-zoneinfo\":[\"std\"]}}", "jni-macros_0.22.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"javac\",\"req\":\"^0.1.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"rusty-fork\",\"req\":\"^0.3.0\"},{\"name\":\"simd_cesu8\",\"req\":\"^1.0.1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"}],\"features\":{}}", "jni-sys-macros_0.4.1": "{\"dependencies\":[{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", + "jni-sys_0.3.1": "{\"dependencies\":[{\"name\":\"jni_sys_04\",\"package\":\"jni-sys\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2\"}],\"features\":{\"default\":[]}}", "jni-sys_0.4.1": "{\"dependencies\":[{\"name\":\"jni-sys-macros\",\"req\":\"^0.4.1\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"}],\"features\":{}}", "jni_0.22.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.13.0\",\"target\":\"cfg(windows)\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"combine\",\"req\":\"^4.1.0\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"name\":\"java-locator\",\"optional\":true,\"req\":\"^0.1.3\",\"target\":\"cfg(not(target_os = \\\"android\\\"))\"},{\"kind\":\"dev\",\"name\":\"javac\",\"req\":\"^0.1.0\"},{\"name\":\"jni-macros\",\"req\":\"=0.22.4\"},{\"name\":\"jni-sys\",\"req\":\"^0.4.1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.8\",\"target\":\"cfg(not(target_os = \\\"android\\\"))\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"kind\":\"dev\",\"name\":\"rusty-fork\",\"req\":\"^0.3.0\"},{\"name\":\"simd_cesu8\",\"req\":\"^1.1.1\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"walkdir\",\"req\":\"^2\"},{\"name\":\"windows-link\",\"req\":\"^0.2\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_System_Threading\",\"Win32_Foundation\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"_cfg_test\":[],\"default\":[],\"invocation\":[\"dep:java-locator\",\"dep:libloading\"]}}", "jobserver_0.1.34": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.3.2\",\"target\":\"cfg(windows)\"},{\"name\":\"libc\",\"req\":\"^0.2.171\",\"target\":\"cfg(unix)\"},{\"features\":[\"fs\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.28.0\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.10.1\"}],\"features\":{}}", @@ -1317,6 +1323,7 @@ "lzma-rs_0.3.0": "{\"dependencies\":[{\"name\":\"byteorder\",\"req\":\"^1.4.3\"},{\"name\":\"crc\",\"req\":\"^3.0.0\"},{\"name\":\"env_logger\",\"optional\":true,\"req\":\"^0.9.0\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.17\"},{\"kind\":\"dev\",\"name\":\"rust-lzma\",\"req\":\"^0.5\"}],\"features\":{\"enable_logging\":[\"env_logger\",\"log\"],\"raw_decoder\":[],\"stream\":[]}}", "lzma-sys_0.1.20": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.34\"},{\"name\":\"libc\",\"req\":\"^0.2.51\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.14\"}],\"features\":{\"static\":[]}}", "mac_address_1.1.8": "{\"dependencies\":[{\"features\":[\"net\"],\"name\":\"nix\",\"req\":\"^0.29\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"macos\\\", target_os = \\\"freebsd\\\", target_os = \\\"netbsd\\\", target_os = \\\"openbsd\\\", target_os = \\\"android\\\", target_os = \\\"illumos\\\"))\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.198\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.59\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.117\"},{\"features\":[\"winerror\",\"ws2def\",\"iphlpapi\"],\"name\":\"winapi\",\"req\":\"^0.3\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "mach2_0.6.0": "{\"dependencies\":[],\"features\":{\"default\":[],\"unstable\":[]}}", "maplit_1.0.2": "{\"dependencies\":[],\"features\":{}}", "matchers_0.2.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"syntax\",\"dfa-build\",\"dfa-search\"],\"name\":\"regex-automata\",\"req\":\"^0.4\"}],\"features\":{\"unicode\":[\"regex-automata/unicode\"]}}", "matchit_0.8.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-router\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.4\"},{\"kind\":\"dev\",\"name\":\"gonzales\",\"req\":\"^0.0.3-beta\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^0.14\"},{\"kind\":\"dev\",\"name\":\"path-tree\",\"req\":\"^0.2.2\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.5.4\"},{\"kind\":\"dev\",\"name\":\"route-recognizer\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"routefinder\",\"req\":\"^0.5.2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"make\",\"util\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.4\"}],\"features\":{\"__test_helpers\":[],\"default\":[]}}", @@ -1347,6 +1354,8 @@ "munge_macro_0.4.7": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"clone-impls\",\"full\",\"parsing\",\"printing\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", "native-tls_0.2.14": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"name\":\"log\",\"req\":\"^0.4.5\",\"target\":\"cfg(not(any(target_os = \\\"windows\\\", target_vendor = \\\"apple\\\")))\"},{\"name\":\"openssl\",\"req\":\"^0.10.69\",\"target\":\"cfg(not(any(target_os = \\\"windows\\\", target_vendor = \\\"apple\\\")))\"},{\"name\":\"openssl-probe\",\"req\":\"^0.1\",\"target\":\"cfg(not(any(target_os = \\\"windows\\\", target_vendor = \\\"apple\\\")))\"},{\"name\":\"openssl-sys\",\"req\":\"^0.9.81\",\"target\":\"cfg(not(any(target_os = \\\"windows\\\", target_vendor = \\\"apple\\\")))\"},{\"name\":\"schannel\",\"req\":\"^0.1.17\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"security-framework\",\"req\":\"^2.0.0\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"name\":\"security-framework-sys\",\"req\":\"^2.0.0\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"name\":\"tempfile\",\"req\":\"^3.1.0\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.0\"},{\"kind\":\"dev\",\"name\":\"test-cert-gen\",\"req\":\"^0.9\"}],\"features\":{\"alpn\":[\"security-framework/alpn\"],\"vendored\":[\"openssl/vendored\"]}}", "ndk-context_0.1.1": "{\"dependencies\":[],\"features\":{}}", + "ndk-sys_0.6.0+11769913": "{\"dependencies\":[{\"name\":\"jni-sys\",\"req\":\"^0.3.0\"}],\"features\":{\"audio\":[],\"bitmap\":[],\"media\":[],\"nativewindow\":[],\"sync\":[],\"test\":[]}}", + "ndk_0.9.0": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.4\"},{\"name\":\"ffi\",\"package\":\"ndk-sys\",\"req\":\"^0.6.0\"},{\"name\":\"jni\",\"optional\":true,\"req\":\"^0.21\"},{\"name\":\"jni-sys\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.3\"},{\"name\":\"log\",\"req\":\"^0.4.6\"},{\"name\":\"num_enum\",\"req\":\"^0.7\"},{\"name\":\"rwh_04\",\"optional\":true,\"package\":\"raw-window-handle\",\"req\":\"^0.4\"},{\"name\":\"rwh_05\",\"optional\":true,\"package\":\"raw-window-handle\",\"req\":\"^0.5\"},{\"name\":\"rwh_06\",\"optional\":true,\"package\":\"raw-window-handle\",\"req\":\"^0.6\"},{\"name\":\"thiserror\",\"req\":\"^1.0.23\"}],\"features\":{\"all\":[\"audio\",\"bitmap\",\"media\",\"nativewindow\",\"sync\",\"api-level-33\",\"rwh_04\",\"rwh_05\",\"rwh_06\"],\"api-level-23\":[],\"api-level-24\":[\"api-level-23\"],\"api-level-25\":[\"api-level-24\"],\"api-level-26\":[\"api-level-25\"],\"api-level-27\":[\"api-level-26\"],\"api-level-28\":[\"api-level-27\"],\"api-level-29\":[\"api-level-28\"],\"api-level-30\":[\"api-level-29\"],\"api-level-31\":[\"api-level-30\"],\"api-level-32\":[\"api-level-31\"],\"api-level-33\":[\"api-level-32\"],\"audio\":[\"ffi/audio\",\"api-level-26\"],\"bitmap\":[\"ffi/bitmap\"],\"default\":[\"rwh_06\"],\"media\":[\"ffi/media\"],\"nativewindow\":[\"ffi/nativewindow\"],\"sync\":[\"ffi/sync\",\"api-level-26\"],\"test\":[\"ffi/test\",\"jni\",\"all\"]}}", "nibble_vec_0.1.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"smallvec\",\"req\":\"^1.0\"}],\"features\":{}}", "nix_0.28.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert-impl\",\"req\":\"^0.1\"},{\"name\":\"bitflags\",\"req\":\"^2.3.1\"},{\"kind\":\"dev\",\"name\":\"caps\",\"req\":\"^0.5.3\",\"target\":\"cfg(any(target_os = \\\"android\\\", target_os = \\\"linux\\\"))\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.1.1\"},{\"features\":[\"extra_traits\"],\"name\":\"libc\",\"req\":\"^0.2.153\"},{\"name\":\"memoffset\",\"optional\":true,\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"name\":\"pin-utils\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"semver\",\"req\":\"^1.0.7\"},{\"kind\":\"dev\",\"name\":\"sysctl\",\"req\":\"^0.4\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.7.1\"}],\"features\":{\"acct\":[],\"aio\":[\"pin-utils\"],\"default\":[],\"dir\":[\"fs\"],\"env\":[],\"event\":[],\"fanotify\":[],\"feature\":[],\"fs\":[],\"hostname\":[],\"inotify\":[],\"ioctl\":[],\"kmod\":[],\"mman\":[],\"mount\":[\"uio\"],\"mqueue\":[\"fs\"],\"net\":[\"socket\"],\"personality\":[],\"poll\":[],\"process\":[],\"pthread\":[],\"ptrace\":[\"process\"],\"quota\":[],\"reboot\":[],\"resource\":[],\"sched\":[\"process\"],\"signal\":[\"process\"],\"socket\":[\"memoffset\"],\"term\":[],\"time\":[],\"ucontext\":[\"signal\"],\"uio\":[],\"user\":[\"feature\"],\"zerocopy\":[\"fs\",\"uio\"]}}", "nix_0.29.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert-impl\",\"req\":\"^0.1\"},{\"name\":\"bitflags\",\"req\":\"^2.3.1\"},{\"kind\":\"dev\",\"name\":\"caps\",\"req\":\"^0.5.3\",\"target\":\"cfg(any(target_os = \\\"android\\\", target_os = \\\"linux\\\"))\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.2\"},{\"features\":[\"extra_traits\"],\"name\":\"libc\",\"req\":\"^0.2.155\"},{\"name\":\"memoffset\",\"optional\":true,\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"name\":\"pin-utils\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"semver\",\"req\":\"^1.0.7\"},{\"kind\":\"dev\",\"name\":\"sysctl\",\"req\":\"^0.4\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.7.1\"}],\"features\":{\"acct\":[],\"aio\":[\"pin-utils\"],\"default\":[],\"dir\":[\"fs\"],\"env\":[],\"event\":[],\"fanotify\":[],\"feature\":[],\"fs\":[],\"hostname\":[],\"inotify\":[],\"ioctl\":[],\"kmod\":[],\"mman\":[],\"mount\":[\"uio\"],\"mqueue\":[\"fs\"],\"net\":[\"socket\"],\"personality\":[],\"poll\":[],\"process\":[],\"pthread\":[],\"ptrace\":[\"process\"],\"quota\":[],\"reboot\":[],\"resource\":[],\"sched\":[\"process\"],\"signal\":[\"process\"],\"socket\":[\"memoffset\"],\"term\":[],\"time\":[],\"ucontext\":[\"signal\"],\"uio\":[],\"user\":[\"feature\"],\"zerocopy\":[\"fs\",\"uio\"]}}", @@ -1370,11 +1379,17 @@ "num-traits_0.2.19": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1\"},{\"name\":\"libm\",\"optional\":true,\"req\":\"^0.2.0\"}],\"features\":{\"default\":[\"std\"],\"i128\":[],\"libm\":[\"dep:libm\"],\"std\":[]}}", "num_0.4.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"num-bigint\",\"optional\":true,\"req\":\"^0.4.5\"},{\"default_features\":false,\"name\":\"num-complex\",\"req\":\"^0.4.6\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-integer\",\"req\":\"^0.1.46\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-iter\",\"req\":\"^0.1.45\"},{\"default_features\":false,\"name\":\"num-rational\",\"req\":\"^0.4.2\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-traits\",\"req\":\"^0.2.19\"}],\"features\":{\"alloc\":[\"dep:num-bigint\",\"num-rational/num-bigint\"],\"default\":[\"std\"],\"libm\":[\"num-complex/libm\",\"num-traits/libm\"],\"num-bigint\":[\"dep:num-bigint\"],\"rand\":[\"num-bigint/rand\",\"num-complex/rand\"],\"serde\":[\"num-bigint/serde\",\"num-complex/serde\",\"num-rational/serde\"],\"std\":[\"dep:num-bigint\",\"num-bigint/std\",\"num-complex/std\",\"num-integer/std\",\"num-iter/std\",\"num-rational/std\",\"num-rational/num-bigint-std\",\"num-traits/std\"]}}", "num_cpus_1.17.0": "{\"dependencies\":[{\"name\":\"hermit-abi\",\"req\":\"^0.5.0\",\"target\":\"cfg(target_os = \\\"hermit\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2.26\",\"target\":\"cfg(not(windows))\"}],\"features\":{}}", + "num_enum_0.7.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.14\"},{\"default_features\":false,\"name\":\"num_enum_derive\",\"req\":\"=0.7.6\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\"},{\"name\":\"rustversion\",\"req\":\"^1.0.4\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.98\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2\"}],\"features\":{\"complex-expressions\":[\"num_enum_derive/complex-expressions\"],\"default\":[\"std\"],\"external_doc\":[],\"std\":[\"num_enum_derive/std\"]}}", + "num_enum_derive_0.7.6": "{\"dependencies\":[{\"name\":\"proc-macro-crate\",\"optional\":true,\"req\":\">=1, <=3\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"derive\",\"extra-traits\",\"parsing\"],\"name\":\"syn\",\"req\":\"^2\"},{\"features\":[\"extra-traits\",\"parsing\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"complex-expressions\":[\"syn/full\"],\"default\":[\"std\"],\"external_doc\":[],\"std\":[\"proc-macro-crate\"]}}", "num_threads_0.1.7": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.107\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"ios\\\", target_os = \\\"freebsd\\\"))\"}],\"features\":{}}", "numtoa_0.2.4": "{\"dependencies\":[],\"features\":{}}", "oauth2_5.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.13\"},{\"name\":\"base64\",\"req\":\">=0.21, <0.23\"},{\"default_features\":false,\"features\":[\"clock\",\"serde\",\"std\",\"wasmbind\"],\"name\":\"chrono\",\"req\":\"^0.4.31\"},{\"name\":\"curl\",\"optional\":true,\"req\":\"^0.4.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"js\"],\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"hmac\",\"req\":\"^0.12\"},{\"name\":\"http\",\"req\":\"^1.0\"},{\"name\":\"rand\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"serde_path_to_error\",\"req\":\"^0.1.2\"},{\"name\":\"sha2\",\"req\":\"^0.10\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"name\":\"ureq\",\"optional\":true,\"req\":\"^2\"},{\"features\":[\"serde\"],\"name\":\"url\",\"req\":\"^2.1\"},{\"features\":[\"v4\"],\"kind\":\"dev\",\"name\":\"uuid\",\"req\":\"^1.10\"}],\"features\":{\"default\":[\"reqwest\",\"rustls-tls\"],\"native-tls\":[\"reqwest/native-tls\"],\"pkce-plain\":[],\"reqwest-blocking\":[\"reqwest/blocking\"],\"rustls-tls\":[\"reqwest/rustls-tls\"],\"timing-resistant-secret-traits\":[]}}", "objc2-app-kit_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CKContainer\",\"CKRecord\",\"CKShare\",\"CKShareMetadata\"],\"name\":\"objc2-cloud-kit\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"NSAttributeDescription\",\"NSEntityDescription\",\"NSFetchRequest\",\"NSManagedObjectContext\",\"NSManagedObjectModel\",\"NSPersistentStoreRequest\",\"NSPropertyDescription\"],\"name\":\"objc2-core-data\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CFCGTypes\",\"CFDate\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGColor\",\"CGColorSpace\",\"CGContext\",\"CGDirectDisplay\",\"CGEventTypes\",\"CGFont\",\"CGImage\",\"CGPath\",\"objc2\"],\"name\":\"objc2-core-graphics\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CIColor\",\"CIContext\",\"CIFilter\",\"CIImage\"],\"name\":\"objc2-core-image\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CTFont\",\"CTFontCollection\",\"CTFontDescriptor\",\"CTGlyphInfo\",\"objc2\"],\"name\":\"objc2-core-text\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CVBase\",\"objc2\"],\"name\":\"objc2-core-video\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGLTypes\"],\"name\":\"objc2-open-gl\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CADisplayLink\",\"CALayer\",\"CAMediaTiming\",\"CAMediaTimingFunction\",\"CAOpenGLLayer\"],\"name\":\"objc2-quartz-core\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"UTType\"],\"name\":\"objc2-uniform-type-identifiers\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"}],\"features\":{\"AppKitDefines\":[],\"AppKitErrors\":[],\"NSATSTypesetter\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibility\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibilityColor\":[\"objc2-foundation/NSString\"],\"NSAccessibilityConstants\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAccessibilityCustomAction\":[\"objc2-foundation/NSString\"],\"NSAccessibilityCustomRotor\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSAccessibilityElement\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibilityProtocols\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSActionCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAdaptiveImageGlyph\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAffineTransform\":[\"objc2-foundation/NSAffineTransform\"],\"NSAlert\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSAlignmentFeedbackFilter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/objc2-core-foundation\"],\"NSAnimation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSAnimationContext\":[\"objc2-foundation/NSDate\"],\"NSAppearance\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAppleScriptExtensions\":[\"objc2-foundation/NSAppleScript\",\"objc2-foundation/NSAttributedString\"],\"NSApplication\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSException\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUserActivity\"],\"NSApplicationScripting\":[\"objc2-foundation/NSArray\"],\"NSArrayController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSAttributedString\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSBackgroundExtensionView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSBezierPath\":[\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSBitmapImageRep\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSBox\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSBrowser\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSBrowserCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSButton\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSButtonCell\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSButtonTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSCIImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCachedImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCandidateListTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSCell\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSClickGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSClipView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewCompositionalLayout\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewFlowLayout\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewGridLayout\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewLayout\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewTransitionLayout\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorList\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSColorPanel\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorPicker\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorPickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColorPicking\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorSampler\":[],\"NSColorSpace\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColorWell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboBox\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboBoxCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboButton\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSController\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSCursor\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCustomImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCustomTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDataAsset\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDatePicker\":[\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSTimeZone\",\"objc2-foundation/objc2-core-foundation\"],\"NSDatePickerCell\":[\"bitflags\",\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTimeZone\"],\"NSDictionaryController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDiffableDataSource\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDirection\":[\"bitflags\"],\"NSDockTile\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDocument\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFilePresenter\",\"objc2-foundation/NSFileVersion\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\"],\"NSDocumentController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSDocumentScripting\":[\"objc2-foundation/NSScriptCommand\",\"objc2-foundation/NSScriptObjectSpecifiers\",\"objc2-foundation/NSScriptStandardSuiteCommands\",\"objc2-foundation/NSString\"],\"NSDragging\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSDraggingItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDraggingSession\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDrawer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSEPSImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSErrors\":[\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSString\"],\"NSEvent\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFilePromiseProvider\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSFilePromiseReceiver\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSFileWrapperExtensions\":[\"objc2-foundation/NSFileWrapper\"],\"NSFont\":[\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFontAssetRequest\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSProgress\"],\"NSFontCollection\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSFontDescriptor\":[\"bitflags\",\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSFontManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSFontPanel\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSForm\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFormCell\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGlassEffectView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSGlyphGenerator\":[\"objc2-foundation/NSAttributedString\"],\"NSGlyphInfo\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSGradient\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSGraphics\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGraphicsContext\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGridView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSGroupTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSHapticFeedback\":[],\"NSHelpManager\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSImage\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSImageCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSImageRep\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSImageView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSInputManager\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSInputServer\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSInterfaceStyle\":[\"objc2-foundation/NSString\"],\"NSItemBadge\":[\"objc2-foundation/NSString\"],\"NSItemProvider\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSKeyValueBinding\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSLayoutAnchor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSLayoutConstraint\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLayoutGuide\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLayoutManager\":[\"bitflags\",\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLevelIndicator\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSLevelIndicatorCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMagnificationGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSMatrix\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMediaLibraryBrowserController\":[\"bitflags\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenu\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSZone\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenuItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMenuItemBadge\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMenuItemCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenuToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMovie\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSNib\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSNibConnector\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSNibControlConnector\":[\"objc2-foundation/NSObject\"],\"NSNibDeclarations\":[],\"NSNibLoading\":[],\"NSNibOutletConnector\":[\"objc2-foundation/NSObject\"],\"NSObjectController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSOpenGL\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSLock\",\"objc2-foundation/NSObject\"],\"NSOpenGLLayer\":[\"objc2-foundation/NSObject\"],\"NSOpenGLView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSOpenPanel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSOutlineView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFInfo\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFPanel\":[\"bitflags\",\"objc2-foundation/NSString\"],\"NSPICTImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPageController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPageLayout\":[\"objc2-foundation/NSArray\"],\"NSPanGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPanel\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSParagraphStyle\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPasteboard\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPasteboardItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSPathCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPathComponentCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPathControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPathControlItem\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentDocument\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFilePresenter\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPopUpButton\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopUpButtonCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopover\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopoverTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPredicateEditor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPredicateEditorRowTemplate\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSComparisonPredicate\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSPressGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"NSPressureConfiguration\":[],\"NSPreviewRepresentingActivityItem\":[\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\"],\"NSPrintInfo\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPrintOperation\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPrintPanel\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSPrinter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSProgressIndicator\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\",\"objc2-foundation/objc2-core-foundation\"],\"NSResponder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUndoManager\"],\"NSRotationGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSRuleEditor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSRulerMarker\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSRulerView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSRunningApplication\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSavePanel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSScreen\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrollView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScroller\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubber\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubberItemView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubberLayout\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSecureTextField\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSegmentedCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSegmentedControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSShadow\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSSharingCollaborationModeRestriction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSharingService\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSSharingServicePickerToolbarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSharingServicePickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSlider\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSliderAccessory\":[\"objc2-foundation/NSObject\"],\"NSSliderCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSliderTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSound\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSpeechRecognizer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSSpeechSynthesizer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSpellChecker\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSOrthography\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/objc2-core-foundation\"],\"NSSpellProtocol\":[],\"NSSplitView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSplitViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSplitViewItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"NSSplitViewItemAccessoryViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSStackView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSStatusBar\":[],\"NSStatusBarButton\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStatusItem\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStepper\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSStepperCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSStepperTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStoryboard\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSString\"],\"NSStoryboardSegue\":[\"objc2-foundation/NSString\"],\"NSStringDrawing\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSwitch\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTabView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTabViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTabViewItem\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableCellView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableColumn\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSTableHeaderCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableHeaderView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableRowView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSEnumerator\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableViewDiffableDataSource\":[],\"NSTableViewRowAction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSText\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextAlternatives\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextAttachment\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextAttachmentCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextCheckingClient\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextCheckingController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\"],\"NSTextContainer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextContent\":[\"objc2-foundation/NSString\"],\"NSTextContentManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\"],\"NSTextField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextFinder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextInputClient\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextInputContext\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"NSTextInsertionIndicator\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextLayoutFragment\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\"],\"NSTextLayoutManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"NSTextLineFragment\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextList\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextListElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSTextRange\":[\"objc2-foundation/NSObjCRuntime\"],\"NSTextSelection\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextSelectionNavigation\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSTextStorage\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextStorageScripting\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\"],\"NSTextTable\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOrthography\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextViewportLayoutController\":[],\"NSTintConfiguration\":[\"objc2-foundation/NSObject\"],\"NSTintProminence\":[],\"NSTitlebarAccessoryViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTokenField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTokenFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSToolbar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSToolbarItem\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSToolbarItemGroup\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTouch\":[\"bitflags\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTouchBar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTrackingArea\":[\"bitflags\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTrackingSeparatorToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTreeController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSTreeNode\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSSortDescriptor\"],\"NSTypesetter\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSUserActivity\":[\"objc2-foundation/NSString\",\"objc2-foundation/NSUserActivity\"],\"NSUserDefaultsController\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUserDefaults\"],\"NSUserInterfaceCompression\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSUserInterfaceItemIdentification\":[\"objc2-foundation/NSString\"],\"NSUserInterfaceItemSearching\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSUserInterfaceLayout\":[],\"NSUserInterfaceValidation\":[],\"NSView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSViewController\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSExtensionContext\",\"objc2-foundation/NSExtensionRequestHandling\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSViewLayoutRegion\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/objc2-core-foundation\"],\"NSVisualEffectView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSWindow\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWindowController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSWindowRestoration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"NSWindowScripting\":[\"objc2-foundation/NSScriptCommand\",\"objc2-foundation/NSScriptStandardSuiteCommands\"],\"NSWindowTab\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\"],\"NSWindowTabGroup\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSWorkspace\":[\"bitflags\",\"objc2-foundation/NSAppleEventDescriptor\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileManager\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWritingToolsCoordinator\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSUUID\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWritingToolsCoordinatorAnimationParameters\":[],\"NSWritingToolsCoordinatorContext\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSUUID\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"AppKitDefines\",\"AppKitErrors\",\"NSATSTypesetter\",\"NSAccessibility\",\"NSAccessibilityColor\",\"NSAccessibilityConstants\",\"NSAccessibilityCustomAction\",\"NSAccessibilityCustomRotor\",\"NSAccessibilityElement\",\"NSAccessibilityProtocols\",\"NSActionCell\",\"NSAdaptiveImageGlyph\",\"NSAffineTransform\",\"NSAlert\",\"NSAlignmentFeedbackFilter\",\"NSAnimation\",\"NSAnimationContext\",\"NSAppearance\",\"NSAppleScriptExtensions\",\"NSApplication\",\"NSApplicationScripting\",\"NSArrayController\",\"NSAttributedString\",\"NSBackgroundExtensionView\",\"NSBezierPath\",\"NSBitmapImageRep\",\"NSBox\",\"NSBrowser\",\"NSBrowserCell\",\"NSButton\",\"NSButtonCell\",\"NSButtonTouchBarItem\",\"NSCIImageRep\",\"NSCachedImageRep\",\"NSCandidateListTouchBarItem\",\"NSCell\",\"NSClickGestureRecognizer\",\"NSClipView\",\"NSCollectionView\",\"NSCollectionViewCompositionalLayout\",\"NSCollectionViewFlowLayout\",\"NSCollectionViewGridLayout\",\"NSCollectionViewLayout\",\"NSCollectionViewTransitionLayout\",\"NSColor\",\"NSColorList\",\"NSColorPanel\",\"NSColorPicker\",\"NSColorPickerTouchBarItem\",\"NSColorPicking\",\"NSColorSampler\",\"NSColorSpace\",\"NSColorWell\",\"NSComboBox\",\"NSComboBoxCell\",\"NSComboButton\",\"NSControl\",\"NSController\",\"NSCursor\",\"NSCustomImageRep\",\"NSCustomTouchBarItem\",\"NSDataAsset\",\"NSDatePicker\",\"NSDatePickerCell\",\"NSDictionaryController\",\"NSDiffableDataSource\",\"NSDirection\",\"NSDockTile\",\"NSDocument\",\"NSDocumentController\",\"NSDocumentScripting\",\"NSDragging\",\"NSDraggingItem\",\"NSDraggingSession\",\"NSDrawer\",\"NSEPSImageRep\",\"NSErrors\",\"NSEvent\",\"NSFilePromiseProvider\",\"NSFilePromiseReceiver\",\"NSFileWrapperExtensions\",\"NSFont\",\"NSFontAssetRequest\",\"NSFontCollection\",\"NSFontDescriptor\",\"NSFontManager\",\"NSFontPanel\",\"NSForm\",\"NSFormCell\",\"NSGestureRecognizer\",\"NSGlassEffectView\",\"NSGlyphGenerator\",\"NSGlyphInfo\",\"NSGradient\",\"NSGraphics\",\"NSGraphicsContext\",\"NSGridView\",\"NSGroupTouchBarItem\",\"NSHapticFeedback\",\"NSHelpManager\",\"NSImage\",\"NSImageCell\",\"NSImageRep\",\"NSImageView\",\"NSInputManager\",\"NSInputServer\",\"NSInterfaceStyle\",\"NSItemBadge\",\"NSItemProvider\",\"NSKeyValueBinding\",\"NSLayoutAnchor\",\"NSLayoutConstraint\",\"NSLayoutGuide\",\"NSLayoutManager\",\"NSLevelIndicator\",\"NSLevelIndicatorCell\",\"NSMagnificationGestureRecognizer\",\"NSMatrix\",\"NSMediaLibraryBrowserController\",\"NSMenu\",\"NSMenuItem\",\"NSMenuItemBadge\",\"NSMenuItemCell\",\"NSMenuToolbarItem\",\"NSMovie\",\"NSNib\",\"NSNibConnector\",\"NSNibControlConnector\",\"NSNibDeclarations\",\"NSNibLoading\",\"NSNibOutletConnector\",\"NSObjectController\",\"NSOpenGL\",\"NSOpenGLLayer\",\"NSOpenGLView\",\"NSOpenPanel\",\"NSOutlineView\",\"NSPDFImageRep\",\"NSPDFInfo\",\"NSPDFPanel\",\"NSPICTImageRep\",\"NSPageController\",\"NSPageLayout\",\"NSPanGestureRecognizer\",\"NSPanel\",\"NSParagraphStyle\",\"NSPasteboard\",\"NSPasteboardItem\",\"NSPathCell\",\"NSPathComponentCell\",\"NSPathControl\",\"NSPathControlItem\",\"NSPersistentDocument\",\"NSPickerTouchBarItem\",\"NSPopUpButton\",\"NSPopUpButtonCell\",\"NSPopover\",\"NSPopoverTouchBarItem\",\"NSPredicateEditor\",\"NSPredicateEditorRowTemplate\",\"NSPressGestureRecognizer\",\"NSPressureConfiguration\",\"NSPreviewRepresentingActivityItem\",\"NSPrintInfo\",\"NSPrintOperation\",\"NSPrintPanel\",\"NSPrinter\",\"NSProgressIndicator\",\"NSResponder\",\"NSRotationGestureRecognizer\",\"NSRuleEditor\",\"NSRulerMarker\",\"NSRulerView\",\"NSRunningApplication\",\"NSSavePanel\",\"NSScreen\",\"NSScrollView\",\"NSScroller\",\"NSScrubber\",\"NSScrubberItemView\",\"NSScrubberLayout\",\"NSSearchField\",\"NSSearchFieldCell\",\"NSSearchToolbarItem\",\"NSSecureTextField\",\"NSSegmentedCell\",\"NSSegmentedControl\",\"NSShadow\",\"NSSharingCollaborationModeRestriction\",\"NSSharingService\",\"NSSharingServicePickerToolbarItem\",\"NSSharingServicePickerTouchBarItem\",\"NSSlider\",\"NSSliderAccessory\",\"NSSliderCell\",\"NSSliderTouchBarItem\",\"NSSound\",\"NSSpeechRecognizer\",\"NSSpeechSynthesizer\",\"NSSpellChecker\",\"NSSpellProtocol\",\"NSSplitView\",\"NSSplitViewController\",\"NSSplitViewItem\",\"NSSplitViewItemAccessoryViewController\",\"NSStackView\",\"NSStatusBar\",\"NSStatusBarButton\",\"NSStatusItem\",\"NSStepper\",\"NSStepperCell\",\"NSStepperTouchBarItem\",\"NSStoryboard\",\"NSStoryboardSegue\",\"NSStringDrawing\",\"NSSwitch\",\"NSTabView\",\"NSTabViewController\",\"NSTabViewItem\",\"NSTableCellView\",\"NSTableColumn\",\"NSTableHeaderCell\",\"NSTableHeaderView\",\"NSTableRowView\",\"NSTableView\",\"NSTableViewDiffableDataSource\",\"NSTableViewRowAction\",\"NSText\",\"NSTextAlternatives\",\"NSTextAttachment\",\"NSTextAttachmentCell\",\"NSTextCheckingClient\",\"NSTextCheckingController\",\"NSTextContainer\",\"NSTextContent\",\"NSTextContentManager\",\"NSTextElement\",\"NSTextField\",\"NSTextFieldCell\",\"NSTextFinder\",\"NSTextInputClient\",\"NSTextInputContext\",\"NSTextInsertionIndicator\",\"NSTextLayoutFragment\",\"NSTextLayoutManager\",\"NSTextLineFragment\",\"NSTextList\",\"NSTextListElement\",\"NSTextRange\",\"NSTextSelection\",\"NSTextSelectionNavigation\",\"NSTextStorage\",\"NSTextStorageScripting\",\"NSTextTable\",\"NSTextView\",\"NSTextViewportLayoutController\",\"NSTintConfiguration\",\"NSTintProminence\",\"NSTitlebarAccessoryViewController\",\"NSTokenField\",\"NSTokenFieldCell\",\"NSToolbar\",\"NSToolbarItem\",\"NSToolbarItemGroup\",\"NSTouch\",\"NSTouchBar\",\"NSTouchBarItem\",\"NSTrackingArea\",\"NSTrackingSeparatorToolbarItem\",\"NSTreeController\",\"NSTreeNode\",\"NSTypesetter\",\"NSUserActivity\",\"NSUserDefaultsController\",\"NSUserInterfaceCompression\",\"NSUserInterfaceItemIdentification\",\"NSUserInterfaceItemSearching\",\"NSUserInterfaceLayout\",\"NSUserInterfaceValidation\",\"NSView\",\"NSViewController\",\"NSViewLayoutRegion\",\"NSVisualEffectView\",\"NSWindow\",\"NSWindowController\",\"NSWindowRestoration\",\"NSWindowScripting\",\"NSWindowTab\",\"NSWindowTabGroup\",\"NSWorkspace\",\"NSWritingToolsCoordinator\",\"NSWritingToolsCoordinatorAnimationParameters\",\"NSWritingToolsCoordinatorContext\",\"bitflags\",\"block2\",\"libc\",\"objc2-cloud-kit\",\"objc2-core-data\",\"objc2-core-foundation\",\"objc2-core-graphics\",\"objc2-core-image\",\"objc2-core-text\",\"objc2-core-video\",\"objc2-quartz-core\"],\"gnustep-1-7\":[\"objc2/gnustep-1-7\",\"block2?/gnustep-1-7\",\"objc2-foundation/gnustep-1-7\",\"objc2-core-data?/gnustep-1-7\",\"objc2-quartz-core?/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2/gnustep-1-8\",\"block2?/gnustep-1-8\",\"objc2-foundation/gnustep-1-8\",\"objc2-core-data?/gnustep-1-8\",\"objc2-quartz-core?/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2/gnustep-1-9\",\"block2?/gnustep-1-9\",\"objc2-foundation/gnustep-1-9\",\"objc2-core-data?/gnustep-1-9\",\"objc2-quartz-core?/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2/gnustep-2-0\",\"block2?/gnustep-2-0\",\"objc2-foundation/gnustep-2-0\",\"objc2-core-data?/gnustep-2-0\",\"objc2-quartz-core?/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2/gnustep-2-1\",\"block2?/gnustep-2-1\",\"objc2-foundation/gnustep-2-1\",\"objc2-core-data?/gnustep-2-1\",\"objc2-quartz-core?/gnustep-2-1\"],\"libc\":[\"dep:libc\"],\"objc2-cloud-kit\":[\"dep:objc2-cloud-kit\"],\"objc2-core-data\":[\"dep:objc2-core-data\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-graphics\":[\"dep:objc2-core-graphics\"],\"objc2-core-image\":[\"dep:objc2-core-image\"],\"objc2-core-text\":[\"dep:objc2-core-text\"],\"objc2-core-video\":[\"dep:objc2-core-video\"],\"objc2-open-gl\":[\"dep:objc2-open-gl\"],\"objc2-quartz-core\":[\"dep:objc2-quartz-core\"],\"objc2-uniform-type-identifiers\":[\"dep:objc2-uniform-type-identifiers\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-audio-toolbox_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\",\"objc2\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"AudioHardware\",\"objc2\"],\"name\":\"objc2-core-audio\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CoreAudioBaseTypes\",\"objc2\"],\"name\":\"objc2-core-audio-types\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CFArray\",\"CFData\",\"CFDate\",\"CFDictionary\",\"CFRunLoop\",\"CFURL\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"MIDICapabilityInquiry\",\"MIDIMessages\",\"MIDIServices\",\"objc2\"],\"name\":\"objc2-core-midi\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"}],\"features\":{\"AUAudioUnit\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSEnumerator\",\"objc2-foundation/NSError\",\"objc2-foundation/NSKeyValueObserving\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"AUAudioUnitImplementation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSExtensionRequestHandling\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"AUCocoaUIView\":[],\"AUComponent\":[\"bitflags\"],\"AUGraph\":[],\"AUParameters\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"AudioCodec\":[\"bitflags\"],\"AudioComponent\":[\"bitflags\"],\"AudioConverter\":[\"bitflags\"],\"AudioFile\":[\"bitflags\"],\"AudioFileStream\":[\"bitflags\"],\"AudioFormat\":[],\"AudioOutputUnit\":[],\"AudioQueue\":[\"bitflags\"],\"AudioServices\":[],\"AudioSession\":[],\"AudioUnit\":[],\"AudioUnitCarbonView\":[],\"AudioUnitParameters\":[],\"AudioUnitProperties\":[\"bitflags\"],\"AudioUnitUtilities\":[],\"AudioWorkInterval\":[],\"CAFFile\":[\"bitflags\"],\"CAShow\":[],\"DefaultAudioOutput\":[],\"ExtendedAudioFile\":[],\"MusicDevice\":[],\"MusicPlayer\":[\"bitflags\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"AUAudioUnit\",\"AUAudioUnitImplementation\",\"AUCocoaUIView\",\"AUComponent\",\"AUGraph\",\"AUParameters\",\"AudioCodec\",\"AudioComponent\",\"AudioConverter\",\"AudioFile\",\"AudioFileStream\",\"AudioFormat\",\"AudioOutputUnit\",\"AudioQueue\",\"AudioServices\",\"AudioSession\",\"AudioUnit\",\"AudioUnitCarbonView\",\"AudioUnitParameters\",\"AudioUnitProperties\",\"AudioUnitUtilities\",\"AudioWorkInterval\",\"CAFFile\",\"CAShow\",\"DefaultAudioOutput\",\"ExtendedAudioFile\",\"MusicDevice\",\"MusicPlayer\",\"bitflags\",\"block2\",\"dispatch2\",\"libc\",\"objc2-core-audio\",\"objc2-core-audio-types\",\"objc2-core-foundation\"],\"dispatch2\":[\"dep:dispatch2\"],\"libc\":[\"dep:libc\"],\"objc2-core-audio\":[\"dep:objc2-core-audio\"],\"objc2-core-audio-types\":[\"dep:objc2-core-audio-types\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-midi\":[\"dep:objc2-core-midi\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-avf-audio_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"AUAudioUnit\",\"AUComponent\",\"AudioComponent\",\"AudioUnitProperties\",\"MusicPlayer\",\"block2\"],\"name\":\"objc2-audio-toolbox\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"},{\"default_features\":false,\"features\":[\"CoreAudioBaseTypes\",\"objc2\"],\"name\":\"objc2-core-audio-types\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CMFormatDescription\",\"objc2\"],\"name\":\"objc2-core-media\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"MIDIServices\",\"objc2\"],\"name\":\"objc2-core-midi\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"}],\"features\":{\"AVAudioApplication\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"AVAudioBuffer\":[\"objc2-foundation/NSObject\"],\"AVAudioChannelLayout\":[\"objc2-foundation/NSObject\"],\"AVAudioConnectionPoint\":[],\"AVAudioConverter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"AVAudioEngine\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"AVAudioEnvironmentNode\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSValue\"],\"AVAudioFile\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"AVAudioFormat\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"AVAudioIONode\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\"],\"AVAudioMixerNode\":[],\"AVAudioMixing\":[],\"AVAudioNode\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSString\"],\"AVAudioPlayer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"AVAudioPlayerNode\":[\"bitflags\"],\"AVAudioRecorder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"AVAudioRoutingArbiter\":[\"objc2-foundation/NSError\"],\"AVAudioSequencer\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"AVAudioSession\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"AVAudioSessionDeprecated\":[\"objc2-foundation/NSError\"],\"AVAudioSessionRoute\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"AVAudioSessionTypes\":[\"bitflags\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"AVAudioSettings\":[\"objc2-foundation/NSString\"],\"AVAudioSinkNode\":[],\"AVAudioSourceNode\":[],\"AVAudioTime\":[\"objc2-foundation/NSDate\"],\"AVAudioTypes\":[],\"AVAudioUnit\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"AVAudioUnitComponent\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\"],\"AVAudioUnitDelay\":[\"objc2-foundation/NSDate\"],\"AVAudioUnitDistortion\":[],\"AVAudioUnitEQ\":[\"objc2-foundation/NSArray\"],\"AVAudioUnitEffect\":[],\"AVAudioUnitGenerator\":[],\"AVAudioUnitMIDIInstrument\":[\"objc2-foundation/NSData\"],\"AVAudioUnitReverb\":[],\"AVAudioUnitSampler\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSURL\"],\"AVAudioUnitTimeEffect\":[],\"AVAudioUnitTimePitch\":[],\"AVAudioUnitVarispeed\":[],\"AVMIDIPlayer\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSURL\"],\"AVMusicEvents\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\"],\"AVSpeechSynthesis\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"AVSpeechSynthesisProvider\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"AVAudioApplication\",\"AVAudioBuffer\",\"AVAudioChannelLayout\",\"AVAudioConnectionPoint\",\"AVAudioConverter\",\"AVAudioEngine\",\"AVAudioEnvironmentNode\",\"AVAudioFile\",\"AVAudioFormat\",\"AVAudioIONode\",\"AVAudioMixerNode\",\"AVAudioMixing\",\"AVAudioNode\",\"AVAudioPlayer\",\"AVAudioPlayerNode\",\"AVAudioRecorder\",\"AVAudioRoutingArbiter\",\"AVAudioSequencer\",\"AVAudioSession\",\"AVAudioSessionDeprecated\",\"AVAudioSessionRoute\",\"AVAudioSessionTypes\",\"AVAudioSettings\",\"AVAudioSinkNode\",\"AVAudioSourceNode\",\"AVAudioTime\",\"AVAudioTypes\",\"AVAudioUnit\",\"AVAudioUnitComponent\",\"AVAudioUnitDelay\",\"AVAudioUnitDistortion\",\"AVAudioUnitEQ\",\"AVAudioUnitEffect\",\"AVAudioUnitGenerator\",\"AVAudioUnitMIDIInstrument\",\"AVAudioUnitReverb\",\"AVAudioUnitSampler\",\"AVAudioUnitTimeEffect\",\"AVAudioUnitTimePitch\",\"AVAudioUnitVarispeed\",\"AVMIDIPlayer\",\"AVMusicEvents\",\"AVSpeechSynthesis\",\"AVSpeechSynthesisProvider\",\"bitflags\",\"block2\",\"objc2-audio-toolbox\",\"objc2-core-audio-types\"],\"objc2-audio-toolbox\":[\"dep:objc2-audio-toolbox\"],\"objc2-core-audio-types\":[\"dep:objc2-core-audio-types\"],\"objc2-core-media\":[\"dep:objc2-core-media\"],\"objc2-core-midi\":[\"dep:objc2-core-midi\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", "objc2-cloud-kit_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CNContact\"],\"name\":\"objc2-contacts\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"features\":[\"CLLocation\"],\"name\":\"objc2-core-location\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"}],\"features\":{\"CKAcceptSharesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKAllowedSharingOptions\":[\"bitflags\",\"objc2-foundation/NSObject\"],\"CKAsset\":[\"objc2-foundation/NSURL\"],\"CKContainer\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CKDatabase\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKDatabaseOperation\":[\"objc2-foundation/NSOperation\"],\"CKDefines\":[],\"CKDiscoverAllUserIdentitiesOperation\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKDiscoverUserIdentitiesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKError\":[\"objc2-foundation/NSString\"],\"CKFetchDatabaseChangesOperation\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKFetchNotificationChangesOperation\":[\"objc2-foundation/NSOperation\"],\"CKFetchRecordChangesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKFetchRecordZoneChangesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKFetchRecordZonesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKFetchRecordsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKFetchShareMetadataOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CKFetchShareParticipantsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKFetchSubscriptionsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKFetchWebAuthTokenOperation\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKLocationSortDescriptor\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/block2\"],\"CKMarkNotificationsReadOperation\":[\"objc2-foundation/NSOperation\"],\"CKModifyBadgeOperation\":[\"objc2-foundation/NSOperation\"],\"CKModifyRecordZonesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKModifyRecordsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKModifySubscriptionsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKNotification\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"CKOperation\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKOperationGroup\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKQuery\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"CKQueryOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKRecord\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"CKRecordID\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKRecordZone\":[\"bitflags\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKRecordZoneID\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKReference\":[\"objc2-foundation/NSObject\"],\"CKServerChangeToken\":[\"objc2-foundation/NSObject\"],\"CKShare\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CKShareAccessRequester\":[\"objc2-foundation/NSObject\"],\"CKShareBlockedIdentity\":[\"objc2-foundation/NSObject\"],\"CKShareMetadata\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKShareParticipant\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKShareRequestAccessOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSURL\"],\"CKSubscription\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"CKSyncEngine\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\"],\"CKSyncEngineConfiguration\":[\"objc2-foundation/NSString\"],\"CKSyncEngineEvent\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"CKSyncEngineRecordZoneChangeBatch\":[\"objc2-foundation/NSArray\"],\"CKSyncEngineState\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"CKSystemSharingUIObserver\":[\"objc2-foundation/NSError\"],\"CKUserIdentity\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPersonNameComponents\",\"objc2-foundation/NSString\"],\"CKUserIdentityLookupInfo\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSItemProvider_CKSharingSupport\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSItemProvider\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CKAcceptSharesOperation\",\"CKAllowedSharingOptions\",\"CKAsset\",\"CKContainer\",\"CKDatabase\",\"CKDatabaseOperation\",\"CKDefines\",\"CKDiscoverAllUserIdentitiesOperation\",\"CKDiscoverUserIdentitiesOperation\",\"CKError\",\"CKFetchDatabaseChangesOperation\",\"CKFetchNotificationChangesOperation\",\"CKFetchRecordChangesOperation\",\"CKFetchRecordZoneChangesOperation\",\"CKFetchRecordZonesOperation\",\"CKFetchRecordsOperation\",\"CKFetchShareMetadataOperation\",\"CKFetchShareParticipantsOperation\",\"CKFetchSubscriptionsOperation\",\"CKFetchWebAuthTokenOperation\",\"CKLocationSortDescriptor\",\"CKMarkNotificationsReadOperation\",\"CKModifyBadgeOperation\",\"CKModifyRecordZonesOperation\",\"CKModifyRecordsOperation\",\"CKModifySubscriptionsOperation\",\"CKNotification\",\"CKOperation\",\"CKOperationGroup\",\"CKQuery\",\"CKQueryOperation\",\"CKRecord\",\"CKRecordID\",\"CKRecordZone\",\"CKRecordZoneID\",\"CKReference\",\"CKServerChangeToken\",\"CKShare\",\"CKShareAccessRequester\",\"CKShareBlockedIdentity\",\"CKShareMetadata\",\"CKShareParticipant\",\"CKShareRequestAccessOperation\",\"CKSubscription\",\"CKSyncEngine\",\"CKSyncEngineConfiguration\",\"CKSyncEngineEvent\",\"CKSyncEngineRecordZoneChangeBatch\",\"CKSyncEngineState\",\"CKSystemSharingUIObserver\",\"CKUserIdentity\",\"CKUserIdentityLookupInfo\",\"NSItemProvider_CKSharingSupport\",\"bitflags\",\"block2\",\"objc2-contacts\",\"objc2-core-location\"],\"objc2-contacts\":[\"dep:objc2-contacts\"],\"objc2-core-location\":[\"dep:objc2-core-location\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-core-audio-types_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.2, <0.8.0\"}],\"features\":{\"AudioSessionTypes\":[],\"CoreAudioBaseTypes\":[\"bitflags\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"default\":[\"std\",\"AudioSessionTypes\",\"CoreAudioBaseTypes\",\"bitflags\",\"objc2\"],\"objc2\":[\"dep:objc2\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-core-audio_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CoreAudioBaseTypes\"],\"name\":\"objc2-core-audio-types\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"name\":\"objc2-core-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"NSArray\",\"NSString\",\"NSUUID\",\"NSValue\",\"alloc\"],\"name\":\"objc2-foundation\",\"optional\":true,\"req\":\"^0.3.2\"}],\"features\":{\"AudioHardware\":[\"objc2-core-foundation/CFDictionary\"],\"AudioHardwareDeprecated\":[\"objc2-core-foundation/CFRunLoop\"],\"AudioServerPlugIn\":[],\"HostTime\":[],\"alloc\":[],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"AudioHardware\",\"AudioHardwareDeprecated\",\"AudioServerPlugIn\",\"HostTime\",\"block2\",\"dispatch2\",\"libc\",\"objc2\",\"objc2-core-audio-types\",\"objc2-foundation\"],\"dispatch2\":[\"dep:dispatch2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"dispatch2?/objc2\",\"objc2-core-audio-types?/objc2\",\"objc2-core-foundation/objc2\"],\"objc2-core-audio-types\":[\"dep:objc2-core-audio-types\"],\"objc2-foundation\":[\"dep:objc2-foundation\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", "objc2-core-data_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CKContainer\",\"CKDatabase\",\"CKRecord\",\"CKRecordID\",\"CKRecordZoneID\",\"CKShare\",\"CKShareMetadata\",\"CKShareParticipant\",\"CKUserIdentityLookupInfo\"],\"name\":\"objc2-cloud-kit\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CSSearchableIndex\",\"CSSearchableItemAttributeSet\"],\"name\":\"objc2-core-spotlight\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(any(target_os = \\\"tvos\\\", target_os = \\\"watchos\\\")))\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"}],\"features\":{\"CloudKit\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\"],\"CoreDataDefines\":[],\"CoreDataErrors\":[\"objc2-foundation/NSString\"],\"NSAtomicStore\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSAtomicStoreCacheNode\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSAttributeDescription\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSBatchDeleteRequest\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"NSBatchInsertRequest\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSBatchUpdateRequest\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSCompositeAttributeDescription\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"NSCoreDataCoreSpotlightDelegate\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"NSCustomMigrationStage\":[\"objc2-foundation/NSError\"],\"NSDerivedAttributeDescription\":[\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\"],\"NSEntityDescription\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSEnumerator\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSEntityMapping\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSString\"],\"NSEntityMigrationPolicy\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSExpressionDescription\":[\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\"],\"NSFetchIndexDescription\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSFetchIndexElementDescription\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSFetchRequest\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSFetchRequestExpression\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\"],\"NSFetchedPropertyDescription\":[\"objc2-foundation/NSObject\"],\"NSFetchedResultsController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSOrderedCollectionDifference\",\"objc2-foundation/NSString\",\"NSFetchRequest\"],\"NSIncrementalStore\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSIncrementalStoreNode\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSLightweightMigrationStage\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSManagedObject\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSKeyValueObserving\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSManagedObjectContext\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSLock\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUndoManager\"],\"NSManagedObjectID\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSURL\"],\"NSManagedObjectModel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSEnumerator\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSManagedObjectModelReference\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSMappingModel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSMergePolicy\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSMigrationManager\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSMigrationStage\":[\"objc2-foundation/NSString\"],\"NSPersistentCloudKitContainer\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSPersistentCloudKitContainerEvent\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUUID\"],\"NSPersistentCloudKitContainerEventRequest\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"NSPersistentCloudKitContainerOptions\":[\"objc2-foundation/NSString\"],\"NSPersistentContainer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentHistoryChange\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\"],\"NSPersistentHistoryChangeRequest\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"NSPersistentHistoryToken\":[\"objc2-foundation/NSObject\"],\"NSPersistentHistoryTransaction\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPersistentStore\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentStoreCoordinator\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSLock\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\"],\"NSPersistentStoreDescription\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentStoreRequest\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"NSPersistentStoreResult\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSProgress\",\"NSFetchRequest\"],\"NSPropertyDescription\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSPropertyMapping\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSString\"],\"NSQueryGenerationToken\":[\"objc2-foundation/NSObject\"],\"NSRelationshipDescription\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\"],\"NSSaveChangesRequest\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\"],\"NSStagedMigrationManager\":[\"objc2-foundation/NSArray\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CloudKit\",\"CoreDataDefines\",\"CoreDataErrors\",\"NSAtomicStore\",\"NSAtomicStoreCacheNode\",\"NSAttributeDescription\",\"NSBatchDeleteRequest\",\"NSBatchInsertRequest\",\"NSBatchUpdateRequest\",\"NSCompositeAttributeDescription\",\"NSCoreDataCoreSpotlightDelegate\",\"NSCustomMigrationStage\",\"NSDerivedAttributeDescription\",\"NSEntityDescription\",\"NSEntityMapping\",\"NSEntityMigrationPolicy\",\"NSExpressionDescription\",\"NSFetchIndexDescription\",\"NSFetchIndexElementDescription\",\"NSFetchRequest\",\"NSFetchRequestExpression\",\"NSFetchedPropertyDescription\",\"NSFetchedResultsController\",\"NSIncrementalStore\",\"NSIncrementalStoreNode\",\"NSLightweightMigrationStage\",\"NSManagedObject\",\"NSManagedObjectContext\",\"NSManagedObjectID\",\"NSManagedObjectModel\",\"NSManagedObjectModelReference\",\"NSMappingModel\",\"NSMergePolicy\",\"NSMigrationManager\",\"NSMigrationStage\",\"NSPersistentCloudKitContainer\",\"NSPersistentCloudKitContainerEvent\",\"NSPersistentCloudKitContainerEventRequest\",\"NSPersistentCloudKitContainerOptions\",\"NSPersistentContainer\",\"NSPersistentHistoryChange\",\"NSPersistentHistoryChangeRequest\",\"NSPersistentHistoryToken\",\"NSPersistentHistoryTransaction\",\"NSPersistentStore\",\"NSPersistentStoreCoordinator\",\"NSPersistentStoreDescription\",\"NSPersistentStoreRequest\",\"NSPersistentStoreResult\",\"NSPropertyDescription\",\"NSPropertyMapping\",\"NSQueryGenerationToken\",\"NSRelationshipDescription\",\"NSSaveChangesRequest\",\"NSStagedMigrationManager\",\"bitflags\",\"block2\",\"objc2-cloud-kit\"],\"gnustep-1-7\":[],\"gnustep-1-8\":[],\"gnustep-1-9\":[],\"gnustep-2-0\":[],\"gnustep-2-1\":[],\"objc2-cloud-kit\":[\"dep:objc2-cloud-kit\"],\"objc2-core-spotlight\":[\"dep:objc2-core-spotlight\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", "objc2-core-foundation_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.2, <0.8.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"CFArray\":[],\"CFAttributedString\":[],\"CFAvailability\":[],\"CFBag\":[],\"CFBase\":[],\"CFBinaryHeap\":[],\"CFBitVector\":[],\"CFBundle\":[],\"CFByteOrder\":[],\"CFCGTypes\":[],\"CFCalendar\":[\"bitflags\"],\"CFCharacterSet\":[],\"CFData\":[\"bitflags\"],\"CFDate\":[\"bitflags\"],\"CFDateFormatter\":[\"bitflags\"],\"CFDictionary\":[],\"CFError\":[],\"CFFileDescriptor\":[],\"CFFileSecurity\":[\"bitflags\"],\"CFLocale\":[],\"CFMachPort\":[],\"CFMessagePort\":[],\"CFNotificationCenter\":[],\"CFNumber\":[],\"CFNumberFormatter\":[\"bitflags\"],\"CFPlugIn\":[],\"CFPlugInCOM\":[],\"CFPreferences\":[],\"CFPropertyList\":[\"bitflags\"],\"CFRunLoop\":[\"bitflags\"],\"CFSet\":[],\"CFSocket\":[\"bitflags\"],\"CFStream\":[\"bitflags\"],\"CFString\":[\"bitflags\"],\"CFStringEncodingExt\":[],\"CFStringTokenizer\":[\"bitflags\"],\"CFTimeZone\":[],\"CFTree\":[],\"CFURL\":[\"bitflags\"],\"CFURLAccess\":[],\"CFURLEnumerator\":[\"bitflags\"],\"CFUUID\":[],\"CFUserNotification\":[],\"CFUtilities\":[],\"CFXMLNode\":[],\"CFXMLParser\":[\"bitflags\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CFArray\",\"CFAttributedString\",\"CFAvailability\",\"CFBag\",\"CFBinaryHeap\",\"CFBitVector\",\"CFBundle\",\"CFByteOrder\",\"CFCGTypes\",\"CFCalendar\",\"CFCharacterSet\",\"CFData\",\"CFDate\",\"CFDateFormatter\",\"CFDictionary\",\"CFError\",\"CFFileDescriptor\",\"CFFileSecurity\",\"CFLocale\",\"CFMachPort\",\"CFMessagePort\",\"CFNotificationCenter\",\"CFNumber\",\"CFNumberFormatter\",\"CFPlugIn\",\"CFPlugInCOM\",\"CFPreferences\",\"CFPropertyList\",\"CFRunLoop\",\"CFSet\",\"CFSocket\",\"CFStream\",\"CFString\",\"CFStringEncodingExt\",\"CFStringTokenizer\",\"CFTimeZone\",\"CFTree\",\"CFURL\",\"CFURLAccess\",\"CFURLEnumerator\",\"CFUUID\",\"CFUserNotification\",\"CFUtilities\",\"CFXMLNode\",\"CFXMLParser\",\"bitflags\",\"block2\",\"dispatch2\",\"libc\",\"objc2\"],\"dispatch2\":[\"dep:dispatch2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"dispatch2?/objc2\"],\"std\":[\"alloc\"],\"unstable-coerce-pointee\":[],\"unstable-darwin-objc\":[]}}", "objc2-core-graphics_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"name\":\"objc2-core-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"IOSurfaceRef\"],\"name\":\"objc2-io-surface\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"},{\"default_features\":false,\"features\":[\"MTLDevice\"],\"name\":\"objc2-metal\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"}],\"features\":{\"CGAffineTransform\":[\"objc2-core-foundation/CFCGTypes\"],\"CGBase\":[],\"CGBitmapContext\":[\"bitflags\",\"objc2-core-foundation/CFByteOrder\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFError\"],\"CGColor\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGColorConversionInfo\":[\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFError\"],\"CGColorSpace\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\"],\"CGContext\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGConvertColorDataWithFormat\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDataConsumer\":[\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFURL\"],\"CGDataProvider\":[\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFURL\"],\"CGDirectDisplay\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDirectDisplayMetal\":[],\"CGDirectPalette\":[],\"CGDisplayConfiguration\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDisplayFade\":[],\"CGDisplayStream\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFRunLoop\"],\"CGEXRToneMappingGamma\":[\"objc2-core-foundation/CFDictionary\"],\"CGError\":[],\"CGEvent\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFMachPort\"],\"CGEventSource\":[\"objc2-core-foundation/CFDate\"],\"CGEventTypes\":[\"bitflags\"],\"CGFont\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFDictionary\"],\"CGFunction\":[\"objc2-core-foundation/CFCGTypes\"],\"CGGeometry\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGGradient\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGITUToneMapping\":[],\"CGImage\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\"],\"CGLayer\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGPDFArray\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFContentStream\":[\"objc2-core-foundation/CFArray\"],\"CGPDFContext\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFURL\"],\"CGPDFDictionary\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFDocument\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFURL\"],\"CGPDFObject\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFOperatorTable\":[],\"CGPDFPage\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFScanner\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFStream\":[\"objc2-core-foundation/CFData\"],\"CGPDFString\":[\"objc2-core-foundation/CFDate\"],\"CGPSConverter\":[\"objc2-core-foundation/CFDictionary\"],\"CGPath\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGPattern\":[\"objc2-core-foundation/CFCGTypes\"],\"CGRemoteOperation\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDate\",\"objc2-core-foundation/CFMachPort\"],\"CGRenderingBufferProvider\":[\"objc2-core-foundation/CFData\"],\"CGSession\":[\"objc2-core-foundation/CFDictionary\"],\"CGShading\":[\"objc2-core-foundation/CFCGTypes\"],\"CGToneMapping\":[\"objc2-core-foundation/CFDictionary\"],\"CGWindow\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGWindowLevel\":[],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CGAffineTransform\",\"CGBase\",\"CGBitmapContext\",\"CGColor\",\"CGColorConversionInfo\",\"CGColorSpace\",\"CGContext\",\"CGConvertColorDataWithFormat\",\"CGDataConsumer\",\"CGDataProvider\",\"CGDirectDisplay\",\"CGDirectDisplayMetal\",\"CGDirectPalette\",\"CGDisplayConfiguration\",\"CGDisplayFade\",\"CGDisplayStream\",\"CGEXRToneMappingGamma\",\"CGError\",\"CGEvent\",\"CGEventSource\",\"CGEventTypes\",\"CGFont\",\"CGFunction\",\"CGGeometry\",\"CGGradient\",\"CGITUToneMapping\",\"CGImage\",\"CGLayer\",\"CGPDFArray\",\"CGPDFContentStream\",\"CGPDFContext\",\"CGPDFDictionary\",\"CGPDFDocument\",\"CGPDFObject\",\"CGPDFOperatorTable\",\"CGPDFPage\",\"CGPDFScanner\",\"CGPDFStream\",\"CGPDFString\",\"CGPSConverter\",\"CGPath\",\"CGPattern\",\"CGRemoteOperation\",\"CGRenderingBufferProvider\",\"CGSession\",\"CGShading\",\"CGToneMapping\",\"CGWindow\",\"CGWindowLevel\",\"bitflags\",\"block2\",\"dispatch2\",\"libc\",\"objc2\",\"objc2-metal\"],\"dispatch2\":[\"dep:dispatch2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"dispatch2?/objc2\",\"objc2-core-foundation/objc2\",\"objc2-io-surface?/objc2\"],\"objc2-io-surface\":[\"dep:objc2-io-surface\"],\"objc2-metal\":[\"dep:objc2-metal\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index d8ee36ddc1..3c921f6465 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -305,6 +305,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.13.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_system_properties" version = "0.1.5" @@ -4970,6 +4992,8 @@ dependencies = [ "codex-process-hardening", "codex-realtime-webrtc", "codex-utils-cargo-bin", + "cpal", + "crossbeam-queue", "futures", "libloading", "pretty_assertions", @@ -5359,6 +5383,50 @@ dependencies = [ "zstd", ] +[[package]] +name = "coreaudio-rs" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5d7dca3ebcf65a035582c9ad4385371a9d9ee6537474d2a278f4e1e475bb58" +dependencies = [ + "bitflags 2.13.1", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "cpal" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f02e8d0327b42d3e2e4ab2119af397344eb9fc54a34bf0ddeaa1277af8681f1" +dependencies = [ + "alsa", + "block2", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "objc2", + "objc2-audio-toolbox", + "objc2-avf-audio", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation", + "web-sys", + "windows 0.62.2", + "windows-core 0.62.2", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -5808,6 +5876,12 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + [[package]] name = "data-encoding" version = "2.10.0" @@ -9125,7 +9199,7 @@ dependencies = [ "cfg-if", "combine", "jni-macros", - "jni-sys", + "jni-sys 0.4.1", "log", "simd_cesu8", "thiserror 2.0.18", @@ -9146,6 +9220,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + [[package]] name = "jni-sys" version = "0.4.1" @@ -9598,6 +9681,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "mach2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" + [[package]] name = "maplit" version = "1.0.2" @@ -9841,12 +9930,35 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.1", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "thiserror 1.0.69", +] + [[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.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + [[package]] name = "nibble_vec" version = "0.1.0" @@ -10093,6 +10205,28 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -10149,6 +10283,32 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" +dependencies = [ + "bitflags 2.13.1", + "libc", + "objc2", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-avf-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a380031deed8e99db00065c45937da434ca987c034e13b87e4441f9e4090be" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-cloud-kit" version = "0.3.2" @@ -10160,6 +10320,29 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "dispatch2", + "objc2", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation", +] + +[[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.13.1", + "objc2", +] + [[package]] name = "objc2-core-data" version = "0.3.2" @@ -10177,7 +10360,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.13.1", + "block2", "dispatch2", + "libc", "objc2", ] diff --git a/codex-rs/realtime-webrtc/src/lib.rs b/codex-rs/realtime-webrtc/src/lib.rs index 3e21ba0889..388235be1f 100644 --- a/codex-rs/realtime-webrtc/src/lib.rs +++ b/codex-rs/realtime-webrtc/src/lib.rs @@ -3,6 +3,7 @@ mod message_reader; mod protocol; pub use client::VoiceHost; +pub use protocol::AudioControls; pub use protocol::MAX_FRAME_BYTES; pub use protocol::Message; pub use protocol::RUNTIME_ENVIRONMENT; diff --git a/codex-rs/realtime-webrtc/src/protocol.rs b/codex-rs/realtime-webrtc/src/protocol.rs index 1eee1c4c92..83e3d574a5 100644 --- a/codex-rs/realtime-webrtc/src/protocol.rs +++ b/codex-rs/realtime-webrtc/src/protocol.rs @@ -8,6 +8,13 @@ use serde::Serialize; pub const MAX_FRAME_BYTES: usize = 128 * 1024; +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct AudioControls { + pub microphone_muted: bool, + pub speaker_suppressed: bool, +} + /// SDP contains ICE credentials. Bound it at construction and never expose it in diagnostics. #[derive(Deserialize, PartialEq, Serialize)] #[serde(try_from = "String")] @@ -66,6 +73,10 @@ pub enum Message { Offer { sdp: SessionDescription }, ApplyAnswer { sdp: SessionDescription }, TransportReady {}, + OpenDevices {}, + DevicesOpened {}, + SetAudioControls { controls: AudioControls }, + AudioControlsApplied {}, Close {}, Closed {}, } diff --git a/codex-rs/voice-host/Cargo.toml b/codex-rs/voice-host/Cargo.toml index b0f1f35684..03328ef698 100644 --- a/codex-rs/voice-host/Cargo.toml +++ b/codex-rs/voice-host/Cargo.toml @@ -20,6 +20,10 @@ rtc = "=0.20.3" tokio = { workspace = true, features = ["rt-multi-thread", "sync", "time"] } webrtc = "=0.20.3" +[target.'cfg(any(target_os = "macos", all(target_os = "linux", target_env = "gnu"), all(windows, target_env = "msvc")))'.dependencies] +cpal = "=0.18.2" +crossbeam-queue = "0.3.12" + [dev-dependencies] anyhow = { workspace = true } codex-install-context = { workspace = true } diff --git a/codex-rs/voice-host/README.md b/codex-rs/voice-host/README.md index ac230b96dd..dfe1d753b1 100644 --- a/codex-rs/voice-host/README.md +++ b/codex-rs/voice-host/README.md @@ -1,8 +1,8 @@ # Private voice helper foundation `codex-voice-host` establishes the inherited-pipe lifecycle for the proposed -bundled voice process and owns WebRTC negotiation. It does not open devices or -enable voice in the TUI. The existing CLI is unchanged. +bundled voice process and owns WebRTC negotiation and opt-in local devices. +It does not enable voice in the TUI. The existing CLI is unchanged. Frames are a big-endian u32 length followed by at most 128 KiB of JSON. SDP is limited to 64 KiB and redacted in diagnostics. The @@ -22,6 +22,38 @@ without native audio initialization and does not establish audio readiness. Negotiation has a deadline; `close` tears down the peer before acknowledging exit. UDP and TCP peer tests use real sockets, without a backend or audio devices. +After native initialization and transport negotiation, `openDevices` opens the +default microphone and speaker, initially muted and suppressed. `setAudioControls` +invalidates old queued audio. Device errors or queue overflow end the helper. +Startup callbacks emit silence without collecting references until worker service +begins. After unmute, capture waits for a following callback's device timestamp +and rejects buffers captured before that cutoff, including buffers crossing it. +This can discard speech onset; it relies on backend timestamp estimates and does +not establish a precise physical mute boundary. +Both streams request roughly 10 ms callbacks within the device's supported range +and the 8,192-frame queue budget. Unknown or incompatible ranges are rejected; +there is no fallback to an unbounded default. Backends may deliver smaller +callbacks than requested, so capture and rendered references pack samples into +full 256-frame queue slots across callbacks without allocating. Each block keeps +its oldest sample timestamp. Partial blocks are discarded on generation changes +or rejected capture callbacks, including mute and invalid or backwards capture timestamps. +Packing retains fewer than 256 samples (5.33 ms at 48 kHz; 32 ms at 8 kHz); +delivery also waits for the next callback. Speaker rendering remains immediate. +Selected callbacks must leave room for packing and the 5 ms service interval before capture becomes +stale at one second. The queue's sample capacity must span more than that service +interval. +Processing lag can still overflow a queue. Before devices open, the worker blocks +on commands instead of polling. +`devicesOpened` confirms device opening only: capture/reference are drained locally +until the following media stage connects encoding, decoding and the peer. + +Cargo Linux builds require ALSA development inputs discoverable by pkg-config +(for example `libasound2-dev` on Debian/Ubuntu). Bazel uses the declared `alsa_lib` +source target through `alsa-sys`; macOS uses CoreAudio and Windows uses WASAPI. +CPAL and its native link inputs belong only to the helper. Producing a closed +Linux distribution still requires the prepared ALSA SDK/runtime/configuration +closure; a successful source build does not establish that packaging proof. + Bazel stamps the binary with `STABLE_GIT_COMMIT`. Cargo builders must provide the same variable; an unstamped source build reports `dev` via `--build-commit` and is not a distributable build identity. The client/control crate has no native audio diff --git a/codex-rs/voice-host/src/device_buffers.rs b/codex-rs/voice-host/src/device_buffers.rs new file mode 100644 index 0000000000..6ff6e3ace5 --- /dev/null +++ b/codex-rs/voice-host/src/device_buffers.rs @@ -0,0 +1,194 @@ +//! Preallocated callback buffers pack small callbacks into full queue slots without locks. +//! Generation changes and rejected capture buffers discard incomplete frames. + +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering; +use std::time::Duration; +use std::time::Instant; + +use crossbeam_queue::ArrayQueue; + +pub(super) const BLOCK: usize = 256; +pub(super) const QUEUE_CAPACITY: usize = 32; + +pub(super) struct Frame { + pub(super) samples: [f32; BLOCK], + pub(super) len: usize, + pub(super) at: Instant, + pub(super) generation: u64, +} + +/// Packs serialized callbacks without allocating; only complete blocks enter the queue. +/// The oldest sample keeps its timestamp. Callers reset after rejected capture data. +#[derive(Default)] +pub(super) struct FramePacker { + frame: Option, +} + +impl FramePacker { + pub(super) fn reset(&mut self) { + self.frame = None; + } + + pub(super) fn push(&mut self, frame: Frame, rate: f64, queue: &ArrayQueue) -> bool { + if self + .frame + .as_ref() + .is_some_and(|partial| partial.generation != frame.generation) + { + self.reset(); + } + let mut offset = 0; + while offset < frame.len { + let partial = self.frame.get_or_insert_with(|| Frame { + samples: [0.0; BLOCK], + len: 0, + at: frame.at + Duration::from_secs_f64(offset as f64 / rate), + generation: frame.generation, + }); + let count = (BLOCK - partial.len).min(frame.len - offset); + partial.samples[partial.len..partial.len + count] + .copy_from_slice(&frame.samples[offset..offset + count]); + partial.len += count; + offset += count; + if partial.len == BLOCK + && let Some(full) = self.frame.take() + && let Err(rejected) = queue.push(full) + { + // Suppressed output is known silence; dropping it cannot lose audible output. + // Never discard an active generation or a block containing real audio. + if rejected.generation.is_multiple_of(2) + || rejected.samples.iter().any(|sample| *sample != 0.0) + { + return false; + } + } + } + true + } +} + +pub(super) struct Buffers { + pub(super) capture: ArrayQueue, + pub(super) rendered: ArrayQueue, + pub(super) playback: ArrayQueue, + pub(super) microphone: AtomicU64, + pub(super) speaker: AtomicU64, + pub(super) serviced: AtomicBool, + pub(super) failed: AtomicBool, +} + +impl Buffers { + pub(super) fn new() -> Self { + Self { + capture: ArrayQueue::new(QUEUE_CAPACITY), + rendered: ArrayQueue::new(QUEUE_CAPACITY), + playback: ArrayQueue::new(QUEUE_CAPACITY), + microphone: AtomicU64::new(/*v*/ 1), + speaker: AtomicU64::new(/*v*/ 1), + serviced: AtomicBool::new(false), + failed: AtomicBool::new(false), + } + } + + // One control worker writes each epoch. Odd epochs are disabled; every + // transition advances the epoch so disable/re-enable cannot replay old audio. + pub(super) fn set_disabled(epoch: &AtomicU64, disabled: bool) -> std::io::Result<()> { + let current = epoch.load(Ordering::Acquire); + if (current % 2 == 1) != disabled { + epoch.store( + current + .checked_add(1) + .ok_or_else(|| std::io::Error::other("audio generation exhausted"))?, + Ordering::Release, + ); + } + Ok(()) + } +} + +/// Rejects capture backlog after unmute, using offsets in the device's clock. +/// This is conservative software admission, not a guarantee of hardware clock accuracy. +#[derive(Default)] +pub(super) struct CaptureBoundary { + generation: Option, + cutoff: Option, + previous_capture: Option, +} + +impl CaptureBoundary { + pub(super) fn accepts( + &mut self, + generation: u64, + callback: Duration, + capture: Duration, + ) -> bool { + if generation % 2 == 1 || self.generation != Some(generation) { + self.generation = Some(generation); + self.cutoff = None; + self.previous_capture = None; + // The current callback timestamp may have been sampled before unmute. + // Wait for the next serialized callback before establishing a cutoff. + return false; + } + let accepted = capture >= *self.cutoff.get_or_insert(callback) + && self + .previous_capture + .is_none_or(|previous| capture >= previous); + if accepted { + self.previous_capture = Some(capture); + } + accepted + } +} + +#[derive(Default)] +pub(super) struct Playback { + frame: Option, + offset: usize, +} + +impl Playback { + pub(super) fn next(&mut self, buffers: &Buffers) -> f32 { + let epoch = buffers.speaker.load(Ordering::Acquire); + if self + .frame + .as_ref() + .is_some_and(|frame| frame.generation != epoch || self.offset == frame.len) + { + self.frame = None; + } + // Bound stale-frame work even if a producer keeps writing during a mute. + for _ in 0..buffers.playback.capacity() { + if self.frame.is_some() { + break; + } + let Some(frame) = buffers.playback.pop() else { + break; + }; + if epoch.is_multiple_of(2) + && frame.generation == epoch + && frame.len > 0 + && frame.len <= BLOCK + { + self.frame = Some(frame); + self.offset = 0; + } + } + let Some(frame) = &self.frame else { + return 0.0; + }; + let sample = frame.samples[self.offset]; + self.offset += 1; + if epoch % 2 == 1 || !sample.is_finite() { + 0.0 + } else { + sample.clamp(-1.0, 1.0) + } + } +} + +#[cfg(test)] +#[path = "device_buffers_tests.rs"] +mod tests; diff --git a/codex-rs/voice-host/src/device_buffers_tests.rs b/codex-rs/voice-host/src/device_buffers_tests.rs new file mode 100644 index 0000000000..b81e38385d --- /dev/null +++ b/codex-rs/voice-host/src/device_buffers_tests.rs @@ -0,0 +1,212 @@ +use super::*; +use pretty_assertions::assert_eq; + +#[test] +fn unmute_rejects_backlog_until_capture_reaches_the_following_callback() { + let mut boundary = CaptureBoundary::default(); + // Times are offsets in the same device clock. The first callback's timestamp + // can precede unmute, so even capture after that timestamp must be rejected. + for (generation, callback, capture, expected) in [ + (1, 20, 10, false), // muted + (2, 21, 15, false), // first observed unmute + (2, 25, 22, false), // next callback establishes cutoff 25 + (2, 26, 24, false), // another old/crossing buffer + (2, 27, 25, true), + (2, 28, 26, true), // same generation must not move the cutoff + (2, 29, 25, false), // backwards device capture is a discontinuity + (2, 30, 26, true), // forward capture can resume without moving the cutoff + (4, 30, 29, false), // mute/unmute happened without a callback + (4, 34, 31, false), + (4, 35, 33, false), + (4, 36, 34, true), + (5, 37, 35, false), // observed mute + (6, 38, 36, false), + (6, 40, 40, true), + (6, 101, 100, true), + (6, 102, 90, false), + (6, 103, 95, false), // rejection must not lower the last accepted timestamp + (6, 104, 101, true), + ] { + assert_eq!( + boundary.accepts( + generation, + Duration::from_millis(callback), + Duration::from_millis(capture), + ), + expected, + "generation={generation}, callback={callback}, capture={capture}", + ); + } +} + +#[test] +fn suppression_discards_partial_and_queued_previous_generations() { + let buffers = Buffers::new(); + let mut playback = Playback::default(); + Buffers::set_disabled(&buffers.speaker, /*disabled*/ false).unwrap(); + let generation = buffers.speaker.load(Ordering::Acquire); + for _ in 0..2 { + assert!( + buffers + .playback + .push(Frame { + samples: [0.5; BLOCK], + len: BLOCK, + at: Instant::now(), + generation + }) + .is_ok() + ); + } + assert_eq!(playback.next(&buffers), 0.5); + Buffers::set_disabled(&buffers.speaker, /*disabled*/ true).unwrap(); + assert_eq!(playback.next(&buffers), 0.0); + Buffers::set_disabled(&buffers.speaker, /*disabled*/ false).unwrap(); + assert_eq!(playback.next(&buffers), 0.0); + assert!(buffers.playback.is_empty()); +} + +#[test] +fn output_is_finite_and_bounded_and_underflow_is_silence() { + let buffers = Buffers::new(); + Buffers::set_disabled(&buffers.speaker, /*disabled*/ false).unwrap(); + let generation = buffers.speaker.load(Ordering::Acquire); + let mut samples = [0.0; BLOCK]; + samples[..4].copy_from_slice(&[f32::NAN, f32::INFINITY, -2.0, 2.0]); + assert!( + buffers + .playback + .push(Frame { + samples, + len: 4, + at: Instant::now(), + generation + }) + .is_ok() + ); + let mut playback = Playback::default(); + let output: Vec<_> = (0..5).map(|_| playback.next(&buffers)).collect(); + assert_eq!(output, [0.0, 0.0, -1.0, 1.0, 0.0]); +} + +#[test] +fn repeated_controls_preserve_epoch_but_transitions_invalidate_capture() { + let buffers = Buffers::new(); + Buffers::set_disabled(&buffers.microphone, /*disabled*/ false).unwrap(); + let first = buffers.microphone.load(Ordering::Acquire); + Buffers::set_disabled(&buffers.microphone, /*disabled*/ false).unwrap(); + assert_eq!(buffers.microphone.load(Ordering::Acquire), first); + Buffers::set_disabled(&buffers.microphone, /*disabled*/ true).unwrap(); + Buffers::set_disabled(&buffers.microphone, /*disabled*/ false).unwrap(); + assert_ne!(buffers.microphone.load(Ordering::Acquire), first); +} + +#[test] +fn tiny_callbacks_share_slots_and_preserve_the_oldest_timestamp() { + let buffers = Buffers::new(); + let start = Instant::now(); + for queue in [&buffers.capture, &buffers.rendered] { + let mut packer = FramePacker::default(); + // Five milliseconds at 384 kHz arrives as 120 sixteen-frame callbacks. + for callback in 0..120 { + assert!(packer.push( + Frame { + samples: [callback as f32; BLOCK], + len: 16, + at: start + Duration::from_secs_f64((callback * 16) as f64 / 384_000.0), + generation: 2, + }, + /*rate*/ 384_000.0, + queue, + )); + } + assert_eq!(queue.len(), 7); + for block in 0..7 { + let frame = queue.pop().unwrap(); + assert_eq!( + (frame.samples, frame.len, frame.at, frame.generation), + ( + std::array::from_fn(|sample| (block * 16 + sample / 16) as f32), + BLOCK, + start + Duration::from_secs_f64((block * BLOCK) as f64 / 384_000.0), + 2, + ), + ); + } + } +} + +#[test] +fn packing_drops_partial_audio_on_generation_changes_and_capture_rejection() { + let queue = ArrayQueue::new(QUEUE_CAPACITY); + let mut packer = FramePacker::default(); + let start = Instant::now(); + for (generation, sample) in [(2, 0.1), (4, 0.2), (4, 0.3)] { + assert!(packer.push( + Frame { + samples: [sample; BLOCK], + len: BLOCK / 2, + at: start, + generation, + }, + /*rate*/ 48_000.0, + &queue, + )); + } + let frame = queue.pop().unwrap(); + assert_eq!( + (frame.samples, frame.len, frame.at, frame.generation), + ( + std::array::from_fn(|i| if i < BLOCK / 2 { 0.2 } else { 0.3 }), + BLOCK, + start, + 4 + ), + ); + for sample in [0.4, 0.5] { + assert!(packer.push( + Frame { + samples: [sample; BLOCK], + len: BLOCK / 2, + at: start, + generation: 4 + }, + /*rate*/ 48_000.0, + &queue, + )); + packer.reset(); + } + assert!(queue.is_empty()); +} + +#[test] +fn packing_preserves_a_remainder_timestamp_across_uneven_callbacks() { + let queue = ArrayQueue::new(QUEUE_CAPACITY); + let mut packer = FramePacker::default(); + let start = Instant::now(); + let second = start + Duration::from_millis(10); + for (len, sample, at) in [(100, 0.1, start), (200, 0.2, second), (212, 0.3, second)] { + assert!(packer.push( + Frame { + samples: [sample; BLOCK], + len, + at, + generation: 2 + }, + /*rate*/ 48_000.0, + &queue, + )); + } + let first = queue.pop().unwrap(); + let remainder = queue.pop().unwrap(); + assert_eq!( + (first.samples, first.at, remainder.samples, remainder.at), + ( + std::array::from_fn(|i| if i < 100 { 0.1 } else { 0.2 }), + start, + std::array::from_fn(|i| if i < 44 { 0.2 } else { 0.3 }), + second + Duration::from_secs_f64(156.0 / 48_000.0), + ), + ); + assert!(queue.is_empty()); +} diff --git a/codex-rs/voice-host/src/devices.rs b/codex-rs/voice-host/src/devices.rs new file mode 100644 index 0000000000..e217881ec4 --- /dev/null +++ b/codex-rs/voice-host/src/devices.rs @@ -0,0 +1,327 @@ +//! Owns local device streams on the helper worker. Callbacks allocate no buffers and take no locks. +//! Small callbacks share full queue slots; processing lag still fails the session closed. +//! Capture and actual rendered output carry device timing. +//! References start with worker service; unmute rejects earlier device capture buffers. + +#[path = "device_buffers.rs"] +mod buffers; + +use std::io; +use std::sync::Arc; +use std::sync::atomic::Ordering; +use std::time::Duration; +use std::time::Instant; + +use cpal::FromSample; +use cpal::Sample; +use cpal::SampleFormat; +use cpal::SizedSample; +use cpal::traits::DeviceTrait; +use cpal::traits::HostTrait; +use cpal::traits::StreamTrait; + +use buffers::BLOCK; +use buffers::Buffers; +use buffers::CaptureBoundary; +use buffers::Frame; +use buffers::FramePacker; +use buffers::Playback; +use buffers::QUEUE_CAPACITY; + +const MAX_CAPTURE_AGE: Duration = Duration::from_secs(/*secs*/ 1); + +pub(super) struct Devices { + _input: cpal::Stream, + _output: cpal::Stream, + buffers: Arc, +} + +macro_rules! stream { + ($format:expr, $build:ident, $($arg:expr),+) => { + match $format { + SampleFormat::I8 => $build::($($arg),+), + SampleFormat::I16 => $build::($($arg),+), + SampleFormat::I32 => $build::($($arg),+), + SampleFormat::I64 => $build::($($arg),+), + SampleFormat::U8 => $build::($($arg),+), + SampleFormat::U16 => $build::($($arg),+), + SampleFormat::U32 => $build::($($arg),+), + SampleFormat::U64 => $build::($($arg),+), + SampleFormat::F32 => $build::($($arg),+), + SampleFormat::F64 => $build::($($arg),+), + _ => Err(cpal::Error::from(cpal::ErrorKind::UnsupportedConfig)), + } + }; +} + +impl Devices { + pub(super) fn open() -> io::Result { + let host = cpal::default_host(); + let input = host + .default_input_device() + .ok_or_else(|| io::Error::other("microphone unavailable"))?; + let output = host + .default_output_device() + .ok_or_else(|| io::Error::other("speaker unavailable"))?; + let input_config = input + .default_input_config() + .map_err(|_| io::Error::other("microphone configuration unavailable"))?; + let output_config = output + .default_output_config() + .map_err(|_| io::Error::other("speaker configuration unavailable"))?; + for config in [&input_config, &output_config] { + if config.channels() == 0 + || config.channels() > 32 + || !(8_000..=384_000).contains(&config.sample_rate()) + { + return Err(io::Error::other("unsupported audio device configuration")); + } + } + let input_stream_config = bounded_stream_config(&input_config)?; + let output_stream_config = bounded_stream_config(&output_config)?; + let buffers = Arc::new(Buffers::new()); + let input = stream!( + input_config.sample_format(), + build_input, + &input, + &input_stream_config, + buffers.clone() + ) + .map_err(|_| io::Error::other("failed to open microphone"))?; + let output = stream!( + output_config.sample_format(), + build_output, + &output, + &output_stream_config, + buffers.clone() + ) + .map_err(|_| io::Error::other("failed to open speaker"))?; + output + .play() + .map_err(|_| io::Error::other("failed to start speaker"))?; + input + .play() + .map_err(|_| io::Error::other("failed to start microphone"))?; + Ok(Self { + _input: input, + _output: output, + buffers, + }) + } + + pub(super) fn set_controls( + &self, + controls: codex_realtime_webrtc::AudioControls, + ) -> io::Result<()> { + Buffers::set_disabled(&self.buffers.microphone, controls.microphone_muted)?; + Buffers::set_disabled(&self.buffers.speaker, controls.speaker_suppressed) + } + + pub(super) fn service(&self) -> io::Result<()> { + // Some backends start callbacks during stream construction, before open returns. + self.buffers.serviced.store(true, Ordering::Release); + // Device-only stage: consume locally until the media worker is attached. + // Reading timestamps here also rejects a stalled callback before readiness is reused. + for queue in [&self.buffers.capture, &self.buffers.rendered] { + for _ in 0..queue.capacity() { + let Some(frame) = queue.pop() else { + break; + }; + if frame.at.elapsed() > MAX_CAPTURE_AGE { + return Err(io::Error::other("audio device processing fell behind")); + } + } + } + if self.buffers.failed.load(Ordering::Acquire) { + return Err(io::Error::other("audio device failed")); + } + Ok(()) + } +} + +fn bounded_stream_config( + supported: &cpal::SupportedStreamConfig, +) -> io::Result { + let cpal::SupportedBufferSize::Range { min, max } = *supported.buffer_size() else { + return Err(io::Error::other("audio callback size range unavailable")); + }; + let mut config = supported.config(); + // Leave room for a partial block and smaller callbacks arriving before service. + let service_frames = (f64::from(config.sample_rate) + * super::DEVICE_SERVICE_INTERVAL.as_secs_f64()) + .ceil() as usize; + let callback_capacity = (BLOCK * QUEUE_CAPACITY).saturating_sub(service_frames + BLOCK); + let min = min.max(1); + let max = max.min(callback_capacity as u32); + if min > max { + return Err(io::Error::other("unsupported audio callback size range")); + } + // Aim for 10 ms without consuming the queue's service headroom. + // Do not fall back to the backend's potentially much larger default buffer. + let frames = (config.sample_rate / 100).clamp(min, max); + let callback_duration = + Duration::from_secs_f64(f64::from(frames) / f64::from(config.sample_rate)); + // Backends may deliver smaller callbacks than requested. Packing makes queue + // coverage depend on samples, while an incomplete block adds bounded latency. + let packing_duration = Duration::from_secs_f64(BLOCK as f64 / f64::from(config.sample_rate)); + let queue_duration = packing_duration * QUEUE_CAPACITY as u32; + if callback_duration + packing_duration + super::DEVICE_SERVICE_INTERVAL >= MAX_CAPTURE_AGE + || queue_duration <= super::DEVICE_SERVICE_INTERVAL + { + return Err(io::Error::other("unsupported audio callback timing")); + } + config.buffer_size = cpal::BufferSize::Fixed(frames); + Ok(config) +} + +// CPAL reports recoverable underruns/overruns through the same callback as device loss. +fn handle_stream_error(buffers: &Buffers, error: cpal::Error) { + if error.kind() != cpal::ErrorKind::Xrun { + buffers.failed.store(true, Ordering::Release); + } +} + +fn build_input( + device: &cpal::Device, + config: &cpal::StreamConfig, + buffers: Arc, +) -> Result +where + T: SizedSample, + f32: FromSample, +{ + let channels = usize::from(config.channels); + let rate = f64::from(config.sample_rate); + let failure = buffers.clone(); + let mut origin = None; + let mut boundary = CaptureBoundary::default(); + let mut capture = FramePacker::default(); + device.build_input_stream( + *config, + move |data: &[T], info: &cpal::InputCallbackInfo| { + let generation = buffers.microphone.load(Ordering::Acquire); + if generation % 2 == 1 { + capture.reset(); + return; + } + let timestamp = info.timestamp(); + let origin = origin.get_or_insert(timestamp.capture); + let (Some(callback), Some(captured)) = ( + timestamp.callback.checked_duration_since(*origin), + timestamp.capture.checked_duration_since(*origin), + ) else { + capture.reset(); + return; + }; + if !boundary.accepts(generation, callback, captured) { + capture.reset(); + return; + } + let start = Instant::now() + .checked_sub( + timestamp + .callback + .checked_duration_since(timestamp.capture) + .unwrap_or_default(), + ) + .unwrap_or_else(Instant::now); + for (index, chunk) in data.chunks(BLOCK * channels).enumerate() { + let mut frame = Frame { + samples: [0.0; BLOCK], + len: chunk.len() / channels, + at: start + Duration::from_secs_f64((index * BLOCK) as f64 / rate), + generation, + }; + for (output, input) in frame.samples.iter_mut().zip(chunk.chunks_exact(channels)) { + *output = input + .iter() + .map(|sample| f32::from_sample(*sample)) + .sum::() + / channels as f32; + if !output.is_finite() { + buffers.failed.store(true, Ordering::Release); + return; + } + } + if !capture.push(frame, rate, &buffers.capture) { + buffers.failed.store(true, Ordering::Release); + return; + } + } + }, + move |error| handle_stream_error(&failure, error), + /*timeout*/ None, + ) +} + +fn build_output( + device: &cpal::Device, + config: &cpal::StreamConfig, + buffers: Arc, +) -> Result +where + T: SizedSample + FromSample, + f32: FromSample, +{ + let channels = usize::from(config.channels); + let rate = f64::from(config.sample_rate); + let failure = buffers.clone(); + let mut output = OutputState::default(); + device.build_output_stream( + *config, + move |data: &mut [T], info: &cpal::OutputCallbackInfo| { + let timestamp = info.timestamp(); + let start = Instant::now() + + timestamp + .playback + .checked_duration_since(timestamp.callback) + .unwrap_or_default(); + render_output(data, channels, rate, start, &buffers, &mut output); + }, + move |error| handle_stream_error(&failure, error), + /*timeout*/ None, + ) +} + +#[derive(Default)] +struct OutputState { + playback: Playback, + reference: FramePacker, +} + +fn render_output( + data: &mut [T], + channels: usize, + rate: f64, + start: Instant, + buffers: &Buffers, + output: &mut OutputState, +) where + T: SizedSample + FromSample, + f32: FromSample, +{ + if !buffers.serviced.load(Ordering::Acquire) { + data.fill(T::from_sample(0.0)); + return; + } + for (index, chunk) in data.chunks_mut(BLOCK * channels).enumerate() { + let mut reference = Frame { + samples: [0.0; BLOCK], + len: chunk.len() / channels, + at: start + Duration::from_secs_f64((index * BLOCK) as f64 / rate), + generation: buffers.speaker.load(Ordering::Acquire), + }; + for (frame, sample) in chunk.chunks_mut(channels).zip(&mut reference.samples) { + let rendered = T::from_sample(output.playback.next(buffers)); + frame.fill(rendered); + *sample = f32::from_sample(rendered); + } + if !output.reference.push(reference, rate, &buffers.rendered) { + buffers.failed.store(true, Ordering::Release); + } + } +} + +#[cfg(test)] +#[path = "devices_tests.rs"] +mod tests; diff --git a/codex-rs/voice-host/src/devices_tests.rs b/codex-rs/voice-host/src/devices_tests.rs new file mode 100644 index 0000000000..5a0235e4c4 --- /dev/null +++ b/codex-rs/voice-host/src/devices_tests.rs @@ -0,0 +1,271 @@ +use super::*; +use pretty_assertions::assert_eq; + +#[test] +fn startup_output_waits_for_service_but_later_overflow_still_fails() { + let buffers = Buffers::new(); + let mut state = OutputState::default(); + let mut output = [1.0_f32; BLOCK * 2]; + let start = Instant::now(); + for _ in 0..QUEUE_CAPACITY + 1 { + render_output( + &mut output, + /*channels*/ 2, + /*rate*/ 384_000.0, + start, + &buffers, + &mut state, + ); + assert_eq!(output, [0.0; BLOCK * 2]); + } + assert!(buffers.rendered.is_empty()); + assert!(!buffers.failed.load(Ordering::Acquire)); + + buffers.serviced.store(true, Ordering::Release); + render_output( + &mut output, + /*channels*/ 2, + /*rate*/ 384_000.0, + start, + &buffers, + &mut state, + ); + let reference = buffers.rendered.pop().unwrap(); + assert_eq!( + ( + reference.samples, + reference.len, + reference.at, + reference.generation + ), + ( + [0.0; BLOCK], + BLOCK, + start, + buffers.speaker.load(Ordering::Acquire) + ), + ); + Buffers::set_disabled(&buffers.speaker, /*disabled*/ false).unwrap(); + for _ in 0..QUEUE_CAPACITY { + render_output( + &mut output, + /*channels*/ 2, + /*rate*/ 384_000.0, + start, + &buffers, + &mut state, + ); + } + assert!(!buffers.failed.load(Ordering::Acquire)); + render_output( + &mut output, + /*channels*/ 2, + /*rate*/ 384_000.0, + start, + &buffers, + &mut state, + ); + assert!(buffers.failed.load(Ordering::Acquire)); +} + +#[test] +fn callback_configuration_fits_supported_range_and_actual_queue() { + for (rate, min, max, frames) in [ + (96_000, 0, u32::MAX, 960), + (96_000, 1, 16, 16), + (384_000, 1, 64, 64), + (8_000, 7_680, 8_192, 7_680), + (384_000, 1, 16, 16), + (96_000, 1, 15, 15), + (48_000, 1, 128, 128), + (48_000, 2_048, 16_384, 2_048), + (384_000, 6_016, 16_384, 6_016), + ] { + let supported = cpal::SupportedStreamConfig::new( + /*channels*/ 2, + rate, + cpal::SupportedBufferSize::Range { min, max }, + cpal::SampleFormat::F32, + ); + let config = bounded_stream_config(&supported).unwrap(); + assert_eq!( + config, + cpal::StreamConfig { + channels: 2, + sample_rate: rate, + buffer_size: cpal::BufferSize::Fixed(frames), + } + ); + let buffers = Buffers::new(); + for queue in [&buffers.capture, &buffers.rendered] { + for offset in (0..frames as usize).step_by(BLOCK) { + assert!( + queue + .push(Frame { + samples: [0.0; BLOCK], + len: (frames as usize - offset).min(BLOCK), + at: Instant::now(), + generation: 0, + }) + .is_ok() + ); + } + } + } +} + +#[test] +fn callback_configuration_reserves_room_for_partial_and_following_callbacks() { + for min in [6_016, 6_017, 8_192] { + let supported = cpal::SupportedStreamConfig::new( + /*channels*/ 1, + 384_000, + cpal::SupportedBufferSize::Range { min, max: 16_384 }, + cpal::SampleFormat::F32, + ); + let config = bounded_stream_config(&supported); + if min != 6_016 { + assert!(config.is_err(), "minimum callback size {min}"); + continue; + } + let cpal::BufferSize::Fixed(frames) = config.unwrap().buffer_size else { + panic!("callback size must be bounded"); + }; + let buffers = Buffers::new(); + buffers.serviced.store(true, Ordering::Release); + let mut output = OutputState::default(); + let start = Instant::now(); + // An existing partial block, a full callback, then 5 ms of 128-frame callbacks. + for count in [BLOCK - 1, frames as usize].into_iter().chain([128; 15]) { + render_output( + &mut vec![0.0_f32; count], + /*channels*/ 1, + /*rate*/ 384_000.0, + start, + &buffers, + &mut output, + ); + } + assert_eq!(buffers.rendered.len(), QUEUE_CAPACITY - 1); + assert!(!buffers.failed.load(Ordering::Acquire)); + } +} + +#[test] +fn unsupported_callback_ranges_do_not_fall_back_to_default() { + for range in [ + cpal::SupportedBufferSize::Unknown, + cpal::SupportedBufferSize::Range { min: 0, max: 0 }, + cpal::SupportedBufferSize::Range { + min: 1_024, + max: 512, + }, + cpal::SupportedBufferSize::Range { + min: 8_193, + max: 16_384, + }, + ] { + let supported = cpal::SupportedStreamConfig::new( + /*channels*/ 2, + 96_000, + range, + cpal::SampleFormat::F32, + ); + assert!(bounded_stream_config(&supported).is_err(), "{range:?}"); + } +} + +#[test] +fn callback_timing_must_fit_service_and_stale_limits() { + for (rate, min, max) in [(8_000, 8_192, 8_192), (8_000, 7_704, 8_192)] { + let supported = cpal::SupportedStreamConfig::new( + /*channels*/ 2, + rate, + cpal::SupportedBufferSize::Range { min, max }, + cpal::SampleFormat::F32, + ); + assert!( + bounded_stream_config(&supported).is_err(), + "rate={rate}, range={min}..={max}", + ); + } +} + +#[test] +fn tiny_output_callbacks_pack_references_without_delaying_rendering() { + let buffers = Buffers::new(); + buffers.serviced.store(true, Ordering::Release); + Buffers::set_disabled(&buffers.speaker, /*disabled*/ false).unwrap(); + let generation = buffers.speaker.load(Ordering::Acquire); + let start = Instant::now(); + for _ in 0..8 { + assert!( + buffers + .playback + .push(Frame { + samples: [0.5; BLOCK], + len: BLOCK, + at: start, + generation, + }) + .is_ok() + ); + } + let mut state = OutputState::default(); + for callback in 0..120 { + let mut output = [0.0_f32; 32]; + render_output( + &mut output, + /*channels*/ 2, + /*rate*/ 384_000.0, + start + Duration::from_secs_f64((callback * 16) as f64 / 384_000.0), + &buffers, + &mut state, + ); + assert_eq!(output, [0.5; 32]); + } + assert_eq!(buffers.rendered.len(), 7); + assert!(!buffers.failed.load(Ordering::Acquire)); +} + +#[test] +fn suppressed_output_does_not_fail_when_reference_queue_is_full() { + let buffers = Buffers::new(); + buffers.serviced.store(true, Ordering::Release); + let mut state = OutputState::default(); + let mut output = [1.0_f32; BLOCK]; + for _ in 0..QUEUE_CAPACITY * 3 { + render_output( + &mut output, + /*channels*/ 1, + /*rate*/ 384_000.0, + Instant::now(), + &buffers, + &mut state, + ); + assert_eq!(output, [0.0; BLOCK]); + } + assert_eq!(buffers.rendered.len(), QUEUE_CAPACITY); + assert!(!buffers.failed.load(Ordering::Acquire)); + Buffers::set_disabled(&buffers.speaker, /*disabled*/ false).unwrap(); + render_output( + &mut output, + /*channels*/ 1, + /*rate*/ 384_000.0, + Instant::now(), + &buffers, + &mut state, + ); + assert!(buffers.failed.load(Ordering::Acquire)); +} + +#[test] +fn stream_xruns_recover_without_clearing_device_failure() { + let buffers = Buffers::new(); + handle_stream_error(&buffers, cpal::ErrorKind::Xrun.into()); + assert!(!buffers.failed.load(Ordering::Acquire)); + handle_stream_error(&buffers, cpal::ErrorKind::DeviceNotAvailable.into()); + assert!(buffers.failed.load(Ordering::Acquire)); + handle_stream_error(&buffers, cpal::ErrorKind::Xrun.into()); + assert!(buffers.failed.load(Ordering::Acquire)); +} diff --git a/codex-rs/voice-host/src/devices_unavailable.rs b/codex-rs/voice-host/src/devices_unavailable.rs new file mode 100644 index 0000000000..711dd0b817 --- /dev/null +++ b/codex-rs/voice-host/src/devices_unavailable.rs @@ -0,0 +1,21 @@ +//! Keep non-native helper targets buildable without linking a host audio backend. + +use std::io; + +pub(super) struct Devices; + +impl Devices { + pub(super) fn open() -> io::Result { + Err(io::Error::other( + "audio devices unavailable for this helper target", + )) + } + + pub(super) fn set_controls(&self, _: codex_realtime_webrtc::AudioControls) -> io::Result<()> { + Self::open().map(|_| ()) + } + + pub(super) fn service(&self) -> io::Result<()> { + Self::open().map(|_| ()) + } +} diff --git a/codex-rs/voice-host/src/main.rs b/codex-rs/voice-host/src/main.rs index b506a9516c..0eb6339414 100644 --- a/codex-rs/voice-host/src/main.rs +++ b/codex-rs/voice-host/src/main.rs @@ -1,5 +1,14 @@ -//! Same-build helper lifecycle, private runtime initialization and owned transport. No devices yet. +//! Same-build helper lifecycle with privately owned runtime, transport and opt-in local devices. +#[cfg_attr( + not(any( + target_os = "macos", + all(target_os = "linux", target_env = "gnu"), + all(windows, target_env = "msvc") + )), + path = "devices_unavailable.rs" +)] +mod devices; mod runtime; mod transport; mod transport_runtime; @@ -13,6 +22,8 @@ use codex_realtime_webrtc::Message; use codex_realtime_webrtc::encode_frame; use codex_realtime_webrtc::read_message; +const DEVICE_SERVICE_INTERVAL: Duration = Duration::from_millis(/*millis*/ 5); + const BUILD_COMMIT: &str = match option_env!("STABLE_GIT_COMMIT") { Some(commit) => commit, None => "dev", @@ -80,8 +91,28 @@ fn run( let executor = tokio::runtime::Runtime::new()?; let mut transport = None; let mut answered = false; + let mut devices: Option = None; loop { - let reply = match receiver.recv() { + let message = if let Some(devices) = &devices { + devices.service()?; + receiver.recv_timeout(DEVICE_SERVICE_INTERVAL) + } else { + receiver + .recv() + .map_err(|_| mpsc::RecvTimeoutError::Disconnected) + }; + let reply = match message { + Ok(Message::OpenDevices {}) if devices.is_none() && runtime.is_some() && answered => { + devices = Some(devices::Devices::open()?); + Message::DevicesOpened {} + } + Ok(Message::SetAudioControls { controls }) => { + devices + .as_ref() + .ok_or_else(|| io::Error::other("audio devices not open"))? + .set_controls(controls)?; + Message::AudioControlsApplied {} + } Ok(Message::StartTransport {}) if transport.is_none() => { let peer = start_transport(&executor)?; let sdp = executor.block_on(peer.offer()).map_err(io::Error::other)?; @@ -108,13 +139,15 @@ fn run( Message::RuntimeReady {} } Ok(Message::Close {}) => { + let _ = devices.take(); if let Some(mut peer) = transport.take() { executor.block_on(peer.close()).map_err(io::Error::other)?; } output.write_all(&encode_frame(&Message::Closed {})?)?; return output.flush(); } - Err(_) => return Ok(()), + Err(mpsc::RecvTimeoutError::Timeout) => continue, + Err(mpsc::RecvTimeoutError::Disconnected) => return Ok(()), Ok( Message::Hello { .. } | Message::Ready {} @@ -123,6 +156,9 @@ fn run( | Message::ApplyAnswer { .. } | Message::Offer { .. } | Message::TransportReady {} + | Message::OpenDevices {} + | Message::DevicesOpened {} + | Message::AudioControlsApplied {} | Message::Closed {}, ) => return Err(io::Error::other("invalid voice control sequence")), };