Skip to content

Commit 300e9ab

Browse files
committed
[#37] Fixes an issue that caused loud noises to be recorded in the audio when recording at 144fps.
1 parent 4585ae4 commit 300e9ab

File tree

4 files changed

+313
-230
lines changed

4 files changed

+313
-230
lines changed

ScreenRecorder/AudioSource/AudioMixer.cs

Lines changed: 83 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,106 +7,126 @@
77

88
namespace ScreenRecorder.AudioSource
99
{
10+
/// <summary>
11+
/// Audio Mixer (2Ch, 16bit, 48000Hz)
12+
/// </summary>
1013
public class AudioMixer : IAudioSource, IDisposable
1114
{
12-
private readonly IAudioSource[] audioSources;
13-
private readonly CircularBuffer circularMixerBuffer;
15+
#region Fields
1416

15-
private Thread mixerThread, renderThread;
16-
private ManualResetEvent needToStop;
17+
private readonly IAudioSource[] _audioSources;
18+
private readonly CircularBuffer _circularMixerBuffer;
19+
private readonly int _samplesPerFrame;
20+
private readonly int _samplesBytesPerFrame;
21+
private readonly int _framesPerAdditinalSample;
22+
23+
private Thread _mixerThread, _renderThread;
24+
private ManualResetEvent _needToStop;
25+
26+
#endregion
27+
28+
#region Constructors
1729

1830
public AudioMixer(params IAudioSource[] audioSources)
1931
{
20-
this.audioSources = audioSources;
21-
var framePerBytes = (int)(48000.0d / VideoClockEvent.Framerate * 4);
22-
circularMixerBuffer = new CircularBuffer(framePerBytes * 6);
32+
_audioSources = audioSources;
33+
_samplesPerFrame = (int)(48000.0d / VideoClockEvent.Framerate);
34+
_samplesBytesPerFrame = _samplesPerFrame * 2 * 2; // 2Ch, 16bit
35+
36+
var remainingSamples = 48000 - (_samplesPerFrame * VideoClockEvent.Framerate);
37+
_framesPerAdditinalSample = remainingSamples != 0 ? VideoClockEvent.Framerate / remainingSamples : 0;
2338

24-
needToStop = new ManualResetEvent(false);
39+
_circularMixerBuffer = new CircularBuffer(_samplesBytesPerFrame * 6);
2540

26-
mixerThread = new Thread(MixerThreadHandler) { Name = "AudioMixer_Mixer", IsBackground = true };
27-
mixerThread.Start();
41+
_needToStop = new ManualResetEvent(false);
2842

29-
renderThread = new Thread(RenderThreadHandler) { Name = "AudioMixer_Render", IsBackground = true };
30-
renderThread.Start();
43+
_mixerThread = new Thread(MixerThreadHandler) { Name = "AudioMixer_Mixer", IsBackground = true };
44+
_mixerThread.Start();
45+
46+
_renderThread = new Thread(RenderThreadHandler) { Name = "AudioMixer_Render", IsBackground = true };
47+
_renderThread.Start();
3148
}
3249

33-
public event NewAudioPacketEventHandler NewAudioPacket;
50+
#endregion
51+
52+
#region Helpers
53+
54+
[DllImport("Kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)]
55+
internal static extern void ZeroMemory(IntPtr dest, IntPtr size);
3456

3557
public void Dispose()
3658
{
37-
if (needToStop != null)
59+
if (_needToStop != null)
3860
{
39-
needToStop.Set();
61+
_needToStop.Set();
4062
}
4163

42-
if (mixerThread != null)
64+
if (_mixerThread != null)
4365
{
44-
if (mixerThread.IsAlive && !mixerThread.Join(500))
66+
if (_mixerThread.IsAlive && !_mixerThread.Join(500))
4567
{
46-
mixerThread.Abort();
68+
_mixerThread.Abort();
4769
}
4870

49-
mixerThread = null;
71+
_mixerThread = null;
5072
}
5173

52-
if (renderThread != null)
74+
if (_renderThread != null)
5375
{
54-
if (renderThread.IsAlive && !renderThread.Join(500))
76+
if (_renderThread.IsAlive && !_renderThread.Join(500))
5577
{
56-
renderThread.Abort();
78+
_renderThread.Abort();
5779
}
5880

59-
renderThread = null;
81+
_renderThread = null;
6082
}
6183

62-
if (needToStop != null)
84+
if (_needToStop != null)
6385
{
64-
needToStop.Close();
86+
_needToStop.Close();
6587
}
6688

67-
needToStop = null;
89+
_needToStop = null;
6890
}
6991

70-
[DllImport("Kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)]
71-
internal static extern void ZeroMemory(IntPtr dest, IntPtr size);
72-
7392
private void MixerThreadHandler()
7493
{
75-
var framePerBytes = (int)(48000.0d / VideoClockEvent.Framerate * 4);
76-
77-
var sources = audioSources.Select(source => new AudioSourceResampler(source, 2, SampleFormat.S16, 48000))
94+
var sources = _audioSources.Select(source => new AudioSourceResampler(source, 2, SampleFormat.S16, 48000))
7895
.ToArray();
7996

80-
var sample = Marshal.AllocHGlobal(framePerBytes);
81-
var mixSample = Marshal.AllocHGlobal(framePerBytes);
97+
var sample = Marshal.AllocHGlobal(_samplesBytesPerFrame + 4);
98+
var mixSample = Marshal.AllocHGlobal(_samplesBytesPerFrame + 4);
8299

83100
using (var systemClockEvent = new VideoClockEvent())
84101
{
85-
while (!needToStop.WaitOne(0, false))
102+
long frames = 0;
103+
while (!_needToStop.WaitOne(0, false))
86104
{
87105
if (systemClockEvent.WaitOne(10))
88106
{
89-
var count = sources[0].Buffer.Read(mixSample, framePerBytes);
90-
if (count < framePerBytes)
107+
var samplesBytesPerFrame = _samplesBytesPerFrame + (frames++ % 3 == 0 ? 4 : 0);
108+
109+
var count = sources[0].Buffer.Read(mixSample, samplesBytesPerFrame);
110+
if (count < samplesBytesPerFrame)
91111
{
92-
ZeroMemory(mixSample + count, new IntPtr(framePerBytes - count));
112+
ZeroMemory(mixSample + count, new IntPtr(samplesBytesPerFrame - count));
93113
}
94114

95115
for (var i = 1; i < sources.Length; i++)
96116
{
97117
if (sources[i].IsValidBuffer)
98118
{
99-
count = sources[i].Buffer.Read(sample, framePerBytes);
100-
if (count < framePerBytes)
119+
count = sources[i].Buffer.Read(sample, samplesBytesPerFrame);
120+
if (count < samplesBytesPerFrame)
101121
{
102-
ZeroMemory(sample + count, new IntPtr(framePerBytes - count));
122+
ZeroMemory(sample + count, new IntPtr(samplesBytesPerFrame - count));
103123
}
104124

105125
MixStereoSamples(sample, mixSample, mixSample, count / 4);
106126
}
107127
}
108128

109-
circularMixerBuffer.Write(mixSample, 0, framePerBytes);
129+
_circularMixerBuffer.Write(mixSample, 0, samplesBytesPerFrame);
110130
}
111131
}
112132
}
@@ -151,26 +171,39 @@ private void MixStereoSamples(IntPtr sample1, IntPtr sample2, IntPtr mix, int sa
151171

152172
private void RenderThreadHandler()
153173
{
154-
var samples = (int)(48000.0d / VideoClockEvent.Framerate);
155-
156-
var mixerAudioBuffer = Marshal.AllocHGlobal(samples * 2 * 2); // 16bit 2channels
174+
var mixerAudioBuffer = Marshal.AllocHGlobal(_samplesBytesPerFrame + 4); // 16bit 2channels
157175
using (var systemClockEvent = new VideoClockEvent())
158176
{
159-
while (!needToStop.WaitOne(0, false))
177+
long frames = 0;
178+
while (!_needToStop.WaitOne(0, false))
160179
{
161180
if (systemClockEvent.WaitOne(10))
162181
{
163-
if (circularMixerBuffer.Count >= samples * 2 * 2)
182+
var samplesBytesPerFrame = _samplesBytesPerFrame + (frames++ % 3 == 0 ? 4 : 0);
183+
184+
if (_circularMixerBuffer.Count >= samplesBytesPerFrame)
164185
{
165-
circularMixerBuffer.Read(mixerAudioBuffer, samples * 2 * 2);
166-
NewAudioPacket?.Invoke(this,
167-
new NewAudioPacketEventArgs(48000, 2, SampleFormat.S16, samples, mixerAudioBuffer));
186+
_circularMixerBuffer.Read(mixerAudioBuffer, samplesBytesPerFrame);
187+
OnNewAudioPacket(new NewAudioPacketEventArgs(48000, 2, SampleFormat.S16, samplesBytesPerFrame / 4, mixerAudioBuffer));
168188
}
169189
}
170190
}
171191
}
172192

173193
Marshal.FreeHGlobal(mixerAudioBuffer);
174194
}
195+
196+
#endregion
197+
198+
#region Events
199+
200+
public event NewAudioPacketEventHandler NewAudioPacket;
201+
202+
public void OnNewAudioPacket(NewAudioPacketEventArgs args)
203+
{
204+
NewAudioPacket?.Invoke(this, args);
205+
}
206+
207+
#endregion
175208
}
176209
}

0 commit comments

Comments
 (0)