diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..e69de29b
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..b9b7c8ad
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,3 @@
+
+
+
diff --git a/NAudio.Alsa/AlsaCard.cs b/NAudio.Alsa/AlsaCard.cs
new file mode 100644
index 00000000..6d6fa2dd
--- /dev/null
+++ b/NAudio.Alsa/AlsaCard.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using NAudio.Wave;
+namespace NAudio.Wave.Alsa
+{
+ public class AlsaCard : IDisposable
+ {
+ public string IdString { get; private set; }
+ public int Index { get; private set; }
+ public string Id { get; private set; }
+ public string Driver { get; private set; }
+ public string Name { get; private set; }
+ public string LongName { get; private set; }
+ public string MixerName { get; private set; }
+ public string Components { get; private set; }
+ private IntPtr handle;
+ public IntPtr Handle
+ {
+ get => handle;
+ }
+ public List OutputDevices { get; private set; } = new List();
+ public string[] GetDeviceNames()
+ {
+ return new string[] { "" };
+ }
+ private AlsaCard(IntPtr handle)
+ {
+ this.handle = handle;
+ }
+ public static unsafe bool Create(string name, out AlsaCard card)
+ {
+ int error;
+ card = null;
+ IntPtr info = default;
+ AlsaInterop.CtlCardInfoMalloc(ref info);
+ if ((error = AlsaInterop.CtlOpen(out IntPtr handle, name, 0)) < 0)
+ {
+ AlsaInterop.CtlCardInfoFree(info);
+ Console.WriteLine(AlsaInterop.ErrorString(error));
+ return false;
+ }
+ if ((error = AlsaInterop.CtlCardInfo(handle, info)) < 0)
+ {
+ AlsaInterop.CtlCardInfoFree(info);
+ Console.WriteLine(AlsaInterop.ErrorString(error));
+ AlsaInterop.CtlClose(handle);
+ return false;
+ }
+ card = new AlsaCard(handle);
+ card.IdString = name;
+ int dev = -1;
+ do
+ {
+ if ((error = AlsaInterop.CtlPcmNextDevice(handle, ref dev)) < 0)
+ {
+ AlsaInterop.CtlCardInfoFree(info);
+ Console.WriteLine(AlsaInterop.ErrorString(error));
+ return false;
+ }
+ if (dev < 0)
+ {
+ break;
+ }
+ if (AlsaOut.Create(card, dev, out AlsaOut device))
+ {
+ card.OutputDevices.Add(device);
+ }
+ } while (true);
+ card.Name = AlsaInterop.CtlCardInfoGetName(info);
+ card.Index = AlsaInterop.CtlCardInfoGetCard(info);
+ card.Id = AlsaInterop.CtlCardInfoGetID(info);
+ card.Driver = AlsaInterop.CtlCardInfoGetDriver(info);
+ card.LongName = AlsaInterop.CtlCardInfoGetLongName(info);
+ card.MixerName = AlsaInterop.CtlCardInfoGetMixerName(info);
+ card.Components = AlsaInterop.CtlCardInfoGetComponents(info);
+ AlsaInterop.CtlCardInfoFree(info);
+ return true;
+ }
+ public void Dispose()
+ {
+ AlsaInterop.CtlClose(handle);
+ }
+ public override string ToString()
+ {
+ return $"card {Index}: {Id} [{Name}]";
+ }
+ ~AlsaCard()
+ {
+ Console.WriteLine("Disposing ALSA Card");
+ Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio.Alsa/AlsaDriver.cs b/NAudio.Alsa/AlsaDriver.cs
new file mode 100644
index 00000000..e76c66cd
--- /dev/null
+++ b/NAudio.Alsa/AlsaDriver.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+namespace NAudio.Wave.Alsa
+{
+ public class AlsaDriver
+ {
+ private IntPtr _playbackPcm;
+ private List _cards = new List();
+ public AlsaDriver()
+ {
+ EnumerateCards();
+ }
+ public void EnumerateCards()
+ {
+ int card = -1;
+ if (AlsaInterop.NextCard(ref card) < 0 || card < 0)
+ {
+ throw new Exception("no soundcards found");
+ }
+ while (card >= 0){
+ string name = $"hw:{card.ToString()}";
+ if (AlsaCard.Create(name, out AlsaCard card_obj))
+ {
+ _cards.Add(card_obj);
+ Console.WriteLine(name);
+ }
+ AlsaInterop.NextCard(ref card);
+ }
+ }
+ internal static PCMFormat GetFormat(WaveFormat format)
+ {
+ switch (format.Encoding)
+ {
+ case WaveFormatEncoding.IeeeFloat:
+ switch (format.BitsPerSample)
+ {
+ case 32:
+ return PCMFormat.SND_PCM_FORMAT_FLOAT_LE;
+ }
+ break;
+ case WaveFormatEncoding.Pcm:
+ switch (format.BitsPerSample)
+ {
+ case 32:
+ return PCMFormat.SND_PCM_FORMAT_S32_LE;
+ case 24:
+ return PCMFormat.SND_PCM_FORMAT_S24_3LE;
+ case 16:
+ return PCMFormat.SND_PCM_FORMAT_S16_LE;
+ case 8:
+ return PCMFormat.SND_PCM_FORMAT_S8;
+ }
+ break;
+ case WaveFormatEncoding.MuLaw:
+ return PCMFormat.SND_PCM_FORMAT_MU_LAW;
+ case WaveFormatEncoding.ALaw:
+ return PCMFormat.SND_PCM_FORMAT_A_LAW;
+ }
+ return PCMFormat.SND_PCM_FORMAT_UNKNOWN;
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio.Alsa/AlsaException.cs b/NAudio.Alsa/AlsaException.cs
new file mode 100644
index 00000000..6043e5f3
--- /dev/null
+++ b/NAudio.Alsa/AlsaException.cs
@@ -0,0 +1,13 @@
+using System;
+using NAudio.Wave.Alsa;
+
+[Serializable]
+public class AlsaException : Exception
+{
+ public AlsaException(int error) : base(AlsaInterop.ErrorString(error))
+ {
+ }
+ public AlsaException(string message, int error) : base(string.Format("{0}: {1}", message, AlsaInterop.ErrorString(error)))
+ {
+ }
+}
\ No newline at end of file
diff --git a/NAudio.Alsa/AlsaIn.cs b/NAudio.Alsa/AlsaIn.cs
new file mode 100644
index 00000000..9b9820b4
--- /dev/null
+++ b/NAudio.Alsa/AlsaIn.cs
@@ -0,0 +1,176 @@
+using System;
+using NAudio.Wave.Alsa;
+using System.Threading.Tasks;
+using System.Linq;
+
+namespace NAudio.Wave
+{
+ public class AlsaIn : AlsaPcm, IWaveIn
+ {
+ private bool recording;
+ private AlsaInterop.PcmCallback callback;
+ public void StartRecording()
+ {
+ if (recording)
+ {
+ throw new InvalidOperationException("Already recording");
+ }
+ InitRecord(WaveFormat);
+ int error;
+ recording = true;
+ if ((error = AlsaInterop.PcmStart(Handle)) < 0)
+ {
+ throw new AlsaException("snd_pcm_start", error);
+ }
+ else if (!Async)
+ {
+ RecordPcmSync();
+ }
+ }
+ public void StopRecording()
+ {
+ recording = false;
+ int error;
+ if ((error = AlsaInterop.PcmDrop(Handle)) < 0)
+ {
+ RaiseRecordingStopped(new AlsaException(error));
+ }
+ else
+ {
+ RaiseRecordingStopped(null);
+ }
+ }
+ public WaveFormat WaveFormat { get; set;}
+ public event EventHandler DataAvailable;
+ public event EventHandler RecordingStopped;
+ public void Dispose()
+ {
+ if (isDisposed)
+ {
+ return;
+ }
+ isDisposed = true;
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ AlsaInterop.PcmSwParamsFree(SwParams);
+ AlsaInterop.PcmClose(Handle);
+ }
+ public AlsaIn(string pcm_name)
+ {
+ int error;
+ if ((error = AlsaInterop.PcmOpen(out Handle, pcm_name, PCMStream.SND_PCM_STREAM_CAPTURE, 0)) < 0)
+ {
+ throw new AlsaException("snd_pcm_open", error);
+ }
+ callback = Callback;
+ ulong buffer_size = PERIOD_SIZE * PERIOD_QUANTITY;
+ if ((error = AlsaInterop.AsyncAddPcmHandler(out IntPtr handler, Handle, callback, default)) != 0)
+ {
+ Async = false;
+ }
+ else
+ {
+ Async = true;
+ }
+ }
+ private void InitRecord(WaveFormat waveFormat)
+ {
+ GetHardwareParams();
+ int error;
+ int dir = 0;
+ uint periods = PERIOD_QUANTITY;
+ if (isInitialized)
+ {
+ throw new InvalidOperationException("Already initialized");
+ }
+ isInitialized = true;
+ InitBuffers();
+ ulong buffer_size = (ulong)WaveBuffer.Length;
+ SetInterleavedAccess();
+ SetFormat(waveFormat);
+ SetPeriods(ref periods, ref dir);
+ SetBufferSize(ref buffer_size);
+ SetHardwareParams();
+ GetSoftwareParams();
+ if ((error = AlsaInterop.PcmSwParamsSetStartThreshold(Handle, SwParams, buffer_size - PERIOD_SIZE)) < 0)
+ {
+ throw new AlsaException(error);
+ }
+ if ((error = AlsaInterop.PcmSwParamsSetAvailMin(Handle, SwParams, PERIOD_SIZE)) < 0)
+ {
+ throw new AlsaException(error);
+ }
+ SetSoftwareParams();
+ AlsaInterop.PcmPrepare(Handle);
+ }
+ public AlsaIn() : this("default")
+ {
+ }
+ ~AlsaIn()
+ {
+ Dispose();
+ }
+ private void Callback(IntPtr cb_info)
+ {
+ ReadPcm();
+ }
+ private void ReadPcm()
+ {
+ ulong avail = AlsaInterop.PcmAvailUpdate(Handle);
+ int bits_per_frame = WaveFormat.BitsPerSample * WaveFormat.Channels;
+ int frame_bytes = bits_per_frame / 8;
+ while (avail >= PERIOD_SIZE)
+ {
+ int frames = AlsaInterop.PcmReadI(Handle, WaveBuffer, PERIOD_SIZE);
+ if (frames < 0)
+ {
+ recording = false;
+ RaiseRecordingStopped(new AlsaException(frames));
+ }
+ if (!Async) SwapBuffers();
+ RaiseDataAvailable(WaveBuffer, frames * frame_bytes);
+ avail = AlsaInterop.PcmAvailUpdate(Handle);
+ }
+ }
+ private void RecordPcmSync()
+ {
+ Task.Run(() => {
+ while (recording)
+ {
+ ReadPcm();
+ }
+ });
+ }
+ private void SetFormat(WaveFormat format)
+ {
+ int error;
+ if (GetValidWaveFormats().Contains(format))
+ {
+ WaveFormat = format;
+ }
+ else
+ {
+ throw new NotSupportedException($"{format} not supported");
+ }
+ var sampleformat = AlsaDriver.GetFormat(WaveFormat);
+ if ((error = AlsaInterop.PcmHwParamsSetFormat(Handle, HwParams, sampleformat)) < 0)
+ {
+ throw new AlsaException(error);
+ }
+ int desiredSampleRate = WaveFormat.SampleRate;
+ SetSampleRate((uint)desiredSampleRate);
+ SetNumberOfChannels((uint)WaveFormat.Channels);
+ }
+ private void RaiseRecordingStopped(Exception e)
+ {
+ var handler = RecordingStopped;
+ if (handler != null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ }
+ private void RaiseDataAvailable(byte[] buffer, int bytes)
+ {
+ DataAvailable?.Invoke(this, new WaveInEventArgs(buffer, bytes));
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio.Alsa/AlsaOut.cs b/NAudio.Alsa/AlsaOut.cs
new file mode 100644
index 00000000..c8c75287
--- /dev/null
+++ b/NAudio.Alsa/AlsaOut.cs
@@ -0,0 +1,265 @@
+using System;
+using NAudio.Wave.Alsa;
+using NAudio.Wave.SampleProviders;
+using System.Threading.Tasks;
+namespace NAudio.Wave
+{
+
+ public class AlsaOut : AlsaPcm, IWavePlayer
+ {
+ private IWaveProvider sourceStream;
+ private PlaybackState playbackState;
+ private readonly AlsaInterop.PcmCallback callback;
+ public WaveFormat OutputWaveFormat { get; private set; }
+ public event EventHandler PlaybackStopped;
+ public float Volume
+ {
+ get => 1.0f;
+ set => throw new NotImplementedException("");
+ }
+ public PlaybackState PlaybackState
+ {
+ get => playbackState;
+ }
+ public bool HasReachedEnd { get; private set; }
+ public void Dispose()
+ {
+ if (isDisposed)
+ {
+ return;
+ }
+ isDisposed = true;
+ AlsaInterop.PcmClose(Handle);
+ if (HwParams != default) AlsaInterop.PcmHwParamsFree(HwParams);
+ if (SwParams != default) AlsaInterop.PcmSwParamsFree(SwParams);
+ }
+
+ public void Init(IWaveProvider waveProvider)
+ {
+ InitPlayback(waveProvider);
+ }
+ public void Play()
+ {
+ if (playbackState == PlaybackState.Paused)
+ {
+ playbackState = PlaybackState.Playing;
+ int error;
+ if ((error = AlsaInterop.PcmPause(Handle, 0)) < 0)
+ {
+ throw new AlsaException(error);
+ }
+ if (!Async)
+ {
+ PlayPcmSync();
+ }
+ }
+ else if (playbackState != PlaybackState.Playing)
+ {
+ if (Async)
+ {
+ int error;
+ if ((error = AlsaInterop.PcmStart(Handle)) < 0)
+ {
+ error = AlsaInterop.PcmRecover(Handle, error, 0);
+ }
+ if (error < 0)
+ {
+ throw new AlsaException("snd_pcm_start", error);
+ }
+ playbackState = PlaybackState.Playing;
+ HasReachedEnd = false;
+ return;
+ }
+ else
+ {
+ playbackState = PlaybackState.Playing;
+ HasReachedEnd = false;
+ BufferUpdate();
+ PlayPcmSync();
+ }
+ }
+ }
+ public void Stop()
+ {
+ playbackState = PlaybackState.Stopped;
+ AlsaInterop.PcmDrop(Handle);
+ HasReachedEnd = false;
+ RaisePlaybackStopped(null);
+ }
+ public void Pause()
+ {
+ if (playbackState == PlaybackState.Playing)
+ {
+ playbackState = PlaybackState.Paused;
+ int error;
+ if ((error = AlsaInterop.PcmPause(Handle, 1)) < 0)
+ {
+ throw new AlsaException(error);
+ }
+ }
+ }
+ public AlsaOut(string pcm_name)
+ {
+ int error;
+ if ((error = AlsaInterop.PcmOpen(out Handle, pcm_name, PCMStream.SND_PCM_STREAM_PLAYBACK, 0)) < 0)
+ {
+ throw new AlsaException("snd_pcm_open", error);
+ }
+ callback = Callback;
+ ulong buffer_size = PERIOD_SIZE * PERIOD_QUANTITY;
+ if ((error = AlsaInterop.AsyncAddPcmHandler(out IntPtr handler, Handle, callback, default)) != 0)
+ {
+ Async = false;
+ }
+ else
+ {
+ Async = true;
+ }
+ GetHardwareParams();
+ }
+ public AlsaOut() : this("default")
+ {
+ }
+ ~AlsaOut()
+ {
+ Dispose();
+ }
+ public static bool Create(AlsaCard card, int device_num, out AlsaOut device)
+ {
+ device = null;
+ return false;
+ }
+ public static bool Create(string pcm_name, out AlsaOut device)
+ {
+ device = null;
+ try
+ {
+ device = new AlsaOut(pcm_name);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ public void InitPlayback(IWaveProvider waveProvider)
+ {
+ int error;
+ int dir = 0;
+ uint periods = PERIOD_QUANTITY;
+ if (isInitialized)
+ {
+ throw new InvalidOperationException("Already initialized this PCM");
+ }
+ isInitialized = true;
+ if (waveProvider != null)
+ {
+ sourceStream = waveProvider;
+ InitBuffers();
+ ulong buffer_size = (ulong)WaveBuffer.Length;
+ SetInterleavedAccess();
+ SetFormat(waveProvider);
+ SetPeriods(ref periods, ref dir);
+ SetBufferSize(ref buffer_size);
+ SetHardwareParams();
+ GetSoftwareParams();
+ if ((error = AlsaInterop.PcmSwParamsSetStartThreshold(Handle, SwParams, buffer_size - PERIOD_SIZE)) < 0)
+ {
+ throw new AlsaException(error);
+ }
+ if ((error = AlsaInterop.PcmSwParamsSetAvailMin(Handle, SwParams, PERIOD_SIZE)) < 0)
+ {
+ throw new AlsaException(error);
+ }
+ SetSoftwareParams();
+ _ = State; // idk... it doesn't work without this
+ if (Async)
+ {
+ AlsaInterop.PcmWriteI(Handle, WaveBuffer, 2 * PERIOD_SIZE);
+ }
+ }
+ }
+ public void SetFormat(IWaveProvider waveProvider)
+ {
+ int error;
+ var format = AlsaDriver.GetFormat(waveProvider.WaveFormat);
+ if (AlsaInterop.PcmHwParamsTestFormat(Handle, HwParams, format) == 0)
+ {
+ OutputWaveFormat = waveProvider.WaveFormat;
+ AlsaInterop.PcmHwParamsSetFormat(Handle, HwParams, format);
+ }
+ else if ((error = AlsaInterop.PcmHwParamsTestFormat(Handle, HwParams, PCMFormat.SND_PCM_FORMAT_S32_LE)) == 0)
+ {
+ AlsaInterop.PcmHwParamsSetFormat(Handle, HwParams, PCMFormat.SND_PCM_FORMAT_S32_LE);
+ sourceStream = new SampleToWaveProvider32(new WaveToSampleProvider(waveProvider));
+ OutputWaveFormat = sourceStream.WaveFormat;
+ }
+ else if ((error = AlsaInterop.PcmHwParamsTestFormat(Handle, HwParams, PCMFormat.SND_PCM_FORMAT_S16_LE)) == 0)
+ {
+ AlsaInterop.PcmHwParamsSetFormat(Handle, HwParams, PCMFormat.SND_PCM_FORMAT_S16_LE);
+ sourceStream = new SampleToWaveProvider16(new WaveToSampleProvider(waveProvider));
+ OutputWaveFormat = sourceStream.WaveFormat;
+ }
+ else
+ {
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ throw new AlsaException(error);
+ }
+ int desiredSampleRate = waveProvider.WaveFormat.SampleRate;
+ SetSampleRate((uint)desiredSampleRate);
+ SetNumberOfChannels((uint)waveProvider.WaveFormat.Channels);
+ }
+ private void PlayPcmSync()
+ {
+ Task.Run(() => {
+ while (playbackState == PlaybackState.Playing)
+ {
+ WritePcm();
+ }
+ });
+ }
+ private void WritePcm()
+ {
+ ulong avail = AlsaInterop.PcmAvailUpdate(Handle);
+ while (avail >= PERIOD_SIZE)
+ {
+ if (!Async) SwapBuffers();
+ int error = AlsaInterop.PcmWriteI(Handle, WaveBuffer, PERIOD_SIZE);
+ BufferUpdate();
+ if (error < 0)
+ {
+ if ((error = AlsaInterop.PcmRecover(Handle, error, 0)) < 0)
+ {
+ RaisePlaybackStopped(new AlsaException(error));
+ }
+ }
+ avail = AlsaInterop.PcmAvailUpdate(Handle);
+ }
+ }
+ private void Callback(IntPtr callback)
+ {
+ WritePcm();
+ }
+ private void BufferUpdate()
+ {
+ int read = sourceStream.Read(WaveBuffer, 0, WaveBuffer.Length);
+ if (read < WaveBuffer.Length)
+ {
+ Array.Clear(WaveBuffer, read, WaveBuffer.Length - read);
+ }
+ if (read == 0)
+ {
+ Stop();
+ HasReachedEnd = true;
+ }
+ }
+ private void RaisePlaybackStopped(Exception e)
+ {
+ var handler = PlaybackStopped;
+ if (handler != null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ }
+ }
+}
diff --git a/NAudio.Alsa/AlsaPcm.cs b/NAudio.Alsa/AlsaPcm.cs
new file mode 100644
index 00000000..73122840
--- /dev/null
+++ b/NAudio.Alsa/AlsaPcm.cs
@@ -0,0 +1,317 @@
+using NAudio.Wave.Alsa;
+using NAudio.Wave;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+public abstract class AlsaPcm
+{
+ protected static readonly uint[] rates =
+ {
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000
+ };
+ private bool isBufferInit = false;
+ private int numBuffers = 2;
+ protected const uint PERIOD_QUANTITY = 8;
+ protected const ulong PERIOD_SIZE = 1024;
+ protected IntPtr Handle = default;
+ protected IntPtr HwParams = default;
+ protected IntPtr SwParams = default;
+ protected int BufferNum;
+ protected byte[] WaveBuffer;
+ protected byte[][] Buffers;
+ protected bool isInitialized = false;
+ protected bool isDisposed = false;
+ internal PCMState State { get=> AlsaInterop.PcmState(Handle); }
+ public int Card { get; private set; }
+ public uint Device { get; private set; }
+ public string Id { get; private set; }
+ public string Name { get; private set; }
+ public int NumberOfBuffers
+ {
+ get =>numBuffers;
+ set
+ {
+ if (!isBufferInit)
+ {
+ numBuffers = value;
+ }
+ }
+ }
+ public bool Async { get; protected set; }
+
+ protected void GetHardwareParams()
+ {
+ if (HwParams != default)
+ {
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ }
+ AlsaInterop.PcmHwParamsMalloc(out HwParams);
+ AlsaInterop.PcmHwParamsAny(Handle, HwParams);
+ }
+ protected void SetHardwareParams()
+ {
+ int error;
+ if ((error = AlsaInterop.PcmHwParams(Handle, HwParams)) != 0)
+ {
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ throw new AlsaException(error);
+ }
+ }
+ protected void GetSoftwareParams()
+ {
+ if (SwParams != default)
+ {
+ AlsaInterop.PcmSwParamsFree(SwParams);
+ }
+ AlsaInterop.PcmSwParamsMalloc(out SwParams);
+ AlsaInterop.PcmSwParamsCurrent(Handle, SwParams);
+ }
+ protected void SetSoftwareParams()
+ {
+ int error;
+ if ((error = AlsaInterop.PcmSwParams(Handle, SwParams)) < 0)
+ {
+ AlsaInterop.PcmSwParamsFree(SwParams);
+ throw new AlsaException(error);
+ }
+ }
+ protected void SetInterleavedAccess()
+ {
+ int error;
+ if ((error = AlsaInterop.PcmHwParamsTestAccess(Handle, HwParams, PCMAccess.SND_PCM_ACCESS_RW_INTERLEAVED)) != 0)
+ {
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ throw new AlsaException(error);
+ }
+ AlsaInterop.PcmHwParamsSetAccess(Handle, HwParams, PCMAccess.SND_PCM_ACCESS_RW_INTERLEAVED);
+ }
+ protected void SetSampleRate(uint sampleRate)
+ {
+ int error;
+ if ((error = AlsaInterop.PcmHwParamsSetRate(Handle, HwParams, sampleRate, 0)) != 0)
+ {
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ throw new AlsaException(error);
+ }
+ }
+ protected void SetNumberOfChannels(uint numChannels)
+ {
+ int error;
+ if ((error = AlsaInterop.PcmHwParamsSetChannels(Handle, HwParams, numChannels)) != 0)
+ {
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ throw new AlsaException(error);
+ }
+ }
+ protected void SetPeriods(ref uint periods, ref int dir)
+ {
+ int error;
+ if ((error = AlsaInterop.PcmHwParamsSetPeriodsNear(Handle, HwParams, ref periods, ref dir)) != 0)
+ {
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ throw new AlsaException(error);
+ }
+ }
+ protected void SetBufferSize(ref ulong buffer_size)
+ {
+ int error;
+ if ((error = AlsaInterop.PcmHwParamsSetBufferSizeNear(Handle, HwParams, ref buffer_size)) != 0)
+ {
+ AlsaInterop.PcmHwParamsFree(HwParams);
+ throw new AlsaException(error);
+ }
+ }
+ protected static ulong GetSampleSize(WaveFormat format)
+ {
+ return (ulong)(format.BitsPerSample / 8 * format.Channels);
+ }
+ private static int[] GetValidChannelValues(IntPtr Handle, IntPtr HwParams)
+ {
+ AlsaInterop.PcmHwParamsGetChannelsMin(HwParams, out uint min);
+ AlsaInterop.PcmHwParamsGetChannelsMax(HwParams, out uint max);
+ int[] result = new int[0];
+ for (uint i = min; i <= max; i++)
+ {
+ if (AlsaInterop.PcmHwParamsTestChannels(Handle, HwParams, i) == 0)
+ {
+ int[] newresult = new int[result.Length + 1];
+ Array.Copy(result, newresult, result.Length);
+ newresult[result.Length] = (int)i;
+ result = newresult;
+ }
+ }
+ return result;
+ }
+ private static int[] GetValidSampleRates(IntPtr Handle, IntPtr HwParams)
+ {
+ int[] result = new int[0];
+ for (uint i = 0; i < rates.Length; i++)
+ {
+ var rate = rates[i];
+ if (AlsaInterop.PcmHwParamsTestRate(Handle, HwParams, rate, 0) == 0)
+ {
+ int[] newresult = new int[result.Length + 1];
+ Array.Copy(result, newresult, result.Length);
+ newresult[result.Length] = (int)rates[i];
+ result = newresult;
+ }
+ }
+ return result;
+ }
+ private static PCMFormat[] GetValidFormats(IntPtr Handle, IntPtr HwParams)
+ {
+ PCMFormat[] result = new PCMFormat[0];
+ for (PCMFormat i = 0; i < PCMFormat.SND_PCM_FORMAT_LAST; i++)
+ {
+ if (AlsaInterop.PcmHwParamsTestFormat(Handle, HwParams, i) == 0)
+ {
+ PCMFormat[] newresult = new PCMFormat[result.Length + 1];
+ Array.Copy(result, newresult, result.Length);
+ newresult[result.Length] = i;
+ result = newresult;
+ }
+ }
+ return result;
+ }
+ private static WaveFormat[] GetValidWaveFormats(IntPtr Handle, IntPtr HwParams)
+ {
+ var valid_rates = GetValidSampleRates(Handle, HwParams);
+ var valid_channels = GetValidChannelValues(Handle, HwParams);
+ var valid_formats = GetValidFormats(Handle, HwParams);
+ List valid_waveformats = new List();
+ for (int i = 0; i < valid_formats.Length; i++)
+ {
+ switch (valid_formats[i])
+ {
+ case PCMFormat.SND_PCM_FORMAT_FLOAT_LE:
+ for (int j = 0; j < valid_rates.Length; j++)
+ {
+ for (int k = 0; k < valid_channels.Length; k++)
+ {
+ valid_waveformats.Add(WaveFormat.CreateIeeeFloatWaveFormat(valid_rates[j], valid_channels[k]));
+ }
+ }
+ break;
+ case PCMFormat.SND_PCM_FORMAT_U8:
+ for (int j = 0; j < valid_rates.Length; j++)
+ {
+ for (int k = 0; k < valid_channels.Length; k++)
+ {
+ valid_waveformats.Add(new WaveFormat(valid_rates[j], 8, valid_channels[k]));
+ }
+ }
+ break;
+ case PCMFormat.SND_PCM_FORMAT_S16_LE:
+ for (int j = 0; j < valid_rates.Length; j++)
+ {
+ for (int k = 0; k < valid_channels.Length; k++)
+ {
+ valid_waveformats.Add(new WaveFormat(valid_rates[j], 16, valid_channels[k]));
+ }
+ }
+ break;
+ case PCMFormat.SND_PCM_FORMAT_S24_3LE:
+ for (int j = 0; j < valid_rates.Length; j++)
+ {
+ for (int k = 0; k < valid_channels.Length; k++)
+ {
+ valid_waveformats.Add(new WaveFormat(valid_rates[j], 24, valid_channels[k]));
+ }
+ }
+ break;
+ case PCMFormat.SND_PCM_FORMAT_S32_LE:
+ for (int j = 0; j < valid_rates.Length; j++)
+ {
+ for (int k = 0; k < valid_channels.Length; k++)
+ {
+ valid_waveformats.Add(new WaveFormat(valid_rates[j], 32, valid_channels[k]));
+ }
+ }
+ break;
+ case PCMFormat.SND_PCM_FORMAT_A_LAW:
+ for (int j = 0; j < valid_rates.Length; j++)
+ {
+ for (int k = 0; k < valid_channels.Length; k++)
+ {
+ valid_waveformats.Add(WaveFormat.CreateALawFormat(valid_rates[j], valid_channels[k]));
+ }
+ }
+ break;
+ case PCMFormat.SND_PCM_FORMAT_MU_LAW:
+ for (int j = 0; j < valid_rates.Length; j++)
+ {
+ for (int k = 0; k < valid_channels.Length; k++)
+ {
+ valid_waveformats.Add(WaveFormat.CreateMuLawFormat(valid_rates[j], valid_channels[k]));
+ }
+ }
+ break;
+
+ }
+ }
+ return valid_waveformats.ToArray();
+ }
+ public WaveFormat[] GetValidWaveFormats()
+ {
+ return GetValidWaveFormats(Handle, HwParams);
+ }
+ public WaveFormat GetCurrentWaveFormat()
+ {
+ WaveFormat result = null;
+ var formats = GetValidWaveFormats();
+ if (formats.Length == 1)
+ {
+ result = formats[0];
+ }
+ return result;
+ }
+ public static bool TestWaveFormat(WaveFormat waveFormat, IntPtr Handle)
+ {
+ AlsaInterop.PcmHwParamsMalloc(out IntPtr hwparams);
+ AlsaInterop.PcmHwParamsAny(Handle, hwparams);
+ var result = GetValidWaveFormats(Handle, hwparams).Contains(waveFormat);
+ AlsaInterop.PcmHwParamsFree(hwparams);
+ return result;
+ }
+ public bool TestWaveFormat(WaveFormat waveFormat)
+ {
+ return TestWaveFormat(waveFormat, Handle);
+ }
+ protected void SwapBuffers()
+ {
+ BufferNum = ++BufferNum % NumberOfBuffers;
+ WaveBuffer = Buffers[BufferNum];
+ }
+ protected void InitBuffers()
+ {
+ isBufferInit = true;
+ int error;
+ ulong buffer_size = PERIOD_SIZE * PERIOD_QUANTITY;
+ if (!Async)
+ {
+ Buffers = new byte[NumberOfBuffers][];
+ for (int i = 0; i < NumberOfBuffers; i++)
+ {
+ Buffers[i] = new byte[buffer_size];
+ }
+ WaveBuffer = Buffers[BufferNum];
+ }
+ else
+ {
+ WaveBuffer = new byte[buffer_size];
+ }
+ }
+}
diff --git a/NAudio.Alsa/Interop/Alsa.Enums.cs b/NAudio.Alsa/Interop/Alsa.Enums.cs
new file mode 100644
index 00000000..ecb82f29
--- /dev/null
+++ b/NAudio.Alsa/Interop/Alsa.Enums.cs
@@ -0,0 +1,105 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+internal enum PCMStream
+{
+ SND_PCM_STREAM_PLAYBACK = 0,
+ SND_PCM_STREAM_CAPTURE = 1,
+ SND_PCM_STREAM_LAST = SND_PCM_STREAM_CAPTURE,
+}
+
+internal enum PCMFormat
+{
+ SND_PCM_FORMAT_UNKNOWN = -1,
+ SND_PCM_FORMAT_S8 = 0,
+ SND_PCM_FORMAT_U8 = 1,
+ SND_PCM_FORMAT_S16_LE = 2,
+ SND_PCM_FORMAT_S16_BE = 3,
+ SND_PCM_FORMAT_U16_LE = 4,
+ SND_PCM_FORMAT_U16_BE = 5,
+ SND_PCM_FORMAT_S24_LE = 6,
+ SND_PCM_FORMAT_S24_BE = 7,
+ SND_PCM_FORMAT_U24_LE = 8,
+ SND_PCM_FORMAT_U24_BE = 9,
+ SND_PCM_FORMAT_S32_LE = 10,
+ SND_PCM_FORMAT_S32_BE = 11,
+ SND_PCM_FORMAT_U32_LE = 12,
+ SND_PCM_FORMAT_U32_BE = 13,
+ SND_PCM_FORMAT_FLOAT_LE = 14,
+ SND_PCM_FORMAT_FLOAT_BE = 15,
+ SND_PCM_FORMAT_FLOAT64_LE = 16,
+ SND_PCM_FORMAT_FLOAT64_BE = 17,
+ SND_PCM_FORMAT_IEC958_SUBFRAME_LE = 18,
+ SND_PCM_FORMAT_IEC958_SUBFRAME_BE = 19,
+ SND_PCM_FORMAT_MU_LAW = 20,
+ SND_PCM_FORMAT_A_LAW = 21,
+ SND_PCM_FORMAT_IMA_ADPCM = 22,
+ SND_PCM_FORMAT_MPEG = 23,
+ SND_PCM_FORMAT_GSM = 24,
+ SND_PCM_FORMAT_SPECIAL = 31,
+ SND_PCM_FORMAT_S24_3LE = 32,
+ SND_PCM_FORMAT_S24_3BE = 33,
+ SND_PCM_FORMAT_U24_3LE = 34,
+ SND_PCM_FORMAT_U24_3BE = 35,
+ SND_PCM_FORMAT_S20_3LE = 36,
+ SND_PCM_FORMAT_S20_3BE = 37,
+ SND_PCM_FORMAT_U20_3LE = 38,
+ SND_PCM_FORMAT_U20_3BE = 39,
+ SND_PCM_FORMAT_S18_3LE = 40,
+ SND_PCM_FORMAT_S18_3BE = 41,
+ SND_PCM_FORMAT_U18_3LE = 42,
+ SND_PCM_FORMAT_U18_3BE = 43,
+ SND_PCM_FORMAT_G723_24 = 44,
+ SND_PCM_FORMAT_G723_24_1B = 45,
+ SND_PCM_FORMAT_G723_40 = 46,
+ SND_PCM_FORMAT_G723_40_1B = 47,
+ SND_PCM_FORMAT_DSD_U8 = 48,
+ SND_PCM_FORMAT_DSD_U16_LE = 49,
+ SND_PCM_FORMAT_DSD_U32_LE = 50,
+ SND_PCM_FORMAT_DSD_U16_BE = 51,
+ SND_PCM_FORMAT_DSD_U32_BE = 52,
+ SND_PCM_FORMAT_LAST = SND_PCM_FORMAT_DSD_U32_BE,
+}
+
+internal enum PCMAccess
+{
+ SND_PCM_ACCESS_MMAP_INTERLEAVED = 0,
+ SND_PCM_ACCESS_MMAP_NONINTERLEAVED = 1,
+ SND_PCM_ACCESS_MMAP_COMPLEX = 2,
+ SND_PCM_ACCESS_RW_INTERLEAVED = 3,
+ SND_PCM_ACCESS_RW_NONINTERLEAVED = 4,
+ SND_PCM_ACCESS_LAST = SND_PCM_ACCESS_RW_NONINTERLEAVED,
+}
+
+internal enum MixerSimpleElementChannelIdentifier
+{
+ SND_MIXER_SCHN_UNKNOWN = -1,
+ SND_MIXER_SCHN_FRONT_LEFT = 0,
+ SND_MIXER_SCHN_FRONT_RIGHT = 1,
+ SND_MIXER_SCHN_REAR_LEFT = 2,
+ SND_MIXER_SCHN_REAR_RIGHT = 3,
+ SND_MIXER_SCHN_FRONT_CENTER = 4,
+ SND_MIXER_SCHN_WOOFER = 5,
+ SND_MIXER_SCHN_SIDE_LEFT = 6,
+ SND_MIXER_SCHN_SIDE_RIGHT = 7,
+ SND_MIXER_SCHN_REAR_CENTER = 8,
+ SND_MIXER_SCHN_LAST = 31,
+ SND_MIXER_SCHN_MONO = SND_MIXER_SCHN_FRONT_LEFT
+}
+
+internal enum PCMState
+{
+ SND_PCM_STATE_OPEN = 0,
+ SND_PCM_STATE_SETUP,
+ SND_PCM_STATE_PREPARED,
+ SND_PCM_STATE_RUNNING,
+ SND_PCM_STATE_XRUN,
+ SND_PCM_STATE_DRAINING,
+ SND_PCM_STATE_PAUSED,
+ SND_PCM_STATE_SUSPENDED,
+ SND_PCM_STATE_DISCONNECTED,
+ SND_PCM_STATE_LAST = SND_PCM_STATE_DISCONNECTED,
+ SND_PCM_STATE_PRIVATE1 = 1024
+
+}
\ No newline at end of file
diff --git a/NAudio.Alsa/Interop/Alsa.cs b/NAudio.Alsa/Interop/Alsa.cs
new file mode 100644
index 00000000..e6f6c44b
--- /dev/null
+++ b/NAudio.Alsa/Interop/Alsa.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Runtime.InteropServices;
+namespace NAudio.Wave.Alsa
+{
+ internal class AlsaInterop
+ {
+ public delegate void PcmCallback(IntPtr handler);
+ private const string AlsaLibrary = "libasound";
+ [DllImport(AlsaLibrary, EntryPoint = "snd_card_next")]
+ internal static extern int NextCard(ref int rcard);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_ctl_open")]
+ internal static extern int CtlOpen(out IntPtr ctlp, string name, int mode);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_ctl_close")]
+ internal static extern int CtlClose(IntPtr ctl);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_ctl_card_info")]
+ internal static unsafe extern int CtlCardInfo(IntPtr ctlp, IntPtr info);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_ctl_card_info_free")]
+ internal static unsafe extern int CtlCardInfoFree(IntPtr info);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_ctl_card_info_get_card")]
+ internal static extern int CtlCardInfoGetCard(IntPtr info);
+ [DllImport(AlsaLibrary, CharSet = CharSet.Ansi, EntryPoint = "snd_ctl_card_info_get_id")]
+ private static extern IntPtr ctlCardInfoGetID(IntPtr info);
+ [DllImport(AlsaLibrary, CharSet = CharSet.Ansi, EntryPoint = "snd_ctl_card_info_get_driver")]
+ private static extern IntPtr ctlCardInfoGetDriver(IntPtr info);
+ [DllImport(AlsaLibrary, CharSet = CharSet.Ansi, EntryPoint = "snd_ctl_card_info_get_name")]
+ private static extern IntPtr ctlCardInfoGetName(IntPtr info);
+ [DllImport(AlsaLibrary, CharSet = CharSet.Ansi, EntryPoint = "snd_ctl_card_info_get_longname")]
+ private static extern IntPtr ctlCardInfoGetLongName(IntPtr info);
+ [DllImport(AlsaLibrary, CharSet = CharSet.Ansi, EntryPoint = "snd_ctl_card_info_get_mixername")]
+ private static extern IntPtr ctlCardInfoGetMixerName(IntPtr info);
+ [DllImport(AlsaLibrary, CharSet = CharSet.Ansi, EntryPoint = "snd_ctl_card_info_get_components")]
+ private static extern IntPtr ctlCardInfoGetComponents(IntPtr info);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_ctl_card_info_malloc")]
+ internal static unsafe extern void CtlCardInfoMalloc(ref IntPtr info);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_malloc")]
+ internal static extern void PcmInfoMalloc(out IntPtr pcmInfo);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_free")]
+ internal static extern void PcmInfoFree(IntPtr pcmInfo);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_stream_name")]
+ internal static extern string PcmStreamName(IntPtr stream);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_ctl_pcm_next_device")]
+ internal static extern int CtlPcmNextDevice(IntPtr ctl, ref int device);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_set_device")]
+ internal static extern void PcmInfoSetDevice(IntPtr pcmInfo, uint val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_set_subdevice")]
+ internal static extern void PcmInfoSetSubdevice(IntPtr pcmInfo, uint val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_set_stream")]
+ internal static extern void PcmInfoSetStream(IntPtr pcmInfo, PCMStream stream);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_get_id")]
+ private static extern IntPtr pcmInfoGetID(IntPtr pcmInfo);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_get_name")]
+ private static extern IntPtr pcmInfoGetName(IntPtr pcmInfo);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_get_stream")]
+ internal static extern PCMStream PcmInfoGetStream(IntPtr pcmInfo);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_get_subdevice_name")]
+ private static extern IntPtr pcmInfoGetSubdeviceName(IntPtr pcmInfo);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_get_subdevices_avail")]
+ internal static extern uint PcmInfoGetSubdevicesAvailable(IntPtr pcmInfo);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_ctl_pcm_info")]
+ internal static extern int CtlPcmInfo(IntPtr ctl, IntPtr info);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_info_get_subdevices_count")]
+ internal static extern uint PcmInfoGetSubdevicesCount(IntPtr pcmInfo);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_strerror")]
+ private static extern IntPtr strError(int error);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_open")]
+ internal static extern int PcmOpen(out IntPtr pcm, string name, PCMStream stream, int mode);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_close")]
+ internal static extern int PcmClose(IntPtr pcm);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_malloc")]
+ internal static unsafe extern int PcmHwParamsMalloc(out IntPtr hwparams);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_free")]
+ internal static unsafe extern void PcmHwParamsFree(IntPtr hwparams);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_test_access")]
+ internal static extern int PcmHwParamsTestAccess(IntPtr pcm, IntPtr hwparams, PCMAccess access);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_any")]
+ internal static extern int PcmHwParamsAny(IntPtr pcm, IntPtr hwparams);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_set_access")]
+ internal static extern int PcmHwParamsSetAccess(IntPtr pcm, IntPtr hwparams, PCMAccess access);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_test_format")]
+ internal static extern int PcmHwParamsTestFormat(IntPtr pcm, IntPtr hwparams, PCMFormat format);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_get_format")]
+ internal static extern int PcmHwParamsGetFormat(IntPtr pcm, out PCMFormat format);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_set_format")]
+ internal static extern int PcmHwParamsSetFormat(IntPtr pcm, IntPtr hwparams, PCMFormat format);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_set_rate")]
+ internal static extern int PcmHwParamsSetRate(IntPtr pcm, IntPtr hwparams, uint val, int dir);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_set_channels")]
+ internal static extern int PcmHwParamsSetChannels(IntPtr pcm, IntPtr hwparams, uint val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_set_periods_near")]
+ internal static extern int PcmHwParamsSetPeriodsNear(IntPtr pcm, IntPtr hwparams, ref uint val, ref int dir);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_set_buffer_size_near")]
+ internal static extern int PcmHwParamsSetBufferSizeNear(IntPtr pcm, IntPtr hwparams, ref ulong val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params")]
+ internal static extern int PcmHwParams(IntPtr pcm, IntPtr hwparams);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_sw_params_malloc")]
+ internal static unsafe extern int PcmSwParamsMalloc(out IntPtr swparams);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_sw_params_free")]
+ internal static unsafe extern void PcmSwParamsFree(IntPtr swparams);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_sw_params_current")]
+ internal static unsafe extern int PcmSwParamsCurrent(IntPtr pcm, IntPtr swparams);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_sw_params")]
+ internal static unsafe extern int PcmSwParams(IntPtr pcm, IntPtr swparams);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_sw_params_set_avail_min")]
+ internal static extern int PcmSwParamsSetAvailMin(IntPtr pcm, IntPtr swparams, ulong val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_writei")]
+ internal static unsafe extern int PcmWriteI(IntPtr pcm, byte[] buffer, ulong size);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_readi")]
+ internal static unsafe extern int PcmReadI(IntPtr pcm, byte[] buffer, ulong size);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_async_add_pcm_handler")]
+ internal static extern int AsyncAddPcmHandler(out IntPtr handler, IntPtr pcm, PcmCallback callback, IntPtr private_data);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_async_handler_get_pcm")]
+ internal static extern IntPtr AsyncHandlerGetPcm(IntPtr handler);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_start")]
+ internal static extern int PcmStart(IntPtr pcm);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_drop")]
+ internal static extern int PcmDrop(IntPtr pcm);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_drain")]
+ internal static extern int PcmDrain(IntPtr pcm);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_pause")]
+ internal static extern int PcmPause(IntPtr pcm, int enable);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_avail_update")]
+ internal static extern ulong PcmAvailUpdate(IntPtr pcm);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_prepare")]
+ internal static extern int PcmPrepare(IntPtr pcm);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_sw_params_set_start_threshold")]
+ internal static extern int PcmSwParamsSetStartThreshold(IntPtr pcm, IntPtr swparams, ulong val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_get_channels_max")]
+ internal static extern int PcmHwParamsGetChannelsMax(IntPtr hwparams, out uint val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_get_channels_min")]
+ internal static extern int PcmHwParamsGetChannelsMin(IntPtr hwparams, out uint val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_test_channels")]
+ internal static extern int PcmHwParamsTestChannels(IntPtr pcm, IntPtr hwparams, uint val);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_hw_params_test_rate")]
+ internal static extern int PcmHwParamsTestRate(IntPtr pcm, IntPtr hwparams, uint val, int dir);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_state")]
+ internal static extern PCMState PcmState(IntPtr pcm);
+ [DllImport(AlsaLibrary, EntryPoint = "snd_pcm_recover")]
+ internal static extern int PcmRecover(IntPtr pcm, int err, int silent);
+ private static string GetString(IntPtr ptr)
+ {
+ if (ptr == IntPtr.Zero)
+ {
+ return string.Empty;
+ }
+ else
+ {
+ return Marshal.PtrToStringAnsi(ptr);
+ }
+ }
+ internal static string CtlCardInfoGetID(IntPtr info)
+ {
+ return GetString(ctlCardInfoGetID(info));
+ }
+ internal static string CtlCardInfoGetDriver(IntPtr info)
+ {
+ return GetString(ctlCardInfoGetDriver(info));
+ }
+ internal static string CtlCardInfoGetName(IntPtr info)
+ {
+ return GetString(ctlCardInfoGetName(info));
+ }
+ internal static string CtlCardInfoGetLongName(IntPtr info)
+ {
+ return GetString(ctlCardInfoGetLongName(info));
+ }
+ internal static string CtlCardInfoGetMixerName(IntPtr info)
+ {
+ return GetString(ctlCardInfoGetMixerName(info));
+ }
+ internal static string CtlCardInfoGetComponents(IntPtr info)
+ {
+ return GetString(ctlCardInfoGetComponents(info));
+ }
+ internal static string PcmInfoGetID(IntPtr pcmInfo)
+ {
+ return GetString(pcmInfoGetID(pcmInfo));
+ }
+ internal static string PcmInfoGetName(IntPtr pcmInfo)
+ {
+ return GetString(pcmInfoGetName(pcmInfo));
+ }
+ internal static string PcmInfoGetSubdeviceName(IntPtr pcmInfo)
+ {
+ return GetString(pcmInfoGetSubdeviceName(pcmInfo));
+ }
+ internal static string ErrorString(int error)
+ {
+ return GetString(strError(error));
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio.Alsa/NAudio.Alsa.csproj b/NAudio.Alsa/NAudio.Alsa.csproj
new file mode 100644
index 00000000..58af10c2
--- /dev/null
+++ b/NAudio.Alsa/NAudio.Alsa.csproj
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+ netcoreapp2.0
+ true
+
+
+
\ No newline at end of file
diff --git a/NAudio.Core/Wave/SampleProviders/SampleToWaveProvider32.cs b/NAudio.Core/Wave/SampleProviders/SampleToWaveProvider32.cs
new file mode 100644
index 00000000..71cb6644
--- /dev/null
+++ b/NAudio.Core/Wave/SampleProviders/SampleToWaveProvider32.cs
@@ -0,0 +1,83 @@
+using System;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Converts a sample provider to 32 bit PCM, optionally clipping and adjusting volume along the way
+ ///
+ public class SampleToWaveProvider32 : IWaveProvider
+ {
+ private readonly ISampleProvider sourceProvider;
+ private readonly WaveFormat waveFormat;
+ private volatile float volume;
+ private float[] sourceBuffer;
+
+ ///
+ /// Converts from an ISampleProvider (IEEE float) to a 16 bit PCM IWaveProvider.
+ /// Number of channels and sample rate remain unchanged.
+ ///
+ /// The input source provider
+ public SampleToWaveProvider32(ISampleProvider sourceProvider)
+ {
+ if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ throw new ArgumentException("Input source provider must be IEEE float", "sourceProvider");
+ if (sourceProvider.WaveFormat.BitsPerSample != 32)
+ throw new ArgumentException("Input source provider must be 32 bit", "sourceProvider");
+
+ waveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 32, sourceProvider.WaveFormat.Channels);
+
+ this.sourceProvider = sourceProvider;
+ volume = 1.0f;
+ }
+
+ ///
+ /// Reads bytes from this wave stream, clipping if necessary
+ ///
+ /// The destination buffer
+ /// Offset into the destination buffer
+ /// Number of bytes read
+ /// Number of bytes read.
+ public int Read(byte[] destBuffer, int offset, int numBytes)
+ {
+ var samplesRequired = numBytes / 4;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, samplesRequired);
+ var sourceSamples = sourceProvider.Read(sourceBuffer, 0, samplesRequired);
+ var destWaveBuffer = new WaveBuffer(destBuffer);
+
+ int destOffset = offset / 4;
+ for (var sample = 0; sample < sourceSamples; sample++)
+ {
+ // adjust volume
+ var sample32 = sourceBuffer[sample] * volume;
+ // clip
+ if (sample32 > 1.0f)
+ sample32 = 1.0f;
+ if (sample32 < -1.0f)
+ sample32 = -1.0f;
+
+ destWaveBuffer.IntBuffer[destOffset++] = (int) (sample32*2147483647);
+ }
+
+ return sourceSamples * 4;
+ }
+
+ ///
+ /// The Format of this IWaveProvider
+ ///
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Volume of this channel. 1.0 = full scale, 0.0 to mute
+ ///
+ public float Volume
+ {
+ get { return volume; }
+ set { volume = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/NAudio.csproj b/NAudio/NAudio.csproj
index f204c07d..29a20d0f 100644
--- a/NAudio/NAudio.csproj
+++ b/NAudio/NAudio.csproj
@@ -24,7 +24,7 @@
-
+