diff --git a/Vocaluxe/Lib/Sound/CPortAudioCommon.cs b/Vocaluxe/Lib/Sound/CPortAudioCommon.cs new file mode 100644 index 00000000..d3584860 --- /dev/null +++ b/Vocaluxe/Lib/Sound/CPortAudioCommon.cs @@ -0,0 +1,161 @@ +using System; +using System.Diagnostics; +using PortAudioSharp; +using Vocaluxe.Base; + +namespace Vocaluxe.Lib.Sound +{ + /// + /// PortAudio can be used for record and playback + /// So do some common stuff here and make sure those 2 do not interfere + /// + /// DO NEVER use following Pa_* functions other than the ones from this class: + /// Initialize, Terminate, OpenStream, CloseStream + /// + abstract class CPortAudioCommon + { + private static int _RefCount; + private static readonly object _Mutex = new object(); + + /// + /// Safe method to init PortAudio (adds a reference) + /// MUST call CloseDriver when done + /// + /// True on success, falso if not initialized (log written) + protected static bool _InitDriver() + { + lock (_Mutex) + { + if (_RefCount == 0) + { + if (_CheckError("Initialize", PortAudio.Pa_Initialize())) + return false; + } + _RefCount++; + } + return true; + } + + /// + /// Safe method to close PortAudio + /// MUST be called _exactly_ once after successfull InitDriver call + /// + protected static void _CloseDriver() + { + lock (_Mutex) + { + Debug.Assert(_RefCount > 0); + _RefCount--; + if (_RefCount == 0) + PortAudio.Pa_Terminate(); + } + } + + protected static PortAudio.PaError _OpenStream(out IntPtr stream, ref PortAudio.PaStreamParameters? inputParameters, ref PortAudio.PaStreamParameters? outputParameters, + double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags, + PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData) + { + lock (_Mutex) + { + return PortAudio.Pa_OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData); + } + } + + /// + /// Convenience method to safely open an input stream and log potential error + /// + /// + /// + /// + /// + /// + /// + /// + /// True on success + protected static bool _OpenInputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? inputParameters, + double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags, + PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData) + { + PortAudio.PaStreamParameters? outputParameters = null; + return + !_CheckError("OpenInputStream", + _OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData)); + } + + /// + /// Convenience method to safely open an output stream and log potential error + /// + /// + /// + /// + /// + /// + /// + /// + /// True on success + protected static bool _OpenOutputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? outputParameters, + double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags, + PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData) + { + PortAudio.PaStreamParameters? inputParameters = null; + return + !_CheckError("OpenOutputStream", + _OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData)); + } + + protected static void _CloseStream(IntPtr stream) + { + lock (_Mutex) + { + PortAudio.Pa_CloseStream(stream); + } + } + + /// + /// Checks if PA returned an error and logs it + /// Returns true on error + /// + /// Action identifier (E.g. openStream) + /// Result returned by Pa_* call + /// True on error + protected static bool _CheckError(String action, PortAudio.PaError errorCode) + { + if (errorCode != PortAudio.PaError.paNoError) + { + CLog.LogError(action + " error: " + PortAudio.Pa_GetErrorText(errorCode)); + if (errorCode == PortAudio.PaError.paUnanticipatedHostError) + { + PortAudio.PaHostErrorInfo errorInfo = PortAudio.Pa_GetLastHostErrorInfo(); + CLog.LogError("- Host error API type: " + errorInfo.hostApiType); + CLog.LogError("- Host error code: " + errorInfo.errorCode); + CLog.LogError("- Host error text: " + errorInfo.errorText); + } + return true; + } + + return false; + } + + /// + /// Selects the most appropriate host api + /// + /// The most appropriate host api + protected static int _GetHostApi() + { + //Caller has to hold a reference anyway so no locking needed + if (_RefCount < 1) + return -1; + + int selectedHostApi = PortAudio.Pa_GetDefaultHostApi(); + int apiCount = PortAudio.Pa_GetHostApiCount(); + for (int i = 0; i < apiCount; i++) + { + PortAudio.PaHostApiInfo apiInfo = PortAudio.Pa_GetHostApiInfo(i); + if ((apiInfo.type == PortAudio.PaHostApiTypeId.paDirectSound) + || (apiInfo.type == PortAudio.PaHostApiTypeId.paALSA)) + selectedHostApi = i; + } + return selectedHostApi; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Playback/IPlayback.cs b/Vocaluxe/Lib/Sound/Playback/IPlayback.cs index c69eed4e..13ce9857 100644 --- a/Vocaluxe/Lib/Sound/Playback/IPlayback.cs +++ b/Vocaluxe/Lib/Sound/Playback/IPlayback.cs @@ -35,7 +35,7 @@ namespace Vocaluxe.Lib.Sound.Playback } } - delegate void Closeproc(int streamID); + delegate void Closeproc(int stream); interface IPlayback { diff --git a/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioPlay.cs b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioPlay.cs index 38742b9f..86f62f4d 100644 --- a/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioPlay.cs +++ b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioPlay.cs @@ -15,42 +15,35 @@ // along with Vocaluxe. If not, see . #endregion -using System; using System.Collections.Generic; -using System.Linq; namespace Vocaluxe.Lib.Sound.Playback.PortAudio { class CPortAudioPlay : IPlayback { private bool _Initialized; - private readonly List _Decoder = new List(); - private Closeproc _Closeproc; - private int _Count = 1; - - private readonly Object _MutexDecoder = new Object(); - - private List _Streams; + private readonly List _Streams = new List(); + private int _NextID; public bool Init() { if (_Initialized) CloseAll(); - _Closeproc = _CloseProc; _Initialized = true; - _Streams = new List(); return true; } public void CloseAll() { - lock (_MutexDecoder) + lock (_Streams) { - for (int i = 0; i < _Decoder.Count; i++) - _Decoder[i].Free(_Closeproc, i + 1); + foreach (CPortAudioStream stream in _Streams) + stream.Free(); + _Streams.Clear(); } + _Initialized = false; } public void SetGlobalVolume(float volume) @@ -66,7 +59,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio if (!_Initialized) return 0; - lock (_MutexDecoder) + lock (_Streams) { return _Streams.Count; } @@ -82,17 +75,16 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio public int Load(string media, bool prescan) { - var stream = new SAudioStreams(0); - var decoder = new CPortAudioStream(); + //Note: We ignore prescan here as the stream does it always - if (decoder.Open(media) > -1) + var stream = new CPortAudioStream(_NextID++, _CloseProc); + + if (stream.Open(media)) { - lock (_MutexDecoder) + lock (_Streams) { - _Decoder.Add(decoder); - stream.Handle = _Count++; _Streams.Add(stream); - return stream.Handle; + return stream.ID; } } return 0; @@ -100,12 +92,15 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio public void Close(int stream) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) + int index = _GetStreamIndex(stream); + if (index >= 0) { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].Free(_Closeproc, stream); + _Streams[index].Free(); + _Streams.RemoveAt(index); } } } @@ -117,216 +112,219 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio public void Play(int stream, bool loop) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) + int index = _GetStreamIndex(stream); + if (index >= 0) { - if (_AlreadyAdded(stream)) - { - _Decoder[_GetStreamIndex(stream)].Loop = loop; - _Decoder[_GetStreamIndex(stream)].Play(); - } + _Streams[index].Loop = loop; + _Streams[index].Play(); } } } public void Pause(int stream) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].Paused = true; - } + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Paused = true; } } public void Stop(int stream) { - Pause(stream); + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Stop(); + } } public void Fade(int stream, float targetVolume, float seconds) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].Fade(targetVolume, seconds); - } + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Fade(targetVolume, seconds); } } public void FadeAndPause(int stream, float targetVolume, float seconds) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].FadeAndPause(targetVolume, seconds); - } + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].FadeAndPause(targetVolume, seconds); } } public void FadeAndClose(int stream, float targetVolume, float seconds) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].FadeAndClose(targetVolume, seconds, _Closeproc, stream); - } + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].FadeAndClose(targetVolume, seconds); } } public void FadeAndStop(int stream, float targetVolume, float seconds) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].FadeAndStop(targetVolume, seconds, _Closeproc, stream); - } + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].FadeAndStop(targetVolume, seconds); } } public void SetStreamVolume(int stream, float volume) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].Volume = volume; - } + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Volume = volume; } } public void SetStreamVolumeMax(int stream, float volume) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].VolumeMax = volume; - } + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].VolumeMax = volume; } } public float GetLength(int stream) { - if (_Initialized) + if (!_Initialized) + return 0f; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - return _Decoder[_GetStreamIndex(stream)].Length; - } + if (_AlreadyAdded(stream)) + return _Streams[_GetStreamIndex(stream)].Length; } return 0f; } public float GetPosition(int stream) { - if (_Initialized) - { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - return _Decoder[_GetStreamIndex(stream)].Position; - } - + if (!_Initialized) return 0f; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + return _Streams[_GetStreamIndex(stream)].Position; } + return 0f; } public bool IsPlaying(int stream) { - if (_Initialized) + if (!_Initialized) + return false; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - return !_Decoder[_GetStreamIndex(stream)].Paused && !_Decoder[_GetStreamIndex(stream)].Finished; - } + if (_AlreadyAdded(stream)) + return !_Streams[_GetStreamIndex(stream)].Paused && !_Streams[_GetStreamIndex(stream)].Finished; } return false; } public bool IsPaused(int stream) { - if (_Initialized) + if (!_Initialized) + return false; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - return _Decoder[_GetStreamIndex(stream)].Paused; - } + if (_AlreadyAdded(stream)) + return _Streams[_GetStreamIndex(stream)].Paused; } return false; } public bool IsFinished(int stream) { - if (_Initialized) + if (!_Initialized) + return true; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - return _Decoder[_GetStreamIndex(stream)].Finished; - } + if (_AlreadyAdded(stream)) + return _Streams[_GetStreamIndex(stream)].Finished; } return true; } public void SetPosition(int stream, float position) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(stream)) - _Decoder[_GetStreamIndex(stream)].Skip(position); - } + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Skip(position); } } #endregion Stream Handling + /// + /// Returns true if strem with given id is found + /// MUST hold _Stream-lock + /// + /// Stream id + /// private bool _AlreadyAdded(int stream) { - return _Streams.Any(st => st.Handle == stream); + return _GetStreamIndex(stream) >= 0; } + /// + /// Returns the index of the stream with the given id + /// MUST hold _Stream-lock + /// + /// Stream id + /// private int _GetStreamIndex(int stream) { for (int i = 0; i < _Streams.Count; i++) { - if (_Streams[i].Handle == stream) + if (_Streams[i].ID == stream) return i; } return -1; } - private void _CloseProc(int streamID) + /// + /// Removes the stream with the given handle + /// + /// Stream handle + private void _CloseProc(int stream) { - if (_Initialized) + if (!_Initialized) + return; + lock (_Streams) { - lock (_MutexDecoder) - { - if (_AlreadyAdded(streamID)) - { - int index = _GetStreamIndex(streamID); - _Decoder.RemoveAt(index); - _Streams.RemoveAt(index); - } - } + if (_AlreadyAdded(stream)) + _Streams.RemoveAt(_GetStreamIndex(stream)); } } } diff --git a/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs index d4cb57c5..109dbd88 100644 --- a/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs +++ b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs @@ -8,15 +8,15 @@ using VocaluxeLib; namespace Vocaluxe.Lib.Sound.Playback.PortAudio { - class CPortAudioStream : IDisposable + class CPortAudioStream : CPortAudioCommon, IDisposable { private const long _Bufsize = 1000000L; private const long _Beginrefill = 800000L; + public readonly int ID; + private readonly Closeproc _Closeproc; + private readonly CSyncTimer _SyncTimer; - private static bool _Initialized; - private static int _NumStreams; - private static readonly Object _Mutex = new object(); private int _ByteCount = 4; private float _Volume = 1f; private float _VolumeMax = 1f; @@ -26,11 +26,9 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio private static PortAudioSharp.PortAudio.PaHostApiInfo _ApiInfo; private static PortAudioSharp.PortAudio.PaDeviceInfo _OutputDeviceInfo; - private IntPtr _Ptr = new IntPtr(0); + private IntPtr _Stream = IntPtr.Zero; - private Closeproc _Closeproc; private PortAudioSharp.PortAudio.PaStreamCallbackDelegate _PaStreamCallback; - private int _StreamID; private IAudioDecoder _Decoder; private float _BytesPerSecond; private bool _NoMoreData; @@ -61,8 +59,10 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio private readonly Object _LockData = new Object(); private readonly Object _LockSyncSignals = new Object(); - public CPortAudioStream() + public CPortAudioStream(int id, Closeproc closeproc) { + ID = id; + _Closeproc = closeproc; _SyncTimer = new CSyncTimer(0f, 1f, 0.02f); } @@ -71,10 +71,8 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio Dispose(); } - public void Free(Closeproc closeProc, int streamID) + public void Free() { - _Closeproc = closeProc; - _StreamID = streamID; _Terminated = true; } @@ -135,6 +133,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio if (Finished) _SyncTimer.Pause(); //TODO: Why not use Decoder.GetPosition()? + //Decoder may return wrong timestamps. If you change this to exessive testing for monoton timestamps espacially for ogg files return _SyncTimer.Time; } } @@ -172,18 +171,14 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio _AfterFadeAction = EStreamAction.Pause; } - public void FadeAndClose(float targetVolume, float fadeTime, Closeproc closeProc, int streamID) + public void FadeAndClose(float targetVolume, float fadeTime) { - _Closeproc = closeProc; - _StreamID = streamID; Fade(targetVolume, fadeTime); _AfterFadeAction = EStreamAction.Close; } - public void FadeAndStop(float targetVolume, float fadeTime, Closeproc closeProc, int streamID) + public void FadeAndStop(float targetVolume, float fadeTime) { - _Closeproc = closeProc; - _StreamID = streamID; Fade(targetVolume, fadeTime); _AfterFadeAction = EStreamAction.Stop; } @@ -192,18 +187,12 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio { Paused = false; _AfterFadeAction = EStreamAction.Nothing; - lock (_Mutex) - { - _ErrorCheck("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Ptr)); - } + _CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream)); } public void Stop() { - lock (_Mutex) - { - _ErrorCheck("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Ptr)); - } + _CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream)); Skip(0f); } @@ -213,60 +202,57 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio set { _SetLoop = value; } } - public int Open(string fileName) + public bool Open(string fileName) { if (_FileOpened) - return -1; + return false; if (!File.Exists(fileName)) - return -1; - - _Decoder = new CAudioDecoderFFmpeg(); - _Decoder.Init(); + return false; + bool driverInitialized = false; try { - lock (_Mutex) - { - if (!_Initialized) - { - if (_ErrorCheck("Initialize", PortAudioSharp.PortAudio.Pa_Initialize())) - return -1; - _Initialized = true; + if (!_InitDriver()) + return false; + driverInitialized = true; - int hostApi = _ApiSelect(); - _ApiInfo = PortAudioSharp.PortAudio.Pa_GetHostApiInfo(hostApi); - _OutputDeviceInfo = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_ApiInfo.defaultOutputDevice); - if (_OutputDeviceInfo.defaultLowOutputLatency < 0.1) - _OutputDeviceInfo.defaultLowOutputLatency = 0.1; - } - } + int hostApi = _GetHostApi(); + _ApiInfo = PortAudioSharp.PortAudio.Pa_GetHostApiInfo(hostApi); + _OutputDeviceInfo = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_ApiInfo.defaultOutputDevice); + if (_OutputDeviceInfo.defaultLowOutputLatency < 0.1) + _OutputDeviceInfo.defaultLowOutputLatency = 0.1; _PaStreamCallback = _ProcessNewData; } catch (Exception) { - _Initialized = false; + if (driverInitialized) + _CloseDriver(); CLog.LogError("Error Init PortAudio Playback"); - return -1; + return false; } + _Decoder = new CAudioDecoderFFmpeg(); + _Decoder.Init(); _Decoder.Open(fileName); - _Duration = _Decoder.GetLength(); SFormatInfo format = _Decoder.GetFormatInfo(); if (format.SamplesPerSecond == 0) - return -1; + { + _CloseDriver(); + _Decoder.Close(); + _Decoder = null; + CLog.LogError("Error Init PortAudio Playback (samples=0)"); + return false; + } + _Duration = _Decoder.GetLength(); _ByteCount = 2 * format.ChannelCount; _BytesPerSecond = format.SamplesPerSecond * _ByteCount; _CurrentTime = 0f; _SyncTimer.Time = _CurrentTime; - var stream = new SAudioStreams(0); - - var data = new IntPtr(0); - PortAudioSharp.PortAudio.PaStreamParameters? outputParams = new PortAudioSharp.PortAudio.PaStreamParameters { channelCount = format.ChannelCount, @@ -274,38 +260,33 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio sampleFormat = PortAudioSharp.PortAudio.PaSampleFormat.paInt16, suggestedLatency = _OutputDeviceInfo.defaultLowOutputLatency }; - PortAudioSharp.PortAudio.PaStreamParameters? inputParams = null; - var bufsize = (uint)CConfig.AudioBufferSize; - lock (_Mutex) + if (!_OpenOutputStream( + out _Stream, + ref outputParams, + format.SamplesPerSecond, + (uint)CConfig.AudioBufferSize, + PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag, + _PaStreamCallback, + IntPtr.Zero) || _Stream == IntPtr.Zero) { - _ErrorCheck("OpenDefaultStream (playback)", PortAudioSharp.PortAudio.Pa_OpenStream( - out _Ptr, - ref inputParams, - ref outputParams, - format.SamplesPerSecond, - bufsize, - PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag, - _PaStreamCallback, - data)); + _CloseDriver(); + _Decoder.Close(); + _Decoder = null; + return false; } - stream.Handle = _Ptr.ToInt32(); + //From now on closing the driver and the decoder is handled by the thread ONLY! - if (stream.Handle != 0) - { - _NumStreams++; - _Paused = true; - _Waiting = true; - _FileOpened = true; - _Data = new CRingBuffer(_Bufsize); - _NoMoreData = false; - _DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(fileName)}; - _DecoderThread.Start(); + _Paused = true; + _Waiting = true; + _FileOpened = true; + _Data = new CRingBuffer(_Bufsize); + _NoMoreData = false; + _DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(fileName)}; + _DecoderThread.Start(); - return stream.Handle; - } - return -1; + return true; } public void Skip(float time) @@ -374,9 +355,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio private void _DoDecode() { - if (!_FileOpened) - return; - if (_Paused) return; @@ -431,22 +409,11 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio private void _DoFree() { - if (_Initialized) - { - Stop(); - _NumStreams--; - if (_NumStreams == 0) - { - lock (_Mutex) - { - PortAudioSharp.PortAudio.Pa_Terminate(); - _Initialized = false; - } - } - _Decoder.Close(); - } + _CloseStream(_Stream); + _CloseDriver(); + _Decoder.Close(); - _Closeproc(_StreamID); + _Closeproc(ID); } #endregion Threading @@ -517,44 +484,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio } #endregion Callbacks - private bool _ErrorCheck(String action, PortAudioSharp.PortAudio.PaError errorCode) - { - if (errorCode != PortAudioSharp.PortAudio.PaError.paNoError) - { - if (errorCode == PortAudioSharp.PortAudio.PaError.paStreamIsNotStopped) - return false; - - CLog.LogError(action + " error (playback): " + PortAudioSharp.PortAudio.Pa_GetErrorText(errorCode)); - if (errorCode == PortAudioSharp.PortAudio.PaError.paUnanticipatedHostError) - { - PortAudioSharp.PortAudio.PaHostErrorInfo errorInfo = PortAudioSharp.PortAudio.Pa_GetLastHostErrorInfo(); - CLog.LogError("- Host error API type: " + errorInfo.hostApiType); - CLog.LogError("- Host error code: " + errorInfo.errorCode); - CLog.LogError("- Host error text: " + errorInfo.errorText); - } - return true; - } - - return false; - } - - private int _ApiSelect() - { - if (!_Initialized) - return 0; - - int selectedHostApi = PortAudioSharp.PortAudio.Pa_GetDefaultHostApi(); - int apiCount = PortAudioSharp.PortAudio.Pa_GetHostApiCount(); - for (int i = 0; i < apiCount; i++) - { - PortAudioSharp.PortAudio.PaHostApiInfo apiInfo = PortAudioSharp.PortAudio.Pa_GetHostApiInfo(i); - if ((apiInfo.type == PortAudioSharp.PortAudio.PaHostApiTypeId.paDirectSound) - || (apiInfo.type == PortAudioSharp.PortAudio.PaHostApiTypeId.paALSA)) - selectedHostApi = i; - } - return selectedHostApi; - } - private void _Update() { if (_Fading != null) diff --git a/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs b/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs index 86b5f3ee..a059d463 100644 --- a/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs +++ b/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs @@ -24,7 +24,7 @@ using Vocaluxe.Base; namespace Vocaluxe.Lib.Sound.Record.PortAudio { - class CPortAudioRecord : IRecord + class CPortAudioRecord : CPortAudioCommon, IRecord { private bool _Initialized; private readonly List _Devices = new List(); @@ -47,16 +47,16 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio /// true if success public bool Init() { + if (_Initialized) + return false; + try { - if (_Initialized) - Close(); - - if (_ErrorCheck("Initialize", PortAudioSharp.PortAudio.Pa_Initialize())) + if (!_InitDriver()) return false; _Devices.Clear(); - int hostAPI = _ApiSelect(); + int hostAPI = _GetHostApi(); int numDevices = PortAudioSharp.PortAudio.Pa_GetDeviceCount(); for (int i = 0; i < numDevices; i++) { @@ -100,9 +100,6 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio Stop(); - foreach (CBuffer buffer in _Buffer) - buffer.Reset(); - foreach (IntPtr handle in _RecHandle) { int waitcount = 0; @@ -113,6 +110,9 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio } } + foreach (CBuffer buffer in _Buffer) + buffer.Reset(); + for (int i = 0; i < _RecHandle.Length; i++) _RecHandle[i] = IntPtr.Zero; @@ -127,19 +127,17 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio sampleFormat = PortAudioSharp.PortAudio.PaSampleFormat.paInt16, suggestedLatency = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_Devices[dev].ID).defaultLowInputLatency }; - PortAudioSharp.PortAudio.PaStreamParameters? outputParams = null; - if (_ErrorCheck("OpenStream (rec)", PortAudioSharp.PortAudio.Pa_OpenStream( + if (!_OpenInputStream( out _RecHandle[dev], ref inputParams, - ref outputParams, 44100, 882, PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag, _MyRecProc, - new IntPtr(dev)))) + new IntPtr(dev))) return false; - if (_ErrorCheck("Start Stream (rec)", PortAudioSharp.PortAudio.Pa_StartStream(_RecHandle[dev]))) + if (_CheckError("Start Stream (rec)", PortAudioSharp.PortAudio.Pa_StartStream(_RecHandle[dev]))) return false; } } @@ -165,13 +163,14 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio /// public void Close() { - Stop(); + if (!_Initialized) + return; - if (_Initialized) - { - PortAudioSharp.PortAudio.Pa_Terminate(); - _Initialized = false; - } + foreach (IntPtr handle in _RecHandle) + _CloseStream(handle); + _CloseDriver(); + + _Initialized = false; //System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer); } @@ -312,40 +311,5 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio return PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue; } - - private bool _ErrorCheck(String action, PortAudioSharp.PortAudio.PaError errorCode) - { - if (errorCode != PortAudioSharp.PortAudio.PaError.paNoError) - { - CLog.LogError(action + " error (rec): " + PortAudioSharp.PortAudio.Pa_GetErrorText(errorCode)); - if (errorCode == PortAudioSharp.PortAudio.PaError.paUnanticipatedHostError) - { - PortAudioSharp.PortAudio.PaHostErrorInfo errorInfo = PortAudioSharp.PortAudio.Pa_GetLastHostErrorInfo(); - CLog.LogError("- Host error API type: " + errorInfo.hostApiType); - CLog.LogError("- Host error code: " + errorInfo.errorCode); - CLog.LogError("- Host error text: " + errorInfo.errorText); - } - return true; - } - - return false; - } - - private int _ApiSelect() - { - if (!_Initialized) - return 0; - - int selectedHostApi = PortAudioSharp.PortAudio.Pa_GetDefaultHostApi(); - int apiCount = PortAudioSharp.PortAudio.Pa_GetHostApiCount(); - for (int i = 0; i < apiCount; i++) - { - PortAudioSharp.PortAudio.PaHostApiInfo apiInfo = PortAudioSharp.PortAudio.Pa_GetHostApiInfo(i); - if ((apiInfo.type == PortAudioSharp.PortAudio.PaHostApiTypeId.paDirectSound) - || (apiInfo.type == PortAudioSharp.PortAudio.PaHostApiTypeId.paALSA)) - selectedHostApi = i; - } - return selectedHostApi; - } } } \ No newline at end of file diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 111f9a7e..047fbfd9 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -221,6 +221,7 @@ +