/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Diagnostics; namespace Tizen.Multimedia { /// /// Provides the ability to control the sound stream. /// /// 3 public class AudioStreamPolicy : IDisposable { private AudioStreamPolicyHandle _handle; private bool _disposed = false; private Interop.AudioStreamPolicy.FocusStateChangedCallback _focusStateChangedCallback; private static AudioDevice _inputDevice = null; private static AudioDevice _outputDevice = null; private const string Tag = "Tizen.Multimedia.AudioStreamPolicy"; /// /// Initializes a new instance of the class with . /// /// /// 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.) /// /// The type of the sound stream for which the policy needs to be created. /// is invalid. /// 3 public AudioStreamPolicy(AudioStreamType streamType) { ValidationUtil.ValidateEnum(typeof(AudioStreamType), streamType, nameof(streamType)); _focusStateChangedCallback = (IntPtr streamInfo, AudioStreamFocusOptions focusMask, AudioStreamFocusState state, AudioStreamFocusChangedReason reason, AudioStreamBehaviors behaviors, string extraInfo, IntPtr _) => { FocusStateChanged?.Invoke(this, new AudioStreamPolicyFocusStateChangedEventArgs(focusMask, state, reason, behaviors, extraInfo)); }; Interop.AudioStreamPolicy.Create(streamType, _focusStateChangedCallback, IntPtr.Zero, out _handle).ThrowIfError("Unable to create stream information"); Debug.Assert(_handle != null); } /// /// Occurs when the state of focus that belongs to the current AudioStreamPolicy is changed. /// /// /// The event is raised in the internal thread. /// /// 4 public event EventHandler FocusStateChanged; /// /// Gets the . /// /// /// If the of the current AudioStreamPolicy is , /// it returns . /// /// 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); /// /// 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. ///
/// /// If you don't want to reacquire the focus you've lost automatically, /// disable the focus reacquisition. /// /// 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; } set { Interop.AudioStreamPolicy.SetFocusReacquisition(Handle, value). ThrowIfError("Failed to set focus reacquisition"); } } internal AudioStreamPolicyHandle Handle { get { if (_disposed) { throw new ObjectDisposedException(nameof(AudioStreamPolicy)); } return _handle; } } /// /// Acquires the stream focus. /// /// 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"); } /// /// Releases the acquired focus. /// /// 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"); } /// /// Applies the stream routing. /// /// /// If the stream has not been made yet, this will be applied when the stream starts to play. /// /// /// /// The has already been disposed of. /// 3 public void ApplyStreamRouting() { Interop.AudioStreamPolicy.ApplyStreamRouting(Handle).ThrowIfError("Failed to apply stream routing"); } /// /// Adds a device for the stream routing. /// /// The device to add. /// /// The available is and . /// /// /// 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) { 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"); } /// /// Removes the device for the stream routing. /// /// The device to remove. /// /// The available is and . /// /// An internal error occurs. /// is null. /// The has already been disposed of. /// /// 3 public void RemoveDeviceForStreamRouting(AudioDevice device) { if (device == null) { throw new ArgumentNullException(nameof(device)); } Interop.AudioStreamPolicy.RemoveDeviceForStreamRouting(Handle, device.Id). ThrowIfError("Failed to remove device for 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. ///
/// /// 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. /// /// 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 { 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; } } /// /// Gets or sets the preferred output device. /// /// /// The instance.
/// The default is null which means any device is not set on this property. ///
/// /// 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. /// /// 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 { 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"); 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; } } /// /// 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. /// /// 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.) /// /// is null. /// An internal error occurs. /// The has already been disposed of. /// /// 6 public bool HasStreamOnDevice(AudioDevice device) { 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) { return; } if (disposing) { if (_handle != null) { _handle.Dispose(); } _disposed = true; } } #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 { add { lock (_streamFocusEventLock) { if (_isWatchCallbackRegistered == false) { RegisterFocusStateWatch(); _isWatchCallbackRegistered = true; } _streamFocusStateChanged += value; } } remove { lock (_streamFocusEventLock) { _streamFocusStateChanged -= value; } } } private static void RegisterFocusStateWatch() { _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 } }