Program.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. using NAudio.Wave;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using static AudioNTR.CmdOps;
  12. // ReSharper disable AccessToModifiedClosure
  13. namespace AudioNTR;
  14. internal class CmdOps
  15. {
  16. public const string CmdTestCaptureLoopback = "test-capture-loopback";
  17. public const string CmdTestPlayback = "test-playback";
  18. public const string CmdTestEcho1 = "test-echo-1";
  19. public const string CmdTestEcho2 = "test-echo-2";
  20. public const string CmdRunReceiverAsServer = "receiver";
  21. public const string CmdRunTransmitterAsClient = "transmitter";
  22. public const string CmdRunTransmitterAsClientSineWave = "transmitter-sine-wave";
  23. public const string CmdRunReceiverAsClient = "receiver-as-client";
  24. public const string CmdRunTransmitterAsServer = "transmitter-as-server";
  25. }
  26. internal class Program
  27. {
  28. private static void Main(string[] args)
  29. {
  30. var op = args.FirstOrDefault() ?? "";
  31. var argsToOp = args.Skip(1).ToArray();
  32. Console.WriteLine("Audio Net Transmitter Receiver");
  33. switch (op.ToLower())
  34. {
  35. default:
  36. Console.WriteLine($"Usage: AudioNTR <op> [args1[ args2[...]]]");
  37. Console.WriteLine($" {CmdTestCaptureLoopback}");
  38. Console.WriteLine($" Test: WasapiLoopbackCapture");
  39. Console.WriteLine($" {CmdTestPlayback}");
  40. Console.WriteLine($" Test: WasapiOut (sin wave)");
  41. Console.WriteLine($" {CmdTestEcho1}");
  42. Console.WriteLine($" Test: WasapiOut (WasapiLoopbackCapture)");
  43. Console.WriteLine($" {CmdTestEcho2}");
  44. Console.WriteLine($" Test: WasapiOut (WasapiLoopbackCapture) with async buffer");
  45. Console.WriteLine($" {CmdRunReceiverAsServer}");
  46. Console.WriteLine($" <port> [bind-address] Run: Receiver as Server");
  47. Console.WriteLine($" {CmdRunTransmitterAsClient}");
  48. Console.WriteLine($" <target-address> <port> Run: Transmitter as Client, Captures default audio output");
  49. Console.WriteLine($" {CmdRunTransmitterAsClientSineWave}");
  50. Console.WriteLine($" <target-address> <port> Run: Transmitter as Client, send Sine wave");
  51. Console.WriteLine($" {CmdRunReceiverAsClient}");
  52. Console.WriteLine($" <target-address> <port> Run: Receiver as Client, Playback to default audio output");
  53. Console.WriteLine($" {CmdRunTransmitterAsServer}");
  54. Console.WriteLine($" <target-address> <port> Run: Transmitter as Server, Captures default audio output");
  55. break;
  56. case CmdTestCaptureLoopback: RunTestCaptureLoopBack(argsToOp); break;
  57. case CmdTestPlayback: RunTestPlayBack(argsToOp); break;
  58. case CmdTestEcho1: RunTestPlayBackEcho(argsToOp); break;
  59. case CmdTestEcho2: RunTestPlayBackEcho2(argsToOp); break;
  60. case CmdRunReceiverAsServer: RunReceiverAsServer(argsToOp); break;
  61. case CmdRunTransmitterAsClient: RunTransmitterAsClient(argsToOp); break;
  62. case CmdRunTransmitterAsClientSineWave: RunTransmitterAsClientSendSineWave(argsToOp); break;
  63. case CmdRunReceiverAsClient: RunReceiverAsClient(argsToOp); break;
  64. case CmdRunTransmitterAsServer: RunTransmitterAsServer(argsToOp); break;
  65. }
  66. }
  67. private static void RunTestCaptureLoopBack(string[] argsToOp)
  68. {
  69. Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
  70. var capture = new WasapiLoopbackCapture();
  71. Console.WriteLine($"Encoding: {capture.WaveFormat.Encoding}");
  72. Console.WriteLine($"Channels: {capture.WaveFormat.Channels}");
  73. Console.WriteLine($"SampleRate: {capture.WaveFormat.SampleRate:N0}");
  74. Console.WriteLine($"BitsPerSample:{capture.WaveFormat.BitsPerSample:N0}");
  75. var bytes = 0;
  76. capture.DataAvailable += (_, e) =>
  77. {
  78. bytes += e.BytesRecorded;
  79. Console.Title = $"Buffer: {e.Buffer.Length} Bytes: {e.BytesRecorded:N0} Total: {bytes:N0}";
  80. };
  81. Console.WriteLine("Starting capture...");
  82. capture.StartRecording();
  83. Console.WriteLine("Capturing, Press ENTER to exit");
  84. Console.ReadLine();
  85. Console.WriteLine("Stopping capture...");
  86. capture.StopRecording();
  87. Console.WriteLine("Stopped.");
  88. }
  89. private static void RunTransmitterAsServer(string[] argsToOp)
  90. {
  91. if (argsToOp.Length < 1) throw new ArgumentException("least 1 args for listen port");
  92. if (false == int.TryParse(argsToOp[0], out var port)) throw new ArgumentException("invalid port");
  93. IPAddress address = null;
  94. if (argsToOp.Length > 1 && false == IPAddress.TryParse(argsToOp[1], out address)) throw new ArgumentException("invalid address");
  95. Console.WriteLine("Creating TcpListener...");
  96. var listener = new TcpListener(address ?? IPAddress.Any, port);
  97. Console.WriteLine("Start Listening...");
  98. listener.Start();
  99. Console.WriteLine($"Listening on {listener.LocalEndpoint}");
  100. var running = true;
  101. var task = Task.Run(() =>
  102. {
  103. while (running)
  104. {
  105. Console.WriteLine("Waiting connection...");
  106. var client = listener.AcceptTcpClient();
  107. client.NoDelay = true;
  108. Console.WriteLine($"Accept connection from {client.Client.RemoteEndPoint}...");
  109. var stream = client.GetStream();
  110. Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
  111. var provider = new WasapiLoopbackCapture();
  112. Console.WriteLine($"Encoding: {provider.WaveFormat.Encoding}");
  113. Console.WriteLine($"Channels: {provider.WaveFormat.Channels}");
  114. Console.WriteLine($"SampleRate: {provider.WaveFormat.SampleRate}");
  115. Console.WriteLine($"BitsPerSample: {provider.WaveFormat.BitsPerSample}");
  116. Console.WriteLine("Sending wave format ...");
  117. var writer = new BinaryWriter(stream, Encoding.UTF8, true);
  118. provider.WaveFormat.Serialize(writer);
  119. writer.Close();
  120. Console.WriteLine("Begin sending chunks...");
  121. long totalSent = 0;
  122. var up = DateTime.Now;
  123. var connectAlive = true;
  124. provider.DataAvailable += (sender, args) =>
  125. {
  126. Console.Title = $"Sent bytes: {totalSent:N0}, UP: {DateTime.Now - up}";
  127. try
  128. {
  129. stream.Write(args.Buffer, 0, args.BytesRecorded);
  130. stream.Flush();
  131. }
  132. catch (Exception e)
  133. {
  134. Console.WriteLine($"connection lost:{e.Message}");
  135. connectAlive = false;
  136. return;
  137. }
  138. totalSent += args.BytesRecorded;
  139. };
  140. provider.StartRecording();
  141. while (running && connectAlive) Thread.Sleep(100);
  142. provider.StopRecording();
  143. }
  144. });
  145. Console.WriteLine("Press ENTER to Exit.");
  146. Console.ReadLine();
  147. running = false;
  148. task.Wait();
  149. Console.WriteLine("Finished.");
  150. }
  151. private static void RunReceiverAsClient(string[] argsToOp)
  152. {
  153. const int bufferMs = 500;
  154. if (argsToOp.Length != 2) throw new ArgumentException("required 2 args for address and port");
  155. if (false == IPAddress.TryParse(argsToOp[0], out var address)) throw new ArgumentException("invalid address");
  156. if (false == int.TryParse(argsToOp[1], out var port)) throw new ArgumentException("invalid port");
  157. var running = true;
  158. var task = Task.Run(() =>
  159. {
  160. do
  161. {
  162. TcpClient client = null;
  163. try
  164. {
  165. Console.WriteLine("Creating TcpClient ...");
  166. client = new TcpClient { NoDelay = true };
  167. var ipEndPoint = new IPEndPoint(address, port);
  168. Console.WriteLine($"Connecting to {ipEndPoint} ...");
  169. client.Connect(ipEndPoint);
  170. var stream = client.GetStream();
  171. Console.WriteLine("Parsing wave format and create BufferedWaveProvider...");
  172. var reader = new BinaryReader(stream, Encoding.UTF8, true);
  173. var provider = new BufferedWaveProvider(new WaveFormat(reader))
  174. {
  175. BufferDuration = TimeSpan.FromMilliseconds(bufferMs),
  176. DiscardOnBufferOverflow = true,
  177. };
  178. reader.Dispose();
  179. Console.WriteLine($"Encoding: {provider.WaveFormat.Encoding}");
  180. Console.WriteLine($"Channels: {provider.WaveFormat.Channels}");
  181. Console.WriteLine($"SampleRate: {provider.WaveFormat.SampleRate}");
  182. Console.WriteLine($"BitsPerSample: {provider.WaveFormat.BitsPerSample}");
  183. Console.WriteLine($"Buffer Duration set to {provider.BufferDuration}");
  184. Console.WriteLine("Creating WasapiOut Instance...");
  185. var output = new WasapiOut();
  186. Console.WriteLine("Setup...");
  187. output.Init(provider);
  188. const int fillSleep = bufferMs * 11 / 20;
  189. Console.WriteLine($"Filling buffer, sleep {fillSleep}ms");
  190. Thread.Sleep(fillSleep);
  191. Console.WriteLine("Starting play...");
  192. output.Play();
  193. try
  194. {
  195. var buf = new byte[1024 * 1024];//1MB
  196. int readCount;
  197. Console.WriteLine("Begin read chunks...");
  198. do
  199. {
  200. Console.Title = $"Buffered bytes: {provider.BufferedBytes:N0}";
  201. readCount = stream.Read(buf, 0, buf.Length);
  202. provider.AddSamples(buf, 0, readCount);
  203. } while (readCount > 0 && running);
  204. }
  205. catch (Exception e)
  206. {
  207. Console.WriteLine(e);
  208. }
  209. Console.WriteLine("Closing connection...");
  210. client.Close();
  211. provider.ClearBuffer();
  212. Console.WriteLine("Stopping Playing...");
  213. output.Stop();
  214. Console.WriteLine("Free resources");
  215. client.Dispose();
  216. }
  217. catch (Exception e)
  218. {
  219. Console.WriteLine(e);
  220. if (running)
  221. {
  222. Console.WriteLine("Wait 10 Seconds to re connection");
  223. Thread.Sleep(10 * 1000);
  224. }
  225. }
  226. finally
  227. {
  228. Console.WriteLine("Free resources");
  229. client?.Close();
  230. client?.Dispose();
  231. }
  232. } while (running);
  233. });
  234. Console.WriteLine("Press ENTER to exit");
  235. Console.ReadLine();
  236. running = false;
  237. task.Wait();
  238. Console.WriteLine("Finished.");
  239. }
  240. private static void RunTestPlayBack(string[] argsToOp)
  241. {
  242. Console.WriteLine("Creating WasapiOut Instance...");
  243. var output = new WasapiOut();
  244. Console.WriteLine("Creating SineWaveSampleProvider Instance...");
  245. var provider = new SineWaveSampleProvider();
  246. Console.WriteLine("Init...");
  247. output.Init(provider);
  248. Console.WriteLine("Starting play...");
  249. output.Play();
  250. Console.WriteLine("Playing, Press ENTER to exit");
  251. Console.ReadLine();
  252. Console.WriteLine("Stopping Playing...");
  253. output.Stop();
  254. Console.WriteLine("Stopped.");
  255. }
  256. private static void RunTestPlayBackEcho(string[] argsToOp)
  257. {
  258. Console.WriteLine("Creating WasapiOut Instance...");
  259. var output = new WasapiOut();
  260. Console.WriteLine("Creating WaveInProvider Instance by WasapiLoopbackCapture...");
  261. var provider = new WaveInProvider(new WasapiLoopbackCapture());
  262. Console.WriteLine("Init...");
  263. output.Init(provider);
  264. //output.Volume = 0.5f;
  265. Console.WriteLine("Starting Capture...");
  266. provider.StartRecording();
  267. Console.WriteLine("Starting play...");
  268. output.Play();
  269. Console.WriteLine("Playing, Press ENTER to exit");
  270. Console.ReadLine();
  271. Console.WriteLine("Stopping Capture...");
  272. provider.StopRecording();
  273. Console.WriteLine("Stopping Playing...");
  274. output.Stop();
  275. Console.WriteLine("Stopped.");
  276. }
  277. private static void RunTestPlayBackEcho2(string[] argsToOp)
  278. {
  279. Console.WriteLine("Creating WasapiOut Instance...");
  280. var output = new WasapiOut();
  281. Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
  282. var capture = new WasapiLoopbackCapture();
  283. Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
  284. var provider = new BufferedWaveProvider(capture.WaveFormat);
  285. Console.WriteLine("Setup...");
  286. capture.DataAvailable += (sender, args) => provider.AddSamples(args.Buffer, 0, args.BytesRecorded);
  287. output.Init(provider);
  288. Console.WriteLine("Starting Capture...");
  289. capture.StartRecording();
  290. Console.WriteLine("Starting play...");
  291. output.Play();
  292. Console.WriteLine("Playing, Press ENTER to exit");
  293. Console.ReadLine();
  294. Console.WriteLine("Stopping Capture...");
  295. capture.StopRecording();
  296. Console.WriteLine("Free resoures...");
  297. provider.ClearBuffer();
  298. Console.WriteLine("Stopping Playing...");
  299. output.Stop();
  300. Console.WriteLine("Stopped.");
  301. }
  302. private static void RunReceiverAsServer(string[] argsToOp)
  303. {
  304. const int bufferMs = 500;
  305. if (argsToOp.Length < 1) throw new ArgumentException("least 1 args for listen port");
  306. if (false == int.TryParse(argsToOp[0], out var port)) throw new ArgumentException("invalid port");
  307. IPAddress address = null;
  308. if (argsToOp.Length > 1 && false == IPAddress.TryParse(argsToOp[1], out address)) throw new ArgumentException("invalid address");
  309. Console.WriteLine("Creating TcpListener...");
  310. var listener = new TcpListener(address ?? IPAddress.Any, port);
  311. Console.WriteLine("Start Listening...");
  312. listener.Start();
  313. Console.WriteLine($"Listening on {listener.LocalEndpoint}");
  314. var running = true;
  315. var task = Task.Run(() =>
  316. {
  317. while (running)
  318. {
  319. Console.WriteLine("Waiting connection...");
  320. var client = listener.AcceptTcpClient();
  321. client.NoDelay = true;
  322. Console.WriteLine($"Accept connection from {client.Client.RemoteEndPoint}...");
  323. var stream = client.GetStream();
  324. Console.WriteLine("Parsing wave format and create BufferedWaveProvider...");
  325. var reader = new BinaryReader(stream, Encoding.UTF8, true);
  326. var provider = new BufferedWaveProvider(new WaveFormat(reader))
  327. {
  328. BufferDuration = TimeSpan.FromMilliseconds(bufferMs),
  329. DiscardOnBufferOverflow = true,
  330. };
  331. reader.Dispose();
  332. Console.WriteLine($"Encoding: {provider.WaveFormat.Encoding}");
  333. Console.WriteLine($"Channels: {provider.WaveFormat.Channels}");
  334. Console.WriteLine($"SampleRate: {provider.WaveFormat.SampleRate}");
  335. Console.WriteLine($"BitsPerSample: {provider.WaveFormat.BitsPerSample}");
  336. Console.WriteLine($"Buffer Duration set to {provider.BufferDuration}");
  337. Console.WriteLine("Creating WasapiOut Instance...");
  338. var output = new WasapiOut();
  339. Console.WriteLine("Setup...");
  340. output.Init(provider);
  341. const int fillSleep = bufferMs * 11 / 20;
  342. Console.WriteLine($"Filling buffer, sleep {fillSleep}ms");
  343. Thread.Sleep(fillSleep);
  344. Console.WriteLine("Starting play...");
  345. output.Play();
  346. try
  347. {
  348. var buf = new byte[1024 * 1024];//1MB
  349. int readCount;
  350. //Console.WriteLine("Sending ACK...");
  351. //stream.WriteByte(1);
  352. Console.WriteLine("Begin read chunks...");
  353. do
  354. {
  355. Console.Title = $"Buffered bytes: {provider.BufferedBytes:N0}";
  356. readCount = stream.Read(buf, 0, buf.Length);
  357. provider.AddSamples(buf, 0, readCount);
  358. } while (readCount > 0 && running);
  359. }
  360. catch (Exception e)
  361. {
  362. Console.WriteLine(e);
  363. }
  364. Console.WriteLine("Closing connection...");
  365. client.Close();
  366. provider.ClearBuffer();
  367. Console.WriteLine("Stopping Playing...");
  368. output.Stop();
  369. Console.WriteLine("Free resources");
  370. client.Dispose();
  371. }
  372. });
  373. Console.WriteLine("Press ENTER to Exit.");
  374. Console.ReadLine();
  375. running = false;
  376. task.Wait();
  377. Console.WriteLine("Finished.");
  378. }
  379. private static void RunTransmitterAsClientSendSineWave(string[] argsToOp)
  380. {
  381. if (argsToOp.Length != 2) throw new ArgumentException("required 2 args for address and port");
  382. if (false == IPAddress.TryParse(argsToOp[0], out var address)) throw new ArgumentException("invalid address");
  383. if (false == int.TryParse(argsToOp[1], out var port)) throw new ArgumentException("invalid port");
  384. var running = true;
  385. var task = Task.Run(() =>
  386. {
  387. do
  388. {
  389. TcpClient client = null;
  390. try
  391. {
  392. Console.WriteLine("Creating TcpClient ...");
  393. client = new TcpClient();
  394. var ipEndPoint = new IPEndPoint(address, port);
  395. Console.WriteLine($"Connecting to {ipEndPoint} ...");
  396. client.Connect(ipEndPoint);
  397. var stream = client.GetStream();
  398. Console.WriteLine("Connected, creating SineWaveSampleProvider ...");
  399. var provider = new SineWaveSampleProvider();
  400. Console.WriteLine($"Encoding: {provider.WaveFormat.Encoding}");
  401. Console.WriteLine($"Channels: {provider.WaveFormat.Channels}");
  402. Console.WriteLine($"SampleRate: {provider.WaveFormat.SampleRate}");
  403. Console.WriteLine($"BitsPerSample: {provider.WaveFormat.BitsPerSample}");
  404. Console.WriteLine("Sending wave format ...");
  405. var writer = new BinaryWriter(stream, Encoding.UTF8, true);
  406. provider.WaveFormat.Serialize(writer);
  407. writer.Close();
  408. Console.WriteLine("Begin sending chunks...");
  409. var buffer = new byte[provider.WaveFormat.AverageBytesPerSecond / 4];
  410. while (running)
  411. {
  412. provider.Read(buffer, 0, buffer.Length);
  413. stream.Write(buffer, 0, buffer.Length);
  414. }
  415. }
  416. catch (Exception e)
  417. {
  418. Console.WriteLine(e);
  419. if (running)
  420. {
  421. Console.WriteLine("Wait 10 Seconds to re connection");
  422. Thread.Sleep(10 * 1000);
  423. }
  424. }
  425. finally
  426. {
  427. Console.WriteLine("Free resources");
  428. client?.Close();
  429. client?.Dispose();
  430. }
  431. } while (running);
  432. });
  433. Console.WriteLine("Press ENTER to exit");
  434. Console.ReadLine();
  435. running = false;
  436. task.Wait();
  437. Console.WriteLine("Finished.");
  438. }
  439. private static void RunTransmitterAsClient(string[] argsToOp)
  440. {
  441. if (argsToOp.Length != 2) throw new ArgumentException("required 2 args for address and port");
  442. if (false == IPAddress.TryParse(argsToOp[0], out var address)) throw new ArgumentException("invalid address");
  443. if (false == int.TryParse(argsToOp[1], out var port)) throw new ArgumentException("invalid port");
  444. var running = true;
  445. var task = Task.Run(() =>
  446. {
  447. do
  448. {
  449. TcpClient client = null;
  450. try
  451. {
  452. Console.WriteLine("Creating TcpClient ...");
  453. client = new TcpClient { NoDelay = true };
  454. var ipEndPoint = new IPEndPoint(address, port);
  455. Console.WriteLine($"Connecting to {ipEndPoint} ...");
  456. client.Connect(ipEndPoint);
  457. var stream = client.GetStream();
  458. Console.WriteLine("Creating WasapiLoopbackCapture Instance...");
  459. var provider = new WasapiLoopbackCapture();
  460. Console.WriteLine("Sending wave format ...");
  461. var writer = new BinaryWriter(stream, Encoding.UTF8, true);
  462. provider.WaveFormat.Serialize(writer);
  463. writer.Close();
  464. //Console.WriteLine("Waiting ACK ...");
  465. //var ack = stream.ReadByte();
  466. //if (ack != 1) throw new Exception("Unexpected ACK");
  467. Console.WriteLine("Begin sending chunks...");
  468. long totalSent = 0;
  469. var up = DateTime.Now;
  470. provider.DataAvailable += (sender, args) =>
  471. {
  472. Console.Title = $"Sent bytes: {totalSent:N0}, UP: {DateTime.Now - up}";
  473. stream.Write(args.Buffer, 0, args.BytesRecorded);
  474. stream.Flush();
  475. totalSent += args.BytesRecorded;
  476. };
  477. provider.StartRecording();
  478. while (running) Thread.Sleep(100);
  479. provider.StopRecording();
  480. }
  481. catch (Exception e)
  482. {
  483. Console.WriteLine(e);
  484. if (running)
  485. {
  486. Console.WriteLine("Wait 10 Seconds to re connection");
  487. Thread.Sleep(10 * 1000);
  488. }
  489. }
  490. finally
  491. {
  492. Console.WriteLine("Free resources");
  493. client?.Close();
  494. client?.Dispose();
  495. }
  496. } while (running);
  497. });
  498. Console.WriteLine("Press ENTER to exit");
  499. Console.ReadLine();
  500. running = false;
  501. task.Wait();
  502. Console.WriteLine("Finished.");
  503. }
  504. }
  505. internal class WaveInProvider : IWaveProvider
  506. {
  507. private readonly IWaveIn _input;
  508. private readonly BlockingCollection<byte> _buffer;
  509. private readonly CancellationTokenSource _cancellationTokenSource = new();
  510. public WaveFormat WaveFormat { get; }
  511. public WaveInProvider(IWaveIn input, int msBuffer = 1000)
  512. {
  513. _input = input;
  514. WaveFormat = _input.WaveFormat;
  515. _input.DataAvailable += Input_DataAvailable;
  516. _buffer = new((int)Math.Ceiling(WaveFormat.SampleRate / 1000f * msBuffer * WaveFormat.Channels * (WaveFormat.BitsPerSample / 8f)));
  517. }
  518. private void Input_DataAvailable(object sender, WaveInEventArgs e)
  519. {
  520. if (_cancellationTokenSource.IsCancellationRequested) return;
  521. for (var i = 0; i < e.BytesRecorded; i++) _buffer.Add(e.Buffer[i], _cancellationTokenSource.Token);
  522. }
  523. public void StartRecording()
  524. {
  525. _input.StartRecording();
  526. }
  527. public void StopRecording()
  528. {
  529. _input.StopRecording();
  530. _cancellationTokenSource.Cancel();
  531. }
  532. public int Read(byte[] buffer, int offset, int count)
  533. {
  534. var readCount = 0;
  535. var eof = offset + count;
  536. do
  537. {
  538. try
  539. {
  540. buffer[offset] = _buffer.Take(_cancellationTokenSource.Token);
  541. }
  542. catch (OperationCanceledException)
  543. {
  544. return readCount;
  545. }
  546. readCount++;
  547. offset++;
  548. } while (_buffer.Count > 0 && offset < eof && _cancellationTokenSource.IsCancellationRequested == false);
  549. return readCount;
  550. }
  551. }
  552. internal class SineWaveSampleProvider : IWaveProvider, ISampleProvider
  553. {
  554. public int Freq { get; }
  555. public float Amp { get; }
  556. public WaveFormat WaveFormat { get; }
  557. private int _sample;
  558. public SineWaveSampleProvider(int sampleRate = 441000, int channels = 1, int freq = 1000, float amp = 0.25f)
  559. {
  560. Freq = freq;
  561. Amp = amp;
  562. WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels);
  563. }
  564. public int Read(byte[] buffer, int offset, int count)
  565. {
  566. var waveBuffer = new WaveBuffer(buffer);
  567. var samplesRequired = count / 4;
  568. var samplesRead = Read(waveBuffer.FloatBuffer, offset / 4, samplesRequired);
  569. return samplesRead * 4;
  570. }
  571. public int Read(float[] buffer, int offset, int sampleCount)
  572. {
  573. var sampleRate = WaveFormat.SampleRate;
  574. for (var n = 0; n < sampleCount; n++)
  575. {
  576. buffer[n + offset] = (float)(Amp * Math.Sin((2 * Math.PI * _sample * Freq) / sampleRate));
  577. _sample++;
  578. if (_sample >= sampleRate) _sample = 0;
  579. }
  580. return sampleCount;
  581. }
  582. }