Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use custom stream to record audio #57

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions Plugin.AudioRecorder.Shared/AudioRecorderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public partial class AudioRecorderService
DateTime? silenceTime;
DateTime? startTime;
TaskCompletionSource<string> recordTask;
FileStream fileStream;

/// <summary>
/// Gets the details of the underlying audio stream.
Expand Down Expand Up @@ -75,6 +76,12 @@ public partial class AudioRecorderService
/// </summary>
/// <remarks>Defaults to .15. Value should be between 0 and 1.</remarks>
public float SilenceThreshold { get; set; } = .15f;

/// <summary>
/// Gets/sets a value indicating if headers will be written to the file/stream.
/// </summary>
/// <remarks>Defaults to <c>true</c></remarks>
public bool WriteHeaders { get; set; } = true;

/// <summary>
/// This event is raised when audio recording is complete and delivers a full filepath to the recorded audio file.
Expand All @@ -95,21 +102,28 @@ public AudioRecorderService ()
/// <summary>
/// Starts recording audio.
/// </summary>
/// <param name="recordStream"><c>null</c> (default) Optional stream to write audio data to, if null, a file will be created.</param>
/// <param name="writeHeaders"><c>false</c> (default) Set to true, to write WAV headers to recordStream after recording. Requires a seekable stream.</param>
/// <returns>A <see cref="Task"/> 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.</returns>
public async Task<Task<string>> StartRecording ()
public async Task<Task<string>> 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 ();
OnRecordingStarting ();

InitializeStream (PreferredSampleRate);

await recorder.StartRecorder (audioStream, FilePath);
await recorder.StartRecorder (audioStream, recordStream, WriteHeaders);

AudioStreamDetails = new AudioStreamDetails
{
Expand All @@ -132,7 +146,8 @@ public async Task<Task<string>> StartRecording ()
/// <returns>A <see cref="Stream"/> object that can be used to read the audio file from the beginning.</returns>
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 ()
Expand Down Expand Up @@ -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)
Expand Down
42 changes: 16 additions & 26 deletions Plugin.AudioRecorder.Shared/WaveRecorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,41 @@ namespace Plugin.AudioRecorder
{
internal class WaveRecorder : IDisposable
{
string audioFilePath;
FileStream fileStream;
StreamWriter streamWriter;
BinaryWriter writer;
int byteCount;
IAudioStream audioStream;
bool writeHeadersToStream;

/// <summary>
/// Starts recording WAVE format audio from the audio stream.
/// </summary>
/// <param name="stream">A <see cref="IAudioStream"/> that provides the audio data.</param>
/// <param name="filePath">The full path of the file to record audio to.</param>
public async Task StartRecorder (IAudioStream stream, string filePath)
/// <param name="recordStream">The stream the audio will be written to.</param>
/// <param name="writeHeaders"><c>false</c> (default) Write WAV headers to stream at the end of recording.</param>
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
if (audioStream != null)
{
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;
Expand All @@ -59,17 +61,7 @@ public async Task StartRecorder (IAudioStream stream, string filePath)
throw;
}
}

/// <summary>
/// Gets a new <see cref="Stream"/> to the audio file in readonly mode.
/// </summary>
/// <returns>A <see cref="Stream"/> object that can be used to read the audio file from the beginning.</returns>
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)
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down