use std::ffi::c_void;
use crate::{arc, cat::audio, cf, define_opts, os};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[doc(alias = "AudioFilePropertyID")]
#[repr(transparent)]
pub struct PropId(pub u32);
impl PropId {
#[doc(alias = "kAudioFilePropertyFileFormat")]
pub const FILE_FORMAT: Self = Self(u32::from_be_bytes(*b"ffmt"));
#[doc(alias = "kAudioFilePropertyDataFormat")]
pub const DATA_FORMAT: Self = Self(u32::from_be_bytes(*b"dfmt"));
#[doc(alias = "kAudioFilePropertyIsOptimized")]
pub const IS_OPTIMIZED: Self = Self(u32::from_be_bytes(*b"optm"));
#[doc(alias = "kAudioFilePropertyMagicCookieData")]
pub const MAGIC_COOKIE_DATA: Self = Self(u32::from_be_bytes(*b"mgic"));
#[doc(alias = "kAudioFilePropertyAudioDataByteCount")]
pub const DATA_BYTE_COUNT: Self = Self(u32::from_be_bytes(*b"bcnt"));
#[doc(alias = "kAudioFilePropertyAudioDataPacketCount")]
pub const DATA_PACKET_COUNT: Self = Self(u32::from_be_bytes(*b"pcnt"));
#[doc(alias = "kAudioFilePropertyMaximumPacketSize")]
pub const MAXIMUM_PACKET_SIZE: Self = Self(u32::from_be_bytes(*b"psze"));
#[doc(alias = "kAudioFilePropertyDataOffset")]
pub const DATA_OFFSET: Self = Self(u32::from_be_bytes(*b"doff"));
#[doc(alias = "kAudioFilePropertyChannelLayout")]
pub const CHANNEL_LAYOUT: Self = Self(u32::from_be_bytes(*b"cmap"));
#[doc(alias = "kAudioFilePropertyDeferSizeUpdates")]
pub const DEFER_SIZE_UPDATES: Self = Self(u32::from_be_bytes(*b"dszu"));
#[doc(alias = "kAudioFilePropertyDataFormatName")]
pub const DATA_FORMAT_NAME: Self = Self(u32::from_be_bytes(*b"fnme"));
#[doc(alias = "kAudioFilePropertyMarkerList")]
pub const MARKER_LIST: Self = Self(u32::from_be_bytes(*b"mkls"));
#[doc(alias = "kAudioFilePropertyRegionList")]
pub const REGION_LIST: Self = Self(u32::from_be_bytes(*b"rgls"));
#[doc(alias = "kAudioFilePropertyPacketToFrame")]
pub const PACKET_TO_FRAME: Self = Self(u32::from_be_bytes(*b"pkfr"));
#[doc(alias = "kAudioFilePropertyFrameToPacket")]
pub const FRAME_TO_PACKET: Self = Self(u32::from_be_bytes(*b"frpk"));
#[doc(alias = "kAudioFilePropertyRestrictsRandomAccess")]
pub const RESTRICTS_RANDOM_ACCESS: Self = Self(u32::from_be_bytes(*b"rrap"));
#[doc(alias = "kAudioFilePropertyPacketToRollDistance")]
pub const PACKET_TO_ROLL_DISTANCE: Self = Self(u32::from_be_bytes(*b"pkrl"));
#[doc(alias = "kAudioFilePropertyPreviousIndependentPacket")]
pub const PREVIOUS_INDEPENDENT_PACKET: Self = Self(u32::from_be_bytes(*b"pind"));
#[doc(alias = "kAudioFilePropertyNextIndependentPacket")]
pub const NEXT_INDEPENDENT_PACKET: Self = Self(u32::from_be_bytes(*b"nind"));
#[doc(alias = "kAudioFilePropertyPacketToDependencyInfo")]
pub const PACKET_TO_DEPENDENCY_INFO: Self = Self(u32::from_be_bytes(*b"pkdp"));
#[doc(alias = "kAudioFilePropertyPacketToByte")]
pub const PACKET_TO_BYTE: Self = Self(u32::from_be_bytes(*b"pkby"));
#[doc(alias = "kAudioFilePropertyByteToPacket")]
pub const BYTE_TO_PACKET: Self = Self(u32::from_be_bytes(*b"bypk"));
#[doc(alias = "kAudioFilePropertyChunkIDs")]
pub const CHUNK_IDS: Self = Self(u32::from_be_bytes(*b"chid"));
#[doc(alias = "kAudioFilePropertyInfoDictionary")]
pub const INFO_DICTIONARY: Self = Self(u32::from_be_bytes(*b"info"));
#[doc(alias = "kAudioFilePropertyPacketTableInfo")]
pub const PACKET_TABLE_INFO: Self = Self(u32::from_be_bytes(*b"pnfo"));
#[doc(alias = "kAudioFilePropertyFormatList")]
pub const FORMAT_LIST: Self = Self(u32::from_be_bytes(*b"flst"));
#[doc(alias = "kAudioFilePropertyPacketSizeUpperBound")]
pub const PACKET_SIZE_UPPER_BOUND: Self = Self(u32::from_be_bytes(*b"pkub"));
#[doc(alias = "kAudioFilePropertyPacketRangeByteCountUpperBound")]
pub const PACKET_RANGE_BYTE_COUNT_UPPER_BOUND: Self = Self(u32::from_be_bytes(*b"prub"));
#[doc(alias = "kAudioFilePropertyReserveDuration")]
pub const RESERVE_DURATION: Self = Self(u32::from_be_bytes(*b"rsrv"));
#[doc(alias = "kAudioFilePropertyEstimatedDuration")]
pub const ESTIMATED_DURATION: Self = Self(u32::from_be_bytes(*b"edur"));
#[doc(alias = "kAudioFilePropertyBitRate")]
pub const BIT_RATE: Self = Self(u32::from_be_bytes(*b"brat"));
#[doc(alias = "kAudioFilePropertyID3Tag")]
pub const ID3_TAG: Self = Self(u32::from_be_bytes(*b"id3t"));
#[doc(alias = "kAudioFilePropertyID3TagOffset")]
pub const ID3_TAG_OFFSET: Self = Self(u32::from_be_bytes(*b"id3o"));
#[doc(alias = "kAudioFilePropertySourceBitDepth")]
pub const SOURCE_BIT_DEPTH: Self = Self(u32::from_be_bytes(*b"sbtd"));
#[doc(alias = "kAudioFilePropertyAlbumArtwork")]
pub const ALBUM_ARTWORK: Self = Self(u32::from_be_bytes(*b"aart"));
#[doc(alias = "kAudioFilePropertyAudioTrackCount")]
pub const AUDIO_TRACK_COUNT: Self = Self(u32::from_be_bytes(*b"atct"));
#[doc(alias = "kAudioFilePropertyUseAudioTrack")]
pub const USE_AUDIO_TRACK: Self = Self(u32::from_be_bytes(*b"uatk"));
}
#[derive(Debug)]
#[doc(alias = "OpaqueAudioFileID")]
#[repr(transparent)]
pub struct OpaqueFileId(c_void);
#[derive(Debug)]
#[doc(alias = "AudioFileID")]
#[repr(transparent)]
pub struct FileId(&'static mut OpaqueFileId);
impl FileId {
pub fn create(
url: &cf::Url,
type_id: FileTypeId,
format: &audio::StreamBasicDesc,
flags: Flags,
) -> os::Result<Self> {
unsafe {
os::result_unchecked(|val| AudioFileCreateWithURL(url, type_id, format, flags, val))
}
}
#[doc(alias = "AudioFileOpenURL")]
pub fn open(
url: &cf::Url,
permissions: Permissions,
type_hint: FileTypeId,
) -> os::Result<Self> {
unsafe { os::result_unchecked(|val| AudioFileOpenURL(url, permissions, type_hint, val)) }
}
#[doc(alias = "AudioFileWritePackets")]
#[inline]
pub fn write_packets(
&mut self,
use_cache: bool,
num_bytes: u32,
packet_descriptions: *const audio::StreamPacketDesc,
starting_packet: isize,
num_packets: *mut u32,
buffer: *const u8,
) -> os::Result {
unsafe {
AudioFileWritePackets(
self.0,
use_cache,
num_bytes,
packet_descriptions,
starting_packet,
num_packets,
buffer,
)
.result()
}
}
#[doc(alias = "AudioFileWritePackets")]
#[inline]
pub fn read_packets(
&mut self,
use_cache: bool,
num_bytes: &mut u32,
packet_descriptions: *mut audio::StreamPacketDesc,
starting_packet: isize,
num_packets: *mut u32,
buffer: *mut u8,
) -> os::Result {
unsafe {
AudioFileReadPacketData(
self.0,
use_cache,
num_bytes,
packet_descriptions,
starting_packet,
num_packets,
buffer,
)
.result()
}
}
#[doc(alias = "AudioFileGetPropertyInfo")]
#[inline]
pub fn property_info(&self, property_id: PropId) -> os::Result<(usize, bool)> {
let mut data_size = 0;
let mut is_writable = 0;
unsafe {
AudioFileGetPropertyInfo(
self.0,
property_id,
Some(&mut data_size),
Some(&mut is_writable),
)
.result()?;
}
Ok((data_size as _, is_writable == 1))
}
#[doc(alias = "AudioFileGetProperty")]
#[inline]
pub unsafe fn get_property(
&self,
property_id: PropId,
data_size: *mut u32,
property_data: *mut c_void,
) -> os::Result {
unsafe { AudioFileGetProperty(self.0, property_id, data_size, property_data).result() }
}
#[doc(alias = "AudioFileGetProperty")]
#[inline]
pub fn prop<T: Default>(&self, property_id: PropId) -> os::Result<T> {
let mut value = T::default();
let mut size = std::mem::size_of::<T>() as u32;
unsafe {
self.get_property(property_id, &mut size, &mut value as *mut T as _)?;
}
Ok(value)
}
#[doc(alias = "AudioFileSetProperty")]
#[inline]
pub fn set_prop<T: Sized>(&mut self, property_id: PropId, val: &T) -> os::Result {
let size = std::mem::size_of::<T>() as u32;
unsafe {
self.set_property(property_id, size, val as *const T as _)?;
}
Ok(())
}
#[doc(alias = "AudioFileSetProperty")]
#[inline]
pub unsafe fn set_property(
&mut self,
prop_id: PropId,
data_size: u32,
prop_data: *const c_void,
) -> os::Result {
unsafe { AudioFileSetProperty(self.0, prop_id, data_size, prop_data).result() }
}
#[inline]
pub unsafe fn prop_vec<T: Sized>(&self, prop_id: PropId) -> os::Result<Vec<T>> {
let (size, _writable) = self.property_info(prop_id)?;
let mut sz: u32 = size as _;
let mut vec = Vec::with_capacity(size / std::mem::size_of::<T>());
unsafe { self.get_property(prop_id, &mut sz, vec.as_mut_ptr() as _) }?;
unsafe { vec.set_len(sz as usize / std::mem::size_of::<T>()) };
Ok(vec)
}
#[inline]
pub fn magic_cookie_data(&self) -> os::Result<Vec<u8>> {
unsafe { self.prop_vec(PropId::MAGIC_COOKIE_DATA) }
}
#[inline]
pub fn maximum_packet_size(&self) -> os::Result<u32> {
self.prop(PropId::MAXIMUM_PACKET_SIZE)
}
#[inline]
pub fn defer_size_updates(&self) -> os::Result<bool> {
Ok(self.prop::<u32>(PropId::DEFER_SIZE_UPDATES)? == 1)
}
#[inline]
pub fn set_defer_size_updates(&mut self, val: bool) -> os::Result {
let v: u32 = val as _;
self.set_prop(PropId::DEFER_SIZE_UPDATES, &v)
}
#[inline]
pub fn optimized(&self) -> os::Result<bool> {
Ok(self.prop::<u32>(PropId::IS_OPTIMIZED)? == 1)
}
#[inline]
pub fn file_format(&self) -> os::Result<FileTypeId> {
self.prop(PropId::FILE_FORMAT)
}
#[inline]
pub fn data_format(&self) -> os::Result<audio::StreamBasicDesc> {
self.prop(PropId::DATA_FORMAT)
}
#[inline]
pub fn set_data_format(&mut self, asbd: &audio::StreamBasicDesc) -> os::Result {
self.set_prop(PropId::DATA_FORMAT, asbd)
}
#[inline]
pub fn reserve_duration(&self) -> os::Result<f64> {
self.prop(PropId::RESERVE_DURATION)
}
#[inline]
pub fn set_reserve_duration(&mut self, val: f64) -> os::Result {
self.set_prop(PropId::RESERVE_DURATION, &val)
}
#[inline]
pub fn estimated_duration(&self) -> os::Result<f64> {
self.prop(PropId::ESTIMATED_DURATION)
}
#[inline]
pub fn info_dictionary(&self) -> os::Result<arc::R<cf::Dictionary>> {
self.prop(PropId::INFO_DICTIONARY)
}
#[doc(alias = "AudioFileClose")]
#[inline]
pub fn close(&mut self) -> os::Result {
unsafe { AudioFileClose(self.0).result() }
}
}
impl Drop for FileId {
fn drop(&mut self) {
let res = self.close();
debug_assert!(res.is_ok(), "failed to close audio file properly {res:?}");
}
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[doc(alias = "AudioFileTypeID")]
#[repr(transparent)]
pub struct FileTypeId(pub u32);
impl FileTypeId {
#[doc(alias = "kAudioFileAIFFType")]
pub const AIFF: Self = Self(u32::from_be_bytes(*b"AIFF"));
#[doc(alias = "kAudioFileAIFCType")]
pub const AIFC: Self = Self(u32::from_be_bytes(*b"AIFC"));
#[doc(alias = "kAudioFileWAVEType")]
pub const WAVE: Self = Self(u32::from_be_bytes(*b"WAVE"));
#[doc(alias = "kAudioFileRF64Type")]
pub const RF64: Self = Self(u32::from_be_bytes(*b"RF64"));
#[doc(alias = "kAudioFileBW64Type")]
pub const BW64: Self = Self(u32::from_be_bytes(*b"BW64"));
#[doc(alias = "kAudioFileWave64Type")]
pub const WAVE64: Self = Self(u32::from_be_bytes(*b"W64f"));
#[doc(alias = "kAudioFileSoundDesigner2Type")]
pub const SOUND_DESIGNER2: Self = Self(u32::from_be_bytes(*b"Sd2f"));
#[doc(alias = "kAudioFileNextType")]
pub const NEXT: Self = Self(u32::from_be_bytes(*b"NeXT"));
#[doc(alias = "kAudioFileMP3Type")]
pub const MP3: Self = Self(u32::from_be_bytes(*b"MPG3"));
#[doc(alias = "kAudioFileMP2Type")]
pub const MP2: Self = Self(u32::from_be_bytes(*b"MPG2"));
#[doc(alias = "kAudioFileMP1Type")]
pub const MP1: Self = Self(u32::from_be_bytes(*b"MPG1"));
#[doc(alias = "kAudioFileAC3Type")]
pub const AC3: Self = Self(u32::from_be_bytes(*b"ac-3"));
#[doc(alias = "kAudioFileAAC_ADTSType")]
pub const AAC_ADTS: Self = Self(u32::from_be_bytes(*b"adts"));
#[doc(alias = "kAudioFileMPEG4Type")]
pub const MPEG4: Self = Self(u32::from_be_bytes(*b"mp4f"));
#[doc(alias = "kAudioFileM4AType")]
pub const M4A: Self = Self(u32::from_be_bytes(*b"m4af"));
#[doc(alias = "kAudioFileM4BType")]
pub const M4B: Self = Self(u32::from_be_bytes(*b"m4bf"));
#[doc(alias = "kAudioFileCAFType")]
pub const CAF: Self = Self(u32::from_be_bytes(*b"caff"));
#[doc(alias = "kAudioFile3GPType")]
pub const _3GP: Self = Self(u32::from_be_bytes(*b"3gpp"));
#[doc(alias = "kAudioFile3GP2Type")]
pub const _3GP2: Self = Self(u32::from_be_bytes(*b"3gp2"));
#[doc(alias = "kAudioFileAMRType")]
pub const AMR: Self = Self(u32::from_be_bytes(*b"amrf"));
#[doc(alias = "kAudioFileFLACType")]
pub const FLAC: Self = Self(u32::from_be_bytes(*b"flac"));
#[doc(alias = "kAudioFileLATMInLOASType")]
pub const LATM_IN_LOAS: Self = Self(u32::from_be_bytes(*b"loas"));
}
pub mod err {
use crate::os::Error;
#[doc(alias = "kAudioFileUnspecifiedError")]
pub const UNSPECIFIED: Error = Error::from_be_bytes(*b"wht?");
#[doc(alias = "kAudioFileUnsupportedFileTypeError")]
pub const UNSUPPORTED_FILE_TYPE: Error = Error::from_be_bytes(*b"typ?");
#[doc(alias = "kAudioFileUnsupportedDataFormatError")]
pub const UNSUPPORTED_DATA_FORMAT: Error = Error::from_be_bytes(*b"fmt?");
#[doc(alias = "kAudioFileUnsupportedPropertyError")]
pub const UNSUPPORTED_PROPERTY: Error = Error::from_be_bytes(*b"pty?");
#[doc(alias = "kAudioFileBadPropertySizeError")]
pub const BAD_PROPERTY_SIZE: Error = Error::from_be_bytes(*b"!siz");
#[doc(alias = "kAudioFilePermissionsError")]
pub const PERMISSIONS: Error = Error::from_be_bytes(*b"!prm");
#[doc(alias = "kAudioFileNotOptimizedError")]
pub const NOT_OPTIMIZED: Error = Error::from_be_bytes(*b"optm");
#[doc(alias = "kAudioFileInvalidChunkError")]
pub const INVALID_CHUNK: Error = Error::from_be_bytes(*b"chk?");
#[doc(alias = "kAudioFileDoesNotAllow64BitDataSizeError")]
pub const DOES_NOT_ALLOW64_BIT_DATA_SIZE: Error = Error::from_be_bytes(*b"off?");
#[doc(alias = "kAudioFileInvalidPacketOffsetError")]
pub const INVALID_PACKET_OFFSET: Error = Error::from_be_bytes(*b"pck?");
#[doc(alias = "kAudioFileInvalidPacketDependencyError")]
pub const INVALID_PACKET_DEPENDENCY: Error = Error::from_be_bytes(*b"dep?");
#[doc(alias = "kAudioFileInvalidFileError")]
pub const INVALID_FILE: Error = Error::from_be_bytes(*b"dta?");
#[doc(alias = "kAudioFileOperationNotSupportedError")]
pub const OPERATION_NOT_SUPPORTED: Error = Error::from_be_bytes(*b"op??");
#[doc(alias = "kAudioFileNotOpenError")]
pub const NOT_OPEN: Error = Error::new_unchecked(-38);
#[doc(alias = "kAudioFileEndOfFileError")]
pub const END_OF_FILE: Error = Error::new_unchecked(-39);
#[doc(alias = "kAudioFilePositionError")]
pub const POS: Error = Error::new_unchecked(-40);
#[doc(alias = "kAudioFileFileNotFoundError")]
pub const FILE_NOT_FOUND: Error = Error::new_unchecked(-43);
}
define_opts!(pub Flags(u32));
impl Flags {
#[doc(alias = "kAudioFileFlags_EraseFile")]
pub const ERASE_FILE: Self = Self(1);
#[doc(alias = "kAudioFileFlags_DontPageAlignAudioData")]
pub const DONT_PAGE_ALIGN_AUDIO_DATA: Self = Self(2);
}
#[repr(i8)]
pub enum Permissions {
#[doc(alias = "kAudioFileReadPermission")]
Read = 0x01,
#[doc(alias = "kAudioFileWritePermission")]
Write = 0x2,
#[doc(alias = "kAudioFileReadWritePermission")]
ReadWrite = 0x3,
}
unsafe extern "C" {
fn AudioFileCreateWithURL(
in_file_url: &cf::Url,
in_file_type: FileTypeId,
in_format: &audio::StreamBasicDesc,
in_flags: Flags,
out_audio_file: *mut Option<FileId>,
) -> os::Status;
fn AudioFileClose(file: *const OpaqueFileId) -> os::Status;
fn AudioFileOpenURL(
file_url: &cf::Url,
permissions: Permissions,
file_type_hint: FileTypeId,
out_audio_file: *mut Option<FileId>,
) -> os::Status;
fn AudioFileGetPropertyInfo(
file: *const OpaqueFileId,
property_id: PropId,
data_size: Option<&mut u32>,
is_writable: Option<&mut u32>,
) -> os::Status;
fn AudioFileGetProperty(
file: *const OpaqueFileId,
property_id: PropId,
data_size: *mut u32,
property_data: *mut c_void,
) -> os::Status;
fn AudioFileSetProperty(
file: *mut OpaqueFileId,
property_id: PropId,
data_size: u32,
property_data: *const c_void,
) -> os::Status;
fn AudioFileWritePackets(
file: *mut OpaqueFileId,
use_cache: bool,
num_bytes: u32,
packet_descriptions: *const audio::StreamPacketDesc,
starting_packet: isize,
num_packets: *mut u32,
buffer: *const u8,
) -> os::Status;
fn AudioFileReadPacketData(
file: *mut OpaqueFileId,
use_cache: bool,
num_bytes: *mut u32,
packet_descriptions: *mut audio::StreamPacketDesc,
starting_packet: isize,
num_packets: *mut u32,
buffer: *mut u8,
) -> os::Status;
}
#[cfg(test)]
mod tests {
use crate::{at::audio, cf};
#[test]
fn basics() {
let path = cf::Url::from_str("file:///tmp/m4a.m4a").unwrap();
let asbd = audio::StreamBasicDesc {
format: audio::Format::MPEG4_AAC,
format_flags: audio::FormatFlags::ALL_CLEAR,
frames_per_packet: 1024,
sample_rate: 48_000.0,
channels_per_frame: 2,
..Default::default()
};
let mut file = audio::FileId::create(
&path,
audio::FileTypeId::M4A,
&asbd,
audio::FileFlags::ERASE_FILE,
)
.unwrap();
let (size, writable) = file
.property_info(audio::FilePropId::DEFER_SIZE_UPDATES)
.unwrap();
assert_eq!(size, std::mem::size_of::<u32>());
assert_eq!(writable, true);
let defer_size_updates = file.defer_size_updates().unwrap();
assert!(defer_size_updates);
file.set_defer_size_updates(false).unwrap();
let defer_size_updates = file.defer_size_updates().unwrap();
assert!(!defer_size_updates);
assert_eq!(file.file_format().unwrap(), audio::FileTypeId::M4A);
let (size, writable) = file.property_info(audio::FilePropId::FILE_FORMAT).unwrap();
assert_eq!(size, std::mem::size_of::<audio::FileTypeId>());
assert_eq!(writable, false);
let (size, writable) = file.property_info(audio::FilePropId::DATA_FORMAT).unwrap();
assert_eq!(size as usize, std::mem::size_of::<audio::StreamBasicDesc>());
assert_eq!(writable, true);
let file_asbd = file.data_format().unwrap();
assert_eq!(file_asbd, asbd);
file.set_data_format(&asbd).unwrap();
let (size, _writable) = file.property_info(audio::FilePropId::IS_OPTIMIZED).unwrap();
assert_eq!(size, std::mem::size_of::<u32>());
assert_eq!(file.optimized().unwrap(), false);
assert!(
file.property_info(audio::FilePropId::DATA_FORMAT_NAME)
.is_err()
);
let (size, writable) = file
.property_info(audio::FilePropId::RESERVE_DURATION)
.unwrap();
assert_eq!(size, std::mem::size_of::<f64>());
assert_eq!(writable, true);
file.set_reserve_duration(1000f64).unwrap();
let (size, writable) = file
.property_info(audio::FilePropId::ESTIMATED_DURATION)
.unwrap();
assert_eq!(size, std::mem::size_of::<f64>());
assert_eq!(writable, false);
assert_eq!(0f64, file.estimated_duration().unwrap());
let (size, _writable) = file
.property_info(audio::FilePropId::INFO_DICTIONARY)
.unwrap();
assert_eq!(size, std::mem::size_of::<usize>());
let info = file.info_dictionary().unwrap();
assert!(!info.is_empty());
info.show();
_ = file.close();
std::mem::forget(file);
}
}