diff --git a/audiotest/OpenTK.dll b/audiotest/OpenTK.dll
new file mode 100644
index 0000000..c335751
--- /dev/null
+++ b/audiotest/OpenTK.dll
Binary files differ
diff --git a/audiotest/OpenTK.dll.config b/audiotest/OpenTK.dll.config
new file mode 100644
index 0000000..7098d39
--- /dev/null
+++ b/audiotest/OpenTK.dll.config
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/audiotest/WavFile.cs b/audiotest/WavFile.cs
new file mode 100644
index 0000000..0ac207b
--- /dev/null
+++ b/audiotest/WavFile.cs
@@ -0,0 +1,64 @@
+using System;
+using System.IO;
+using System.Text;
+using OpenTK.Audio.OpenAL;
+
+public class WavFile
+{
+ public byte[] Data;
+ public ALFormat Format; // Al.FORMAT_*
+ public int SampleRate;
+
+ public static WavFile Load(string path)
+ {
+ using (var fs = File.OpenRead(path))
+ using (var br = new BinaryReader(fs))
+ {
+ if (new string(br.ReadChars(4)) != "RIFF") throw new Exception("Not RIFF: " + path);
+ br.ReadInt32();
+ if (new string(br.ReadChars(4)) != "WAVE") throw new Exception("Not WAVE: " + path);
+
+ short channels = 0, bits = 0;
+ int rate = 0;
+ byte[] data = null;
+
+ while (fs.Position < fs.Length - 8)
+ {
+ string id = new string(br.ReadChars(4));
+ int len = br.ReadInt32();
+
+ if (id == "fmt ")
+ {
+ br.ReadInt16(); // audio format (1 = PCM)
+ channels = br.ReadInt16();
+ rate = br.ReadInt32();
+ br.ReadInt32(); // byte rate
+ br.ReadInt16(); // block align
+ bits = br.ReadInt16();
+ if (len > 16) br.ReadBytes(len - 16);
+ }
+ else if (id == "data")
+ {
+ data = br.ReadBytes(len);
+ }
+ else
+ {
+ br.ReadBytes(len); // skip unknown chunk
+ }
+
+ if ((len & 1) == 1) br.ReadByte(); // chunks are word-aligned
+ }
+
+ if (data == null) throw new Exception("No data chunk: " + path);
+
+ ALFormat fmt;
+ if (channels == 1 && bits == 8) fmt = ALFormat.Mono8;
+ else if (channels == 1 && bits == 16) fmt = ALFormat.Mono16;
+ else if (channels == 2 && bits == 8) fmt = ALFormat.Stereo8;
+ else if (channels == 2 && bits == 16) fmt = ALFormat.Stereo16;
+ else throw new Exception("Unsupported WAV: " + channels + "ch " + bits + "bit");
+
+ return new WavFile { Data = data, Format = fmt, SampleRate = rate };
+ }
+ }
+}
\ No newline at end of file
diff --git a/audiotest/alsoft.conf b/audiotest/alsoft.conf
new file mode 100644
index 0000000..fdbb03b
--- /dev/null
+++ b/audiotest/alsoft.conf
@@ -0,0 +1,6 @@
+[general]
+drivers = pulse,alsa
+resampler = bsinc24
+period_size = 1024
+periods = 4
+output-limiter = true
\ No newline at end of file
diff --git a/audiotest/altest.cs b/audiotest/altest.cs
new file mode 100644
index 0000000..8b812af
--- /dev/null
+++ b/audiotest/altest.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Threading;
+using OpenTK.Audio;
+using OpenTK.Audio.OpenAL;
+
+class AlTest
+{
+ static void Main(string[] args)
+ {
+ if (args.Length < 1)
+ {
+ Console.WriteLine("usage: altest [gain 0..1]");
+ return;
+ }
+ float gain = args.Length > 1 ? float.Parse(args[1]) : 1.0f;
+
+ var ctx = new AudioContext();
+ ctx.MakeCurrent();
+ Console.WriteLine("device: " + AudioContext.CurrentContext.CurrentDevice);
+
+ WavFile w = WavFile.Load(args[0]);
+ Console.WriteLine("{0} {1} Hz {2} bytes", w.Format, w.SampleRate, w.Data.Length);
+
+ int buf = AL.GenBuffer();
+ AL.BufferData(buf, w.Format, w.Data, w.Data.Length, w.SampleRate);
+
+ int src = AL.GenSource();
+ AL.Source(src, ALSourcei.Buffer, buf);
+ AL.Source(src, ALSourcef.Gain, gain);
+ AL.SourcePlay(src);
+
+ ALError e = AL.GetError();
+ if (e != ALError.NoError) Console.WriteLine("AL error: " + AL.GetErrorString(e));
+
+ while (AL.GetSourceState(src) == ALSourceState.Playing)
+ Thread.Sleep(50);
+
+ AL.DeleteSource(src);
+ AL.DeleteBuffer(buf);
+ ctx.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/audiotest/altest.exe b/audiotest/altest.exe
new file mode 100644
index 0000000..283c6d2
--- /dev/null
+++ b/audiotest/altest.exe
Binary files differ
diff --git a/audiotest/build.sh b/audiotest/build.sh
new file mode 100644
index 0000000..e734470
--- /dev/null
+++ b/audiotest/build.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+set -e
+cd "$(dirname "$0")/.."
+
+OUT=audiotest
+rm -rf "$OUT"
+mkdir -p "$OUT/src" "$OUT/sound"
+
+cp altest.cs WavFile.cs "$OUT/src/"
+cp OpenTK.dll OpenTK.dll.config "$OUT/"
+cp bin/linux/sound/SND200.wav "$OUT/sound/"
+cp audiotest_files/{install.sh,run.sh,alsoft.conf} "$OUT/"
+
+mcs "$OUT/src/altest.cs" "$OUT/src/WavFile.cs" \
+ -r:"$OUT/OpenTK.dll" -out:"$OUT/altest.exe"
+
+chmod +x "$OUT"/*.sh
+tar czf audiotest.tar.gz "$OUT"
+echo "built audiotest.tar.gz"
\ No newline at end of file
diff --git a/audiotest/readme.md b/audiotest/readme.md
new file mode 100644
index 0000000..923cf84
--- /dev/null
+++ b/audiotest/readme.md
@@ -0,0 +1,3 @@
+sudo apt install mono-runtime libmono-system-drawing4.0-cil libopenal1 pulseaudio-utils sox
+paplay sound/SND200.wav
+mono ./altest.exe sound/SND200.wav
\ No newline at end of file