Xamarin-macios: Missing binding for latency in AudioUnit

Created on 2 Dec 2018  路  4Comments  路  Source: xamarin/xamarin-macios

This issue was previously reported as Bugzilla bug 30004.

Steps to Reproduce

  1. Get the default output audio component using AudioComponent.FindComponent(AudioTypeOutput.Default).
  2. Create an instance of AudioUnit using the output audio component.
  3. Try to retrieve the latency property.

Expected Behavior

It should be possible to retrieve the latency property.

Actual Behavior

Unable to retrieve latency as there is no property binding to it, and the generic AudioUnitGetProperty method is private.

Environment

=== Visual Studio Community 2017 for Mac ===

Version 7.7 (build 1868)
Installation UUID: 40d8d7bc-fa1a-41f1-81b0-0ae2d5b45e1c
    GTK+ 2.24.23 (Raleigh theme)
    Xamarin.Mac 4.4.1.178 (master / eeaeb7e6)

    Package version: 516000221

=== Mono Framework MDK ===

Runtime:
    Mono 5.16.0.221 (2018-06/b63e5378e38) (64-bit)
    Package version: 516000221

=== NuGet ===

Version: 4.7.0.5148

=== .NET Core ===

Runtime: /usr/local/share/dotnet/dotnet
Runtime Version: 2.1.2
SDK: /usr/local/share/dotnet/sdk/2.1.302/Sdks
SDK Version: 2.1.302
MSBuild SDKs: /Library/Frameworks/Mono.framework/Versions/5.16.0/lib/mono/msbuild/15.0/bin/Sdks

=== Xamarin.Profiler ===

'/Applications/Xamarin Profiler.app' not found

=== Apple Developer Tools ===

Xcode 10.1 (14460.46)
Build 10B61

=== Xamarin.Android ===

Version: 9.1.0.38 (Visual Studio Community)
Android SDK: /Users/Orry/Library/Android/sdk
    Supported Android versions:
        8.1 (API level 27)

SDK Tools Version: 26.1.1
SDK Platform Tools Version: 28.0.1
SDK Build Tools Version: 28.0.3

Java SDK: /Users/Orry/Library/Developer/Xamarin/jdk/microsoft_dist_openjdk_1.8.0.9
openjdk version "1.8.0-9"
OpenJDK Runtime Environment (build 1.8.0-9-microsoft-b00)
OpenJDK 64-Bit Server VM (build 25.71-b00, mixed mode)

Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

=== Android Device Manager ===

Version: 7.7.1.0
Hash: 06ceaea1

=== Xamarin.Mac ===

Version: 5.2.1.11 (Visual Studio Community)
Hash: 5ef73d34
Branch: 
Build date: 2018-11-21 00:03:25-0500

=== Xamarin.iOS ===

Version: 12.2.1.11 (Visual Studio Community)
Hash: 5ef73d34
Branch: d15-9
Build date: 2018-11-21 00:03:24-0500

=== Xamarin Inspector ===

Version: 1.4.3
Hash: db27525
Branch: 1.4-release
Build date: Mon, 09 Jul 2018 21:20:18 GMT
Client compatibility: 1

=== Build Information ===

Release ID: 707001868
Git revision: 8bcdfa96d67c8c88dc45728cc968a2f84b55322f
Build date: 2018-11-22 12:19:29+00
Build branch: release-7.7
Xamarin extensions: d66dbedcda237cd9b546abfecfeecafba6f8e3cb

=== Operating System ===

Mac OS X 10.14.1
Darwin 18.2.0 Darwin Kernel Version 18.2.0
    Fri Oct  5 19:41:49 PDT 2018
    root:xnu-4903.221.2~2/RELEASE_X86_64 x86_64
enhancement iOS macOS

Most helpful comment

Hello @orryverducci, thank you for your feedback!

We want to add the API you need in PR #5221 but I need confirmation from you that said API works as expected, I have prepared a class that you can add to your current application with almost identical logic as PR #5221, please give this a try and let us know if this resolves you issue and works for you as expected (Bonus points if you could provide us with a small test case demoing this API). If you confirm that this works as expected, then I'll be able to merge said PR.

All you need to do is add the following class to your project and add using XamWorkaround; to your cs file where you want to use it, once that is done your audio unit instances should be able to access SetLatency and GetLatency.

Thank you again for your enhancement request, I hope this unblocks you and I'll wait very impatiently 馃槈 for your response.

using System;
using System.Runtime.InteropServices;
using AudioUnit;
using ObjCRuntime;

namespace XamWorkaround {
    public static class AULatencyExtensions {

        [DllImport (Constants.AudioUnitLibrary)]
        static extern AudioUnitStatus AudioUnitSetProperty (IntPtr inUnit, AudioUnitPropertyIDType inID, AudioUnitScopeType inScope, uint inElement, ref double inData, uint inDataSize);

        public static AudioUnitStatus SetLatency (this AudioUnit.AudioUnit audioUnit, double latency, AudioUnitScopeType scope = AudioUnitScopeType.Global)
        {
            // ElementCount: Float64
            return AudioUnitSetProperty (audioUnit.Handle, AudioUnitPropertyIDType.Latency, scope, 0, ref latency, sizeof (double));
        }

        [DllImport (Constants.AudioUnitLibrary)]
        static extern AudioUnitStatus AudioUnitGetProperty (IntPtr inUnit, AudioUnitPropertyIDType inID, AudioUnitScopeType inScope, uint inElement, ref double outData, ref uint ioDataSize);

        public static double GetLatency (this AudioUnit.AudioUnit audioUnit, AudioUnitScopeType scope = AudioUnitScopeType.Global)
        {
            uint size = sizeof (double);
            double latency = 0;
            var err = AudioUnitGetProperty (audioUnit.Handle, AudioUnitPropertyIDType.Latency, scope, 0, ref latency, ref size);
            if (err != 0)
                throw new Exception (err.ToString ());
            return latency;
        }
    }
}

All 4 comments

Yes it does not look like src/AudioUnit/AudioUnit.cs has anything for the latency.

We'd need something similar to https://github.com/xamarin/xamarin-macios/blob/master/src/AudioUnit/AudioUnit.cs#L539-L552

This is a good first PR for anyone who'd like to contribute.

Also please note that as a _workaround_, if you really need to, you could just copy one of those dll imports in your code: https://github.com/xamarin/xamarin-macios/blob/master/src/AudioUnit/AudioUnit.cs#L1048-L1080 in order to use AudioUnitGetProperty.

Hello @orryverducci, thank you for your feedback!

We want to add the API you need in PR #5221 but I need confirmation from you that said API works as expected, I have prepared a class that you can add to your current application with almost identical logic as PR #5221, please give this a try and let us know if this resolves you issue and works for you as expected (Bonus points if you could provide us with a small test case demoing this API). If you confirm that this works as expected, then I'll be able to merge said PR.

All you need to do is add the following class to your project and add using XamWorkaround; to your cs file where you want to use it, once that is done your audio unit instances should be able to access SetLatency and GetLatency.

Thank you again for your enhancement request, I hope this unblocks you and I'll wait very impatiently 馃槈 for your response.

using System;
using System.Runtime.InteropServices;
using AudioUnit;
using ObjCRuntime;

namespace XamWorkaround {
    public static class AULatencyExtensions {

        [DllImport (Constants.AudioUnitLibrary)]
        static extern AudioUnitStatus AudioUnitSetProperty (IntPtr inUnit, AudioUnitPropertyIDType inID, AudioUnitScopeType inScope, uint inElement, ref double inData, uint inDataSize);

        public static AudioUnitStatus SetLatency (this AudioUnit.AudioUnit audioUnit, double latency, AudioUnitScopeType scope = AudioUnitScopeType.Global)
        {
            // ElementCount: Float64
            return AudioUnitSetProperty (audioUnit.Handle, AudioUnitPropertyIDType.Latency, scope, 0, ref latency, sizeof (double));
        }

        [DllImport (Constants.AudioUnitLibrary)]
        static extern AudioUnitStatus AudioUnitGetProperty (IntPtr inUnit, AudioUnitPropertyIDType inID, AudioUnitScopeType inScope, uint inElement, ref double outData, ref uint ioDataSize);

        public static double GetLatency (this AudioUnit.AudioUnit audioUnit, AudioUnitScopeType scope = AudioUnitScopeType.Global)
        {
            uint size = sizeof (double);
            double latency = 0;
            var err = AudioUnitGetProperty (audioUnit.Handle, AudioUnitPropertyIDType.Latency, scope, 0, ref latency, ref size);
            if (err != 0)
                throw new Exception (err.ToString ());
            return latency;
        }
    }
}

Thanks @dalexsoto for addressing this so quickly.

I've had tested it in my application and can confirm that it is working as expected. My only bit of feedback is the set and get methods allow you to specify an AudioUnitScopeType, but only AudioUnitScopeType.Global is valid for latency (all others return an invalid scope error). For simplicity it might be sensible not to provide this option and always use AudioUnitScopeType.Global.

Regarding a test case I don't have anything I could provide right now, but if one is required it should be possible to use AudioComponent to retrieve all the system provided AudioUnit's, and use this latency property to get their provided latency values.

@orryverducci Thank you for your feedback!

Was this page helpful?
0 / 5 - 0 ratings