diff --git a/Source/Examples/OpenAL/Test/OpenALDiagnostics.cs b/Source/Examples/OpenAL/Test/OpenALDiagnostics.cs
new file mode 100644
index 00000000..ccce4533
--- /dev/null
+++ b/Source/Examples/OpenAL/Test/OpenALDiagnostics.cs
@@ -0,0 +1,635 @@
+#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;
+using System.Threading;
+using System.Runtime.InteropServices;
+
+using OpenTK;
+using OpenTK.Audio;
+
+namespace Examples
+{
+
+ ///
+ /// A text-based diagnosis program for OpenAL.
+ /// The constructors will call the OpenAL commands, the Print() methods just show the information.
+ ///
+ [Example("OpenAL diagnostics", ExampleCategory.OpenAL, "Test", 0, true)]
+ class OpenALDiagnostics
+ {
+ public static void checkForErrors()
+ {
+ {
+ IntPtr device = Alc.GetContextsDevice(Alc.GetCurrentContext());
+ AlcError error = Alc.GetError(device);
+
+ if (error != AlcError.NoError)
+ Trace.WriteLine("ALC ERROR: (" + error + ") " + Alc.GetString(device, (AlcGetString)error));
+ }
+ {
+ ALError error = AL.GetError();
+ if (error != ALError.NoError)
+ Trace.WriteLine("AL ERROR: (" + error + ") " + AL.GetErrorString(error));
+ }
+ }
+
+ [STAThread]
+ static void Main()
+ {
+ Trace.Listeners.RemoveAt(0);
+ Trace.Listeners.Add(new ConsoleTraceListener());
+
+ Trace.WriteLine("This application is currently running as " + (IntPtr.Size == 4 ? "x86" : "x64"));
+
+ DeviceDiagnostic DevDiag = new DeviceDiagnostic();
+ DevDiag.Print();
+ DevDiag = null;
+
+ using (AudioContext A = new AudioContext())
+ {
+ AlcDiagnostic AlcDiag = new AlcDiagnostic(Alc.GetContextsDevice(Alc.GetCurrentContext()));
+ checkForErrors();
+ AlcDiag.Print();
+ AlcDiag = null;
+
+ ALDiagnostic ALdiag = new ALDiagnostic(A);
+ checkForErrors();
+ ALdiag.Print();
+ ALdiag = null;
+
+ EfxDiagnostic EfxDiag = new EfxDiagnostic();
+ checkForErrors();
+ EfxDiag.Print();
+ EfxDiag = null;
+
+ XRamDiagnostic XRamDiag = new XRamDiagnostic();
+ checkForErrors();
+ XRamDiag.Print();
+ XRamDiag = null;
+
+ RecorderDiagnostic rec = new RecorderDiagnostic();
+ rec.Print();
+ rec = null;
+ }
+
+ Trace.WriteLine("All done. Press Enter to exit.");
+ Console.ReadLine();
+ }
+ }
+
+ class DeviceDiagnostic
+ {
+ #region Fields
+ public readonly IList AllPlaybackDevices;
+ public readonly IList AllRecordingDevices;
+
+ public readonly string DefaultPlaybackDevice;
+ public readonly string DefaultRecordingDevice;
+ #endregion Fields
+
+ public DeviceDiagnostic()
+ {
+ Trace.WriteLine("--- Device related errors ---");
+
+ AllPlaybackDevices = AudioContext.AvailableDevices;
+ AllRecordingDevices = AudioCapture.AvailableDevices;
+
+ DefaultPlaybackDevice = AudioContext.Default;
+ DefaultRecordingDevice = AudioCapture.Default;
+ }
+
+ public void Print()
+ {
+ Trace.WriteLine("--- Device related analysis ---");
+ Trace.Indent();
+ {
+ Trace.WriteLine("Default playback device: " + DefaultPlaybackDevice);
+ Trace.WriteLine("All known playback devices:");
+ Trace.Indent();
+ {
+ foreach (string s in AllPlaybackDevices)
+ Trace.WriteLine(s);
+ }
+ Trace.Unindent();
+
+ Trace.WriteLine("Default recording device: " + DefaultRecordingDevice);
+ Trace.WriteLine("All known recording devices:");
+ Trace.Indent();
+ {
+ foreach (string s in AllRecordingDevices)
+ Trace.WriteLine(s);
+ }
+ Trace.Unindent();
+ }
+ Trace.Unindent();
+ }
+ }
+
+ class AlcDiagnostic
+ {
+ #region Fields
+ private string[] Alc_Extension_C_Names = new string[]
+ {
+ "ALC_ENUMERATE_ALL_EXT",
+ "ALC_ENUMERATION_EXT",
+ "ALC_EXT_ASA",
+ "ALC_EXT_ASA_DISTORTION",
+ "ALC_EXT_ASA_ROGER_BEEP",
+ "ALC_EXT_BRS_GAME_LICENSE_REQUIRED",
+ "ALC_EXT_capture",
+ "ALC_EXT_DEDICATED",
+ "ALC_EXT_EFX",
+ };
+
+ public readonly int MajorVersion;
+ public readonly int MinorVersion;
+ public readonly int EfxMajorVersion;
+ public readonly int EfxMinorVersion;
+ public readonly int EfxMaxAuxiliarySends;
+ public readonly string ExtensionString;
+ public readonly Dictionary Extensions = new Dictionary();
+ #endregion Fields
+
+ public AlcDiagnostic(IntPtr dev)
+ {
+ Trace.WriteLine("--- Alc related errors ---");
+
+ Alc.GetInteger(dev, AlcGetInteger.MajorVersion, 1, out MajorVersion);
+ Alc.GetInteger(dev, AlcGetInteger.MinorVersion, 1, out MinorVersion);
+ Alc.GetInteger(dev, AlcGetInteger.EfxMajorVersion, 1, out EfxMajorVersion);
+ Alc.GetInteger(dev, AlcGetInteger.EfxMinorVersion, 1, out EfxMinorVersion);
+ Alc.GetInteger(dev, AlcGetInteger.EfxMaxAuxiliarySends, 1, out EfxMaxAuxiliarySends);
+
+ ExtensionString = Alc.GetString(dev, AlcGetString.Extensions);
+
+ foreach (string s in Alc_Extension_C_Names)
+ Extensions.Add(s, Alc.IsExtensionPresent(dev, s));
+ }
+
+ public void Print()
+ {
+ Trace.WriteLine("--- Alc related analysis ---");
+ Trace.Indent();
+ {
+ Trace.WriteLine("Alc Version: " + MajorVersion + "." + MinorVersion);
+ Trace.WriteLine("Efx Version: " + EfxMajorVersion + "." + EfxMinorVersion);
+ Trace.WriteLine("Efx max. Auxiliary sends: " + EfxMaxAuxiliarySends);
+ Trace.WriteLine("Alc Extension string: " + ExtensionString);
+
+ Trace.WriteLine("Confirmed Alc Extensions:");
+ Trace.Indent();
+ {
+ foreach (KeyValuePair pair in Extensions)
+ Trace.WriteLine(pair.Key + ": " + pair.Value);
+ }
+ Trace.Unindent();
+ }
+ Trace.Unindent();
+ }
+ }
+
+ class ALDiagnostic
+ {
+ #region Fields
+ private string[] AL_Extension_Names = new string[]
+ {
+ "AL_EXT_ALAW",
+ "AL_EXT_BFORMAT",
+ "AL_EXT_double",
+ "AL_EXT_EXPONENT_DISTANCE",
+ "AL_EXT_float32",
+ "AL_EXT_FOLDBACK",
+ "AL_EXT_IMA4",
+ "AL_EXT_LINEAR_DISTANCE",
+ "AL_EXT_MCFORMATS",
+ "AL_EXT_mp3",
+ "AL_EXT_MULAW",
+ "AL_EXT_OFFSET",
+ "AL_EXT_vorbis",
+ "AL_LOKI_quadriphonic",
+ "EAX-RAM",
+ "EAX",
+ "EAX1.0",
+ "EAX2.0",
+ "EAX3.0",
+ "EAX3.0EMULATED",
+ "EAX4.0",
+ "EAX4.0EMULATED",
+ "EAX5.0"
+ };
+
+ public readonly Dictionary Extensions = new Dictionary();
+
+ public readonly string DeviceName;
+ public readonly float SpeedOfSound;
+ public readonly string ExtensionString;
+ public readonly string Renderer;
+ public readonly string Vendor;
+ public readonly string Version;
+ public readonly ALDistanceModel DistanceModel;
+
+ const uint MaxSourcesLimit = 128;
+ public readonly uint MaxSources;
+ #endregion Fields
+
+ public ALDiagnostic(AudioContext ac)
+ {
+ Trace.WriteLine("--- AL related errors ---");
+
+ DeviceName = ac.CurrentDeviceName;
+
+ ExtensionString = AL.Get(ALGetString.Extensions);
+ Renderer = AL.Get(ALGetString.Renderer);
+ Vendor = AL.Get(ALGetString.Vendor);
+ Version = AL.Get(ALGetString.Version);
+
+ SpeedOfSound = AL.Get(ALGetFloat.SpeedOfSound);
+ DistanceModel = AL.GetDistanceModel();
+
+ foreach (string s in AL_Extension_Names)
+ Extensions.Add(s, AL.IsExtensionPresent(s));
+
+ AL.GetError(); // clear it, need this for the source counting to work properly
+ uint[] dummy_sources = new uint[MaxSourcesLimit];
+ for (MaxSources = 0; MaxSources < MaxSourcesLimit; MaxSources++)
+ {
+ AL.GenSource(out dummy_sources[MaxSources]);
+ if (AL.GetError() != ALError.NoError)
+ break;
+ }
+
+ for (int i = 0; i < MaxSources; i++)
+ AL.DeleteSource(ref dummy_sources[i]);
+ }
+
+ public void Print()
+ {
+ Trace.WriteLine("--- AL related analysis ---");
+ Trace.Indent();
+ {
+ Trace.WriteLine("Used Device: "+DeviceName);
+ Trace.WriteLine("AL Renderer: " + Renderer);
+ Trace.WriteLine("AL Vendor: " + Vendor);
+ Trace.WriteLine("AL Version: " + Version);
+
+ Trace.WriteLine("AL Speed of sound: " + SpeedOfSound);
+ Trace.WriteLine("AL Distance Model: " + DistanceModel.ToString());
+ Trace.WriteLine("AL Maximum simultanous Sources: " + MaxSources);
+
+ Trace.WriteLine("AL Extension string: " + ExtensionString);
+ Trace.WriteLine("Confirmed AL Extensions:");
+ Trace.Indent();
+ {
+ foreach (KeyValuePair pair in Extensions)
+ Trace.WriteLine(pair.Key + ": " + pair.Value);
+ }
+ Trace.Unindent();
+ }
+ Trace.Unindent();
+ }
+ }
+
+ class EfxDiagnostic
+ {
+ #region Fields
+ private EfxEffectType[] EffectNames = new EfxEffectType[]
+ {
+ EfxEffectType.Autowah,
+ EfxEffectType.Chorus,
+ EfxEffectType.Compressor,
+ EfxEffectType.Distortion,
+ EfxEffectType.EaxReverb,
+ EfxEffectType.Echo,
+ EfxEffectType.Equalizer,
+ EfxEffectType.Flanger,
+ EfxEffectType.FrequencyShifter,
+ EfxEffectType.PitchShifter,
+ EfxEffectType.Reverb,
+ EfxEffectType.RingModulator,
+ EfxEffectType.VocalMorpher,
+ };
+
+ private EfxFilterType[] FilterNames = new EfxFilterType[]
+ {
+ EfxFilterType.Bandpass,
+ EfxFilterType.Highpass,
+ EfxFilterType.Lowpass,
+ };
+
+ public readonly Dictionary Effects = new Dictionary();
+ public readonly Dictionary Filters = new Dictionary();
+
+ const uint MaxAuxiliaryEffectSlotsLimit = 4;
+ public readonly uint MaxAuxiliaryEffectSlots;
+ #endregion Fields
+
+ public EfxDiagnostic()
+ {
+ Trace.WriteLine("--- Efx related errors ---");
+
+ EffectsExtension Efx = new EffectsExtension();
+ Trace.Assert(Efx.IsInitialized);
+
+ #region Test for available Effects
+ uint effect;
+ Efx.GenEffect(out effect);
+ AL.GetError();
+ foreach (EfxEffectType e in EffectNames)
+ {
+ Efx.BindEffect(effect, e);
+ Effects.Add(e.ToString(), AL.GetError() == ALError.NoError);
+ }
+ Efx.DeleteEffect(ref effect);
+ #endregion Test for available Effects
+
+ #region Test for available Filters
+ uint filter;
+ Efx.GenFilter(out filter);
+ AL.GetError();
+ foreach (EfxFilterType f in FilterNames)
+ {
+ Efx.Filter(filter, EfxFilteri.FilterType, (int)f);
+ Filters.Add(f.ToString(), AL.GetError() == ALError.NoError);
+ }
+ Efx.DeleteFilter(ref filter);
+ #endregion Test for available Filters
+
+ AL.GetError();
+ uint[] dummy_slots = new uint[MaxAuxiliaryEffectSlotsLimit];
+ for (MaxAuxiliaryEffectSlots = 0; MaxAuxiliaryEffectSlots < MaxAuxiliaryEffectSlotsLimit; MaxAuxiliaryEffectSlots++)
+ {
+ Efx.GenAuxiliaryEffectSlot(out dummy_slots[MaxAuxiliaryEffectSlots]);
+ if (AL.GetError() != ALError.NoError)
+ break;
+ }
+ for (uint i = 0; i < MaxAuxiliaryEffectSlots; i++)
+ Efx.DeleteAuxiliaryEffectSlot(ref dummy_slots[i]);
+ }
+
+ public void Print()
+ {
+ Trace.WriteLine("--- Efx related analysis ---");
+ Trace.Indent();
+ {
+ Trace.WriteLine("Efx Effects supported:");
+ Trace.Indent();
+ {
+ foreach (KeyValuePair pair in Effects)
+ Trace.WriteLine(pair.Key + ": " + pair.Value);
+ }
+ Trace.Unindent();
+
+ Trace.WriteLine("Efx Filters supported:");
+ Trace.Indent();
+ {
+ foreach (KeyValuePair pair in Filters)
+ Trace.WriteLine(pair.Key + ": " + pair.Value);
+ }
+ Trace.Unindent();
+
+ Trace.WriteLine("Efx max. Auxiliary Effect Slots: " + MaxAuxiliaryEffectSlots);
+ }
+ Trace.Unindent();
+ }
+ }
+
+ class XRamDiagnostic
+ {
+ #region Fields
+ private XRamExtension.XRamStorage[] storagemodes = new XRamExtension.XRamStorage[]
+ {
+ XRamExtension.XRamStorage.Hardware,
+ XRamExtension.XRamStorage.Accessible,
+ XRamExtension.XRamStorage.Automatic,
+ };
+
+ public readonly bool XRamFound;
+ public readonly int RamTotal;
+ public readonly int RamFree;
+ public readonly Dictionary BufferModes = new Dictionary();
+ #endregion Fields
+
+ public XRamDiagnostic()
+ {
+ Trace.WriteLine("--- X-RAM related errors ---");
+
+ XRamExtension XRam = new XRamExtension();
+ if (XRam.IsInitialized)
+ {
+ XRamFound = true;
+
+ RamTotal = XRam.GetRamSize;
+ RamFree = XRam.GetRamFree;
+
+ uint buffer;
+ AL.GenBuffer(out buffer);
+
+ foreach (XRamExtension.XRamStorage m in storagemodes)
+ {
+ bool result = XRam.SetBufferMode(1, ref buffer, m);
+ BufferModes.Add(m.ToString(), m == XRam.GetBufferMode(ref buffer));
+ }
+
+ AL.DeleteBuffer(ref buffer);
+ }
+ else
+ XRamFound = false;
+ }
+
+ public void Print()
+ {
+ Trace.WriteLine("--- X-RAM related analysis ---");
+
+ if (XRamFound)
+ {
+ Trace.Indent(); // *
+ Trace.WriteLine("X-RAM extension found.");
+ }
+ else
+ {
+ Trace.Indent();
+ {
+ Trace.WriteLine("X-RAM extension is not available, skipping test.");
+ }
+ Trace.Unindent();
+ return;
+ }
+
+ Trace.WriteLine("Ram status (free/total) in bytes: " + RamFree + "/" + RamTotal);
+
+ Trace.WriteLine("Available buffer modes:");
+ Trace.Indent();
+ {
+ foreach (KeyValuePair pair in BufferModes)
+ Trace.WriteLine(pair.Key + ": " + pair.Value);
+ }
+ Trace.Unindent();
+
+ Trace.Unindent(); // *
+ }
+ }
+
+ class RecorderDiagnostic
+ {
+ #region Fields
+ bool IsDeviceAvailable;
+ bool BufferContentsAllZero;
+ short MaxSample = short.MinValue;
+ short MinSample = short.MaxValue;
+
+ private AudioCapture r;
+ Dictionary Errorlist = new Dictionary();
+
+ string DeviceName;
+
+ #endregion Fields
+
+ private void CheckRecorderError(string location)
+ {
+ AlcError err = r.CurrentAlcError;
+ if (err != AlcError.NoError)
+ Errorlist.Add(location, err);
+ }
+
+ public RecorderDiagnostic()
+ {
+ Trace.WriteLine("--- AudioCapture related errors ---");
+ IsDeviceAvailable = false;
+
+ try
+ {
+ r = new AudioCapture(AudioCapture.Default, (uint)16000, ALFormat.Mono16, 4096);
+ }
+ catch (AudioDeviceException ade)
+ {
+ Trace.WriteLine("AudioCapture Exception caught: " + ade.Message);
+ return;
+ }
+ IsDeviceAvailable = true;
+ DeviceName = r.CurrentDeviceName;
+ CheckRecorderError("Alc.CaptureOpenDevice");
+
+ r.Start();
+ CheckRecorderError("Alc.CaptureStart");
+ Thread.Sleep(100);
+ r.Stop();
+ CheckRecorderError("Alc.CaptureStop");
+
+ byte[] Buffer = new byte[8192];
+ GCHandle BufferHandle = GCHandle.Alloc(Buffer, GCHandleType.Pinned);
+ IntPtr BufferPtr = BufferHandle.AddrOfPinnedObject();
+
+ int SamplesBefore = r.AvailableSamples;
+ CheckRecorderError("Alc.GetInteger(...CaptureSamples...)");
+ r.GetSamples(BufferPtr, (SamplesBefore > 4096 ? 4096 : SamplesBefore));
+ CheckRecorderError("Alc.CaptureSamples");
+ Thread.Sleep(50); // getsamples doesn't block
+
+ int SamplesCaptured = SamplesBefore - r.AvailableSamples;
+
+ uint ZeroCounter = 0;
+ for (int i = 0; i < SamplesCaptured * 2; i++)
+ {
+ if (Buffer[i] == 0)
+ ZeroCounter++;
+ }
+
+ for (int i = 0; i < SamplesCaptured; i++)
+ {
+ short sample = BitConverter.ToInt16(Buffer, i * 2);
+ if (sample > MaxSample)
+ MaxSample = sample;
+ if (sample < MinSample)
+ MinSample = sample;
+ }
+
+ if (ZeroCounter < SamplesCaptured * 2 && SamplesCaptured > 0)
+ BufferContentsAllZero = false;
+ else
+ BufferContentsAllZero = true;
+
+ r.Dispose();
+ CheckRecorderError("Alc.CaptureCloseDevice");
+
+ // no playback test needed due to Parrot test app.
+ /*
+ uint buf;
+ AL.GenBuffer(out buf);
+ AL.BufferData(buf, ALFormat.Mono16, BufferPtr, SamplesCaptured * 2, 16000);
+ uint src;
+ AL.GenSource(out src);
+ AL.BindBufferToSource(src, buf);
+ AL.Listener(ALListenerf.Gain, 16.0f);
+ AL.SourcePlay(src);
+ while (AL.GetSourceState(src) == ALSourceState.Playing)
+ {
+ Thread.Sleep(0);
+ }
+ AL.SourceStop(src);
+
+ AL.DeleteSource(ref src);
+ AL.DeleteBuffer(ref buf);
+ */
+
+ BufferHandle.Free();
+ }
+
+ public void Print()
+ {
+ Trace.WriteLine("--- AudioCapture related analysis ---");
+
+ if (!IsDeviceAvailable)
+ {
+ Trace.WriteLine("Capture Device could not be initlialized (no microphone plugged into the jack?). Skipping test.");
+ return;
+ }
+
+ Trace.Indent();
+ {
+ Trace.WriteLine("Using capture device: " + DeviceName);
+ if (Errorlist.Count > 0)
+ {
+ Trace.WriteLine("Found Alc Errors:");
+ Trace.Indent();
+ {
+ foreach (KeyValuePair pair in Errorlist)
+ Trace.WriteLine(pair.Key + ": " + pair.Value);
+ }
+ Trace.Unindent();
+
+
+ }
+ Trace.WriteLine("Buffer contents after capture was all 0's: " + BufferContentsAllZero);
+ Trace.WriteLine("Sample min/max in recorded data: " + MinSample + " / " + MaxSample);
+ }
+ Trace.Unindent();
+ }
+ }
+}
\ No newline at end of file