Skip to main content

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