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;
98 GC.SuppressFinalize(this);
101 protected virtual void Dispose(bool disposing)
108 if (_handle != IntPtr.Zero)
110 if (_state != AudioIOState.Idle)
121 AudioInput.Destroy(_handle);
122 _handle = IntPtr.Zero;
127 internal void ValidateNotDisposed()
131 throw new ObjectDisposedException(GetType().Name);
136 internal void ValidateState(params AudioIOState[] desiredStates)
138 ValidateNotDisposed();
140 AudioIOUtil.ValidateState(_state, desiredStates);
144 /// Gets the sample rate of the audio input data stream, in Hertz (Hz).
146 public int SampleRate { get; }
149 /// Gets the channel type of the audio input data stream.
151 public AudioChannel Channel { get; }
154 /// Gets the sample type of the audio input data stream.
156 public AudioSampleType SampleType { get; }
159 /// Gets the size allocated for the audio input buffer.
161 /// <exception cref="ObjectDisposedException">The AudioPlayback has already been disposed.</exception>
162 public int GetBufferSize()
164 AudioIOUtil.ThrowIfError(AudioInput.GetBufferSize(_handle, out var size));
169 /// Prepares the AudioCapture for reading audio data by starting buffering of audio data from the device.
171 /// <exception cref="InvalidOperationException">
172 /// Operation failed due to internal error.\n
174 /// The current state is not <see cref="AudioIOState.Idle"/>.
176 /// <seealso cref="Unprepare"/>
177 public void Prepare()
179 ValidateState(AudioIOState.Idle);
181 AudioIOUtil.ThrowIfError(AudioInput.Prepare(_handle),
182 "Failed to prepare the AudioCapture");
186 /// Unprepares the AudioCapture.
188 /// <exception cref="InvalidOperationException">
189 /// Operation failed due to internal error.\n
191 /// The current state is <see cref="AudioIOState.Idle"/>.
193 /// <seealso cref="Prepare"/>
194 public void Unprepare()
196 ValidateState(AudioIOState.Running, AudioIOState.Paused);
198 AudioIOUtil.ThrowIfError(AudioInput.Unprepare(_handle),
199 "Failed to unprepare the AudioCapture");
203 /// Pauses buffering of audio data from the device.
205 /// <exception cref="InvalidOperationException">
206 /// The current state is <see cref="AudioIOState.Idle"/>.\n
208 /// The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
210 /// <seealso cref="Resume"/>
213 if (_state == AudioIOState.Paused)
217 ValidateState(AudioIOState.Running);
219 AudioIOUtil.ThrowIfError(AudioInput.Pause(_handle));
222 /// Resumes buffering audio data from the device.
224 /// <exception cref="InvalidOperationException">
225 /// The current state is <see cref="AudioIOState.Idle"/>.\n
227 /// The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
229 /// <seealso cref="Pause"/>
232 if (_state == AudioIOState.Running)
236 ValidateState(AudioIOState.Paused);
238 AudioIOUtil.ThrowIfError(AudioInput.Resume(_handle));
241 /// Flushes and discards buffered audio data from the input stream.
243 /// <exception cref="InvalidOperationException">The current state is <see cref="AudioIOState.Idle"/>.</exception>
246 ValidateState(AudioIOState.Running, AudioIOState.Paused);
248 int ret = AudioInput.Flush(_handle);
250 MultimediaDebug.AssertNoError(ret);
254 /// Sets the sound stream information to the audio input.
256 /// <param name="streamPolicy">The <see cref="AudioStreamPolicy"/> to apply for the AudioCapture.</param>
257 /// <exception cref="ArgumentNullException"><paramref name="streamPolicy"/> is null.</exception>
258 /// <exception cref="ObjectDisposedException"><paramref name="streamPolicy"/> has already been disposed.</exception>
259 /// <exception cref="NotSupportedException"><paramref name="streamPolicy"/> is not supported.</exception>
260 /// <exception cref="ArgumentException">Not able to retrieve information from <paramref name="streamPolicy"/>.</exception>
261 public void ApplyStreamPolicy(AudioStreamPolicy streamPolicy)
263 if (streamPolicy == null)
265 throw new ArgumentNullException(nameof(streamPolicy));
268 ValidateNotDisposed();
270 AudioIOUtil.ThrowIfError(AudioInput.SetStreamInfo(_handle, streamPolicy.Handle));
275 /// Provides the ability to record audio from system audio input devices in synchronous way.
277 /// <privilege>http://tizen.org/privilege/recorder</privilege>
278 public class AudioCapture : AudioCaptureBase
281 /// Initializes a new instance of the AudioCapture class with the specified sample rate, channel and sampleType.
283 /// <param name="sampleRate">The audio sample rate.(8000 ~ 48000Hz)</param>
284 /// <param name="channel">The audio channel type.</param>
285 /// <param name="sampleType">The audio sample type.</param>
286 /// <exception cref="ArgumentOutOfRangeException">
287 /// <paramref name="sampleRate"/> is less than <see cref="AudioCaptureBase.MinSampleRate"/>.\n
289 /// <paramref name="sampleRate"/> is greater than <see cref="AudioCaptureBase.MaxSampleRate"/>.
291 /// <exception cref="ArgumentException">
292 /// <paramref name="channel"/> is invalid.\n
294 /// <paramref name="sampleType"/> is invalid.
296 /// <exception cref="InvalidOperationException">The required privilege is not specified.</exception>
297 /// <exception cref="NotSupportedException">The system does not support microphone.</exception>
298 public AudioCapture(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
299 : base(sampleRate, channel, sampleType)
304 /// Reads audio data from the audio input buffer.
306 /// <param name="count">The number of bytes to be read.</param>
307 /// <returns>The buffer of audio data captured.</returns>
308 /// <exception cref="InvalidOperationException">The current state is not <see cref="AudioIOState.Running"/>.</exception>
309 /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is equal to or less than zero.</exception>
310 public byte[] Read(int count)
314 throw new ArgumentOutOfRangeException(nameof(count), count,
315 $"{ nameof(count) } can't be equal to or less than zero.");
317 ValidateState(AudioIOState.Running);
319 byte[] buffer = new byte[count];
321 AudioIOUtil.ThrowIfError(AudioInput.Read(_handle, buffer, count),
329 /// Provides the ability to record audio from system audio input devices in asynchronous way.
331 /// <privilege>http://tizen.org/privilege/recorder</privilege>
332 public class AsyncAudioCapture : AudioCaptureBase
336 /// Occurs when audio data is available.
338 public event EventHandler<AudioDataAvailableEventArgs> DataAvailable;
341 /// Initializes a new instance of the AsyncAudioCapture class with the specified sample rate, channel and sampleType.
343 /// <param name="sampleRate">The audio sample rate.(8000 ~ 48000Hz)</param>
344 /// <param name="channel">The audio channel type.</param>
345 /// <param name="sampleType">The audio sample type.</param>
346 /// <exception cref="ArgumentOutOfRangeException">
347 /// <paramref name="sampleRate"/> is less than <see cref="AudioCaptureBase.MinSampleRate"/>.\n
349 /// <paramref name="sampleRate"/> is greater than <see cref="AudioCaptureBase.MaxSampleRate"/>.
351 /// <exception cref="ArgumentException">
352 /// <paramref name="channel"/> is invalid.\n
354 /// <paramref name="sampleType"/> is invalid.
356 /// <exception cref="InvalidOperationException">The required privilege is not specified.</exception>
357 /// <exception cref="NotSupportedException">The system does not support microphone.</exception>
358 public AsyncAudioCapture(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
359 : base(sampleRate, channel, sampleType)
361 _streamCallback = (IntPtr handle, uint length, IntPtr _) => { OnInputDataAvailable(handle, length); };
363 AudioIOUtil.ThrowIfError(
364 AudioInput.SetStreamCallback(_handle, _streamCallback, IntPtr.Zero),
365 $"Failed to initialize a { nameof(AsyncAudioCapture) }");
368 private AudioStreamCallback _streamCallback;
370 private void OnInputDataAvailable(IntPtr handle, uint length)
377 IntPtr ptr = IntPtr.Zero;
380 AudioIOUtil.ThrowIfError(AudioInput.Peek(_handle, out ptr, ref length));
382 byte[] buffer = new byte[length];
383 Marshal.Copy(ptr, buffer, 0, (int)length);
385 AudioInput.Drop(_handle);
387 DataAvailable?.Invoke(this, new AudioDataAvailableEventArgs(buffer));
391 Log.Error(nameof(AsyncAudioCapture), e.Message);