Fix PortAudio to allow recording and playback with the same library using a base class the implements appropriate locking

This commit is contained in:
Flamefire committed 2014-03-18 13:33:12 +01:00
1 parent e1f8bf4843
commit f9d29b6141
6 files changed
+371 -318

No files matched your search

+161
View File
@@ -0,0 +1,161 @@
using System;
using System.Diagnostics;
using PortAudioSharp;
using Vocaluxe.Base;
namespace Vocaluxe.Lib.Sound
{
/// <summary>
/// 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
/// </summary>
abstract class CPortAudioCommon
{
private static int _RefCount;
private static readonly object _Mutex = new object();
/// <summary>
/// Safe method to init PortAudio (adds a reference)
/// MUST call CloseDriver when done
/// </summary>
/// <returns>True on success, falso if not initialized (log written)</returns>
protected static bool _InitDriver()
{
lock (_Mutex)
{
if (_RefCount == 0)
{
if (_CheckError("Initialize", PortAudio.Pa_Initialize()))
return false;
}
_RefCount++;
}
return true;
}
/// <summary>
/// Safe method to close PortAudio
/// MUST be called _exactly_ once after successfull InitDriver call
/// </summary>
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);
}
}
/// <summary>
/// Convenience method to safely open an input stream and log potential error
/// </summary>
/// <param name="stream"></param>
/// <param name="inputParameters"></param>
/// <param name="sampleRate"></param>
/// <param name="framesPerBuffer"></param>
/// <param name="streamFlags"></param>
/// <param name="streamCallback"></param>
/// <param name="userData"></param>
/// <returns>True on success</returns>
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));
}
/// <summary>
/// Convenience method to safely open an output stream and log potential error
/// </summary>
/// <param name="stream"></param>
/// <param name="outputParameters"></param>
/// <param name="sampleRate"></param>
/// <param name="framesPerBuffer"></param>
/// <param name="streamFlags"></param>
/// <param name="streamCallback"></param>
/// <param name="userData"></param>
/// <returns>True on success</returns>
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);
}
}
/// <summary>
/// Checks if PA returned an error and logs it
/// Returns true on error
/// </summary>
/// <param name="action">Action identifier (E.g. openStream)</param>
/// <param name="errorCode">Result returned by Pa_* call</param>
/// <returns>True on error</returns>
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;
}
/// <summary>
/// Selects the most appropriate host api
/// </summary>
/// <returns>The most appropriate host api</returns>
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;
}
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ namespace Vocaluxe.Lib.Sound.Playback
}
}
delegate void Closeproc(int streamID);
delegate void Closeproc(int stream);
interface IPlayback
{
@@ -15,42 +15,35 @@
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#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<CPortAudioStream> _Decoder = new List<CPortAudioStream>();
private Closeproc _Closeproc;
private int _Count = 1;
private readonly Object _MutexDecoder = new Object();
private List<SAudioStreams> _Streams;
private readonly List<CPortAudioStream> _Streams = new List<CPortAudioStream>();
private int _NextID;
public bool Init()
{
if (_Initialized)
CloseAll();
_Closeproc = _CloseProc;
_Initialized = true;
_Streams = new List<SAudioStreams>();
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
/// <summary>
/// Returns true if strem with given id is found
/// MUST hold _Stream-lock
/// </summary>
/// <param name="stream">Stream id</param>
/// <returns></returns>
private bool _AlreadyAdded(int stream)
{
return _Streams.Any(st => st.Handle == stream);
return _GetStreamIndex(stream) >= 0;
}
/// <summary>
/// Returns the index of the stream with the given id
/// MUST hold _Stream-lock
/// </summary>
/// <param name="stream">Stream id</param>
/// <returns></returns>
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)
/// <summary>
/// Removes the stream with the given handle
/// </summary>
/// <param name="stream">Stream handle</param>
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));
}
}
}
@@ -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)
@@ -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<CRecordDevice> _Devices = new List<CRecordDevice>();
@@ -47,16 +47,16 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
/// <returns>true if success</returns>
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
/// </summary>
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;
}
}
}
+1
View File
@@ -221,6 +221,7 @@
<Compile Include="Lib\Sound\Playback\GstreamerSharp\CGstreamerSharpAudioStream.cs" />
<Compile Include="Lib\Sound\Playback\OpenAL\COpenAlStream.cs" />
<Compile Include="Lib\Sound\Playback\PortAudio\CPortAudioStream.cs" />
<Compile Include="Lib\Sound\CPortAudioCommon.cs" />
<Compile Include="Lib\Sound\Record\CBuffer.cs" />
<Compile Include="Base\CConfig.cs" />
<Compile Include="Base\CCover.cs" />