opus_codec/
encoder.rs

1//! Opus encoder implementation with safe wrappers
2
3use crate::bindings::{
4    OPUS_AUTO, OPUS_BANDWIDTH_FULLBAND, OPUS_BITRATE_MAX, OPUS_GET_BANDWIDTH_REQUEST,
5    OPUS_GET_BITRATE_REQUEST, OPUS_GET_COMPLEXITY_REQUEST, OPUS_GET_DTX_REQUEST,
6    OPUS_GET_EXPERT_FRAME_DURATION_REQUEST, OPUS_GET_FINAL_RANGE_REQUEST,
7    OPUS_GET_FORCE_CHANNELS_REQUEST, OPUS_GET_IN_DTX_REQUEST, OPUS_GET_INBAND_FEC_REQUEST,
8    OPUS_GET_LOOKAHEAD_REQUEST, OPUS_GET_LSB_DEPTH_REQUEST, OPUS_GET_MAX_BANDWIDTH_REQUEST,
9    OPUS_GET_PACKET_LOSS_PERC_REQUEST, OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST,
10    OPUS_GET_PREDICTION_DISABLED_REQUEST, OPUS_GET_SIGNAL_REQUEST, OPUS_GET_VBR_CONSTRAINT_REQUEST,
11    OPUS_GET_VBR_REQUEST, OPUS_SET_BANDWIDTH_REQUEST, OPUS_SET_BITRATE_REQUEST,
12    OPUS_SET_COMPLEXITY_REQUEST, OPUS_SET_DTX_REQUEST, OPUS_SET_EXPERT_FRAME_DURATION_REQUEST,
13    OPUS_SET_FORCE_CHANNELS_REQUEST, OPUS_SET_INBAND_FEC_REQUEST, OPUS_SET_LSB_DEPTH_REQUEST,
14    OPUS_SET_MAX_BANDWIDTH_REQUEST, OPUS_SET_PACKET_LOSS_PERC_REQUEST,
15    OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, OPUS_SET_PREDICTION_DISABLED_REQUEST,
16    OPUS_SET_SIGNAL_REQUEST, OPUS_SET_VBR_CONSTRAINT_REQUEST, OPUS_SET_VBR_REQUEST, OpusEncoder,
17    opus_encode, opus_encode_float, opus_encoder_create, opus_encoder_ctl, opus_encoder_destroy,
18    opus_encoder_get_size, opus_encoder_init,
19};
20use crate::constants::max_frame_samples_for;
21use crate::error::{Error, Result};
22use crate::types::{
23    Application, Bandwidth, Bitrate, Channels, Complexity, ExpertFrameDuration, SampleRate, Signal,
24};
25use crate::{AlignedBuffer, Ownership, RawHandle};
26use std::marker::PhantomData;
27use std::num::NonZeroUsize;
28use std::ops::{Deref, DerefMut};
29use std::ptr::NonNull;
30
31/// Safe wrapper around a libopus `OpusEncoder`.
32pub struct Encoder {
33    raw: RawHandle<OpusEncoder>,
34    sample_rate: SampleRate,
35    channels: Channels,
36}
37
38unsafe impl Send for Encoder {}
39unsafe impl Sync for Encoder {}
40
41/// Borrowed wrapper around an encoder state.
42pub struct EncoderRef<'a> {
43    inner: Encoder,
44    _marker: PhantomData<&'a mut OpusEncoder>,
45}
46
47unsafe impl Send for EncoderRef<'_> {}
48unsafe impl Sync for EncoderRef<'_> {}
49
50impl Encoder {
51    fn from_raw(
52        ptr: NonNull<OpusEncoder>,
53        sample_rate: SampleRate,
54        channels: Channels,
55        ownership: Ownership,
56    ) -> Self {
57        Self {
58            raw: RawHandle::new(ptr, ownership, opus_encoder_destroy),
59            sample_rate,
60            channels,
61        }
62    }
63
64    /// Size in bytes of an encoder state for external allocation.
65    ///
66    /// # Errors
67    /// Returns [`Error::BadArg`] if the channel count is invalid or libopus reports
68    /// an impossible size.
69    pub fn size(channels: Channels) -> Result<usize> {
70        let raw = unsafe { opus_encoder_get_size(channels.as_i32()) };
71        if raw <= 0 {
72            return Err(Error::BadArg);
73        }
74        usize::try_from(raw).map_err(|_| Error::InternalError)
75    }
76
77    /// Initialize a previously allocated encoder state.
78    ///
79    /// # Safety
80    /// The caller must provide a valid pointer to `Encoder::size()` bytes,
81    /// aligned to at least `align_of::<usize>()` (malloc-style alignment).
82    ///
83    /// # Errors
84    /// Returns [`Error::BadArg`] if `ptr` is null, or a mapped libopus error.
85    pub unsafe fn init_in_place(
86        ptr: *mut OpusEncoder,
87        sample_rate: SampleRate,
88        channels: Channels,
89        application: Application,
90    ) -> Result<()> {
91        if ptr.is_null() {
92            return Err(Error::BadArg);
93        }
94        if !crate::opus_ptr_is_aligned(ptr.cast()) {
95            return Err(Error::BadArg);
96        }
97        let r = unsafe {
98            opus_encoder_init(
99                ptr,
100                sample_rate.as_i32(),
101                channels.as_i32(),
102                application as i32,
103            )
104        };
105        if r != 0 {
106            return Err(Error::from_code(r));
107        }
108        Ok(())
109    }
110
111    /// Create a new encoder.
112    ///
113    /// # Errors
114    /// Returns an error if allocation fails or arguments are invalid.
115    pub fn new(
116        sample_rate: SampleRate,
117        channels: Channels,
118        application: Application,
119    ) -> Result<Self> {
120        // Validate sample rate
121        if !sample_rate.is_valid() {
122            return Err(Error::BadArg);
123        }
124
125        let mut error = 0i32;
126        let encoder = unsafe {
127            opus_encoder_create(
128                sample_rate.as_i32(),
129                channels.as_i32(),
130                application as i32,
131                std::ptr::addr_of_mut!(error),
132            )
133        };
134
135        if error != 0 {
136            return Err(Error::from_code(error));
137        }
138
139        let encoder = NonNull::new(encoder).ok_or(Error::AllocFail)?;
140
141        Ok(Self::from_raw(
142            encoder,
143            sample_rate,
144            channels,
145            Ownership::Owned,
146        ))
147    }
148
149    /// Encode 16-bit PCM into an Opus packet.
150    ///
151    /// # Errors
152    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for
153    /// invalid buffer sizes or frame size, or a mapped libopus error.
154    pub fn encode(&mut self, input: &[i16], output: &mut [u8]) -> Result<usize> {
155        // Validate input buffer size
156        if input.is_empty() {
157            return Err(Error::BadArg);
158        }
159
160        // Ensure input buffer is properly sized for the number of channels
161        if !input.len().is_multiple_of(self.channels.as_usize()) {
162            return Err(Error::BadArg);
163        }
164
165        let frame_size = input.len() / self.channels.as_usize();
166        let frame_size = NonZeroUsize::new(frame_size).ok_or(Error::BadArg)?;
167        // Validate frame size is within Opus limits for the configured sample rate
168        if frame_size.get() > max_frame_samples_for(self.sample_rate) {
169            return Err(Error::BadArg);
170        }
171
172        // Validate output buffer size
173        if output.is_empty() {
174            return Err(Error::BadArg);
175        }
176        if output.len() > i32::MAX as usize {
177            return Err(Error::BadArg);
178        }
179
180        let frame_size_i32 = i32::try_from(frame_size.get()).map_err(|_| Error::BadArg)?;
181        let out_len_i32 = i32::try_from(output.len()).map_err(|_| Error::BadArg)?;
182        let result = unsafe {
183            opus_encode(
184                self.raw.as_ptr(),
185                input.as_ptr(),
186                frame_size_i32,
187                output.as_mut_ptr(),
188                out_len_i32,
189            )
190        };
191
192        if result < 0 {
193            return Err(Error::from_code(result));
194        }
195
196        usize::try_from(result).map_err(|_| Error::InternalError)
197    }
198
199    /// Encode 16-bit PCM, capping output to `max_data_bytes`.
200    ///
201    /// Note: This does not itself enable FEC; use `set_inband_fec(true)` and
202    /// `set_packet_loss_perc(…)` to actually make the encoder produce FEC.
203    ///
204    /// # Errors
205    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for
206    /// invalid buffer sizes or frame size, or a mapped libopus error.
207    pub fn encode_limited(
208        &mut self,
209        input: &[i16],
210        output: &mut [u8],
211        max_data_bytes: usize,
212    ) -> Result<usize> {
213        // Validate input buffer size
214        if input.is_empty() {
215            return Err(Error::BadArg);
216        }
217
218        // Ensure input buffer is properly sized for the number of channels
219        if !input.len().is_multiple_of(self.channels.as_usize()) {
220            return Err(Error::BadArg);
221        }
222
223        let frame_size = input.len() / self.channels.as_usize();
224        let frame_size = NonZeroUsize::new(frame_size).ok_or(Error::BadArg)?;
225        // Validate frame size is within Opus limits for the configured sample rate
226        if frame_size.get() > max_frame_samples_for(self.sample_rate) {
227            return Err(Error::BadArg);
228        }
229
230        // Validate output buffer size
231        if output.is_empty() {
232            return Err(Error::BadArg);
233        }
234        if output.len() > i32::MAX as usize {
235            return Err(Error::BadArg);
236        }
237        // Validate max_data_bytes parameter
238        if max_data_bytes == 0 || max_data_bytes > output.len() {
239            return Err(Error::BadArg);
240        }
241
242        let frame_size_i32 = i32::try_from(frame_size.get()).map_err(|_| Error::BadArg)?;
243        let max_bytes_i32 = i32::try_from(max_data_bytes).map_err(|_| Error::BadArg)?;
244        let result = unsafe {
245            opus_encode(
246                self.raw.as_ptr(),
247                input.as_ptr(),
248                frame_size_i32,
249                output.as_mut_ptr(),
250                max_bytes_i32,
251            )
252        };
253
254        if result < 0 {
255            return Err(Error::from_code(result));
256        }
257
258        usize::try_from(result).map_err(|_| Error::InternalError)
259    }
260
261    /// Encode f32 PCM into an Opus packet.
262    ///
263    /// # Errors
264    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for
265    /// invalid buffer sizes or frame size, or a mapped libopus error.
266    pub fn encode_float(&mut self, input: &[f32], output: &mut [u8]) -> Result<usize> {
267        if input.is_empty() {
268            return Err(Error::BadArg);
269        }
270        if !input.len().is_multiple_of(self.channels.as_usize()) {
271            return Err(Error::BadArg);
272        }
273        let frame_size = input.len() / self.channels.as_usize();
274        let frame_size = NonZeroUsize::new(frame_size).ok_or(Error::BadArg)?;
275        if frame_size.get() > max_frame_samples_for(self.sample_rate) {
276            return Err(Error::BadArg);
277        }
278        if output.is_empty() || output.len() > i32::MAX as usize {
279            return Err(Error::BadArg);
280        }
281        let frame_i32 = i32::try_from(frame_size.get()).map_err(|_| Error::BadArg)?;
282        let out_len_i32 = i32::try_from(output.len()).map_err(|_| Error::BadArg)?;
283        let n = unsafe {
284            opus_encode_float(
285                self.raw.as_ptr(),
286                input.as_ptr(),
287                frame_i32,
288                output.as_mut_ptr(),
289                out_len_i32,
290            )
291        };
292        if n < 0 {
293            return Err(Error::from_code(n));
294        }
295        usize::try_from(n).map_err(|_| Error::InternalError)
296    }
297
298    // ===== Common encoder CTLs =====
299
300    /// Enable/disable in-band FEC generation (decoder can recover from losses).
301    ///
302    /// # Errors
303    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
304    pub fn set_inband_fec(&mut self, enabled: bool) -> Result<()> {
305        self.simple_ctl(OPUS_SET_INBAND_FEC_REQUEST as i32, i32::from(enabled))
306    }
307    /// Query in-band FEC setting.
308    ///
309    /// # Errors
310    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
311    pub fn inband_fec(&mut self) -> Result<bool> {
312        self.get_bool_ctl(OPUS_GET_INBAND_FEC_REQUEST as i32)
313    }
314
315    /// Hint expected packet loss percentage [0..=100].
316    ///
317    /// # Errors
318    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for out-of-range values,
319    /// or a mapped libopus error.
320    pub fn set_packet_loss_perc(&mut self, perc: i32) -> Result<()> {
321        if !(0..=100).contains(&perc) {
322            return Err(Error::BadArg);
323        }
324        self.simple_ctl(OPUS_SET_PACKET_LOSS_PERC_REQUEST as i32, perc)
325    }
326    /// Query packet loss percentage hint.
327    ///
328    /// # Errors
329    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
330    pub fn packet_loss_perc(&mut self) -> Result<i32> {
331        self.get_int_ctl(OPUS_GET_PACKET_LOSS_PERC_REQUEST as i32)
332    }
333
334    /// Enable/disable DTX (discontinuous transmission).
335    ///
336    /// # Errors
337    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
338    pub fn set_dtx(&mut self, enabled: bool) -> Result<()> {
339        self.simple_ctl(OPUS_SET_DTX_REQUEST as i32, i32::from(enabled))
340    }
341    /// Query DTX setting.
342    ///
343    /// # Errors
344    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
345    pub fn dtx(&mut self) -> Result<bool> {
346        self.get_bool_ctl(OPUS_GET_DTX_REQUEST as i32)
347    }
348    /// Returns true if encoder is currently in DTX.
349    ///
350    /// # Errors
351    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
352    pub fn in_dtx(&mut self) -> Result<bool> {
353        self.get_bool_ctl(OPUS_GET_IN_DTX_REQUEST as i32)
354    }
355
356    /// Constrain VBR to reduce instant bitrate swings.
357    ///
358    /// # Errors
359    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
360    pub fn set_vbr_constraint(&mut self, constrained: bool) -> Result<()> {
361        self.simple_ctl(
362            OPUS_SET_VBR_CONSTRAINT_REQUEST as i32,
363            i32::from(constrained),
364        )
365    }
366    /// Query VBR constraint setting.
367    ///
368    /// # Errors
369    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
370    pub fn vbr_constraint(&mut self) -> Result<bool> {
371        self.get_bool_ctl(OPUS_GET_VBR_CONSTRAINT_REQUEST as i32)
372    }
373
374    /// Set maximum audio bandwidth the encoder may use.
375    ///
376    /// # Errors
377    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
378    pub fn set_max_bandwidth(&mut self, bw: Bandwidth) -> Result<()> {
379        self.simple_ctl(OPUS_SET_MAX_BANDWIDTH_REQUEST as i32, bw as i32)
380    }
381    /// Query max bandwidth.
382    ///
383    /// # Errors
384    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
385    pub fn max_bandwidth(&mut self) -> Result<Bandwidth> {
386        self.get_bandwidth_ctl(OPUS_GET_MAX_BANDWIDTH_REQUEST as i32)
387    }
388
389    /// Force a specific bandwidth (overrides automatic).
390    ///
391    /// # Errors
392    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
393    pub fn set_bandwidth(&mut self, bw: Bandwidth) -> Result<()> {
394        self.simple_ctl(OPUS_SET_BANDWIDTH_REQUEST as i32, bw as i32)
395    }
396    /// Query current forced bandwidth.
397    ///
398    /// # Errors
399    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
400    pub fn bandwidth(&mut self) -> Result<Bandwidth> {
401        self.get_bandwidth_ctl(OPUS_GET_BANDWIDTH_REQUEST as i32)
402    }
403
404    /// Force mono/stereo output, or None for automatic.
405    ///
406    /// # Errors
407    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
408    pub fn set_force_channels(&mut self, channels: Option<Channels>) -> Result<()> {
409        let val = match channels {
410            Some(Channels::Mono) => 1,
411            Some(Channels::Stereo) => 2,
412            None => crate::bindings::OPUS_AUTO,
413        };
414        self.simple_ctl(OPUS_SET_FORCE_CHANNELS_REQUEST as i32, val)
415    }
416    /// Query forced channels.
417    ///
418    /// # Errors
419    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
420    pub fn force_channels(&mut self) -> Result<Option<Channels>> {
421        let v = self.get_int_ctl(OPUS_GET_FORCE_CHANNELS_REQUEST as i32)?;
422        Ok(match v {
423            1 => Some(Channels::Mono),
424            2 => Some(Channels::Stereo),
425            _ => None,
426        })
427    }
428
429    /// Hint content type (voice or music).
430    ///
431    /// # Errors
432    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
433    pub fn set_signal(&mut self, signal: Signal) -> Result<()> {
434        self.simple_ctl(OPUS_SET_SIGNAL_REQUEST as i32, signal as i32)
435    }
436    /// Query current signal hint.
437    ///
438    /// # Errors
439    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::InternalError`] if the
440    /// response is not recognized, or a mapped libopus error.
441    pub fn signal(&mut self) -> Result<Signal> {
442        let v = self.get_int_ctl(OPUS_GET_SIGNAL_REQUEST as i32)?;
443        match v {
444            x if x == OPUS_AUTO => Ok(Signal::Auto),
445            x if x == crate::bindings::OPUS_SIGNAL_VOICE as i32 => Ok(Signal::Voice),
446            x if x == crate::bindings::OPUS_SIGNAL_MUSIC as i32 => Ok(Signal::Music),
447            _ => Err(Error::InternalError),
448        }
449    }
450
451    /// Encoder algorithmic lookahead (in samples at 48 kHz domain).
452    ///
453    /// # Errors
454    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
455    pub fn lookahead(&mut self) -> Result<i32> {
456        self.get_int_ctl(OPUS_GET_LOOKAHEAD_REQUEST as i32)
457    }
458    /// Final RNG state from the last encode (debugging/bitstream id).
459    ///
460    /// # Errors
461    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
462    pub fn final_range(&mut self) -> Result<u32> {
463        let mut val: u32 = 0;
464        let r = unsafe {
465            opus_encoder_ctl(
466                self.raw.as_ptr(),
467                OPUS_GET_FINAL_RANGE_REQUEST as i32,
468                &mut val,
469            )
470        };
471        if r != 0 {
472            return Err(Error::from_code(r));
473        }
474        Ok(val)
475    }
476
477    /// Set input LSB depth (typically 16-24 bits).
478    ///
479    /// # Errors
480    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for an
481    /// out-of-range bit depth, or a mapped libopus error.
482    pub fn set_lsb_depth(&mut self, bits: i32) -> Result<()> {
483        if !(8..=24).contains(&bits) {
484            return Err(Error::BadArg);
485        }
486        self.simple_ctl(OPUS_SET_LSB_DEPTH_REQUEST as i32, bits)
487    }
488    /// Query input LSB depth.
489    ///
490    /// # Errors
491    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
492    pub fn lsb_depth(&mut self) -> Result<i32> {
493        self.get_int_ctl(OPUS_GET_LSB_DEPTH_REQUEST as i32)
494    }
495
496    /// Set expert frame duration choice.
497    ///
498    /// # Errors
499    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
500    pub fn set_expert_frame_duration(&mut self, dur: ExpertFrameDuration) -> Result<()> {
501        self.simple_ctl(OPUS_SET_EXPERT_FRAME_DURATION_REQUEST as i32, dur as i32)
502    }
503    /// Query expert frame duration.
504    ///
505    /// # Errors
506    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::InternalError`] if the
507    /// response is not recognized, or a mapped libopus error.
508    pub fn expert_frame_duration(&mut self) -> Result<ExpertFrameDuration> {
509        let v = self.get_int_ctl(OPUS_GET_EXPERT_FRAME_DURATION_REQUEST as i32)?;
510        let vu = u32::try_from(v).map_err(|_| Error::InternalError)?;
511        match vu {
512            x if x == crate::bindings::OPUS_FRAMESIZE_ARG => Ok(ExpertFrameDuration::Auto),
513            x if x == crate::bindings::OPUS_FRAMESIZE_2_5_MS => Ok(ExpertFrameDuration::Ms2_5),
514            x if x == crate::bindings::OPUS_FRAMESIZE_5_MS => Ok(ExpertFrameDuration::Ms5),
515            x if x == crate::bindings::OPUS_FRAMESIZE_10_MS => Ok(ExpertFrameDuration::Ms10),
516            x if x == crate::bindings::OPUS_FRAMESIZE_20_MS => Ok(ExpertFrameDuration::Ms20),
517            x if x == crate::bindings::OPUS_FRAMESIZE_40_MS => Ok(ExpertFrameDuration::Ms40),
518            x if x == crate::bindings::OPUS_FRAMESIZE_60_MS => Ok(ExpertFrameDuration::Ms60),
519            x if x == crate::bindings::OPUS_FRAMESIZE_80_MS => Ok(ExpertFrameDuration::Ms80),
520            x if x == crate::bindings::OPUS_FRAMESIZE_100_MS => Ok(ExpertFrameDuration::Ms100),
521            x if x == crate::bindings::OPUS_FRAMESIZE_120_MS => Ok(ExpertFrameDuration::Ms120),
522            _ => Err(Error::InternalError),
523        }
524    }
525
526    /// Disable/enable inter-frame prediction (expert option).
527    ///
528    /// # Errors
529    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
530    pub fn set_prediction_disabled(&mut self, disabled: bool) -> Result<()> {
531        self.simple_ctl(
532            OPUS_SET_PREDICTION_DISABLED_REQUEST as i32,
533            i32::from(disabled),
534        )
535    }
536    /// Query prediction disabled flag.
537    ///
538    /// # Errors
539    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
540    pub fn prediction_disabled(&mut self) -> Result<bool> {
541        self.get_bool_ctl(OPUS_GET_PREDICTION_DISABLED_REQUEST as i32)
542    }
543
544    /// Disable/enable phase inversion (stereo decorrelation) in CELT (expert option).
545    ///
546    /// # Errors
547    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
548    pub fn set_phase_inversion_disabled(&mut self, disabled: bool) -> Result<()> {
549        self.simple_ctl(
550            OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST as i32,
551            i32::from(disabled),
552        )
553    }
554    /// Query phase inversion disabled flag.
555    ///
556    /// # Errors
557    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
558    pub fn phase_inversion_disabled(&mut self) -> Result<bool> {
559        self.get_bool_ctl(OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST as i32)
560    }
561
562    // --- internal helpers ---
563    fn simple_ctl(&mut self, req: i32, val: i32) -> Result<()> {
564        let r = unsafe { opus_encoder_ctl(self.raw.as_ptr(), req, val) };
565        if r != 0 {
566            return Err(Error::from_code(r));
567        }
568        Ok(())
569    }
570    fn get_bool_ctl(&mut self, req: i32) -> Result<bool> {
571        Ok(self.get_int_ctl(req)? != 0)
572    }
573    fn get_int_ctl(&mut self, req: i32) -> Result<i32> {
574        let mut v: i32 = 0;
575        let r = unsafe { opus_encoder_ctl(self.raw.as_ptr(), req, &mut v) };
576        if r != 0 {
577            return Err(Error::from_code(r));
578        }
579        Ok(v)
580    }
581    fn get_bandwidth_ctl(&mut self, req: i32) -> Result<Bandwidth> {
582        let v = self.get_int_ctl(req)?;
583        let vu = u32::try_from(v).map_err(|_| Error::InternalError)?;
584        match vu {
585            x if x == crate::bindings::OPUS_BANDWIDTH_NARROWBAND => Ok(Bandwidth::Narrowband),
586            x if x == crate::bindings::OPUS_BANDWIDTH_MEDIUMBAND => Ok(Bandwidth::Mediumband),
587            x if x == crate::bindings::OPUS_BANDWIDTH_WIDEBAND => Ok(Bandwidth::Wideband),
588            x if x == crate::bindings::OPUS_BANDWIDTH_SUPERWIDEBAND => Ok(Bandwidth::SuperWideband),
589            x if x == OPUS_BANDWIDTH_FULLBAND => Ok(Bandwidth::Fullband),
590            _ => Err(Error::InternalError),
591        }
592    }
593
594    /// Set target bitrate.
595    ///
596    /// # Errors
597    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
598    pub fn set_bitrate(&mut self, bitrate: Bitrate) -> Result<()> {
599        let result = unsafe {
600            opus_encoder_ctl(
601                self.raw.as_ptr(),
602                OPUS_SET_BITRATE_REQUEST as i32,
603                bitrate.value(),
604            )
605        };
606
607        if result != 0 {
608            return Err(Error::from_code(result));
609        }
610
611        Ok(())
612    }
613
614    /// Query current bitrate.
615    ///
616    /// # Errors
617    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
618    pub fn bitrate(&mut self) -> Result<Bitrate> {
619        let mut bitrate = 0i32;
620        let result = unsafe {
621            opus_encoder_ctl(
622                self.raw.as_ptr(),
623                OPUS_GET_BITRATE_REQUEST as i32,
624                &mut bitrate,
625            )
626        };
627
628        if result != 0 {
629            return Err(Error::from_code(result));
630        }
631
632        match bitrate {
633            OPUS_AUTO => Ok(Bitrate::Auto),
634            OPUS_BITRATE_MAX => Ok(Bitrate::Max),
635            bps => Ok(Bitrate::Custom(bps)),
636        }
637    }
638
639    /// Set encoder complexity [0..=10].
640    ///
641    /// # Errors
642    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
643    pub fn set_complexity(&mut self, complexity: Complexity) -> Result<()> {
644        let result = unsafe {
645            opus_encoder_ctl(
646                self.raw.as_ptr(),
647                OPUS_SET_COMPLEXITY_REQUEST as i32,
648                complexity.value() as i32,
649            )
650        };
651
652        if result != 0 {
653            return Err(Error::from_code(result));
654        }
655
656        Ok(())
657    }
658
659    /// Query encoder complexity.
660    ///
661    /// # Errors
662    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
663    pub fn complexity(&mut self) -> Result<Complexity> {
664        let mut complexity = 0i32;
665        let result = unsafe {
666            opus_encoder_ctl(
667                self.raw.as_ptr(),
668                OPUS_GET_COMPLEXITY_REQUEST as i32,
669                &mut complexity,
670            )
671        };
672
673        if result != 0 {
674            return Err(Error::from_code(result));
675        }
676
677        Ok(Complexity::new(
678            u32::try_from(complexity).map_err(|_| Error::InternalError)?,
679        ))
680    }
681
682    /// Enable or disable VBR.
683    ///
684    /// # Errors
685    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
686    pub fn set_vbr(&mut self, enabled: bool) -> Result<()> {
687        let vbr = i32::from(enabled);
688        let result =
689            unsafe { opus_encoder_ctl(self.raw.as_ptr(), OPUS_SET_VBR_REQUEST as i32, vbr) };
690
691        if result != 0 {
692            return Err(Error::from_code(result));
693        }
694
695        Ok(())
696    }
697
698    /// Query VBR status.
699    ///
700    /// # Errors
701    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
702    pub fn vbr(&mut self) -> Result<bool> {
703        let mut vbr = 0i32;
704        let result =
705            unsafe { opus_encoder_ctl(self.raw.as_ptr(), OPUS_GET_VBR_REQUEST as i32, &mut vbr) };
706
707        if result != 0 {
708            return Err(Error::from_code(result));
709        }
710
711        Ok(vbr != 0)
712    }
713
714    /// The encoder's configured sample rate.
715    #[must_use]
716    pub const fn sample_rate(&self) -> SampleRate {
717        self.sample_rate
718    }
719
720    /// The encoder's channel configuration.
721    #[must_use]
722    pub const fn channels(&self) -> Channels {
723        self.channels
724    }
725
726    /// Reset the encoder to its initial state (same config, cleared history).
727    ///
728    /// # Errors
729    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
730    pub fn reset(&mut self) -> Result<()> {
731        let r = unsafe {
732            opus_encoder_ctl(self.raw.as_ptr(), crate::bindings::OPUS_RESET_STATE as i32)
733        };
734        if r != 0 {
735            return Err(Error::from_code(r));
736        }
737        Ok(())
738    }
739}
740
741impl<'a> EncoderRef<'a> {
742    /// Wrap an externally-initialized encoder without taking ownership.
743    ///
744    /// # Safety
745    /// - `ptr` must point to valid, initialized memory of at least [`Encoder::size()`] bytes
746    /// - `ptr` must be aligned to at least `align_of::<usize>()` (malloc-style alignment)
747    /// - The memory must remain valid for the lifetime `'a`
748    /// - Caller is responsible for freeing the memory after this wrapper is dropped
749    ///
750    /// Use [`Encoder::init_in_place`] to initialize the memory before calling this.
751    #[must_use]
752    pub unsafe fn from_raw(
753        ptr: *mut OpusEncoder,
754        sample_rate: SampleRate,
755        channels: Channels,
756    ) -> Self {
757        debug_assert!(!ptr.is_null(), "from_raw called with null ptr");
758        debug_assert!(crate::opus_ptr_is_aligned(ptr.cast()));
759        let encoder = Encoder::from_raw(
760            unsafe { NonNull::new_unchecked(ptr) },
761            sample_rate,
762            channels,
763            Ownership::Borrowed,
764        );
765        Self {
766            inner: encoder,
767            _marker: PhantomData,
768        }
769    }
770
771    /// Initialize and wrap an externally allocated buffer.
772    ///
773    /// # Errors
774    /// Returns [`Error::BadArg`] if the buffer is too small, or a mapped libopus error.
775    pub fn init_in(
776        buf: &'a mut AlignedBuffer,
777        sample_rate: SampleRate,
778        channels: Channels,
779        application: Application,
780    ) -> Result<Self> {
781        let required = Encoder::size(channels)?;
782        if buf.capacity_bytes() < required {
783            return Err(Error::BadArg);
784        }
785        let ptr = buf.as_mut_ptr::<OpusEncoder>();
786        unsafe { Encoder::init_in_place(ptr, sample_rate, channels, application)? };
787        Ok(unsafe { Self::from_raw(ptr, sample_rate, channels) })
788    }
789}
790
791impl Deref for EncoderRef<'_> {
792    type Target = Encoder;
793
794    fn deref(&self) -> &Self::Target {
795        &self.inner
796    }
797}
798
799impl DerefMut for EncoderRef<'_> {
800    fn deref_mut(&mut self) -> &mut Self::Target {
801        &mut self.inner
802    }
803}