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;
20 namespace Tizen.Multimedia
23 /// Provides the ability to directly manage the system audio input devices.
25 /// <remarks>The recorder privilege(http://tizen.org/privilege/recorder) is required.</remarks>
26 public abstract class AudioCaptureBase : IDisposable
29 /// Specifies the minimum value allowed for the audio capture.
31 public static readonly int MinSampleRate = 8000;
34 /// Specifies the maximum value allowed for the audio capture.
36 public static readonly int MaxSampleRate = 48000;
38 internal IntPtr _handle = IntPtr.Zero;
40 private AudioIOState _state = AudioIOState.Idle;
42 internal AudioCaptureBase(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
44 if (sampleRate < MinSampleRate || MaxSampleRate < sampleRate)
46 throw new ArgumentOutOfRangeException(nameof(sampleRate), sampleRate,
47 $"Valid sampleRate range is { MinSampleRate } <= x <= { MaxSampleRate }.");
50 ValidationUtil.ValidateEnum(typeof(AudioChannel), channel, nameof(channel));
51 ValidationUtil.ValidateEnum(typeof(AudioSampleType), sampleType, nameof(sampleType));
53 SampleRate = sampleRate;
55 SampleType = sampleType;
57 AudioIOUtil.ThrowIfError(
58 Interop.AudioIO.AudioInput.Create(SampleRate, (int)Channel, (int)SampleType, out _handle));
60 RegisterStateChangedCallback();
69 /// Occurs when the state of the AudioCapture is changed.
71 public event EventHandler<AudioIOStateChangedEventArgs> StateChanged;
73 private Interop.AudioIO.AudioStateChangedCallback _stateChangedCallback;
75 private void RegisterStateChangedCallback()
77 _stateChangedCallback = (IntPtr handle, int previous, int current, bool byPolicy, IntPtr _) =>
79 _state = (AudioIOState)current;
81 StateChanged?.Invoke(this,
82 new AudioIOStateChangedEventArgs((AudioIOState)previous, _state, byPolicy));
85 AudioIOUtil.ThrowIfError(
86 Interop.AudioIO.AudioInput.SetStateChangedCallback(_handle, _stateChangedCallback, IntPtr.Zero));
89 #region Dispose support
90 private bool _isDisposed = false;
95 GC.SuppressFinalize(this);
98 protected virtual void Dispose(bool disposing)
105 if (_handle != IntPtr.Zero)
107 if (_state != AudioIOState.Idle)
118 Interop.AudioIO.AudioInput.Destroy(_handle);
119 _handle = IntPtr.Zero;
124 internal void ValidateNotDisposed()
128 throw new ObjectDisposedException(GetType().Name);
133 internal void ValidateState(params AudioIOState[] desiredStates)
135 ValidateNotDisposed();
137 AudioIOUtil.ValidateState(_state, desiredStates);
141 /// Gets the sample rate of the audio input data stream.
143 public int SampleRate { get; }
146 /// Gets the channel type of the audio input data stream.
148 public AudioChannel Channel { get; }
151 /// Gets the sample type of the audio input data stream.
153 public AudioSampleType SampleType { get; }
156 /// Gets the size allocated for the audio input buffer.
158 /// <exception cref="ObjectDisposedException">The AudioPlayback has already been disposed.</exception>
159 public int GetBufferSize()
161 AudioIOUtil.ThrowIfError(Interop.AudioIO.AudioInput.GetBufferSize(_handle, out var size));
166 /// Prepares the AudioCapture for reading audio data by starting buffering of audio data from the device.
168 /// <exception cref="InvalidOperationException">
169 /// Operation failed due to internal error.\n
171 /// The current state is not <see cref="AudioIOState.Idle"/>.
173 /// <seealso cref="Unprepare"/>
174 public void Prepare()
176 ValidateState(AudioIOState.Idle);
178 AudioIOUtil.ThrowIfError(Interop.AudioIO.AudioInput.Prepare(_handle),
179 "Failed to prepare the AudioCapture");
183 /// Unprepares the AudioCapture.
185 /// <exception cref="InvalidOperationException">
186 /// Operation failed due to internal error.\n
188 /// The current state is <see cref="AudioIOState.Idle"/>.
190 /// <seealso cref="Prepare"/>
191 public void Unprepare()
193 ValidateState(AudioIOState.Running, AudioIOState.Paused);
195 AudioIOUtil.ThrowIfError(Interop.AudioIO.AudioInput.Unprepare(_handle),
196 "Failed to unprepare the AudioCapture");
200 /// Pauses buffering of audio data from the device.
202 /// <exception cref="InvalidOperationException">
203 /// The current state is <see cref="AudioState.Idle"/>.\n
205 /// The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
207 /// <seealso cref="Resume"/>
210 if (_state == AudioIOState.Paused)
214 ValidateState(AudioIOState.Running);
216 AudioIOUtil.ThrowIfError(Interop.AudioIO.AudioInput.Pause(_handle));
219 /// Resumes buffering audio data from the device.
221 /// <exception cref="InvalidOperationException">
222 /// The current state is <see cref="AudioState.Idle"/>.\n
224 /// The method is called in the <see cref="AsyncAudioCapture.DataAvailable"/> event handler.
226 /// <seealso cref="Pause"/>
229 if (_state == AudioIOState.Running)
233 ValidateState(AudioIOState.Paused);
235 AudioIOUtil.ThrowIfError(Interop.AudioIO.AudioInput.Resume(_handle));
238 /// Flushes and discards buffered audio data from the input stream.
240 /// <exception cref="InvalidOperationException">The current state is <see cref="AudioState.Idle"/>.</exception>
243 ValidateState(AudioIOState.Running, AudioIOState.Paused);
245 int ret = Interop.AudioIO.AudioInput.Flush(_handle);
247 MultimediaDebug.AssertNoError(ret);
251 /// Sets the sound stream information to the audio input.
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)
260 if (streamPolicy == null)
262 throw new ArgumentNullException(nameof(streamPolicy));
265 if (streamPolicy.Handle == IntPtr.Zero)
267 throw new ObjectDisposedException(nameof(streamPolicy));
270 ValidateNotDisposed();
272 AudioIOUtil.ThrowIfError(Interop.AudioIO.AudioInput.SetStreamInfo(_handle, streamPolicy.Handle));
277 /// Provides the ability to record audio from system audio input devices in synchronous way.
279 /// <privilege>http://tizen.org/privilege/recorder</privilege>
280 public class AudioCapture : AudioCaptureBase
283 /// Initializes a new instance of the AudioCapture class with the specified sample rate, channel and sampleType.
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
291 /// <paramref name="sampleRate"/> is greater than <see cref="MaxSampleRate"/>.
293 /// <exception cref="ArgumentException">
294 /// <paramref name="channel"/> is invalid.\n
296 /// <paramref name="sampleType"/> is invalid.
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)
306 /// Reads audio data from the audio input buffer.
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)
316 throw new ArgumentOutOfRangeException(nameof(count), count,
317 $"{ nameof(count) } can't be equal to or less than zero.");
319 ValidateState(AudioIOState.Running);
321 byte[] buffer = new byte[count];
323 AudioIOUtil.ThrowIfError(Interop.AudioIO.AudioInput.Read(_handle, buffer, count),
331 /// Provides the ability to record audio from system audio input devices in asynchronous way.
333 /// <privilege>http://tizen.org/privilege/recorder</privilege>
334 public class AsyncAudioCapture : AudioCaptureBase
338 /// Occurs when audio data is available.
340 public event EventHandler<AudioDataAvailableEventArgs> DataAvailable;
343 /// Initializes a new instance of the AsyncAudioCapture class with the specified sample rate, channel and sampleType.
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
351 /// <paramref name="sampleRate"/> is greater than <see cref="MaxSampleRate"/>.
353 /// <exception cref="ArgumentException">
354 /// <paramref name="channel"/> is invalid.\n
356 /// <paramref name="sampleType"/> is invalid.
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)
363 _streamCallback = (IntPtr handle, uint length, IntPtr _) => { OnInputDataAvailable(handle, length); };
365 AudioIOUtil.ThrowIfError(
366 Interop.AudioIO.AudioInput.SetStreamCallback(_handle, _streamCallback, IntPtr.Zero),
367 $"Failed to initialize a { nameof(AsyncAudioCapture) }");
370 private Interop.AudioIO.AudioStreamCallback _streamCallback;
372 private void OnInputDataAvailable(IntPtr handle, uint length)
379 IntPtr ptr = IntPtr.Zero;
382 AudioIOUtil.ThrowIfError(Interop.AudioIO.AudioInput.Peek(_handle, out ptr, ref length));
384 byte[] buffer = new byte[length];
385 Marshal.Copy(ptr, buffer, 0, (int)length);
387 Interop.AudioIO.AudioInput.Drop(_handle);
389 DataAvailable?.Invoke(this, new AudioDataAvailableEventArgs(buffer));
393 Log.Error(nameof(AsyncAudioCapture), e.Message);