WAVRecorder.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /*
  2. * NatCorder
  3. * Copyright (c) 2020 Yusuf Olokoba.
  4. */
  5. namespace NatSuite.Recorders {
  6. using System;
  7. using System.IO;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using Internal;
  11. /// <summary>
  12. /// Waveform audio recorder.
  13. /// </summary>
  14. public sealed class WAVRecorder : IMediaRecorder {
  15. #region --Client API--
  16. /// <summary>
  17. /// Not supported.
  18. /// </summary>
  19. public (int width, int height) frameSize => default;
  20. /// <summary>
  21. /// Create an WAV recorder.
  22. /// </summary>
  23. /// <param name="sampleRate">Audio sample rate.</param>
  24. /// <param name="channelCount">Audio channel count.</param>
  25. public WAVRecorder (int sampleRate, int channelCount) {
  26. this.sampleRate = sampleRate;
  27. this.channelCount = channelCount;
  28. this.stream = new FileStream(Utility.GetPath(@".wav"), FileMode.Create);
  29. this.sampleCount = 0;
  30. // Pre-allocate WAVE header
  31. var header = new byte[44];
  32. stream.Write(header, 0, header.Length);
  33. }
  34. /// <summary>
  35. /// This recorder does not support committing pixel buffers.
  36. /// </summary>
  37. public void CommitFrame<T> (T[] pixelBuffer = default, long timestamp = default) where T : struct { }
  38. /// <summary>
  39. /// This recorder does not support committing pixel buffers.
  40. /// </summary>
  41. public void CommitFrame (IntPtr nativeBuffer = default, long timestamp = default) { }
  42. /// <summary>
  43. /// Commit an audio sample buffer for encoding.
  44. /// </summary>
  45. /// <param name="sampleBuffer">Linear PCM audio sample buffer, interleaved by channel.</param>
  46. /// <param name="timestamp">Not used.</param>
  47. public void CommitSamples (float[] sampleBuffer, long timestamp = default) {
  48. // Convert to short array
  49. var shortBuffer = new short[sampleBuffer.Length];
  50. var byteBuffer = new byte[Buffer.ByteLength(shortBuffer)];
  51. for (int i = 0; i < sampleBuffer.Length; i++)
  52. shortBuffer[i] = (short)(sampleBuffer[i] * short.MaxValue);
  53. // Write to output stream
  54. Buffer.BlockCopy(shortBuffer, 0, byteBuffer, 0, byteBuffer.Length);
  55. stream.Write(byteBuffer, 0, byteBuffer.Length);
  56. sampleCount += sampleBuffer.Length;
  57. }
  58. /// <summary>
  59. /// Finish writing and return the path to the recorded media file.
  60. /// </summary>
  61. public Task<string> FinishWriting () {
  62. // Write header
  63. stream.Seek(0, SeekOrigin.Begin);
  64. stream.Write(Encoding.UTF8.GetBytes("RIFF"), 0, 4);
  65. stream.Write(BitConverter.GetBytes(stream.Length - 8), 0, 4);
  66. stream.Write(Encoding.UTF8.GetBytes("WAVE"), 0, 4);
  67. stream.Write(Encoding.UTF8.GetBytes("fmt "), 0, 4);
  68. stream.Write(BitConverter.GetBytes(16), 0, 4);
  69. stream.Write(BitConverter.GetBytes((ushort)1), 0, 2);
  70. stream.Write(BitConverter.GetBytes(channelCount), 0, 2); // Channel count
  71. stream.Write(BitConverter.GetBytes(sampleRate), 0, 4); // Sample rate
  72. stream.Write(BitConverter.GetBytes(sampleRate * channelCount * sizeof(short)), 0, 4); // Output rate in bytes
  73. stream.Write(BitConverter.GetBytes((ushort)(channelCount * 2)), 0, 2); // Block alignment
  74. stream.Write(BitConverter.GetBytes((ushort)16), 0, 2); // Bits per sample
  75. stream.Write(Encoding.UTF8.GetBytes("data"), 0, 4);
  76. stream.Write(BitConverter.GetBytes(sampleCount * sizeof(ushort)), 0, 4); // Total sample count
  77. // Close stream and return
  78. stream.Dispose();
  79. return Task.FromResult(stream.Name);
  80. }
  81. #endregion
  82. #region --Operations--
  83. private readonly int sampleRate, channelCount;
  84. private readonly FileStream stream;
  85. private int sampleCount;
  86. #endregion
  87. }
  88. }