Add a base record class to handle common functions (avoid duplicate code)

This commit is contained in:
Flamefire committed 2014-03-23 12:57:10 +01:00
1 parent 9054f529a0
commit 3bc8fd516f
6 files changed
+277 -310

No files matched your search

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using PortAudioSharp; using PortAudioSharp;
using Vocaluxe.Base; using Vocaluxe.Base;
@@ -8,40 +9,53 @@ namespace Vocaluxe.Lib.Sound
/// <summary> /// <summary>
/// PortAudio can be used for record and playback /// PortAudio can be used for record and playback
/// So do some common stuff here and make sure those 2 do not interfere /// So do some common stuff here and make sure those 2 do not interfere
/// Basic lifetime: On Init() get a new handle, close/dispose it in your close/dispose
/// ///
/// DO NEVER use following Pa_* functions other than the ones from this class: /// DO NEVER use following Pa_* functions other than the ones from this class:
/// Initialize, Terminate, OpenStream, CloseStream /// Initialize, Terminate, OpenStream, CloseStream
/// </summary> /// </summary>
static class CPortAudioCommon class CPortAudioHandle : IDisposable
{ {
private static int _RefCount; private static int _RefCount;
private static readonly object _Mutex = new object(); private static readonly object _Mutex = new object();
private bool _Disposed;
private readonly List<IntPtr> _Streams = new List<IntPtr>();
/// <summary> /// <summary>
/// Safe method to init PortAudio (adds a reference) /// Initializes PortAudio library (if required)
/// MUST call CloseDriver when done
/// </summary> /// </summary>
/// <returns>True on success, falso if not initialized (log written)</returns> public CPortAudioHandle()
public static bool InitDriver()
{ {
lock (_Mutex) lock (_Mutex)
{ {
if (_RefCount == 0) if (_RefCount == 0)
{ {
if (CheckError("Initialize", PortAudio.Pa_Initialize())) if (CheckError("Initialize", PortAudio.Pa_Initialize()))
return false; throw new Exception();
} }
_RefCount++; _RefCount++;
} }
return true;
} }
/// <summary> ~CPortAudioHandle()
/// Safe method to close PortAudio
/// MUST be called _exactly_ once after successfull InitDriver call
/// </summary>
public static void CloseDriver()
{ {
_Dispose(false);
}
private void _Dispose(bool disposing)
{
if (_Disposed)
return;
if (!disposing)
CLog.LogDebug("Did not close CPortAudioHandle");
//Make sure we do not leek any streams as we may keep PA open
if (_Streams.Count > 0)
{
CLog.LogDebug("Did not close " + _Streams.Count + "PortAudio-Stream(s)");
while (_Streams.Count > 0)
CloseStream(_Streams[0]);
}
lock (_Mutex) lock (_Mutex)
{ {
Debug.Assert(_RefCount > 0); Debug.Assert(_RefCount > 0);
@@ -49,15 +63,37 @@ namespace Vocaluxe.Lib.Sound
if (_RefCount == 0) if (_RefCount == 0)
PortAudio.Pa_Terminate(); PortAudio.Pa_Terminate();
} }
_Disposed = true;
} }
public static PortAudio.PaError OpenStream(out IntPtr stream, ref PortAudio.PaStreamParameters? inputParameters, ref PortAudio.PaStreamParameters? outputParameters, public void Dispose()
double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags,
PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData)
{ {
_Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Close the PortAudio handle once you are done
/// </summary>
public void Close()
{
Dispose();
}
public 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)
{
if (_Disposed)
throw new ObjectDisposedException("PortAudioHandle already disposed");
lock (_Mutex) lock (_Mutex)
{ {
return PortAudio.Pa_OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData); PortAudio.PaError res = PortAudio.Pa_OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback,
userData);
if (res == PortAudio.PaError.paNoError)
_Streams.Add(stream);
return res;
} }
} }
@@ -72,9 +108,9 @@ namespace Vocaluxe.Lib.Sound
/// <param name="streamCallback"></param> /// <param name="streamCallback"></param>
/// <param name="userData"></param> /// <param name="userData"></param>
/// <returns>True on success</returns> /// <returns>True on success</returns>
public static bool OpenInputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? inputParameters, public bool OpenInputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? inputParameters,
double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags, double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags,
PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData) PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData)
{ {
PortAudio.PaStreamParameters? outputParameters = null; PortAudio.PaStreamParameters? outputParameters = null;
return return
@@ -93,9 +129,9 @@ namespace Vocaluxe.Lib.Sound
/// <param name="streamCallback"></param> /// <param name="streamCallback"></param>
/// <param name="userData"></param> /// <param name="userData"></param>
/// <returns>True on success</returns> /// <returns>True on success</returns>
public static bool OpenOutputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? outputParameters, public bool OpenOutputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? outputParameters,
double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags, double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags,
PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData) PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData)
{ {
PortAudio.PaStreamParameters? inputParameters = null; PortAudio.PaStreamParameters? inputParameters = null;
return return
@@ -103,11 +139,15 @@ namespace Vocaluxe.Lib.Sound
OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData)); OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData));
} }
public static void CloseStream(IntPtr stream) public void CloseStream(IntPtr stream)
{ {
if (_Disposed)
throw new ObjectDisposedException("PortAudioHandle already disposed");
lock (_Mutex) lock (_Mutex)
{ {
PortAudio.Pa_CloseStream(stream); PortAudio.Pa_CloseStream(stream);
_Streams.Remove(stream);
} }
} }
@@ -118,8 +158,11 @@ namespace Vocaluxe.Lib.Sound
/// <param name="action">Action identifier (E.g. openStream)</param> /// <param name="action">Action identifier (E.g. openStream)</param>
/// <param name="errorCode">Result returned by Pa_* call</param> /// <param name="errorCode">Result returned by Pa_* call</param>
/// <returns>True on error</returns> /// <returns>True on error</returns>
public static bool CheckError(String action, PortAudio.PaError errorCode) public bool CheckError(String action, PortAudio.PaError errorCode)
{ {
if (_Disposed)
throw new ObjectDisposedException("PortAudioHandle already disposed");
if (errorCode != PortAudio.PaError.paNoError) if (errorCode != PortAudio.PaError.paNoError)
{ {
CLog.LogError(action + " error: " + PortAudio.Pa_GetErrorText(errorCode)); CLog.LogError(action + " error: " + PortAudio.Pa_GetErrorText(errorCode));
@@ -140,11 +183,10 @@ namespace Vocaluxe.Lib.Sound
/// Selects the most appropriate host api /// Selects the most appropriate host api
/// </summary> /// </summary>
/// <returns>The most appropriate host api</returns> /// <returns>The most appropriate host api</returns>
public static int GetHostApi() public int GetHostApi()
{ {
//Caller has to hold a reference anyway so no locking needed if (_Disposed)
if (_RefCount < 1) throw new ObjectDisposedException("PortAudioHandle already disposed");
return -1;
int selectedHostApi = PortAudio.Pa_GetDefaultHostApi(); int selectedHostApi = PortAudio.Pa_GetDefaultHostApi();
int apiCount = PortAudio.Pa_GetHostApiCount(); int apiCount = PortAudio.Pa_GetHostApiCount();
@@ -15,6 +15,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
public readonly int ID; public readonly int ID;
private readonly Closeproc _Closeproc; private readonly Closeproc _Closeproc;
private CPortAudioHandle _PaHandle;
private readonly CSyncTimer _SyncTimer; private readonly CSyncTimer _SyncTimer;
private int _ByteCount = 4; private int _ByteCount = 4;
@@ -187,12 +188,12 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
{ {
Paused = false; Paused = false;
_AfterFadeAction = EStreamAction.Nothing; _AfterFadeAction = EStreamAction.Nothing;
CPortAudioCommon.CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream)); _PaHandle.CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream));
} }
public void Stop() public void Stop()
{ {
CPortAudioCommon.CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream)); _PaHandle.CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream));
Skip(0f); Skip(0f);
} }
@@ -210,14 +211,11 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
if (!File.Exists(fileName)) if (!File.Exists(fileName))
return false; return false;
bool driverInitialized = false;
try try
{ {
if (!CPortAudioCommon.InitDriver()) _PaHandle = new CPortAudioHandle();
return false;
driverInitialized = true;
int hostApi = CPortAudioCommon.GetHostApi(); int hostApi = _PaHandle.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)
@@ -227,8 +225,11 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
} }
catch (Exception) catch (Exception)
{ {
if (driverInitialized) if (_PaHandle != null)
CPortAudioCommon.CloseDriver(); {
_PaHandle.Close();
_PaHandle = null;
}
CLog.LogError("Error Init PortAudio Playback"); CLog.LogError("Error Init PortAudio Playback");
return false; return false;
} }
@@ -240,7 +241,8 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
SFormatInfo format = _Decoder.GetFormatInfo(); SFormatInfo format = _Decoder.GetFormatInfo();
if (format.SamplesPerSecond == 0) if (format.SamplesPerSecond == 0)
{ {
CPortAudioCommon.CloseDriver(); _PaHandle.Close();
_PaHandle = null;
_Decoder.Close(); _Decoder.Close();
_Decoder = null; _Decoder = null;
CLog.LogError("Error Init PortAudio Playback (samples=0)"); CLog.LogError("Error Init PortAudio Playback (samples=0)");
@@ -262,7 +264,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
hostApiSpecificStreamInfo = IntPtr.Zero hostApiSpecificStreamInfo = IntPtr.Zero
}; };
if (!CPortAudioCommon.OpenOutputStream( if (!_PaHandle.OpenOutputStream(
out _Stream, out _Stream,
ref outputParams, ref outputParams,
format.SamplesPerSecond, format.SamplesPerSecond,
@@ -271,7 +273,8 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
_PaStreamCallback, _PaStreamCallback,
IntPtr.Zero) || _Stream == IntPtr.Zero) IntPtr.Zero) || _Stream == IntPtr.Zero)
{ {
CPortAudioCommon.CloseDriver(); _PaHandle.Close();
_PaHandle = null;
_Decoder.Close(); _Decoder.Close();
_Decoder = null; _Decoder = null;
return false; return false;
@@ -410,9 +413,11 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
private void _DoFree() private void _DoFree()
{ {
CPortAudioCommon.CloseStream(_Stream); _PaHandle.CloseStream(_Stream);
CPortAudioCommon.CloseDriver(); _PaHandle.Close();
_PaHandle = null;
_Decoder.Close(); _Decoder.Close();
_Decoder = null;
_Closeproc(ID); _Closeproc(ID);
} }
+148
View File
@@ -0,0 +1,148 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Vocaluxe.Base;
namespace Vocaluxe.Lib.Sound.Record
{
/// <summary>
/// Base class to implement common functionality for Record classes
/// </summary>
abstract class CRecordBase
{
private bool _Initialized;
protected List<CRecordDevice> _Devices;
protected CBuffer[] _Buffer;
/// <summary>
/// Init recording
/// Child class should overwrite this to list devices
/// </summary>
/// <returns>true if success</returns>
public virtual bool Init()
{
if (_Initialized)
return false;
_Devices = new List<CRecordDevice>();
_Buffer = new CBuffer[CSettings.MaxNumPlayer];
for (int i = 0; i < _Buffer.Length; i++)
_Buffer[i] = new CBuffer();
_Initialized = true;
return true;
}
/// <summary>
/// Stop all voice capturing streams and terminates recording freeing resources
/// </summary>
public virtual void Close()
{
if (!_Initialized)
return;
_Devices = null;
if (_Buffer != null)
{
foreach (CBuffer buffer in _Buffer)
buffer.Dispose();
_Buffer = null;
}
_Initialized = false;
}
/// <summary>
/// Procedure to be called by descendent classes with data that got recorded
/// </summary>
/// <param name="device">Device from which the data was recorded</param>
/// <param name="data">Recorded samples, assume Int16 and interleaved for multi-channels</param>
protected void _HandleData(CRecordDevice device, byte[] data)
{
byte[] leftBuffer;
byte[] rightBuffer;
if (device.Channels == 2)
{
leftBuffer = new byte[data.Length / 2];
rightBuffer = new byte[data.Length / 2];
//[]: Sample, L: Left channel R: Right channel
//[LR][LR][LR][LR][LR][LR]
//The data is interleaved and needs to be demultiplexed
for (int i = 0; i < data.Length / 4; i++)
{
leftBuffer[i * 2] = data[i * 4];
leftBuffer[i * 2 + 1] = data[i * 4 + 1];
rightBuffer[i * 2] = data[i * 4 + 2];
rightBuffer[i * 2 + 1] = data[i * 4 + 3];
}
}
else
leftBuffer = rightBuffer = data;
if (device.PlayerChannel1 > 0)
_Buffer[device.PlayerChannel1 - 1].ProcessNewBuffer(leftBuffer);
if (device.PlayerChannel2 > 0)
_Buffer[device.PlayerChannel2 - 1].ProcessNewBuffer(rightBuffer);
}
/// <summary>
/// Detect Pitch and Volume of the newest voice buffer
/// </summary>
/// <param name="player"></param>
public void AnalyzeBuffer(int player)
{
if (!_Initialized)
return;
_Buffer[player].AnalyzeBuffer();
}
public int GetToneAbs(int player)
{
if (!_Initialized)
return 0;
return _Buffer[player].ToneAbs;
}
public int GetTone(int player)
{
return _Initialized ? _Buffer[player].Tone : 0;
}
public void SetTone(int player, int tone)
{
if (!_Initialized)
return;
_Buffer[player].Tone = tone;
}
public float GetMaxVolume(int player)
{
return _Initialized ? _Buffer[player].MaxVolume : 0f;
}
public bool ToneValid(int player)
{
return _Initialized && _Buffer[player].ToneValid;
}
public int NumHalfTones()
{
return CBuffer.NumHalfTones;
}
public float[] ToneWeigth(int player)
{
return _Initialized ? _Buffer[player].ToneWeigth : null;
}
public ReadOnlyCollection<CRecordDevice> RecordDevices()
{
return (_Initialized && _Devices.Count > 0) ? _Devices.AsReadOnly() : null;
}
}
}
@@ -23,39 +23,28 @@ using Vocaluxe.Base;
namespace Vocaluxe.Lib.Sound.Record.DirectSound namespace Vocaluxe.Lib.Sound.Record.DirectSound
{ {
class CDirectSoundRecord : IRecord class CDirectSoundRecord : CRecordBase, IRecord
{ {
private bool _Initialized; private bool _Initialized;
private List<CRecordDevice> _Devices;
private List<CSoundCardSource> _Sources; private List<CSoundCardSource> _Sources;
private readonly CBuffer[] _Buffer; public override bool Init()
public CDirectSoundRecord()
{ {
_Buffer = new CBuffer[CSettings.MaxNumPlayer]; if (!base.Init())
for (int i = 0; i < _Buffer.Length; i++) return false;
_Buffer[i] = new CBuffer();
Init();
}
public bool Init()
{
DeviceCollection devices = DirectSoundCapture.GetDevices();
_Devices = new List<CRecordDevice>();
_Sources = new List<CSoundCardSource>(); _Sources = new List<CSoundCardSource>();
int id = 0; DeviceCollection devices = DirectSoundCapture.GetDevices();
foreach (DeviceInformation dev in devices) foreach (DeviceInformation dev in devices)
{ {
using (var ds = new DirectSoundCapture(dev.DriverGuid)) using (var ds = new DirectSoundCapture(dev.DriverGuid))
{ {
var device = new CRecordDevice(id, dev.DriverGuid.ToString(), dev.Description, ds.Capabilities.Channels); var device = new CRecordDevice(_Devices.Count, dev.DriverGuid.ToString(), dev.Description, ds.Capabilities.Channels);
_Devices.Add(device); _Devices.Add(device);
id++;
} }
} }
@@ -64,14 +53,11 @@ namespace Vocaluxe.Lib.Sound.Record.DirectSound
return true; return true;
} }
public void Close() public override void Close()
{ {
if (_Initialized) Stop();
{ _Initialized = false;
Stop(); base.Close();
_Initialized = false;
}
//System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer);
} }
public bool Start() public bool Start()
@@ -112,81 +98,6 @@ namespace Vocaluxe.Lib.Sound.Record.DirectSound
return true; return true;
} }
public void AnalyzeBuffer(int player)
{
if (!_Initialized)
return;
_Buffer[player].AnalyzeBuffer();
}
public int GetToneAbs(int player)
{
if (!_Initialized)
return 0;
return _Buffer[player].ToneAbs;
}
public int GetTone(int player)
{
if (!_Initialized)
return 0;
return _Buffer[player].Tone;
}
public void SetTone(int player, int tone)
{
if (!_Initialized)
return;
_Buffer[player].Tone = tone;
}
public float GetMaxVolume(int player)
{
if (!_Initialized)
return 0f;
return _Buffer[player].MaxVolume;
}
public bool ToneValid(int player)
{
if (!_Initialized)
return false;
return _Buffer[player].ToneValid;
}
public ReadOnlyCollection<CRecordDevice> RecordDevices()
{
if (!_Initialized)
return null;
if (_Devices.Count == 0)
return null;
return _Devices.AsReadOnly();
}
public int NumHalfTones()
{
if (!_Initialized)
return 0;
return CBuffer.NumHalfTones;
}
public float[] ToneWeigth(int player)
{
if (!_Initialized)
return null;
return _Buffer[player].ToneWeigth;
}
private void _OnDataReady(object sender, CSampleDataEventArgs e) private void _OnDataReady(object sender, CSampleDataEventArgs e)
{ {
if (!_Initialized) if (!_Initialized)
@@ -194,32 +105,7 @@ namespace Vocaluxe.Lib.Sound.Record.DirectSound
CRecordDevice dev = _Devices.FirstOrDefault(device => device.Driver == e.Guid); CRecordDevice dev = _Devices.FirstOrDefault(device => device.Driver == e.Guid);
if (dev == null) if (dev == null)
return; return;
byte[] leftBuffer; _HandleData(dev, e.Data);
byte[] rightBuffer;
if (dev.Channels == 2)
{
leftBuffer = new byte[e.Data.Length / 2];
rightBuffer = new byte[e.Data.Length / 2];
//[]: Sample, L: Left channel R: Right channel
//[LR][LR][LR][LR][LR][LR]
//The data is interleaved and needs to be demultiplexed
for (int i = 0; i < e.Data.Length / 4; i++)
{
leftBuffer[i * 2] = e.Data[i * 4];
leftBuffer[i * 2 + 1] = e.Data[i * 4 + 1];
rightBuffer[i * 2] = e.Data[i * 4 + 2];
rightBuffer[i * 2 + 1] = e.Data[i * 4 + 3];
}
}
else
leftBuffer = rightBuffer = e.Data;
if (dev.PlayerChannel1 > 0)
_Buffer[dev.PlayerChannel1 - 1].ProcessNewBuffer(leftBuffer);
if (dev.PlayerChannel2 > 0)
_Buffer[dev.PlayerChannel2 - 1].ProcessNewBuffer(rightBuffer);
} }
} }
} }
@@ -16,47 +16,33 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using Vocaluxe.Base; using Vocaluxe.Base;
namespace Vocaluxe.Lib.Sound.Record.PortAudio namespace Vocaluxe.Lib.Sound.Record.PortAudio
{ {
class CPortAudioRecord : IRecord class CPortAudioRecord : CRecordBase, IRecord
{ {
private bool _Initialized; private bool _Initialized;
private readonly List<CRecordDevice> _Devices = new List<CRecordDevice>(); private CPortAudioHandle _PaHandle;
private PortAudioSharp.PortAudio.PaStreamCallbackDelegate _MyRecProc; private PortAudioSharp.PortAudio.PaStreamCallbackDelegate _MyRecProc;
private IntPtr[] _RecHandle; private IntPtr[] _RecHandle;
private readonly CBuffer[] _Buffer;
public CPortAudioRecord()
{
_Buffer = new CBuffer[CSettings.MaxNumPlayer];
for (int i = 0; i < _Buffer.Length; i++)
_Buffer[i] = new CBuffer();
}
/// <summary> /// <summary>
/// Init PortAudio and list record devices /// Init PortAudio and list record devices
/// </summary> /// </summary>
/// <returns>true if success</returns> /// <returns>true if success</returns>
public bool Init() public override bool Init()
{ {
if (_Initialized) if (!base.Init())
return false; return false;
try try
{ {
if (!CPortAudioCommon.InitDriver()) _PaHandle = new CPortAudioHandle();
return false;
_Devices.Clear(); int hostAPI = _PaHandle.GetHostApi();
int hostAPI = CPortAudioCommon.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++)
{ {
@@ -69,9 +55,6 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
} }
} }
foreach (CBuffer buffer in _Buffer)
buffer.Reset();
_RecHandle = new IntPtr[_Devices.Count]; _RecHandle = new IntPtr[_Devices.Count];
_MyRecProc = _MyPaStreamCallback; _MyRecProc = _MyPaStreamCallback;
_Initialized = true; _Initialized = true;
@@ -80,6 +63,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
{ {
_Initialized = false; _Initialized = false;
CLog.LogError("Error initializing PortAudio: " + e.Message); CLog.LogError("Error initializing PortAudio: " + e.Message);
Close();
return false; return false;
} }
@@ -95,9 +79,6 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
if (!_Initialized) if (!_Initialized)
return false; return false;
if (_RecHandle == null || _RecHandle.Length == 0)
return false;
Stop(); Stop();
foreach (IntPtr handle in _RecHandle) foreach (IntPtr handle in _RecHandle)
@@ -128,7 +109,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
suggestedLatency = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_Devices[dev].ID).defaultLowInputLatency, suggestedLatency = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_Devices[dev].ID).defaultLowInputLatency,
hostApiSpecificStreamInfo = IntPtr.Zero hostApiSpecificStreamInfo = IntPtr.Zero
}; };
if (!CPortAudioCommon.OpenInputStream( if (!_PaHandle.OpenInputStream(
out _RecHandle[dev], out _RecHandle[dev],
ref inputParams, ref inputParams,
44100, 44100,
@@ -138,7 +119,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
new IntPtr(dev))) new IntPtr(dev)))
return false; return false;
if (CPortAudioCommon.CheckError("Start Stream (rec)", PortAudioSharp.PortAudio.Pa_StartStream(_RecHandle[dev]))) if (_PaHandle.CheckError("Start Stream (rec)", PortAudioSharp.PortAudio.Pa_StartStream(_RecHandle[dev])))
return false; return false;
} }
} }
@@ -162,97 +143,25 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
/// <summary> /// <summary>
/// Stop all voice capturing streams and terminate PortAudio /// Stop all voice capturing streams and terminate PortAudio
/// </summary> /// </summary>
public void Close() public override void Close()
{ {
if (!_Initialized) if (_RecHandle != null)
return; {
foreach (IntPtr handle in _RecHandle)
foreach (IntPtr handle in _RecHandle) {
CPortAudioCommon.CloseStream(handle); if (handle != IntPtr.Zero)
CPortAudioCommon.CloseDriver(); _PaHandle.CloseStream(handle);
}
}
if (_PaHandle != null)
{
_PaHandle.Close();
_PaHandle = null;
}
_Initialized = false; _Initialized = false;
//System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer); base.Close();
}
/// <summary>
/// Detect Pitch and Volume of the newest voice buffer
/// </summary>
/// <param name="player"></param>
public void AnalyzeBuffer(int player)
{
if (!_Initialized)
return;
_Buffer[player].AnalyzeBuffer();
}
public int GetToneAbs(int player)
{
if (!_Initialized)
return 0;
return _Buffer[player].ToneAbs;
}
public int GetTone(int player)
{
if (!_Initialized)
return 0;
return _Buffer[player].Tone;
}
public void SetTone(int player, int tone)
{
if (!_Initialized)
return;
_Buffer[player].Tone = tone;
}
public float GetMaxVolume(int player)
{
if (!_Initialized)
return 0f;
return _Buffer[player].MaxVolume;
}
public bool ToneValid(int player)
{
if (!_Initialized)
return false;
return _Buffer[player].ToneValid;
}
public int NumHalfTones()
{
if (!_Initialized)
return 0;
return CBuffer.NumHalfTones;
}
public float[] ToneWeigth(int player)
{
if (!_Initialized)
return null;
return _Buffer[player].ToneWeigth;
}
public ReadOnlyCollection<CRecordDevice> RecordDevices()
{
if (!_Initialized)
return null;
if (_Devices.Count == 0)
return null;
return _Devices.AsReadOnly();
} }
private PortAudioSharp.PortAudio.PaStreamCallbackResult _MyPaStreamCallback( private PortAudioSharp.PortAudio.PaStreamCallbackResult _MyPaStreamCallback(
@@ -275,34 +184,10 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
numBytes = frameCount * 2; numBytes = frameCount * 2;
byte[] recbuffer = new byte[numBytes]; byte[] recbuffer = new byte[numBytes];
byte[] leftBuffer;
byte[] rightBuffer;
// copy from managed to unmanaged memory // copy from managed to unmanaged memory
Marshal.Copy(input, recbuffer, 0, (int)numBytes); Marshal.Copy(input, recbuffer, 0, (int)numBytes);
if (dev.Channels == 2) _HandleData(dev, recbuffer);
{
leftBuffer = new byte[numBytes / 2];
rightBuffer = new byte[numBytes / 2];
//[]: Sample, L: Left channel R: Right channel
//[LR][LR][LR][LR][LR][LR]
//The data is interleaved and needs to be demultiplexed
for (int i = 0; i < frameCount; i++)
{
leftBuffer[i * 2] = recbuffer[i * 4];
leftBuffer[i * 2 + 1] = recbuffer[i * 4 + 1];
rightBuffer[i * 2] = recbuffer[i * 4 + 2];
rightBuffer[i * 2 + 1] = recbuffer[i * 4 + 3];
}
}
else
leftBuffer = rightBuffer = recbuffer;
if (dev.PlayerChannel1 > 0)
_Buffer[dev.PlayerChannel1 - 1].ProcessNewBuffer(leftBuffer);
if (dev.PlayerChannel2 > 0)
_Buffer[dev.PlayerChannel2 - 1].ProcessNewBuffer(rightBuffer);
} }
} }
catch (Exception e) catch (Exception e)
+2 -1
View File
@@ -222,7 +222,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\CPortAudioHandle.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" />
@@ -264,6 +264,7 @@
<Compile Include="GameModes\CGameModes.cs" /> <Compile Include="GameModes\CGameModes.cs" />
<Compile Include="Lib\Input\CControllerFramework.cs" /> <Compile Include="Lib\Input\CControllerFramework.cs" />
<Compile Include="Lib\Sound\Playback\GstreamerSharp\CGstreamerSharpAudio.cs" /> <Compile Include="Lib\Sound\Playback\GstreamerSharp\CGstreamerSharpAudio.cs" />
<Compile Include="Lib\Sound\Record\CRecordBase.cs" />
<Compile Include="Lib\Sound\Record\DirectSound\CSampleDataEventArgs.cs" /> <Compile Include="Lib\Sound\Record\DirectSound\CSampleDataEventArgs.cs" />
<Compile Include="Lib\Sound\Record\DirectSound\CSoundCardSource.cs" /> <Compile Include="Lib\Sound\Record\DirectSound\CSoundCardSource.cs" />
<Compile Include="Lib\Video\Acinerella\CDecoder.cs" /> <Compile Include="Lib\Video\Acinerella\CDecoder.cs" />