Fix PortAudio to allow recording and playback with the same library using a base class the implements appropriate locking
This commit is contained in:
1 parent
e1f8bf4843
commit
f9d29b6141
6 files changed
+371
-318
No files matched your search
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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" />
|
||||||
|
|||||||
Reference in new issue
Block a user