-
Notifications
You must be signed in to change notification settings - Fork 0
/
Recorder.cs
99 lines (82 loc) · 3.45 KB
/
Recorder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
using System;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using NAudio.CoreAudioApi;
using NAudio.Wave;
namespace WhatAmIHearing.Audio;
internal sealed class Recorder : IDisposable
{
private readonly WasapiLoopbackCapture _audioCapturer;
private readonly WaveFormat _waveFormat;
private readonly long _bytesToRecord;
private readonly double _secondsOfAudioToRecord;
private readonly CancellationToken _cancelToken;
private readonly WaveFileWriter _audioWriter;
private readonly MemoryStream _recordedFileStream;
private readonly ManualResetEvent _recordingFinishedEvent = new( false );
public event EventHandler<RecordingProgressEventArgs> RecordingProgress;
public Recorder( MMDevice device, WaveFormat waveFormat, long bytesToRecord, CancellationToken cancelToken )
{
_audioCapturer = new WasapiLoopbackCapture( device ) { WaveFormat = waveFormat };
_audioCapturer.DataAvailable += OnDataCaptured;
_audioCapturer.RecordingStopped += OnRecordingStopped;
_waveFormat = waveFormat;
_bytesToRecord = bytesToRecord;
_cancelToken = cancelToken;
_recordedFileStream = new MemoryStream();
_audioWriter = new WaveFileWriter( _recordedFileStream, _waveFormat );
_secondsOfAudioToRecord = Math.Round( (double)_bytesToRecord / _waveFormat.AverageBytesPerSecond, 2 );
}
public void Dispose()
{
_audioWriter.Dispose();
_recordedFileStream.Dispose();
_audioCapturer.Dispose();
_audioCapturer.DataAvailable -= OnDataCaptured;
_audioCapturer.RecordingStopped -= OnRecordingStopped;
_recordingFinishedEvent.Dispose();
}
public async Task<RecordingResult> RecordAsync()
{
RecordingProgress.Invoke( this, new RecordingProgressEventArgs( 0, GetStatusText( 0 ) ) );
_audioCapturer.StartRecording();
await Task.Run( _recordingFinishedEvent.WaitOne );
byte[] data = null;
if ( !_cancelToken.IsCancellationRequested )
{
_audioWriter.Flush();
data = _recordedFileStream.ToArray();
}
return new RecordingResult( data, _waveFormat );
}
private void OnDataCaptured( object sender, WaveInEventArgs e )
{
if ( _cancelToken.IsCancellationRequested || _audioWriter.Position + e.BytesRecorded >= _bytesToRecord )
{
_audioCapturer.StopRecording();
}
else
{
_audioWriter.Write( e.Buffer, 0, e.BytesRecorded );
RecordingProgress.Invoke( this, new RecordingProgressEventArgs( (double)_audioWriter.Position / _bytesToRecord, GetStatusText( _audioWriter.Position ) ) );
}
}
private void OnRecordingStopped( object sender, StoppedEventArgs e ) => _recordingFinishedEvent.Set();
private string GetStatusText( long bytesRecorded )
{
switch ( AppSettings.Instance.ProgressType )
{
case ProgressDisplayType.None:
return string.Empty;
case ProgressDisplayType.Bytes:
return $"Recording: {bytesRecorded}/{_bytesToRecord} bytes";
case ProgressDisplayType.Seconds:
var secondsRecorded = Math.Round( (double)bytesRecorded / _waveFormat.AverageBytesPerSecond, 2 );
return $"Recording: {secondsRecorded}/{_secondsOfAudioToRecord} seconds";
default:
throw new InvalidEnumArgumentException();
}
}
}