diff --git a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs index 1d542c4..5dce405 100644 --- a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs +++ b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs @@ -21,6 +21,7 @@ public partial class AudioRecorderService DateTime? silenceTime; DateTime? startTime; TaskCompletionSource recordTask; + FileStream fileStream; /// /// Gets the details of the underlying audio stream. @@ -75,6 +76,12 @@ public partial class AudioRecorderService /// /// Defaults to .15. Value should be between 0 and 1. public float SilenceThreshold { get; set; } = .15f; + + /// + /// Gets/sets a value indicating if headers will be written to the file/stream. + /// + /// Defaults to true + public bool WriteHeaders { get; set; } = true; /// /// This event is raised when audio recording is complete and delivers a full filepath to the recorded audio file. @@ -95,13 +102,20 @@ public AudioRecorderService () /// /// Starts recording audio. /// + /// null (default) Optional stream to write audio data to, if null, a file will be created. + /// false (default) Set to true, to write WAV headers to recordStream after recording. Requires a seekable stream. /// A that will complete when recording is finished. /// The task result will be the path to the recorded audio file, or null if no audio was recorded. - public async Task> StartRecording () + public async Task> StartRecording (Stream recordStream = null) { - if (FilePath == null) + if (recordStream == null) { - FilePath = await GetDefaultFilePath (); + if (FilePath == null) + { + FilePath = await GetDefaultFilePath (); + } + fileStream = new FileStream (FilePath, FileMode.Create, FileAccess.Write, FileShare.Read); + recordStream = fileStream; } ResetAudioDetection (); @@ -109,7 +123,7 @@ public async Task> StartRecording () InitializeStream (PreferredSampleRate); - await recorder.StartRecorder (audioStream, FilePath); + await recorder.StartRecorder (audioStream, recordStream, WriteHeaders); AudioStreamDetails = new AudioStreamDetails { @@ -132,7 +146,8 @@ public async Task> StartRecording () /// A object that can be used to read the audio file from the beginning. public Stream GetAudioFileStream () { - return recorder.GetAudioFileStream (); + //return a new stream to the same audio file, in Read mode + return new FileStream (FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } void ResetAudioDetection () @@ -216,10 +231,11 @@ public async Task StopRecording (bool continueProcessing = true) Debug.WriteLine ("Error in StopRecording: {0}", ex); } + fileStream?.Dispose(); OnRecordingStopped (); var returnedFilePath = GetAudioFilePath (); - // complete the recording Task for anthing waiting on this + // complete the recording Task for anything waiting on this recordTask.TrySetResult (returnedFilePath); if (continueProcessing) diff --git a/Plugin.AudioRecorder.Shared/WaveRecorder.cs b/Plugin.AudioRecorder.Shared/WaveRecorder.cs index 50a6fd9..bca7388 100644 --- a/Plugin.AudioRecorder.Shared/WaveRecorder.cs +++ b/Plugin.AudioRecorder.Shared/WaveRecorder.cs @@ -8,25 +8,31 @@ namespace Plugin.AudioRecorder { internal class WaveRecorder : IDisposable { - string audioFilePath; - FileStream fileStream; - StreamWriter streamWriter; BinaryWriter writer; int byteCount; IAudioStream audioStream; + bool writeHeadersToStream; /// /// Starts recording WAVE format audio from the audio stream. /// /// A that provides the audio data. - /// The full path of the file to record audio to. - public async Task StartRecorder (IAudioStream stream, string filePath) + /// The stream the audio will be written to. + /// false (default) Write WAV headers to stream at the end of recording. + public async Task StartRecorder (IAudioStream stream, Stream recordStream, bool writeHeaders = false) { if (stream == null) { throw new ArgumentNullException (nameof (stream)); } + if (recordStream == null) + { + throw new ArgumentNullException (nameof (recordStream)); + } + + writeHeadersToStream = writeHeaders; + try { //if we're restarting, let's see if we have an existing stream configured that can be stopped @@ -34,13 +40,9 @@ public async Task StartRecorder (IAudioStream stream, string filePath) { await audioStream.Stop (); } - - audioFilePath = filePath; + audioStream = stream; - - fileStream = new FileStream (filePath, FileMode.Create, FileAccess.Write, FileShare.Read); - streamWriter = new StreamWriter (fileStream); - writer = new BinaryWriter (streamWriter.BaseStream, Encoding.UTF8); + writer = new BinaryWriter (recordStream, Encoding.UTF8, true); byteCount = 0; audioStream.OnBroadcast += OnStreamBroadcast; @@ -59,17 +61,7 @@ public async Task StartRecorder (IAudioStream stream, string filePath) throw; } } - - /// - /// Gets a new to the audio file in readonly mode. - /// - /// A object that can be used to read the audio file from the beginning. - public Stream GetAudioFileStream () - { - //return a new stream to the same audio file, in Read mode - return new FileStream (audioFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - } - + void StreamActiveChanged (object sender, bool active) { if (!active) @@ -82,7 +74,7 @@ void OnStreamBroadcast (object sender, byte [] bytes) { try { - if (writer != null && streamWriter != null) + if (writer != null) { writer.Write (bytes); byteCount += bytes.Length; @@ -111,7 +103,7 @@ public void StopRecorder () if (writer != null) { - if (streamWriter.BaseStream.CanWrite) + if (writeHeadersToStream && writer.BaseStream.CanWrite && writer.BaseStream.CanSeek) { //now that audio is finished recording, write a WAV/RIFF header at the beginning of the file writer.Seek (0, SeekOrigin.Begin); @@ -120,8 +112,6 @@ public void StopRecorder () writer.Dispose (); //this should properly close/dispose the underlying stream as well writer = null; - fileStream = null; - streamWriter = null; } audioStream = null;