From 5d4c40a6bd44a9e142f12a3fbbdeb2459ed6ef27 Mon Sep 17 00:00:00 2001 From: JochemH Date: Mon, 16 Nov 2020 12:26:08 +0100 Subject: [PATCH 1/4] Created injectable stream --- .../AudioRecorderService.cs | 22 ++++++++--- Plugin.AudioRecorder.Shared/WaveRecorder.cs | 38 ++++++------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs index 1d542c4..bb313c4 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. @@ -95,13 +96,20 @@ public AudioRecorderService () /// /// Starts recording audio. /// + /// null (default) optional stream to write audio data to, if null, a file will be created. /// 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 +117,7 @@ public async Task> StartRecording () InitializeStream (PreferredSampleRate); - await recorder.StartRecorder (audioStream, FilePath); + await recorder.StartRecorder (audioStream, recordStream); AudioStreamDetails = new AudioStreamDetails { @@ -132,7 +140,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 +225,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..37eeed1 100644 --- a/Plugin.AudioRecorder.Shared/WaveRecorder.cs +++ b/Plugin.AudioRecorder.Shared/WaveRecorder.cs @@ -8,9 +8,6 @@ namespace Plugin.AudioRecorder { internal class WaveRecorder : IDisposable { - string audioFilePath; - FileStream fileStream; - StreamWriter streamWriter; BinaryWriter writer; int byteCount; IAudioStream audioStream; @@ -19,14 +16,19 @@ internal class WaveRecorder : IDisposable /// 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. + public async Task StartRecorder (IAudioStream stream, Stream recordStream) { if (stream == null) { throw new ArgumentNullException (nameof (stream)); } + if (recordStream == null) + { + throw new ArgumentNullException (nameof (stream)); + } + try { //if we're restarting, let's see if we have an existing stream configured that can be stopped @@ -34,13 +36,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 +57,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 +70,7 @@ void OnStreamBroadcast (object sender, byte [] bytes) { try { - if (writer != null && streamWriter != null) + if (writer != null) { writer.Write (bytes); byteCount += bytes.Length; @@ -111,7 +99,7 @@ public void StopRecorder () if (writer != null) { - if (streamWriter.BaseStream.CanWrite) + if (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 +108,6 @@ public void StopRecorder () writer.Dispose (); //this should properly close/dispose the underlying stream as well writer = null; - fileStream = null; - streamWriter = null; } audioStream = null; From 077ac3d7e7234292e486b09f2b11b383755608c2 Mon Sep 17 00:00:00 2001 From: Jochem Harmes Date: Sun, 22 Nov 2020 17:26:28 +0100 Subject: [PATCH 2/4] Added parameter to control writing of headers. --- Plugin.AudioRecorder.Shared/AudioRecorderService.cs | 11 ++++++----- Plugin.AudioRecorder.Shared/WaveRecorder.cs | 10 +++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs index bb313c4..9d60d5f 100644 --- a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs +++ b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs @@ -96,11 +96,11 @@ public AudioRecorderService () /// /// Starts recording audio. /// - /// null (default) optional stream to write audio data to, if null, a file will be created. + /// 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 after recording. Note that stream should be seekable. /// 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 (Stream recordStream = null) + public async Task> StartRecording (Stream recordStream = null, bool writeHeaders = false) { if (recordStream == null) { @@ -109,7 +109,8 @@ public async Task> StartRecording (Stream recordStream = null) FilePath = await GetDefaultFilePath (); } fileStream = new FileStream (FilePath, FileMode.Create, FileAccess.Write, FileShare.Read); - recordStream = fileStream; + recordStream = fileStream; + writeHeaders = true; } ResetAudioDetection (); @@ -117,7 +118,7 @@ public async Task> StartRecording (Stream recordStream = null) InitializeStream (PreferredSampleRate); - await recorder.StartRecorder (audioStream, recordStream); + await recorder.StartRecorder (audioStream, recordStream, writeHeaders); AudioStreamDetails = new AudioStreamDetails { diff --git a/Plugin.AudioRecorder.Shared/WaveRecorder.cs b/Plugin.AudioRecorder.Shared/WaveRecorder.cs index 37eeed1..bca7388 100644 --- a/Plugin.AudioRecorder.Shared/WaveRecorder.cs +++ b/Plugin.AudioRecorder.Shared/WaveRecorder.cs @@ -11,13 +11,15 @@ internal class WaveRecorder : IDisposable BinaryWriter writer; int byteCount; IAudioStream audioStream; + bool writeHeadersToStream; /// /// Starts recording WAVE format audio from the audio stream. /// /// A that provides the audio data. /// The stream the audio will be written to. - public async Task StartRecorder (IAudioStream stream, Stream recordStream) + /// 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) { @@ -26,9 +28,11 @@ public async Task StartRecorder (IAudioStream stream, Stream recordStream) if (recordStream == null) { - throw new ArgumentNullException (nameof (stream)); + 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 @@ -99,7 +103,7 @@ public void StopRecorder () if (writer != null) { - if (writer.BaseStream.CanWrite && writer.BaseStream.CanSeek) + 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); From 217ea44a6bac5c2f0cca29636801a71c4edfa7df Mon Sep 17 00:00:00 2001 From: Jochem Harmes Date: Sun, 22 Nov 2020 18:16:24 +0100 Subject: [PATCH 3/4] updated docs --- Plugin.AudioRecorder.Shared/AudioRecorderService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs index 9d60d5f..979ce30 100644 --- a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs +++ b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs @@ -97,7 +97,7 @@ 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 after recording. Note that stream should be seekable. + /// 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 (Stream recordStream = null, bool writeHeaders = false) From 3c56e98dd5935555ec2c891309e79f53c72b286d Mon Sep 17 00:00:00 2001 From: JochemH Date: Fri, 21 May 2021 09:21:26 +0200 Subject: [PATCH 4/4] made writeheaders a property --- Plugin.AudioRecorder.Shared/AudioRecorderService.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs index 979ce30..5dce405 100644 --- a/Plugin.AudioRecorder.Shared/AudioRecorderService.cs +++ b/Plugin.AudioRecorder.Shared/AudioRecorderService.cs @@ -76,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. @@ -100,7 +106,7 @@ public AudioRecorderService () /// 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 (Stream recordStream = null, bool writeHeaders = false) + public async Task> StartRecording (Stream recordStream = null) { if (recordStream == null) { @@ -110,7 +116,6 @@ public async Task> StartRecording (Stream recordStream = null, bool } fileStream = new FileStream (FilePath, FileMode.Create, FileAccess.Write, FileShare.Read); recordStream = fileStream; - writeHeaders = true; } ResetAudioDetection (); @@ -118,7 +123,7 @@ public async Task> StartRecording (Stream recordStream = null, bool InitializeStream (PreferredSampleRate); - await recorder.StartRecorder (audioStream, recordStream, writeHeaders); + await recorder.StartRecorder (audioStream, recordStream, WriteHeaders); AudioStreamDetails = new AudioStreamDetails {