1#![warn(missing_docs)]
28
29mod addon_error;
30pub use addon_error::AddonErrorKind;
31
32pub mod backend;
33pub mod capabilities;
34pub use capabilities::{
35 Availability, BackendKind, Capabilities, DeviceSelectionBehavior, DeviceWatcherCapabilities,
36 OsVersion, PidScope, ProcessCapabilities, SideEffectCapabilities, SideEffectCapability,
37 SideEffectCondition, SourceCapabilities, SourceCapability, SystemExclusionCapabilities,
38 UnavailableReason, WatchScope, WatcherMode,
39};
40pub mod chunk_ring;
41pub mod clock;
42pub mod diagnostics;
43pub mod error_context;
44mod errors;
45mod spawn_error;
46pub use spawn_error::spawn_error;
47pub mod inventory;
48mod loss;
49pub mod normalizer;
50pub mod process_list;
51pub mod quant;
52pub mod raw_ring;
53pub mod secondary_ring;
54pub mod startup;
55pub mod types;
56mod vad_error;
57
58pub use backend::{CaptureBackend, RawSink};
60pub use chunk_ring::{chunk_ring, ChunkConsumer, ChunkProducer};
61pub use clock::{monotonic_now_ns, ClockNormalizer};
62pub use normalizer::{InnerProcessor, NormalizedChunk, Normalizer, CHUNK_FRAMES};
63pub use quant::quantize_i16;
64pub use raw_ring::{raw_ring, RawConsumer, RawProducer};
65pub use secondary_ring::{secondary_chunk_ring, SecondaryChunkConsumer, SecondaryChunkProducer};
66pub use types::{
67 AudioChunk, AudioLoss, AudioPath, ChunkFlags, DefaultDeviceKind, DeviceEvent, DeviceInfo,
68 Error, ErrorContext, ErrorGroup, ErrorKind, Event, LossReason, MixLane, NativeStatus,
69 Operation, OutputFormat, OutputTap, Permission, ProcessInfo, Result, SecondaryChunk,
70 ShutdownReport, SourceKind, StreamConfig, CHANNELS, SAMPLE_RATE,
71};
72
73pub use diagnostics::CaptureDiagnostics;
74pub use startup::StartupDropReason;
75pub use vad_error::VadErrorCode;
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum ChunkMsOption {
80 SnakeCase,
82 JavaScript,
84}
85pub fn chunk_ms_error(option: ChunkMsOption) -> Error {
87 let name = match option {
88 ChunkMsOption::SnakeCase => "chunk_ms",
89 ChunkMsOption::JavaScript => "chunkMs",
90 };
91 Error::InvalidArg(format!(
92 "capture uses the canonical 20 ms frame contract; set {name} to 20"
93 ))
94}
95pub fn validate_chunk_ms(chunk_ms: u32) -> Result<()> {
97 if chunk_ms == 20 {
98 Ok(())
99 } else {
100 Err(chunk_ms_error(ChunkMsOption::SnakeCase))
101 }
102}
103#[cfg(test)]
104mod chunk_contract_tests {
105 #[test]
106 fn chunk_duration_rejection_explains_contract_and_remedy() {
107 let error = super::validate_chunk_ms(10).expect_err("noncanonical duration must fail");
108 let message = error.to_string();
109 assert!(
110 message.contains("canonical 20 ms frame contract"),
111 "{message}"
112 );
113 assert!(message.contains("set chunk_ms to 20"), "{message}");
114 assert!(super::validate_chunk_ms(20).is_ok());
115 }
116}
117
118#[cfg(test)]
119mod chunk_option_tests {
120 #[test]
121 fn javascript_chunk_duration_uses_its_public_option_spelling() {
122 let error = super::chunk_ms_error(super::ChunkMsOption::JavaScript);
123 let message = error.to_string();
124 assert!(message.contains("set chunkMs to 20"), "{message}");
125 assert!(message.contains("canonical 20 ms frame contract"));
126 }
127}