diff --git a/Source/OpenTK/Audio/AudioCapture.cs b/Source/OpenTK/Audio/AudioCapture.cs
new file mode 100644
index 00000000..427f5577
--- /dev/null
+++ b/Source/OpenTK/Audio/AudioCapture.cs
@@ -0,0 +1,286 @@
+#region License
+//
+// The Open Toolkit Library License
+//
+// Copyright (c) 2006 - 2009 the Open Toolkit library.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights to
+// 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
+// so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace OpenTK.Audio
+{
+
+ ///
+ /// Provides methods to instantiate, use and destroy an audio device for recording.
+ /// Static methods are provided to list available devices known by the driver.
+ ///
+ public sealed class AudioCapture : IDisposable
+ {
+ #region private fields
+ /// This must stay private info so the end-user cannot call any Alc commands for the recording device.
+ private IntPtr Handle;
+
+ /// Alc.CaptureStop should be called prior to device shutdown, this keeps track of Alc.CaptureStart/Stop calls.
+ private bool _isrecording = false;
+ #endregion private fields
+
+ #region Device Name
+ private string device_name;
+ /// The name of the device associated with this instance.
+ public string CurrentDeviceName
+ {
+ get
+ {
+ return device_name;
+ }
+ }
+ #endregion Device Name
+
+ #region public static properties
+ /// Returns a list of strings containing all known recording devices.
+ public static IList AvailableDevices
+ {
+ get
+ {
+ return AudioDeviceEnumerator.AvailableRecordingDevices;
+ }
+ }
+
+ /// Returns the name of the device that will be used as recording default.
+ public static string Default
+ {
+ get
+ {
+ return AudioDeviceEnumerator.DefaultRecordingDevice;
+ }
+ }
+ #endregion public static properties
+
+ #region Constructor
+
+ static AudioCapture()
+ {
+ if (AudioDeviceEnumerator.IsOpenALSupported) // forces enumeration
+ {
+ }
+ }
+
+ #region Internal Error handling
+ private string ErrorMessage(string devicename, uint frequency, ALFormat bufferformat, int buffersize)
+ {
+ string alcerrmsg;
+ AlcError alcerrcode = Alc.GetError(IntPtr.Zero);
+ switch (alcerrcode)
+ {
+ case AlcError.OutOfMemory:
+ alcerrmsg = alcerrcode.ToString() + ": The specified device is invalid, or can not capture audio.";
+ break;
+ case AlcError.InvalidValue:
+ alcerrmsg = alcerrcode.ToString() + ": One of the parameters has an invalid value.";
+ break;
+ default:
+ alcerrmsg = alcerrcode.ToString();
+ break;
+ }
+ return "The handle returned by Alc.CaptureOpenDevice is null." +
+ "\nAlc Error: " + alcerrmsg +
+ "\nDevice Name: " + devicename +
+ "\nCapture frequency: " + frequency +
+ "\nBuffer format: " + bufferformat +
+ "\nBuffer Size: " + buffersize;
+ }
+
+ private List ErrorMessages = new List();
+ #endregion Internal Error handling
+
+ ///
+ /// Opens the default device for audio recording.
+ /// Implicitly set parameters are: 22050Hz, 16Bit Mono, 4096 samples ringbuffer.
+ ///
+ public AudioCapture():this(AudioCapture.Default, 22050, ALFormat.Mono16, 4096)
+ {
+ }
+
+ /// Opens a device for audio recording.
+ /// The device name.
+ /// The frequency that the data should be captured at.
+ /// The requested capture buffer format.
+ /// The size of OpenAL's capture internal ring-buffer. This value expects number of samples, not bytes.
+ public AudioCapture(string devicename, int frequency, ALFormat bufferformat, int buffersize)
+ : this(devicename, (uint)frequency, bufferformat, buffersize)
+ {
+ }
+
+ /// Opens a device for audio recording.
+ /// The device name.
+ /// The frequency that the data should be captured at.
+ /// The requested capture buffer format.
+ /// The size of OpenAL's capture internal ring-buffer. This value expects number of samples, not bytes.
+ [CLSCompliant(false)]
+ public AudioCapture(string devicename, uint frequency, ALFormat bufferformat, int buffersize)
+ {
+ if (!AudioDeviceEnumerator.IsOpenALSupported)
+ throw new DllNotFoundException("openal32.dll");
+
+ device_name = devicename;
+ Handle = Alc.CaptureOpenDevice(devicename, frequency, bufferformat, buffersize);
+ if (Handle == IntPtr.Zero)
+ {
+ ErrorMessages.Add(ErrorMessage(devicename, frequency, bufferformat, buffersize));
+ device_name = "IntPtr.Zero";
+ Handle = Alc.CaptureOpenDevice(null, frequency, bufferformat, buffersize);
+ }
+ if (Handle == IntPtr.Zero)
+ {
+ ErrorMessages.Add(ErrorMessage("IntPtr.Zero", frequency, bufferformat, buffersize));
+ device_name = AudioDeviceEnumerator.DefaultRecordingDevice;
+ Handle = Alc.CaptureOpenDevice(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, bufferformat, buffersize);
+ }
+ if (Handle == IntPtr.Zero)
+ {
+ ErrorMessages.Add(ErrorMessage(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, bufferformat, buffersize));
+ // everything failed
+ device_name = "None";
+ foreach (string s in ErrorMessages)
+ Debug.WriteLine(s);
+ 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
+ AlcError err = this.CurrentAlcError;
+ switch (err)
+ {
+ case AlcError.NoError:
+ // everything went fine, do nothing
+ break;
+ case AlcError.OutOfMemory:
+ throw new AudioDeviceException("Alc.CaptureOpenDevice (Handle: " + Handle + ") reports Alc Error (" + err.ToString() + ") The specified device is invalid, or can not capture audio.");
+ case AlcError.InvalidValue:
+ throw new AudioDeviceException("Alc.CaptureOpenDevice (Handle: " + Handle + ") reports Alc Error (" + err.ToString() + ") One of the parameters has an invalid value.");
+ default:
+ throw new AudioDeviceException("Alc.CaptureOpenDevice (Handle: " + Handle + ") reports Alc Error: " + err.ToString());
+ }
+ }
+
+ #endregion Constructor
+
+ #region Destructor
+
+ ~AudioCapture()
+ {
+ Dispose();
+ }
+
+ private bool IsDisposed;
+
+ /// Closes the device and disposes the instance.
+ 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 Error Checking
+
+ /// Returns the first encountered Alc Error by this device.
+ public AlcError CurrentAlcError
+ {
+ get
+ {
+ return Alc.GetError(Handle);
+ }
+ }
+
+ #endregion Error Checking
+
+ #region Start & Stop Capture
+
+ ///
+ /// Start recording samples.
+ /// The number of available samples can be obtained through the AvailableSamples property.
+ /// The data can be queried with any GetSamples() method.
+ ///
+ public void Start()
+ {
+ Alc.CaptureStart(Handle);
+ _isrecording = true;
+ }
+
+ /// Stop recording samples. This will not clear previously recorded samples.
+ public void Stop()
+ {
+ Alc.CaptureStop(Handle);
+ _isrecording = false;
+ }
+
+ #endregion Start & Stop Capture
+
+ #region Available samples property
+
+ /// Returns the number of available samples for capture.
+ public int AvailableSamples
+ {
+ get
+ {
+ // 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
+ int result;
+ Alc.GetInteger(Handle, AlcGetInteger.CaptureSamples, 1, out result);
+ return result;
+ }
+ }
+
+ #endregion Available samples property
+
+ #region Capture previously recorded samples
+
+ /// This function will write previously recorded samples to a location in memory. It does not block.
+ /// A pointer to a previously initialized and pinned array.
+ /// The number of samples to be written to the buffer.
+ public void GetSamples(IntPtr buffer, int samplecount)
+ {
+ Alc.CaptureSamples(Handle, buffer, samplecount);
+ }
+
+ #endregion Capture previously recorded samples
+ }
+}