Release 4.0.0-preview1-00201
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia / AudioManager / AudioStreamPolicy.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 using System;
18 using System.Diagnostics;
19
20 namespace Tizen.Multimedia
21 {
22     /// <summary>
23     /// Provides the ability to control the sound stream.
24     /// </summary>
25     public class AudioStreamPolicy : IDisposable
26     {
27         private AudioStreamPolicyHandle _handle;
28         private bool _disposed = false;
29         private Interop.AudioStreamPolicy.FocusStateChangedCallback _focusStateChangedCallback;
30
31         /// <summary>
32         /// Initializes a new instance of the <see cref="AudioStreamPolicy"/> class with <see cref="AudioStreamType"/>
33         /// </summary>
34         /// <remarks>
35         /// To apply the stream policy according to this stream information, the AudioStreamPolicy should
36         /// be passed to other APIs related to playback or recording. (For example., <see cref="Player"/>, <see cref="WavPlayer"/> , etc.)
37         /// </remarks>
38         /// <param name="streamType">The type of the sound stream for which the policy needs to be created.</param>
39         /// <exception cref="ArgumentException"><paramref name="streamType"/> is invalid.</exception>
40         public AudioStreamPolicy(AudioStreamType streamType)
41         {
42             ValidationUtil.ValidateEnum(typeof(AudioStreamType), streamType, nameof(streamType));
43
44             _focusStateChangedCallback = (IntPtr streamInfo, AudioStreamFocusOptions focusMask,
45                 AudioStreamFocusState state, AudioStreamFocusChangedReason reason, AudioStreamBehaviors behaviors,
46                 string extraInfo, IntPtr userData) =>
47             {
48                 FocusStateChanged?.Invoke(this,
49                     new AudioStreamPolicyFocusStateChangedEventArgs(focusMask, state, reason, behaviors, extraInfo));
50             };
51
52             Interop.AudioStreamPolicy.Create(streamType, _focusStateChangedCallback,
53                 IntPtr.Zero, out _handle).Validate("Unable to create stream information");
54
55             Debug.Assert(_handle != null);
56         }
57
58         /// <summary>
59         /// Occurs when the state of focus that belongs to the current AudioStreamPolicy is changed.
60         /// </summary>
61         /// <remarks>
62         /// The event is raised in the internal thread.
63         /// </remarks>
64         public event EventHandler<AudioStreamPolicyFocusStateChangedEventArgs> FocusStateChanged;
65
66         /// <summary>
67         /// Gets the <see cref="AudioVolumeType"/>.
68         /// </summary>
69         /// <remarks>
70         /// If the <see cref="AudioStreamType"/> of the current AudioStreamPolicy is <see cref="AudioStreamType.Emergency"/>,
71         /// it returns <see cref="AudioVolumeType.None"/>.
72         /// </remarks>
73         /// <value>The <see cref="AudioVolumeType"/> of the policy instance.</value>
74         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
75         public AudioVolumeType VolumeType
76         {
77             get
78             {
79                 AudioVolumeType type;
80                 var ret = Interop.AudioStreamPolicy.GetSoundType(Handle, out type);
81                 if (ret == AudioManagerError.NoData)
82                 {
83                     return AudioVolumeType.None;
84                 }
85
86                 ret.Validate("Failed to get volume type");
87
88                 return type;
89             }
90         }
91
92         private AudioStreamFocusState GetFocusState(bool playback)
93         {
94             int ret = Interop.AudioStreamPolicy.GetFocusState(Handle, out var stateForPlayback, out var stateForRecording);
95             MultimediaDebug.AssertNoError(ret);
96
97             return playback ? stateForPlayback : stateForRecording;
98         }
99
100         /// <summary>
101         /// Gets the state of focus for the playback.
102         /// </summary>
103         /// <value>The state of focus for playback.</value>
104         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
105         public AudioStreamFocusState PlaybackFocusState => GetFocusState(true);
106
107         /// <summary>
108         /// Gets the state of focus for the recording.
109         /// </summary>
110         /// <value>The state of focus for recording.</value>
111         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
112         public AudioStreamFocusState RecordingFocusState => GetFocusState(false);
113
114         /// <summary>
115         /// Gets or sets the auto focus reacquisition.
116         /// </summary>
117         /// <value>
118         /// true if the auto focus reacquisition is enabled; otherwise, false.\n
119         /// The default is true.
120         /// </value>
121         /// <remarks>
122         /// If you don't want to reacquire the focus you've lost automatically,
123         /// disable the focus reacquisition.
124         /// </remarks>
125         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
126         public bool FocusReacquisitionEnabled
127         {
128             get
129             {
130                 Interop.AudioStreamPolicy.GetFocusReacquisition(Handle, out var enabled).
131                     Validate("Failed to get focus reacquisition state");
132
133                 return enabled;
134             }
135             set
136             {
137                 Interop.AudioStreamPolicy.SetFocusReacquisition(Handle, value).
138                     Validate("Failed to set focus reacquisition");
139             }
140         }
141
142         internal AudioStreamPolicyHandle Handle
143         {
144             get
145             {
146                 if (_disposed)
147                 {
148                     throw new ObjectDisposedException(nameof(AudioStreamPolicy));
149                 }
150                 return _handle;
151             }
152         }
153
154         /// <summary>
155         /// Acquires the stream focus.
156         /// </summary>
157         /// <param name="options">The focuses that you want to acquire.</param>
158         /// <param name="behaviors">The requesting behaviors.</param>
159         /// <param name="extraInfo">The extra information for this request. This value can be null.</param>
160         /// <exception cref="ArgumentException"><paramref name="options"/> is zero.</exception>
161         /// <exception cref="ArgumentOutOfRangeException">
162         ///     <paramref name="options"/> contain a invalid bit.\n
163         ///     -or-\n
164         ///     <paramref name="behaviors"/> contain a invalid bit.\n
165         /// </exception>
166         /// <exception cref="InvalidOperationException">The focus has already been acquired.</exception>
167         /// <exception cref="AudioPolicyException">Called in <see cref="FocusStateChanged"/> raised by releasing focus.</exception>
168         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
169         public void AcquireFocus(AudioStreamFocusOptions options, AudioStreamBehaviors behaviors, string extraInfo)
170         {
171             if (options == 0)
172             {
173                 throw new ArgumentException("options can't be zero.", nameof(options));
174             }
175
176             if (options.IsValid() == false)
177             {
178                 throw new ArgumentOutOfRangeException(nameof(options), options, "options contains a invalid bit.");
179             }
180
181             if (behaviors.IsValid() == false)
182             {
183                 throw new ArgumentOutOfRangeException(nameof(behaviors), behaviors, "behaviors contains a invalid bit.");
184             }
185
186             Interop.AudioStreamPolicy.AcquireFocus(Handle, options, behaviors, extraInfo).
187                 Validate("Failed to acquire focus");
188         }
189
190         /// <summary>
191         /// Releases the acquired focus.
192         /// </summary>
193         /// <param name="options">The focus mask that you want to release.</param>
194         /// <param name="behaviors">The requesting behaviors.</param>
195         /// <param name="extraInfo">The extra information for this request. This value can be null.</param>
196         /// <exception cref="ArgumentException"><paramref name="options"/> is zero.</exception>
197         /// <exception cref="ArgumentOutOfRangeException">
198         ///     <paramref name="options"/> contain a invalid bit.\n
199         ///     -or-\n
200         ///     <paramref name="behaviors"/> contain a invalid bit.\n
201         /// </exception>
202         /// <exception cref="InvalidOperationException">The focus has not been acquired.</exception>
203         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
204         public void ReleaseFocus(AudioStreamFocusOptions options, AudioStreamBehaviors behaviors, string extraInfo)
205         {
206             if (options == 0)
207             {
208                 throw new ArgumentException("options can't be zero.", nameof(options));
209             }
210
211             if (options.IsValid() == false)
212             {
213                 throw new ArgumentOutOfRangeException(nameof(options), options, "options contains a invalid bit.");
214             }
215
216             if (behaviors.IsValid() == false)
217             {
218                 throw new ArgumentOutOfRangeException(nameof(behaviors), behaviors, "behaviors contains a invalid bit.");
219             }
220
221             Interop.AudioStreamPolicy.ReleaseFocus(Handle, options, behaviors, extraInfo).
222                 Validate("Failed to release focus");
223         }
224
225         /// <summary>
226         /// Applies the stream routing.
227         /// </summary>
228         /// <remarks>
229         /// If the stream has not been made yet, this will be applied when the stream starts to play.
230         /// </remarks>
231         /// <seealso cref="AddDeviceForStreamRouting(AudioDevice)"/>
232         /// <seealso cref="RemoveDeviceForStreamRouting(AudioDevice)"/>
233         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
234         public void ApplyStreamRouting()
235         {
236             Interop.AudioStreamPolicy.ApplyStreamRouting(Handle).Validate("Failed to apply stream routing");
237         }
238
239         /// <summary>
240         /// Adds a device for the stream routing.
241         /// </summary>
242         /// <param name="device">The device to add.</param>
243         /// <remarks>
244         /// The available <see cref="AudioStreamType"/> is <see cref="AudioStreamType.Voip"/> and <see cref="AudioStreamType.MediaExternalOnly"/>.
245         /// </remarks>
246         /// <exception cref="InvalidOperationException">
247         ///     The device is not connected.\n
248         ///     -or-\n
249         ///     An internal error occurs.
250         /// </exception>
251         /// <exception cref="ArgumentNullException"><paramref name="device"> is null.</exception>
252         /// <exception cref="AudioPolicyException"><see cref="AudioStreamType"/> of <paramref name="device"/> is unavailable for this.</exception>
253         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
254         /// <seealso cref="AudioManager.GetConnectedDevices()"/>
255         /// <seealso cref="ApplyStreamRouting"/>
256         public void AddDeviceForStreamRouting(AudioDevice device)
257         {
258             if (device == null)
259             {
260                 throw new ArgumentNullException(nameof(device));
261             }
262
263             var ret = Interop.AudioStreamPolicy.AddDeviceForStreamRouting(Handle, device.Id);
264
265             if (ret == AudioManagerError.NoData)
266             {
267                 throw new InvalidOperationException("The device seems not connected.");
268             }
269
270             ret.Validate("Failed to add device for stream routing");
271         }
272
273         /// <summary>
274         /// Removes the device for the stream routing.
275         /// </summary>
276         /// <param name="device">The device to remove.</param>
277         /// <remarks>
278         /// The available <see cref="AudioStreamType"/> is <see cref="AudioStreamType.Voip"/> and <see cref="AudioStreamType.MediaExternalOnly"/>.
279         /// </remarks>
280         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
281         /// <exception cref="ArgumentNullException"><paramref name="device"> is null.</exception>
282         /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
283         /// <seealso cref="AudioManager.GetConnectedDevices()"/>
284         public void RemoveDeviceForStreamRouting(AudioDevice device)
285         {
286             if (device == null)
287             {
288                 throw new ArgumentNullException(nameof(device));
289             }
290
291             Interop.AudioStreamPolicy.RemoveDeviceForStreamRouting(Handle, device.Id).
292                 Validate("Failed to remove device for stream routing");
293         }
294
295         /// <summary>
296         /// Releases all resources used by the <see cref="AudioStreamPolicy"/>.
297         /// </summary>
298         public void Dispose()
299         {
300             Dispose(true);
301         }
302
303         /// <summary>
304         /// Releases the unmanaged resources used by the <see cref="AudioStreamPolicy"/>.
305         /// </summary>
306         /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
307         protected virtual void Dispose(bool disposing)
308         {
309             if (!_disposed)
310             {
311                 if (_handle != null)
312                 {
313                     _handle.Dispose();
314                 }
315                 _disposed = true;
316             }
317         }
318
319         #region Static events
320
321         private static bool _isWatchCallbackRegistered;
322         private static EventHandler<StreamFocusStateChangedEventArgs> _streamFocusStateChanged;
323         private static Interop.AudioStreamPolicy.FocusStateWatchCallback _focusStateWatchCallback;
324         private static object _streamFocusEventLock = new object();
325
326         /// <summary>
327         /// Occurs when the focus state for stream types is changed regardless of the process.
328         /// </summary>
329         public static event EventHandler<StreamFocusStateChangedEventArgs> StreamFocusStateChanged
330         {
331             add
332             {
333                 lock (_streamFocusEventLock)
334                 {
335                     if (_isWatchCallbackRegistered == false)
336                     {
337                         RegisterFocusStateWatch();
338                         _isWatchCallbackRegistered = true;
339                     }
340                     _streamFocusStateChanged += value;
341                 }
342             }
343             remove
344             {
345                 lock (_streamFocusEventLock)
346                 {
347                     _streamFocusStateChanged -= value;
348                 }
349             }
350         }
351
352         private static void RegisterFocusStateWatch()
353         {
354             _focusStateWatchCallback = (int id, AudioStreamFocusOptions options, AudioStreamFocusState focusState,
355                 AudioStreamFocusChangedReason reason, string extraInfo, IntPtr userData) =>
356             {
357                 _streamFocusStateChanged?.Invoke(null,
358                     new StreamFocusStateChangedEventArgs(options, focusState, reason, extraInfo));
359             };
360
361             Interop.AudioStreamPolicy.AddFocusStateWatchCallback(
362                 AudioStreamFocusOptions.Playback | AudioStreamFocusOptions.Recording,
363                 _focusStateWatchCallback, IntPtr.Zero, out var cbId).
364                 Validate("Failed to initialize focus state event");
365         }
366         #endregion
367     }
368 }