diff --git a/src/AudioToolbox/AudioConverter.cs b/src/AudioToolbox/AudioConverter.cs index 109b4e08496..02522c3f35e 100644 --- a/src/AudioToolbox/AudioConverter.cs +++ b/src/AudioToolbox/AudioConverter.cs @@ -43,6 +43,8 @@ public enum AudioConverterError // Impliclty cast to OSStatus in AudioConverter. { /// To be added. None = 0, + /// One or more of the parameters are invalid. + ParameterError = -50, /// To be added. FormatNotSupported = 0x666d743f, // 'fmt?' /// To be added. @@ -72,8 +74,6 @@ public enum AudioConverterError // Impliclty cast to OSStatus in AudioConverter. } /// Constants for the sample rate conversion algorithm. - /// - /// public enum AudioConverterSampleRateConverterComplexity // typedef UInt32 AudioConverterPropertyID { /// Represents lowest quality sample rate. @@ -85,8 +85,6 @@ public enum AudioConverterSampleRateConverterComplexity // typedef UInt32 AudioC } /// Constants for the rendering quality of the sample rate converter. - /// - /// public enum AudioConverterQuality // typedef UInt32 AudioConverterPropertyID { /// Represents maximum quality. @@ -102,8 +100,6 @@ public enum AudioConverterQuality // typedef UInt32 AudioConverterPropertyID } /// The prime method constants. - /// - /// public enum AudioConverterPrimeMethod // typedef UInt32 AudioConverterPropertyID { /// Represents primes with both leading and trailing input frames. @@ -125,8 +121,6 @@ public enum AudioConverterOptions : uint { } /// The priming information for an audio converter. - /// - /// [SupportedOSPlatform ("ios")] [SupportedOSPlatform ("maccatalyst")] [SupportedOSPlatform ("macos")] @@ -134,12 +128,8 @@ public enum AudioConverterOptions : uint { [StructLayout (LayoutKind.Sequential)] public struct AudioConverterPrimeInfo { /// The number of leading input frames. - /// - /// public int LeadingFrames; /// The number of trailing input frames. - /// - /// public int TrailingFrames; } @@ -148,8 +138,6 @@ public delegate AudioConverterError AudioConverterComplexInputData (ref int numb ref AudioStreamPacketDescription []? dataPacketDescription); /// The linear PCM audio formats converter. - /// - /// [SupportedOSPlatform ("ios")] [SupportedOSPlatform ("maccatalyst")] [SupportedOSPlatform ("macos")] @@ -167,79 +155,51 @@ internal AudioConverter (NativeHandle handle, bool owns) } /// The size in bytes of the smallest buffer of input data. - /// - /// - /// - /// public uint MinimumInputBufferSize { get { - return GetUIntProperty (AudioConverterPropertyID.MinimumInputBufferSize); + return GetProperty (AudioConverterPropertyID.MinimumInputBufferSize); } } /// The size in bytes of the smallest buffer of output data. - /// - /// - /// - /// public uint MinimumOutputBufferSize { get { - return GetUIntProperty (AudioConverterPropertyID.MinimumOutputBufferSize); + return GetProperty (AudioConverterPropertyID.MinimumOutputBufferSize); } } /// The size in bytes of the largest single packet of data in the input format. - /// - /// - /// - /// public uint MaximumInputPacketSize { get { - return GetUIntProperty (AudioConverterPropertyID.MaximumInputPacketSize); + return GetProperty (AudioConverterPropertyID.MaximumInputPacketSize); } } /// The size in bytes of the largest single packet of data in the output format. - /// - /// - /// - /// public uint MaximumOutputPacketSize { get { - return GetUIntProperty (AudioConverterPropertyID.MaximumOutputPacketSize); + return GetProperty (AudioConverterPropertyID.MaximumOutputPacketSize); } } - /// To be added. - /// - /// - /// - /// + /// On input, the desired size (in bytes) of the output data. On output, the size (in bytes) of the input required to generate the desired output data size. public uint CalculateInputBufferSize { get { - return GetUIntProperty (AudioConverterPropertyID.CalculateInputBufferSize); + return GetProperty (AudioConverterPropertyID.CalculateInputBufferSize); } } - /// To be added. - /// - /// - /// - /// + /// On input, the desired size (in bytes) of the input data. On output, the size (in bytes) of the output data that will be generated from the desired input data size. public uint CalculateOutputBufferSize { get { - return GetUIntProperty (AudioConverterPropertyID.CalculateOutputBufferSize); + return GetProperty (AudioConverterPropertyID.CalculateOutputBufferSize); } } /// The initial sub-sample position of the sample rate converter. - /// - /// - /// - /// public double SampleRateConverterInitialPhase { get { - return GetDoubleProperty (AudioConverterPropertyID.SampleRateConverterInitialPhase); + return GetProperty (AudioConverterPropertyID.SampleRateConverterInitialPhase); } set { SetProperty (AudioConverterPropertyID.SampleRateConverterInitialPhase, value); @@ -247,13 +207,9 @@ public double SampleRateConverterInitialPhase { } /// The sample rate converter algorithm. - /// - /// - /// - /// public AudioConverterSampleRateConverterComplexity SampleRateConverterComplexity { get { - return (AudioConverterSampleRateConverterComplexity) GetUIntProperty (AudioConverterPropertyID.SampleRateConverterComplexity); + return GetProperty (AudioConverterPropertyID.SampleRateConverterComplexity); } set { SetProperty (AudioConverterPropertyID.SampleRateConverterComplexity, (uint) value); @@ -261,13 +217,9 @@ public AudioConverterSampleRateConverterComplexity SampleRateConverterComplexity } /// The rendering quality of the sample rate converter. - /// - /// - /// - /// public AudioConverterQuality SampleRateConverterQuality { get { - return (AudioConverterQuality) GetUIntProperty (AudioConverterPropertyID.SampleRateConverterQuality); + return GetProperty (AudioConverterPropertyID.SampleRateConverterQuality); } set { SetProperty (AudioConverterPropertyID.SampleRateConverterQuality, (uint) value); @@ -275,13 +227,9 @@ public AudioConverterQuality SampleRateConverterQuality { } /// Rendering quality of the converter codec. - /// - /// - /// - /// public AudioConverterQuality CodecQuality { get { - return (AudioConverterQuality) GetUIntProperty (AudioConverterPropertyID.CodecQuality); + return GetProperty (AudioConverterPropertyID.CodecQuality); } set { SetProperty (AudioConverterPropertyID.CodecQuality, (uint) value); @@ -289,13 +237,9 @@ public AudioConverterQuality CodecQuality { } /// The priming information for converter's priming method. - /// - /// - /// - /// public AudioConverterPrimeMethod PrimeMethod { get { - return (AudioConverterPrimeMethod) GetUIntProperty (AudioConverterPropertyID.PrimeMethod); + return GetProperty (AudioConverterPropertyID.PrimeMethod); } set { SetProperty (AudioConverterPropertyID.PrimeMethod, (uint) value); @@ -303,97 +247,45 @@ public AudioConverterPrimeMethod PrimeMethod { } /// The priming method. - /// - /// - /// - /// public unsafe AudioConverterPrimeInfo PrimeInfo { get { - AudioConverterPrimeInfo value; - var size = sizeof (AudioConverterPrimeInfo); - var res = AudioConverterGetProperty (Handle, AudioConverterPropertyID.PrimeInfo, ref size, out value); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); - - return value; + return GetProperty (AudioConverterPropertyID.PrimeInfo); } } /// Input to Output channel mapping. - /// - /// - /// - /// public int []? ChannelMap { get { - return GetArray (AudioConverterPropertyID.ChannelMap, sizeof (int)); + return GetArray (AudioConverterPropertyID.ChannelMap); } } /// Gets or sets a magic cookie that is used for compression. - /// - /// - /// - /// public byte []? CompressionMagicCookie { get { - int size; - bool writable; - if (AudioConverterGetPropertyInfo (Handle, AudioConverterPropertyID.CompressionMagicCookie, out size, out writable) != AudioConverterError.None) - return null; - - var cookie = new byte [size]; - if (AudioConverterGetProperty (Handle, AudioConverterPropertyID.CompressionMagicCookie, ref size, cookie) != AudioConverterError.None) - return null; - - return cookie; + return GetArray (AudioConverterPropertyID.CompressionMagicCookie); } set { - if (value is null) - ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (value)); - - var res = AudioConverterSetProperty (Handle, AudioConverterPropertyID.CompressionMagicCookie, value.Length, value); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); + SetArray (AudioConverterPropertyID.CompressionMagicCookie, value); } } /// Gets or sets a magic cookie that is used for decompression. - /// - /// - /// If the audio data format has a magic cookie associated with it, you must add this information to appropriately decompress the data. + /// If the audio data format has a magic cookie associated with it, you must add this information to appropriately decompress the data. public byte []? DecompressionMagicCookie { get { - int size; - bool writable; - if (AudioConverterGetPropertyInfo (Handle, AudioConverterPropertyID.DecompressionMagicCookie, out size, out writable) != AudioConverterError.None) - return null; - - var cookie = new byte [size]; - if (AudioConverterGetProperty (Handle, AudioConverterPropertyID.DecompressionMagicCookie, ref size, cookie) != AudioConverterError.None) - return null; - - return cookie; + return GetArray (AudioConverterPropertyID.DecompressionMagicCookie); } set { - if (value is null) - ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (value)); - - var res = AudioConverterSetProperty (Handle, AudioConverterPropertyID.DecompressionMagicCookie, value.Length, value); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); + SetArray (AudioConverterPropertyID.DecompressionMagicCookie, value); } } /// The number of bits per second to aim for when encoding data. - /// - /// - /// - /// public uint EncodeBitRate { get { - return GetUIntProperty (AudioConverterPropertyID.EncodeBitRate); + return GetProperty (AudioConverterPropertyID.EncodeBitRate); } set { SetProperty (AudioConverterPropertyID.EncodeBitRate, value); @@ -401,13 +293,9 @@ public uint EncodeBitRate { } /// An an output sample rate. - /// - /// - /// - /// public double EncodeAdjustableSampleRate { get { - return GetDoubleProperty (AudioConverterPropertyID.EncodeAdjustableSampleRate); + return GetProperty (AudioConverterPropertyID.EncodeAdjustableSampleRate); } set { SetProperty (AudioConverterPropertyID.EncodeAdjustableSampleRate, value); @@ -415,19 +303,9 @@ public double EncodeAdjustableSampleRate { } /// Input audio channels layout. - /// - /// - /// - /// public AudioChannelLayout? InputChannelLayout { get { - int size; - bool writable; - if (AudioConverterGetPropertyInfo (Handle, AudioConverterPropertyID.InputChannelLayout, out size, out writable) != AudioConverterError.None) - return null; - - IntPtr ptr = Marshal.AllocHGlobal (size); - var res = AudioConverterGetProperty (Handle, AudioConverterPropertyID.InputChannelLayout, ref size, ptr); + var res = GetPropertyIntoNativeMemory (AudioConverterPropertyID.InputChannelLayout, out var ptr); var layout = res == AudioConverterError.None ? new AudioChannelLayout (ptr) : null; Marshal.FreeHGlobal (ptr); return layout; @@ -435,19 +313,9 @@ public AudioChannelLayout? InputChannelLayout { } /// Output audio channels layout. - /// - /// - /// - /// public AudioChannelLayout? OutputChannelLayout { get { - int size; - bool writable; - if (AudioConverterGetPropertyInfo (Handle, AudioConverterPropertyID.OutputChannelLayout, out size, out writable) != AudioConverterError.None) - return null; - - IntPtr ptr = Marshal.AllocHGlobal (size); - var res = AudioConverterGetProperty (Handle, AudioConverterPropertyID.OutputChannelLayout, ref size, ptr); + var res = GetPropertyIntoNativeMemory (AudioConverterPropertyID.OutputChannelLayout, out var ptr); var layout = res == AudioConverterError.None ? new AudioChannelLayout (ptr) : null; Marshal.FreeHGlobal (ptr); return layout; @@ -455,114 +323,60 @@ public AudioChannelLayout? OutputChannelLayout { } /// All applicable bit rates based on current settings. - /// - /// - /// - /// public AudioValueRange []? ApplicableEncodeBitRates { get { - return GetAudioValueRange (AudioConverterPropertyID.ApplicableEncodeBitRates); + return GetArray (AudioConverterPropertyID.ApplicableEncodeBitRates); } } /// All available bit rates for the input format. - /// - /// - /// - /// public AudioValueRange []? AvailableEncodeBitRates { get { - return GetAudioValueRange (AudioConverterPropertyID.AvailableEncodeBitRates); + return GetArray (AudioConverterPropertyID.AvailableEncodeBitRates); } } /// All applicable sample rates based on current settings. - /// - /// - /// - /// public AudioValueRange []? ApplicableEncodeSampleRates { get { - return GetAudioValueRange (AudioConverterPropertyID.ApplicableEncodeSampleRates); + return GetArray (AudioConverterPropertyID.ApplicableEncodeSampleRates); } } /// All applicable sample rates based on current settings. - /// - /// - /// - /// public AudioValueRange []? AvailableEncodeSampleRates { get { - return GetAudioValueRange (AudioConverterPropertyID.AvailableEncodeSampleRates); + return GetArray (AudioConverterPropertyID.AvailableEncodeSampleRates); } } /// All audio channel layouts for the input format. - /// - /// - /// - /// public AudioChannelLayoutTag []? AvailableEncodeChannelLayoutTags { get { - return GetArray (AudioConverterPropertyID.AvailableEncodeChannelLayoutTags, sizeof (AudioChannelLayoutTag)); + return GetArray (AudioConverterPropertyID.AvailableEncodeChannelLayoutTags); } } /// Completely filled output audio description. - /// - /// - /// The property can be used to obtain converter filled for output audio stream. + /// The property can be used to obtain converter filled for output audio stream. public unsafe AudioStreamBasicDescription CurrentOutputStreamDescription { get { - int size; - bool writable; - var res = AudioConverterGetPropertyInfo (Handle, AudioConverterPropertyID.CurrentOutputStreamDescription, out size, out writable); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); - - IntPtr ptr = Marshal.AllocHGlobal (size); - res = AudioConverterGetProperty (Handle, AudioConverterPropertyID.CurrentOutputStreamDescription, ref size, ptr); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); - - var asbd = *(AudioStreamBasicDescription*) ptr; - Marshal.FreeHGlobal (ptr); - return asbd; + return GetProperty (AudioConverterPropertyID.CurrentOutputStreamDescription); } } /// Completely filled input audio description. - /// - /// - /// The property can be used to obtain converter filled for input audio stream. + /// The property can be used to obtain converter filled for input audio stream. public unsafe AudioStreamBasicDescription CurrentInputStreamDescription { get { - int size; - bool writable; - var res = AudioConverterGetPropertyInfo (Handle, AudioConverterPropertyID.CurrentInputStreamDescription, out size, out writable); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); - - IntPtr ptr = Marshal.AllocHGlobal (size); - res = AudioConverterGetProperty (Handle, AudioConverterPropertyID.CurrentInputStreamDescription, ref size, ptr); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); - - var asbd = *(AudioStreamBasicDescription*) ptr; - Marshal.FreeHGlobal (ptr); - return asbd; + return GetProperty (AudioConverterPropertyID.CurrentInputStreamDescription); } } /// The source bit depth to preserve. - /// - /// - /// - /// public int BitDepthHint { get { - return (int) GetUIntProperty (AudioConverterPropertyID.PropertyBitDepthHint); + return GetProperty (AudioConverterPropertyID.PropertyBitDepthHint); } set { SetProperty (AudioConverterPropertyID.PropertyBitDepthHint, value); @@ -570,51 +384,65 @@ public int BitDepthHint { } /// All the data formats produced by the converter encoder. - /// - /// - /// - /// public unsafe AudioFormat []? FormatList { get { - return GetArray (AudioConverterPropertyID.PropertyFormatList, sizeof (AudioFormat)); + return GetArray (AudioConverterPropertyID.PropertyFormatList); } } #if !MONOMAC /// The underlying codec supports resumption following an interruption. - /// - /// - /// - /// public bool CanResumeFromInterruption { get { - return GetUIntProperty (AudioConverterPropertyID.CanResumeFromInterruption) != 0; + return GetProperty (AudioConverterPropertyID.CanResumeFromInterruption) != 0; } } #endif + /// Whether to perform a mix from input to output channels. + /// Use to specify how the mix is done. + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + [SupportedOSPlatform ("tvos26.0")] + public bool PerformDownmix { + get { + return GetProperty (AudioConverterPropertyID.PerformDownmix) != 0; + } + set { + SetProperty (AudioConverterPropertyID.PerformDownmix, value ? 1 : 0); + } + } + + /// An array of gain values to apply to input and output channels. Each gain value is a value between 0.0 and 1.0. + /// must be set to first. + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + [SupportedOSPlatform ("tvos26.0")] + public float []? ChannelMixMap { + get { + return GetArray (AudioConverterPropertyID.ChannelMixMap); + } + set { + SetArray (AudioConverterPropertyID.ChannelMixMap, value); + } + } + /// Creates a new audio converter instance based on specified audio formats. /// Input audio format. - /// Output audio format. - /// Creates a new audio converter instance based on specified audio formats. - /// - /// - /// - /// + /// Output audio format. + /// A new instance if successful, otherwise. public static AudioConverter? Create (AudioStreamBasicDescription sourceFormat, AudioStreamBasicDescription destinationFormat) { AudioConverterError res; return Create (sourceFormat, destinationFormat, out res); } + /// Creates a new audio converter instance using a specified codec. /// The format of the source audio. - /// The destination audio format. - /// - /// - /// Creates a new audio converter instance using a specified codec. - /// - /// - /// - /// + /// The destination audio format. + /// In case of failure, will contain the error code for the failure. Otherwise the value will be returned. + /// A new instance if successful, otherwise. public static AudioConverter? Create (AudioStreamBasicDescription sourceFormat, AudioStreamBasicDescription destinationFormat, out AudioConverterError error) { IntPtr ptr = new IntPtr (); @@ -627,14 +455,11 @@ public bool CanResumeFromInterruption { return new AudioConverter (ptr, true); } + /// Creates a new audio converter instance using a specified codec. /// Input audio format. - /// Output audio format. - /// A list of codec to be used. - /// Creates a new audio converter instance using a specified codec. - /// - /// - /// - /// + /// Output audio format. + /// A list of codec to be used. + /// A new instance if successful, otherwise. public static AudioConverter? Create (AudioStreamBasicDescription sourceFormat, AudioStreamBasicDescription destinationFormat, AudioClassDescription [] descriptions) { if (descriptions is null) @@ -688,10 +513,6 @@ public bool CanResumeFromInterruption { } /// All valid converter input formats. - /// - /// - /// - /// public static AudioFormatType []? DecodeFormats { get { return GetFormats (AudioFormatProperty.DecodeFormatIDs); @@ -699,10 +520,6 @@ public static AudioFormatType []? DecodeFormats { } /// All valid converter output formats. - /// - /// - /// - /// public static AudioFormatType []? EncodeFormats { get { return GetFormats (AudioFormatProperty.EncodeFormatIDs); @@ -723,13 +540,10 @@ protected override void Dispose (bool disposing) base.Dispose (disposing); } + /// Converts audio data from one linear PCM format to another. /// The input audio data. - /// The output audio data. - /// Converts audio data from one linear PCM format to another. - /// - /// - /// - /// + /// The output audio data. + /// In case of failure, will the error code for the failure. Otherwise the value will be returned. public AudioConverterError ConvertBuffer (byte [] input, byte [] output) { if (input is null) @@ -747,14 +561,11 @@ public AudioConverterError ConvertBuffer (byte [] input, byte [] output) } } + /// Converts audio data from one linear PCM format to another where both use the same sample rate. /// The number of linear PCM frames to convert. - /// The input audio data. - /// The output audio data. - /// Converts audio data from one linear PCM format to another where both use the same sample rate. - /// - /// - /// - /// + /// The input audio data. + /// The output audio data. + /// In case of failure, will the error code for the failure. Otherwise the value will be returned. public AudioConverterError ConvertComplexBuffer (int numberPCMFrames, AudioBuffers inputData, AudioBuffers outputData) { if (inputData is null) @@ -765,13 +576,12 @@ public AudioConverterError ConvertComplexBuffer (int numberPCMFrames, AudioBuffe return AudioConverterConvertComplexBuffer (Handle, numberPCMFrames, (IntPtr) inputData, (IntPtr) outputData); } - /// To be added. - /// To be added. - /// To be added. - /// To be added. - /// To be added. - /// To be added. - /// To be added. + /// Converts audio data supporting non-interleaved and packetized formats. + /// The capacity of converted output data expressed in packets + /// The converted output data. + /// An array of packet descriptions. + /// A callback that will be called to supply audio data for the conversion. + /// In case of failure, will the error code for the failure. Otherwise the value will be returned. public AudioConverterError FillComplexBuffer (ref int outputDataPacketSize, AudioBuffers outputData, AudioStreamPacketDescription [] packetDescription, AudioConverterComplexInputData newInputDataHandler) { @@ -784,19 +594,14 @@ public AudioConverterError FillComplexBuffer (ref int outputDataPacketSize, return FillComplexBuffer (ref outputDataPacketSize, outputData, packetDescription, new Tuple (this, newInputDataHandler)); } + /// Converts audio data supporting non-interleaved and packetized formats. /// The capacity of converted output data expressed in packets - /// The converted output data. - /// An array of packet descriptions. - /// Converts audio data supporting non-interleaved and packetized formats. - /// - /// - /// - /// The - /// - /// event is invoked to supply the input data for the - /// conversion. - /// - /// + /// The converted output data. + /// An array of packet descriptions. + /// In case of failure, will the error code for the failure. Otherwise the value will be returned. + /// + /// The event is invoked to supply the input data for the conversion. + /// public AudioConverterError FillComplexBuffer (ref int outputDataPacketSize, AudioBuffers outputData, AudioStreamPacketDescription [] packetDescription) { @@ -837,7 +642,7 @@ AudioConverterError FillComplexBuffer (ref int outputDataPacketSize, // outDataPacketDescription should be `ref IntPtr' but using IntPtr we get easier access to pointer address // [UnmanagedCallersOnly] - static AudioConverterError FillComplexBufferShared (IntPtr inAudioConverter, IntPtr ioNumberDataPacketsPtr, IntPtr ioData, + unsafe static AudioConverterError FillComplexBufferShared (IntPtr inAudioConverter, uint* ioNumberDataPackets, IntPtr ioData, IntPtr outDataPacketDescription, IntPtr inUserData) { var handler = GCHandle.FromIntPtr (inUserData); @@ -863,17 +668,12 @@ static AudioConverterError FillComplexBufferShared (IntPtr inAudioConverter, Int // Using 0-size array as marker because the size of pre-allocated memory is not known // var data = outDataPacketDescription == IntPtr.Zero ? null : new AudioStreamPacketDescription [0]; - // tricky - this in !NET this is an argument - // in NET it's a local so all the other code - // flows - var ioNumberDataPackets = Marshal.ReadInt32 (ioNumberDataPacketsPtr); var res = inst.InputData is not null ? - inst.InputData (ref ioNumberDataPackets, buffers, ref data) : - callback! (ref ioNumberDataPackets, buffers, ref data); - Marshal.WriteInt32 (ioNumberDataPacketsPtr, ioNumberDataPackets); + inst.InputData (ref Unsafe.AsRef (ioNumberDataPackets), buffers, ref data) : + callback! (ref Unsafe.AsRef (ioNumberDataPackets), buffers, ref data); if (outDataPacketDescription != IntPtr.Zero) { - if (ioNumberDataPackets > 0) { + if (*ioNumberDataPackets > 0) { if (data is null || data.Length == 0) throw new ArgumentException ("ref argument outDataPacketDescription has to be set"); @@ -952,10 +752,6 @@ public unsafe static void Prepare (PrepareCompletionCallback completionCallback) } /// Resets an audio converter. - /// - /// - /// - /// public AudioConverterError Reset () { return AudioConverterReset (Handle); @@ -981,47 +777,20 @@ public AudioConverterError Reset () } } - uint GetUIntProperty (AudioConverterPropertyID propertyID) - { - uint value; - var size = sizeof (uint); - var res = AudioConverterGetProperty (Handle, propertyID, ref size, out value); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); - - return value; - } - - double GetDoubleProperty (AudioConverterPropertyID propertyID) - { - double value; - var size = sizeof (double); - var res = AudioConverterGetProperty (Handle, propertyID, ref size, out value); - if (res != AudioConverterError.None) - throw new ArgumentException (res.ToString ()); - - return value; - } - - unsafe AudioValueRange []? GetAudioValueRange (AudioConverterPropertyID prop) - { - return GetArray (prop, sizeof (AudioValueRange)); - } - - unsafe T []? GetArray (AudioConverterPropertyID prop, int elementSize) where T : unmanaged + unsafe T []? GetArray (AudioConverterPropertyID prop) where T : unmanaged { int size; - bool writable; - if (AudioConverterGetPropertyInfo (Handle, prop, out size, out writable) != AudioConverterError.None) + if (AudioConverterGetPropertyInfo (Handle, prop, &size, null) != AudioConverterError.None) return null; if (size == 0) return Array.Empty (); + var elementSize = sizeof (T); var data = new T [size / elementSize]; fixed (T* ptr = data) { - var res = AudioConverterGetProperty (Handle, prop, ref size, (IntPtr) ptr); + var res = AudioConverterGetProperty (Handle, prop, &size, ptr); if (res != 0) return null; @@ -1030,27 +799,54 @@ double GetDoubleProperty (AudioConverterPropertyID propertyID) } } - void SetProperty (AudioConverterPropertyID propertyID, uint value) + unsafe void SetArray (AudioConverterPropertyID propertyId, T []? value) where T : unmanaged { - var res = AudioConverterSetProperty (Handle, propertyID, sizeof (uint), ref value); + // 'inPropertyData' is nullable because the properties are declared as nullable, which is because the getters can return null. + if (value is null) + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (value)); + + AudioConverterError res; + fixed (T* valuePtr = value) + res = AudioConverterSetProperty (GetCheckedHandle (), propertyId, sizeof (T) * (value?.Length ?? 0), valuePtr); if (res != AudioConverterError.None) throw new ArgumentException (res.ToString ()); } - void SetProperty (AudioConverterPropertyID propertyID, int value) + unsafe T GetProperty (AudioConverterPropertyID propertyID) where T : unmanaged { - var res = AudioConverterSetProperty (Handle, propertyID, sizeof (int), ref value); + T value; + var size = sizeof (T); + var res = AudioConverterGetProperty (Handle, propertyID, &size, &value); if (res != AudioConverterError.None) throw new ArgumentException (res.ToString ()); + + return value; } - void SetProperty (AudioConverterPropertyID propertyID, double value) + unsafe void SetProperty (AudioConverterPropertyID propertyID, T value) where T : unmanaged { - var res = AudioConverterSetProperty (Handle, propertyID, sizeof (double), ref value); + var res = AudioConverterSetProperty (Handle, propertyID, sizeof (T), &value); if (res != AudioConverterError.None) throw new ArgumentException (res.ToString ()); } + /// Get a converter property and copy it into a newly allocated block of native memory. + /// The property to fetch. + /// The native memory with the property value. Must be freed with Marshal.FreeHGlobal. + unsafe AudioConverterError GetPropertyIntoNativeMemory (AudioConverterPropertyID propertyId, out IntPtr memory) + { + int size; + + memory = IntPtr.Zero; + + var res = AudioConverterGetPropertyInfo (Handle, AudioConverterPropertyID.InputChannelLayout, &size, null); + if (res != AudioConverterError.None) + return res; + + memory = Marshal.AllocHGlobal (size); + return AudioConverterGetProperty (Handle, AudioConverterPropertyID.InputChannelLayout, &size, (void*) memory); + } + [DllImport (Constants.AudioToolboxLibrary)] unsafe static extern AudioConverterError AudioConverterNew (AudioStreamBasicDescription* inSourceFormat, AudioStreamBasicDescription* inDestinationFormat, IntPtr* outAudioConverter); @@ -1087,127 +883,131 @@ static extern AudioConverterError AudioConverterConvertComplexBuffer (IntPtr inA IntPtr inInputData, IntPtr outOutputData); [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int* ioPropertyDataSize, uint* outPropertyData); - - unsafe static AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, ref int ioPropertyDataSize, out uint outPropertyData) - { - outPropertyData = default (uint); - return AudioConverterGetProperty (inAudioConverter, inPropertyID, (int*) Unsafe.AsPointer (ref ioPropertyDataSize), (uint*) Unsafe.AsPointer (ref outPropertyData)); - } - - [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int* ioPropertyDataSize, int* outPropertyData); - - unsafe static AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, ref int ioPropertyDataSize, out int outPropertyData) - { - outPropertyData = default (int); - return AudioConverterGetProperty (inAudioConverter, inPropertyID, (int*) Unsafe.AsPointer (ref ioPropertyDataSize), (int*) Unsafe.AsPointer (ref outPropertyData)); - } + unsafe static extern AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, int* ioPropertyDataSize, void* outPropertyData); [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int* ioPropertyDataSize, double* outPropertyData); - - unsafe static AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, ref int ioPropertyDataSize, out double outPropertyData) - { - outPropertyData = default (double); - return AudioConverterGetProperty (inAudioConverter, inPropertyID, (int*) Unsafe.AsPointer (ref ioPropertyDataSize), (double*) Unsafe.AsPointer (ref outPropertyData)); - } + unsafe static extern AudioConverterError AudioConverterGetPropertyInfo (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, int* outSize, byte* outWritable); [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int* ioPropertyDataSize, byte* outPropertyData); - - unsafe static AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, ref int ioPropertyDataSize, byte [] outPropertyData) - { - fixed (byte* outPropertyDataPtr = outPropertyData) - return AudioConverterGetProperty (inAudioConverter, inPropertyID, (int*) Unsafe.AsPointer (ref ioPropertyDataSize), outPropertyDataPtr); - } + unsafe static extern AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, int inPropertyDataSize, void* inPropertyData); [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int* ioPropertyDataSize, AudioConverterPrimeInfo* outPropertyData); - - unsafe static AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, ref int ioPropertyDataSize, out AudioConverterPrimeInfo outPropertyData) - { - outPropertyData = default (AudioConverterPrimeInfo); - return AudioConverterGetProperty (inAudioConverter, inPropertyID, (int*) Unsafe.AsPointer (ref ioPropertyDataSize), (AudioConverterPrimeInfo*) Unsafe.AsPointer (ref outPropertyData)); - } + unsafe static extern AudioConverterError AudioConverterConvertBuffer (IntPtr inAudioConverter, int inInputDataSize, byte* inInputData, + int* ioOutputDataSize, byte* outOutputData); [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int* ioPropertyDataSize, IntPtr outPropertyData); + static unsafe extern AudioConverterError AudioConverterFillComplexBuffer (IntPtr inAudioConverter, + delegate* unmanaged inInputDataProc, IntPtr inInputDataProcUserData, + IntPtr ioOutputDataPacketSize, IntPtr outOutputData, + IntPtr outPacketDescription); - unsafe static AudioConverterError AudioConverterGetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, ref int ioPropertyDataSize, IntPtr outPropertyData) + /// Converts audio data, supplied by an event handler, supporting packet dependencies. + /// On input, the size if the output buffer (). On output, the number of converted audio packets. + /// The buffer where the converted audio data is to be written. + /// An array of packet descriptions that will contain the decoded packet descriptions upon return. + /// An array of packet dependencies that will contain the decoded packet dependencies (if any) upon return. + /// An error code in case of failure, otherwise. + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("tvos26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + public AudioConverterError FillComplexBuffer ( + ref int outputDataPacketSize, + AudioBuffers outputData, + AudioStreamPacketDescription [] packetDescription, + AudioStreamPacketDependencyDescription [] packetDependencies) { - return AudioConverterGetProperty (inAudioConverter, inPropertyID, (int*) Unsafe.AsPointer (ref ioPropertyDataSize), outPropertyData); - } - - [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterGetPropertyInfo (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, int* outSize, byte* outWritable); - - static AudioConverterError AudioConverterGetPropertyInfo (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, out int outSize, out bool outWritable) + if (InputData is null) + throw new InvalidOperationException ($"No event handler has been added to the '{nameof (InputData)}' event."); + + return FillComplexBufferWithPacketDependencies (ref outputDataPacketSize, outputData, packetDescription, null, packetDependencies); + } + + /// Converts audio data, supplied by a callback function, supporting packet dependencies. + /// On input, the size if the output buffer (). On output, the number of converted audio packets. + /// The buffer where the converted audio data is to be written. + /// An array of packet descriptions that will contain the decoded packet descriptions upon return. + /// The callback that will be called to supply audio data for the conversion. + /// An array of packet dependencies that will contain the decoded packet dependencies (if any) upon return. + /// An error code in case of failure, otherwise. + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("tvos26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + public AudioConverterError FillComplexBuffer ( + ref int outputDataPacketSize, + AudioBuffers outputData, + AudioStreamPacketDescription []? packetDescription, + AudioConverterComplexInputData dataHandler, + AudioStreamPacketDependencyDescription [] packetDependencies) { - byte writable = 0; - outSize = 0; - AudioConverterError rv; - unsafe { - rv = AudioConverterGetPropertyInfo (inAudioConverter, inPropertyID, (int*) Unsafe.AsPointer (ref outSize), &writable); - } - outWritable = writable != 0; - return rv; - } - - [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int inPropertyDataSize, uint* inPropertyData); - - unsafe static AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int inPropertyDataSize, ref uint inPropertyData) - { - return AudioConverterSetProperty (inAudioConverter, inPropertyID, inPropertyDataSize, (uint*) Unsafe.AsPointer (ref inPropertyData)); - } - - [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int inPropertyDataSize, int* inPropertyData); - - unsafe static AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int inPropertyDataSize, ref int inPropertyData) + if (dataHandler is null) + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (dataHandler)); + + return FillComplexBufferWithPacketDependencies (ref outputDataPacketSize, outputData, packetDescription, dataHandler, packetDependencies); + } + + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("tvos26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + AudioConverterError FillComplexBufferWithPacketDependencies ( + ref int outputDataPacketSize, + AudioBuffers outputData, + AudioStreamPacketDescription []? packetDescriptions, + AudioConverterComplexInputData? dataHandler, + AudioStreamPacketDependencyDescription [] packetDependencies + ) { - return AudioConverterSetProperty (inAudioConverter, inPropertyID, inPropertyDataSize, (int*) Unsafe.AsPointer (ref inPropertyData)); - } - - [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int inPropertyDataSize, double* inPropertyData); + if (outputData is null) + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (outputData)); - unsafe static AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, int inPropertyDataSize, ref double inPropertyData) - { - return AudioConverterSetProperty (inAudioConverter, inPropertyID, inPropertyDataSize, (double*) Unsafe.AsPointer (ref inPropertyData)); - } + if (packetDependencies is null) + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (packetDependencies)); + if (packetDependencies.Length < outputDataPacketSize) + ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (packetDependencies), $"Length must be equal to or greater than '{nameof (outputDataPacketSize)}'."); - [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, - int inPropertyDataSize, byte* inPropertyData); + // A null value for 'packetDescriptions' is allowed, but not a too small array. + if (packetDescriptions is not null && packetDescriptions.Length < outputDataPacketSize) + ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (packetDescriptions), $"Length must be equal to or greater than '{nameof (outputDataPacketSize)}'."); - unsafe static AudioConverterError AudioConverterSetProperty (IntPtr inAudioConverter, AudioConverterPropertyID inPropertyID, int inPropertyDataSize, byte [] inPropertyData) - { - fixed (byte* inPropertyDataPtr = inPropertyData) - return AudioConverterSetProperty (inAudioConverter, inPropertyID, inPropertyDataSize, inPropertyDataPtr); + var instanceData = new Tuple (this, dataHandler); + var this_handle = GCHandle.Alloc (instanceData); + try { + unsafe { + fixed (AudioStreamPacketDependencyDescription* packetDependenciesPtr = packetDependencies) { + fixed (AudioStreamPacketDescription* packetDescriptionPtr = packetDescriptions) { + return AudioConverterFillComplexBufferWithPacketDependencies ( + GetCheckedHandle (), + &FillComplexBufferShared, + (IntPtr) this_handle, + (uint*) Unsafe.AsPointer (ref outputDataPacketSize), + (IntPtr) outputData, + packetDescriptionPtr, + packetDependenciesPtr); + } + } + } + } finally { + this_handle.Free (); + GC.KeepAlive (outputData); + } } + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("tvos26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] [DllImport (Constants.AudioToolboxLibrary)] - unsafe static extern AudioConverterError AudioConverterConvertBuffer (IntPtr inAudioConverter, int inInputDataSize, byte* inInputData, - int* ioOutputDataSize, byte* outOutputData); + static unsafe extern AudioConverterError /* OSStatus */ AudioConverterFillComplexBufferWithPacketDependencies ( + IntPtr /* AudioConverterRef */ inAudioConverter, + delegate* unmanaged inInputDataProc, + IntPtr /* void * __nullable */ inInputDataProcUserData, + uint* /* UInt32 * */ ioOutputDataPacketSize, + IntPtr /* AudioBufferList * */ outOutputData, + AudioStreamPacketDescription* /* AudioStreamPacketDescription * __nullable */ outPacketDescription, + AudioStreamPacketDependencyDescription* /* AudioStreamPacketDependencyDescription * */ outPacketDependencies); - [DllImport (Constants.AudioToolboxLibrary)] - static unsafe extern AudioConverterError AudioConverterFillComplexBuffer (IntPtr inAudioConverter, - delegate* unmanaged inInputDataProc, IntPtr inInputDataProcUserData, - IntPtr ioOutputDataPacketSize, IntPtr outOutputData, - IntPtr outPacketDescription); } enum AudioConverterPropertyID // typedef UInt32 AudioConverterPropertyID @@ -1251,5 +1051,17 @@ enum AudioConverterPropertyID // typedef UInt32 AudioConverterPropertyID PropertyBitDepthHint = 0x61636264, // 'acbd' PropertyFormatList = 0x666c7374, // 'flst' CanResumeFromInterruption = 0x63726669, // 'crfi' + + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + [SupportedOSPlatform ("tvos26.0")] + PerformDownmix = 0x646d6978, // 'dmix' + + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + [SupportedOSPlatform ("tvos26.0")] + ChannelMixMap = 0x6d6d6170, // 'mmap' } } diff --git a/src/AudioToolbox/AudioFile.cs b/src/AudioToolbox/AudioFile.cs index 60cc6e94338..bf2db91846d 100644 --- a/src/AudioToolbox/AudioFile.cs +++ b/src/AudioToolbox/AudioFile.cs @@ -1636,6 +1636,89 @@ public AudioFileError WritePackets (bool useCache, int numBytes, AudioStreamPack } } + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("tvos26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + [DllImport (Constants.AudioToolboxLibrary)] + unsafe extern static /* OSStatus */ AudioFileError AudioFileWritePacketsWithDependencies ( + AudioFileID inAudioFile, + byte /* Boolean */ inUseCache, + uint /* UInt32 */ inNumBytes, + AudioStreamPacketDescription* /* const AudioStreamPacketDescription * __nullable */ inPacketDescriptions, + AudioStreamPacketDependencyDescription* /* const AudioStreamPacketDependencyDescription * */ inPacketDependencies, + long /* SInt64 */ inStartingPacket, + uint* /* UInt32 * */ ioNumPackets, + IntPtr /* const void * */inBuffer); + + /// Writes audio packets to the file. + /// Whether the data should be kept in the cache. + /// An array of packet descriptions that describe the content of the buffer. + /// An array of packet dependencies for the audio data. + /// The index the first packet in the buffer to write. + /// The number of packets to write replaced with the number of packets actually written. + /// The buffer containing the audio data to write. + /// The number of bytes to write. + /// if successful, otherwise a status error code. + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("tvos26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + public AudioFileError WritePackets (bool useCache, AudioStreamPacketDescription []? packetDescriptions, AudioStreamPacketDependencyDescription [] packetDependencies, long startingPacket, ref int numPackets, IntPtr buffer, int numBytes) + { + if (buffer == IntPtr.Zero) + throw new ArgumentException (nameof (buffer)); + + if (packetDependencies is null) + ThrowHelper.ThrowArgumentNullException (nameof (packetDependencies)); + + unsafe { + fixed (AudioStreamPacketDescription* packetDescriptionsPtr = packetDescriptions) { + fixed (AudioStreamPacketDependencyDescription* packetDependenciesPtr = packetDependencies) { + return AudioFileWritePacketsWithDependencies ( + GetCheckedHandle (), + useCache.AsByte (), + (uint) numBytes, + packetDescriptionsPtr, + packetDependenciesPtr, + startingPacket, + (uint*) Unsafe.AsPointer (ref numPackets), + buffer); + } + } + } + } + + /// Writes audio packets to the file. + /// Whether the data should be kept in the cache. + /// The number of bytes to write. + /// An array of packet descriptions that describe the content of the buffer. + /// An array of packet dependencies for the audio data. + /// The index the first packet in the buffer to write. + /// The number of packets to write replaced with the number of packets actually written. + /// The buffer containing the audio data to write. + /// An offset into where the audio data to write starts. + /// if successful, otherwise a status error code. + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("tvos26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + public AudioFileError WritePackets (bool useCache, AudioStreamPacketDescription []? packetDescriptions, AudioStreamPacketDependencyDescription [] packetDependencies, long startingPacket, ref int numPackets, byte [] buffer, int offset, int numBytes) + { + if (buffer is null) + ThrowHelper.ThrowArgumentNullException (nameof (buffer)); + if (offset < 0) + throw new ArgumentOutOfRangeException (nameof (offset), "< 0"); + if (numBytes < 0) + throw new ArgumentOutOfRangeException (nameof (numBytes), "< 0"); + if (offset > buffer.Length - numBytes) + throw new ArgumentException ("Reading would overrun buffer"); + unsafe { + fixed (byte* bufferPtr = &buffer [offset]) + return WritePackets (useCache, packetDescriptions, packetDependencies, startingPacket, ref numPackets, (IntPtr) bufferPtr, numBytes); + } + } + [DllImport (Constants.AudioToolboxLibrary)] unsafe extern static OSStatus AudioFileCountUserData (AudioFileID handle, uint userData, int* count); diff --git a/src/AudioToolbox/AudioFormat.cs b/src/AudioToolbox/AudioFormat.cs index 87f40882f27..6483b44543f 100644 --- a/src/AudioToolbox/AudioFormat.cs +++ b/src/AudioToolbox/AudioFormat.cs @@ -108,9 +108,8 @@ public enum AudioFormatError : int // Implictly cast to OSType UnsupportedDataFormat = 0x666d743f, // 'fmt?' /// To be added. UnknownFormat = 0x21666d74, // '!fmt' - - // TODO: Not documented - // '!dat' + /// The format is unsupported. + UnsupportedFormat = 0x21646174, // '!dat' } /// A struct that holds minimum and maximum float values, indicating a range. @@ -441,6 +440,7 @@ enum AudioFormatProperty : uint // UInt32 AudioFormatPropertyID FirstPlayableFormatFromList = 0x6670666c, // 'fpfl' FormatIsVBR = 0x66766272, // 'fvbr' FormatIsExternallyFramed = 0x66657866, // 'fexf' + FormatEmploysDependentPackets = 0x66646570, // 'fdep' FormatIsEncrypted = 0x63727970, // 'cryp' Encoders = 0x6176656e, // 'aven' Decoders = 0x61766465, // 'avde' diff --git a/src/AudioToolbox/AudioType.cs b/src/AudioToolbox/AudioType.cs index fb00a16cf30..5013590a4c0 100644 --- a/src/AudioToolbox/AudioType.cs +++ b/src/AudioToolbox/AudioType.cs @@ -516,6 +516,21 @@ public unsafe bool IsVariableBitrate { } } + /// Whether this format is able to combining independently decodable packets with dependent packets. + public unsafe bool EmploysDependentPackets { + get { + uint data; + var size = sizeof (uint); + + fixed (AudioStreamBasicDescription* self = &this) { + if (AudioFormatPropertyNative.AudioFormatGetProperty (AudioFormatProperty.FormatEmploysDependentPackets, sizeof (AudioStreamBasicDescription), self, &size, &data) != 0) + return false; + } + + return data != 0; + } + } + /// Renders a debugging-friendly description of the contents of the AudioStreamBasicDescription. /// /// @@ -556,6 +571,49 @@ public override string ToString () } } + /// A structure that describes dependencies between audio packets. + [SupportedOSPlatform ("ios26.0")] + [SupportedOSPlatform ("maccatalyst26.0")] + [SupportedOSPlatform ("macos26.0")] + [SupportedOSPlatform ("tvos26.0")] + public struct AudioStreamPacketDependencyDescription { + uint isIndependentlyDecodable; + uint preRollCount; + uint flags; + uint reserved; + + /// Specifies whether an audio packet is independency decodable, or if more audio packets are required to reset the decoder. + public bool IsIndependentlyDecodable { + get => isIndependentlyDecodable != 0; + set => isIndependentlyDecodable = value ? (uint) 1 : (uint) 0; + } + + /// Specifies how many additional audio packets are required to reset the decoder for audio packets that aren't independenly decodable. + /// Ignored for independenly decodable audio packets. + public uint PreRollCount { + get => preRollCount; + set => preRollCount = value; + } + + /// Currently unused. + public uint Flags { + get => flags; + set => flags = value; + } + + /// Currently unused. + public uint Reserved { + get => reserved; + set => reserved = value; + } + + /// Provides a string representation of the packet dependency description. + public override string ToString () + { + return $"AudioStreamPacketDependencyDescription[IsIndependentlyDecodable={IsIndependentlyDecodable};PreRollCount={PreRollCount};Flags={Flags}]"; + } + } + /// Flags for the property. /// To be added. [Flags] diff --git a/src/AudioToolbox/Enums.cs b/src/AudioToolbox/Enums.cs index e481636bbac..3d274d5652a 100644 --- a/src/AudioToolbox/Enums.cs +++ b/src/AudioToolbox/Enums.cs @@ -61,4 +61,134 @@ public enum AUVoiceIOOtherAudioDuckingLevel : uint { Mid = 20, Max = 30, } + + /// This enum specifies properties for audio units. + [Mac (26, 0), iOS (26, 0), MacCatalyst (26, 0), NoTV] + public enum AUAudioMixProperty { + /// Remix the data from the file asset. + /// This is a read-write property. + SpatialAudioMixMetadata = 5000, + /// If spatialization is enabled or not. + /// This is a read-write property, with two possible values: 0 or 1. + EnableSpatialization = 5001, + } + + /// This enum specifies parameters for audio units. + public enum AUAudioMixParameter { + /// Get or set the style of the audio mixing. + /// This property is an enum of type . + [Mac (26, 0), iOS (26, 0), MacCatalyst (26, 0), NoTV] + Style = 0, + /// The remix amount. + /// This property is a , with valid values ranging from 0.0f to 1.0f. The default value is 0.5f. + [Mac (26, 0), iOS (26, 0), MacCatalyst (26, 0), NoTV] + RemixAmount = 1, + } + + /// This enum provides the possible values for the parameter. + [Mac (26, 0), iOS (26, 0), MacCatalyst (26, 0), NoTV] + public enum AUAudioMixRenderingStyle : uint { + /// Render with a cinematic style. This is the default value. + Cinematic = 0, + /// Render with a studio style. + Studio = 1, + /// Render with an in-frame style. + InFrame = 2, + /// Render the background only with a cinematic style. + CinematicBackgroundStem = 3, + /// Render the foreground only with a cinematic style. + CinematicForegroundStem = 4, + /// Render the foreground only with a stydio style. + StudioForegroundStem = 5, + /// Render the foreground only with an in-frame style. + InFrameForegroundStem = 6, + /// Render with a standard style. + Standard = 7, + /// Render the background only with a studio style. + StudioBackgroundStem = 8, + /// Render the background only with an in-frame style. + InFrameBackgroundStem = 9, + } + + public enum AudioCodecPropertyId : uint { + InputBufferSize = 0x74627566, // 'tbuf' + PacketFrameSize = 0x70616b66, // 'pakf' + HasVariablePacketByteSizes = 0x76706b3f, // 'vpk?' + EmploysDependentPackets = 0x64706b3f, // 'dpk?' + MaximumPacketByteSize = 0x70616b62, // 'pakb' + PacketSizeLimitForVbr = 0x70616b6c, // 'pakl' + CurrentInputFormat = 0x69666d74, // 'ifmt' + CurrentOutputFormat = 0x6f666d74, // 'ofmt' + MagicCookie = 0x6b756b69, // 'kuki' + UsedInputBufferSize = 0x75627566, // 'ubuf' + IsInitialized = 0x696e6974, // 'init' + CurrentTargetBitRate = 0x62726174, // 'brat' + CurrentInputSampleRate = 0x63697372, // 'cisr' + CurrentOutputSampleRate = 0x636f7372, // 'cosr' + QualitySetting = 0x73726371, // 'srcq' + ApplicableBitRateRange = 0x62727461, // 'brta' + RecommendedBitRateRange = 0x62727472, // 'brtr' + ApplicableInputSampleRates = 0x69737261, // 'isra' + ApplicableOutputSampleRates = 0x6f737261, // 'osra' + PaddedZeros = 0x70616430, // 'pad0' + PrimeMethod = 0x70726d6d, // 'prmm' + PrimeInfo = 0x7072696d, // 'prim' + CurrentInputChannelLayout = 0x69636c20, // 'icl ' + CurrentOutputChannelLayout = 0x6f636c20, // 'ocl ' + Settings = 0x61637320, // 'acs ' + FormatList = 0x6163666c, // 'acfl' + BitRateControlMode = 0x61636266, // 'acbf' + SoundQualityForVbr = 0x76627271, // 'vbrq' + BitRateForVbr = 0x76627262, // 'vbrb' + DelayMode = 0x646d6f64, // 'dmod' + AdjustLocalQuality = 0x5e71616c, // '^qal' + DynamicRangeControlMode = 0x6d647263, // 'mdrc' + AdjustCompressionProfile = 0x5e70726f, // '^pro' + ProgramTargetLevelConstant = 0x70746c63, // 'ptlc' + AdjustTargetLevelConstant = 0x5e746c63, // '^tlc' + ProgramTargetLevel = 0x7070746c, // 'pptl' + AdjustTargetLevel = 0x5e70746c, // '^ptl' + [iOS (26, 0), TV (26, 0), Mac (26, 0), MacCatalyst (26, 0)] + DynamicRangeControlConfiguration = 0x63647263, // 'cdrc' + [iOS (26, 0), TV (26, 0), Mac (26, 0), MacCatalyst (26, 0)] + ContentSource = 0x63737263, // 'csrc' + [iOS (26, 0), TV (26, 0), Mac (26, 0), MacCatalyst (26, 0)] + AspFrequency = 0x61737066, // 'aspf' + } + + [iOS (26, 0), TV (26, 0), Mac (26, 0), MacCatalyst (26, 0)] + public enum AudioCodecDynamicRangeControlConfiguration : uint { + None = 0, + Music = 1, + Speech = 2, + Movie = 3, + Capture = 4, + } + + [iOS (26, 0), TV (26, 0), Mac (26, 0), MacCatalyst (26, 0)] + public enum AudioCodecContentSource : int { + Unspecified = -1, + Reserved = 0, + AppleCaptureTraditional = 1, + AppleCaptureSpatial = 2, + AppleCaptureSpatialEnhanced = 3, + AppleMusicTraditional = 4, + AppleMusicSpatial = 5, + AppleAVTraditionalOffline = 6, + AppleAVSpatialOffline = 7, + AppleAVTraditionalLive = 8, + AppleAVSpatialLive = 9, + ApplePassthrough = 10, + + CaptureTraditional = 33, + CaptureSpatial = 34, + CaptureSpatialEnhanced = 35, + MusicTraditional = 36, + MusicSpatial = 37, + AVTraditionalOffline = 38, + AVSpatialOffline = 39, + AVTraditionalLive = 40, + AVSpatialLive = 41, + Passthrough = 42, + } } diff --git a/src/AudioUnit/AUEnums.cs b/src/AudioUnit/AUEnums.cs index dccaa59a25a..d8237a41f55 100644 --- a/src/AudioUnit/AUEnums.cs +++ b/src/AudioUnit/AUEnums.cs @@ -1479,7 +1479,17 @@ public enum AudioUnitSubType : uint { ScheduledSoundPlayer = 0x7373706C, // 'sspl' /// To be added. AudioFilePlayer = 0x6166706C, // 'afpl' - + /// A light reverb. + /// The FourCC value for 'rvb2', for the native constant kAudioUnitSubType_Reverb2. + Revert2 = 0x72766232, // 'rvb2' + /// An audio unit that can be used to isolate a sound type. + /// The FourCC value for 'vois', for the native constant kAudioUnitSubType_AUSoundIsolation. + [iOS (16, 0), Mac (13, 0), MacCatalyst (16, 0), NoTV] + AUSoundIsolation = 0x766f6973, // 'vois' + /// An audio unit that supports AudioMix separate-and-remix. + /// The FourCC value for 'amix', for the native constant kAudioUnitSubType_AUAudioMix. + [iOS (26, 0), Mac (26, 0), MacCatalyst (26, 0), NoTV] + AUAudioMix = 0x616d6978, // 'amix' #if MONOMAC /// To be added. HALOutput = 0x6168616C, // 'ahal' diff --git a/tests/monotouch-test/AudioToolbox/AudioConverterTest.cs b/tests/monotouch-test/AudioToolbox/AudioConverterTest.cs index 60b17a43eaf..7f956899104 100644 --- a/tests/monotouch-test/AudioToolbox/AudioConverterTest.cs +++ b/tests/monotouch-test/AudioToolbox/AudioConverterTest.cs @@ -23,6 +23,66 @@ namespace MonoTouchFixtures.AudioToolbox { [Preserve (AllMembers = true)] public class AudioConverterTest { + [Test] + public void Properties () + { + TestRuntime.AssertXcodeVersion (26, 0); + + var srcFormat = new AudioStreamBasicDescription () { + SampleRate = 8000, + Format = AudioFormatType.LinearPCM, + FormatFlags = AudioFormatFlags.IsSignedInteger | AudioFormatFlags.IsPacked, + BytesPerPacket = 4, + FramesPerPacket = 1, + BytesPerFrame = 4, + ChannelsPerFrame = 2, + BitsPerChannel = 16, + }; + + var dstFormat = new AudioStreamBasicDescription () { + SampleRate = 8000, + Format = AudioFormatType.AppleLossless, + // FormatFlags = 0, + BytesPerPacket = 0, + FramesPerPacket = 4096, + BytesPerFrame = 0, + ChannelsPerFrame = 2, + BitsPerChannel = 0, + }; + + // create the AudioConverter + using AudioConverter? converter = AudioConverter.Create (srcFormat, dstFormat, out var createResult); + Assert.AreEqual (AudioConverterError.None, createResult, $"AudioConverterCreate ({srcFormat} -> {dstFormat}): {createResult}"); + + Assert.That (converter.PerformDownmix, Is.EqualTo (false), "PerformDownmix #0"); + converter.PerformDownmix = true; + Assert.That (converter.PerformDownmix, Is.EqualTo (true), "PerformDownmix #1"); + converter.PerformDownmix = false; + Assert.That (converter.PerformDownmix, Is.EqualTo (false), "PerformDownmix #2"); + converter.PerformDownmix = true; + Assert.That (converter.PerformDownmix, Is.EqualTo (true), "PerformDownmix #3"); + + Assert.That (converter.ChannelMixMap, Is.Not.Null, "ChannelMixMap #0"); + Assert.That (converter.ChannelMixMap.Length, Is.EqualTo (0), "ChannelMixMap #0.Length"); + converter.ChannelMixMap = new float [] { 0.25f, 0.6f, 0.75f, 0.4f }; + Assert.That (converter.ChannelMixMap, Is.Not.Null, "ChannelMixMap #1"); + Assert.That (converter.ChannelMixMap.Length, Is.EqualTo (4), "ChannelMixMap #1.Length"); + Assert.That (converter.ChannelMixMap [0], Is.EqualTo (0.25f), "ChannelMixMap #1[0]"); + Assert.That (converter.ChannelMixMap [1], Is.EqualTo (0.60f), "ChannelMixMap #1[1]"); + Assert.That (converter.ChannelMixMap [2], Is.EqualTo (0.75f), "ChannelMixMap #1[2]"); + Assert.That (converter.ChannelMixMap [3], Is.EqualTo (0.40f), "ChannelMixMap #1[3]"); + + Assert.Throws (() => converter.ChannelMixMap = null, "ChannelMixMap #3"); + + converter.ChannelMixMap = new float [4]; + Assert.That (converter.ChannelMixMap, Is.Not.Null, "ChannelMixMap #2"); + Assert.That (converter.ChannelMixMap.Length, Is.EqualTo (4), "ChannelMixMap #2.Length"); + Assert.That (converter.ChannelMixMap [0], Is.EqualTo (0), "ChannelMixMap #2[0]"); + Assert.That (converter.ChannelMixMap [1], Is.EqualTo (0), "ChannelMixMap #2[1]"); + Assert.That (converter.ChannelMixMap [2], Is.EqualTo (0), "ChannelMixMap #2[2]"); + Assert.That (converter.ChannelMixMap [3], Is.EqualTo (0), "ChannelMixMap #2[3]"); + } + [Test] public void Formats () { @@ -93,7 +153,20 @@ public void Convert () Convert (output1, output2, AudioFormatType.LinearPCM); } - void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType outputFormatType, int? sampleRate = null, AudioConverterOptions? options = null) + [Test] + [TestCase (AudioFormatType.Apac)] + public void ConvertWithPacketDependencies (AudioFormatType targetType) + { + TestRuntime.AssertXcodeVersion (26, 0); + + var sourcePath = Path.Combine (NSBundle.MainBundle.ResourcePath, "Hand.wav"); + var paths = NSSearchPath.GetDirectories (NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomain.User); + + var output1 = Path.Combine (paths [0], "output1.caf"); + Convert (sourcePath, output1, targetType, withPacketDependencies: true); + } + + void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType outputFormatType, int? sampleRate = null, AudioConverterOptions? options = null, bool withPacketDependencies = false) { var destinationUrl = NSUrl.FromFilename (destinationFilePath); var sourceUrl = NSUrl.FromFilename (sourceFilePath); @@ -107,22 +180,29 @@ void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType // setup the output file format dstFormat.SampleRate = sampleRate ?? srcFormat.SampleRate; + dstFormat.Format = outputFormatType; if (outputFormatType == AudioFormatType.LinearPCM) { // if the output format is PCM create a 16 - bit int PCM file format - dstFormat.Format = AudioFormatType.LinearPCM; dstFormat.ChannelsPerFrame = srcFormat.ChannelsPerFrame; dstFormat.BitsPerChannel = 16; dstFormat.BytesPerPacket = dstFormat.BytesPerFrame = 2 * dstFormat.ChannelsPerFrame; dstFormat.FramesPerPacket = 1; dstFormat.FormatFlags = AudioFormatFlags.LinearPCMIsPacked | AudioFormatFlags.LinearPCMIsSignedInteger; - } else { + } else if (outputFormatType == AudioFormatType.Apac) { + // No samples or example code from Apple or anybody else, so: + Assert.Ignore ("Couldn't figure out the right properties to make the Apac encoder work:/"); + // use AudioFormat API to fill out the rest of the description + var afe = AudioStreamBasicDescription.GetFormatInfo (ref dstFormat); + Assert.AreEqual (AudioFormatError.None, afe, $"GetFormatInfo: {name}"); + } else if (outputFormatType == AudioFormatType.AppleLossless) { // compressed format - need to set at least format, sample rate and channel fields for kAudioFormatProperty_FormatInfo - dstFormat.Format = outputFormatType; dstFormat.ChannelsPerFrame = srcFormat.ChannelsPerFrame; // for iLBC num channels must be 1 // use AudioFormat API to fill out the rest of the description var afe = AudioStreamBasicDescription.GetFormatInfo (ref dstFormat); Assert.AreEqual (AudioFormatError.None, afe, $"GetFormatInfo: {name}"); + } else { + throw new NotImplementedException (); } // create the AudioConverter @@ -130,7 +210,7 @@ void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType using AudioConverter? converter = options.HasValue ? AudioConverter.Create (srcFormat, dstFormat, options.Value, out ce) : AudioConverter.Create (srcFormat, dstFormat, out ce); - Assert.AreEqual (AudioConverterError.None, ce, $"AudioConverterCreate: {name}"); + Assert.AreEqual (AudioConverterError.None, ce, $"AudioConverterCreate : {name}\n\tSource format: {srcFormat}\n\tDestination format: {dstFormat})"); // set up source buffers and data proc info struct var afio = new AudioFileIO (32 * 1024); // 32Kb @@ -193,6 +273,12 @@ void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType outputPacketDescriptions = new AudioStreamPacketDescription [theOutputBufSize / outputSizePerPacket]; } int numOutputPackets = theOutputBufSize / outputSizePerPacket; + AudioStreamPacketDependencyDescription[] packetDependencies = null; + + if (withPacketDependencies) { + Assert.That (dstFormat.EmploysDependentPackets, Is.True, "EmploysDependentPackets"); + packetDependencies = new AudioStreamPacketDependencyDescription [numOutputPackets]; + } // if the destination format has a cookie, get it and set it on the output file WriteCookie (converter, destinationFile); @@ -212,7 +298,12 @@ void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType // convert data int ioOutputDataPackets = numOutputPackets; - var fe = converter.FillComplexBuffer (ref ioOutputDataPackets, fillBufList, outputPacketDescriptions); + AudioConverterError fe; + if (withPacketDependencies) { + fe = converter.FillComplexBuffer (ref ioOutputDataPackets, fillBufList, outputPacketDescriptions, packetDependencies); + } else { + fe = converter.FillComplexBuffer (ref ioOutputDataPackets, fillBufList, outputPacketDescriptions); + } // if interrupted in the process of the conversion call, we must handle the error appropriately Assert.AreEqual (AudioConverterError.None, fe, $"FillComplexBuffer: {name}"); diff --git a/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-AudioToolbox.todo b/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-AudioToolbox.todo deleted file mode 100644 index 6f7efac7d54..00000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/MacCatalyst-AudioToolbox.todo +++ /dev/null @@ -1,4 +0,0 @@ -!missing-enum! AUAudioMixRenderingStyle not bound -!missing-pinvoke! AudioConverterFillComplexBufferRealtimeSafe is not bound -!missing-pinvoke! AudioConverterFillComplexBufferWithPacketDependencies is not bound -!missing-pinvoke! AudioFileWritePacketsWithDependencies is not bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/common-AudioToolbox.ignore b/tests/xtro-sharpie/api-annotations-dotnet/common-AudioToolbox.ignore index 8582753a5f5..d70264440b4 100644 --- a/tests/xtro-sharpie/api-annotations-dotnet/common-AudioToolbox.ignore +++ b/tests/xtro-sharpie/api-annotations-dotnet/common-AudioToolbox.ignore @@ -96,3 +96,5 @@ !missing-pinvoke! AUParameterValueFromLinear is not bound !missing-pinvoke! AUParameterValueToLinear is not bound +# We can't provide any realtime guarantees, so we should probably not bind any APIs that require/give realtiem guarantees +!missing-pinvoke! AudioConverterFillComplexBufferRealtimeSafe is not bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/iOS-AudioToolbox.ignore b/tests/xtro-sharpie/api-annotations-dotnet/iOS-AudioToolbox.ignore index 9268424ba42..648c963fb86 100644 --- a/tests/xtro-sharpie/api-annotations-dotnet/iOS-AudioToolbox.ignore +++ b/tests/xtro-sharpie/api-annotations-dotnet/iOS-AudioToolbox.ignore @@ -1,2 +1,5 @@ !missing-selector! AUAudioUnit::messageChannelFor: not bound !missing-protocol! AUMessageChannel not bound + +# this enum is declared as not available on iOS, not sure why it shows up (it's visionOS-only) +!missing-enum! CASoundStageSize not bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/iOS-AudioToolbox.todo b/tests/xtro-sharpie/api-annotations-dotnet/iOS-AudioToolbox.todo deleted file mode 100644 index 5ea527c47d2..00000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/iOS-AudioToolbox.todo +++ /dev/null @@ -1,5 +0,0 @@ -!missing-enum! AUAudioMixRenderingStyle not bound -!missing-enum! CASoundStageSize not bound -!missing-pinvoke! AudioConverterFillComplexBufferRealtimeSafe is not bound -!missing-pinvoke! AudioConverterFillComplexBufferWithPacketDependencies is not bound -!missing-pinvoke! AudioFileWritePacketsWithDependencies is not bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/macOS-AudioToolbox.todo b/tests/xtro-sharpie/api-annotations-dotnet/macOS-AudioToolbox.todo deleted file mode 100644 index 6f7efac7d54..00000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/macOS-AudioToolbox.todo +++ /dev/null @@ -1,4 +0,0 @@ -!missing-enum! AUAudioMixRenderingStyle not bound -!missing-pinvoke! AudioConverterFillComplexBufferRealtimeSafe is not bound -!missing-pinvoke! AudioConverterFillComplexBufferWithPacketDependencies is not bound -!missing-pinvoke! AudioFileWritePacketsWithDependencies is not bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/tvOS-AudioToolbox.ignore b/tests/xtro-sharpie/api-annotations-dotnet/tvOS-AudioToolbox.ignore index b44488f4b83..4d0f84eeba2 100644 --- a/tests/xtro-sharpie/api-annotations-dotnet/tvOS-AudioToolbox.ignore +++ b/tests/xtro-sharpie/api-annotations-dotnet/tvOS-AudioToolbox.ignore @@ -1,4 +1,10 @@ # CoreMIDI is not present on tvOS !missing-pinvoke! MusicSequenceSetMIDIEndpoint is not bound !missing-pinvoke! MusicTrackGetDestMIDIEndpoint is not bound -!missing-pinvoke! MusicTrackSetDestMIDIEndpoint is not bound \ No newline at end of file +!missing-pinvoke! MusicTrackSetDestMIDIEndpoint is not bound + +# the enum is available on tvOS, but none of its values are, so just don't bind the enum itself +!missing-enum! AUAudioMixRenderingStyle not bound + +# this enum is declared as not available on tvOS, not sure why it shows up (it's visionOS-only) +!missing-enum! CASoundStageSize not bound diff --git a/tests/xtro-sharpie/api-annotations-dotnet/tvOS-AudioToolbox.todo b/tests/xtro-sharpie/api-annotations-dotnet/tvOS-AudioToolbox.todo deleted file mode 100644 index 5ea527c47d2..00000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/tvOS-AudioToolbox.todo +++ /dev/null @@ -1,5 +0,0 @@ -!missing-enum! AUAudioMixRenderingStyle not bound -!missing-enum! CASoundStageSize not bound -!missing-pinvoke! AudioConverterFillComplexBufferRealtimeSafe is not bound -!missing-pinvoke! AudioConverterFillComplexBufferWithPacketDependencies is not bound -!missing-pinvoke! AudioFileWritePacketsWithDependencies is not bound