diff --git a/Vocaluxe/Lib/Sound/CPortAudioHandle.cs b/Vocaluxe/Lib/Sound/CPortAudioHandle.cs
index d8ce237e..49244cde 100644
--- a/Vocaluxe/Lib/Sound/CPortAudioHandle.cs
+++ b/Vocaluxe/Lib/Sound/CPortAudioHandle.cs
@@ -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);
}
}
diff --git a/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs b/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs
index dde1cbe9..11030ab0 100644
--- a/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs
+++ b/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs
@@ -74,7 +74,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
}
///
- /// Start Voice Capturing
+ /// Voice Capturing
///
///
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;
}
}
-}
\ No newline at end of file
+}