X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2FTizen.Multimedia%2FAudioManager%2FAudioStreamPolicy.cs;h=5a50f737b0480c42b7232e9e44224fc36c9eaaca;hb=4c05cfedbfe61d66ffdfefe61e0bb41df720dd9a;hp=b914c9c12393099858f9f2f7642cf7b6e3ed58bd;hpb=60a6696cc226d99a69aa13bf4060c7bdcc85e398;p=platform%2Fcore%2Fcsapi%2Ftizenfx.git diff --git a/src/Tizen.Multimedia/AudioManager/AudioStreamPolicy.cs b/src/Tizen.Multimedia/AudioManager/AudioStreamPolicy.cs old mode 100755 new mode 100644 index b914c9c..5a50f73 --- a/src/Tizen.Multimedia/AudioManager/AudioStreamPolicy.cs +++ b/src/Tizen.Multimedia/AudioManager/AudioStreamPolicy.cs @@ -1,4 +1,4 @@ - /* +/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); @@ -15,320 +15,486 @@ */ using System; +using System.Diagnostics; namespace Tizen.Multimedia { - internal static class AudioStreamPolicyLog - { - internal const string Tag = "Tizen.Multimedia.AudioStreamPolicy"; - } - /// - /// The Stream Policy API provides functions to control a sound stream. + /// Provides the ability to control the sound stream. /// + /// 3 public class AudioStreamPolicy : IDisposable { - private static int _focusStateWatchCounter = 0; - private static EventHandler _focusStateWatchForPlayback; - private static EventHandler _focusStateWatchForRecording; - private static Interop.SoundStreamFocusStateWatchCallback _focusStateWatchCallback; - private static int _focusWatchCbId; - - private IntPtr _streamInfo; - private AudioStreamType _streamType; + private AudioStreamPolicyHandle _handle; private bool _disposed = false; - private EventHandler _focusStateChanged; - private Interop.SoundStreamFocusStateChangedCallback _focusStateChangedCallback; + private Interop.AudioStreamPolicy.FocusStateChangedCallback _focusStateChangedCallback; + private static AudioDevice _inputDevice = null; + private static AudioDevice _outputDevice = null; + private const string Tag = "Tizen.Multimedia.AudioStreamPolicy"; /// - /// Creates and returns an AudioStreamPolicy object + /// Initializes a new instance of the class with . /// /// - /// To apply the stream policy according to this stream information, this object should be passed to other APIs - /// related to playback or recording. (e.g., player, wav-player, audio-io, etc.) + /// To apply the stream policy according to this stream information, the AudioStreamPolicy should + /// be passed to other APIs related to playback or recording. (For example., , + /// , etc.) /// - /// Type of sound stream for which policy needs to be created - /// StreamPolicy object + /// The type of the sound stream for which the policy needs to be created. + /// is invalid. + /// 3 public AudioStreamPolicy(AudioStreamType streamType) { - _streamType = streamType; + ValidationUtil.ValidateEnum(typeof(AudioStreamType), streamType, nameof(streamType)); - _focusStateChangedCallback = (IntPtr streamInfo, AudioStreamFocusOptions focusMask, AudioStreamFocusState focusState, int reason, int audioStreamBehavior, string extraInfo, IntPtr userData) => { - StreamFocusStateChangedEventArgs eventArgs = new StreamFocusStateChangedEventArgs((AudioStreamFocusChangedReason)reason, extraInfo); - _focusStateChanged?.Invoke(this, eventArgs); + _focusStateChangedCallback = (IntPtr streamInfo, AudioStreamFocusOptions focusMask, + AudioStreamFocusState state, AudioStreamFocusChangedReason reason, AudioStreamBehaviors behaviors, + string extraInfo, IntPtr _) => + { + FocusStateChanged?.Invoke(this, + new AudioStreamPolicyFocusStateChangedEventArgs(focusMask, state, reason, behaviors, extraInfo)); }; - int ret = Interop.AudioStreamPolicy.CreateStreamInformation((int)streamType, _focusStateChangedCallback, IntPtr.Zero, out _streamInfo); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to create stream information"); - } - ~AudioStreamPolicy() - { - Dispose(false); + Interop.AudioStreamPolicy.Create(streamType, _focusStateChangedCallback, + IntPtr.Zero, out _handle).ThrowIfError("Unable to create stream information"); + + Debug.Assert(_handle != null); } /// - /// Registers the watch function to be invoked when the focus state for each sound stream type is changed regardless of the process. + /// Occurs when the state of focus that belongs to the current AudioStreamPolicy is changed. + /// /// - /// Remarks: You can set this only once per process. + /// The event is raised in the internal thread. /// - /// - public static event EventHandler PlaybackFocusStateWatch { - add { - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _focusStateWatchCounter" + _focusStateWatchCounter); - if(_focusStateWatchCounter == 0) { - RegisterFocusStateWatchEvent(); - } - _focusStateWatchCounter++; - _focusStateWatchForPlayback += value; - } - remove { - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _focusStateWatchCounter" + _focusStateWatchCounter); - _focusStateWatchForPlayback -= value; - _focusStateWatchCounter--; - if(_focusStateWatchCounter == 0) { - UnregisterFocusStateWatch(); - } - } - } + /// 4 + public event EventHandler FocusStateChanged; /// - /// Registers the watch function to be invoked when the focus state for each sound stream type is changed regardless of the process. + /// Gets the . + /// /// - /// Remarks: You can set this only once per process. + /// If the of the current AudioStreamPolicy is , + /// it returns . /// - /// - public static event EventHandler RecordingFocusStateWatch { - add { - if(_focusStateWatchCounter == 0) { - RegisterFocusStateWatchEvent(); - } - _focusStateWatchCounter++; - _focusStateWatchForRecording += value; - } - remove { - _focusStateWatchForRecording -= value; - _focusStateWatchCounter--; - if(_focusStateWatchCounter == 0) { - UnregisterFocusStateWatch(); + /// The of the policy instance. + /// The has already been disposed of. + /// 3 + public AudioVolumeType VolumeType + { + get + { + var ret = Interop.AudioStreamPolicy.GetSoundType(Handle, out var type); + if (ret == AudioManagerError.NoData) + { + return AudioVolumeType.None; } + + ret.ThrowIfError("Failed to get volume type"); + + return type; } } + private AudioStreamFocusState GetFocusState(bool playback) + { + int ret = Interop.AudioStreamPolicy.GetFocusState(Handle, out var stateForPlayback, out var stateForRecording); + MultimediaDebug.AssertNoError(ret); + + return playback ? stateForPlayback : stateForRecording; + } + + /// + /// Gets the state of focus for the playback. + /// + /// The state of focus for playback. + /// The has already been disposed of. + /// 3 + public AudioStreamFocusState PlaybackFocusState => GetFocusState(true); + /// - /// Registers function to be called when the state of focus that belongs to the current - /// streamInfo is changed. + /// Gets the state of focus for the recording. /// + /// The state of focus for recording. + /// The has already been disposed of. + /// 3 + public AudioStreamFocusState RecordingFocusState => GetFocusState(false); + + /// + /// Gets or sets the auto focus reacquisition. + /// + /// + /// true if the auto focus reacquisition is enabled; otherwise, false.
+ /// The default is true. + ///
/// - /// Remarks: This function is issued in the internal thread of the sound manager. Therefore it is recommended not to call UI update function in this function. - /// Postcondition : Check PlaybackFocusState and RecordingFocusState in the registered event handler to figure out how the focus state of the StreamInfo has been changed. + /// If you don't want to reacquire the focus you've lost automatically, + /// disable the focus reacquisition. /// - public event EventHandler StreamFocusStateChanged { - add { - _focusStateChanged += value; + /// The has already been disposed of. + /// 3 + public bool FocusReacquisitionEnabled + { + get + { + Interop.AudioStreamPolicy.GetFocusReacquisition(Handle, out var enabled). + ThrowIfError("Failed to get focus reacquisition state"); + + return enabled; } - remove { - _focusStateChanged -= value; + set + { + Interop.AudioStreamPolicy.SetFocusReacquisition(Handle, value). + ThrowIfError("Failed to set focus reacquisition"); } } - /// - /// The sound type of the stream information. - /// - public AudioVolumeType VolumeType { - get { - AudioVolumeType soundType; - int ret = Interop.AudioStreamPolicy.GetSoundType(_streamInfo, out soundType); - if(ret != 0) { - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Unable to get sound type:" + (AudioManagerError)ret); - return AudioVolumeType.None; + internal AudioStreamPolicyHandle Handle + { + get + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(AudioStreamPolicy)); } - return soundType; + return _handle; } } /// - /// The state of focus for playback. + /// Acquires the stream focus. /// - public AudioStreamFocusState PlaybackFocusState { - get { - AudioStreamFocusState stateForPlayback; - AudioStreamFocusState stateForRecording; - int ret = Interop.AudioStreamPolicy.GetFocusState(_streamInfo, out stateForPlayback, out stateForRecording); - if(ret != 0) { - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Unable to get focus state" + (AudioManagerError)ret); - return AudioStreamFocusState.Released; - } - return stateForPlayback; + /// The focuses that you want to acquire. + /// The requesting behaviors. + /// The extra information for this request. This value can be null. + /// is zero. + /// + /// contain a invalid bit.
+ /// -or-
+ /// contain a invalid bit. + ///
+ /// The focus has already been acquired. + /// Called in raised by releasing focus. + /// The has already been disposed of. + /// 3 + public void AcquireFocus(AudioStreamFocusOptions options, AudioStreamBehaviors behaviors, string extraInfo) + { + if (options == 0) + { + throw new ArgumentException("options can't be zero.", nameof(options)); + } + + if (options.IsValid() == false) + { + throw new ArgumentOutOfRangeException(nameof(options), options, "options contains a invalid bit."); } + + if (behaviors.IsValid() == false) + { + throw new ArgumentOutOfRangeException(nameof(behaviors), behaviors, "behaviors contains a invalid bit."); + } + + Interop.AudioStreamPolicy.AcquireFocus(Handle, options, behaviors, extraInfo). + ThrowIfError("Failed to acquire focus"); } /// - /// The state of focus for recording. + /// Releases the acquired focus. /// - public AudioStreamFocusState RecordingFocusState { - get { - AudioStreamFocusState stateForPlayback; - AudioStreamFocusState stateForRecording; - int ret = Interop.AudioStreamPolicy.GetFocusState(_streamInfo, out stateForPlayback, out stateForRecording); - if(ret != 0) { - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Unable to get focus state" + (AudioManagerError)ret); - return AudioStreamFocusState.Released; - } - return stateForRecording; + /// The focus mask that you want to release. + /// The requesting behaviors. + /// The extra information for this request. This value can be null. + /// is zero. + /// + /// contain a invalid bit.
+ /// -or-
+ /// contain a invalid bit. + ///
+ /// The focus has not been acquired. + /// The has already been disposed of. + /// 3 + public void ReleaseFocus(AudioStreamFocusOptions options, AudioStreamBehaviors behaviors, string extraInfo) + { + if (options == 0) + { + throw new ArgumentException("options can't be zero.", nameof(options)); + } + + if (options.IsValid() == false) + { + throw new ArgumentOutOfRangeException(nameof(options), options, "options contains a invalid bit."); + } + + if (behaviors.IsValid() == false) + { + throw new ArgumentOutOfRangeException(nameof(behaviors), behaviors, "behaviors contains a invalid bit."); } + + Interop.AudioStreamPolicy.ReleaseFocus(Handle, options, behaviors, extraInfo). + ThrowIfError("Failed to release focus"); } /// - /// Auto focus reacquisition property + /// Applies the stream routing. /// /// - /// The focus reacquistion is set as default. If you don't want to reacquire the focus you've lost automatically, disable the focus reacqusition setting by using this API and vice versa. + /// If the stream has not been made yet, this will be applied when the stream starts to play. /// - public bool FocusReacquisitionEnabled { - get { - bool enabled; - int ret = Interop.AudioStreamPolicy.GetFocusReacquisition(_streamInfo, out enabled); - if(ret != 0) { - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Unable to get focus reacquisition" + (AudioManagerError)ret); - return true; - } - return enabled; - } - set { - int ret = Interop.AudioStreamPolicy.SetFocusReacquisition(_streamInfo, value); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to set focus reacquisition"); - } - } - - public IntPtr Handle { - get { - return _streamInfo; - } + /// + /// + /// The has already been disposed of. + /// 3 + public void ApplyStreamRouting() + { + Interop.AudioStreamPolicy.ApplyStreamRouting(Handle).ThrowIfError("Failed to apply stream routing"); } /// - /// Acquires the stream focus. + /// Adds a device for the stream routing. /// - /// The focus mask that user wants to acquire - /// The required action for releaser - /// The Extra information for this request (optional, this can be null) + /// The device to add. /// - /// Do not call this API within event handlers of FocuStateChanged and StreamFocusStateWatch else it will throw and exception + /// The available is and . /// - public void AcquireFocus(AudioStreamFocusOptions options, AudioStreamBehavior audioStreamBehavior, string extraInformation) + /// + /// The device is not connected.
+ /// -or-
+ /// An internal error occurs. + ///
+ /// is null. + /// of is unavailable for this. + /// The has already been disposed of. + /// + /// + /// 3 + public void AddDeviceForStreamRouting(AudioDevice device) { - int ret = Interop.AudioStreamPolicy.AcquireFocus(_streamInfo, options, (int)audioStreamBehavior, extraInformation); - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Acquire focus return: " + ret); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to acquire focus"); + if (device == null) + { + throw new ArgumentNullException(nameof(device)); + } + + var ret = Interop.AudioStreamPolicy.AddDeviceForStreamRouting(Handle, device.Id); + + if (ret == AudioManagerError.NoData) + { + throw new InvalidOperationException("The device seems not connected."); + } + + ret.ThrowIfError("Failed to add device for stream routing"); } /// - /// Releases the acquired focus. + /// Removes the device for the stream routing. /// - /// The focus mask that user wants to release - /// The required action for acquirer - /// he Extra information for this request (optional, this can be null) + /// The device to remove. /// - /// Do not call this API within event handlers of FocuStateChanged and StreamFocusStateWatch else it will throw and exception + /// The available is and . /// - public void ReleaseFocus(AudioStreamFocusOptions options, AudioStreamBehavior audioStreamBehavior, string extraInformation) + /// An internal error occurs. + /// is null. + /// The has already been disposed of. + /// + /// 3 + public void RemoveDeviceForStreamRouting(AudioDevice device) { - int ret = Interop.AudioStreamPolicy.ReleaseFocus(_streamInfo, options, (int)audioStreamBehavior, extraInformation); - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Release focus return: " + ret); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to release focus"); + if (device == null) + { + throw new ArgumentNullException(nameof(device)); + } + + Interop.AudioStreamPolicy.RemoveDeviceForStreamRouting(Handle, device.Id). + ThrowIfError("Failed to remove device for stream routing"); } /// - /// Applies the stream routing. + /// Gets or sets the preferred input device. /// + /// + /// The instance.
+ /// The default is null which means any device is not set on this property. + ///
/// - /// If the stream has not been made yet, this setting will be applied when the stream starts to play. - /// Precondition: Call AddDeviceForStreamRouting() before calling this function. + /// This property is to set a specific built-in device when the system has multiple devices of the same built-in device type. + /// When there's only one device for a built-in device type in the system, nothing will happen even if this property is set successfully. /// - public void ApplyStreamRouting() + /// A device is not for input. + /// An internal error occurs. + /// A device is not supported by this instance. + /// The has already been disposed of. + /// + /// 6 + public AudioDevice PreferredInputDevice { - int ret = Interop.AudioStreamPolicy.ApplyStreamRouting(_streamInfo); - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Apply Routing: " + (AudioManagerError)ret); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to apply stream routing"); + get + { + /* This P/Invoke intends to validate if the core audio system + * is normal. Otherwise, it'll throw an error here. */ + Interop.AudioStreamPolicy.GetPreferredDevice(Handle, out var inDeviceId, out _). + ThrowIfError("Failed to get preferred input device"); + + Log.Debug(Tag, $"preferred input device id:{inDeviceId}"); + + return _inputDevice; + } + set + { + Interop.AudioStreamPolicy.SetPreferredDevice(Handle, AudioDeviceIoDirection.Input, value?.Id ?? 0). + ThrowIfError("Failed to set preferred input device"); + + _inputDevice = value; + } } /// - /// Adds the device to the stream information for the stream routing. + /// Gets or sets the preferred output device. /// + /// + /// The instance.
+ /// The default is null which means any device is not set on this property. + ///
/// - /// Remarks: Use SoundManager.GetCurrentDeviceList() to get the device. - /// The available types of the StreamInfo for this API are SoundStreamTypeVoip and SoundStreamTypeMediaExternalOnly. - /// Postcondition: You can apply this setting by calling ApplyStreamRouting(). + /// This property is to set a specific built-in device when the system has multiple devices of the same built-in device type. + /// When there's only one device for a built-in device type in the system, nothing will happen even if this property is set successfully. /// - /// The device item from the current sound devices list. - public void AddDeviceForStreamRouting(AudioDevice soundDevice) + /// A device is not for output. + /// An internal error occurs. + /// A device is not supported by this instance. + /// The has already been disposed of. + /// + /// 6 + public AudioDevice PreferredOutputDevice { - int ret = Interop.AudioStreamPolicy.AddDeviceForStreamRouting(_streamInfo, soundDevice.Handle); - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Add stream routing: " + (AudioManagerError)ret); + get + { + /* This P/Invoke intends to validate if the core audio system + * is normal. Otherwise, it'll throw an error here. */ + Interop.AudioStreamPolicy.GetPreferredDevice(Handle, out _, out var outDeviceId). + ThrowIfError("Failed to get preferred output device"); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to add device for stream routing"); + Log.Debug(Tag, $"preferred output device id:{outDeviceId}"); + + return _outputDevice; + } + set + { + Interop.AudioStreamPolicy.SetPreferredDevice(Handle, AudioDeviceIoDirection.Output, value?.Id ?? 0). + ThrowIfError("Failed to set preferred output device"); + + _outputDevice = value; + } } /// - /// Removes the device to the stream information for the stream routing. + /// Checks if any stream from the current AudioStreamPolicy is using the device. /// + /// true if any audio stream from the current AudioStreamPolicy is using the device; otherwise, false. + /// The device to be checked. /// - /// Remarks: Use SoundManager.GetCurrentDeviceList() to get the device. - /// The available types of the StreamInfo for this API are SoundStreamTypeVoip and SoundStreamTypeMediaExternalOnly. - /// Postcondition: You can apply this setting by calling ApplyStreamRouting(). + /// The AudioStreamPolicy can be applied to each playback or recording stream via other API set. + /// (For example., , , + /// , , etc.) + /// This method returns true only when the device is used for the stream which meets to the two conditions. + /// One is that the current AudioStreamPolicy sets a audio route path to the device and the other is that the playback + /// or recording stream from other API set should have already started to prepare or to play.(It depends on the API set.) /// - /// The device item from the current sound devices list. - public void RemoveDeviceForStreamRouting(AudioDevice soundDevice) + /// is null. + /// An internal error occurs. + /// The has already been disposed of. + /// + /// 6 + public bool HasStreamOnDevice(AudioDevice device) { - int ret = Interop.AudioStreamPolicy.RemoveDeviceForStreamRouting(_streamInfo, soundDevice.Handle); - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Remove stream routing: " + (AudioManagerError)ret); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to remove device for stream routing"); + if (device == null) + { + throw new ArgumentNullException(nameof(device)); + } + + var ret = Interop.AudioStreamPolicy.IsStreamOnDevice(Handle, device.Id, out var isOn); + ret.ThrowIfError("Failed to check stream on device"); + + return isOn; } + /// + /// Releases all resources used by the . + /// + /// 3 public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } + /// + /// Releases the unmanaged resources used by the . + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// 3 protected virtual void Dispose(bool disposing) { - if(!_disposed) { - if(disposing) { - // to be used if there are any other disposable objects - } - if(_streamInfo != IntPtr.Zero) { - Interop.AudioStreamPolicy.DestroyStreamInformation(_streamInfo); // Destroy the handle - _streamInfo = IntPtr.Zero; + if (_disposed) + { + return; + } + + if (disposing) + { + if (_handle != null) + { + _handle.Dispose(); } _disposed = true; } } - private static void RegisterFocusStateWatchEvent() + #region Static events + + private static bool _isWatchCallbackRegistered; + private static EventHandler _streamFocusStateChanged; + private static Interop.AudioStreamPolicy.FocusStateWatchCallback _focusStateWatchCallback; + private static readonly object _streamFocusEventLock = new object(); + + /// + /// Occurs when the focus state for stream types is changed regardless of the process. + /// + /// 3 + public static event EventHandler StreamFocusStateChanged { - _focusStateWatchCallback = (int id, AudioStreamFocusOptions options, AudioStreamFocusState focusState, AudioStreamFocusChangedReason reason, string extraInfo, IntPtr userData) => { - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _Inside _focusStateWatchCallback : id = " + id + "options = " + options); - FocusStateChangedEventArgs eventArgs = new FocusStateChangedEventArgs(focusState, reason, extraInfo); - if(options == AudioStreamFocusOptions.Playback) { - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _eventArgs = " + eventArgs); - _focusStateWatchForPlayback?.Invoke(null, eventArgs); - } else if(options == AudioStreamFocusOptions.Recording) { - _focusStateWatchForRecording?.Invoke(null, eventArgs); - } else if(options == (AudioStreamFocusOptions.Playback | AudioStreamFocusOptions.Recording)) { - _focusStateWatchForPlayback?.Invoke(null, eventArgs); - _focusStateWatchForRecording?.Invoke(null, eventArgs); + add + { + lock (_streamFocusEventLock) + { + if (_isWatchCallbackRegistered == false) + { + RegisterFocusStateWatch(); + _isWatchCallbackRegistered = true; + } + _streamFocusStateChanged += value; } - }; - int ret = Interop.AudioStreamPolicy.AddFocusStateWatchCallback(AudioStreamFocusOptions.Playback | AudioStreamFocusOptions.Recording, _focusStateWatchCallback, IntPtr.Zero, out _focusWatchCbId); - Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _AddFocusStateWatchCallback : ret = " + ret + " ID = " + _focusWatchCbId); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to set focus state watch callback"); + } + remove + { + lock (_streamFocusEventLock) + { + _streamFocusStateChanged -= value; + } + } } - private static void UnregisterFocusStateWatch() + private static void RegisterFocusStateWatch() { - int ret = Interop.AudioStreamPolicy.RemoveFocusStateWatchCallback(_focusWatchCbId); - AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to unset focus state watch callback"); + _focusStateWatchCallback = (id, options, focusState, reason, extraInfo, _) => + { + _streamFocusStateChanged?.Invoke(null, + new StreamFocusStateChangedEventArgs(options, focusState, reason, extraInfo)); + }; + + Interop.AudioStreamPolicy.AddFocusStateWatchCallback( + AudioStreamFocusOptions.Playback | AudioStreamFocusOptions.Recording, + _focusStateWatchCallback, IntPtr.Zero, out var cbId). + ThrowIfError("Failed to initialize focus state event"); } + #endregion } }