123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- using NAudio.Wave;
- using System;
- using System.Collections.Concurrent;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- // ReSharper disable AccessToModifiedClosure
- namespace AudioNTR
- {
- internal class Program
- {
- private static void Main(string[] args)
- {
- var op = args.FirstOrDefault() ?? "";
- var argsToOp = args.Skip(1).ToArray();
- Console.WriteLine("Audio Net Transmitter Receiver");
- switch (op.ToLower())
- {
- default:
- Console.WriteLine("Usage: AudioNTR <op> [args1[ args2[...]]]");
- Console.WriteLine(" capture");
- Console.WriteLine(" Test run WasapiLoopbackCapture");
- Console.WriteLine(" playback");
- Console.WriteLine(" Test run WasapiOut (sin wave)");
- Console.WriteLine(" playback-echo");
- Console.WriteLine(" Test run WasapiOut (WasapiLoopbackCapture)");
- Console.WriteLine(" playback-echo-2");
- Console.WriteLine(" Test run WasapiOut (WasapiLoopbackCapture)");
- Console.WriteLine(" receiver");
- Console.WriteLine(" <port> [bind-address] Run as Receiver");
- Console.WriteLine(" transmitter");
- Console.WriteLine(" <target-address> <port> Run as Transmitter Captures default audio output");
- Console.WriteLine(" transmitter-sine-wave");
- Console.WriteLine(" <target-address> <port> Run as Transmitter Sine wave");
- Console.WriteLine(" receiver-as-client");
- Console.WriteLine(" <target-address> <port> Run receiver as client");
- Console.WriteLine(" transmitter-as-server");
- Console.WriteLine(" <target-address> <port> Run transmitter as server Captures default audio output");
- break;
- case "capture": TestRunCapture(argsToOp); break;
- case "playback": TestRunPlayBack(argsToOp); break;
- case "playback-echo": TestRunPlayBackEcho(argsToOp); break;
- case "playback-echo-2": TestRunPlayBackEcho2(argsToOp); break;
- case "receiver": RunReceiver(argsToOp); break;
- case "transmitter": RunTransmitter(argsToOp); break;
- case "transmitter-sine-wave": RunTransmitterSineWave(argsToOp); break;
- case "receiver-as-client": RunReceiverAsClient(argsToOp); break;
- case "transmitter-as-server": RunTransmitterAsServer(argsToOp); break;
- }
- }
- private static void RunTransmitterAsServer(string[] argsToOp)
- {
- if (argsToOp.Length < 1) throw new ArgumentException("least 1 args for listen port");
- if (false == int.TryParse(argsToOp[0], out var port)) throw new ArgumentException("invalid port");
- IPAddress address = null;
- if (argsToOp.Length > 1 && false == IPAddress.TryParse(argsToOp[1], out address)) throw new ArgumentException("invalid address");
- Console.WriteLine("Creating TcpListener...");
- var listener = new TcpListener(address ?? IPAddress.Any, port);
- Console.WriteLine("Start Listening...");
- listener.Start();
- Console.WriteLine($"Listening on {listener.LocalEndpoint}");
- var running = true;
- var task = Task.Run(() =>
- {
- while (running)
- {
- Console.WriteLine("Waiting connection...");
- var client = listener.AcceptTcpClient();
- client.NoDelay = true;
- Console.WriteLine($"Accept connection from {client.Client.RemoteEndPoint}...");
- var stream = client.GetStream();
- Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
- var provider = new WasapiLoopbackCapture();
- Console.WriteLine($"Encoding: {provider.WaveFormat.Encoding}");
- Console.WriteLine($"Channels: {provider.WaveFormat.Channels}");
- Console.WriteLine($"SampleRate: {provider.WaveFormat.SampleRate}");
- Console.WriteLine($"BitsPerSample: {provider.WaveFormat.BitsPerSample}");
- Console.WriteLine("Sending wave format ...");
- var writer = new BinaryWriter(stream, Encoding.UTF8, true);
- provider.WaveFormat.Serialize(writer);
- writer.Close();
- Console.WriteLine("Begin sending chunks...");
- long totalSent = 0;
- var up = DateTime.Now;
- var connectAlive = true;
- provider.DataAvailable += (sender, args) =>
- {
- Console.Title = $"Sent bytes: {totalSent:N0}, UP: {DateTime.Now - up}";
- try
- {
- stream.Write(args.Buffer, 0, args.BytesRecorded);
- stream.Flush();
- }
- catch (Exception e)
- {
- Console.WriteLine($"connection lost:{e.Message}");
- connectAlive = false;
- return;
- }
- totalSent += args.BytesRecorded;
- };
- provider.StartRecording();
- while (running && connectAlive) Thread.Sleep(100);
- provider.StopRecording();
- }
- });
- Console.WriteLine("Press ENTER to Exit.");
- Console.ReadLine();
- running = false;
- task.Wait();
- Console.WriteLine("Finished.");
- }
- private static void RunReceiverAsClient(string[] argsToOp)
- {
- const int bufferMs = 500;
-
- if (argsToOp.Length != 2) throw new ArgumentException("required 2 args for address and port");
- if (false == IPAddress.TryParse(argsToOp[0], out var address)) throw new ArgumentException("invalid address");
- if (false == int.TryParse(argsToOp[1], out var port)) throw new ArgumentException("invalid port");
- var running = true;
- var task = Task.Run(() =>
- {
- do
- {
- TcpClient client = null;
- try
- {
- Console.WriteLine("Creating TcpClient ...");
- client = new TcpClient { NoDelay = true };
- var ipEndPoint = new IPEndPoint(address, port);
- Console.WriteLine($"Connecting to {ipEndPoint} ...");
- client.Connect(ipEndPoint);
- var stream = client.GetStream();
- Console.WriteLine("Parsing wave format and create BufferedWaveProvider...");
- var reader = new BinaryReader(stream, Encoding.UTF8, true);
- var provider = new BufferedWaveProvider(new WaveFormat(reader))
- {
- BufferDuration = TimeSpan.FromMilliseconds(bufferMs),
- DiscardOnBufferOverflow = true,
- };
- reader.Dispose();
- Console.WriteLine($"Encoding: {provider.WaveFormat.Encoding}");
- Console.WriteLine($"Channels: {provider.WaveFormat.Channels}");
- Console.WriteLine($"SampleRate: {provider.WaveFormat.SampleRate}");
- Console.WriteLine($"BitsPerSample: {provider.WaveFormat.BitsPerSample}");
- Console.WriteLine($"Buffer Duration set to {provider.BufferDuration}");
- Console.WriteLine("Creating WasapiOut Instance...");
- var output = new WasapiOut();
- Console.WriteLine("Setup...");
- output.Init(provider);
- const int fillSleep = bufferMs * 11 / 20;
- Console.WriteLine($"Filling buffer, sleep {fillSleep}ms");
- Thread.Sleep(fillSleep);
- Console.WriteLine("Starting play...");
- output.Play();
- try
- {
- var buf = new byte[1024 * 1024];//1MB
- int readCount;
- Console.WriteLine("Begin read chunks...");
- do
- {
- Console.Title = $"Buffered bytes: {provider.BufferedBytes:N0}";
- readCount = stream.Read(buf, 0, buf.Length);
- provider.AddSamples(buf, 0, readCount);
- } while (readCount > 0 && running);
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- }
- Console.WriteLine("Closing connection...");
- client.Close();
- provider.ClearBuffer();
- Console.WriteLine("Stopping Playing...");
- output.Stop();
- Console.WriteLine("Free resources");
- client.Dispose();
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- if (running)
- {
- Console.WriteLine("Wait 10 Seconds to re connection");
- Thread.Sleep(10 * 1000);
- }
- }
- finally
- {
- Console.WriteLine("Free resources");
- client?.Close();
- client?.Dispose();
- }
- } while (running);
- });
- Console.WriteLine("Press ENTER to exit");
- Console.ReadLine();
- running = false;
- task.Wait();
- Console.WriteLine("Finished.");
- }
- private static void TestRunCapture(string[] argsToOp)
- {
- Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
- var capture = new WasapiLoopbackCapture();
- Console.WriteLine($"Encoding: {capture.WaveFormat.Encoding}");
- Console.WriteLine($"Channels: {capture.WaveFormat.Channels}");
- Console.WriteLine($"SampleRate: {capture.WaveFormat.SampleRate:N0}");
- Console.WriteLine($"BitsPerSample:{capture.WaveFormat.BitsPerSample:N0}");
- var bytes = 0;
- capture.DataAvailable += (_, e) =>
- {
- bytes += e.BytesRecorded;
- Console.Title = $"Buffer: {e.Buffer.Length} Bytes: {e.BytesRecorded:N0} Total: {bytes:N0}";
- };
- Console.WriteLine("Starting capture...");
- capture.StartRecording();
- Console.WriteLine("Capturing, Press ENTER to exit");
- Console.ReadLine();
- Console.WriteLine("Stopping capture...");
- capture.StopRecording();
- Console.WriteLine("Stopped.");
- }
- private static void TestRunPlayBack(string[] argsToOp)
- {
- Console.WriteLine("Creating WasapiOut Instance...");
- var output = new WasapiOut();
- Console.WriteLine("Creating SineWaveSampleProvider Instance...");
- var provider = new SineWaveSampleProvider();
- Console.WriteLine("Init...");
- output.Init(provider);
- Console.WriteLine("Starting play...");
- output.Play();
- Console.WriteLine("Playing, Press ENTER to exit");
- Console.ReadLine();
- Console.WriteLine("Stopping Playing...");
- output.Stop();
- Console.WriteLine("Stopped.");
- }
- private static void TestRunPlayBackEcho(string[] argsToOp)
- {
- Console.WriteLine("Creating WasapiOut Instance...");
- var output = new WasapiOut();
- Console.WriteLine("Creating WaveInProvider Instance by WasapiLoopbackCapture...");
- var provider = new WaveInProvider(new WasapiLoopbackCapture());
- Console.WriteLine("Init...");
- output.Init(provider);
- //output.Volume = 0.5f;
- Console.WriteLine("Starting Capture...");
- provider.StartRecording();
- Console.WriteLine("Starting play...");
- output.Play();
- Console.WriteLine("Playing, Press ENTER to exit");
- Console.ReadLine();
- Console.WriteLine("Stopping Capture...");
- provider.StopRecording();
- Console.WriteLine("Stopping Playing...");
- output.Stop();
- Console.WriteLine("Stopped.");
- }
- private static void TestRunPlayBackEcho2(string[] argsToOp)
- {
- Console.WriteLine("Creating WasapiOut Instance...");
- var output = new WasapiOut();
- Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
- var capture = new WasapiLoopbackCapture();
- Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
- var provider = new BufferedWaveProvider(capture.WaveFormat);
- Console.WriteLine("Setup...");
- capture.DataAvailable += (sender, args) => provider.AddSamples(args.Buffer, 0, args.BytesRecorded);
- output.Init(provider);
- Console.WriteLine("Starting Capture...");
- capture.StartRecording();
- Console.WriteLine("Starting play...");
- output.Play();
- Console.WriteLine("Playing, Press ENTER to exit");
- Console.ReadLine();
- Console.WriteLine("Stopping Capture...");
- capture.StopRecording();
- Console.WriteLine("Free resoures...");
- provider.ClearBuffer();
- Console.WriteLine("Stopping Playing...");
- output.Stop();
- Console.WriteLine("Stopped.");
- }
- private static void RunReceiver(string[] argsToOp)
- {
- const int bufferMs = 500;
- if (argsToOp.Length < 1) throw new ArgumentException("least 1 args for listen port");
- if (false == int.TryParse(argsToOp[0], out var port)) throw new ArgumentException("invalid port");
- IPAddress address = null;
- if (argsToOp.Length > 1 && false == IPAddress.TryParse(argsToOp[1], out address)) throw new ArgumentException("invalid address");
- Console.WriteLine("Creating TcpListener...");
- var listener = new TcpListener(address ?? IPAddress.Any, port);
- Console.WriteLine("Start Listening...");
- listener.Start();
- Console.WriteLine($"Listening on {listener.LocalEndpoint}");
- var running = true;
- var task = Task.Run(() =>
- {
- while (running)
- {
- Console.WriteLine("Waiting connection...");
- var client = listener.AcceptTcpClient();
- client.NoDelay = true;
- Console.WriteLine($"Accept connection from {client.Client.RemoteEndPoint}...");
- var stream = client.GetStream();
- Console.WriteLine("Parsing wave format and create BufferedWaveProvider...");
- var reader = new BinaryReader(stream, Encoding.UTF8, true);
- var provider = new BufferedWaveProvider(new WaveFormat(reader))
- {
- BufferDuration = TimeSpan.FromMilliseconds(bufferMs),
- DiscardOnBufferOverflow = true,
- };
- reader.Dispose();
- Console.WriteLine($"Encoding: {provider.WaveFormat.Encoding}");
- Console.WriteLine($"Channels: {provider.WaveFormat.Channels}");
- Console.WriteLine($"SampleRate: {provider.WaveFormat.SampleRate}");
- Console.WriteLine($"BitsPerSample: {provider.WaveFormat.BitsPerSample}");
- Console.WriteLine($"Buffer Duration set to {provider.BufferDuration}");
- Console.WriteLine("Creating WasapiOut Instance...");
- var output = new WasapiOut();
- Console.WriteLine("Setup...");
- output.Init(provider);
- const int fillSleep = bufferMs * 11 / 20;
- Console.WriteLine($"Filling buffer, sleep {fillSleep}ms");
- Thread.Sleep(fillSleep);
- Console.WriteLine("Starting play...");
- output.Play();
- try
- {
- var buf = new byte[1024 * 1024];//1MB
- int readCount;
- //Console.WriteLine("Sending ACK...");
- //stream.WriteByte(1);
- Console.WriteLine("Begin read chunks...");
- do
- {
- Console.Title = $"Buffered bytes: {provider.BufferedBytes:N0}";
- readCount = stream.Read(buf, 0, buf.Length);
- provider.AddSamples(buf, 0, readCount);
- } while (readCount > 0 && running);
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- }
- Console.WriteLine("Closing connection...");
- client.Close();
- provider.ClearBuffer();
- Console.WriteLine("Stopping Playing...");
- output.Stop();
- Console.WriteLine("Free resources");
- client.Dispose();
- }
- });
- Console.WriteLine("Press ENTER to Exit.");
- Console.ReadLine();
- running = false;
- task.Wait();
- Console.WriteLine("Finished.");
- }
- private static void RunTransmitterSineWave(string[] argsToOp)
- {
- if (argsToOp.Length != 2) throw new ArgumentException("required 2 args for address and port");
- if (false == IPAddress.TryParse(argsToOp[0], out var address)) throw new ArgumentException("invalid address");
- if (false == int.TryParse(argsToOp[1], out var port)) throw new ArgumentException("invalid port");
- var running = true;
- var task = Task.Run(() =>
- {
- do
- {
- TcpClient client = null;
- try
- {
- Console.WriteLine("Creating TcpClient ...");
- client = new TcpClient();
- var ipEndPoint = new IPEndPoint(address, port);
- Console.WriteLine($"Connecting to {ipEndPoint} ...");
- client.Connect(ipEndPoint);
- var stream = client.GetStream();
- Console.WriteLine("Connected, creating SineWaveSampleProvider ...");
- var provider = new SineWaveSampleProvider();
- Console.WriteLine($"Encoding: {provider.WaveFormat.Encoding}");
- Console.WriteLine($"Channels: {provider.WaveFormat.Channels}");
- Console.WriteLine($"SampleRate: {provider.WaveFormat.SampleRate}");
- Console.WriteLine($"BitsPerSample: {provider.WaveFormat.BitsPerSample}");
- Console.WriteLine("Sending wave format ...");
- var writer = new BinaryWriter(stream, Encoding.UTF8, true);
- provider.WaveFormat.Serialize(writer);
- writer.Close();
- Console.WriteLine("Begin sending chunks...");
- var buffer = new byte[provider.WaveFormat.AverageBytesPerSecond / 4];
- while (running)
- {
- provider.Read(buffer, 0, buffer.Length);
- stream.Write(buffer, 0, buffer.Length);
- }
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- if (running)
- {
- Console.WriteLine("Wait 10 Seconds to re connection");
- Thread.Sleep(10 * 1000);
- }
- }
- finally
- {
- Console.WriteLine("Free resources");
- client?.Close();
- client?.Dispose();
- }
- } while (running);
- });
- Console.WriteLine("Press ENTER to exit");
- Console.ReadLine();
- running = false;
- task.Wait();
- Console.WriteLine("Finished.");
- }
- private static void RunTransmitter(string[] argsToOp)
- {
- if (argsToOp.Length != 2) throw new ArgumentException("required 2 args for address and port");
- if (false == IPAddress.TryParse(argsToOp[0], out var address)) throw new ArgumentException("invalid address");
- if (false == int.TryParse(argsToOp[1], out var port)) throw new ArgumentException("invalid port");
- var running = true;
- var task = Task.Run(() =>
- {
- do
- {
- TcpClient client = null;
- try
- {
- Console.WriteLine("Creating TcpClient ...");
- client = new TcpClient { NoDelay = true };
- var ipEndPoint = new IPEndPoint(address, port);
- Console.WriteLine($"Connecting to {ipEndPoint} ...");
- client.Connect(ipEndPoint);
- var stream = client.GetStream();
- Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
- var provider = new WasapiLoopbackCapture();
- Console.WriteLine("Sending wave format ...");
- var writer = new BinaryWriter(stream, Encoding.UTF8, true);
- provider.WaveFormat.Serialize(writer);
- writer.Close();
- //Console.WriteLine("Waiting ACK ...");
- //var ack = stream.ReadByte();
- //if (ack != 1) throw new Exception("Unexpected ACK");
- Console.WriteLine("Begin sending chunks...");
- long totalSent = 0;
- var up = DateTime.Now;
- provider.DataAvailable += (sender, args) =>
- {
- Console.Title = $"Sent bytes: {totalSent:N0}, UP: {DateTime.Now - up}";
- stream.Write(args.Buffer, 0, args.BytesRecorded);
- stream.Flush();
- totalSent += args.BytesRecorded;
- };
- provider.StartRecording();
- while (running) Thread.Sleep(100);
- provider.StopRecording();
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- if (running)
- {
- Console.WriteLine("Wait 10 Seconds to re connection");
- Thread.Sleep(10 * 1000);
- }
- }
- finally
- {
- Console.WriteLine("Free resources");
- client?.Close();
- client?.Dispose();
- }
- } while (running);
- });
- Console.WriteLine("Press ENTER to exit");
- Console.ReadLine();
- running = false;
- task.Wait();
- Console.WriteLine("Finished.");
- }
- }
- internal class WaveInProvider : IWaveProvider
- {
- private readonly IWaveIn _input;
- private readonly BlockingCollection<byte> _buffer;
- private readonly CancellationTokenSource _cancellationTokenSource = new();
- public WaveFormat WaveFormat { get; }
- public WaveInProvider(IWaveIn input, int msBuffer = 1000)
- {
- _input = input;
- WaveFormat = _input.WaveFormat;
- _input.DataAvailable += Input_DataAvailable;
- _buffer = new((int)Math.Ceiling(WaveFormat.SampleRate / 1000f * msBuffer * WaveFormat.Channels * (WaveFormat.BitsPerSample / 8f)));
- }
- private void Input_DataAvailable(object sender, WaveInEventArgs e)
- {
- if (_cancellationTokenSource.IsCancellationRequested) return;
- for (var i = 0; i < e.BytesRecorded; i++) _buffer.Add(e.Buffer[i], _cancellationTokenSource.Token);
- }
- public void StartRecording()
- {
- _input.StartRecording();
- }
- public void StopRecording()
- {
- _input.StopRecording();
- _cancellationTokenSource.Cancel();
- }
- public int Read(byte[] buffer, int offset, int count)
- {
- var readCount = 0;
- var eof = offset + count;
- do
- {
- try
- {
- buffer[offset] = _buffer.Take(_cancellationTokenSource.Token);
- }
- catch (OperationCanceledException)
- {
- return readCount;
- }
- readCount++;
- offset++;
- } while (_buffer.Count > 0 && offset < eof && _cancellationTokenSource.IsCancellationRequested == false);
- return readCount;
- }
- }
- internal class SineWaveSampleProvider : IWaveProvider, ISampleProvider
- {
- public int Freq { get; }
- public float Amp { get; }
- public WaveFormat WaveFormat { get; }
- private int _sample;
- public SineWaveSampleProvider(int sampleRate = 441000, int channels = 1, int freq = 1000, float amp = 0.25f)
- {
- Freq = freq;
- Amp = amp;
- WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels);
- }
- public int Read(byte[] buffer, int offset, int count)
- {
- var waveBuffer = new WaveBuffer(buffer);
- var samplesRequired = count / 4;
- var samplesRead = Read(waveBuffer.FloatBuffer, offset / 4, samplesRequired);
- return samplesRead * 4;
- }
- public int Read(float[] buffer, int offset, int sampleCount)
- {
- var sampleRate = WaveFormat.SampleRate;
- for (var n = 0; n < sampleCount; n++)
- {
- buffer[n + offset] = (float)(Amp * Math.Sin((2 * Math.PI * _sample * Freq) / sampleRate));
- _sample++;
- if (_sample >= sampleRate) _sample = 0;
- }
- return sampleCount;
- }
- }
- }
|