/* * 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; 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. /// 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 bool _disposed = false; private EventHandler _focusStateChanged; private Interop.SoundStreamFocusStateChangedCallback _focusStateChangedCallback; /// /// Creates and returns an AudioStreamPolicy object /// /// /// 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.) /// /// Type of sound stream for which policy needs to be created /// StreamPolicy object public AudioStreamPolicy(AudioStreamType streamType) { _streamType = 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); }; int ret = Interop.AudioStreamPolicy.CreateStreamInformation((int)streamType, _focusStateChangedCallback, IntPtr.Zero, out _streamInfo); AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to create stream information"); } ~AudioStreamPolicy() { Dispose(false); } /// /// Registers the watch function to be invoked when the focus state for each sound stream type is changed regardless of the process. /// /// Remarks: You can set this only once per process. /// /// 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(); } } } /// /// Registers the watch function to be invoked when the focus state for each sound stream type is changed regardless of the process. /// /// Remarks: You can set this only once per process. /// /// public static event EventHandler RecordingFocusStateWatch { add { if(_focusStateWatchCounter == 0) { RegisterFocusStateWatchEvent(); } _focusStateWatchCounter++; _focusStateWatchForRecording += value; } remove { _focusStateWatchForRecording -= value; _focusStateWatchCounter--; if(_focusStateWatchCounter == 0) { UnregisterFocusStateWatch(); } } } /// /// Registers function to be called when the state of focus that belongs to the current /// streamInfo is changed. /// /// /// 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. /// public event EventHandler StreamFocusStateChanged { add { _focusStateChanged += value; } remove { _focusStateChanged -= value; } } /// /// 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; } return soundType; } } /// /// The state of focus for playback. /// 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 state of focus for recording. /// 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; } } /// /// Auto focus reacquisition property /// /// /// 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. /// 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; } } /// /// Acquires the stream focus. /// /// The focus mask that user wants to acquire /// The required action for releaser /// The Extra information for this request (optional, this can be null) /// /// Do not call this API within event handlers of FocuStateChanged and StreamFocusStateWatch else it will throw and exception /// public void AcquireFocus(AudioStreamFocusOptions options, AudioStreamBehavior audioStreamBehavior, string extraInformation) { 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"); } /// /// Releases the acquired focus. /// /// The focus mask that user wants to release /// The required action for acquirer /// he Extra information for this request (optional, this can be null) /// /// Do not call this API within event handlers of FocuStateChanged and StreamFocusStateWatch else it will throw and exception /// public void ReleaseFocus(AudioStreamFocusOptions options, AudioStreamBehavior audioStreamBehavior, string extraInformation) { 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"); } /// /// Applies the stream routing. /// /// /// 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. /// public void ApplyStreamRouting() { int ret = Interop.AudioStreamPolicy.ApplyStreamRouting(_streamInfo); Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Apply Routing: " + (AudioManagerError)ret); AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to apply stream routing"); } /// /// Adds the device to the stream information for the stream routing. /// /// /// 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 device item from the current sound devices list. public void AddDeviceForStreamRouting(AudioDevice soundDevice) { int ret = Interop.AudioStreamPolicy.AddDeviceForStreamRouting(_streamInfo, soundDevice.Handle); Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Add stream routing: " + (AudioManagerError)ret); AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to add device for stream routing"); } /// /// Removes the device to the stream information for the stream routing. /// /// /// 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 device item from the current sound devices list. public void RemoveDeviceForStreamRouting(AudioDevice soundDevice) { 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"); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } 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; } _disposed = true; } } private static void RegisterFocusStateWatchEvent() { _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); } }; 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"); } private static void UnregisterFocusStateWatch() { int ret = Interop.AudioStreamPolicy.RemoveFocusStateWatchCallback(_focusWatchCbId); AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to unset focus state watch callback"); } } }