Added AudioCapture.IsRunning property.

Fixed formatting.
This commit is contained in:
the_fiddler 2009-07-19 20:55:18 +00:00
parent 80697dcc60
commit 325ce4aaa2
2 changed files with 278 additions and 247 deletions

View file

@ -1,188 +1,174 @@
#region License #region License
// //
// The Open Toolkit Library License // The Open Toolkit Library License
// //
// Copyright (c) 2006 - 2009 the Open Toolkit library. // Copyright (c) 2006 - 2009 the Open Toolkit library.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights to // in the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do // the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions: // so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. // OTHER DEALINGS IN THE SOFTWARE.
// //
#endregion #endregion
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace OpenTK.Audio namespace OpenTK.Audio
{ {
/// <summary> /// <summary>
/// Provides methods to instantiate, use and destroy an audio device for recording. /// Provides methods to instantiate, use and destroy an audio device for recording.
/// Static methods are provided to list available devices known by the driver. /// Static methods are provided to list available devices known by the driver.
/// </summary> /// </summary>
public sealed class AudioCapture : IDisposable public sealed class AudioCapture : IDisposable
{ {
#region Fields #region Fields
// This must stay private info so the end-user cannot call any Alc commands for the recording device. // This must stay private info so the end-user cannot call any Alc commands for the recording device.
IntPtr Handle; IntPtr Handle;
// Alc.CaptureStop should be called prior to device shutdown, this keeps track of Alc.CaptureStart/Stop calls. // Alc.CaptureStop should be called prior to device shutdown, this keeps track of Alc.CaptureStart/Stop calls.
bool _isrecording = false; bool _isrecording = false;
ALFormat sample_format; ALFormat sample_format;
int sample_frequency; int sample_frequency;
#endregion #endregion
#region Device Name #region Constructors
private string device_name;
/// <summary>The name of the device associated with this instance.</summary> static AudioCapture()
public string CurrentDeviceName {
{ if (AudioDeviceEnumerator.IsOpenALSupported) // forces enumeration
get {
{ }
return device_name; }
}
} /// <summary>
#endregion Device Name /// Opens the default device for audio recording.
/// Implicitly set parameters are: 22050Hz, 16Bit Mono, 4096 samples ringbuffer.
#region public static properties /// </summary>
/// <summary>Returns a list of strings containing all known recording devices.</summary>
public static IList<string> AvailableDevices
{
get
{
return AudioDeviceEnumerator.AvailableRecordingDevices;
}
}
/// <summary>Returns the name of the device that will be used as recording default.</summary>
public static string DefaultDevice
{
get
{
return AudioDeviceEnumerator.DefaultRecordingDevice;
}
}
#endregion public static properties
#region Constructor
static AudioCapture()
{
if (AudioDeviceEnumerator.IsOpenALSupported) // forces enumeration
{
}
}
/// <summary>
/// Opens the default device for audio recording.
/// Implicitly set parameters are: 22050Hz, 16Bit Mono, 4096 samples ringbuffer.
/// </summary>
public AudioCapture() public AudioCapture()
: this(AudioCapture.DefaultDevice, 22050, ALFormat.Mono16, 4096) : this(AudioCapture.DefaultDevice, 22050, ALFormat.Mono16, 4096)
{ {
} }
/// <summary>Opens a device for audio recording.</summary> /// <summary>Opens a device for audio recording.</summary>
/// <param name="deviceName">The device name.</param> /// <param name="deviceName">The device name.</param>
/// <param name="frequency">The frequency that the data should be captured at.</param> /// <param name="frequency">The frequency that the data should be captured at.</param>
/// <param name="sampleFormat">The requested capture buffer format.</param> /// <param name="sampleFormat">The requested capture buffer format.</param>
/// <param name="bufferSize">The size of OpenAL's capture internal ring-buffer. This value expects number of samples, not bytes.</param> /// <param name="bufferSize">The size of OpenAL's capture internal ring-buffer. This value expects number of samples, not bytes.</param>
public AudioCapture(string deviceName, int frequency, ALFormat sampleFormat, int bufferSize) public AudioCapture(string deviceName, int frequency, ALFormat sampleFormat, int bufferSize)
{ {
if (!AudioDeviceEnumerator.IsOpenALSupported) if (!AudioDeviceEnumerator.IsOpenALSupported)
throw new DllNotFoundException("openal32.dll"); throw new DllNotFoundException("openal32.dll");
if (frequency <= 0)
// Try to open specified device. If it fails, try to open default device. throw new ArgumentOutOfRangeException("frequency");
device_name = deviceName; if (bufferSize <= 0)
Handle = Alc.CaptureOpenDevice(deviceName, frequency, sampleFormat, bufferSize); throw new ArgumentOutOfRangeException("bufferSize");
if (Handle == IntPtr.Zero) // Try to open specified device. If it fails, try to open default device.
{ device_name = deviceName;
Debug.WriteLine(ErrorMessage(deviceName, frequency, sampleFormat, bufferSize)); Handle = Alc.CaptureOpenDevice(deviceName, frequency, sampleFormat, bufferSize);
device_name = "IntPtr.Zero";
Handle = Alc.CaptureOpenDevice(null, frequency, sampleFormat, bufferSize); if (Handle == IntPtr.Zero)
}
if (Handle == IntPtr.Zero)
{ {
Debug.WriteLine(ErrorMessage("IntPtr.Zero", frequency, sampleFormat, bufferSize)); Debug.WriteLine(ErrorMessage(deviceName, frequency, sampleFormat, bufferSize));
device_name = AudioDeviceEnumerator.DefaultRecordingDevice; device_name = "IntPtr.Zero";
Handle = Alc.CaptureOpenDevice(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, sampleFormat, bufferSize); Handle = Alc.CaptureOpenDevice(null, frequency, sampleFormat, bufferSize);
} }
if (Handle == IntPtr.Zero) if (Handle == IntPtr.Zero)
{ {
// Everything we tried failed. Capture may not be supported, bail out. Debug.WriteLine(ErrorMessage("IntPtr.Zero", frequency, sampleFormat, bufferSize));
Debug.WriteLine(ErrorMessage(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, sampleFormat, bufferSize)); device_name = AudioDeviceEnumerator.DefaultRecordingDevice;
device_name = "None"; Handle = Alc.CaptureOpenDevice(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, sampleFormat, bufferSize);
}
throw new AudioDeviceException("All attempts to open capture devices returned IntPtr.Zero. See debug log for verbose list.");
} if (Handle == IntPtr.Zero)
{
// Everything we tried failed. Capture may not be supported, bail out.
Debug.WriteLine(ErrorMessage(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, sampleFormat, bufferSize));
device_name = "None";
throw new AudioDeviceException("All attempts to open capture devices returned IntPtr.Zero. See debug log for verbose list.");
}
// handle is not null, check for some Alc Error // handle is not null, check for some Alc Error
CheckErrors(); CheckErrors();
SampleFormat = sampleFormat; SampleFormat = sampleFormat;
SampleFrequency = frequency; SampleFrequency = frequency;
} }
#endregion Constructor #endregion Constructor
#region IDisposable Members
~AudioCapture()
{
Dispose();
}
private bool IsDisposed;
/// <summary>Closes the device and disposes the instance.</summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool manual)
{
if (!this.IsDisposed)
{
if (this.Handle != IntPtr.Zero)
{
if (this._isrecording)
this.Stop();
Alc.CaptureCloseDevice(this.Handle);
}
this.IsDisposed = true;
}
}
#endregion Destructor
#region Public Members #region Public Members
#region CurrentDevice
private string device_name;
/// <summary>
/// The name of the device associated with this instance.
/// </summary>
public string CurrentDevice
{
get
{
return device_name;
}
}
#endregion
#region AvailableDevices
/// <summary>
/// Returns a list of strings containing all known recording devices.
/// </summary>
public static IList<string> AvailableDevices
{
get
{
return AudioDeviceEnumerator.AvailableRecordingDevices;
}
}
#endregion
#region DefaultDevice
/// <summary>
/// Returns the name of the device that will be used as recording default.
/// </summary>
public static string DefaultDevice
{
get
{
return AudioDeviceEnumerator.DefaultRecordingDevice;
}
}
#endregion
#region CheckErrors #region CheckErrors
/// <summary> /// <summary>
@ -195,70 +181,70 @@ namespace OpenTK.Audio
public void CheckErrors() public void CheckErrors()
{ {
new AudioDeviceErrorChecker(Handle).Dispose(); new AudioDeviceErrorChecker(Handle).Dispose();
} }
#endregion #endregion
#region CurrentError #region CurrentError
/// <summary>Returns the ALC error code for this device.</summary> /// <summary>Returns the ALC error code for this device.</summary>
public AlcError CurrentError public AlcError CurrentError
{ {
get get
{ {
return Alc.GetError(Handle); return Alc.GetError(Handle);
} }
} }
#endregion #endregion
#region Start & Stop #region Start & Stop
/// <summary> /// <summary>
/// Start recording samples. /// Start recording samples.
/// The number of available samples can be obtained through the <see cref="AvailableSamples"/> property. /// The number of available samples can be obtained through the <see cref="AvailableSamples"/> property.
/// The data can be queried with any <see cref="ReadSamples"/> method. /// The data can be queried with any <see cref="ReadSamples"/> method.
/// </summary> /// </summary>
public void Start() public void Start()
{ {
Alc.CaptureStart(Handle); Alc.CaptureStart(Handle);
_isrecording = true; _isrecording = true;
} }
/// <summary>Stop recording samples. This will not clear previously recorded samples.</summary> /// <summary>Stop recording samples. This will not clear previously recorded samples.</summary>
public void Stop() public void Stop()
{ {
Alc.CaptureStop(Handle); Alc.CaptureStop(Handle);
_isrecording = false; _isrecording = false;
} }
#endregion Start & Stop Capture #endregion Start & Stop Capture
#region AvailableSamples #region AvailableSamples
/// <summary>Returns the number of available samples for capture.</summary> /// <summary>Returns the number of available samples for capture.</summary>
public int AvailableSamples public int AvailableSamples
{ {
get get
{ {
// TODO: Investigate inconsistency between documentation and actual usage. // TODO: Investigate inconsistency between documentation and actual usage.
// Doc claims the 3rd param is Number-of-Bytes, but it appears to be Number-of-Int32s // Doc claims the 3rd param is Number-of-Bytes, but it appears to be Number-of-Int32s
int result; int result;
Alc.GetInteger(Handle, AlcGetInteger.CaptureSamples, 1, out result); Alc.GetInteger(Handle, AlcGetInteger.CaptureSamples, 1, out result);
return result; return result;
} }
} }
#endregion Available samples property #endregion Available samples property
#region ReadSamples #region ReadSamples
/// <summary>Fills the specified buffer with samples from the internal capture ring-buffer. This method does not block: it is an error to specify a sampleCount larger than AvailableSamples.</summary> /// <summary>Fills the specified buffer with samples from the internal capture ring-buffer. This method does not block: it is an error to specify a sampleCount larger than AvailableSamples.</summary>
/// <param name="buffer">A pointer to a previously initialized and pinned array.</param> /// <param name="buffer">A pointer to a previously initialized and pinned array.</param>
/// <param name="sampleCount">The number of samples to be written to the buffer.</param> /// <param name="sampleCount">The number of samples to be written to the buffer.</param>
public void ReadSamples(IntPtr buffer, int sampleCount) public void ReadSamples(IntPtr buffer, int sampleCount)
{ {
Alc.CaptureSamples(Handle, buffer, sampleCount); Alc.CaptureSamples(Handle, buffer, sampleCount);
} }
/// <summary>Fills the specified buffer with samples from the internal capture ring-buffer. This method does not block: it is an error to specify a sampleCount larger than AvailableSamples.</summary> /// <summary>Fills the specified buffer with samples from the internal capture ring-buffer. This method does not block: it is an error to specify a sampleCount larger than AvailableSamples.</summary>
@ -284,8 +270,8 @@ namespace OpenTK.Audio
GCHandle buffer_ptr = GCHandle.Alloc(buffer, GCHandleType.Pinned); GCHandle buffer_ptr = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try { ReadSamples(buffer_ptr.AddrOfPinnedObject(), sampleCount); } try { ReadSamples(buffer_ptr.AddrOfPinnedObject(), sampleCount); }
finally { buffer_ptr.Free(); } finally { buffer_ptr.Free(); }
} }
#endregion #endregion
#region SampleFormat & SampleFrequency #region SampleFormat & SampleFrequency
@ -310,6 +296,18 @@ namespace OpenTK.Audio
#endregion #endregion
#region IsRunning
/// <summary>
/// Gets a value indicating whether this instance is currently capturing samples.
/// </summary>
public bool IsRunning
{
get { return _isrecording; }
}
#endregion
#endregion #endregion
#region Private Members #region Private Members
@ -332,7 +330,7 @@ namespace OpenTK.Audio
case ALFormat.MultiQuad8Ext: return 4; case ALFormat.MultiQuad8Ext: return 4;
case ALFormat.MultiQuad16Ext: return 8; case ALFormat.MultiQuad16Ext: return 8;
case ALFormat.MultiQuad32Ext: return 16; case ALFormat.MultiQuad32Ext: return 16;
case ALFormat.Multi51Chn8Ext: return 6; case ALFormat.Multi51Chn8Ext: return 6;
case ALFormat.Multi51Chn16Ext: return 12; case ALFormat.Multi51Chn16Ext: return 12;
case ALFormat.Multi51Chn32Ext: return 24; case ALFormat.Multi51Chn32Ext: return 24;
@ -375,5 +373,38 @@ namespace OpenTK.Audio
} }
#endregion #endregion
}
} #region IDisposable Members
~AudioCapture()
{
Dispose();
}
private bool IsDisposed;
/// <summary>Closes the device and disposes the instance.</summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool manual)
{
if (!this.IsDisposed)
{
if (this.Handle != IntPtr.Zero)
{
if (this._isrecording)
this.Stop();
Alc.CaptureCloseDevice(this.Handle);
}
this.IsDisposed = true;
}
}
#endregion Destructor
}
}

View file

@ -1,28 +1,28 @@
#region License #region License
// //
// The Open Toolkit Library License // The Open Toolkit Library License
// //
// Copyright (c) 2006 - 2009 the Open Toolkit library. // Copyright (c) 2006 - 2009 the Open Toolkit library.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights to // in the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do // the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions: // so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. // OTHER DEALINGS IN THE SOFTWARE.
// //
#endregion #endregion
using System; using System;