Skip to main content

opus_codec/
types.rs

1//! Common types and constants used by the Opus codec
2
3use crate::bindings::{
4    OPUS_APPLICATION_AUDIO, OPUS_APPLICATION_RESTRICTED_LOWDELAY, OPUS_APPLICATION_VOIP, OPUS_AUTO,
5    OPUS_BANDWIDTH_FULLBAND, OPUS_BANDWIDTH_MEDIUMBAND, OPUS_BANDWIDTH_NARROWBAND,
6    OPUS_BANDWIDTH_SUPERWIDEBAND, OPUS_BANDWIDTH_WIDEBAND, OPUS_BITRATE_MAX, OPUS_FRAMESIZE_2_5_MS,
7    OPUS_FRAMESIZE_5_MS, OPUS_FRAMESIZE_10_MS, OPUS_FRAMESIZE_20_MS, OPUS_FRAMESIZE_40_MS,
8    OPUS_FRAMESIZE_60_MS, OPUS_FRAMESIZE_80_MS, OPUS_FRAMESIZE_100_MS, OPUS_FRAMESIZE_120_MS,
9    OPUS_FRAMESIZE_ARG, OPUS_SIGNAL_MUSIC, OPUS_SIGNAL_VOICE,
10};
11
12/// Encoder application mode.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14pub enum Application {
15    /// Optimize for conversational speech.
16    #[default]
17    Voip = OPUS_APPLICATION_VOIP as isize,
18    /// Optimize for general audio/music.
19    Audio = OPUS_APPLICATION_AUDIO as isize,
20    /// Low-delay mode, reduced algorithmic delay.
21    RestrictedLowDelay = OPUS_APPLICATION_RESTRICTED_LOWDELAY as isize,
22}
23
24/// Audio channel layout.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum Channels {
27    /// Single-channel audio.
28    Mono = 1,
29    /// Two-channel interleaved audio.
30    Stereo = 2,
31}
32
33impl Channels {
34    /// As `usize`.
35    #[must_use]
36    pub const fn as_usize(self) -> usize {
37        self as usize
38    }
39
40    /// As `i32`.
41    #[must_use]
42    pub const fn as_i32(self) -> i32 {
43        self as i32
44    }
45}
46
47/// Supported input/output sample rates.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
49pub enum SampleRate {
50    /// 8 kHz.
51    Hz8000 = 8000,
52    /// 12 kHz.
53    Hz12000 = 12000,
54    /// 16 kHz.
55    Hz16000 = 16000,
56    /// 24 kHz.
57    Hz24000 = 24000,
58    /// 48 kHz.
59    #[default]
60    Hz48000 = 48000,
61}
62
63impl SampleRate {
64    /// As `i32`.
65    #[must_use]
66    pub const fn as_i32(self) -> i32 {
67        self as i32
68    }
69
70    /// Return true if the sample rate is valid for Opus.
71    #[must_use]
72    pub const fn is_valid(self) -> bool {
73        matches!(
74            self,
75            Self::Hz8000 | Self::Hz12000 | Self::Hz16000 | Self::Hz24000 | Self::Hz48000
76        )
77    }
78}
79
80/// Coded bandwidth classifications in packets.
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum Bandwidth {
83    /// 4 kHz bandpass.
84    Narrowband = OPUS_BANDWIDTH_NARROWBAND as isize,
85    /// 6 kHz bandpass.
86    Mediumband = OPUS_BANDWIDTH_MEDIUMBAND as isize,
87    /// 8 kHz bandpass.
88    Wideband = OPUS_BANDWIDTH_WIDEBAND as isize,
89    /// 12 kHz bandpass.
90    SuperWideband = OPUS_BANDWIDTH_SUPERWIDEBAND as isize,
91    /// 20 kHz bandpass.
92    Fullband = OPUS_BANDWIDTH_FULLBAND as isize,
93}
94
95/// Convenience frame sizes in milliseconds.
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum FrameSize {
98    /// 2.5 ms.
99    Ms2_5 = 25,
100    /// 5 ms.
101    Ms5 = 50,
102    /// 10 ms.
103    Ms10 = 100,
104    /// 20 ms.
105    Ms20 = 200,
106    /// 40 ms.
107    Ms40 = 400,
108    /// 60 ms.
109    Ms60 = 600,
110}
111
112impl FrameSize {
113    /// Number of samples for this duration at `sample_rate`.
114    #[must_use]
115    pub const fn samples(self, sample_rate: SampleRate) -> usize {
116        // FrameSize discriminants count 0.1 ms units, so divide by 10_000 to convert to seconds
117        (self as usize * (sample_rate as usize)) / 10_000
118    }
119}
120
121/// Hint the encoder about the type of content.
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123pub enum Signal {
124    /// Automatic selection (default).
125    Auto = OPUS_AUTO as isize,
126    /// Voice-optimized mode.
127    Voice = OPUS_SIGNAL_VOICE as isize,
128    /// Music/general audio optimized mode.
129    Music = OPUS_SIGNAL_MUSIC as isize,
130}
131
132/// Expert frame duration settings for the encoder.
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum ExpertFrameDuration {
135    /// Select frame size from the argument (default).
136    Auto = OPUS_FRAMESIZE_ARG as isize,
137    /// 2.5 ms.
138    Ms2_5 = OPUS_FRAMESIZE_2_5_MS as isize,
139    /// 5 ms.
140    Ms5 = OPUS_FRAMESIZE_5_MS as isize,
141    /// 10 ms.
142    Ms10 = OPUS_FRAMESIZE_10_MS as isize,
143    /// 20 ms.
144    Ms20 = OPUS_FRAMESIZE_20_MS as isize,
145    /// 40 ms.
146    Ms40 = OPUS_FRAMESIZE_40_MS as isize,
147    /// 60 ms.
148    Ms60 = OPUS_FRAMESIZE_60_MS as isize,
149    /// 80 ms.
150    Ms80 = OPUS_FRAMESIZE_80_MS as isize,
151    /// 100 ms.
152    Ms100 = OPUS_FRAMESIZE_100_MS as isize,
153    /// 120 ms.
154    Ms120 = OPUS_FRAMESIZE_120_MS as isize,
155}
156
157/// Encoder complexity wrapper in the range 0..=10.
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub struct Complexity(u32);
160
161impl Complexity {
162    /// Create a complexity value when `complexity` is in the range 0..=10.
163    #[must_use]
164    pub const fn try_new(complexity: u32) -> Option<Self> {
165        if complexity <= 10 {
166            Some(Self(complexity))
167        } else {
168            None
169        }
170    }
171
172    /// Create a new complexity value in range 0..=10.
173    ///
174    /// # Panics
175    /// Panics when `complexity` is greater than 10.
176    #[must_use]
177    pub const fn new(complexity: u32) -> Self {
178        assert!(complexity <= 10, "Complexity must be between 0 and 10");
179        Self(complexity)
180    }
181
182    /// Raw complexity value.
183    #[must_use]
184    pub const fn value(self) -> u32 {
185        self.0
186    }
187}
188
189impl Default for Complexity {
190    fn default() -> Self {
191        Self::new(10)
192    }
193}
194
195/// Bitrate control options.
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197pub enum Bitrate {
198    /// Let the encoder choose.
199    Auto,
200    /// Maximum allowed.
201    Max,
202    /// Explicit bits-per-second.
203    Custom(i32),
204}
205
206impl Bitrate {
207    /// Convert to libopus `i32` value.
208    #[must_use]
209    pub const fn value(self) -> i32 {
210        match self {
211            Self::Auto => OPUS_AUTO,
212            Self::Max => OPUS_BITRATE_MAX,
213            Self::Custom(bps) => bps,
214        }
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221
222    #[test]
223    fn frame_size_samples_are_correct() {
224        assert_eq!(FrameSize::Ms20.samples(SampleRate::Hz48000), 960);
225        assert_eq!(FrameSize::Ms5.samples(SampleRate::Hz16000), 80);
226        assert_eq!(FrameSize::Ms2_5.samples(SampleRate::Hz8000), 20);
227    }
228
229    #[test]
230    fn complexity_checked_constructor_rejects_out_of_range_values() {
231        assert_eq!(Complexity::try_new(0), Some(Complexity::new(0)));
232        assert_eq!(Complexity::try_new(10), Some(Complexity::new(10)));
233        assert_eq!(Complexity::try_new(11), None);
234    }
235}