Add a base record class to handle common functions (avoid duplicate code)
This commit is contained in:
1 parent
9054f529a0
commit
3bc8fd516f
6 files changed
+277
-310
No files matched your search
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using PortAudioSharp;
|
||||
using Vocaluxe.Base;
|
||||
@@ -8,40 +9,53 @@ 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
|
||||
/// 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:
|
||||
/// Initialize, Terminate, OpenStream, CloseStream
|
||||
/// </summary>
|
||||
static class CPortAudioCommon
|
||||
class CPortAudioHandle : IDisposable
|
||||
{
|
||||
private static int _RefCount;
|
||||
private static readonly object _Mutex = new object();
|
||||
|
||||
private bool _Disposed;
|
||||
private readonly List<IntPtr> _Streams = new List<IntPtr>();
|
||||
|
||||
/// <summary>
|
||||
/// Safe method to init PortAudio (adds a reference)
|
||||
/// MUST call CloseDriver when done
|
||||
/// Initializes PortAudio library (if required)
|
||||
/// </summary>
|
||||
/// <returns>True on success, falso if not initialized (log written)</returns>
|
||||
public static bool InitDriver()
|
||||
public CPortAudioHandle()
|
||||
{
|
||||
lock (_Mutex)
|
||||
{
|
||||
if (_RefCount == 0)
|
||||
{
|
||||
if (CheckError("Initialize", PortAudio.Pa_Initialize()))
|
||||
return false;
|
||||
throw new Exception();
|
||||
}
|
||||
_RefCount++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safe method to close PortAudio
|
||||
/// MUST be called _exactly_ once after successfull InitDriver call
|
||||
/// </summary>
|
||||
public static void CloseDriver()
|
||||
~CPortAudioHandle()
|
||||
{
|
||||
_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)
|
||||
{
|
||||
Debug.Assert(_RefCount > 0);
|
||||
@@ -49,15 +63,37 @@ namespace Vocaluxe.Lib.Sound
|
||||
if (_RefCount == 0)
|
||||
PortAudio.Pa_Terminate();
|
||||
}
|
||||
_Disposed = true;
|
||||
}
|
||||
|
||||
public 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)
|
||||
public void Dispose()
|
||||
{
|
||||
_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)
|
||||
{
|
||||
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="userData"></param>
|
||||
/// <returns>True on success</returns>
|
||||
public static bool OpenInputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? inputParameters,
|
||||
double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags,
|
||||
PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData)
|
||||
public 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
|
||||
@@ -93,9 +129,9 @@ namespace Vocaluxe.Lib.Sound
|
||||
/// <param name="streamCallback"></param>
|
||||
/// <param name="userData"></param>
|
||||
/// <returns>True on success</returns>
|
||||
public static bool OpenOutputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? outputParameters,
|
||||
double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags,
|
||||
PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData)
|
||||
public 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
|
||||
@@ -103,11 +139,15 @@ namespace Vocaluxe.Lib.Sound
|
||||
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)
|
||||
{
|
||||
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="errorCode">Result returned by Pa_* call</param>
|
||||
/// <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)
|
||||
{
|
||||
CLog.LogError(action + " error: " + PortAudio.Pa_GetErrorText(errorCode));
|
||||
@@ -140,11 +183,10 @@ namespace Vocaluxe.Lib.Sound
|
||||
/// Selects the most appropriate host api
|
||||
/// </summary>
|
||||
/// <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 (_RefCount < 1)
|
||||
return -1;
|
||||
if (_Disposed)
|
||||
throw new ObjectDisposedException("PortAudioHandle already disposed");
|
||||
|
||||
int selectedHostApi = PortAudio.Pa_GetDefaultHostApi();
|
||||
int apiCount = PortAudio.Pa_GetHostApiCount();
|
||||
@@ -15,6 +15,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
|
||||
public readonly int ID;
|
||||
private readonly Closeproc _Closeproc;
|
||||
private CPortAudioHandle _PaHandle;
|
||||
|
||||
private readonly CSyncTimer _SyncTimer;
|
||||
private int _ByteCount = 4;
|
||||
@@ -187,12 +188,12 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
{
|
||||
Paused = false;
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
CPortAudioCommon.CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream));
|
||||
_PaHandle.CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
CPortAudioCommon.CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream));
|
||||
_PaHandle.CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream));
|
||||
Skip(0f);
|
||||
}
|
||||
|
||||
@@ -210,14 +211,11 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
if (!File.Exists(fileName))
|
||||
return false;
|
||||
|
||||
bool driverInitialized = false;
|
||||
try
|
||||
{
|
||||
if (!CPortAudioCommon.InitDriver())
|
||||
return false;
|
||||
driverInitialized = true;
|
||||
_PaHandle = new CPortAudioHandle();
|
||||
|
||||
int hostApi = CPortAudioCommon.GetHostApi();
|
||||
int hostApi = _PaHandle.GetHostApi();
|
||||
_ApiInfo = PortAudioSharp.PortAudio.Pa_GetHostApiInfo(hostApi);
|
||||
_OutputDeviceInfo = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_ApiInfo.defaultOutputDevice);
|
||||
if (_OutputDeviceInfo.defaultLowOutputLatency < 0.1)
|
||||
@@ -227,8 +225,11 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (driverInitialized)
|
||||
CPortAudioCommon.CloseDriver();
|
||||
if (_PaHandle != null)
|
||||
{
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
}
|
||||
CLog.LogError("Error Init PortAudio Playback");
|
||||
return false;
|
||||
}
|
||||
@@ -240,7 +241,8 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
SFormatInfo format = _Decoder.GetFormatInfo();
|
||||
if (format.SamplesPerSecond == 0)
|
||||
{
|
||||
CPortAudioCommon.CloseDriver();
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
_Decoder.Close();
|
||||
_Decoder = null;
|
||||
CLog.LogError("Error Init PortAudio Playback (samples=0)");
|
||||
@@ -262,7 +264,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
hostApiSpecificStreamInfo = IntPtr.Zero
|
||||
};
|
||||
|
||||
if (!CPortAudioCommon.OpenOutputStream(
|
||||
if (!_PaHandle.OpenOutputStream(
|
||||
out _Stream,
|
||||
ref outputParams,
|
||||
format.SamplesPerSecond,
|
||||
@@ -271,7 +273,8 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
_PaStreamCallback,
|
||||
IntPtr.Zero) || _Stream == IntPtr.Zero)
|
||||
{
|
||||
CPortAudioCommon.CloseDriver();
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
_Decoder.Close();
|
||||
_Decoder = null;
|
||||
return false;
|
||||
@@ -410,9 +413,11 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
|
||||
private void _DoFree()
|
||||
{
|
||||
CPortAudioCommon.CloseStream(_Stream);
|
||||
CPortAudioCommon.CloseDriver();
|
||||
_PaHandle.CloseStream(_Stream);
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
_Decoder.Close();
|
||||
_Decoder = null;
|
||||
|
||||
_Closeproc(ID);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
class CDirectSoundRecord : IRecord
|
||||
class CDirectSoundRecord : CRecordBase, IRecord
|
||||
{
|
||||
private bool _Initialized;
|
||||
private List<CRecordDevice> _Devices;
|
||||
|
||||
private List<CSoundCardSource> _Sources;
|
||||
|
||||
private readonly CBuffer[] _Buffer;
|
||||
|
||||
public CDirectSoundRecord()
|
||||
public override bool Init()
|
||||
{
|
||||
_Buffer = new CBuffer[CSettings.MaxNumPlayer];
|
||||
for (int i = 0; i < _Buffer.Length; i++)
|
||||
_Buffer[i] = new CBuffer();
|
||||
if (!base.Init())
|
||||
return false;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
public bool Init()
|
||||
{
|
||||
DeviceCollection devices = DirectSoundCapture.GetDevices();
|
||||
_Devices = new List<CRecordDevice>();
|
||||
_Sources = new List<CSoundCardSource>();
|
||||
|
||||
int id = 0;
|
||||
DeviceCollection devices = DirectSoundCapture.GetDevices();
|
||||
|
||||
foreach (DeviceInformation dev in devices)
|
||||
{
|
||||
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);
|
||||
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,14 +53,11 @@ namespace Vocaluxe.Lib.Sound.Record.DirectSound
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
public override void Close()
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
Stop();
|
||||
_Initialized = false;
|
||||
}
|
||||
//System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer);
|
||||
Stop();
|
||||
_Initialized = false;
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public bool Start()
|
||||
@@ -112,81 +98,6 @@ namespace Vocaluxe.Lib.Sound.Record.DirectSound
|
||||
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)
|
||||
{
|
||||
if (!_Initialized)
|
||||
@@ -194,32 +105,7 @@ namespace Vocaluxe.Lib.Sound.Record.DirectSound
|
||||
CRecordDevice dev = _Devices.FirstOrDefault(device => device.Driver == e.Guid);
|
||||
if (dev == null)
|
||||
return;
|
||||
byte[] leftBuffer;
|
||||
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);
|
||||
_HandleData(dev, e.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,47 +16,33 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Vocaluxe.Base;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
{
|
||||
class CPortAudioRecord : IRecord
|
||||
class CPortAudioRecord : CRecordBase, IRecord
|
||||
{
|
||||
private bool _Initialized;
|
||||
private readonly List<CRecordDevice> _Devices = new List<CRecordDevice>();
|
||||
|
||||
private CPortAudioHandle _PaHandle;
|
||||
private PortAudioSharp.PortAudio.PaStreamCallbackDelegate _MyRecProc;
|
||||
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>
|
||||
/// Init PortAudio and list record devices
|
||||
/// </summary>
|
||||
/// <returns>true if success</returns>
|
||||
public bool Init()
|
||||
public override bool Init()
|
||||
{
|
||||
if (_Initialized)
|
||||
if (!base.Init())
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (!CPortAudioCommon.InitDriver())
|
||||
return false;
|
||||
_PaHandle = new CPortAudioHandle();
|
||||
|
||||
_Devices.Clear();
|
||||
int hostAPI = CPortAudioCommon.GetHostApi();
|
||||
int hostAPI = _PaHandle.GetHostApi();
|
||||
int numDevices = PortAudioSharp.PortAudio.Pa_GetDeviceCount();
|
||||
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];
|
||||
_MyRecProc = _MyPaStreamCallback;
|
||||
_Initialized = true;
|
||||
@@ -80,6 +63,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
{
|
||||
_Initialized = false;
|
||||
CLog.LogError("Error initializing PortAudio: " + e.Message);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -95,9 +79,6 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
|
||||
if (_RecHandle == null || _RecHandle.Length == 0)
|
||||
return false;
|
||||
|
||||
Stop();
|
||||
|
||||
foreach (IntPtr handle in _RecHandle)
|
||||
@@ -128,7 +109,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
suggestedLatency = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_Devices[dev].ID).defaultLowInputLatency,
|
||||
hostApiSpecificStreamInfo = IntPtr.Zero
|
||||
};
|
||||
if (!CPortAudioCommon.OpenInputStream(
|
||||
if (!_PaHandle.OpenInputStream(
|
||||
out _RecHandle[dev],
|
||||
ref inputParams,
|
||||
44100,
|
||||
@@ -138,7 +119,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
new IntPtr(dev)))
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -162,97 +143,25 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
/// <summary>
|
||||
/// Stop all voice capturing streams and terminate PortAudio
|
||||
/// </summary>
|
||||
public void Close()
|
||||
public override void Close()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
|
||||
foreach (IntPtr handle in _RecHandle)
|
||||
CPortAudioCommon.CloseStream(handle);
|
||||
CPortAudioCommon.CloseDriver();
|
||||
if (_RecHandle != null)
|
||||
{
|
||||
foreach (IntPtr handle in _RecHandle)
|
||||
{
|
||||
if (handle != IntPtr.Zero)
|
||||
_PaHandle.CloseStream(handle);
|
||||
}
|
||||
}
|
||||
if (_PaHandle != null)
|
||||
{
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
}
|
||||
|
||||
_Initialized = false;
|
||||
|
||||
//System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer);
|
||||
}
|
||||
|
||||
/// <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();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
private PortAudioSharp.PortAudio.PaStreamCallbackResult _MyPaStreamCallback(
|
||||
@@ -275,34 +184,10 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio
|
||||
numBytes = frameCount * 2;
|
||||
|
||||
byte[] recbuffer = new byte[numBytes];
|
||||
byte[] leftBuffer;
|
||||
byte[] rightBuffer;
|
||||
|
||||
// copy from managed to unmanaged memory
|
||||
Marshal.Copy(input, recbuffer, 0, (int)numBytes);
|
||||
if (dev.Channels == 2)
|
||||
{
|
||||
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);
|
||||
_HandleData(dev, recbuffer);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
<Compile Include="Lib\Sound\Playback\GstreamerSharp\CGstreamerSharpAudioStream.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\OpenAL\COpenAlStream.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\PortAudio\CPortAudioStream.cs" />
|
||||
<Compile Include="Lib\Sound\CPortAudioCommon.cs" />
|
||||
<Compile Include="Lib\Sound\CPortAudioHandle.cs" />
|
||||
<Compile Include="Lib\Sound\Record\CBuffer.cs" />
|
||||
<Compile Include="Base\CConfig.cs" />
|
||||
<Compile Include="Base\CCover.cs" />
|
||||
@@ -264,6 +264,7 @@
|
||||
<Compile Include="GameModes\CGameModes.cs" />
|
||||
<Compile Include="Lib\Input\CControllerFramework.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\CSoundCardSource.cs" />
|
||||
<Compile Include="Lib\Video\Acinerella\CDecoder.cs" />
|
||||
|
||||
Reference in new issue
Block a user