Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AudioToolbox] Implement Xcode 16.0 beta 1-6 changes. #20856

Merged
merged 10 commits into from
Aug 27, 2024
125 changes: 125 additions & 0 deletions src/AudioToolbox/AudioConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ public enum AudioConverterPrimeMethod // typedef UInt32 AudioConverterPropertyID
None = 2
}

[Flags]
#if NET
[SupportedOSPlatform ("ios18.0")]
[SupportedOSPlatform ("maccatalyst18.0")]
[SupportedOSPlatform ("macos15.0")]
[SupportedOSPlatform ("tvos18.0")]
#else
[Watch (11, 0), TV (18, 0), Mac (15, 0), iOS (18, 0), MacCatalyst (18, 0)]
#endif
public enum AudioConverterOptions : uint {
None = 0,
Unbuffered = 1 << 16,
}

#if NET
[SupportedOSPlatform ("ios")]
[SupportedOSPlatform ("maccatalyst")]
Expand Down Expand Up @@ -441,6 +455,49 @@ public bool CanResumeFromInterruption {
return new AudioConverter (ptr, true);
}

/// <summary>Create a new AudioConverter with the specified options.</summary>
/// <param name="sourceFormat">The format of the source audio to be converted.</param>
/// <param name="destinationFormat">The format to convert the source audio to.</param>
/// <param name="options">Any <see cref="AudioConverterOptions" /> to use.</param>
/// <param name="error">In case of failure, will contain the error code for the failure. Otherwise the value <see cref="AudioConverterError.None" /> will be returned.</param>
/// <returns>A new AudioConverter instance, or null in case of failure.</returns>
#if NET
[SupportedOSPlatform ("ios18.0")]
[SupportedOSPlatform ("maccatalyst18.0")]
[SupportedOSPlatform ("macos15.0")]
[SupportedOSPlatform ("tvos18.0")]
#else
[Watch (11, 0), TV (18, 0), Mac (15, 0), iOS (18, 0), MacCatalyst (18, 0)]
#endif
public static AudioConverter? Create (AudioStreamBasicDescription sourceFormat, AudioStreamBasicDescription destinationFormat, AudioConverterOptions options, out AudioConverterError error)
{
IntPtr ptr = default (IntPtr);
unsafe {
error = AudioConverterNewWithOptions (&sourceFormat, &destinationFormat, options, &ptr);
}
if (error != AudioConverterError.None)
return null;
return new AudioConverter (ptr, true);
}

/// <summary>Create a new AudioConverter with the specified options.</summary>
/// <param name="sourceFormat">The format of the source audio to be converted.</param>
/// <param name="destinationFormat">The format to convert the source audio to.</param>
/// <param name="options">Any <see cref="AudioConverterOptions" /> to use.</param>
/// <returns>A new AudioConverter instance, or null in case of failure.</returns>
#if NET
[SupportedOSPlatform ("ios18.0")]
[SupportedOSPlatform ("maccatalyst18.0")]
[SupportedOSPlatform ("macos15.0")]
[SupportedOSPlatform ("tvos18.0")]
#else
[Watch (11, 0), TV (18, 0), Mac (15, 0), iOS (18, 0), MacCatalyst (18, 0)]
#endif
public static AudioConverter? Create (AudioStreamBasicDescription sourceFormat, AudioStreamBasicDescription destinationFormat, AudioConverterOptions options)
{
return Create (sourceFormat, destinationFormat, options, out var _);
}

public static AudioFormatType []? DecodeFormats {
get {
return GetFormats (AudioFormatProperty.DecodeFormatIDs);
Expand Down Expand Up @@ -641,6 +698,48 @@ static AudioConverterError FillComplexBufferShared (IntPtr inAudioConverter, ref
}
}

#if NET
public delegate void PrepareCompletionCallback (AudioConverterError status);

[UnmanagedCallersOnly]
static void PrepareCompletionHandler (IntPtr block, int status)
{
var del = BlockLiteral.GetTarget<PrepareCompletionCallback> (block);
if (del is not null)
del ((AudioConverterError) status);
}

/// <summary>Optimizes any subsequent creation of audio converters in this process.</summary>
/// <param name="flags">Reserved; always pass 0.</param>
/// <param name="ioReserved">Reserved; always pass IntPtr.Zero.</param>
/// <param name="completionCallback">Optional callback to invoke when the preparation is complete.</param>
[SupportedOSPlatform ("ios18.0")]
[SupportedOSPlatform ("maccatalyst18.0")]
[SupportedOSPlatform ("macos15.0")]
[SupportedOSPlatform ("tvos18.0")]
public unsafe static void Prepare (uint flags = 0, IntPtr ioReserved = default (IntPtr), PrepareCompletionCallback? completionCallback = null)
{
if (completionCallback is null) {
AudioConverterPrepare (flags, ioReserved, null);
} else {
delegate* unmanaged<IntPtr, int, void> trampoline = &PrepareCompletionHandler;
using var block = new BlockLiteral (trampoline, completionCallback, typeof (AudioConverter), nameof (PrepareCompletionHandler));
AudioConverterPrepare (flags, ioReserved, &block);
}
}

/// <summary>Optimizes any subsequent creation of audio converters in this process.</summary>
/// <param name="completionCallback">Callback to invoke when the preparation is complete.</param>
[SupportedOSPlatform ("ios18.0")]
[SupportedOSPlatform ("maccatalyst18.0")]
[SupportedOSPlatform ("macos15.0")]
[SupportedOSPlatform ("tvos18.0")]
public unsafe static void Prepare (PrepareCompletionCallback completionCallback)
{
Prepare (0, IntPtr.Zero, completionCallback);
}
#endif // NET

public AudioConverterError Reset ()
{
return AudioConverterReset (Handle);
Expand Down Expand Up @@ -743,6 +842,32 @@ void SetProperty (AudioConverterPropertyID propertyID, double value)
unsafe static extern AudioConverterError AudioConverterNewSpecific (AudioStreamBasicDescription* inSourceFormat, AudioStreamBasicDescription* inDestinationFormat,
int inNumberClassDescriptions, AudioClassDescription* inClassDescriptions, IntPtr* outAudioConverter);

#if NET
[SupportedOSPlatform ("ios18.0")]
[SupportedOSPlatform ("maccatalyst18.0")]
[SupportedOSPlatform ("macos15.0")]
[SupportedOSPlatform ("tvos18.0")]
#else
[Watch (11, 0), TV (18, 0), Mac (15, 0), iOS (18, 0), MacCatalyst (18, 0)]
#endif
[DllImport (Constants.AudioToolboxLibrary)]
unsafe static extern void AudioConverterPrepare (uint inFlags, IntPtr ioReserved, BlockLiteral* block);

#if NET
[SupportedOSPlatform ("ios18.0")]
[SupportedOSPlatform ("maccatalyst18.0")]
[SupportedOSPlatform ("macos15.0")]
[SupportedOSPlatform ("tvos18.0")]
#else
[Watch (11, 0), TV (18, 0), Mac (15, 0), iOS (18, 0), MacCatalyst (18, 0)]
#endif
[DllImport (Constants.AudioToolboxLibrary)]
unsafe static extern /* OSStatus */ AudioConverterError AudioConverterNewWithOptions (
/* const AudioStreamBasicDescription * */ AudioStreamBasicDescription* inSourceFormat,
/* const AudioStreamBasicDescription * */ AudioStreamBasicDescription* inDestinationFormat,
/* AudioConverterOptions */ AudioConverterOptions inOptions,
/* AudioConverterRef __nullable * __nonnull */ IntPtr* outAudioConverter);

[DllImport (Constants.AudioToolboxLibrary)]
static extern AudioConverterError AudioConverterDispose (IntPtr inAudioConverter);

Expand Down
9 changes: 3 additions & 6 deletions src/AudioToolbox/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,11 @@ public enum AUSpatialMixerSourceMode : uint {
[iOS (16, 0)]
[MacCatalyst (16, 0)]
public enum AUSpatialMixerPersonalizedHrtfMode : uint {
[NoiOS, NoTV]
[NoMacCatalyst]
[iOS (18, 0), TV (18, 0), MacCatalyst (18, 0)]
Off = 0,
[NoiOS, NoTV]
[NoMacCatalyst]
[iOS (18, 0), TV (18, 0), MacCatalyst (18, 0)]
On = 1,
[NoiOS, NoTV]
[NoMacCatalyst]
[iOS (18, 0), TV (18, 0), MacCatalyst (18, 0)]
Auto = 2,
}

Expand Down
39 changes: 24 additions & 15 deletions src/AudioUnit/AUEnums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
Expand Down Expand Up @@ -418,21 +419,29 @@ enum AudioUnitPropertyIDType { // UInt32 AudioUnitPropertyID
ReverbRoomType = 10,
UsesInternalReverb = 1005,
SpatializationAlgorithm = 3000,
[Deprecated (PlatformName.iOS, 9, 0)]
[Deprecated (PlatformName.TvOS, 9, 0)]
[Deprecated (PlatformName.MacCatalyst, 13, 1)]
[Deprecated (PlatformName.MacOSX, 10, 11)]
DistanceParams = 3010,
[Deprecated (PlatformName.iOS, 9, 0)]
[Deprecated (PlatformName.TvOS, 9, 0)]
[Deprecated (PlatformName.MacCatalyst, 13, 1)]
[Deprecated (PlatformName.MacOSX, 10, 11)]
AttenuationCurve = 3013,
[Deprecated (PlatformName.iOS, 9, 0)]
[Deprecated (PlatformName.TvOS, 9, 0)]
[Deprecated (PlatformName.MacCatalyst, 13, 1)]
[Deprecated (PlatformName.MacOSX, 10, 11)]
RenderingFlags = 3003,
SpatialMixerRenderingFlags = 3003,
SpatialMixerSourceMode = 3005,
SpatialMixerDistanceParams = 3010,
#if !XAMCORE_5_0
[Obsolete ("Use 'SpatialMixerDistanceParams' instead.")]
[EditorBrowsable (EditorBrowsableState.Never)]
DistanceParams = SpatialMixerDistanceParams,
[Obsolete ("Use 'SpatialMixerAttenuationCurve' instead.")]
[EditorBrowsable (EditorBrowsableState.Never)]
AttenuationCurve = SpatialMixerAttenuationCurve,
[Obsolete ("Use 'SpatialMixerRenderingFlags' instead.")]
[EditorBrowsable (EditorBrowsableState.Never)]
RenderingFlags = SpatialMixerRenderingFlags,
#endif
SpatialMixerAttenuationCurve = 3013,
SpatialMixerOutputType = 3100,
SpatialMixerPointSourceInHeadMode = 3103,
[Mac (12, 3), iOS (18, 0), TV (18, 0), MacCatalyst (18, 0), NoWatch]
SpatialMixerEnableHeadTracking = 3111,
[Mac (13, 0), iOS (18, 0), TV (18, 0), MacCatalyst (18, 0), NoWatch]
SpatialMixerPersonalizedHrtfMode = 3113,
[Mac (14, 0), iOS (18, 0), TV (18, 0), MacCatalyst (18, 0), NoWatch]
SpatialMixerAnyInputIsUsingPersonalizedHrtf = 3116,

// AUScheduledSoundPlayer
ScheduleAudioSlice = 3300,
Expand Down
4 changes: 4 additions & 0 deletions src/audiounit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ AUParameterTree ParameterTree {
[NoWatch, NoTV, NoiOS]
[Export ("isLoadedInProcess")]
bool IsLoadedInProcess { get; }

[Watch (9, 0), TV (16, 0), Mac (13, 0), iOS (16, 0), MacCatalyst (16, 0)]
[Export ("migrateFromPlugin")]
NSData [] MigrateFromPlugin { get; }
}

// kept separate from AUAudioUnit, quote:
Expand Down
5 changes: 5 additions & 0 deletions tests/cecil-tests/Documentation.KnownFailures.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3513,6 +3513,8 @@ F:AudioToolbox.AudioConverterError.OutputSampleRateOutOfRange
F:AudioToolbox.AudioConverterError.PropertyNotSupported
F:AudioToolbox.AudioConverterError.RequiresPacketDescriptionsError
F:AudioToolbox.AudioConverterError.UnspecifiedError
F:AudioToolbox.AudioConverterOptions.None
F:AudioToolbox.AudioConverterOptions.Unbuffered
F:AudioToolbox.AudioConverterPrimeInfo.LeadingFrames
F:AudioToolbox.AudioConverterPrimeInfo.TrailingFrames
F:AudioToolbox.AudioConverterPrimeMethod.None
Expand Down Expand Up @@ -56019,6 +56021,7 @@ P:AudioUnit.AUAudioUnit.MaximumFramesToRender
P:AudioUnit.AUAudioUnit.MidiOutputBufferSizeHint
P:AudioUnit.AUAudioUnit.MidiOutputEventBlock
P:AudioUnit.AUAudioUnit.MidiOutputNames
P:AudioUnit.AUAudioUnit.MigrateFromPlugin
P:AudioUnit.AUAudioUnit.MusicDeviceOrEffect
P:AudioUnit.AUAudioUnit.OutputBusses
P:AudioUnit.AUAudioUnit.ParameterTree
Expand Down Expand Up @@ -77699,8 +77702,10 @@ T:AudioToolbox.AudioChannelLayoutTagExtensions
T:AudioToolbox.AudioClassDescription
T:AudioToolbox.AudioCodecComponentType
T:AudioToolbox.AudioConverter
T:AudioToolbox.AudioConverter.PrepareCompletionCallback
T:AudioToolbox.AudioConverterComplexInputData
T:AudioToolbox.AudioConverterError
T:AudioToolbox.AudioConverterOptions
T:AudioToolbox.AudioConverterPrimeInfo
T:AudioToolbox.AudioConverterPrimeMethod
T:AudioToolbox.AudioConverterQuality
Expand Down
49 changes: 47 additions & 2 deletions tests/monotouch-test/AudioToolbox/AudioConverterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

using AudioToolbox;
using AudioUnit;
Expand Down Expand Up @@ -34,6 +35,47 @@ public void Formats ()
Assert.That (encodeFormats.Length, Is.GreaterThan (10), "Encode Length #1");
}

[Test]
public void Prepare ()
{
TestRuntime.AssertXcodeVersion (16, 0);

AudioConverter.Prepare ();
}

[Test]
public void PrepareWithCallback ()
{
TestRuntime.AssertXcodeVersion (16, 0);

var tcs = new TaskCompletionSource<AudioConverterError> ();
AudioConverter.Prepare ((status) => tcs.SetResult (status));
var timeout = TimeSpan.FromSeconds (5);
if (!tcs.Task.Wait (timeout)) {
// Preparation might take a long time, so don't assert on the bots.
// We might have to bump the timeout for local test runs as well.
if (!TestRuntime.IsInCI)
Assert.Fail ($"Callback wasn't called within {timeout.TotalSeconds} s");
}
}

[Test]
public void CreateWithOptions ()
{
TestRuntime.AssertXcodeVersion (16, 0);

var sourcePath = Path.Combine (NSBundle.MainBundle.ResourcePath, "Hand.wav");
var paths = NSSearchPath.GetDirectories (NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomain.User);

// Convert once
var output1 = Path.Combine (paths [0], "outputOptions1.caf");
Convert (sourcePath, output1, AudioFormatType.AppleLossless, options: AudioConverterOptions.None);

// Convert converted output
var output2 = Path.Combine (paths [0], "outputOptions2.wav");
Convert (output1, output2, AudioFormatType.LinearPCM, options: AudioConverterOptions.None);
}

[Test]
public void Convert ()
{
Expand All @@ -51,7 +93,7 @@ public void Convert ()
Convert (output1, output2, AudioFormatType.LinearPCM);
}

void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType outputFormatType, int? sampleRate = null)
void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType outputFormatType, int? sampleRate = null, AudioConverterOptions? options = null)
{
var destinationUrl = NSUrl.FromFilename (destinationFilePath);
var sourceUrl = NSUrl.FromFilename (sourceFilePath);
Expand Down Expand Up @@ -84,7 +126,10 @@ void Convert (string sourceFilePath, string destinationFilePath, AudioFormatType
}

// create the AudioConverter
using var converter = AudioConverter.Create (srcFormat, dstFormat, out var ce);
AudioConverterError ce;
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}");

// set up source buffers and data proc info struct
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
!missing-enum! AUVoiceIOOtherAudioDuckingLevel not bound
!missing-pinvoke! AudioFileGetUserDataAtOffset is not bound
!missing-pinvoke! AudioFileGetUserDataSize64 is not bound
!missing-enum! AudioConverterOptions not bound
!missing-pinvoke! AudioConverterNewWithOptions is not bound
!missing-pinvoke! AudioConverterPrepare is not bound
!missing-selector! AUAudioUnit::migrateFromPlugin not bound
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,3 @@
!missing-pinvoke! AudioFileComponentGetUserDataSize64 is not bound
!missing-pinvoke! AudioFileGetUserDataAtOffset is not bound
!missing-pinvoke! AudioFileGetUserDataSize64 is not bound
!missing-enum! AudioConverterOptions not bound
!missing-pinvoke! AudioConverterNewWithOptions is not bound
!missing-pinvoke! AudioConverterPrepare is not bound
!missing-selector! AUAudioUnit::migrateFromPlugin not bound
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
!missing-enum! AUVoiceIOOtherAudioDuckingLevel not bound
!missing-pinvoke! AudioFileGetUserDataAtOffset is not bound
!missing-pinvoke! AudioFileGetUserDataSize64 is not bound
!missing-enum! AudioConverterOptions not bound
!missing-pinvoke! AudioConverterNewWithOptions is not bound
!missing-pinvoke! AudioConverterPrepare is not bound
!missing-selector! AUAudioUnit::migrateFromPlugin not bound
4 changes: 0 additions & 4 deletions tests/xtro-sharpie/iOS-AudioToolbox.todo
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
!missing-enum! AUVoiceIOOtherAudioDuckingLevel not bound
!missing-pinvoke! AudioFileGetUserDataAtOffset is not bound
!missing-pinvoke! AudioFileGetUserDataSize64 is not bound
!missing-enum! AudioConverterOptions not bound
!missing-pinvoke! AudioConverterNewWithOptions is not bound
!missing-pinvoke! AudioConverterPrepare is not bound
!missing-selector! AUAudioUnit::migrateFromPlugin not bound
4 changes: 0 additions & 4 deletions tests/xtro-sharpie/macOS-AudioToolbox.todo
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,3 @@
!missing-pinvoke! AudioFileComponentGetUserDataSize64 is not bound
!missing-pinvoke! AudioFileGetUserDataAtOffset is not bound
!missing-pinvoke! AudioFileGetUserDataSize64 is not bound
!missing-enum! AudioConverterOptions not bound
!missing-pinvoke! AudioConverterNewWithOptions is not bound
!missing-pinvoke! AudioConverterPrepare is not bound
!missing-selector! AUAudioUnit::migrateFromPlugin not bound
4 changes: 0 additions & 4 deletions tests/xtro-sharpie/tvOS-AudioToolbox.todo
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
!missing-enum! AUVoiceIOOtherAudioDuckingLevel not bound
!missing-pinvoke! AudioFileGetUserDataAtOffset is not bound
!missing-pinvoke! AudioFileGetUserDataSize64 is not bound
!missing-enum! AudioConverterOptions not bound
!missing-pinvoke! AudioConverterNewWithOptions is not bound
!missing-pinvoke! AudioConverterPrepare is not bound
!missing-selector! AUAudioUnit::migrateFromPlugin not bound
Loading