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