/*
* 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");
}
}
}