#![cfg_attr(not(test), no_std)]
extern crate alloc;
use alloc::{borrow::ToOwned, boxed::Box, ffi::CString, string::String};
use core::{ffi::CStr, fmt};
use libopusenc_static_sys as ope;
use opus_static_sys as opus;
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncError {
BadArg = ope::OPE_BAD_ARG,
InternalError = ope::OPE_INTERNAL_ERROR,
Unimplemented = ope::OPE_UNIMPLEMENTED,
AllocFail = ope::OPE_ALLOC_FAIL,
CannotOpen = ope::OPE_CANNOT_OPEN,
TooLate = ope::OPE_TOO_LATE,
InvalidPicture = ope::OPE_INVALID_PICTURE,
InvalidIcon = ope::OPE_INVALID_ICON,
WriteFail = ope::OPE_WRITE_FAIL,
CloseFail = ope::OPE_CLOSE_FAIL,
RustStringConversion = 12,
Unknown = -1,
}
impl OpusEncError {
#[must_use]
pub(crate) fn from_native(error: core::ffi::c_int) -> Self {
match error {
-11 => Self::BadArg,
-13 => Self::InternalError,
-15 => Self::Unimplemented,
-17 => Self::AllocFail,
-30 => Self::CannotOpen,
-31 => Self::TooLate,
-32 => Self::InvalidPicture,
-33 => Self::InvalidIcon,
-34 => Self::WriteFail,
-35 => Self::CloseFail,
_ => Self::Unknown,
}
}
}
impl fmt::Display for OpusEncError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe {
let c_str = CStr::from_ptr(ope::ope_strerror(*self as core::ffi::c_int));
write!(f, "{}", c_str.to_str().unwrap_or_default())
}
}
}
#[allow(clippy::cast_sign_loss)]
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncBandwidth {
Auto = opus::OPUS_AUTO as u32,
Narrowband4kHz = opus::OPUS_BANDWIDTH_NARROWBAND,
Mediumband6kHz = opus::OPUS_BANDWIDTH_MEDIUMBAND,
Wideband8kHz = opus::OPUS_BANDWIDTH_WIDEBAND,
SuperWideband12kHz = opus::OPUS_BANDWIDTH_SUPERWIDEBAND,
Fullband20kHz = opus::OPUS_BANDWIDTH_FULLBAND,
}
impl OpusEncBandwidth {
#[allow(clippy::cast_sign_loss)]
#[must_use]
pub(crate) fn from_native(val: core::ffi::c_int) -> Self {
match val as u32 {
opus::OPUS_BANDWIDTH_NARROWBAND => Self::Narrowband4kHz,
opus::OPUS_BANDWIDTH_MEDIUMBAND => Self::Mediumband6kHz,
opus::OPUS_BANDWIDTH_WIDEBAND => Self::Wideband8kHz,
opus::OPUS_BANDWIDTH_SUPERWIDEBAND => Self::SuperWideband12kHz,
opus::OPUS_BANDWIDTH_FULLBAND => Self::Fullband20kHz,
_ => Self::Auto,
}
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncApplication {
Voip = opus::OPUS_APPLICATION_VOIP,
Audio = opus::OPUS_APPLICATION_AUDIO,
RestrictedLowDelay = opus::OPUS_APPLICATION_RESTRICTED_LOWDELAY,
}
impl OpusEncApplication {
#[allow(clippy::cast_sign_loss)]
#[must_use]
pub(crate) fn from_native(val: core::ffi::c_int) -> Self {
match val as u32 {
opus::OPUS_APPLICATION_VOIP => Self::Voip,
opus::OPUS_APPLICATION_RESTRICTED_LOWDELAY => Self::RestrictedLowDelay,
_ => Self::Audio,
}
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncFrameSize {
MSARG = opus::OPUS_FRAMESIZE_ARG,
MS2_5 = opus::OPUS_FRAMESIZE_2_5_MS,
MS5 = opus::OPUS_FRAMESIZE_5_MS,
MS10 = opus::OPUS_FRAMESIZE_10_MS,
MS20 = opus::OPUS_FRAMESIZE_20_MS,
MS40 = opus::OPUS_FRAMESIZE_40_MS,
MS60 = opus::OPUS_FRAMESIZE_60_MS,
MS80 = opus::OPUS_FRAMESIZE_80_MS,
MS100 = opus::OPUS_FRAMESIZE_100_MS,
MS120 = opus::OPUS_FRAMESIZE_120_MS,
}
impl OpusEncFrameSize {
#[allow(clippy::cast_sign_loss)]
#[must_use]
pub(crate) fn from_native(val: core::ffi::c_int) -> Self {
match val as u32 {
opus::OPUS_FRAMESIZE_2_5_MS => Self::MS2_5,
opus::OPUS_FRAMESIZE_5_MS => Self::MS5,
opus::OPUS_FRAMESIZE_10_MS => Self::MS10,
opus::OPUS_FRAMESIZE_20_MS => Self::MS20,
opus::OPUS_FRAMESIZE_40_MS => Self::MS40,
opus::OPUS_FRAMESIZE_60_MS => Self::MS60,
opus::OPUS_FRAMESIZE_80_MS => Self::MS80,
opus::OPUS_FRAMESIZE_100_MS => Self::MS100,
opus::OPUS_FRAMESIZE_120_MS => Self::MS120,
_ => Self::MSARG,
}
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncForcedChannels {
Auto = opus::OPUS_AUTO,
Mono = 1,
Stereo = 2,
}
impl OpusEncForcedChannels {
#[must_use]
pub(crate) fn from_native(val: core::ffi::c_int) -> Self {
match val {
1 => Self::Mono,
2 => Self::Stereo,
_ => Self::Auto,
}
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncInbandFec {
Disabled = 0,
EnabledAlwaysSwitch = 1,
EnabledSometimesSwitch = 2,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncPrediction {
Enabled = 0,
Disabled = 1,
}
#[allow(clippy::cast_sign_loss)]
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncSignalBias {
Auto = opus::OPUS_AUTO as u32,
LpcHybrid = opus::OPUS_SIGNAL_VOICE,
MDCT = opus::OPUS_SIGNAL_MUSIC,
}
impl OpusEncSignalBias {
#[allow(clippy::cast_sign_loss)]
#[must_use]
pub(crate) fn from_native(val: core::ffi::c_int) -> Self {
match val as u32 {
opus::OPUS_SIGNAL_VOICE => Self::LpcHybrid,
opus::OPUS_SIGNAL_MUSIC => Self::MDCT,
_ => Self::Auto,
}
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncBitrate {
Auto = opus::OPUS_AUTO,
Max = opus::OPUS_BITRATE_MAX,
Explicit(u32),
}
impl OpusEncBitrate {
#[must_use]
#[allow(clippy::cast_sign_loss)]
pub(crate) fn from_native(val: core::ffi::c_int) -> Self {
match val {
opus::OPUS_AUTO => Self::Auto,
opus::OPUS_BITRATE_MAX => Self::Max,
val => Self::Explicit(val as u32),
}
}
#[must_use]
#[allow(clippy::cast_possible_wrap)]
pub(crate) fn to_native(self) -> core::ffi::c_int {
match self {
Self::Auto => opus::OPUS_AUTO,
Self::Max => opus::OPUS_BITRATE_MAX,
Self::Explicit(val) => val as core::ffi::c_int,
}
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncRequest {
ResetState = opus::OPUS_RESET_STATE,
GetFinalRange = opus::OPUS_GET_FINAL_RANGE_REQUEST,
SetBandwidth = opus::OPUS_SET_BANDWIDTH_REQUEST,
GetBandwidth = opus::OPUS_GET_BANDWIDTH_REQUEST,
GetSampleRate = opus::OPUS_GET_SAMPLE_RATE_REQUEST,
SetPhaseInversionDisabled = opus::OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST,
GetPhaseInversionDisabled = opus::OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST,
GetInDtx = opus::OPUS_GET_IN_DTX_REQUEST,
SetComplexity = opus::OPUS_SET_COMPLEXITY_REQUEST,
GetComplexity = opus::OPUS_GET_COMPLEXITY_REQUEST,
SetBitrate = opus::OPUS_SET_BITRATE_REQUEST,
GetBitrate = opus::OPUS_GET_BITRATE_REQUEST,
SetVbr = opus::OPUS_SET_VBR_REQUEST,
GetVbr = opus::OPUS_GET_VBR_REQUEST,
SetVbrConstraint = opus::OPUS_SET_VBR_CONSTRAINT_REQUEST,
GetVbrConstraint = opus::OPUS_GET_VBR_CONSTRAINT_REQUEST,
SetForceChannels = opus::OPUS_SET_FORCE_CHANNELS_REQUEST,
GetForceChannels = opus::OPUS_GET_FORCE_CHANNELS_REQUEST,
SetMaxBandwidth = opus::OPUS_SET_MAX_BANDWIDTH_REQUEST,
GetMaxBandwidth = opus::OPUS_GET_MAX_BANDWIDTH_REQUEST,
SetSignal = opus::OPUS_SET_SIGNAL_REQUEST,
GetSignal = opus::OPUS_GET_SIGNAL_REQUEST,
SetApplication = opus::OPUS_SET_APPLICATION_REQUEST,
GetApplication = opus::OPUS_GET_APPLICATION_REQUEST,
GetLookahead = opus::OPUS_GET_LOOKAHEAD_REQUEST,
SetInbandFec = opus::OPUS_SET_INBAND_FEC_REQUEST,
GetInbandFec = opus::OPUS_GET_INBAND_FEC_REQUEST,
SetPacketLossPerc = opus::OPUS_SET_PACKET_LOSS_PERC_REQUEST,
GetPacketLossPerc = opus::OPUS_GET_PACKET_LOSS_PERC_REQUEST,
SetDtx = opus::OPUS_SET_DTX_REQUEST,
GetDtx = opus::OPUS_GET_DTX_REQUEST,
SetLsbDepth = opus::OPUS_SET_LSB_DEPTH_REQUEST,
GetLsbDepth = opus::OPUS_GET_LSB_DEPTH_REQUEST,
SetExpertFrameDuration = opus::OPUS_SET_EXPERT_FRAME_DURATION_REQUEST,
GetExpertFrameDuration = opus::OPUS_GET_EXPERT_FRAME_DURATION_REQUEST,
SetPredictionDisabled = opus::OPUS_SET_PREDICTION_DISABLED_REQUEST,
GetPredictionDisabled = opus::OPUS_GET_PREDICTION_DISABLED_REQUEST,
SetDredDuration = opus::OPUS_SET_DRED_DURATION_REQUEST,
GetDredDuration = opus::OPUS_GET_DRED_DURATION_REQUEST,
SetDnnBlob = opus::OPUS_SET_DNN_BLOB_REQUEST,
SetDecisionDelay = ope::OPE_SET_DECISION_DELAY_REQUEST,
GetDecisionDelay = ope::OPE_GET_DECISION_DELAY_REQUEST,
SetMuxingDelay = ope::OPE_SET_MUXING_DELAY_REQUEST,
GetMuxingDelay = ope::OPE_GET_MUXING_DELAY_REQUEST,
SetCommentPadding = ope::OPE_SET_COMMENT_PADDING_REQUEST,
GetCommentPadding = ope::OPE_GET_COMMENT_PADDING_REQUEST,
SetSerialNo = ope::OPE_SET_SERIALNO_REQUEST,
GetSerialNo = ope::OPE_GET_SERIALNO_REQUEST,
SetPacketCallback = ope::OPE_SET_PACKET_CALLBACK_REQUEST,
SetHeaderGain = ope::OPE_SET_HEADER_GAIN_REQUEST,
GetHeaderGain = ope::OPE_GET_HEADER_GAIN_REQUEST,
GetNbStreams = ope::OPE_GET_NB_STREAMS_REQUEST,
GetNbCoupledStreams = ope::OPE_GET_NB_COUPLED_STREAMS_REQUEST,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncPictureType {
Default = -1,
Other = 0,
PngFileIcon32x32 = 1,
OtherFileIcon = 2,
FrontCover = 3,
BackCover = 4,
LeafletPage = 5,
MediaLabel = 6,
LeadArtist = 7,
ArtistPerformer = 8,
Conductor = 9,
BandOrchestra = 10,
Composer = 11,
Lyricist = 12,
RecordingLocation = 13,
DuringRecording = 14,
DuringPerformance = 15,
MovieScreenCap = 16,
BrightColoredFish = 17,
Illustration = 18,
ArtistLogotype = 19,
PublisherLogotype = 20,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncChannelMapping {
MonoStereo = 0,
Surround = 1,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
pub enum OpusEncSampleRate {
Hz48000 = 48000,
Hz24000 = 24000,
Hz16000 = 16000,
Hz12000 = 12000,
Hz8000 = 8000,
}
impl OpusEncSampleRate {
#[must_use]
pub(crate) fn from_native(val: core::ffi::c_int) -> Self {
match val {
24000 => Self::Hz24000,
16000 => Self::Hz16000,
12000 => Self::Hz12000,
8000 => Self::Hz8000,
_ => Self::Hz48000,
}
}
}
pub type OpeWriteFunc<T> = fn(user_data: &mut T, ogg_opus_data: &[u8]) -> bool;
pub type OpeCloseFunc<T> = fn(user_data: &mut T) -> bool;
#[must_use]
pub fn get_version_string() -> String {
unsafe {
let c_str = CStr::from_ptr(ope::ope_get_version_string());
c_str.to_str().unwrap_or_default().to_owned()
}
}
#[must_use]
pub fn get_abi_version() -> i32 {
unsafe { ope::ope_get_abi_version() }
}
pub struct OpusEncComments {
ptr: *mut ope::OggOpusComments,
}
impl Drop for OpusEncComments {
fn drop(&mut self) {
unsafe {
if !self.ptr.is_null() {
ope::ope_comments_destroy(self.ptr);
}
}
}
}
impl OpusEncComments {
pub fn create() -> Result<Self, OpusEncError> {
let ptr = unsafe { ope::ope_comments_create() };
if ptr.is_null() {
Err(OpusEncError::AllocFail)
} else {
Ok(Self { ptr })
}
}
pub fn add(&mut self, tag: &str, val: &str) -> Result<&mut Self, OpusEncError> {
let tag = CString::new(tag).map_err(|_| OpusEncError::RustStringConversion)?;
let val = CString::new(val).map_err(|_| OpusEncError::RustStringConversion)?;
unsafe {
match ope::ope_comments_add(self.ptr, tag.as_ptr(), val.as_ptr()) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn add_string(&mut self, tag_and_val: &str) -> Result<&mut Self, OpusEncError> {
let tag_and_val = CString::new(tag_and_val).map_err(|_| OpusEncError::RustStringConversion)?;
unsafe {
match ope::ope_comments_add_string(self.ptr, tag_and_val.as_ptr()) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn add_picture(
&mut self,
filename: &str,
picture_type: OpusEncPictureType,
description: &str,
) -> Result<&mut Self, OpusEncError> {
let filename = CString::new(filename).map_err(|_| OpusEncError::RustStringConversion)?;
let description = CString::new(description).map_err(|_| OpusEncError::RustStringConversion)?;
unsafe {
match ope::ope_comments_add_picture(
self.ptr,
filename.as_ptr(),
picture_type as core::ffi::c_int,
description.as_ptr(),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn add_picture_from_memory(
&mut self,
data: &[u8],
picture_type: OpusEncPictureType,
description: &str,
) -> Result<&mut Self, OpusEncError> {
let description = CString::new(description).map_err(|_| OpusEncError::RustStringConversion)?;
unsafe {
match ope::ope_comments_add_picture_from_memory(
self.ptr,
data.as_ptr().cast(),
data.len(),
picture_type as core::ffi::c_int,
description.as_ptr(),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
}
pub struct OpusEnc<'a, T> {
ptr: *mut ope::OggOpusEnc,
write_cb: Option<OpeWriteFunc<T>>,
close_cb: Option<OpeCloseFunc<T>>,
cb_user_data: Option<&'a mut T>,
}
impl<T> Drop for OpusEnc<'_, T> {
fn drop(&mut self) {
unsafe {
if !self.ptr.is_null() {
ope::ope_encoder_drain(self.ptr);
ope::ope_encoder_destroy(self.ptr);
}
}
}
}
impl<'a, T> OpusEnc<'a, T> {
#[allow(clippy::cast_sign_loss)]
unsafe extern "C" fn write_cb(user_data: *mut core::ffi::c_void, data: *const u8, len: i32) -> i32 {
unsafe {
let enc = &mut *(user_data.cast::<Self>());
if let Some(callback) = enc.write_cb {
let ogg_opus_data = if data.is_null() {
&[]
} else {
alloc::slice::from_raw_parts(data, len as usize)
};
if let Some(cb_user_data) = &mut enc.cb_user_data {
i32::from(!callback(cb_user_data, ogg_opus_data))
} else {
1
}
} else {
1
}
}
}
unsafe extern "C" fn close_cb(user_data: *mut core::ffi::c_void) -> i32 {
unsafe {
let enc = &mut *(user_data.cast::<Self>());
if let Some(callback) = enc.close_cb {
if let Some(cb_user_data) = &mut enc.cb_user_data {
i32::from(!callback(cb_user_data))
} else {
1
}
} else {
1
}
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub fn write_float(&mut self, pcm: &[f32], num_channels: usize) -> Result<&mut Self, OpusEncError> {
let samples_per_channel = (pcm.len() / num_channels) as core::ffi::c_int;
unsafe {
match ope::ope_encoder_write_float(self.ptr, pcm.as_ptr(), samples_per_channel) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub fn write(&mut self, pcm: &[i16], num_channels: usize) -> Result<&mut Self, OpusEncError> {
let samples_per_channel = (pcm.len() / num_channels) as core::ffi::c_int;
unsafe {
match ope::ope_encoder_write(self.ptr, pcm.as_ptr(), samples_per_channel) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
#[allow(clippy::cast_sign_loss)]
#[must_use]
pub fn get_page(&mut self, flush: bool) -> Option<&[u8]> {
let mut page_size: ope::opus_int32 = 0;
let mut page_ptr: *mut core::ffi::c_uchar = core::ptr::null_mut();
unsafe {
if ope::ope_encoder_get_page(self.ptr, &mut page_ptr, &mut page_size, core::ffi::c_int::from(flush)) == 1 {
Some(core::slice::from_raw_parts(page_ptr, page_size as usize))
} else {
None
}
}
}
pub fn drain(&mut self) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_drain(self.ptr) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn chain_current(&mut self, comments: &mut OpusEncComments) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_chain_current(self.ptr, comments.ptr) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn continue_new_file(&mut self, path: &str, comments: &mut OpusEncComments) -> Result<&mut Self, OpusEncError> {
let path = CString::new(path).map_err(|_| OpusEncError::RustStringConversion)?;
unsafe {
match ope::ope_encoder_continue_new_file(self.ptr, path.as_ptr(), comments.ptr) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn continue_new_callbacks(
&mut self,
user_data: Option<&'a mut T>,
comments: &mut OpusEncComments,
) -> Result<&mut Self, OpusEncError> {
unsafe {
self.cb_user_data = user_data;
match ope::ope_encoder_continue_new_callbacks(self.ptr, core::ptr::from_mut(self).cast(), comments.ptr) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn flush_header(&mut self) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_flush_header(self.ptr) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn reset_state(&mut self) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(self.ptr, OpusEncRequest::ResetState as core::ffi::c_int) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_final_range(&mut self) -> Result<u32, OpusEncError> {
let mut final_range: u32 = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetFinalRange as core::ffi::c_int,
core::ptr::from_mut(&mut final_range),
) {
0 => Ok(final_range),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_bandwidth(&mut self, bandwidth: OpusEncBandwidth) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetBandwidth as core::ffi::c_int,
bandwidth as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_bandwidth(&mut self) -> Result<OpusEncBandwidth, OpusEncError> {
let mut bandwidth: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetBandwidth as core::ffi::c_int,
core::ptr::from_mut(&mut bandwidth),
) {
0 => Ok(OpusEncBandwidth::from_native(bandwidth)),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_sample_rate(&mut self) -> Result<OpusEncSampleRate, OpusEncError> {
let mut rate: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetSampleRate as core::ffi::c_int,
core::ptr::from_mut(&mut rate),
) {
0 => Ok(OpusEncSampleRate::from_native(rate)),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_phase_inversion_disabled(&mut self, disabled: bool) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetPhaseInversionDisabled as core::ffi::c_int,
core::ffi::c_int::from(disabled),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_phase_inversion_disabled(&mut self) -> Result<bool, OpusEncError> {
let mut disabled: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetPhaseInversionDisabled as core::ffi::c_int,
core::ptr::from_mut(&mut disabled),
) {
0 => Ok(disabled != 0),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_in_dtx(&mut self) -> Result<bool, OpusEncError> {
let mut in_dtx: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetInDtx as core::ffi::c_int,
core::ptr::from_mut(&mut in_dtx),
) {
0 => Ok(in_dtx != 0),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_complexity(&mut self, complexity: i32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetComplexity as core::ffi::c_int,
complexity.clamp(0, 10) as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_complexity(&mut self) -> Result<i32, OpusEncError> {
let mut complexity: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetComplexity as core::ffi::c_int,
core::ptr::from_mut(&mut complexity),
) {
0 => Ok(complexity),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_bitrate(&mut self, bitrate: OpusEncBitrate) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetBitrate as core::ffi::c_int,
bitrate.to_native(),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_bitrate(&mut self) -> Result<OpusEncBitrate, OpusEncError> {
let mut bitrate: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetBitrate as core::ffi::c_int,
core::ptr::from_mut(&mut bitrate),
) {
0 => Ok(OpusEncBitrate::from_native(bitrate)),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_vbr(&mut self, vbr: bool) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetVbr as core::ffi::c_int,
core::ffi::c_int::from(vbr),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_vbr(&mut self) -> Result<bool, OpusEncError> {
let mut vbr: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetVbr as core::ffi::c_int,
core::ptr::from_mut(&mut vbr),
) {
0 => Ok(vbr != 0),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_vbr_constraint(&mut self, constrained: bool) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetVbrConstraint as core::ffi::c_int,
core::ffi::c_int::from(constrained),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_vbr_constraint(&mut self) -> Result<bool, OpusEncError> {
let mut constrained: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetVbrConstraint as core::ffi::c_int,
core::ptr::from_mut(&mut constrained),
) {
0 => Ok(constrained != 0),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_force_channels(&mut self, channels: OpusEncForcedChannels) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetForceChannels as core::ffi::c_int,
channels as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_force_channels(&mut self) -> Result<OpusEncForcedChannels, OpusEncError> {
let mut channels: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetForceChannels as core::ffi::c_int,
core::ptr::from_mut(&mut channels),
) {
0 => Ok(OpusEncForcedChannels::from_native(channels)),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_max_bandwidth(&mut self, bandwidth: OpusEncBandwidth) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetMaxBandwidth as core::ffi::c_int,
bandwidth as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_max_bandwidth(&mut self) -> Result<OpusEncBandwidth, OpusEncError> {
let mut bandwidth: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetMaxBandwidth as core::ffi::c_int,
core::ptr::from_mut(&mut bandwidth),
) {
0 => Ok(OpusEncBandwidth::from_native(bandwidth)),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_signal_bias(&mut self, signal: OpusEncSignalBias) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetSignal as core::ffi::c_int,
signal as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_signal_bias(&mut self) -> Result<OpusEncSignalBias, OpusEncError> {
let mut signal: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetSignal as core::ffi::c_int,
core::ptr::from_mut(&mut signal),
) {
0 => Ok(OpusEncSignalBias::from_native(signal)),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_application(&mut self, application: OpusEncApplication) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetApplication as core::ffi::c_int,
application as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_application(&mut self) -> Result<OpusEncApplication, OpusEncError> {
let mut application: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetApplication as core::ffi::c_int,
core::ptr::from_mut(&mut application),
) {
0 => Ok(OpusEncApplication::from_native(application)),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_lookahead(&mut self) -> Result<i32, OpusEncError> {
let mut lookahead: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetLookahead as core::ffi::c_int,
core::ptr::from_mut(&mut lookahead),
) {
0 => Ok(lookahead),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_inband_fec(&mut self, fec: bool) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetInbandFec as core::ffi::c_int,
core::ffi::c_int::from(fec),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_inband_fec(&mut self) -> Result<bool, OpusEncError> {
let mut fec: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetInbandFec as core::ffi::c_int,
core::ptr::from_mut(&mut fec),
) {
0 => Ok(fec != 0),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_packet_loss_perc(&mut self, loss_perc: i32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetPacketLossPerc as core::ffi::c_int,
loss_perc.clamp(0, 100) as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_packet_loss_perc(&mut self) -> Result<i32, OpusEncError> {
let mut loss_perc: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetPacketLossPerc as core::ffi::c_int,
core::ptr::from_mut(&mut loss_perc),
) {
0 => Ok(loss_perc),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_dtx_enabled(&mut self, enabled: bool) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetDtx as core::ffi::c_int,
core::ffi::c_int::from(enabled),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_dtx_enabled(&mut self) -> Result<bool, OpusEncError> {
let mut dtx: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetDtx as core::ffi::c_int,
core::ptr::from_mut(&mut dtx),
) {
0 => Ok(dtx != 0),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_lsb_depth(&mut self, depth: i32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetLsbDepth as core::ffi::c_int,
depth as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_lsb_depth(&mut self) -> Result<i32, OpusEncError> {
let mut depth: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetLsbDepth as core::ffi::c_int,
core::ptr::from_mut(&mut depth),
) {
0 => Ok(depth),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_expert_frame_duration(&mut self, duration: OpusEncFrameSize) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetExpertFrameDuration as core::ffi::c_int,
duration as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_expert_frame_duration(&mut self) -> Result<OpusEncFrameSize, OpusEncError> {
let mut duration: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetExpertFrameDuration as core::ffi::c_int,
core::ptr::from_mut(&mut duration),
) {
0 => Ok(OpusEncFrameSize::from_native(duration)),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_prediction_disabled(&mut self, disabled: bool) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetPredictionDisabled as core::ffi::c_int,
core::ffi::c_int::from(disabled),
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_prediction_disabled(&mut self) -> Result<bool, OpusEncError> {
let mut disabled: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetPredictionDisabled as core::ffi::c_int,
core::ptr::from_mut(&mut disabled),
) {
0 => Ok(disabled != 0),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_dred_duration(&mut self, duration: i32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetDredDuration as core::ffi::c_int,
duration as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_dred_duration(&mut self) -> Result<i32, OpusEncError> {
let mut duration: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetDredDuration as core::ffi::c_int,
core::ptr::from_mut(&mut duration),
) {
0 => Ok(duration),
err => Err(OpusEncError::from_native(err)),
}
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub fn set_dnn_blob(&mut self, blob: &[u8]) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetDnnBlob as core::ffi::c_int,
blob.as_ptr(),
blob.len() as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_decision_delay(&mut self, delay: i32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetDecisionDelay as core::ffi::c_int,
delay as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_decision_delay(&mut self) -> Result<i32, OpusEncError> {
let mut delay: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetDecisionDelay as core::ffi::c_int,
core::ptr::from_mut(&mut delay),
) {
0 => Ok(delay),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_muxing_delay(&mut self, delay: i32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetMuxingDelay as core::ffi::c_int,
delay as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_muxing_delay(&mut self) -> Result<i32, OpusEncError> {
let mut delay: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetMuxingDelay as core::ffi::c_int,
core::ptr::from_mut(&mut delay),
) {
0 => Ok(delay),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_comment_padding(&mut self, padding: i32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetCommentPadding as core::ffi::c_int,
padding as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_comment_padding(&mut self) -> Result<i32, OpusEncError> {
let mut padding: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetCommentPadding as core::ffi::c_int,
core::ptr::from_mut(&mut padding),
) {
0 => Ok(padding),
err => Err(OpusEncError::from_native(err)),
}
}
}
#[allow(clippy::cast_possible_wrap)]
pub fn set_serial_no(&mut self, serial_no: u32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetSerialNo as core::ffi::c_int,
serial_no as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
#[allow(clippy::cast_sign_loss)]
pub fn get_serial_no(&mut self) -> Result<u32, OpusEncError> {
let mut serial_no: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetSerialNo as core::ffi::c_int,
core::ptr::from_mut(&mut serial_no),
) {
0 => Ok(serial_no as u32),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn set_header_gain(&mut self, gain: i32) -> Result<&mut Self, OpusEncError> {
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::SetHeaderGain as core::ffi::c_int,
gain as core::ffi::c_int,
) {
0 => Ok(self),
err => Err(OpusEncError::from_native(err)),
}
}
}
pub fn get_header_gain(&mut self) -> Result<i32, OpusEncError> {
let mut gain: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetHeaderGain as core::ffi::c_int,
core::ptr::from_mut(&mut gain),
) {
0 => Ok(gain),
err => Err(OpusEncError::from_native(err)),
}
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub fn get_nb_streams(&mut self) -> Result<u8, OpusEncError> {
let mut nb_streams: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetNbStreams as core::ffi::c_int,
core::ptr::from_mut(&mut nb_streams),
) {
0 => Ok(nb_streams as u8),
err => Err(OpusEncError::from_native(err)),
}
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub fn get_nb_coupled_streams(&mut self) -> Result<u8, OpusEncError> {
let mut nb_coupled_streams: core::ffi::c_int = 0;
unsafe {
match ope::ope_encoder_ctl(
self.ptr,
OpusEncRequest::GetNbCoupledStreams as core::ffi::c_int,
core::ptr::from_mut(&mut nb_coupled_streams),
) {
0 => Ok(nb_coupled_streams as u8),
err => Err(OpusEncError::from_native(err)),
}
}
}
}
pub struct OpusEncoder;
impl OpusEncoder {
pub fn create_file<'a>(
path: &str,
comments: &'a mut OpusEncComments,
rate: OpusEncSampleRate,
channels: u8,
family: OpusEncChannelMapping,
) -> Result<Box<OpusEnc<'a, ()>>, OpusEncError> {
let mut error = core::ffi::c_int::default();
let path = CString::new(path).map_err(|_| OpusEncError::RustStringConversion)?;
unsafe {
let encoder = ope::ope_encoder_create_file(
path.as_ptr(),
comments.ptr,
rate as ope::opus_int32,
core::ffi::c_int::from(channels),
family as core::ffi::c_int,
core::ptr::from_mut(&mut error),
);
if encoder.is_null() {
Err(OpusEncError::from_native(error))
} else {
Ok(Box::new(OpusEnc {
ptr: encoder,
write_cb: None,
close_cb: None,
cb_user_data: None,
}))
}
}
}
pub fn create_callbacks<'a, T>(
write_callback: OpeWriteFunc<T>,
close_callback: OpeCloseFunc<T>,
user_data: Option<&'a mut T>,
comments: &mut OpusEncComments,
rate: OpusEncSampleRate,
channels: u8,
family: OpusEncChannelMapping,
) -> Result<Box<OpusEnc<'a, T>>, OpusEncError> {
let mut error = core::ffi::c_int::default();
let native_callbacks = ope::OpusEncCallbacks {
write: Some(OpusEnc::<T>::write_cb),
close: Some(OpusEnc::<T>::close_cb),
};
let mut enc = Box::new(OpusEnc {
ptr: core::ptr::null_mut(),
write_cb: Some(write_callback),
close_cb: Some(close_callback),
cb_user_data: user_data,
});
unsafe {
let encoder = ope::ope_encoder_create_callbacks(
&native_callbacks,
core::ptr::from_mut(enc.as_mut()).cast(),
comments.ptr,
rate as ope::opus_int32,
core::ffi::c_int::from(channels),
family as core::ffi::c_int,
core::ptr::from_mut(&mut error),
);
if encoder.is_null() {
Err(OpusEncError::from_native(error))
} else {
enc.ptr = encoder;
Ok(enc)
}
}
}
pub fn create_pull<'a>(
comments: &mut OpusEncComments,
rate: OpusEncSampleRate,
channels: u8,
family: OpusEncChannelMapping,
) -> Result<Box<OpusEnc<'a, ()>>, OpusEncError> {
let mut error = core::ffi::c_int::default();
unsafe {
let encoder = ope::ope_encoder_create_pull(
comments.ptr,
rate as ope::opus_int32,
core::ffi::c_int::from(channels),
family as core::ffi::c_int,
core::ptr::from_mut(&mut error),
);
if encoder.is_null() {
Err(OpusEncError::from_native(error))
} else {
Ok(Box::new(OpusEnc {
ptr: encoder,
write_cb: None,
close_cb: None,
cb_user_data: None,
}))
}
}
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use std::io::Write;
fn read_wav_file(path: &str) -> Vec<i16> {
let wav_reader = hound::WavReader::open(path).expect("Failed to open WAV file");
wav_reader
.into_samples::<i16>()
.collect::<Result<Vec<_>, _>>()
.expect("Failed to read WAV file")
}
#[test]
fn test_file_encoding() {
let audio = read_wav_file("test_assets/wav_test1.wav");
let mut comments = crate::OpusEncComments::create().expect("Failed to create OpusEncComments");
comments
.add("TITLE", "Some Track")
.expect("Failed to add comment")
.add_string("ARTIST=Test Artist")
.expect("Failed to add comment string")
.add_string("ALBUM=Test Album")
.expect("Failed to add comment string");
crate::OpusEncoder::create_file(
"test_assets/test1.opus",
&mut comments,
crate::OpusEncSampleRate::Hz48000,
1,
crate::OpusEncChannelMapping::MonoStereo,
)
.expect("Failed to create OpusEnc")
.set_vbr(true)
.expect("Failed to set VBR")
.set_complexity(10)
.expect("Failed to set complexity")
.set_bandwidth(crate::OpusEncBandwidth::SuperWideband12kHz)
.expect("Failed to set bandwidth")
.set_application(crate::OpusEncApplication::Audio)
.expect("Failed to set application")
.set_bitrate(crate::OpusEncBitrate::Explicit(24000))
.expect("Failed to set bitrate")
.write(&audio, 1)
.expect("Failed to write audio");
}
#[test]
fn test_file_encoding_callbacks() {
fn write_callback(file: &mut std::fs::File, data: &[u8]) -> bool {
file
.write_all(data)
.expect("Failed to write data to file from within callback");
true
}
fn close_callback(_file: &mut std::fs::File) -> bool {
true
}
let audio = read_wav_file("test_assets/wav_test1.wav");
let mut output_file = std::fs::File::create("test_assets/test_cb.opus").expect("Failed to create output file");
let mut comments = crate::OpusEncComments::create().expect("Failed to create OpusEncComments");
comments
.add("TITLE", "Some Track CB")
.expect("Failed to add comment")
.add_string("ARTIST=Test Artist CB")
.expect("Failed to add comment string")
.add_string("ALBUM=Test Album CB")
.expect("Failed to add comment string");
crate::OpusEncoder::create_callbacks(
write_callback,
close_callback,
Some(&mut output_file),
&mut comments,
crate::OpusEncSampleRate::Hz48000,
1,
crate::OpusEncChannelMapping::MonoStereo,
)
.expect("Failed to create OpusEnc")
.set_vbr(true)
.expect("Failed to set VBR")
.set_complexity(10)
.expect("Failed to set complexity")
.set_bandwidth(crate::OpusEncBandwidth::SuperWideband12kHz)
.expect("Failed to set bandwidth")
.set_application(crate::OpusEncApplication::Audio)
.expect("Failed to set application")
.set_bitrate(crate::OpusEncBitrate::Explicit(24000))
.expect("Failed to set bitrate")
.write(&audio, 1)
.expect("Failed to write audio");
}
}