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 interface IPlayback
{ {
@@ -15,42 +15,35 @@
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>. // along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#endregion #endregion
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Vocaluxe.Lib.Sound.Playback.PortAudio namespace Vocaluxe.Lib.Sound.Playback.PortAudio
{ {
class CPortAudioPlay : IPlayback class CPortAudioPlay : IPlayback
{ {
private bool _Initialized; private bool _Initialized;
private readonly List<CPortAudioStream> _Decoder = new List<CPortAudioStream>(); private readonly List<CPortAudioStream> _Streams = new List<CPortAudioStream>();
private Closeproc _Closeproc; private int _NextID;
private int _Count = 1;
private readonly Object _MutexDecoder = new Object();
private List<SAudioStreams> _Streams;
public bool Init() public bool Init()
{ {
if (_Initialized) if (_Initialized)
CloseAll(); CloseAll();
_Closeproc = _CloseProc;
_Initialized = true; _Initialized = true;
_Streams = new List<SAudioStreams>();
return true; return true;
} }
public void CloseAll() public void CloseAll()
{ {
lock (_MutexDecoder) lock (_Streams)
{ {
for (int i = 0; i < _Decoder.Count; i++) foreach (CPortAudioStream stream in _Streams)
_Decoder[i].Free(_Closeproc, i + 1); stream.Free();
_Streams.Clear();
} }
_Initialized = false;
} }
public void SetGlobalVolume(float volume) public void SetGlobalVolume(float volume)
@@ -66,7 +59,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
if (!_Initialized) if (!_Initialized)
return 0; return 0;
lock (_MutexDecoder) lock (_Streams)
{ {
return _Streams.Count; return _Streams.Count;
} }
@@ -82,17 +75,16 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
public int Load(string media, bool prescan) public int Load(string media, bool prescan)
{ {
var stream = new SAudioStreams(0); //Note: We ignore prescan here as the stream does it always
var decoder = new CPortAudioStream();
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); _Streams.Add(stream);
return stream.Handle; return stream.ID;
} }
} }
return 0; return 0;
@@ -100,12 +92,15 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
public void Close(int stream) public void Close(int stream)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) int index = _GetStreamIndex(stream);
if (index >= 0)
{ {
if (_AlreadyAdded(stream)) _Streams[index].Free();
_Decoder[_GetStreamIndex(stream)].Free(_Closeproc, stream); _Streams.RemoveAt(index);
} }
} }
} }
@@ -117,216 +112,219 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
public void Play(int stream, bool loop) 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)) _Streams[index].Loop = loop;
{ _Streams[index].Play();
_Decoder[_GetStreamIndex(stream)].Loop = loop;
_Decoder[_GetStreamIndex(stream)].Play();
}
} }
} }
} }
public void Pause(int stream) public void Pause(int stream)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ _Streams[_GetStreamIndex(stream)].Paused = true;
if (_AlreadyAdded(stream))
_Decoder[_GetStreamIndex(stream)].Paused = true;
}
} }
} }
public void Stop(int stream) 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) public void Fade(int stream, float targetVolume, float seconds)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ _Streams[_GetStreamIndex(stream)].Fade(targetVolume, seconds);
if (_AlreadyAdded(stream))
_Decoder[_GetStreamIndex(stream)].Fade(targetVolume, seconds);
}
} }
} }
public void FadeAndPause(int stream, float targetVolume, float seconds) public void FadeAndPause(int stream, float targetVolume, float seconds)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ _Streams[_GetStreamIndex(stream)].FadeAndPause(targetVolume, seconds);
if (_AlreadyAdded(stream))
_Decoder[_GetStreamIndex(stream)].FadeAndPause(targetVolume, seconds);
}
} }
} }
public void FadeAndClose(int stream, float targetVolume, float seconds) public void FadeAndClose(int stream, float targetVolume, float seconds)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ _Streams[_GetStreamIndex(stream)].FadeAndClose(targetVolume, seconds);
if (_AlreadyAdded(stream))
_Decoder[_GetStreamIndex(stream)].FadeAndClose(targetVolume, seconds, _Closeproc, stream);
}
} }
} }
public void FadeAndStop(int stream, float targetVolume, float seconds) public void FadeAndStop(int stream, float targetVolume, float seconds)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ _Streams[_GetStreamIndex(stream)].FadeAndStop(targetVolume, seconds);
if (_AlreadyAdded(stream))
_Decoder[_GetStreamIndex(stream)].FadeAndStop(targetVolume, seconds, _Closeproc, stream);
}
} }
} }
public void SetStreamVolume(int stream, float volume) public void SetStreamVolume(int stream, float volume)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ _Streams[_GetStreamIndex(stream)].Volume = volume;
if (_AlreadyAdded(stream))
_Decoder[_GetStreamIndex(stream)].Volume = volume;
}
} }
} }
public void SetStreamVolumeMax(int stream, float volume) public void SetStreamVolumeMax(int stream, float volume)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ _Streams[_GetStreamIndex(stream)].VolumeMax = volume;
if (_AlreadyAdded(stream))
_Decoder[_GetStreamIndex(stream)].VolumeMax = volume;
}
} }
} }
public float GetLength(int stream) public float GetLength(int stream)
{ {
if (_Initialized) if (!_Initialized)
return 0f;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ return _Streams[_GetStreamIndex(stream)].Length;
if (_AlreadyAdded(stream))
return _Decoder[_GetStreamIndex(stream)].Length;
}
} }
return 0f; return 0f;
} }
public float GetPosition(int stream) public float GetPosition(int stream)
{ {
if (_Initialized) if (!_Initialized)
{
lock (_MutexDecoder)
{
if (_AlreadyAdded(stream))
return _Decoder[_GetStreamIndex(stream)].Position;
}
return 0f; return 0f;
lock (_Streams)
{
if (_AlreadyAdded(stream))
return _Streams[_GetStreamIndex(stream)].Position;
} }
return 0f; return 0f;
} }
public bool IsPlaying(int stream) public bool IsPlaying(int stream)
{ {
if (_Initialized) if (!_Initialized)
return false;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ return !_Streams[_GetStreamIndex(stream)].Paused && !_Streams[_GetStreamIndex(stream)].Finished;
if (_AlreadyAdded(stream))
return !_Decoder[_GetStreamIndex(stream)].Paused && !_Decoder[_GetStreamIndex(stream)].Finished;
}
} }
return false; return false;
} }
public bool IsPaused(int stream) public bool IsPaused(int stream)
{ {
if (_Initialized) if (!_Initialized)
return false;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ return _Streams[_GetStreamIndex(stream)].Paused;
if (_AlreadyAdded(stream))
return _Decoder[_GetStreamIndex(stream)].Paused;
}
} }
return false; return false;
} }
public bool IsFinished(int stream) public bool IsFinished(int stream)
{ {
if (_Initialized) if (!_Initialized)
return true;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ return _Streams[_GetStreamIndex(stream)].Finished;
if (_AlreadyAdded(stream))
return _Decoder[_GetStreamIndex(stream)].Finished;
}
} }
return true; return true;
} }
public void SetPosition(int stream, float position) public void SetPosition(int stream, float position)
{ {
if (_Initialized) if (!_Initialized)
return;
lock (_Streams)
{ {
lock (_MutexDecoder) if (_AlreadyAdded(stream))
{ _Streams[_GetStreamIndex(stream)].Skip(position);
if (_AlreadyAdded(stream))
_Decoder[_GetStreamIndex(stream)].Skip(position);
}
} }
} }
#endregion Stream Handling #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) 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) private int _GetStreamIndex(int stream)
{ {
for (int i = 0; i < _Streams.Count; i++) for (int i = 0; i < _Streams.Count; i++)
{ {
if (_Streams[i].Handle == stream) if (_Streams[i].ID == stream)
return i; return i;
} }
return -1; 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(stream))
{ _Streams.RemoveAt(_GetStreamIndex(stream));
if (_AlreadyAdded(streamID))
{
int index = _GetStreamIndex(streamID);
_Decoder.RemoveAt(index);
_Streams.RemoveAt(index);
}
}
} }
} }
} }
@@ -8,15 +8,15 @@ using VocaluxeLib;
namespace Vocaluxe.Lib.Sound.Playback.PortAudio namespace Vocaluxe.Lib.Sound.Playback.PortAudio
{ {
class CPortAudioStream : IDisposable class CPortAudioStream : CPortAudioCommon, IDisposable
{ {
private const long _Bufsize = 1000000L; private const long _Bufsize = 1000000L;
private const long _Beginrefill = 800000L; private const long _Beginrefill = 800000L;
public readonly int ID;
private readonly Closeproc _Closeproc;
private readonly CSyncTimer _SyncTimer; private readonly CSyncTimer _SyncTimer;
private static bool _Initialized;
private static int _NumStreams;
private static readonly Object _Mutex = new object();
private int _ByteCount = 4; private int _ByteCount = 4;
private float _Volume = 1f; private float _Volume = 1f;
private float _VolumeMax = 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.PaHostApiInfo _ApiInfo;
private static PortAudioSharp.PortAudio.PaDeviceInfo _OutputDeviceInfo; 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 PortAudioSharp.PortAudio.PaStreamCallbackDelegate _PaStreamCallback;
private int _StreamID;
private IAudioDecoder _Decoder; private IAudioDecoder _Decoder;
private float _BytesPerSecond; private float _BytesPerSecond;
private bool _NoMoreData; private bool _NoMoreData;
@@ -61,8 +59,10 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
private readonly Object _LockData = new Object(); private readonly Object _LockData = new Object();
private readonly Object _LockSyncSignals = 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); _SyncTimer = new CSyncTimer(0f, 1f, 0.02f);
} }
@@ -71,10 +71,8 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
Dispose(); Dispose();
} }
public void Free(Closeproc closeProc, int streamID) public void Free()
{ {
_Closeproc = closeProc;
_StreamID = streamID;
_Terminated = true; _Terminated = true;
} }
@@ -135,6 +133,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
if (Finished) if (Finished)
_SyncTimer.Pause(); _SyncTimer.Pause();
//TODO: Why not use Decoder.GetPosition()? //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; return _SyncTimer.Time;
} }
} }
@@ -172,18 +171,14 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
_AfterFadeAction = EStreamAction.Pause; _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); Fade(targetVolume, fadeTime);
_AfterFadeAction = EStreamAction.Close; _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); Fade(targetVolume, fadeTime);
_AfterFadeAction = EStreamAction.Stop; _AfterFadeAction = EStreamAction.Stop;
} }
@@ -192,18 +187,12 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
{ {
Paused = false; Paused = false;
_AfterFadeAction = EStreamAction.Nothing; _AfterFadeAction = EStreamAction.Nothing;
lock (_Mutex) _CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream));
{
_ErrorCheck("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Ptr));
}
} }
public void Stop() public void Stop()
{ {
lock (_Mutex) _CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream));
{
_ErrorCheck("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Ptr));
}
Skip(0f); Skip(0f);
} }
@@ -213,60 +202,57 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
set { _SetLoop = value; } set { _SetLoop = value; }
} }
public int Open(string fileName) public bool Open(string fileName)
{ {
if (_FileOpened) if (_FileOpened)
return -1; return false;
if (!File.Exists(fileName)) if (!File.Exists(fileName))
return -1; return false;
_Decoder = new CAudioDecoderFFmpeg();
_Decoder.Init();
bool driverInitialized = false;
try try
{ {
lock (_Mutex) if (!_InitDriver())
{ return false;
if (!_Initialized) driverInitialized = true;
{
if (_ErrorCheck("Initialize", PortAudioSharp.PortAudio.Pa_Initialize()))
return -1;
_Initialized = true;
int hostApi = _ApiSelect(); int hostApi = _GetHostApi();
_ApiInfo = PortAudioSharp.PortAudio.Pa_GetHostApiInfo(hostApi); _ApiInfo = PortAudioSharp.PortAudio.Pa_GetHostApiInfo(hostApi);
_OutputDeviceInfo = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_ApiInfo.defaultOutputDevice); _OutputDeviceInfo = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_ApiInfo.defaultOutputDevice);
if (_OutputDeviceInfo.defaultLowOutputLatency < 0.1) if (_OutputDeviceInfo.defaultLowOutputLatency < 0.1)
_OutputDeviceInfo.defaultLowOutputLatency = 0.1; _OutputDeviceInfo.defaultLowOutputLatency = 0.1;
}
}
_PaStreamCallback = _ProcessNewData; _PaStreamCallback = _ProcessNewData;
} }
catch (Exception) catch (Exception)
{ {
_Initialized = false; if (driverInitialized)
_CloseDriver();
CLog.LogError("Error Init PortAudio Playback"); CLog.LogError("Error Init PortAudio Playback");
return -1; return false;
} }
_Decoder = new CAudioDecoderFFmpeg();
_Decoder.Init();
_Decoder.Open(fileName); _Decoder.Open(fileName);
_Duration = _Decoder.GetLength();
SFormatInfo format = _Decoder.GetFormatInfo(); SFormatInfo format = _Decoder.GetFormatInfo();
if (format.SamplesPerSecond == 0) 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; _ByteCount = 2 * format.ChannelCount;
_BytesPerSecond = format.SamplesPerSecond * _ByteCount; _BytesPerSecond = format.SamplesPerSecond * _ByteCount;
_CurrentTime = 0f; _CurrentTime = 0f;
_SyncTimer.Time = _CurrentTime; _SyncTimer.Time = _CurrentTime;
var stream = new SAudioStreams(0);
var data = new IntPtr(0);
PortAudioSharp.PortAudio.PaStreamParameters? outputParams = new PortAudioSharp.PortAudio.PaStreamParameters PortAudioSharp.PortAudio.PaStreamParameters? outputParams = new PortAudioSharp.PortAudio.PaStreamParameters
{ {
channelCount = format.ChannelCount, channelCount = format.ChannelCount,
@@ -274,38 +260,33 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
sampleFormat = PortAudioSharp.PortAudio.PaSampleFormat.paInt16, sampleFormat = PortAudioSharp.PortAudio.PaSampleFormat.paInt16,
suggestedLatency = _OutputDeviceInfo.defaultLowOutputLatency suggestedLatency = _OutputDeviceInfo.defaultLowOutputLatency
}; };
PortAudioSharp.PortAudio.PaStreamParameters? inputParams = null;
var bufsize = (uint)CConfig.AudioBufferSize; if (!_OpenOutputStream(
lock (_Mutex) 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( _CloseDriver();
out _Ptr, _Decoder.Close();
ref inputParams, _Decoder = null;
ref outputParams, return false;
format.SamplesPerSecond,
bufsize,
PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag,
_PaStreamCallback,
data));
} }
stream.Handle = _Ptr.ToInt32(); //From now on closing the driver and the decoder is handled by the thread ONLY!
if (stream.Handle != 0) _Paused = true;
{ _Waiting = true;
_NumStreams++; _FileOpened = true;
_Paused = true; _Data = new CRingBuffer(_Bufsize);
_Waiting = true; _NoMoreData = false;
_FileOpened = true; _DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(fileName)};
_Data = new CRingBuffer(_Bufsize); _DecoderThread.Start();
_NoMoreData = false;
_DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(fileName)};
_DecoderThread.Start();
return stream.Handle; return true;
}
return -1;
} }
public void Skip(float time) public void Skip(float time)
@@ -374,9 +355,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
private void _DoDecode() private void _DoDecode()
{ {
if (!_FileOpened)
return;
if (_Paused) if (_Paused)
return; return;
@@ -431,22 +409,11 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
private void _DoFree() private void _DoFree()
{ {
if (_Initialized) _CloseStream(_Stream);
{ _CloseDriver();
Stop(); _Decoder.Close();
_NumStreams--;
if (_NumStreams == 0)
{
lock (_Mutex)
{
PortAudioSharp.PortAudio.Pa_Terminate();
_Initialized = false;
}
}
_Decoder.Close();
}
_Closeproc(_StreamID); _Closeproc(ID);
} }
#endregion Threading #endregion Threading
@@ -517,44 +484,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
} }
#endregion Callbacks #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() private void _Update()
{ {
if (_Fading != null) if (_Fading != null)
@@ -24,7 +24,7 @@ using Vocaluxe.Base;
namespace Vocaluxe.Lib.Sound.Record.PortAudio namespace Vocaluxe.Lib.Sound.Record.PortAudio
{ {
class CPortAudioRecord : IRecord class CPortAudioRecord : CPortAudioCommon, IRecord
{ {
private bool _Initialized; private bool _Initialized;
private readonly List<CRecordDevice> _Devices = new List<CRecordDevice>(); private readonly List<CRecordDevice> _Devices = new List<CRecordDevice>();
@@ -47,16 +47,16 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
/// <returns>true if success</returns> /// <returns>true if success</returns>
public bool Init() public bool Init()
{ {
if (_Initialized)
return false;
try try
{ {
if (_Initialized) if (!_InitDriver())
Close();
if (_ErrorCheck("Initialize", PortAudioSharp.PortAudio.Pa_Initialize()))
return false; return false;
_Devices.Clear(); _Devices.Clear();
int hostAPI = _ApiSelect(); int hostAPI = _GetHostApi();
int numDevices = PortAudioSharp.PortAudio.Pa_GetDeviceCount(); int numDevices = PortAudioSharp.PortAudio.Pa_GetDeviceCount();
for (int i = 0; i < numDevices; i++) for (int i = 0; i < numDevices; i++)
{ {
@@ -100,9 +100,6 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
Stop(); Stop();
foreach (CBuffer buffer in _Buffer)
buffer.Reset();
foreach (IntPtr handle in _RecHandle) foreach (IntPtr handle in _RecHandle)
{ {
int waitcount = 0; 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++) for (int i = 0; i < _RecHandle.Length; i++)
_RecHandle[i] = IntPtr.Zero; _RecHandle[i] = IntPtr.Zero;
@@ -127,19 +127,17 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
sampleFormat = PortAudioSharp.PortAudio.PaSampleFormat.paInt16, sampleFormat = PortAudioSharp.PortAudio.PaSampleFormat.paInt16,
suggestedLatency = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_Devices[dev].ID).defaultLowInputLatency suggestedLatency = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_Devices[dev].ID).defaultLowInputLatency
}; };
PortAudioSharp.PortAudio.PaStreamParameters? outputParams = null; if (!_OpenInputStream(
if (_ErrorCheck("OpenStream (rec)", PortAudioSharp.PortAudio.Pa_OpenStream(
out _RecHandle[dev], out _RecHandle[dev],
ref inputParams, ref inputParams,
ref outputParams,
44100, 44100,
882, 882,
PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag, PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag,
_MyRecProc, _MyRecProc,
new IntPtr(dev)))) new IntPtr(dev)))
return false; 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; return false;
} }
} }
@@ -165,13 +163,14 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
/// </summary> /// </summary>
public void Close() public void Close()
{ {
Stop(); if (!_Initialized)
return;
if (_Initialized) foreach (IntPtr handle in _RecHandle)
{ _CloseStream(handle);
PortAudioSharp.PortAudio.Pa_Terminate(); _CloseDriver();
_Initialized = false;
} _Initialized = false;
//System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer); //System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer);
} }
@@ -312,40 +311,5 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
return PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue; 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\GstreamerSharp\CGstreamerSharpAudioStream.cs" />
<Compile Include="Lib\Sound\Playback\OpenAL\COpenAlStream.cs" /> <Compile Include="Lib\Sound\Playback\OpenAL\COpenAlStream.cs" />
<Compile Include="Lib\Sound\Playback\PortAudio\CPortAudioStream.cs" /> <Compile Include="Lib\Sound\Playback\PortAudio\CPortAudioStream.cs" />
<Compile Include="Lib\Sound\CPortAudioCommon.cs" />
<Compile Include="Lib\Sound\Record\CBuffer.cs" /> <Compile Include="Lib\Sound\Record\CBuffer.cs" />
<Compile Include="Base\CConfig.cs" /> <Compile Include="Base\CConfig.cs" />
<Compile Include="Base\CCover.cs" /> <Compile Include="Base\CCover.cs" />