From f00acf1290febd7ea87ecc1f18bb840b63b873fb Mon Sep 17 00:00:00 2001 From: Craig Watson Date: Fri, 24 Apr 2026 16:36:16 -0700 Subject: [PATCH 1/7] Make microphone polling interval configurable --- src/microphone.rs | 3 ++- src/microphone/builder.rs | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/microphone.rs b/src/microphone.rs index 74d0b031..9720f290 100644 --- a/src/microphone.rs +++ b/src/microphone.rs @@ -262,6 +262,7 @@ impl Microphone { fn open( device: Device, config: InputConfig, + poll_interval: Duration, mut error_callback: impl FnMut(cpal::Error) + Send + 'static, ) -> Result { let timeout = Some(Duration::from_millis(100)); @@ -318,7 +319,7 @@ impl Microphone { _stream_handle: stream, buffer: rx, config, - poll_interval: Duration::from_millis(5), + poll_interval, error_occurred, }) } diff --git a/src/microphone/builder.rs b/src/microphone/builder.rs index a698424b..57f91dad 100644 --- a/src/microphone/builder.rs +++ b/src/microphone/builder.rs @@ -1,4 +1,4 @@ -use std::{fmt::Debug, marker::PhantomData}; +use std::{fmt::Debug, marker::PhantomData, time::Duration}; use cpal::{ traits::{DeviceTrait, HostTrait}, @@ -62,6 +62,7 @@ where { device: Option<(cpal::Device, Vec)>, config: Option, + poll_interval: Duration, error_callback: E, device_set: PhantomData, @@ -93,6 +94,7 @@ impl Default for MicrophoneBuilder { device: None, config: None, error_callback: default_error_callback, + poll_interval: Duration::from_millis(5), device_set: PhantomData, config_set: PhantomData, @@ -151,6 +153,7 @@ where device: Some((device, supported_configs)), config: self.config, error_callback: self.error_callback.clone(), + poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -182,6 +185,7 @@ where device: Some((default_device, supported_configs)), config: self.config, error_callback: self.error_callback.clone(), + poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -230,6 +234,7 @@ where device: self.device.clone(), config: Some(config), error_callback: self.error_callback.clone(), + poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -262,6 +267,7 @@ where device: self.device.clone(), config: Some(config), error_callback: self.error_callback.clone(), + poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -316,6 +322,7 @@ where device: self.device.clone(), config: Some(new_config), error_callback: self.error_callback.clone(), + poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -367,6 +374,7 @@ where device: self.device.clone(), config: Some(final_config), error_callback: self.error_callback.clone(), + poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, } @@ -395,6 +403,7 @@ where device: self.device.clone(), config: Some(new_config), error_callback: self.error_callback.clone(), + poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -457,6 +466,7 @@ where device: self.device.clone(), config: Some(new_config), error_callback: self.error_callback.clone(), + poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -505,6 +515,26 @@ where config.buffer_size = cpal::BufferSize::Fixed(size) }) } + + /// Set polling interval for the microphone iterator + /// + /// Lower values = lower latency but higher CPU usage + /// Higher values = higher latency but lower CPU usage + /// + /// Recommended values: + /// - Real-time effects: 1-2ms + /// - Interactive applications: 5ms (default) + /// - Recording/batch processing: 10-20ms + pub fn poll_interval(&self, interval: Duration) -> Self { + MicrophoneBuilder { + device: self.device.clone(), + config: self.config, + error_callback: self.error_callback.clone(), + poll_interval: interval, + device_set: PhantomData, + config_set: PhantomData, + } + } } impl MicrophoneBuilder @@ -551,6 +581,7 @@ where Microphone::open( self.device.as_ref().expect("DeviceIsSet").0.clone(), *self.config.as_ref().expect("ConfigIsSet"), + self.poll_interval, self.error_callback.clone(), ) } From c50e64c00b5b04543e1246d4dff77903e421daee Mon Sep 17 00:00:00 2001 From: Craig Watson Date: Mon, 4 May 2026 13:55:00 -0700 Subject: [PATCH 2/7] Add script to measure and plot sampling jitter --- Cargo.lock | 456 +++++++++++++++++++++++++++++++ Cargo.toml | 1 + examples/microphone_profiling.rs | 119 ++++++++ 3 files changed, 576 insertions(+) create mode 100644 examples/microphone_profiling.rs diff --git a/Cargo.lock b/Cargo.lock index 070c4096..00868b46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.4" @@ -54,6 +60,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -72,6 +84,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "1.3.2" @@ -111,6 +129,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.11.1" @@ -152,6 +176,15 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "num-traits", +] + [[package]] name = "clap" version = "4.6.1" @@ -193,6 +226,18 @@ dependencies = [ "cc", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorous" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e18bf7a165bf7028fde98609a0f1e8f7498d762a212598e6c891f6893556ec" + [[package]] name = "combine" version = "4.6.7" @@ -218,6 +263,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + [[package]] name = "coreaudio-rs" version = "0.14.1" @@ -271,6 +325,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -319,6 +382,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + [[package]] name = "derive_more" version = "2.1.1" @@ -436,12 +505,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + [[package]] name = "extended" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fdk-aac" version = "0.8.0" @@ -466,12 +553,51 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a6f9af55fb97ad673fb7a69533eb2f967648a06fa21f8c9bb2cd6d33975716" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "futures-core" version = "0.3.32" @@ -560,6 +686,16 @@ dependencies = [ "wasip3", ] +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "glob" version = "0.3.3" @@ -599,6 +735,22 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "image-webp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + [[package]] name = "indexmap" version = "2.14.0" @@ -697,6 +849,29 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + +[[package]] +name = "kuva" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455280de2b34485b88f1f8b80ee5e0df4289233ecad06d66dda79bf307e24f10" +dependencies = [ + "chrono", + "colorous", + "resvg", + "ryu", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -780,6 +955,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + [[package]] name = "minimp3-sys" version = "0.3.2" @@ -800,6 +984,16 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.0" @@ -1060,6 +1254,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1072,6 +1272,19 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1118,6 +1331,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quickcheck" version = "1.1.0" @@ -1257,6 +1476,32 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" +[[package]] +name = "resvg" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958" +dependencies = [ + "gif", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", + "zune-jpeg", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + [[package]] name = "rodio" version = "0.22.2" @@ -1270,6 +1515,7 @@ dependencies = [ "divan", "hound", "inquire", + "kuva", "lewton", "minimp3_fixed", "num-rational", @@ -1286,6 +1532,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rstest" version = "0.26.1" @@ -1374,6 +1626,30 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rustybuzz" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85d1ccd519e61834798eb52c4e886e8c2d7d698dd3d6ce0b1b47eb8557f1181" +dependencies = [ + "bitflags 2.11.1", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "same-file" version = "1.0.6" @@ -1474,6 +1750,27 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + [[package]] name = "slab" version = "0.4.12" @@ -1491,6 +1788,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -1503,6 +1809,25 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo", + "siphasher", +] + [[package]] name = "symphonia" version = "0.5.5" @@ -1791,6 +2116,32 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinyvec" version = "1.11.0" @@ -1877,18 +2228,63 @@ dependencies = [ "strength_reduce", ] +[[package]] +name = "ttf-parser" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" +dependencies = [ + "core_maths", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f" + +[[package]] +name = "unicode-ccc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + +[[package]] +name = "unicode-script" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" + [[package]] name = "unicode-segmentation" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.2.2" @@ -1901,6 +2297,39 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "usvg" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "walkdir" version = "2.5.0" @@ -2034,6 +2463,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + [[package]] name = "winapi" version = "0.3.9" @@ -2353,6 +2788,12 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "zerocopy" version = "0.8.48" @@ -2378,3 +2819,18 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 7a1d5d25..02a6f753 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,6 +144,7 @@ approx = "0.5.1" divan = "0.1.14" inquire = "0.9.3" symphonia-adapter-fdk-aac = "0.1" +kuva = { version = "0.1.0", features = ["png"] } [[bench]] name = "effects" diff --git a/examples/microphone_profiling.rs b/examples/microphone_profiling.rs new file mode 100644 index 00000000..3542d38d --- /dev/null +++ b/examples/microphone_profiling.rs @@ -0,0 +1,119 @@ +//! Example to acquire microphone data, and measure and plot the interval between samples. +//! This should be run in release mode. + +use kuva::prelude::*; +use rodio::microphone::MicrophoneBuilder; +use rodio::Source; +use std::error::Error; +use std::path::Path; +use std::time::{Duration, Instant}; + +fn main() -> Result<(), Box> { + let mut input = MicrophoneBuilder::new() + .default_device()? + .default_config()? + .prefer_sample_rates(vec![44_100.try_into()?, 48_000.try_into()?]) + .prefer_buffer_sizes(vec![16, 32, 64, 128, 256]) + .open_stream()?; + let config = *input.config(); + println!("Device config: {config:#?}"); + + let duration = Duration::from_secs(5); + println!("Recording for {duration:#?}..."); + let n_samples_est = input.sample_rate().get() as usize + * duration.as_secs() as usize + * input.channels().get() as usize; + let mut intervals = Vec::with_capacity(n_samples_est); + let start = Instant::now(); + let mut last_t = start; + while start.elapsed() < duration { + let _sample = input.next().unwrap(); + let dt = last_t.elapsed(); + last_t = Instant::now(); + intervals.push(dt); + } + println!( + "Recorded {} samples; mean sampling rate: {:.2} Hz ({:.2} μs/sample)", + intervals.len(), + intervals.len() as f64 / duration.as_secs_f64(), + duration.as_secs_f64() / intervals.len() as f64 * 1e6, + ); + print_stats(&intervals); + + println!("Rendering plots..."); + plot_intervals( + &intervals, + "microphone_intervals.png", + format!("{config:?}"), + )?; + println!("Plots saved to microphone_intervals.png"); + + /* + let csv_file_path = "microphone_intervals.csv"; + let mut csv_file = std::fs::File::create(csv_file_path)?; + use std::io::Write; + for dt in &intervals { + writeln!(csv_file, "{}", dt.as_nanos())?; + } + println!("Saved intervals to {}", csv_file_path); + */ + Ok(()) +} + +fn print_stats(intervals: &[Duration]) { + let mean = intervals.iter().sum::() / intervals.len() as u32; + let std = intervals + .iter() + .map(|dt| (dt.saturating_sub(mean).as_nanos() as f64).powf(2.0)) + .sum::() + / intervals.len() as f64; + let std = Duration::from_nanos(std.sqrt() as u64); + let max = intervals.iter().max().unwrap(); + let min = intervals.iter().min().unwrap(); + println!("Measured interval between samples:"); + println!(" mean: {:.2} μs", mean.as_nanos() as f64 / 1000.0); + println!(" std dev: {:.2} μs", std.as_nanos() as f64 / 1000.0); + println!(" max: {:.2} μs", max.as_nanos() as f64 / 1000.0); + println!(" min: {:.2} μs", min.as_nanos() as f64 / 1000.0); +} + +fn plot_intervals( + intervals: &[Duration], + dest: impl AsRef, + title: String, +) -> Result<(), Box> { + let durations = intervals.iter().map(|dt| dt.as_nanos() as f64 / 1000.0); + let data_x_y = durations.clone().enumerate().map(|(i, dt)| (i as f64, dt)); + let scatter = ScatterPlot::new() + .with_data(data_x_y) + .with_marker(MarkerShape::Plus) + .with_color("steelblue") + .into(); + let scatter_plot = vec![scatter]; + let scatter_layout = Layout::auto_from_plots(&scatter_plot) + .with_x_label("Sample number") + .with_y_label("Interval [μs]"); + + let histogram = Histogram::new() + .with_data(durations) + .with_bins(100) + .with_color("steelblue") + .into(); + let hist_plot = vec![histogram]; + let hist_layout = Layout::auto_from_plots(&hist_plot) + .with_log_y() + .with_x_label("Interval [μs]") + .with_y_label("Count"); + + let scene = Figure::new(1, 2) + .with_plots(vec![scatter_plot, hist_plot]) + .with_layouts(vec![scatter_layout, hist_layout]) + .with_labels() + .with_title(title) + .with_title_size(10) + .render(); + + let png = PngBackend::new().with_scale(4.0).render_scene(&scene)?; + std::fs::write(dest, &png[..])?; + Ok(()) +} From a9e5463978fbcf2b5b731fee21bcac3ea10490ab Mon Sep 17 00:00:00 2001 From: Craig Watson Date: Mon, 4 May 2026 14:28:59 -0700 Subject: [PATCH 3/7] Replace rtrb with std::sync::mpsc --- src/microphone.rs | 29 ++++++++--------------------- src/microphone/builder.rs | 21 --------------------- 2 files changed, 8 insertions(+), 42 deletions(-) diff --git a/src/microphone.rs b/src/microphone.rs index 9720f290..035f2dfa 100644 --- a/src/microphone.rs +++ b/src/microphone.rs @@ -100,8 +100,9 @@ use core::fmt; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc; use std::sync::Arc; -use std::{thread, time::Duration}; +use std::time::Duration; use crate::common::assert_error_traits; use crate::conversions::SampleTypeConverter; @@ -116,7 +117,6 @@ use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, Device, }; -use rtrb::RingBuffer; /// Error that can occur when we can not list the input devices #[derive(Debug, thiserror::Error, Clone)] @@ -184,10 +184,8 @@ pub fn available_inputs() -> Result, ListError> { /// A microphone input stream that can be used as `Source`. pub struct Microphone { _stream_handle: cpal::Stream, - buffer: rtrb::Consumer, + receiver: mpsc::Receiver, config: InputConfig, - poll_interval: Duration, - error_occurred: Arc, } impl Source for Microphone { @@ -227,19 +225,11 @@ impl Iterator for Microphone { type Item = Sample; fn next(&mut self) -> Option { - loop { - if let Ok(sample) = self.buffer.pop() { - return Some(sample); - } else if self.error_occurred.load(Ordering::Relaxed) { - return None; - } else { - thread::sleep(self.poll_interval) - } - } + self.receiver.iter().next() } fn size_hint(&self) -> (usize, Option) { - (self.buffer.slots(), None) + self.receiver.iter().size_hint() } } @@ -262,13 +252,12 @@ impl Microphone { fn open( device: Device, config: InputConfig, - poll_interval: Duration, mut error_callback: impl FnMut(cpal::Error) + Send + 'static, ) -> Result { let timeout = Some(Duration::from_millis(100)); let hundred_ms_of_samples = config.channel_count.get() as u32 * config.sample_rate.get() / 10; - let (mut tx, rx) = RingBuffer::::new(hundred_ms_of_samples as usize); + let (tx, rx) = mpsc::sync_channel::(hundred_ms_of_samples as usize); let error_occurred = Arc::new(AtomicBool::new(false)); let error_callback = { let error_occurred = error_occurred.clone(); @@ -286,7 +275,7 @@ impl Microphone { config.stream_config(), move |data, _info| { for sample in SampleTypeConverter::<_, Sample>::new(data.into_iter().copied()) { - let _skip_if_player_is_behind = tx.push(sample); + let _skip_if_player_is_behind = tx.send(sample).is_err(); } }, error_callback, @@ -317,10 +306,8 @@ impl Microphone { Ok(Microphone { _stream_handle: stream, - buffer: rx, + receiver: rx, config, - poll_interval, - error_occurred, }) } diff --git a/src/microphone/builder.rs b/src/microphone/builder.rs index 57f91dad..072fb174 100644 --- a/src/microphone/builder.rs +++ b/src/microphone/builder.rs @@ -515,26 +515,6 @@ where config.buffer_size = cpal::BufferSize::Fixed(size) }) } - - /// Set polling interval for the microphone iterator - /// - /// Lower values = lower latency but higher CPU usage - /// Higher values = higher latency but lower CPU usage - /// - /// Recommended values: - /// - Real-time effects: 1-2ms - /// - Interactive applications: 5ms (default) - /// - Recording/batch processing: 10-20ms - pub fn poll_interval(&self, interval: Duration) -> Self { - MicrophoneBuilder { - device: self.device.clone(), - config: self.config, - error_callback: self.error_callback.clone(), - poll_interval: interval, - device_set: PhantomData, - config_set: PhantomData, - } - } } impl MicrophoneBuilder @@ -581,7 +561,6 @@ where Microphone::open( self.device.as_ref().expect("DeviceIsSet").0.clone(), *self.config.as_ref().expect("ConfigIsSet"), - self.poll_interval, self.error_callback.clone(), ) } From 931e83a978110565733b7153750ddee8b3140ea2 Mon Sep 17 00:00:00 2001 From: Craig Watson Date: Tue, 5 May 2026 16:42:30 -0700 Subject: [PATCH 4/7] Remove remaining poll_interval references --- src/microphone/builder.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/microphone/builder.rs b/src/microphone/builder.rs index 072fb174..a698424b 100644 --- a/src/microphone/builder.rs +++ b/src/microphone/builder.rs @@ -1,4 +1,4 @@ -use std::{fmt::Debug, marker::PhantomData, time::Duration}; +use std::{fmt::Debug, marker::PhantomData}; use cpal::{ traits::{DeviceTrait, HostTrait}, @@ -62,7 +62,6 @@ where { device: Option<(cpal::Device, Vec)>, config: Option, - poll_interval: Duration, error_callback: E, device_set: PhantomData, @@ -94,7 +93,6 @@ impl Default for MicrophoneBuilder { device: None, config: None, error_callback: default_error_callback, - poll_interval: Duration::from_millis(5), device_set: PhantomData, config_set: PhantomData, @@ -153,7 +151,6 @@ where device: Some((device, supported_configs)), config: self.config, error_callback: self.error_callback.clone(), - poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -185,7 +182,6 @@ where device: Some((default_device, supported_configs)), config: self.config, error_callback: self.error_callback.clone(), - poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -234,7 +230,6 @@ where device: self.device.clone(), config: Some(config), error_callback: self.error_callback.clone(), - poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -267,7 +262,6 @@ where device: self.device.clone(), config: Some(config), error_callback: self.error_callback.clone(), - poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -322,7 +316,6 @@ where device: self.device.clone(), config: Some(new_config), error_callback: self.error_callback.clone(), - poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -374,7 +367,6 @@ where device: self.device.clone(), config: Some(final_config), error_callback: self.error_callback.clone(), - poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, } @@ -403,7 +395,6 @@ where device: self.device.clone(), config: Some(new_config), error_callback: self.error_callback.clone(), - poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) @@ -466,7 +457,6 @@ where device: self.device.clone(), config: Some(new_config), error_callback: self.error_callback.clone(), - poll_interval: self.poll_interval, device_set: PhantomData, config_set: PhantomData, }) From 35519d1286c0e21ba273c67d39ebe9837da86f3a Mon Sep 17 00:00:00 2001 From: Craig Watson Date: Tue, 5 May 2026 16:44:28 -0700 Subject: [PATCH 5/7] Remove commented-out code --- examples/microphone_profiling.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/examples/microphone_profiling.rs b/examples/microphone_profiling.rs index 3542d38d..6a3feb00 100644 --- a/examples/microphone_profiling.rs +++ b/examples/microphone_profiling.rs @@ -48,15 +48,6 @@ fn main() -> Result<(), Box> { )?; println!("Plots saved to microphone_intervals.png"); - /* - let csv_file_path = "microphone_intervals.csv"; - let mut csv_file = std::fs::File::create(csv_file_path)?; - use std::io::Write; - for dt in &intervals { - writeln!(csv_file, "{}", dt.as_nanos())?; - } - println!("Saved intervals to {}", csv_file_path); - */ Ok(()) } From 2e17e66cc25b8ccda08ffc3707996b9739dfe1f3 Mon Sep 17 00:00:00 2001 From: Craig Watson Date: Sun, 10 May 2026 17:30:42 -0700 Subject: [PATCH 6/7] Revert "Add script to measure and plot sampling jitter" This reverts commit c50e64c00b5b04543e1246d4dff77903e421daee. --- Cargo.lock | 456 ------------------------------- Cargo.toml | 1 - examples/microphone_profiling.rs | 110 -------- 3 files changed, 567 deletions(-) delete mode 100644 examples/microphone_profiling.rs diff --git a/Cargo.lock b/Cargo.lock index 00868b46..070c4096 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "aho-corasick" version = "1.1.4" @@ -60,12 +54,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - [[package]] name = "arrayvec" version = "0.7.6" @@ -84,12 +72,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - [[package]] name = "bitflags" version = "1.3.2" @@ -129,12 +111,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" version = "1.11.1" @@ -176,15 +152,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "num-traits", -] - [[package]] name = "clap" version = "4.6.1" @@ -226,18 +193,6 @@ dependencies = [ "cc", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "colorous" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e18bf7a165bf7028fde98609a0f1e8f7498d762a212598e6c891f6893556ec" - [[package]] name = "combine" version = "4.6.7" @@ -263,15 +218,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - [[package]] name = "coreaudio-rs" version = "0.14.1" @@ -325,15 +271,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -382,12 +319,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" -[[package]] -name = "data-url" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" - [[package]] name = "derive_more" version = "2.1.1" @@ -505,30 +436,12 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "euclid" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" -dependencies = [ - "num-traits", -] - [[package]] name = "extended" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - [[package]] name = "fdk-aac" version = "0.8.0" @@ -553,51 +466,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "fontconfig-parser" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" -dependencies = [ - "roxmltree", -] - -[[package]] -name = "fontdb" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a6f9af55fb97ad673fb7a69533eb2f967648a06fa21f8c9bb2cd6d33975716" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - [[package]] name = "futures-core" version = "0.3.32" @@ -686,16 +560,6 @@ dependencies = [ "wasip3", ] -[[package]] -name = "gif" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "glob" version = "0.3.3" @@ -735,22 +599,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" -[[package]] -name = "image-webp" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imagesize" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" - [[package]] name = "indexmap" version = "2.14.0" @@ -849,29 +697,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kurbo" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" -dependencies = [ - "arrayvec", - "euclid", - "smallvec", -] - -[[package]] -name = "kuva" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455280de2b34485b88f1f8b80ee5e0df4289233ecad06d66dda79bf307e24f10" -dependencies = [ - "chrono", - "colorous", - "resvg", - "ryu", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -955,15 +780,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "memmap2" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" -dependencies = [ - "libc", -] - [[package]] name = "minimp3-sys" version = "0.3.2" @@ -984,16 +800,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "mio" version = "1.2.0" @@ -1254,12 +1060,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1272,19 +1072,6 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1331,12 +1118,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quickcheck" version = "1.1.0" @@ -1476,32 +1257,6 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" -[[package]] -name = "resvg" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958" -dependencies = [ - "gif", - "image-webp", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", - "zune-jpeg", -] - -[[package]] -name = "rgb" -version = "0.8.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" -dependencies = [ - "bytemuck", -] - [[package]] name = "rodio" version = "0.22.2" @@ -1515,7 +1270,6 @@ dependencies = [ "divan", "hound", "inquire", - "kuva", "lewton", "minimp3_fixed", "num-rational", @@ -1532,12 +1286,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - [[package]] name = "rstest" version = "0.26.1" @@ -1626,30 +1374,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "rustybuzz" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85d1ccd519e61834798eb52c4e886e8c2d7d698dd3d6ce0b1b47eb8557f1181" -dependencies = [ - "bitflags 2.11.1", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "same-file" version = "1.0.6" @@ -1750,27 +1474,6 @@ dependencies = [ "libc", ] -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "simplecss" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - [[package]] name = "slab" version = "0.4.12" @@ -1788,15 +1491,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "slotmap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" -dependencies = [ - "version_check", -] - [[package]] name = "smallvec" version = "1.15.1" @@ -1809,25 +1503,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - -[[package]] -name = "svgtypes" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" -dependencies = [ - "kurbo", - "siphasher", -] - [[package]] name = "symphonia" version = "0.5.5" @@ -2116,32 +1791,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - [[package]] name = "tinyvec" version = "1.11.0" @@ -2228,63 +1877,18 @@ dependencies = [ "strength_reduce", ] -[[package]] -name = "ttf-parser" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" -dependencies = [ - "core_maths", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f" - -[[package]] -name = "unicode-ccc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42" - [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - -[[package]] -name = "unicode-script" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" - [[package]] name = "unicode-segmentation" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - [[package]] name = "unicode-width" version = "0.2.2" @@ -2297,39 +1901,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "usvg" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6" -dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree", - "rustybuzz", - "simplecss", - "siphasher", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "walkdir" version = "2.5.0" @@ -2463,12 +2034,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "weezl" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" - [[package]] name = "winapi" version = "0.3.9" @@ -2788,12 +2353,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - [[package]] name = "zerocopy" version = "0.8.48" @@ -2819,18 +2378,3 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] -name = "zune-jpeg" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" -dependencies = [ - "zune-core", -] diff --git a/Cargo.toml b/Cargo.toml index 02a6f753..7a1d5d25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,6 @@ approx = "0.5.1" divan = "0.1.14" inquire = "0.9.3" symphonia-adapter-fdk-aac = "0.1" -kuva = { version = "0.1.0", features = ["png"] } [[bench]] name = "effects" diff --git a/examples/microphone_profiling.rs b/examples/microphone_profiling.rs deleted file mode 100644 index 6a3feb00..00000000 --- a/examples/microphone_profiling.rs +++ /dev/null @@ -1,110 +0,0 @@ -//! Example to acquire microphone data, and measure and plot the interval between samples. -//! This should be run in release mode. - -use kuva::prelude::*; -use rodio::microphone::MicrophoneBuilder; -use rodio::Source; -use std::error::Error; -use std::path::Path; -use std::time::{Duration, Instant}; - -fn main() -> Result<(), Box> { - let mut input = MicrophoneBuilder::new() - .default_device()? - .default_config()? - .prefer_sample_rates(vec![44_100.try_into()?, 48_000.try_into()?]) - .prefer_buffer_sizes(vec![16, 32, 64, 128, 256]) - .open_stream()?; - let config = *input.config(); - println!("Device config: {config:#?}"); - - let duration = Duration::from_secs(5); - println!("Recording for {duration:#?}..."); - let n_samples_est = input.sample_rate().get() as usize - * duration.as_secs() as usize - * input.channels().get() as usize; - let mut intervals = Vec::with_capacity(n_samples_est); - let start = Instant::now(); - let mut last_t = start; - while start.elapsed() < duration { - let _sample = input.next().unwrap(); - let dt = last_t.elapsed(); - last_t = Instant::now(); - intervals.push(dt); - } - println!( - "Recorded {} samples; mean sampling rate: {:.2} Hz ({:.2} μs/sample)", - intervals.len(), - intervals.len() as f64 / duration.as_secs_f64(), - duration.as_secs_f64() / intervals.len() as f64 * 1e6, - ); - print_stats(&intervals); - - println!("Rendering plots..."); - plot_intervals( - &intervals, - "microphone_intervals.png", - format!("{config:?}"), - )?; - println!("Plots saved to microphone_intervals.png"); - - Ok(()) -} - -fn print_stats(intervals: &[Duration]) { - let mean = intervals.iter().sum::() / intervals.len() as u32; - let std = intervals - .iter() - .map(|dt| (dt.saturating_sub(mean).as_nanos() as f64).powf(2.0)) - .sum::() - / intervals.len() as f64; - let std = Duration::from_nanos(std.sqrt() as u64); - let max = intervals.iter().max().unwrap(); - let min = intervals.iter().min().unwrap(); - println!("Measured interval between samples:"); - println!(" mean: {:.2} μs", mean.as_nanos() as f64 / 1000.0); - println!(" std dev: {:.2} μs", std.as_nanos() as f64 / 1000.0); - println!(" max: {:.2} μs", max.as_nanos() as f64 / 1000.0); - println!(" min: {:.2} μs", min.as_nanos() as f64 / 1000.0); -} - -fn plot_intervals( - intervals: &[Duration], - dest: impl AsRef, - title: String, -) -> Result<(), Box> { - let durations = intervals.iter().map(|dt| dt.as_nanos() as f64 / 1000.0); - let data_x_y = durations.clone().enumerate().map(|(i, dt)| (i as f64, dt)); - let scatter = ScatterPlot::new() - .with_data(data_x_y) - .with_marker(MarkerShape::Plus) - .with_color("steelblue") - .into(); - let scatter_plot = vec![scatter]; - let scatter_layout = Layout::auto_from_plots(&scatter_plot) - .with_x_label("Sample number") - .with_y_label("Interval [μs]"); - - let histogram = Histogram::new() - .with_data(durations) - .with_bins(100) - .with_color("steelblue") - .into(); - let hist_plot = vec![histogram]; - let hist_layout = Layout::auto_from_plots(&hist_plot) - .with_log_y() - .with_x_label("Interval [μs]") - .with_y_label("Count"); - - let scene = Figure::new(1, 2) - .with_plots(vec![scatter_plot, hist_plot]) - .with_layouts(vec![scatter_layout, hist_layout]) - .with_labels() - .with_title(title) - .with_title_size(10) - .render(); - - let png = PngBackend::new().with_scale(4.0).render_scene(&scene)?; - std::fs::write(dest, &png[..])?; - Ok(()) -} From fa13c6a62a5c9b91959fcfae750e2a1753dc3bf2 Mon Sep 17 00:00:00 2001 From: Craig Watson Date: Sun, 10 May 2026 17:35:28 -0700 Subject: [PATCH 7/7] `send` -> `try_send` --- src/microphone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microphone.rs b/src/microphone.rs index 035f2dfa..77b9b483 100644 --- a/src/microphone.rs +++ b/src/microphone.rs @@ -275,7 +275,7 @@ impl Microphone { config.stream_config(), move |data, _info| { for sample in SampleTypeConverter::<_, Sample>::new(data.into_iter().copied()) { - let _skip_if_player_is_behind = tx.send(sample).is_err(); + let _skip_if_player_is_behind = tx.try_send(sample).is_err(); } }, error_callback,