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