/* * NatCorder * Copyright (c) 2020 Yusuf Olokoba. */ namespace NatSuite.Recorders { using System; using System.IO; using System.Text; using System.Threading.Tasks; using Internal; /// /// Waveform audio recorder. /// public sealed class WAVRecorder : IMediaRecorder { #region --Client API-- /// /// Not supported. /// public (int width, int height) frameSize => default; /// /// Create an WAV recorder. /// /// Audio sample rate. /// Audio channel count. public WAVRecorder (int sampleRate, int channelCount) { this.sampleRate = sampleRate; this.channelCount = channelCount; this.stream = new FileStream(Utility.GetPath(@".wav"), FileMode.Create); this.sampleCount = 0; // Pre-allocate WAVE header var header = new byte[44]; stream.Write(header, 0, header.Length); } /// /// This recorder does not support committing pixel buffers. /// public void CommitFrame (T[] pixelBuffer = default, long timestamp = default) where T : struct { } /// /// This recorder does not support committing pixel buffers. /// public void CommitFrame (IntPtr nativeBuffer = default, long timestamp = default) { } /// /// Commit an audio sample buffer for encoding. /// /// Linear PCM audio sample buffer, interleaved by channel. /// Not used. public void CommitSamples (float[] sampleBuffer, long timestamp = default) { // Convert to short array var shortBuffer = new short[sampleBuffer.Length]; var byteBuffer = new byte[Buffer.ByteLength(shortBuffer)]; for (int i = 0; i < sampleBuffer.Length; i++) shortBuffer[i] = (short)(sampleBuffer[i] * short.MaxValue); // Write to output stream Buffer.BlockCopy(shortBuffer, 0, byteBuffer, 0, byteBuffer.Length); stream.Write(byteBuffer, 0, byteBuffer.Length); sampleCount += sampleBuffer.Length; } /// /// Finish writing and return the path to the recorded media file. /// public Task FinishWriting () { // Write header stream.Seek(0, SeekOrigin.Begin); stream.Write(Encoding.UTF8.GetBytes("RIFF"), 0, 4); stream.Write(BitConverter.GetBytes(stream.Length - 8), 0, 4); stream.Write(Encoding.UTF8.GetBytes("WAVE"), 0, 4); stream.Write(Encoding.UTF8.GetBytes("fmt "), 0, 4); stream.Write(BitConverter.GetBytes(16), 0, 4); stream.Write(BitConverter.GetBytes((ushort)1), 0, 2); stream.Write(BitConverter.GetBytes(channelCount), 0, 2); // Channel count stream.Write(BitConverter.GetBytes(sampleRate), 0, 4); // Sample rate stream.Write(BitConverter.GetBytes(sampleRate * channelCount * sizeof(short)), 0, 4); // Output rate in bytes stream.Write(BitConverter.GetBytes((ushort)(channelCount * 2)), 0, 2); // Block alignment stream.Write(BitConverter.GetBytes((ushort)16), 0, 2); // Bits per sample stream.Write(Encoding.UTF8.GetBytes("data"), 0, 4); stream.Write(BitConverter.GetBytes(sampleCount * sizeof(ushort)), 0, 4); // Total sample count // Close stream and return stream.Dispose(); return Task.FromResult(stream.Name); } #endregion #region --Operations-- private readonly int sampleRate, channelCount; private readonly FileStream stream; private int sampleCount; #endregion } }