[Tizen.Multimedia] Fix XML Doc warnings
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.AudioIO / AudioIO / AudioCapture.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.Runtime.InteropServices;
19 using static Interop.AudioIO;
20
21 namespace Tizen.Multimedia
22 {
23     /// <summary>
24     /// Provides the ability to directly manage the system audio input devices.
25     /// </summary>
26     /// <privilege>http://tizen.org/privilege/recorder</privilege>
27     public abstract class AudioCaptureBase : IDisposable
28     {
29         /// <summary>
30         /// Specifies the minimum value allowed for the audio capture, in Hertz (Hz).
31         /// </summary>
32         /// <seealso cref="SampleRate"/>
33         public static readonly int MinSampleRate = 8000;
34
35         /// <summary>
36         /// Specifies the maximum value allowed for the audio capture, in Hertz (Hz).
37         /// </summary>
38         /// <seealso cref="SampleRate"/>
39         public static readonly int MaxSampleRate = 48000;
40
41         internal IntPtr _handle = IntPtr.Zero;
42
43         private AudioIOState _state = AudioIOState.Idle;
44
45         internal AudioCaptureBase(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
46         {
47             if (sampleRate < MinSampleRate || MaxSampleRate < sampleRate)
48             {
49                 throw new ArgumentOutOfRangeException(nameof(sampleRate), sampleRate,
50                     $"Valid sampleRate range is { MinSampleRate } <= x <= { MaxSampleRate }.");
51             }
52
53             ValidationUtil.ValidateEnum(typeof(AudioChannel), channel, nameof(channel));
54             ValidationUtil.ValidateEnum(typeof(AudioSampleType), sampleType, nameof(sampleType));
55
56             SampleRate = sampleRate;
57             Channel = channel;
58             SampleType = sampleType;
59
60             AudioIOUtil.ThrowIfError(
61                 AudioInput.Create(SampleRate, (int)Channel, (int)SampleType, out _handle));
62
63             RegisterStateChangedCallback();
64         }
65
66         /// <summary>
67         /// Finalizes an instance of the AudioCaptureBase class.
68         /// </summary>
69         ~AudioCaptureBase()
70         {
71             Dispose(false);
72         }
73
74         /// <summary>
75         /// Occurs when the state of the AudioCapture is changed.
76         /// </summary>
77         public event EventHandler<AudioIOStateChangedEventArgs> StateChanged;
78
79         private AudioStateChangedCallback _stateChangedCallback;
80
81         private void RegisterStateChangedCallback()
82         {
83             _stateChangedCallback = (IntPtr handle, int previous, int current, bool byPolicy, IntPtr _) =>
84             {
85                 _state = (AudioIOState)current;
86
87                 StateChanged?.Invoke(this,
88                     new AudioIOStateChangedEventArgs((AudioIOState)previous, _state, byPolicy));
89             };
90
91             AudioIOUtil.ThrowIfError(
92                 AudioInput.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero));
93         }
94
95         #region Dispose support
96         private bool _isDisposed = false;
97
98         /// <summary>
99         /// Releases all resources used by the <see cref="AudioCaptureBase"/> object.
100         /// </summary>
101         public void Dispose()
102         {
103             Dispose(true);
104             GC.SuppressFinalize(this);
105         }
106
107         /// <summary>
108         /// Releases the resources used by the <see cref="AudioCaptureBase"/> object.
109         /// </summary>
110         /// <param name="disposing">
111         /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
112         /// </param>
113         protected virtual void Dispose(bool disposing)
114         {
115             if (_isDisposed)
116             {
117                 return;
118             }
119
120             if (_handle != IntPtr.Zero)
121             {
122                 if (_state != AudioIOState.Idle)
123                 {
124                     try
125                     {
126                         Unprepare();
127                     }
128                     catch (Exception)
129                     {
130                     }
131                 }
132
133                 AudioInput.Destroy(_handle);
134                 _handle = IntPtr.Zero;
135                 _isDisposed = true;
136             }
137         }
138
139         internal void ValidateNotDisposed()
140         {
141             if (_isDisposed)
142             {
143                 throw new ObjectDisposedException(GetType().Name);
144             }
145         }
146         #endregion
147
148         internal void ValidateState(params AudioIOState[] desiredStates)
149         {
150             ValidateNotDisposed();
151
152             AudioIOUtil.ValidateState(_state, desiredStates);
153         }
154
155         /// <summary>
156         /// Gets the sample rate of the audio input data stream, in Hertz (Hz).
157         /// </summary>
158         public int SampleRate { get; }
159
160         /// <summary>
161         /// Gets the channel type of the audio input data stream.
162         /// </summary>
163         public AudioChannel Channel { get; }
164
165         /// <summary>
166         /// Gets the sample type of the audio input data stream.
167         /// </summary>
168         public AudioSampleType SampleType { get; }
169
170         /// <summary>
171         /// Gets the size allocated for the audio input buffer.
172         /// </summary>
173         /// <exception cref="ObjectDisposedException">The AudioPlayback has already been disposed of.</exception>
174         public int GetBufferSize()
175         {
176             AudioIOUtil.ThrowIfError(AudioInput.GetBufferSize(_handle, out var size));
177             return size;
178         }
179
180         /// <summary>
181         /// Prepares the AudioCapture for reading audio data by starting buffering of audio data from the device.
182         /// </summary>
183         /// <exception cref="InvalidOperationException">
184         ///     Operation failed due to an internal error.\n
185         ///     -or-\n
186         ///     The current state is not <see cref="AudioIOState.Idle"/>.
187         /// </exception>
188         /// <seealso cref="Unprepare"/>
189         public void Prepare()
190         {
191             ValidateState(AudioIOState.Idle);
192
193             AudioIOUtil.ThrowIfError(AudioInput.Prepare(_handle),
194                 "Failed to prepare the AudioCapture");
195         }
196
197         /// <summary>
198         /// Unprepares the AudioCapture.
199         /// </summary>
200         /// <exception cref="InvalidOperationException">
201         ///     Operation failed due to an internal error.\n
202         ///     -or-\n
203         ///     The current state is <see cref="AudioIOState.Idle"/>.
204         /// </exception>
205         /// <seealso cref="Prepare"/>
206         public void Unprepare()
207         {
208             ValidateState(AudioIOState.Running, AudioIOState.Paused);
209
210             AudioIOUtil.ThrowIfError(AudioInput.Unprepare(_handle),
211                 "Failed to unprepare the AudioCapture");
212         }
213
214         /// <summary>
215         /// Pauses buffering of audio data from the device.
216         /// </summary>
217         /// <exception cref="InvalidOperationException">
218         ///     The current state is <see cref="AudioIOState.Idle"/>.\n
219         ///     -or-\n
220         ///     The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
221         /// </exception>
222         /// <seealso cref="Resume"/>
223         public void Pause()
224         {
225             if (_state == AudioIOState.Paused)
226             {
227                 return;
228             }
229             ValidateState(AudioIOState.Running);
230
231             AudioIOUtil.ThrowIfError(AudioInput.Pause(_handle));
232         }
233         /// <summary>
234         /// Resumes buffering audio data from the device.
235         /// </summary>
236         /// <exception cref="InvalidOperationException">
237         ///     The current state is <see cref="AudioIOState.Idle"/>.\n
238         ///     -or-\n
239         ///     The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
240         /// </exception>
241         /// <seealso cref="Pause"/>
242         public void Resume()
243         {
244             if (_state == AudioIOState.Running)
245             {
246                 return;
247             }
248             ValidateState(AudioIOState.Paused);
249
250             AudioIOUtil.ThrowIfError(AudioInput.Resume(_handle));
251         }
252
253         /// <summary>
254         /// Flushes and discards buffered audio data from the input stream.
255         /// </summary>
256         /// <exception cref="InvalidOperationException">The current state is <see cref="AudioIOState.Idle"/>.</exception>
257         public void Flush()
258         {
259             ValidateState(AudioIOState.Running, AudioIOState.Paused);
260
261             int ret = AudioInput.Flush(_handle);
262
263             MultimediaDebug.AssertNoError(ret);
264         }
265
266         /// <summary>
267         /// Sets the sound stream information to the audio input.
268         /// </summary>
269         /// <param name="streamPolicy">The <see cref="AudioStreamPolicy"/> to apply for the AudioCapture.</param>
270         /// <exception cref="ArgumentNullException"><paramref name="streamPolicy"/> is null.</exception>
271         /// <exception cref="ObjectDisposedException"><paramref name="streamPolicy"/> has already been disposed of.</exception>
272         /// <exception cref="NotSupportedException"><paramref name="streamPolicy"/> is not supported.</exception>
273         /// <exception cref="ArgumentException">Not able to retrieve information from <paramref name="streamPolicy"/>.</exception>
274         public void ApplyStreamPolicy(AudioStreamPolicy streamPolicy)
275         {
276             if (streamPolicy == null)
277             {
278                 throw new ArgumentNullException(nameof(streamPolicy));
279             }
280
281             ValidateNotDisposed();
282
283             AudioIOUtil.ThrowIfError(AudioInput.SetStreamInfo(_handle, streamPolicy.Handle));
284         }
285     }
286
287     /// <summary>
288     /// Provides the ability to record audio from system audio input devices in a synchronous way.
289     /// </summary>
290     /// <privilege>http://tizen.org/privilege/recorder</privilege>
291     public class AudioCapture : AudioCaptureBase
292     {
293         /// <summary>
294         /// Initializes a new instance of the AudioCapture class with the specified sample rate, channel, and sampleType.
295         /// </summary>
296         /// <param name="sampleRate">The audio sample rate (8000 ~ 48000Hz).</param>
297         /// <param name="channel">The audio channel type.</param>
298         /// <param name="sampleType">The audio sample type.</param>
299         /// <exception cref="ArgumentOutOfRangeException">
300         ///     <paramref name="sampleRate"/> is less than <see cref="AudioCaptureBase.MinSampleRate"/>.\n
301         ///     -or-\n
302         ///     <paramref name="sampleRate"/> is greater than <see cref="AudioCaptureBase.MaxSampleRate"/>.
303         /// </exception>
304         /// <exception cref="ArgumentException">
305         ///     <paramref name="channel"/> is invalid.\n
306         ///     -or-\n
307         ///     <paramref name="sampleType"/> is invalid.
308         /// </exception>
309         /// <exception cref="InvalidOperationException">The required privilege is not specified.</exception>
310         /// <exception cref="NotSupportedException">The system does not support microphone.</exception>
311         public AudioCapture(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
312             : base(sampleRate, channel, sampleType)
313         {
314         }
315
316         /// <summary>
317         /// Reads audio data from the audio input buffer.
318         /// </summary>
319         /// <param name="count">The number of bytes to be read.</param>
320         /// <returns>The buffer of audio data captured.</returns>
321         /// <exception cref="InvalidOperationException">The current state is not <see cref="AudioIOState.Running"/>.</exception>
322         /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is equal to or less than zero.</exception>
323         public byte[] Read(int count)
324         {
325             if (count <= 0)
326             {
327                 throw new ArgumentOutOfRangeException(nameof(count), count,
328                     $"{ nameof(count) } can't be equal to or less than zero.");
329             }
330             ValidateState(AudioIOState.Running);
331
332             byte[] buffer = new byte[count];
333
334             AudioIOUtil.ThrowIfError(AudioInput.Read(_handle, buffer, count),
335                 "Failed to read");
336
337             return buffer;
338         }
339     }
340
341     /// <summary>
342     /// Provides the ability to record audio from system audio input devices in an asynchronous way.
343     /// </summary>
344     /// <privilege>http://tizen.org/privilege/recorder</privilege>
345     public class AsyncAudioCapture : AudioCaptureBase
346     {
347
348         /// <summary>
349         /// Occurs when audio data is available.
350         /// </summary>
351         public event EventHandler<AudioDataAvailableEventArgs> DataAvailable;
352
353         /// <summary>
354         /// Initializes a new instance of the AsyncAudioCapture class with the specified sample rate, channel and sampleType.
355         /// </summary>
356         /// <param name="sampleRate">The audio sample rate (8000 ~ 48000Hz).</param>
357         /// <param name="channel">The audio channel type.</param>
358         /// <param name="sampleType">The audio sample type.</param>
359         /// <exception cref="ArgumentOutOfRangeException">
360         ///     <paramref name="sampleRate"/> is less than <see cref="AudioCaptureBase.MinSampleRate"/>.\n
361         ///     -or-\n
362         ///     <paramref name="sampleRate"/> is greater than <see cref="AudioCaptureBase.MaxSampleRate"/>.
363         /// </exception>
364         /// <exception cref="ArgumentException">
365         ///     <paramref name="channel"/> is invalid.\n
366         ///     -or-\n
367         ///     <paramref name="sampleType"/> is invalid.
368         /// </exception>
369         /// <exception cref="InvalidOperationException">The required privilege is not specified.</exception>
370         /// <exception cref="NotSupportedException">The system does not support microphone.</exception>
371         public AsyncAudioCapture(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
372             : base(sampleRate, channel, sampleType)
373         {
374             _streamCallback = (IntPtr handle, uint length, IntPtr _) => { OnInputDataAvailable(handle, length); };
375
376             AudioIOUtil.ThrowIfError(
377                 AudioInput.SetStreamCallback(_handle, _streamCallback, IntPtr.Zero),
378                 $"Failed to initialize a { nameof(AsyncAudioCapture) }");
379         }
380
381         private AudioStreamCallback _streamCallback;
382
383         private void OnInputDataAvailable(IntPtr handle, uint length)
384         {
385             if (length == 0U)
386             {
387                 return;
388             }
389
390             IntPtr ptr = IntPtr.Zero;
391             try
392             {
393                 AudioIOUtil.ThrowIfError(AudioInput.Peek(_handle, out ptr, ref length));
394
395                 byte[] buffer = new byte[length];
396                 Marshal.Copy(ptr, buffer, 0, (int)length);
397
398                 AudioInput.Drop(_handle);
399
400                 DataAvailable?.Invoke(this, new AudioDataAvailableEventArgs(buffer));
401             }
402             catch (Exception e)
403             {
404                 Log.Error(nameof(AsyncAudioCapture), e.Message);
405             }
406         }
407     }
408 }