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