2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Runtime.InteropServices;
19 using static Interop.AudioIO;
21 namespace Tizen.Multimedia
24 /// Provides the ability to directly manage the system audio input devices.
26 /// <privilege>http://tizen.org/privilege/recorder</privilege>
27 public abstract class AudioCaptureBase : IDisposable
30 /// Specifies the minimum value allowed for the audio capture, in Hertz (Hz).
32 /// <seealso cref="SampleRate"/>
33 public static readonly int MinSampleRate = 8000;
36 /// Specifies the maximum value allowed for the audio capture, in Hertz (Hz).
38 /// <seealso cref="SampleRate"/>
39 public static readonly int MaxSampleRate = 48000;
41 internal IntPtr _handle = IntPtr.Zero;
43 private AudioIOState _state = AudioIOState.Idle;
45 internal AudioCaptureBase(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
47 if (sampleRate < MinSampleRate || MaxSampleRate < sampleRate)
49 throw new ArgumentOutOfRangeException(nameof(sampleRate), sampleRate,
50 $"Valid sampleRate range is { MinSampleRate } <= x <= { MaxSampleRate }.");
53 ValidationUtil.ValidateEnum(typeof(AudioChannel), channel, nameof(channel));
54 ValidationUtil.ValidateEnum(typeof(AudioSampleType), sampleType, nameof(sampleType));
56 SampleRate = sampleRate;
58 SampleType = sampleType;
60 AudioIOUtil.ThrowIfError(
61 AudioInput.Create(SampleRate, (int)Channel, (int)SampleType, out _handle));
63 RegisterStateChangedCallback();
72 /// Occurs when the state of the AudioCapture is changed.
74 public event EventHandler<AudioIOStateChangedEventArgs> StateChanged;
76 private AudioStateChangedCallback _stateChangedCallback;
78 private void RegisterStateChangedCallback()
80 _stateChangedCallback = (IntPtr handle, int previous, int current, bool byPolicy, IntPtr _) =>
82 _state = (AudioIOState)current;
84 StateChanged?.Invoke(this,
85 new AudioIOStateChangedEventArgs((AudioIOState)previous, _state, byPolicy));
88 AudioIOUtil.ThrowIfError(
89 AudioInput.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero));
92 #region Dispose support
93 private bool _isDisposed = false;
96 /// Releases all resources used by the <see cref="AudioCaptureBase"/> object.
101 GC.SuppressFinalize(this);
105 /// Releases the resources used by the <see cref="AudioCaptureBase"/> object.
107 /// <param name="disposing">
108 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
110 protected virtual void Dispose(bool disposing)
117 if (_handle != IntPtr.Zero)
119 if (_state != AudioIOState.Idle)
130 AudioInput.Destroy(_handle);
131 _handle = IntPtr.Zero;
136 internal void ValidateNotDisposed()
140 throw new ObjectDisposedException(GetType().Name);
145 internal void ValidateState(params AudioIOState[] desiredStates)
147 ValidateNotDisposed();
149 AudioIOUtil.ValidateState(_state, desiredStates);
153 /// Gets the sample rate of the audio input data stream, in Hertz (Hz).
155 public int SampleRate { get; }
158 /// Gets the channel type of the audio input data stream.
160 public AudioChannel Channel { get; }
163 /// Gets the sample type of the audio input data stream.
165 public AudioSampleType SampleType { get; }
168 /// Gets the size allocated for the audio input buffer.
170 /// <exception cref="ObjectDisposedException">The AudioPlayback has already been disposed of.</exception>
171 public int GetBufferSize()
173 AudioIOUtil.ThrowIfError(AudioInput.GetBufferSize(_handle, out var size));
178 /// Prepares the AudioCapture for reading audio data by starting buffering of audio data from the device.
180 /// <exception cref="InvalidOperationException">
181 /// Operation failed due to an internal error.\n
183 /// The current state is not <see cref="AudioIOState.Idle"/>.
185 /// <seealso cref="Unprepare"/>
186 public void Prepare()
188 ValidateState(AudioIOState.Idle);
190 AudioIOUtil.ThrowIfError(AudioInput.Prepare(_handle),
191 "Failed to prepare the AudioCapture");
195 /// Unprepares the AudioCapture.
197 /// <exception cref="InvalidOperationException">
198 /// Operation failed due to an internal error.\n
200 /// The current state is <see cref="AudioIOState.Idle"/>.
202 /// <seealso cref="Prepare"/>
203 public void Unprepare()
205 ValidateState(AudioIOState.Running, AudioIOState.Paused);
207 AudioIOUtil.ThrowIfError(AudioInput.Unprepare(_handle),
208 "Failed to unprepare the AudioCapture");
212 /// Pauses buffering of audio data from the device.
214 /// <exception cref="InvalidOperationException">
215 /// The current state is <see cref="AudioIOState.Idle"/>.\n
217 /// The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
219 /// <seealso cref="Resume"/>
222 if (_state == AudioIOState.Paused)
226 ValidateState(AudioIOState.Running);
228 AudioIOUtil.ThrowIfError(AudioInput.Pause(_handle));
231 /// Resumes buffering audio data from the device.
233 /// <exception cref="InvalidOperationException">
234 /// The current state is <see cref="AudioIOState.Idle"/>.\n
236 /// The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
238 /// <seealso cref="Pause"/>
241 if (_state == AudioIOState.Running)
245 ValidateState(AudioIOState.Paused);
247 AudioIOUtil.ThrowIfError(AudioInput.Resume(_handle));
251 /// Flushes and discards buffered audio data from the input stream.
253 /// <exception cref="InvalidOperationException">The current state is <see cref="AudioIOState.Idle"/>.</exception>
256 ValidateState(AudioIOState.Running, AudioIOState.Paused);
258 int ret = AudioInput.Flush(_handle);
260 MultimediaDebug.AssertNoError(ret);
264 /// Sets the sound stream information to the audio input.
266 /// <param name="streamPolicy">The <see cref="AudioStreamPolicy"/> to apply for the AudioCapture.</param>
267 /// <exception cref="ArgumentNullException"><paramref name="streamPolicy"/> is null.</exception>
268 /// <exception cref="ObjectDisposedException"><paramref name="streamPolicy"/> has already been disposed of.</exception>
269 /// <exception cref="NotSupportedException"><paramref name="streamPolicy"/> is not supported.</exception>
270 /// <exception cref="ArgumentException">Not able to retrieve information from <paramref name="streamPolicy"/>.</exception>
271 public void ApplyStreamPolicy(AudioStreamPolicy streamPolicy)
273 if (streamPolicy == null)
275 throw new ArgumentNullException(nameof(streamPolicy));
278 ValidateNotDisposed();
280 AudioIOUtil.ThrowIfError(AudioInput.SetStreamInfo(_handle, streamPolicy.Handle));
285 /// Provides the ability to record audio from system audio input devices in a synchronous way.
287 /// <privilege>http://tizen.org/privilege/recorder</privilege>
288 public class AudioCapture : AudioCaptureBase
291 /// Initializes a new instance of the AudioCapture class with the specified sample rate, channel, and sampleType.
293 /// <param name="sampleRate">The audio sample rate (8000 ~ 48000Hz).</param>
294 /// <param name="channel">The audio channel type.</param>
295 /// <param name="sampleType">The audio sample type.</param>
296 /// <exception cref="ArgumentOutOfRangeException">
297 /// <paramref name="sampleRate"/> is less than <see cref="AudioCaptureBase.MinSampleRate"/>.\n
299 /// <paramref name="sampleRate"/> is greater than <see cref="AudioCaptureBase.MaxSampleRate"/>.
301 /// <exception cref="ArgumentException">
302 /// <paramref name="channel"/> is invalid.\n
304 /// <paramref name="sampleType"/> is invalid.
306 /// <exception cref="InvalidOperationException">The required privilege is not specified.</exception>
307 /// <exception cref="NotSupportedException">The system does not support microphone.</exception>
308 public AudioCapture(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
309 : base(sampleRate, channel, sampleType)
314 /// Reads audio data from the audio input buffer.
316 /// <param name="count">The number of bytes to be read.</param>
317 /// <returns>The buffer of audio data captured.</returns>
318 /// <exception cref="InvalidOperationException">The current state is not <see cref="AudioIOState.Running"/>.</exception>
319 /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is equal to or less than zero.</exception>
320 public byte[] Read(int count)
324 throw new ArgumentOutOfRangeException(nameof(count), count,
325 $"{ nameof(count) } can't be equal to or less than zero.");
327 ValidateState(AudioIOState.Running);
329 byte[] buffer = new byte[count];
331 AudioIOUtil.ThrowIfError(AudioInput.Read(_handle, buffer, count),
339 /// Provides the ability to record audio from system audio input devices in an asynchronous way.
341 /// <privilege>http://tizen.org/privilege/recorder</privilege>
342 public class AsyncAudioCapture : AudioCaptureBase
346 /// Occurs when audio data is available.
348 public event EventHandler<AudioDataAvailableEventArgs> DataAvailable;
351 /// Initializes a new instance of the AsyncAudioCapture class with the specified sample rate, channel and sampleType.
353 /// <param name="sampleRate">The audio sample rate (8000 ~ 48000Hz).</param>
354 /// <param name="channel">The audio channel type.</param>
355 /// <param name="sampleType">The audio sample type.</param>
356 /// <exception cref="ArgumentOutOfRangeException">
357 /// <paramref name="sampleRate"/> is less than <see cref="AudioCaptureBase.MinSampleRate"/>.\n
359 /// <paramref name="sampleRate"/> is greater than <see cref="AudioCaptureBase.MaxSampleRate"/>.
361 /// <exception cref="ArgumentException">
362 /// <paramref name="channel"/> is invalid.\n
364 /// <paramref name="sampleType"/> is invalid.
366 /// <exception cref="InvalidOperationException">The required privilege is not specified.</exception>
367 /// <exception cref="NotSupportedException">The system does not support microphone.</exception>
368 public AsyncAudioCapture(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
369 : base(sampleRate, channel, sampleType)
371 _streamCallback = (IntPtr handle, uint length, IntPtr _) => { OnInputDataAvailable(handle, length); };
373 AudioIOUtil.ThrowIfError(
374 AudioInput.SetStreamCallback(_handle, _streamCallback, IntPtr.Zero),
375 $"Failed to initialize a { nameof(AsyncAudioCapture) }");
378 private AudioStreamCallback _streamCallback;
380 private void OnInputDataAvailable(IntPtr handle, uint length)
387 IntPtr ptr = IntPtr.Zero;
390 AudioIOUtil.ThrowIfError(AudioInput.Peek(_handle, out ptr, ref length));
392 byte[] buffer = new byte[length];
393 Marshal.Copy(ptr, buffer, 0, (int)length);
395 AudioInput.Drop(_handle);
397 DataAvailable?.Invoke(this, new AudioDataAvailableEventArgs(buffer));
401 Log.Error(nameof(AsyncAudioCapture), e.Message);