use std::{
ffi::c_void,
mem::size_of,
ops::{Deref, DerefMut},
ptr::NonNull,
};
use crate::{api, cat::audio, define_opts, os};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct PropId(pub u32);
impl PropId {
#[doc(alias = "kAudioConverterPropertyMinimumInputBufferSize")]
pub const MINIMUM_INPUT_BUFFER_SIZE: Self = Self(u32::from_be_bytes(*b"mibs"));
#[doc(alias = "kAudioConverterPropertyMinimumOutputBufferSize")]
pub const MINIMUM_OUTPUT_BUFFER_SIZE: Self = Self(u32::from_be_bytes(*b"mobs"));
#[doc(alias = "kAudioConverterPropertyMaximumInputPacketSize")]
pub const MAXIMUM_INPUT_PACKET_SIZE: Self = Self(u32::from_be_bytes(*b"xips"));
#[doc(alias = "kAudioConverterPropertyMaximumOutputPacketSize")]
pub const MAXIMUM_OUTPUT_PACKET_SIZE: Self = Self(u32::from_be_bytes(*b"xops"));
#[doc(alias = "kAudioConverterPropertyCalculateInputBufferSize")]
pub const CALCULATE_INPUT_BUFFER_SIZE: Self = Self(u32::from_be_bytes(*b"cibs"));
#[doc(alias = "kAudioConverterPropertyCalculateOutputBufferSize")]
pub const CALCULATE_OUTPUT_BUFFER_SIZE: Self = Self(u32::from_be_bytes(*b"cobs"));
#[doc(alias = "kAudioConverterPropertyInputCodecParameters")]
pub const INPUT_CODEC_PARAMETERS: Self = Self(u32::from_be_bytes(*b"icdp"));
#[doc(alias = "kAudioConverterPropertyOutputCodecParameters")]
pub const OUTPUT_CODEC_PARAMETERS: Self = Self(u32::from_be_bytes(*b"ocdp"));
#[doc(alias = "kAudioConverterSampleRateConverterComplexity")]
pub const SAMPLE_RATE_CONVERTER_COMPLEXITY: Self = Self(u32::from_be_bytes(*b"srca"));
#[doc(alias = "kAudioConverterSampleRateConverterQuality")]
pub const SAMPLE_RATE_CONVERTER_QUALITY: Self = Self(u32::from_be_bytes(*b"srcq"));
#[doc(alias = "kAudioConverterSampleRateConverterInitialPhase")]
pub const SAMPLE_RATE_CONVERTER_INITIAL_PHASE: Self = Self(u32::from_be_bytes(*b"srcp"));
#[doc(alias = "kAudioConverterCodecQuality")]
pub const CODEC_QUALITY: Self = Self(u32::from_be_bytes(*b"cdqu"));
#[doc(alias = "kAudioConverterPrimeMethod")]
pub const PRIME_METHOD: Self = Self(u32::from_be_bytes(*b"prmm"));
#[doc(alias = "kAudioConverterPrimeInfo")]
pub const PRIME_INFO: Self = Self(u32::from_be_bytes(*b"prim"));
#[doc(alias = "kAudioConverterChannelMap")]
pub const CHANNEL_MAP: Self = Self(u32::from_be_bytes(*b"chmp"));
#[doc(alias = "kAudioConverterDecompressionMagicCookie")]
pub const DECOMPRESSION_MAGIC_COOKIE: Self = Self(u32::from_be_bytes(*b"dmgc"));
#[doc(alias = "kAudioConverterCompressionMagicCookie")]
pub const COMPRESSION_MAGIC_COOKIE: Self = Self(u32::from_be_bytes(*b"cmgc"));
#[doc(alias = "kAudioConverterEncodeBitRate")]
pub const ENCODE_BIT_RATE: Self = Self(u32::from_be_bytes(*b"brat"));
#[doc(alias = "kAudioConverterEncodeAdjustableSampleRate")]
pub const ENCODE_ADJUSTABLE_SAMPLE_RATE: Self = Self(u32::from_be_bytes(*b"ajsr"));
#[doc(alias = "kAudioConverterInputChannelLayout")]
pub const INPUT_CHANNEL_LAYOUT: Self = Self(u32::from_be_bytes(*b"icl "));
#[doc(alias = "kAudioConverterOutputChannelLayout")]
pub const OUTPUT_CHANNEL_LAYOUT: Self = Self(u32::from_be_bytes(*b"ocl "));
#[doc(alias = "kAudioConverterApplicableEncodeBitRates")]
pub const APPLICABLE_ENCODE_BIT_RATES: Self = Self(u32::from_be_bytes(*b"aebr"));
#[doc(alias = "kAudioConverterAvailableEncodeBitRates")]
pub const AVAILABLE_ENCODE_BIT_RATES: Self = Self(u32::from_be_bytes(*b"vebr"));
#[doc(alias = "kAudioConverterApplicableEncodeSampleRates")]
pub const APPLICABLE_ENCODE_SAMPLE_RATES: Self = Self(u32::from_be_bytes(*b"aesr"));
#[doc(alias = "kAudioConverterAvailableEncodeSampleRates")]
pub const AVAILABLE_ENCODE_SAMPLE_RATES: Self = Self(u32::from_be_bytes(*b"vesr"));
#[doc(alias = "kAudioConverterAvailableEncodeChannelLayoutTags")]
pub const AVAILABLE_ENCODE_CHANNEL_LAYOUT_TAGS: Self = Self(u32::from_be_bytes(*b"aecl"));
#[doc(alias = "kAudioConverterCurrentOutputStreamDescription")]
pub const CURRENT_OUTPUT_STREAM_DESCRIPTION: Self = Self(u32::from_be_bytes(*b"acod"));
#[doc(alias = "kAudioConverterCurrentInputStreamDescription")]
pub const CURRENT_INPUT_STREAM_DESCRIPTION: Self = Self(u32::from_be_bytes(*b"acid"));
#[doc(alias = "kAudioConverterPropertySettings")]
pub const SETTINGS: Self = Self(u32::from_be_bytes(*b"acps"));
#[doc(alias = "kAudioConverterPropertyBitDepthHint")]
pub const BIT_DEPTH_HINT: Self = Self(u32::from_be_bytes(*b"acbd"));
#[doc(alias = "kAudioConverterPropertyFormatList")]
pub const FORMAT_LIST: Self = Self(u32::from_be_bytes(*b"flst"));
#[cfg(target_os = "macos")]
#[doc(alias = "kAudioConverterPropertyDithering")]
pub const DITHERING: Self = Self(u32::from_be_bytes(*b"dith"));
#[cfg(target_os = "macos")]
#[doc(alias = "kAudioConverterPropertyDitherBitDepth")]
pub const DITHER_BIT_DEPTH: Self = Self(u32::from_be_bytes(*b"dbit"));
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(transparent)]
pub struct DitherAlgorithm(pub u32);
impl DitherAlgorithm {
#[doc(alias = "kDitherAlgorithm_TPDF")]
pub const TPDF: Self = Self(1);
#[doc(alias = "kDitherAlgorithm_NoiseShaping")]
pub const NOISE_SHAPING: Self = Self(2);
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(transparent)]
pub struct Quality(pub u32);
impl Quality {
#[doc(alias = "kAudioConverterQuality_Max")]
pub const MAX: Self = Self(0x7F);
#[doc(alias = "kAudioConverterQuality_High")]
pub const HIGH: Self = Self(0x60);
#[doc(alias = "kAudioConverterQuality_Medium")]
pub const MEDIUM: Self = Self(0x40);
#[doc(alias = "kAudioConverterQuality_Low")]
pub const LOW: Self = Self(0x20);
#[doc(alias = "kAudioConverterQuality_Min")]
pub const MIN: Self = Self(0);
}
impl Default for Quality {
fn default() -> Self {
Self::MEDIUM
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(transparent)]
pub struct SampleRateConverterComplexity(pub u32);
impl Default for SampleRateConverterComplexity {
fn default() -> Self {
Self::NORMAL
}
}
impl SampleRateConverterComplexity {
#[doc(alias = "kAudioConverterSampleRateConverterComplexity_Linear")]
pub const LINEAR: Self = Self(u32::from_be_bytes(*b"line"));
#[doc(alias = "kAudioConverterSampleRateConverterComplexity_Normal")]
pub const NORMAL: Self = Self(u32::from_be_bytes(*b"norm"));
#[doc(alias = "kAudioConverterSampleRateConverterComplexity_Mastering")]
pub const MASTERING: Self = Self(u32::from_be_bytes(*b"bats"));
#[doc(alias = "kAudioConverterSampleRateConverterComplexity_MinimumPhase")]
pub const MINIMUM_PHASE: Self = Self(u32::from_be_bytes(*b"minp"));
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(u32)]
pub enum PrimeMethod {
Pre = 0,
Normal,
None,
}
impl Default for PrimeMethod {
fn default() -> Self {
Self::Normal
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(C)]
pub struct PrimeInfo {
pub leading_frames: u32,
pub trailing_frames: u32,
}
pub mod err {
use crate::os::Error;
pub const FORMAT_NOT_SUPPORTED: Error = Error::from_be_bytes(*b"fmt?");
pub const OPERATION_NOT_SUPPORTED: Error = Error::new_unchecked(0x6F703F3F);
pub const PROPERTY_NOT_SUPPORTED: Error = Error::from_be_bytes(*b"prop");
pub const INVALID_INPUT_SIZE: Error = Error::from_be_bytes(*b"insz");
pub const INVALID_OUTPUT_SIZE: Error = Error::from_be_bytes(*b"otsz");
pub const UNSPECIFIED_ERROR: Error = Error::from_be_bytes(*b"what");
pub const BAD_PROPERTY_SIZE_ERROR: Error = Error::from_be_bytes(*b"!siz");
pub const REQUIRES_PACKET_DESCRIPTIONS_ERROR: Error = Error::from_be_bytes(*b"!pkd");
pub const INPUT_SAMPLE_RATE_OUT_OF_RANGE: Error = Error::from_be_bytes(*b"!isr");
pub const OUTPUT_SAMPLE_RATE_OUT_OF_RANGE: Error = Error::from_be_bytes(*b"!osr");
pub const HARDWARE_IN_USE: Error = Error::from_be_bytes(*b"hwiu");
pub const NO_HARDWARE_PERMISSION: Error = Error::from_be_bytes(*b"perm");
}
#[derive(Debug)]
#[repr(transparent)]
pub struct Converter(c_void);
#[derive(Debug)]
#[repr(transparent)]
pub struct ConverterRef(NonNull<Converter>);
unsafe impl Send for ConverterRef {}
impl Deref for ConverterRef {
type Target = Converter;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl DerefMut for ConverterRef {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.0.as_mut() }
}
}
#[doc(alias = "AudioConverterComplexInputDataProc")]
pub type ComplexInputDataProc<const N: usize = 1, D = c_void> = extern "C-unwind" fn(
converter: &Converter,
io_number_data_packets: &mut u32,
io_data: &mut audio::BufList<N>,
out_data_packet_descriptions: *mut *mut audio::StreamPacketDesc,
in_user_data: *mut D,
) -> os::Status;
impl ConverterRef {
pub unsafe fn new(
src_format: &audio::StreamBasicDesc,
dst_format: &audio::StreamBasicDesc,
out_audio_converter: *mut Option<Self>,
) -> os::Status {
unsafe { AudioConverterNew(src_format, dst_format, out_audio_converter) }
}
pub fn with_formats(
src_fmt: &audio::StreamBasicDesc,
dst_fmt: &audio::StreamBasicDesc,
) -> os::Result<Self> {
unsafe { os::result_unchecked(|val| Self::new(src_fmt, dst_fmt, val)) }
}
}
impl ConverterRef {
#[doc(alias = "AudioConverterNewWithOptions")]
#[api::available(macos = 15.0, ios = 18.0, tvos = 18.0, watchos = 11.0, visionos = 2.0)]
pub fn new_with_options(
src_format: &audio::StreamBasicDesc,
dst_format: &audio::StreamBasicDesc,
options: Opts,
out_audio_converter: *mut Option<Self>,
) -> os::Status {
unsafe {
AudioConverterNewWithOptions(src_format, dst_format, options, out_audio_converter)
}
}
#[api::available(macos = 15.0, ios = 18.0, tvos = 18.0, watchos = 11.0, visionos = 2.0)]
pub fn with_options(
src_fmt: &audio::StreamBasicDesc,
dst_fmt: &audio::StreamBasicDesc,
options: Opts,
) -> os::Result<Self> {
unsafe {
os::result_unchecked(|val| Self::new_with_options(src_fmt, dst_fmt, options, val))
}
}
}
impl Converter {
#[inline]
pub fn reset(&self) -> os::Result {
unsafe { AudioConverterReset(self).result() }
}
#[inline]
pub fn property_info(&self, prop_id: PropId) -> os::Result<PropInfo> {
let mut size = 0;
let mut writable = false;
unsafe {
AudioConverterGetPropertyInfo(self, prop_id, &mut size, &mut writable).result()?;
}
Ok(PropInfo { size, writable })
}
#[inline]
pub unsafe fn get_property(
&self,
prop_id: PropId,
io_prop_data_size: *mut u32,
out_prop_data: *mut c_void,
) -> os::Result {
unsafe {
AudioConverterGetProperty(self, prop_id, io_prop_data_size, out_prop_data).result()
}
}
#[inline]
pub unsafe fn set_property(
&mut self,
prop_id: PropId,
in_prop_data_size: u32,
in_prop_data: *const c_void,
) -> os::Result {
unsafe {
AudioConverterSetProperty(self, prop_id, in_prop_data_size, in_prop_data).result()
}
}
pub unsafe fn set_prop<T: Sized>(&mut self, prop_id: PropId, val: &T) -> os::Result {
let size = size_of::<T>() as u32;
unsafe { self.set_property(prop_id, size, val as *const _ as _) }
}
#[inline]
pub unsafe fn prop_vec<T: Sized>(&self, prop_id: PropId) -> os::Result<Vec<T>> {
let mut info = self.property_info(prop_id)?;
let mut vec = Vec::with_capacity(info.size as usize / size_of::<T>());
unsafe { self.get_property(prop_id, &mut info.size, vec.as_mut_ptr() as _) }?;
unsafe { vec.set_len(info.size as usize / size_of::<T>()) };
Ok(vec)
}
#[inline]
pub unsafe fn set_prop_vec<T: Sized>(&mut self, prop_id: PropId, val: Vec<T>) -> os::Result {
unsafe {
self.set_property(
prop_id,
(val.len() * std::mem::size_of::<T>()) as u32,
val.as_ptr() as _,
)
}?;
Ok(())
}
#[inline]
pub unsafe fn prop<T: Sized + Default>(&self, prop_id: PropId) -> os::Result<T> {
let mut size = size_of::<T>() as u32;
let mut value = Default::default();
unsafe { self.get_property(prop_id, &mut size, &mut value as *mut _ as _) }?;
Ok(value)
}
#[inline]
pub fn max_output_packet_size(&self) -> os::Result<u32> {
unsafe { self.prop(PropId::MAXIMUM_OUTPUT_PACKET_SIZE) }
}
#[inline]
pub fn sample_rate_converter_quality(&self) -> os::Result<Quality> {
unsafe { self.prop(PropId::SAMPLE_RATE_CONVERTER_QUALITY) }
}
#[inline]
pub fn sample_rate_converter_complexity(&self) -> os::Result<SampleRateConverterComplexity> {
unsafe { self.prop(PropId::SAMPLE_RATE_CONVERTER_COMPLEXITY) }
}
#[inline]
pub fn codec_quality(&self) -> os::Result<Quality> {
unsafe { self.prop(PropId::CODEC_QUALITY) }
}
#[inline]
pub fn set_codec_quality(&mut self, val: Quality) -> os::Result {
unsafe { self.set_prop(PropId::CODEC_QUALITY, &val) }
}
#[inline]
pub fn applicable_encode_bit_rates(&self) -> os::Result<Vec<audio::ValueRange>> {
unsafe { self.prop_vec(PropId::APPLICABLE_ENCODE_BIT_RATES) }
}
#[inline]
pub fn applicable_encode_sample_rates(&self) -> os::Result<Vec<audio::ValueRange>> {
unsafe { self.prop_vec(PropId::APPLICABLE_ENCODE_SAMPLE_RATES) }
}
#[inline]
pub fn compression_magic_cookie(&self) -> os::Result<Vec<u8>> {
unsafe { self.prop_vec(PropId::COMPRESSION_MAGIC_COOKIE) }
}
#[inline]
pub fn decompression_magic_cookie(&self) -> os::Result<Vec<u8>> {
unsafe { self.prop_vec(PropId::DECOMPRESSION_MAGIC_COOKIE) }
}
#[inline]
pub fn set_decompression_magic_cookie(&mut self, val: Vec<u8>) -> os::Result {
unsafe { self.set_prop_vec(PropId::DECOMPRESSION_MAGIC_COOKIE, val) }
}
#[inline]
pub fn current_output_stream_desc(&self) -> os::Result<audio::StreamBasicDesc> {
unsafe { self.prop(PropId::CURRENT_OUTPUT_STREAM_DESCRIPTION) }
}
#[inline]
pub fn current_input_stream_desc(&self) -> os::Result<audio::StreamBasicDesc> {
unsafe { self.prop(PropId::CURRENT_INPUT_STREAM_DESCRIPTION) }
}
#[inline]
pub fn encode_bit_rate(&self) -> os::Result<u32> {
unsafe { self.prop(PropId::ENCODE_BIT_RATE) }
}
#[inline]
pub fn set_encode_bit_rate(&mut self, val: u32) -> os::Result {
unsafe { self.set_prop(PropId::ENCODE_BIT_RATE, &val) }
}
#[inline]
pub fn prime_method(&self) -> os::Result<PrimeMethod> {
unsafe { self.prop(PropId::PRIME_METHOD) }
}
#[inline]
pub fn set_prime_method(&mut self, val: PrimeMethod) -> os::Result {
unsafe { self.set_prop(PropId::PRIME_METHOD, &val) }
}
#[inline]
pub unsafe fn fill_complex_buffer<const NI: usize, const NO: usize, D>(
&self,
in_input_data_proc: ComplexInputDataProc<NI, D>,
in_input_data_proc_user_data: *mut D,
io_output_data_packet_size: &mut u32,
out_output_data: &mut audio::BufList<NO>,
out_packet_description: *mut audio::StreamPacketDesc,
) -> os::Result {
unsafe {
AudioConverterFillComplexBuffer(
self,
std::mem::transmute(in_input_data_proc),
in_input_data_proc_user_data as _,
io_output_data_packet_size,
std::mem::transmute(out_output_data),
out_packet_description,
)
.result()
}
}
#[inline]
pub unsafe fn convert_complex_buffer<const N1: usize, const N2: usize>(
&self,
in_number_pcm_frames: u32,
in_input_data: *const audio::BufList<N1>,
out_output_data: *mut audio::BufList<N2>,
) -> os::Result {
unsafe {
AudioConverterConvertComplexBuffer(
self,
in_number_pcm_frames,
in_input_data as _,
out_output_data as _,
)
.result()
}
}
#[doc(alias = "AudioConverterConvertComplexBuffer")]
#[inline]
pub fn convert_complex_buf<const N1: usize, const N2: usize>(
&self,
frames: u32,
input: &audio::BufList<N1>,
output: &mut audio::BufList<N2>,
) -> os::Result {
unsafe { self.convert_complex_buffer(frames, input as *const _, output as *mut _) }
}
#[doc(alias = "AudioConverterConvertBuffer")]
#[inline]
pub fn convert_buf(&self, input: &[u8], output: &mut [u8]) -> os::Result<usize> {
let mut n = output.len() as u32;
unsafe {
self.convert_buffer(
input.len() as _,
input.as_ptr() as _,
&mut n,
output.as_mut_ptr() as _,
)
}?;
Ok(n as _)
}
#[doc(alias = "AudioConverterConvertBuffer")]
#[inline]
pub unsafe fn convert_buffer(
&self,
in_input_data_size: u32,
in_input_data: *const c_void,
io_output_data_size: *mut u32,
out_output_data: *mut c_void,
) -> os::Result {
unsafe {
AudioConverterConvertBuffer(
self,
in_input_data_size,
in_input_data,
io_output_data_size,
out_output_data,
)
.result()
}
}
#[inline]
pub fn fill_complex_buf_desc<const NI: usize, const NO: usize, D>(
&self,
proc: ComplexInputDataProc<NI, D>,
user_data: &mut D,
io_output_data_packet_size: &mut u32,
out_output_data: &mut audio::BufList<NO>,
out_packet_description: &mut Vec<audio::StreamPacketDesc>,
) -> os::Result {
unsafe {
self.fill_complex_buffer(
proc,
user_data,
io_output_data_packet_size,
out_output_data,
out_packet_description.as_mut_ptr(),
)
}
}
#[inline]
pub fn fill_complex_buf<const NI: usize, const NO: usize, D>(
&self,
proc: ComplexInputDataProc<NI, D>,
user_data: &mut D,
io_output_data_packet_size: &mut u32,
out_output_data: &mut audio::BufList<NO>,
) -> os::Result {
unsafe {
self.fill_complex_buffer(
proc,
user_data,
io_output_data_packet_size,
out_output_data,
std::ptr::null_mut(),
)
}
}
}
impl Drop for ConverterRef {
fn drop(&mut self) {
let res = unsafe { AudioConverterDispose(self) };
debug_assert!(res.is_ok());
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct PropInfo {
pub size: u32,
pub writable: bool,
}
define_opts!(
#[doc(alias = "AudioConverterOptions")]
pub Opts(u32)
);
impl Opts {
pub const UNBUFFERED: Self = Self(1 << 16);
}
#[link(name = "AudioToolbox", kind = "framework")]
#[api::weak]
unsafe extern "C-unwind" {
fn AudioConverterNew(
in_source_format: &audio::StreamBasicDesc,
in_destination_format: &audio::StreamBasicDesc,
out_audio_converer: *mut Option<ConverterRef>,
) -> os::Status;
#[api::available(macos = 15.0, ios = 18.0, tvos = 18.0, watchos = 11.0, visionos = 2.0)]
fn AudioConverterNewWithOptions(
in_source_format: &audio::StreamBasicDesc,
in_destination_format: &audio::StreamBasicDesc,
in_options: Opts,
out_audio_converer: *mut Option<ConverterRef>,
) -> os::Status;
fn AudioConverterReset(converter: &Converter) -> os::Status;
fn AudioConverterGetPropertyInfo(
converter: &Converter,
prop_id: PropId,
out_size: *mut u32,
out_writable: *mut bool,
) -> os::Status;
fn AudioConverterGetProperty(
converter: &Converter,
prop_id: PropId,
io_prop_data_size: *mut u32,
out_prop_data: *mut c_void,
) -> os::Status;
fn AudioConverterSetProperty(
converter: &Converter,
prop_id: PropId,
in_prop_data_size: u32,
in_prop_data: *const c_void,
) -> os::Status;
fn AudioConverterFillComplexBuffer(
converter: &Converter,
in_input_data_proc: ComplexInputDataProc,
in_input_data_proc_user_data: *mut c_void,
io_output_data_packet_size: &mut u32,
out_output_data: &mut audio::BufList,
out_packet_description: *mut audio::StreamPacketDesc,
) -> os::Status;
fn AudioConverterConvertComplexBuffer(
converter: &Converter,
in_number_pcm_frames: u32,
in_input_data: *const audio::BufList,
out_output_data: *mut audio::BufList,
) -> os::Status;
fn AudioConverterConvertBuffer(
converter: &Converter,
in_input_data_size: u32,
in_input_data: *const c_void,
io_output_data_size: *mut u32,
out_output_data: *mut c_void,
) -> os::Status;
fn AudioConverterDispose(converter: &Converter) -> os::Status;
}
#[cfg(test)]
mod tests {
use crate::at;
#[cfg(not(feature = "macos_15_0"))]
use crate::api;
#[cfg(feature = "macos_15_0")]
#[test]
fn basics() {
let src_fmt = at::audio::StreamBasicDesc::common_f32(44_100.0f64, 2, true);
let dst_fmt = at::audio::StreamBasicDesc::common_f32(44_100.0f64, 2, false);
let opts = at::audio::ConverterOpts::UNBUFFERED;
let converter = at::audio::ConverterRef::with_options(&src_fmt, &dst_fmt, opts).unwrap();
let size = converter.max_output_packet_size().unwrap();
assert_eq!(size, 4);
}
#[cfg(not(feature = "macos_15_0"))]
#[test]
fn basics() {
let src_fmt = at::audio::StreamBasicDesc::common_f32(44_100.0f64, 2, true);
let dst_fmt = at::audio::StreamBasicDesc::common_f32(44_100.0f64, 2, false);
let converter = if api::version!(macos = 15.0, ios = 18.0, tvos = 18.0) {
let opts = at::audio::ConverterOpts::UNBUFFERED;
unsafe { at::audio::ConverterRef::with_options(&src_fmt, &dst_fmt, opts).unwrap() }
} else {
at::audio::ConverterRef::with_formats(&src_fmt, &dst_fmt).unwrap()
};
let size = converter.max_output_packet_size().unwrap();
assert_eq!(size, 4);
}
}