mirror of
https://github.com/Ryujinx/Opentk.git
synced 2024-12-25 14:25:39 +00:00
Added SampleFormat and SampleFrequency properties.
Added CheckErrors method. Renamed GetSamples to ReadSamples and added generic overload. Improved error checking code.
This commit is contained in:
parent
61a48526e3
commit
bdd49a2aa1
|
@ -27,7 +27,8 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace OpenTK.Audio
|
namespace OpenTK.Audio
|
||||||
{
|
{
|
||||||
|
@ -38,13 +39,18 @@ namespace OpenTK.Audio
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AudioCapture : IDisposable
|
public sealed class AudioCapture : IDisposable
|
||||||
{
|
{
|
||||||
#region private fields
|
#region Fields
|
||||||
/// <summary>This must stay private info so the end-user cannot call any Alc commands for the recording device.</summary>
|
|
||||||
private IntPtr Handle;
|
|
||||||
|
|
||||||
/// <summary>Alc.CaptureStop should be called prior to device shutdown, this keeps track of Alc.CaptureStart/Stop calls.</summary>
|
// This must stay private info so the end-user cannot call any Alc commands for the recording device.
|
||||||
private bool _isrecording = false;
|
IntPtr Handle;
|
||||||
#endregion private fields
|
|
||||||
|
// Alc.CaptureStop should be called prior to device shutdown, this keeps track of Alc.CaptureStart/Stop calls.
|
||||||
|
bool _isrecording = false;
|
||||||
|
|
||||||
|
ALFormat sample_format;
|
||||||
|
int sample_frequency;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Device Name
|
#region Device Name
|
||||||
private string device_name;
|
private string device_name;
|
||||||
|
@ -68,8 +74,8 @@ namespace OpenTK.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Returns the name of the device that will be used as recording default.</summary>
|
/// <summary>Returns the name of the device that will be used as recording default.</summary>
|
||||||
public static string Default
|
public static string DefaultDevice
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -87,106 +93,62 @@ namespace OpenTK.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#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<string> ErrorMessages = new List<string>();
|
|
||||||
#endregion Internal Error handling
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the default device for audio recording.
|
/// Opens the default device for audio recording.
|
||||||
/// Implicitly set parameters are: 22050Hz, 16Bit Mono, 4096 samples ringbuffer.
|
/// Implicitly set parameters are: 22050Hz, 16Bit Mono, 4096 samples ringbuffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AudioCapture():this(AudioCapture.Default, 22050, ALFormat.Mono16, 4096)
|
public AudioCapture()
|
||||||
|
: 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="bufferformat">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 bufferformat, int buffersize)
|
public AudioCapture(string deviceName, int frequency, ALFormat sampleFormat, int bufferSize)
|
||||||
: this(devicename, (uint)frequency, bufferformat, buffersize)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Opens a device for audio recording.</summary>
|
|
||||||
/// <param name="devicename">The device name.</param>
|
|
||||||
/// <param name="frequency">The frequency that the data should be captured at.</param>
|
|
||||||
/// <param name="bufferformat">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>
|
|
||||||
[CLSCompliant(false)]
|
|
||||||
public AudioCapture(string devicename, uint frequency, ALFormat bufferformat, int buffersize)
|
|
||||||
{
|
{
|
||||||
if (!AudioDeviceEnumerator.IsOpenALSupported)
|
if (!AudioDeviceEnumerator.IsOpenALSupported)
|
||||||
throw new DllNotFoundException("openal32.dll");
|
throw new DllNotFoundException("openal32.dll");
|
||||||
|
|
||||||
device_name = devicename;
|
// Try to open specified device. If it fails, try to open default device.
|
||||||
Handle = Alc.CaptureOpenDevice(devicename, frequency, bufferformat, buffersize);
|
device_name = deviceName;
|
||||||
|
Handle = Alc.CaptureOpenDevice(deviceName, frequency, sampleFormat, bufferSize);
|
||||||
|
|
||||||
if (Handle == IntPtr.Zero)
|
if (Handle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
ErrorMessages.Add(ErrorMessage(devicename, frequency, bufferformat, buffersize));
|
Debug.WriteLine(ErrorMessage(deviceName, frequency, sampleFormat, bufferSize));
|
||||||
device_name = "IntPtr.Zero";
|
device_name = "IntPtr.Zero";
|
||||||
Handle = Alc.CaptureOpenDevice(null, frequency, bufferformat, buffersize);
|
Handle = Alc.CaptureOpenDevice(null, frequency, sampleFormat, bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Handle == IntPtr.Zero)
|
if (Handle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
ErrorMessages.Add(ErrorMessage("IntPtr.Zero", frequency, bufferformat, buffersize));
|
Debug.WriteLine(ErrorMessage("IntPtr.Zero", frequency, sampleFormat, bufferSize));
|
||||||
device_name = AudioDeviceEnumerator.DefaultRecordingDevice;
|
device_name = AudioDeviceEnumerator.DefaultRecordingDevice;
|
||||||
Handle = Alc.CaptureOpenDevice(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, bufferformat, buffersize);
|
Handle = Alc.CaptureOpenDevice(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, sampleFormat, bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Handle == IntPtr.Zero)
|
if (Handle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
ErrorMessages.Add(ErrorMessage(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, bufferformat, buffersize));
|
// Everything we tried failed. Capture may not be supported, bail out.
|
||||||
// everything failed
|
Debug.WriteLine(ErrorMessage(AudioDeviceEnumerator.DefaultRecordingDevice, frequency, sampleFormat, bufferSize));
|
||||||
device_name = "None";
|
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.");
|
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
|
||||||
AlcError err = this.CurrentAlcError;
|
CheckErrors();
|
||||||
switch (err)
|
|
||||||
{
|
SampleFormat = sampleFormat;
|
||||||
case AlcError.NoError:
|
SampleFrequency = frequency;
|
||||||
// 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
|
#endregion Constructor
|
||||||
|
|
||||||
#region Destructor
|
#region IDisposable Members
|
||||||
|
|
||||||
~AudioCapture()
|
~AudioCapture()
|
||||||
{
|
{
|
||||||
|
@ -217,12 +179,30 @@ namespace OpenTK.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Destructor
|
#endregion Destructor
|
||||||
|
|
||||||
|
#region Public Members
|
||||||
|
|
||||||
|
#region CheckErrors
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for ALC error conditions.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="OutOfMemoryException">Raised when an out of memory error is detected.</exception>
|
||||||
|
/// <exception cref="AudioValueException">Raised when an invalid value is detected.</exception>
|
||||||
|
/// <exception cref="AudioDeviceException">Raised when an invalid device is detected.</exception>
|
||||||
|
/// <exception cref="AudioContextException">Raised when an invalid context is detected.</exception>
|
||||||
|
public void CheckErrors()
|
||||||
|
{
|
||||||
|
new AudioDeviceErrorChecker(Handle).Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
#region Error Checking
|
#endregion
|
||||||
|
|
||||||
/// <summary>Returns the first encountered Alc Error by this device.</summary>
|
#region CurrentError
|
||||||
public AlcError CurrentAlcError
|
|
||||||
|
/// <summary>Returns the ALC error code for this device.</summary>
|
||||||
|
public AlcError CurrentError
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -230,14 +210,14 @@ namespace OpenTK.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Error Checking
|
#endregion
|
||||||
|
|
||||||
#region Start & Stop Capture
|
#region Start & Stop
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start recording samples.
|
/// Start recording samples.
|
||||||
/// The number of available samples can be obtained through the AvailableSamples property.
|
/// The number of available samples can be obtained through the <see cref="AvailableSamples"/> property.
|
||||||
/// The data can be queried with any GetSamples() method.
|
/// The data can be queried with any <see cref="ReadSamples"/> method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
|
@ -252,10 +232,10 @@ namespace OpenTK.Audio
|
||||||
_isrecording = false;
|
_isrecording = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Start & Stop Capture
|
#endregion Start & Stop Capture
|
||||||
|
|
||||||
#region Available samples property
|
#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
|
||||||
{
|
{
|
||||||
|
@ -269,18 +249,131 @@ namespace OpenTK.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Available samples property
|
#endregion Available samples property
|
||||||
|
|
||||||
#region Capture previously recorded samples
|
#region ReadSamples
|
||||||
|
|
||||||
/// <summary>This function will write previously recorded samples to a location in memory. It does not block.</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 GetSamples(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>
|
||||||
|
/// <param name="buffer">The buffer to fill.</param>
|
||||||
|
/// <param name="sampleCount">The number of samples to be written to the buffer.</param>
|
||||||
|
/// <exception cref="System.ArgumentNullException">Raised when buffer is null.</exception>
|
||||||
|
/// <exception cref="System.ArgumentOutOfRangeException">Raised when sampleCount is larger than the buffer.</exception>
|
||||||
|
public void ReadSamples<TBuffer>(TBuffer[] buffer, int sampleCount)
|
||||||
|
where TBuffer : struct
|
||||||
|
{
|
||||||
|
if (buffer == null)
|
||||||
|
throw new ArgumentNullException("buffer");
|
||||||
|
|
||||||
|
int buffer_size = BlittableValueType<TBuffer>.Stride * buffer.Length;
|
||||||
|
// This is more of a heuristic than a 100% valid check. However, it will work
|
||||||
|
// correctly for 99.9% of all use cases.
|
||||||
|
// This should never produce a false positive, but a false negative might
|
||||||
|
// be produced with compressed sample formats (which are very rare).
|
||||||
|
// Still, this is better than no check at all.
|
||||||
|
if (sampleCount * GetSampleSize(SampleFormat) > buffer_size)
|
||||||
|
throw new ArgumentOutOfRangeException("sampleCount");
|
||||||
|
|
||||||
|
GCHandle buffer_ptr = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
try { ReadSamples(buffer_ptr.AddrOfPinnedObject(), sampleCount); }
|
||||||
|
finally { buffer_ptr.Free(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Capture previously recorded samples
|
#endregion
|
||||||
|
|
||||||
|
#region SampleFormat & SampleFrequency
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the OpenTK.Audio.ALFormat for this instance.
|
||||||
|
/// </summary>
|
||||||
|
public ALFormat SampleFormat
|
||||||
|
{
|
||||||
|
get { return sample_format; }
|
||||||
|
private set { sample_format = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the sampling rate for this instance.
|
||||||
|
/// </summary>
|
||||||
|
public int SampleFrequency
|
||||||
|
{
|
||||||
|
get { return sample_frequency; }
|
||||||
|
private set { sample_frequency = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Members
|
||||||
|
|
||||||
|
// Retrieves the sample size in bytes for various ALFormats.
|
||||||
|
// Compressed formats always return 1.
|
||||||
|
static int GetSampleSize(ALFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case ALFormat.Mono8: return 1;
|
||||||
|
case ALFormat.Mono16: return 2;
|
||||||
|
case ALFormat.Stereo8: return 2;
|
||||||
|
case ALFormat.Stereo16: return 4;
|
||||||
|
case ALFormat.MonoFloat32Ext: return 4;
|
||||||
|
case ALFormat.MonoDoubleExt: return 8;
|
||||||
|
case ALFormat.StereoFloat32Ext: return 8;
|
||||||
|
case ALFormat.StereoDoubleExt: return 16;
|
||||||
|
|
||||||
|
case ALFormat.MultiQuad8Ext: return 4;
|
||||||
|
case ALFormat.MultiQuad16Ext: return 8;
|
||||||
|
case ALFormat.MultiQuad32Ext: return 16;
|
||||||
|
|
||||||
|
case ALFormat.Multi51Chn8Ext: return 6;
|
||||||
|
case ALFormat.Multi51Chn16Ext: return 12;
|
||||||
|
case ALFormat.Multi51Chn32Ext: return 24;
|
||||||
|
|
||||||
|
case ALFormat.Multi61Chn8Ext: return 7;
|
||||||
|
case ALFormat.Multi71Chn16Ext: return 14;
|
||||||
|
case ALFormat.Multi71Chn32Ext: return 28;
|
||||||
|
|
||||||
|
case ALFormat.MultiRear8Ext: return 1;
|
||||||
|
case ALFormat.MultiRear16Ext: return 2;
|
||||||
|
case ALFormat.MultiRear32Ext: return 4;
|
||||||
|
|
||||||
|
default: return 1; // Unknown sample size.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts an error code to an error string with additional information.
|
||||||
|
string ErrorMessage(string devicename, int frequency, ALFormat bufferformat, int buffersize)
|
||||||
|
{
|
||||||
|
string alcerrmsg;
|
||||||
|
AlcError alcerrcode = CurrentError;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue