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 +}