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