2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Diagnostics;
20 namespace Tizen.Multimedia
23 /// Provides the ability to control the sound stream.
25 /// <since_tizen> 3 </since_tizen>
26 public class AudioStreamPolicy : IDisposable
28 private AudioStreamPolicyHandle _handle;
29 private bool _disposed = false;
30 private Interop.AudioStreamPolicy.FocusStateChangedCallback _focusStateChangedCallback;
31 private static AudioDevice _inputDevice = null;
32 private static AudioDevice _outputDevice = null;
33 private const string Tag = "Tizen.Multimedia.AudioStreamPolicy";
36 /// Initializes a new instance of the <see cref="AudioStreamPolicy"/> class with <see cref="AudioStreamType"/>.
39 /// To apply the stream policy according to this stream information, the AudioStreamPolicy should
40 /// be passed to other APIs related to playback or recording. (For example., <see cref="T:Tizen.Multimedia.Player"/>,
41 /// <see cref="T:Tizen.Multimedia.WavPlayer"/> , etc.)
43 /// <param name="streamType">The type of the sound stream for which the policy needs to be created.</param>
44 /// <exception cref="ArgumentException"><paramref name="streamType"/> is invalid.</exception>
45 /// <since_tizen> 3 </since_tizen>
46 public AudioStreamPolicy(AudioStreamType streamType)
48 ValidationUtil.ValidateEnum(typeof(AudioStreamType), streamType, nameof(streamType));
50 _focusStateChangedCallback = (IntPtr streamInfo, AudioStreamFocusOptions focusMask,
51 AudioStreamFocusState state, AudioStreamFocusChangedReason reason, AudioStreamBehaviors behaviors,
52 string extraInfo, IntPtr _) =>
54 FocusStateChanged?.Invoke(this,
55 new AudioStreamPolicyFocusStateChangedEventArgs(focusMask, state, reason, behaviors, extraInfo));
58 Interop.AudioStreamPolicy.Create(streamType, _focusStateChangedCallback,
59 IntPtr.Zero, out _handle).ThrowIfError("Unable to create stream information");
61 Debug.Assert(_handle != null);
65 /// Occurs when the state of focus that belongs to the current AudioStreamPolicy is changed.
68 /// The event is raised in the internal thread.
70 /// <since_tizen> 4 </since_tizen>
71 public event EventHandler<AudioStreamPolicyFocusStateChangedEventArgs> FocusStateChanged;
74 /// Gets the <see cref="AudioVolumeType"/>.
77 /// If the <see cref="AudioStreamType"/> of the current AudioStreamPolicy is <see cref="AudioStreamType.Emergency"/>,
78 /// it returns <see cref="AudioVolumeType.None"/>.
80 /// <value>The <see cref="AudioVolumeType"/> of the policy instance.</value>
81 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
82 /// <since_tizen> 3 </since_tizen>
83 public AudioVolumeType VolumeType
87 var ret = Interop.AudioStreamPolicy.GetSoundType(Handle, out var type);
88 if (ret == AudioManagerError.NoData)
90 return AudioVolumeType.None;
93 ret.ThrowIfError("Failed to get volume type");
99 private AudioStreamFocusState GetFocusState(bool playback)
101 int ret = Interop.AudioStreamPolicy.GetFocusState(Handle, out var stateForPlayback, out var stateForRecording);
102 MultimediaDebug.AssertNoError(ret);
104 return playback ? stateForPlayback : stateForRecording;
108 /// Gets the state of focus for the playback.
110 /// <value>The state of focus for playback.</value>
111 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
112 /// <since_tizen> 3 </since_tizen>
113 public AudioStreamFocusState PlaybackFocusState => GetFocusState(true);
116 /// Gets the state of focus for the recording.
118 /// <value>The state of focus for recording.</value>
119 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
120 /// <since_tizen> 3 </since_tizen>
121 public AudioStreamFocusState RecordingFocusState => GetFocusState(false);
124 /// Gets or sets the auto focus reacquisition.
127 /// true if the auto focus reacquisition is enabled; otherwise, false.<br/>
128 /// The default is true.
131 /// If you don't want to reacquire the focus you've lost automatically,
132 /// disable the focus reacquisition.
134 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
135 /// <since_tizen> 3 </since_tizen>
136 public bool FocusReacquisitionEnabled
140 Interop.AudioStreamPolicy.GetFocusReacquisition(Handle, out var enabled).
141 ThrowIfError("Failed to get focus reacquisition state");
147 Interop.AudioStreamPolicy.SetFocusReacquisition(Handle, value).
148 ThrowIfError("Failed to set focus reacquisition");
152 internal AudioStreamPolicyHandle Handle
158 throw new ObjectDisposedException(nameof(AudioStreamPolicy));
165 /// Acquires the stream focus.
167 /// <param name="options">The focuses that you want to acquire.</param>
168 /// <param name="behaviors">The requesting behaviors.</param>
169 /// <param name="extraInfo">The extra information for this request. This value can be null.</param>
170 /// <exception cref="ArgumentException"><paramref name="options"/> is zero.</exception>
171 /// <exception cref="ArgumentOutOfRangeException">
172 /// <paramref name="options"/> contain a invalid bit.<br/>
174 /// <paramref name="behaviors"/> contain a invalid bit.
176 /// <exception cref="InvalidOperationException">The focus has already been acquired.</exception>
177 /// <exception cref="AudioPolicyException">Called in <see cref="FocusStateChanged"/> raised by releasing focus.</exception>
178 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
179 /// <since_tizen> 3 </since_tizen>
180 public void AcquireFocus(AudioStreamFocusOptions options, AudioStreamBehaviors behaviors, string extraInfo)
184 throw new ArgumentException("options can't be zero.", nameof(options));
187 if (options.IsValid() == false)
189 throw new ArgumentOutOfRangeException(nameof(options), options, "options contains a invalid bit.");
192 if (behaviors.IsValid() == false)
194 throw new ArgumentOutOfRangeException(nameof(behaviors), behaviors, "behaviors contains a invalid bit.");
197 Interop.AudioStreamPolicy.AcquireFocus(Handle, options, behaviors, extraInfo).
198 ThrowIfError("Failed to acquire focus");
202 /// Releases the acquired focus.
204 /// <param name="options">The focus mask that you want to release.</param>
205 /// <param name="behaviors">The requesting behaviors.</param>
206 /// <param name="extraInfo">The extra information for this request. This value can be null.</param>
207 /// <exception cref="ArgumentException"><paramref name="options"/> is zero.</exception>
208 /// <exception cref="ArgumentOutOfRangeException">
209 /// <paramref name="options"/> contain a invalid bit.<br/>
211 /// <paramref name="behaviors"/> contain a invalid bit.
213 /// <exception cref="InvalidOperationException">The focus has not been acquired.</exception>
214 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
215 /// <since_tizen> 3 </since_tizen>
216 public void ReleaseFocus(AudioStreamFocusOptions options, AudioStreamBehaviors behaviors, string extraInfo)
220 throw new ArgumentException("options can't be zero.", nameof(options));
223 if (options.IsValid() == false)
225 throw new ArgumentOutOfRangeException(nameof(options), options, "options contains a invalid bit.");
228 if (behaviors.IsValid() == false)
230 throw new ArgumentOutOfRangeException(nameof(behaviors), behaviors, "behaviors contains a invalid bit.");
233 Interop.AudioStreamPolicy.ReleaseFocus(Handle, options, behaviors, extraInfo).
234 ThrowIfError("Failed to release focus");
238 /// Applies the stream routing.
241 /// If the stream has not been made yet, this will be applied when the stream starts to play.
243 /// <seealso cref="AddDeviceForStreamRouting(AudioDevice)"/>
244 /// <seealso cref="RemoveDeviceForStreamRouting(AudioDevice)"/>
245 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
246 /// <since_tizen> 3 </since_tizen>
247 public void ApplyStreamRouting()
249 Interop.AudioStreamPolicy.ApplyStreamRouting(Handle).ThrowIfError("Failed to apply stream routing");
253 /// Adds a device for the stream routing.
255 /// <param name="device">The device to add.</param>
257 /// The available <see cref="AudioStreamType"/> is <see cref="AudioStreamType.Voip"/> and <see cref="AudioStreamType.MediaExternalOnly"/>.
259 /// <exception cref="InvalidOperationException">
260 /// The device is not connected.<br/>
262 /// An internal error occurs.
264 /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
265 /// <exception cref="AudioPolicyException"><see cref="AudioStreamType"/> of <paramref name="device"/> is unavailable for this.</exception>
266 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
267 /// <seealso cref="AudioManager.GetConnectedDevices()"/>
268 /// <seealso cref="ApplyStreamRouting"/>
269 /// <since_tizen> 3 </since_tizen>
270 public void AddDeviceForStreamRouting(AudioDevice device)
274 throw new ArgumentNullException(nameof(device));
277 var ret = Interop.AudioStreamPolicy.AddDeviceForStreamRouting(Handle, device.Id);
279 if (ret == AudioManagerError.NoData)
281 throw new InvalidOperationException("The device seems not connected.");
284 ret.ThrowIfError("Failed to add device for stream routing");
288 /// Removes the device for the stream routing.
290 /// <param name="device">The device to remove.</param>
292 /// The available <see cref="AudioStreamType"/> is <see cref="AudioStreamType.Voip"/> and <see cref="AudioStreamType.MediaExternalOnly"/>.
294 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
295 /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
296 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
297 /// <seealso cref="AudioManager.GetConnectedDevices()"/>
298 /// <since_tizen> 3 </since_tizen>
299 public void RemoveDeviceForStreamRouting(AudioDevice device)
303 throw new ArgumentNullException(nameof(device));
306 Interop.AudioStreamPolicy.RemoveDeviceForStreamRouting(Handle, device.Id).
307 ThrowIfError("Failed to remove device for stream routing");
311 /// Gets or sets the preferred input device.
314 /// The <see cref="AudioDevice"/> instance.<br/>
315 /// The default is null which means any device is not set on this property.
318 /// This property is to set a specific built-in device when the system has multiple devices of the same built-in device type.
319 /// 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.
321 /// <exception cref="ArgumentException">A device is not for input.</exception>
322 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
323 /// <exception cref="AudioPolicyException">A device is not supported by this <see cref="AudioStreamPolicy"/> instance.</exception>
324 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
325 /// <seealso cref="AudioManager.GetConnectedDevices()"/>
326 /// <since_tizen> 6 </since_tizen>
327 public AudioDevice PreferredInputDevice
331 /* This P/Invoke intends to validate if the core audio system
332 * is normal. Otherwise, it'll throw an error here. */
333 Interop.AudioStreamPolicy.GetPreferredDevice(Handle, out var inDeviceId, out _).
334 ThrowIfError("Failed to get preferred input device");
336 Log.Debug(Tag, $"preferred input device id:{inDeviceId}");
342 Interop.AudioStreamPolicy.SetPreferredDevice(Handle, AudioDeviceIoDirection.Input, value?.Id ?? 0).
343 ThrowIfError("Failed to set preferred input device");
345 _inputDevice = value;
350 /// Gets or sets the preferred output device.
353 /// The <see cref="AudioDevice"/> instance.<br/>
354 /// The default is null which means any device is not set on this property.
357 /// This property is to set a specific built-in device when the system has multiple devices of the same built-in device type.
358 /// 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.
360 /// <exception cref="ArgumentException">A device is not for output.</exception>
361 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
362 /// <exception cref="AudioPolicyException">A device is not supported by this <see cref="AudioStreamPolicy"/> instance.</exception>
363 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
364 /// <seealso cref="AudioManager.GetConnectedDevices()"/>
365 /// <since_tizen> 6 </since_tizen>
366 public AudioDevice PreferredOutputDevice
370 /* This P/Invoke intends to validate if the core audio system
371 * is normal. Otherwise, it'll throw an error here. */
372 Interop.AudioStreamPolicy.GetPreferredDevice(Handle, out _, out var outDeviceId).
373 ThrowIfError("Failed to get preferred output device");
375 Log.Debug(Tag, $"preferred output device id:{outDeviceId}");
377 return _outputDevice;
381 Interop.AudioStreamPolicy.SetPreferredDevice(Handle, AudioDeviceIoDirection.Output, value?.Id ?? 0).
382 ThrowIfError("Failed to set preferred output device");
384 _outputDevice = value;
389 /// Checks if any stream from the current AudioStreamPolicy is using the device.
391 /// <returns>true if any audio stream from the current AudioStreamPolicy is using the device; otherwise, false.</returns>
392 /// <param name="device">The device to be checked.</param>
394 /// The AudioStreamPolicy can be applied to each playback or recording stream via other API set.
395 /// (For example., <see cref="T:Tizen.Multimedia.Player"/>, <see cref="T:Tizen.Multimedia.WavPlayer"/>,
396 /// <see cref="T:Tizen.Multimedia.AudioPlayback"/>, <see cref="T:Tizen.Multimedia.AudioCapture"/>, etc.)
397 /// This method returns true only when the device is used for the stream which meets to the two conditions.
398 /// One is that the current AudioStreamPolicy sets a audio route path to the device and the other is that the playback
399 /// or recording stream from other API set should have already started to prepare or to play.(It depends on the API set.)
401 /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
402 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
403 /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
404 /// <seealso cref="AudioManager.GetConnectedDevices()"/>
405 /// <since_tizen> 6 </since_tizen>
406 public bool HasStreamOnDevice(AudioDevice device)
410 throw new ArgumentNullException(nameof(device));
413 var ret = Interop.AudioStreamPolicy.IsStreamOnDevice(Handle, device.Id, out var isOn);
414 ret.ThrowIfError("Failed to check stream on device");
420 /// Releases all resources used by the <see cref="AudioStreamPolicy"/>.
422 /// <since_tizen> 3 </since_tizen>
423 public void Dispose()
426 GC.SuppressFinalize(this);
430 /// Releases the unmanaged resources used by the <see cref="AudioStreamPolicy"/>.
432 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
433 /// <since_tizen> 3 </since_tizen>
434 protected virtual void Dispose(bool disposing)
451 #region Static events
453 private static bool _isWatchCallbackRegistered;
454 private static EventHandler<StreamFocusStateChangedEventArgs> _streamFocusStateChanged;
455 private static Interop.AudioStreamPolicy.FocusStateWatchCallback _focusStateWatchCallback;
456 private static readonly object _streamFocusEventLock = new object();
459 /// Occurs when the focus state for stream types is changed regardless of the process.
461 /// <since_tizen> 3 </since_tizen>
462 public static event EventHandler<StreamFocusStateChangedEventArgs> StreamFocusStateChanged
466 lock (_streamFocusEventLock)
468 if (_isWatchCallbackRegistered == false)
470 RegisterFocusStateWatch();
471 _isWatchCallbackRegistered = true;
473 _streamFocusStateChanged += value;
478 lock (_streamFocusEventLock)
480 _streamFocusStateChanged -= value;
485 private static void RegisterFocusStateWatch()
487 _focusStateWatchCallback = (id, options, focusState, reason, extraInfo, _) =>
489 _streamFocusStateChanged?.Invoke(null,
490 new StreamFocusStateChangedEventArgs(options, focusState, reason, extraInfo));
493 Interop.AudioStreamPolicy.AddFocusStateWatchCallback(
494 AudioStreamFocusOptions.Playback | AudioStreamFocusOptions.Recording,
495 _focusStateWatchCallback, IntPtr.Zero, out var cbId).
496 ThrowIfError("Failed to initialize focus state event");