Fix PortAudio stream cleanup on shutdown and restart (#860)
* Crash Fix * Crash Fix * Crash Fix * Crash Fix
This commit is contained in:
1 parent
3bc71b8241
commit
0b6bc14563
2 files changed
+187
-36
No files matched your search
@@ -64,21 +64,30 @@ namespace Vocaluxe.Lib.Sound
|
||||
{
|
||||
if (_Disposed)
|
||||
return;
|
||||
|
||||
if (!disposing)
|
||||
CLog.Debug("Did not close CPortAudioHandle");
|
||||
//Make sure we do not leek any streams as we may keep PA open
|
||||
if (_Streams.Count > 0)
|
||||
|
||||
IntPtr[] streamsToClose;
|
||||
lock (_Mutex)
|
||||
{
|
||||
CLog.Debug("Did not close " + _Streams.Count + "PortAudio-Stream(s)");
|
||||
while (_Streams.Count > 0)
|
||||
CloseStream(_Streams[0]);
|
||||
streamsToClose = _Streams.ToArray();
|
||||
}
|
||||
|
||||
if (streamsToClose.Length > 0)
|
||||
CLog.Debug("Did not close " + streamsToClose.Length + " PortAudio-Stream(s)");
|
||||
|
||||
foreach (IntPtr stream in streamsToClose)
|
||||
CloseStream(stream);
|
||||
|
||||
lock (_Mutex)
|
||||
{
|
||||
if (_Disposed)
|
||||
return;
|
||||
|
||||
Debug.Assert(_RefCount > 0);
|
||||
_RefCount--;
|
||||
|
||||
if (_RefCount == 0)
|
||||
{
|
||||
try
|
||||
@@ -90,6 +99,7 @@ namespace Vocaluxe.Lib.Sound
|
||||
CLog.Error(ex, "Error disposing PortAudio");
|
||||
}
|
||||
}
|
||||
|
||||
_Disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -172,22 +182,53 @@ namespace Vocaluxe.Lib.Sound
|
||||
lock (_Mutex)
|
||||
{
|
||||
if (_Disposed)
|
||||
throw new ObjectDisposedException("PortAudioHandle already disposed");
|
||||
return;
|
||||
|
||||
if (stream == IntPtr.Zero)
|
||||
{
|
||||
CLog.Debug("Stream is null, skipping close.");
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasTracked = _Streams.Remove(stream);
|
||||
if (!wasTracked)
|
||||
{
|
||||
CLog.Debug("Stream was already removed or never tracked, skipping duplicate close.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (stream == IntPtr.Zero)
|
||||
try
|
||||
{
|
||||
CLog.Debug("Stream is null, skipping close.");
|
||||
return;
|
||||
PortAudio.PaError isStoppedResult = PortAudio.Pa_IsStreamStopped(stream);
|
||||
if (isStoppedResult == PortAudio.PaError.paStreamIsNotStopped)
|
||||
{
|
||||
PortAudio.PaError stopResult = PortAudio.Pa_StopStream(stream);
|
||||
if (stopResult != PortAudio.PaError.paNoError &&
|
||||
stopResult != PortAudio.PaError.paStreamIsStopped)
|
||||
{
|
||||
CLog.Error("StopStream before close failed: " + PortAudio.Pa_GetErrorText(stopResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
PortAudio.Pa_CloseStream(stream);
|
||||
catch (Exception ex)
|
||||
{
|
||||
CLog.Error(ex, "Error stopping stream before close:");
|
||||
}
|
||||
|
||||
PortAudio.PaError closeResult = PortAudio.Pa_CloseStream(stream);
|
||||
if (closeResult != PortAudio.PaError.paNoError)
|
||||
CLog.Error("CloseStream error: " + PortAudio.Pa_GetErrorText(closeResult));
|
||||
}
|
||||
catch (AccessViolationException ex)
|
||||
{
|
||||
CLog.Error(ex, "Access violation while closing PortAudio stream.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CLog.Error(ex, "Error closing stream:");
|
||||
}
|
||||
_Streams.Remove(stream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start Voice Capturing
|
||||
/// Voice Capturing
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Start()
|
||||
@@ -84,32 +84,65 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
|
||||
Stop();
|
||||
|
||||
foreach (IntPtr handle in _RecHandle)
|
||||
if (_RecHandle != null && _PaHandle != null)
|
||||
{
|
||||
int waitcount = 0;
|
||||
while (waitcount < 5 && PortAudioSharp.PortAudio.Pa_IsStreamStopped(handle) == PortAudioSharp.PortAudio.PaError.paStreamIsNotStopped)
|
||||
for (int i = 0; i < _RecHandle.Length; i++)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
waitcount++;
|
||||
IntPtr handle = _RecHandle[i];
|
||||
if (handle == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
int waitcount = 0;
|
||||
while (waitcount < 5)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PortAudioSharp.PortAudio.Pa_IsStreamStopped(handle) !=
|
||||
PortAudioSharp.PortAudio.PaError.paStreamIsNotStopped)
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CLog.Error(ex, "Error while waiting for PortAudio stream to stop:");
|
||||
break;
|
||||
}
|
||||
|
||||
Thread.Sleep(1);
|
||||
waitcount++;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_PaHandle.CloseStream(handle);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CLog.Error(ex, "Error closing old PortAudio record stream before restart:");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_RecHandle[i] = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CBuffer buffer in _Buffer)
|
||||
buffer.Reset();
|
||||
|
||||
for (int i = 0; i < _RecHandle.Length; i++)
|
||||
_RecHandle[i] = IntPtr.Zero;
|
||||
|
||||
for (int dev = 0; dev < _Devices.Count; dev++)
|
||||
{
|
||||
bool usingDevice = false;
|
||||
for (int ch = 0; ch < _Devices[dev].Channels; ++ch) {
|
||||
for (int ch = 0; ch < _Devices[dev].Channels; ++ch)
|
||||
{
|
||||
if (_Devices[dev].PlayerChannel[ch] > 0)
|
||||
usingDevice = true;
|
||||
}
|
||||
if (usingDevice)
|
||||
{
|
||||
PortAudioSharp.PortAudio.PaStreamParameters? inputParams = new PortAudioSharp.PortAudio.PaStreamParameters
|
||||
|
||||
if (!usingDevice)
|
||||
continue;
|
||||
|
||||
PortAudioSharp.PortAudio.PaStreamParameters? inputParams =
|
||||
new PortAudioSharp.PortAudio.PaStreamParameters
|
||||
{
|
||||
channelCount = _Devices[dev].Channels,
|
||||
device = _Devices[dev].ID,
|
||||
@@ -117,7 +150,8 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
suggestedLatency = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_Devices[dev].ID).defaultLowInputLatency,
|
||||
hostApiSpecificStreamInfo = IntPtr.Zero
|
||||
};
|
||||
if (!_PaHandle.OpenInputStream(
|
||||
|
||||
if (!_PaHandle.OpenInputStream(
|
||||
out _RecHandle[dev],
|
||||
ref inputParams,
|
||||
44100,
|
||||
@@ -125,12 +159,52 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag,
|
||||
_MyRecProc,
|
||||
new IntPtr(dev)))
|
||||
return false;
|
||||
{
|
||||
for (int j = 0; j < _RecHandle.Length; j++)
|
||||
{
|
||||
if (_RecHandle[j] == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
if (_PaHandle.CheckError("Start Stream (rec)", PortAudioSharp.PortAudio.Pa_StartStream(_RecHandle[dev])))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
_PaHandle.CloseStream(_RecHandle[j]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CLog.Error(ex, "Error rolling back PortAudio stream after open failure:");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_RecHandle[j] = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_PaHandle.CheckError("Start Stream (rec)", PortAudioSharp.PortAudio.Pa_StartStream(_RecHandle[dev])))
|
||||
{
|
||||
for (int j = 0; j < _RecHandle.Length; j++)
|
||||
{
|
||||
if (_RecHandle[j] == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
_PaHandle.CloseStream(_RecHandle[j]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CLog.Error(ex, "Error rolling back PortAudio stream after start failure:");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_RecHandle[j] = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -142,16 +216,34 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
|
||||
if (_RecHandle == null)
|
||||
return true;
|
||||
|
||||
foreach (IntPtr handle in _RecHandle)
|
||||
{
|
||||
if (handle != IntPtr.Zero)
|
||||
if (handle == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
PortAudioSharp.PortAudio.Pa_StopStream(handle);
|
||||
PortAudioSharp.PortAudio.Pa_CloseStream(handle);
|
||||
PortAudioSharp.PortAudio.PaError isStoppedResult = PortAudioSharp.PortAudio.Pa_IsStreamStopped(handle);
|
||||
if (isStoppedResult == PortAudioSharp.PortAudio.PaError.paStreamIsNotStopped)
|
||||
{
|
||||
PortAudioSharp.PortAudio.PaError stopResult = PortAudioSharp.PortAudio.Pa_StopStream(handle);
|
||||
if (stopResult != PortAudioSharp.PortAudio.PaError.paNoError &&
|
||||
stopResult != PortAudioSharp.PortAudio.PaError.paStreamIsStopped)
|
||||
{
|
||||
CLog.Error("StopStream error: " + PortAudioSharp.PortAudio.Pa_GetErrorText(stopResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CLog.Error(ex, "Error stopping PortAudio record stream:");
|
||||
}
|
||||
}
|
||||
_RecHandle = new IntPtr[_Devices.Count];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -162,16 +254,34 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
{
|
||||
if (_RecHandle != null && _RecHandle.Length > 0)
|
||||
{
|
||||
foreach (IntPtr handle in _RecHandle)
|
||||
Stop();
|
||||
|
||||
if (_PaHandle != null)
|
||||
{
|
||||
if (handle != IntPtr.Zero)
|
||||
for (int i = 0; i < _RecHandle.Length; i++)
|
||||
{
|
||||
_PaHandle.CloseStream(handle);
|
||||
IntPtr handle = _RecHandle[i];
|
||||
if (handle == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
_PaHandle.CloseStream(handle);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CLog.Error(ex, "Error closing PortAudio record stream:");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_RecHandle[i] = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_RecHandle = new IntPtr[_Devices.Count];
|
||||
}
|
||||
|
||||
if (_PaHandle != null)
|
||||
{
|
||||
_PaHandle.Close();
|
||||
@@ -214,4 +324,4 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
return PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user