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();
67 /// Finalizes an instance of the AudioCaptureBase class.
75 /// Occurs when the state of the AudioCapture is changed.
77 public event EventHandler<AudioIOStateChangedEventArgs> StateChanged;
79 private AudioStateChangedCallback _stateChangedCallback;
81 private void RegisterStateChangedCallback()
83 _stateChangedCallback = (IntPtr handle, int previous, int current, bool byPolicy, IntPtr _) =>
85 _state = (AudioIOState)current;
87 StateChanged?.Invoke(this,
88 new AudioIOStateChangedEventArgs((AudioIOState)previous, _state, byPolicy));
91 AudioIOUtil.ThrowIfError(
92 AudioInput.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero));
95 #region Dispose support
96 private bool _isDisposed = false;
99 /// Releases all resources used by the <see cref="AudioCaptureBase"/> object.
101 public void Dispose()
104 GC.SuppressFinalize(this);
108 /// Releases the resources used by the <see cref="AudioCaptureBase"/> object.
110 /// <param name="disposing">
111 /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
113 protected virtual void Dispose(bool disposing)
120 if (_handle != IntPtr.Zero)
122 if (_state != AudioIOState.Idle)
133 AudioInput.Destroy(_handle);
134 _handle = IntPtr.Zero;
139 internal void ValidateNotDisposed()
143 throw new ObjectDisposedException(GetType().Name);
148 internal void ValidateState(params AudioIOState[] desiredStates)
150 ValidateNotDisposed();
152 AudioIOUtil.ValidateState(_state, desiredStates);
156 /// Gets the sample rate of the audio input data stream, in Hertz (Hz).
158 public int SampleRate { get; }
161 /// Gets the channel type of the audio input data stream.
163 public AudioChannel Channel { get; }
166 /// Gets the sample type of the audio input data stream.
168 public AudioSampleType SampleType { get; }
171 /// Gets the size allocated for the audio input buffer.
173 /// <exception cref="ObjectDisposedException">The AudioPlayback has already been disposed of.</exception>
174 public int GetBufferSize()
176 AudioIOUtil.ThrowIfError(AudioInput.GetBufferSize(_handle, out var size));
181 /// Prepares the AudioCapture for reading audio data by starting buffering of audio data from the device.
183 /// <exception cref="InvalidOperationException">
184 /// Operation failed due to an internal error.<br/>
186 /// The current state is not <see cref="AudioIOState.Idle"/>.
188 /// <seealso cref="Unprepare"/>
189 public void Prepare()
191 ValidateState(AudioIOState.Idle);
193 AudioIOUtil.ThrowIfError(AudioInput.Prepare(_handle),
194 "Failed to prepare the AudioCapture");
198 /// Unprepares the AudioCapture.
200 /// <exception cref="InvalidOperationException">
201 /// Operation failed due to an internal error.<br/>
203 /// The current state is <see cref="AudioIOState.Idle"/>.
205 /// <seealso cref="Prepare"/>
206 public void Unprepare()
208 ValidateState(AudioIOState.Running, AudioIOState.Paused);
210 AudioIOUtil.ThrowIfError(AudioInput.Unprepare(_handle),
211 "Failed to unprepare the AudioCapture");
215 /// Pauses buffering of audio data from the device.
217 /// <exception cref="InvalidOperationException">
218 /// The current state is <see cref="AudioIOState.Idle"/>.<br/>
220 /// The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
222 /// <seealso cref="Resume"/>
225 if (_state == AudioIOState.Paused)
229 ValidateState(AudioIOState.Running);
231 AudioIOUtil.ThrowIfError(AudioInput.Pause(_handle));
234 /// Resumes buffering audio data from the device.
236 /// <exception cref="InvalidOperationException">
237 /// The current state is <see cref="AudioIOState.Idle"/>.<br/>
239 /// The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
241 /// <seealso cref="Pause"/>
244 if (_state == AudioIOState.Running)
248 ValidateState(AudioIOState.Paused);
250 AudioIOUtil.ThrowIfError(AudioInput.Resume(_handle));
254 /// Flushes and discards buffered audio data from the input stream.
256 /// <exception cref="InvalidOperationException">The current state is <see cref="AudioIOState.Idle"/>.</exception>
259 ValidateState(AudioIOState.Running, AudioIOState.Paused);
261 int ret = AudioInput.Flush(_handle);
263 MultimediaDebug.AssertNoError(ret);
267 /// Sets the sound stream information to the audio input.
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)
276 if (streamPolicy == null)
278 throw new ArgumentNullException(nameof(streamPolicy));
281 ValidateNotDisposed();
283 AudioIOUtil.ThrowIfError(AudioInput.SetStreamInfo(_handle, streamPolicy.Handle));
288 /// Provides the ability to record audio from system audio input devices in a synchronous way.
290 /// <privilege>http://tizen.org/privilege/recorder</privilege>
291 public class AudioCapture : AudioCaptureBase
294 /// Initializes a new instance of the AudioCapture class with the specified sample rate, channel, and sampleType.
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"/>.<br/>
302 /// <paramref name="sampleRate"/> is greater than <see cref="AudioCaptureBase.MaxSampleRate"/>.
304 /// <exception cref="ArgumentException">
305 /// <paramref name="channel"/> is invalid.<br/>
307 /// <paramref name="sampleType"/> is invalid.
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)
317 /// Reads audio data from the audio input buffer.
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)
327 throw new ArgumentOutOfRangeException(nameof(count), count,
328 $"{ nameof(count) } can't be equal to or less than zero.");
330 ValidateState(AudioIOState.Running);
332 byte[] buffer = new byte[count];
334 AudioIOUtil.ThrowIfError(AudioInput.Read(_handle, buffer, count),
342 /// Provides the ability to record audio from system audio input devices in an asynchronous way.
344 /// <privilege>http://tizen.org/privilege/recorder</privilege>
345 public class AsyncAudioCapture : AudioCaptureBase
349 /// Occurs when audio data is available.
351 public event EventHandler<AudioDataAvailableEventArgs> DataAvailable;
354 /// Initializes a new instance of the AsyncAudioCapture class with the specified sample rate, channel and sampleType.
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"/>.<br/>
362 /// <paramref name="sampleRate"/> is greater than <see cref="AudioCaptureBase.MaxSampleRate"/>.
364 /// <exception cref="ArgumentException">
365 /// <paramref name="channel"/> is invalid.<br/>
367 /// <paramref name="sampleType"/> is invalid.
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)
374 _streamCallback = (IntPtr handle, uint length, IntPtr _) => { OnInputDataAvailable(handle, length); };
376 AudioIOUtil.ThrowIfError(
377 AudioInput.SetStreamCallback(_handle, _streamCallback, IntPtr.Zero),
378 $"Failed to initialize a { nameof(AsyncAudioCapture) }");
381 private AudioStreamCallback _streamCallback;
383 private void OnInputDataAvailable(IntPtr handle, uint length)
390 IntPtr ptr = IntPtr.Zero;
393 AudioIOUtil.ThrowIfError(AudioInput.Peek(_handle, out ptr, ref length));
395 byte[] buffer = new byte[length];
396 Marshal.Copy(ptr, buffer, 0, (int)length);
398 AudioInput.Drop(_handle);
400 DataAvailable?.Invoke(this, new AudioDataAvailableEventArgs(buffer));
404 Log.Error(nameof(AsyncAudioCapture), e.Message);