opusic_c/
lib.rs

1//!High level bindings to [libopus](https://github.com/xiph/opus)
2//!
3//!Target version [1.5.2](https://github.com/xiph/opus/releases/tag/v1.5.2)
4//!
5//!## Allocator
6//!
7//!This library uses Rust's allocator whenever possible
8//!
9//!## Features
10//!
11//!- `bundled` - Enables use of bundled OPUS code to build static library. Enabled by default. Refer to [opusic-sys](https://github.com/DoumanAsh/opusic-sys) for details
12//!- `dred` - Enables experimental DRED decoder. Disabled by default.
13//!- `osce` - Enables OSCE. Disabled by default.
14//!- `no-hardening` - disable run-time checks that are cheap and safe for use in production. Disabled by default.
15//!- `no-stack-protector` = disable stack protection. Disabled by default.
16//!- `no-fortify-source` - disable protection against buffer overflows. Disabled by default.
17//!
18
19#![no_std]
20#![warn(missing_docs)]
21#![allow(clippy::style)]
22#![allow(clippy::missing_transmute_annotations)]
23#![allow(clippy::needless_lifetimes)]
24
25use core::{slice, str};
26
27pub use opusic_sys as sys;
28
29macro_rules! map_sys_error {
30    ($result:expr => $ok:expr) => {{
31        let result = $result;
32        if result < 0 {
33            Err(result.into())
34        } else {
35            Ok($ok)
36        }
37    }};
38}
39
40mod mem;
41mod encoder;
42pub use encoder::*;
43mod decoder;
44pub use decoder::*;
45#[cfg(feature = "dred")]
46pub mod dred;
47pub mod repacketizer;
48pub mod multistream;
49pub mod utils;
50
51///Computes OPUS frame size in bytes for specified duration
52pub const fn frame_bytes_size(sample_rate: SampleRate, channels: Channels, duration_ms: usize) -> usize {
53    ((sample_rate as usize) * (channels as usize) * duration_ms) / 1000
54}
55
56const _FRAME_SIZE_TEST: () = {
57    assert!(frame_bytes_size(SampleRate::Hz48000, Channels::Mono, 10) == 480);
58    assert!(frame_bytes_size(SampleRate::Hz48000, Channels::Stereo, 10) == 960);
59};
60
61#[repr(i32)]
62#[derive(Debug, Clone, Copy, Eq, PartialEq)]
63///Underlying libopus error codes
64pub enum ErrorCode {
65    ///No error
66    Ok = sys::OPUS_OK,
67    ///One or more invalid/out of range arguments
68    BadArg = sys::OPUS_BAD_ARG,
69    ///Memory allocation has failed
70    AllocFail = sys::OPUS_ALLOC_FAIL,
71    ///An encoder or decoder structure is invalid or already freed
72    InvalidState = sys::OPUS_INVALID_STATE,
73    ///The compressed data passed is corrupted
74    InvalidPacket = sys::OPUS_INVALID_PACKET,
75    ///Not enough bytes allocated in the buffer
76    BufferTooSmall = sys::OPUS_BUFFER_TOO_SMALL,
77    ///An internal error was detected
78    Internal = sys::OPUS_INTERNAL_ERROR,
79    ///Invalid/unsupported request number
80    Unimplemented = sys::OPUS_UNIMPLEMENTED,
81    ///Unknown error variant. Should not be possible
82    Unknown = -200,
83}
84
85impl ErrorCode {
86    #[cold]
87    #[inline(never)]
88    const fn unknown() -> Self {
89        Self::Unknown
90    }
91
92    #[cold]
93    #[inline(never)]
94    const fn invalid_packet() -> Self {
95        Self::InvalidPacket
96    }
97
98    #[cold]
99    #[inline(never)]
100    const fn bad_arg() -> Self {
101        Self::BadArg
102    }
103
104    #[cold]
105    #[inline(never)]
106    const fn alloc_fail() -> Self {
107        Self::AllocFail
108    }
109
110    #[inline]
111    ///Returns text representation of error
112    pub const fn message(&self) -> &'static str {
113        match self {
114            Self::Ok => "No error",
115            Self::BadArg => "One or more invalid/out of range arguments",
116            Self::AllocFail => "Memory allocation has failed",
117            Self::InvalidState => "An encoder or decoder structure is invalid or already freed",
118            Self::InvalidPacket => "The compressed data passed is corrupted",
119            Self::BufferTooSmall => "Not enough bytes allocated in the buffer",
120            Self::Internal => "An internal error was detected",
121            Self::Unimplemented => "Invalid/unsupported request number",
122            Self::Unknown => "Unknown error",
123        }
124    }
125}
126
127impl From<i32> for ErrorCode {
128    #[inline]
129    fn from(value: i32) -> Self {
130        match value {
131            sys::OPUS_OK => Self::Ok,
132            sys::OPUS_UNIMPLEMENTED => Self::Unimplemented,
133            sys::OPUS_INVALID_STATE => Self::InvalidState,
134            sys::OPUS_INVALID_PACKET => Self::InvalidPacket,
135            sys::OPUS_INTERNAL_ERROR => Self::Internal,
136            sys::OPUS_BUFFER_TOO_SMALL => Self::BufferTooSmall,
137            sys::OPUS_BAD_ARG => Self::BadArg,
138            sys::OPUS_ALLOC_FAIL => Self::AllocFail,
139            _ => Self::unknown(),
140        }
141    }
142}
143
144///Codec's bitrate configuration
145#[derive(Debug, Clone, Copy, Eq, PartialEq)]
146pub enum Bitrate {
147    ///Value set in bits rates per second
148    Value(u32),
149    ///Default setting. Determined by number of channels and sample rate
150    Auto,
151    ///Specifies to the codec to use as much rate as it can,
152    ///which is useful for controlling the rate by adjusting the output buffer size
153    Max,
154}
155
156impl From<i32> for Bitrate {
157    #[inline(always)]
158    fn from(value: i32) -> Self {
159        match value {
160            sys::OPUS_AUTO => Self::Auto,
161            //This actually cannot happen (because it is only instruction to set max value)
162            //But just in case have it
163            sys::OPUS_BITRATE_MAX => Self::Max,
164            value => Self::Value(value as _)
165        }
166    }
167}
168
169impl From<Bitrate> for i32 {
170    #[inline(always)]
171    fn from(value: Bitrate) -> Self {
172        match value {
173            Bitrate::Max => sys::OPUS_BITRATE_MAX,
174            Bitrate::Auto => sys::OPUS_AUTO,
175            Bitrate::Value(value) => value as _,
176        }
177    }
178}
179
180#[repr(i32)]
181#[derive(Debug, Clone, Copy, Eq, PartialEq)]
182///Coding mode
183pub enum Application {
184    ///Best for most VoIP/videoconference applications where listening quality and intelligibility matter most.
185    Voip = sys::OPUS_APPLICATION_VOIP,
186    ///Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input.
187    Audio = sys::OPUS_APPLICATION_AUDIO,
188    ///Only use when lowest-achievable latency is what matters most.
189    ///
190    ///Voice-optimized modes cannot be used.
191    LowDelay = sys::OPUS_APPLICATION_RESTRICTED_LOWDELAY,
192}
193
194impl Application {
195    #[inline(always)]
196    const fn from_sys(value: i32) -> Option<Self> {
197        match value {
198            sys::OPUS_APPLICATION_AUDIO => Some(Self::Audio),
199            sys::OPUS_APPLICATION_VOIP => Some(Self::Voip),
200            sys::OPUS_APPLICATION_RESTRICTED_LOWDELAY => Some(Self::LowDelay),
201            _ => None,
202        }
203    }
204}
205
206#[repr(i32)]
207#[derive(Debug, Clone, Copy, Eq, PartialEq)]
208///Possible sample rates to use
209pub enum SampleRate {
210    ///8000
211    Hz8000 = 8000,
212    ///12000
213    Hz12000 = 12000,
214    ///16000
215    Hz16000 = 16000,
216    ///24000
217    Hz24000 = 24000,
218    ///48000
219    Hz48000 = 48000,
220}
221
222#[repr(i32)]
223#[derive(Debug, Clone, Copy, Eq, PartialEq)]
224///The available bandwidth level settings.
225pub enum Bandwidth {
226    ///Auto/default setting.
227    Auto = sys::OPUS_AUTO,
228    ///4kHz bandpass.
229    Narrow = sys::OPUS_BANDWIDTH_NARROWBAND,
230    ///6kHz bandpass.
231    Medium = sys::OPUS_BANDWIDTH_MEDIUMBAND,
232    ///8kHz bandpass.
233    Wide = sys::OPUS_BANDWIDTH_WIDEBAND,
234    ///12kHz bandpass.
235    Superwide = sys::OPUS_BANDWIDTH_SUPERWIDEBAND,
236    ///20kHz bandpass.
237    Full = sys::OPUS_BANDWIDTH_FULLBAND,
238}
239
240impl From<i32> for Bandwidth {
241    #[inline(always)]
242    fn from(value: i32) -> Self {
243        match value {
244            sys::OPUS_BANDWIDTH_FULLBAND => Self::Full,
245            sys::OPUS_BANDWIDTH_SUPERWIDEBAND => Self::Superwide,
246            sys::OPUS_BANDWIDTH_WIDEBAND => Self::Wide,
247            sys::OPUS_BANDWIDTH_MEDIUMBAND => Self::Medium,
248            sys::OPUS_BANDWIDTH_NARROWBAND => Self::Narrow,
249            _ => Self::Auto
250        }
251    }
252}
253
254#[repr(u8)]
255#[derive(Debug, Clone, Copy, Eq, PartialEq)]
256///Number of channels
257pub enum Channels {
258    ///Single channel
259    Mono = 1,
260    ///Two channels
261    Stereo = 2,
262}
263
264#[repr(i32)]
265#[derive(Debug, Clone, Copy, Eq, PartialEq)]
266///Signal type
267pub enum Signal {
268    ///Default value
269    Auto = sys::OPUS_AUTO,
270    ///Bias thresholds towards choosing LPC or Hybrid modes
271    Voice = sys::OPUS_SIGNAL_VOICE,
272    ///Bias thresholds towards choosing MDCT modes
273    Music = sys::OPUS_SIGNAL_MUSIC,
274}
275
276impl From<i32> for Signal {
277    #[inline(always)]
278    fn from(value: i32) -> Self {
279        match value {
280            sys::OPUS_SIGNAL_MUSIC => Self::Music,
281            sys::OPUS_SIGNAL_VOICE => Self::Voice,
282            _ => Self::Auto
283        }
284    }
285}
286
287#[repr(i32)]
288#[derive(Debug, Clone, Copy, Eq, PartialEq)]
289///Possible values of inband forward error correction configuration.
290pub enum InbandFec {
291    ///Inband FEC disabled (default)
292    Off = 0,
293    ///Inband FEC enabled.
294    ///
295    ///If the packet loss rate is sufficiently high,
296    ///Opus will automatically switch to SILK even at high rates to enable use of that FEC.
297    Mode1 = 1,
298    ///Inband FEC enabled, but does not necessarily switch to SILK if we have music.
299    Mode2 = 2,
300}
301
302#[repr(i32)]
303#[derive(Debug, Clone, Copy, Eq, PartialEq)]
304///Frame duration configuration values
305pub enum FrameDuration {
306    ///Select frame size from the argument (default)
307    SizeArg = sys::OPUS_FRAMESIZE_ARG,
308    ///Use 2.5 ms frames
309    Size2_5 = sys::OPUS_FRAMESIZE_2_5_MS,
310    ///Use 5 ms frames
311    Size5 = sys::OPUS_FRAMESIZE_5_MS,
312    ///Use 10 ms frames
313    Size10 = sys::OPUS_FRAMESIZE_10_MS,
314    ///Use 20 ms frames
315    Size20 = sys::OPUS_FRAMESIZE_20_MS,
316    ///Use 40 ms frames
317    Size40 = sys::OPUS_FRAMESIZE_40_MS,
318    ///Use 60 ms frames
319    Size60 = sys::OPUS_FRAMESIZE_60_MS,
320    ///Use 80 ms frames
321    Size80 = sys::OPUS_FRAMESIZE_80_MS,
322    ///Use 100 ms frames
323    Size100 = sys::OPUS_FRAMESIZE_100_MS,
324    ///Use 120 ms frames
325    Size120 = sys::OPUS_FRAMESIZE_120_MS,
326}
327
328///Returns libopus version
329pub fn version() -> &'static str {
330    //Version string is always valid ASCII string so no need to worry about utf-8 validity
331    unsafe {
332        let ptr = sys::opus_get_version_string();
333        let mut len = 0usize;
334
335        while *ptr.add(len) != 0 {
336            len = len.saturating_add(1);
337        }
338
339        let slice = slice::from_raw_parts(ptr as _, len);
340        core::str::from_utf8_unchecked(slice)
341    }
342}