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.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);
}
+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
{
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)
+2 -1
View File
@@ -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" />