mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
1649 lines
50 KiB
C++
1649 lines
50 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
Copyright 2004-9 by Raw Material Software Ltd.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the GNU General
|
|
Public License (Version 2), as published by the Free Software Foundation.
|
|
A copy of the license is included in the JUCE distribution, or can be found
|
|
online at www.gnu.org/licenses.
|
|
|
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
To release a closed-source product which uses JUCE, commercial licenses are
|
|
available: visit www.rawmaterialsoftware.com/juce for more information.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
// (This file gets included by juce_win32_NativeCode.cpp, rather than being
|
|
// compiled on its own).
|
|
#if JUCE_INCLUDED_FILE && JUCE_DIRECTSOUND
|
|
|
|
//==============================================================================
|
|
END_JUCE_NAMESPACE
|
|
|
|
extern "C"
|
|
{
|
|
|
|
// Declare just the minimum number of interfaces for the DSound objects that we need..
|
|
typedef struct typeDSBUFFERDESC
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwFlags;
|
|
DWORD dwBufferBytes;
|
|
DWORD dwReserved;
|
|
LPWAVEFORMATEX lpwfxFormat;
|
|
GUID guid3DAlgorithm;
|
|
} DSBUFFERDESC;
|
|
|
|
struct IDirectSoundBuffer;
|
|
|
|
#undef INTERFACE
|
|
#define INTERFACE IDirectSound
|
|
DECLARE_INTERFACE_(IDirectSound, IUnknown)
|
|
{
|
|
STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
|
|
STDMETHOD_(ULONG,AddRef) (THIS) PURE;
|
|
STDMETHOD_(ULONG,Release) (THIS) PURE;
|
|
STDMETHOD(CreateSoundBuffer) (THIS_ DSBUFFERDESC*, IDirectSoundBuffer**, LPUNKNOWN) PURE;
|
|
STDMETHOD(GetCaps) (THIS_ void*) PURE;
|
|
STDMETHOD(DuplicateSoundBuffer) (THIS_ IDirectSoundBuffer*, IDirectSoundBuffer**) PURE;
|
|
STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE;
|
|
STDMETHOD(Compact) (THIS) PURE;
|
|
STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE;
|
|
STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE;
|
|
STDMETHOD(Initialize) (THIS_ const GUID*) PURE;
|
|
};
|
|
|
|
#undef INTERFACE
|
|
#define INTERFACE IDirectSoundBuffer
|
|
DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown)
|
|
{
|
|
STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
|
|
STDMETHOD_(ULONG,AddRef) (THIS) PURE;
|
|
STDMETHOD_(ULONG,Release) (THIS) PURE;
|
|
STDMETHOD(GetCaps) (THIS_ void*) PURE;
|
|
STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE;
|
|
STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE;
|
|
STDMETHOD(GetVolume) (THIS_ LPLONG) PURE;
|
|
STDMETHOD(GetPan) (THIS_ LPLONG) PURE;
|
|
STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE;
|
|
STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE;
|
|
STDMETHOD(Initialize) (THIS_ IDirectSound*, DSBUFFERDESC*) PURE;
|
|
STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE;
|
|
STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE;
|
|
STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE;
|
|
STDMETHOD(SetFormat) (THIS_ const WAVEFORMATEX*) PURE;
|
|
STDMETHOD(SetVolume) (THIS_ LONG) PURE;
|
|
STDMETHOD(SetPan) (THIS_ LONG) PURE;
|
|
STDMETHOD(SetFrequency) (THIS_ DWORD) PURE;
|
|
STDMETHOD(Stop) (THIS) PURE;
|
|
STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE;
|
|
STDMETHOD(Restore) (THIS) PURE;
|
|
};
|
|
|
|
//==============================================================================
|
|
typedef struct typeDSCBUFFERDESC
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwFlags;
|
|
DWORD dwBufferBytes;
|
|
DWORD dwReserved;
|
|
LPWAVEFORMATEX lpwfxFormat;
|
|
} DSCBUFFERDESC;
|
|
|
|
struct IDirectSoundCaptureBuffer;
|
|
|
|
#undef INTERFACE
|
|
#define INTERFACE IDirectSoundCapture
|
|
DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown)
|
|
{
|
|
STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
|
|
STDMETHOD_(ULONG,AddRef) (THIS) PURE;
|
|
STDMETHOD_(ULONG,Release) (THIS) PURE;
|
|
STDMETHOD(CreateCaptureBuffer) (THIS_ DSCBUFFERDESC*, IDirectSoundCaptureBuffer**, LPUNKNOWN) PURE;
|
|
STDMETHOD(GetCaps) (THIS_ void*) PURE;
|
|
STDMETHOD(Initialize) (THIS_ const GUID*) PURE;
|
|
};
|
|
|
|
#undef INTERFACE
|
|
#define INTERFACE IDirectSoundCaptureBuffer
|
|
DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown)
|
|
{
|
|
STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
|
|
STDMETHOD_(ULONG,AddRef) (THIS) PURE;
|
|
STDMETHOD_(ULONG,Release) (THIS) PURE;
|
|
STDMETHOD(GetCaps) (THIS_ void*) PURE;
|
|
STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE;
|
|
STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE;
|
|
STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE;
|
|
STDMETHOD(Initialize) (THIS_ IDirectSoundCapture*, DSCBUFFERDESC*) PURE;
|
|
STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE;
|
|
STDMETHOD(Start) (THIS_ DWORD) PURE;
|
|
STDMETHOD(Stop) (THIS) PURE;
|
|
STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE;
|
|
};
|
|
|
|
};
|
|
|
|
//==============================================================================
|
|
BEGIN_JUCE_NAMESPACE
|
|
|
|
static const String getDSErrorMessage (HRESULT hr)
|
|
{
|
|
const char* result = 0;
|
|
|
|
switch (hr)
|
|
{
|
|
case MAKE_HRESULT(1, 0x878, 10):
|
|
result = "Device already allocated";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 30):
|
|
result = "Control unavailable";
|
|
break;
|
|
case E_INVALIDARG:
|
|
result = "Invalid parameter";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 50):
|
|
result = "Invalid call";
|
|
break;
|
|
case E_FAIL:
|
|
result = "Generic error";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 70):
|
|
result = "Priority level error";
|
|
break;
|
|
case E_OUTOFMEMORY:
|
|
result = "Out of memory";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 100):
|
|
result = "Bad format";
|
|
break;
|
|
case E_NOTIMPL:
|
|
result = "Unsupported function";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 120):
|
|
result = "No driver";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 130):
|
|
result = "Already initialised";
|
|
break;
|
|
case CLASS_E_NOAGGREGATION:
|
|
result = "No aggregation";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 150):
|
|
result = "Buffer lost";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 160):
|
|
result = "Another app has priority";
|
|
break;
|
|
case MAKE_HRESULT(1, 0x878, 170):
|
|
result = "Uninitialised";
|
|
break;
|
|
case E_NOINTERFACE:
|
|
result = "No interface";
|
|
break;
|
|
case S_OK:
|
|
result = "No error";
|
|
break;
|
|
|
|
default:
|
|
return "Unknown error: " + String ((int) hr);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//==============================================================================
|
|
#define DS_DEBUGGING 1
|
|
|
|
#ifdef DS_DEBUGGING
|
|
#define CATCH JUCE_CATCH_EXCEPTION
|
|
#undef log
|
|
#define log(a) Logger::writeToLog(a);
|
|
#undef logError
|
|
#define logError(a) logDSError(a, __LINE__);
|
|
|
|
static void logDSError (HRESULT hr, int lineNum)
|
|
{
|
|
if (hr != S_OK)
|
|
{
|
|
String error ("DS error at line ");
|
|
error << lineNum << T(" - ") << getDSErrorMessage (hr);
|
|
log (error);
|
|
}
|
|
}
|
|
#else
|
|
#define CATCH JUCE_CATCH_ALL
|
|
#define log(a)
|
|
#define logError(a)
|
|
#endif
|
|
|
|
|
|
//==============================================================================
|
|
#define DSOUND_FUNCTION(functionName, params) \
|
|
typedef HRESULT (WINAPI *type##functionName) params; \
|
|
static type##functionName ds##functionName = 0;
|
|
|
|
#define DSOUND_FUNCTION_LOAD(functionName) \
|
|
ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \
|
|
jassert (ds##functionName != 0);
|
|
|
|
typedef BOOL (CALLBACK *LPDSENUMCALLBACKW) (LPGUID, LPCWSTR, LPCWSTR, LPVOID);
|
|
typedef BOOL (CALLBACK *LPDSENUMCALLBACKA) (LPGUID, LPCSTR, LPCSTR, LPVOID);
|
|
|
|
DSOUND_FUNCTION (DirectSoundCreate, (const GUID*, IDirectSound**, LPUNKNOWN))
|
|
DSOUND_FUNCTION (DirectSoundCaptureCreate, (const GUID*, IDirectSoundCapture**, LPUNKNOWN))
|
|
DSOUND_FUNCTION (DirectSoundEnumerateW, (LPDSENUMCALLBACKW, LPVOID))
|
|
DSOUND_FUNCTION (DirectSoundCaptureEnumerateW, (LPDSENUMCALLBACKW, LPVOID))
|
|
|
|
static void initialiseDSoundFunctions()
|
|
{
|
|
if (dsDirectSoundCreate == 0)
|
|
{
|
|
HMODULE h = LoadLibraryA ("dsound.dll");
|
|
|
|
DSOUND_FUNCTION_LOAD (DirectSoundCreate)
|
|
DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate)
|
|
DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW)
|
|
DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW)
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
class DSoundInternalOutChannel
|
|
{
|
|
String name;
|
|
LPGUID guid;
|
|
int sampleRate, bufferSizeSamples;
|
|
float* leftBuffer;
|
|
float* rightBuffer;
|
|
|
|
IDirectSound* pDirectSound;
|
|
IDirectSoundBuffer* pOutputBuffer;
|
|
DWORD writeOffset;
|
|
int totalBytesPerBuffer;
|
|
int bytesPerBuffer;
|
|
unsigned int lastPlayCursor;
|
|
|
|
public:
|
|
int bitDepth;
|
|
bool doneFlag;
|
|
|
|
DSoundInternalOutChannel (const String& name_,
|
|
LPGUID guid_,
|
|
int rate,
|
|
int bufferSize,
|
|
float* left,
|
|
float* right)
|
|
: name (name_),
|
|
guid (guid_),
|
|
sampleRate (rate),
|
|
bufferSizeSamples (bufferSize),
|
|
leftBuffer (left),
|
|
rightBuffer (right),
|
|
pDirectSound (0),
|
|
pOutputBuffer (0),
|
|
bitDepth (16)
|
|
{
|
|
}
|
|
|
|
~DSoundInternalOutChannel()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void close()
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (pOutputBuffer != 0)
|
|
{
|
|
JUCE_TRY
|
|
{
|
|
log (T("closing dsound out: ") + name);
|
|
hr = pOutputBuffer->Stop();
|
|
logError (hr);
|
|
}
|
|
CATCH
|
|
|
|
JUCE_TRY
|
|
{
|
|
hr = pOutputBuffer->Release();
|
|
logError (hr);
|
|
}
|
|
CATCH
|
|
|
|
pOutputBuffer = 0;
|
|
}
|
|
|
|
if (pDirectSound != 0)
|
|
{
|
|
JUCE_TRY
|
|
{
|
|
hr = pDirectSound->Release();
|
|
logError (hr);
|
|
}
|
|
CATCH
|
|
|
|
pDirectSound = 0;
|
|
}
|
|
}
|
|
|
|
const String open()
|
|
{
|
|
log (T("opening dsound out device: ") + name
|
|
+ T(" rate=") + String (sampleRate)
|
|
+ T(" bits=") + String (bitDepth)
|
|
+ T(" buf=") + String (bufferSizeSamples));
|
|
|
|
pDirectSound = 0;
|
|
pOutputBuffer = 0;
|
|
writeOffset = 0;
|
|
|
|
String error;
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
if (dsDirectSoundCreate != 0)
|
|
hr = dsDirectSoundCreate (guid, &pDirectSound, 0);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
|
|
totalBytesPerBuffer = (3 * bytesPerBuffer) & ~15;
|
|
const int numChannels = 2;
|
|
|
|
hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */);
|
|
logError (hr);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
IDirectSoundBuffer* pPrimaryBuffer;
|
|
|
|
DSBUFFERDESC primaryDesc;
|
|
zerostruct (primaryDesc);
|
|
|
|
primaryDesc.dwSize = sizeof (DSBUFFERDESC);
|
|
primaryDesc.dwFlags = 1 /* DSBCAPS_PRIMARYBUFFER */;
|
|
primaryDesc.dwBufferBytes = 0;
|
|
primaryDesc.lpwfxFormat = 0;
|
|
|
|
log ("opening dsound out step 2");
|
|
hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0);
|
|
logError (hr);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
WAVEFORMATEX wfFormat;
|
|
wfFormat.wFormatTag = WAVE_FORMAT_PCM;
|
|
wfFormat.nChannels = (unsigned short) numChannels;
|
|
wfFormat.nSamplesPerSec = sampleRate;
|
|
wfFormat.wBitsPerSample = (unsigned short) bitDepth;
|
|
wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * wfFormat.wBitsPerSample / 8);
|
|
wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
|
|
wfFormat.cbSize = 0;
|
|
|
|
hr = pPrimaryBuffer->SetFormat (&wfFormat);
|
|
logError (hr);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
DSBUFFERDESC secondaryDesc;
|
|
zerostruct (secondaryDesc);
|
|
|
|
secondaryDesc.dwSize = sizeof (DSBUFFERDESC);
|
|
secondaryDesc.dwFlags = 0x8000 /* DSBCAPS_GLOBALFOCUS */
|
|
| 0x10000 /* DSBCAPS_GETCURRENTPOSITION2 */;
|
|
secondaryDesc.dwBufferBytes = totalBytesPerBuffer;
|
|
secondaryDesc.lpwfxFormat = &wfFormat;
|
|
|
|
hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0);
|
|
logError (hr);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
log ("opening dsound out step 3");
|
|
|
|
DWORD dwDataLen;
|
|
unsigned char* pDSBuffData;
|
|
|
|
hr = pOutputBuffer->Lock (0, totalBytesPerBuffer,
|
|
(LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0);
|
|
logError (hr);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
zeromem (pDSBuffData, dwDataLen);
|
|
|
|
hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pOutputBuffer->SetCurrentPosition (0);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */);
|
|
|
|
if (hr == S_OK)
|
|
return String::empty;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
error = getDSErrorMessage (hr);
|
|
close();
|
|
return error;
|
|
}
|
|
|
|
void synchronisePosition()
|
|
{
|
|
if (pOutputBuffer != 0)
|
|
{
|
|
DWORD playCursor;
|
|
pOutputBuffer->GetCurrentPosition (&playCursor, &writeOffset);
|
|
}
|
|
}
|
|
|
|
bool service()
|
|
{
|
|
if (pOutputBuffer == 0)
|
|
return true;
|
|
|
|
DWORD playCursor, writeCursor;
|
|
|
|
for (;;)
|
|
{
|
|
HRESULT hr = pOutputBuffer->GetCurrentPosition (&playCursor, &writeCursor);
|
|
|
|
if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
|
|
{
|
|
pOutputBuffer->Restore();
|
|
continue;
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
break;
|
|
|
|
logError (hr);
|
|
jassertfalse
|
|
return true;
|
|
}
|
|
|
|
int playWriteGap = writeCursor - playCursor;
|
|
if (playWriteGap < 0)
|
|
playWriteGap += totalBytesPerBuffer;
|
|
|
|
int bytesEmpty = playCursor - writeOffset;
|
|
|
|
if (bytesEmpty < 0)
|
|
bytesEmpty += totalBytesPerBuffer;
|
|
|
|
if (bytesEmpty > (totalBytesPerBuffer - playWriteGap))
|
|
{
|
|
writeOffset = writeCursor;
|
|
bytesEmpty = totalBytesPerBuffer - playWriteGap;
|
|
}
|
|
|
|
if (bytesEmpty >= bytesPerBuffer)
|
|
{
|
|
LPBYTE lpbuf1 = 0;
|
|
LPBYTE lpbuf2 = 0;
|
|
DWORD dwSize1 = 0;
|
|
DWORD dwSize2 = 0;
|
|
|
|
HRESULT hr = pOutputBuffer->Lock (writeOffset,
|
|
bytesPerBuffer,
|
|
(void**) &lpbuf1, &dwSize1,
|
|
(void**) &lpbuf2, &dwSize2, 0);
|
|
|
|
if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
|
|
{
|
|
pOutputBuffer->Restore();
|
|
|
|
hr = pOutputBuffer->Lock (writeOffset,
|
|
bytesPerBuffer,
|
|
(void**) &lpbuf1, &dwSize1,
|
|
(void**) &lpbuf2, &dwSize2, 0);
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (bitDepth == 16)
|
|
{
|
|
const float gainL = 32767.0f;
|
|
const float gainR = 32767.0f;
|
|
|
|
int* dest = (int*)lpbuf1;
|
|
const float* left = leftBuffer;
|
|
const float* right = rightBuffer;
|
|
int samples1 = dwSize1 >> 2;
|
|
int samples2 = dwSize2 >> 2;
|
|
|
|
if (left == 0)
|
|
{
|
|
while (--samples1 >= 0)
|
|
{
|
|
int r = roundFloatToInt (gainR * *right++);
|
|
|
|
if (r < -32768)
|
|
r = -32768;
|
|
else if (r > 32767)
|
|
r = 32767;
|
|
|
|
*dest++ = (r << 16);
|
|
}
|
|
|
|
dest = (int*)lpbuf2;
|
|
|
|
while (--samples2 >= 0)
|
|
{
|
|
int r = roundFloatToInt (gainR * *right++);
|
|
|
|
if (r < -32768)
|
|
r = -32768;
|
|
else if (r > 32767)
|
|
r = 32767;
|
|
|
|
*dest++ = (r << 16);
|
|
}
|
|
}
|
|
else if (right == 0)
|
|
{
|
|
while (--samples1 >= 0)
|
|
{
|
|
int l = roundFloatToInt (gainL * *left++);
|
|
|
|
if (l < -32768)
|
|
l = -32768;
|
|
else if (l > 32767)
|
|
l = 32767;
|
|
|
|
l &= 0xffff;
|
|
|
|
*dest++ = l;
|
|
}
|
|
|
|
dest = (int*)lpbuf2;
|
|
|
|
while (--samples2 >= 0)
|
|
{
|
|
int l = roundFloatToInt (gainL * *left++);
|
|
|
|
if (l < -32768)
|
|
l = -32768;
|
|
else if (l > 32767)
|
|
l = 32767;
|
|
|
|
l &= 0xffff;
|
|
|
|
*dest++ = l;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (--samples1 >= 0)
|
|
{
|
|
int l = roundFloatToInt (gainL * *left++);
|
|
|
|
if (l < -32768)
|
|
l = -32768;
|
|
else if (l > 32767)
|
|
l = 32767;
|
|
|
|
l &= 0xffff;
|
|
|
|
int r = roundFloatToInt (gainR * *right++);
|
|
|
|
if (r < -32768)
|
|
r = -32768;
|
|
else if (r > 32767)
|
|
r = 32767;
|
|
|
|
*dest++ = (r << 16) | l;
|
|
}
|
|
|
|
dest = (int*)lpbuf2;
|
|
|
|
while (--samples2 >= 0)
|
|
{
|
|
int l = roundFloatToInt (gainL * *left++);
|
|
|
|
if (l < -32768)
|
|
l = -32768;
|
|
else if (l > 32767)
|
|
l = 32767;
|
|
|
|
l &= 0xffff;
|
|
|
|
int r = roundFloatToInt (gainR * *right++);
|
|
|
|
if (r < -32768)
|
|
r = -32768;
|
|
else if (r > 32767)
|
|
r = 32767;
|
|
|
|
*dest++ = (r << 16) | l;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jassertfalse
|
|
}
|
|
|
|
writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer;
|
|
|
|
pOutputBuffer->Unlock (lpbuf1, dwSize1, lpbuf2, dwSize2);
|
|
}
|
|
else
|
|
{
|
|
jassertfalse
|
|
logError (hr);
|
|
}
|
|
|
|
bytesEmpty -= bytesPerBuffer;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
struct DSoundInternalInChannel
|
|
{
|
|
String name;
|
|
LPGUID guid;
|
|
int sampleRate, bufferSizeSamples;
|
|
float* leftBuffer;
|
|
float* rightBuffer;
|
|
|
|
IDirectSound* pDirectSound;
|
|
IDirectSoundCapture* pDirectSoundCapture;
|
|
IDirectSoundCaptureBuffer* pInputBuffer;
|
|
|
|
public:
|
|
unsigned int readOffset;
|
|
int bytesPerBuffer, totalBytesPerBuffer;
|
|
int bitDepth;
|
|
bool doneFlag;
|
|
|
|
DSoundInternalInChannel (const String& name_,
|
|
LPGUID guid_,
|
|
int rate,
|
|
int bufferSize,
|
|
float* left,
|
|
float* right)
|
|
: name (name_),
|
|
guid (guid_),
|
|
sampleRate (rate),
|
|
bufferSizeSamples (bufferSize),
|
|
leftBuffer (left),
|
|
rightBuffer (right),
|
|
pDirectSound (0),
|
|
pDirectSoundCapture (0),
|
|
pInputBuffer (0),
|
|
bitDepth (16)
|
|
{
|
|
}
|
|
|
|
~DSoundInternalInChannel()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void close()
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (pInputBuffer != 0)
|
|
{
|
|
JUCE_TRY
|
|
{
|
|
log (T("closing dsound in: ") + name);
|
|
hr = pInputBuffer->Stop();
|
|
logError (hr);
|
|
}
|
|
CATCH
|
|
|
|
JUCE_TRY
|
|
{
|
|
hr = pInputBuffer->Release();
|
|
logError (hr);
|
|
}
|
|
CATCH
|
|
|
|
pInputBuffer = 0;
|
|
}
|
|
|
|
if (pDirectSoundCapture != 0)
|
|
{
|
|
JUCE_TRY
|
|
{
|
|
hr = pDirectSoundCapture->Release();
|
|
logError (hr);
|
|
}
|
|
CATCH
|
|
|
|
pDirectSoundCapture = 0;
|
|
}
|
|
|
|
if (pDirectSound != 0)
|
|
{
|
|
JUCE_TRY
|
|
{
|
|
hr = pDirectSound->Release();
|
|
logError (hr);
|
|
}
|
|
CATCH
|
|
|
|
pDirectSound = 0;
|
|
}
|
|
}
|
|
|
|
const String open()
|
|
{
|
|
log (T("opening dsound in device: ") + name
|
|
+ T(" rate=") + String (sampleRate) + T(" bits=") + String (bitDepth) + T(" buf=") + String (bufferSizeSamples));
|
|
|
|
pDirectSound = 0;
|
|
pDirectSoundCapture = 0;
|
|
pInputBuffer = 0;
|
|
readOffset = 0;
|
|
totalBytesPerBuffer = 0;
|
|
|
|
String error;
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
if (dsDirectSoundCaptureCreate != 0)
|
|
hr = dsDirectSoundCaptureCreate (guid, &pDirectSoundCapture, 0);
|
|
|
|
logError (hr);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
const int numChannels = 2;
|
|
bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
|
|
totalBytesPerBuffer = (3 * bytesPerBuffer) & ~15;
|
|
|
|
WAVEFORMATEX wfFormat;
|
|
wfFormat.wFormatTag = WAVE_FORMAT_PCM;
|
|
wfFormat.nChannels = (unsigned short)numChannels;
|
|
wfFormat.nSamplesPerSec = sampleRate;
|
|
wfFormat.wBitsPerSample = (unsigned short)bitDepth;
|
|
wfFormat.nBlockAlign = (unsigned short)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
|
|
wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
|
|
wfFormat.cbSize = 0;
|
|
|
|
DSCBUFFERDESC captureDesc;
|
|
zerostruct (captureDesc);
|
|
|
|
captureDesc.dwSize = sizeof (DSCBUFFERDESC);
|
|
captureDesc.dwFlags = 0;
|
|
captureDesc.dwBufferBytes = totalBytesPerBuffer;
|
|
captureDesc.lpwfxFormat = &wfFormat;
|
|
|
|
log (T("opening dsound in step 2"));
|
|
hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0);
|
|
|
|
logError (hr);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */);
|
|
logError (hr);
|
|
|
|
if (hr == S_OK)
|
|
return String::empty;
|
|
}
|
|
}
|
|
|
|
error = getDSErrorMessage (hr);
|
|
close();
|
|
|
|
return error;
|
|
}
|
|
|
|
void synchronisePosition()
|
|
{
|
|
if (pInputBuffer != 0)
|
|
{
|
|
DWORD capturePos;
|
|
pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*)&readOffset);
|
|
}
|
|
}
|
|
|
|
bool service()
|
|
{
|
|
if (pInputBuffer == 0)
|
|
return true;
|
|
|
|
DWORD capturePos, readPos;
|
|
HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos);
|
|
logError (hr);
|
|
|
|
if (hr != S_OK)
|
|
return true;
|
|
|
|
int bytesFilled = readPos - readOffset;
|
|
if (bytesFilled < 0)
|
|
bytesFilled += totalBytesPerBuffer;
|
|
|
|
if (bytesFilled >= bytesPerBuffer)
|
|
{
|
|
LPBYTE lpbuf1 = 0;
|
|
LPBYTE lpbuf2 = 0;
|
|
DWORD dwsize1 = 0;
|
|
DWORD dwsize2 = 0;
|
|
|
|
HRESULT hr = pInputBuffer->Lock (readOffset,
|
|
bytesPerBuffer,
|
|
(void**) &lpbuf1, &dwsize1,
|
|
(void**) &lpbuf2, &dwsize2, 0);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (bitDepth == 16)
|
|
{
|
|
const float g = 1.0f / 32768.0f;
|
|
|
|
float* destL = leftBuffer;
|
|
float* destR = rightBuffer;
|
|
int samples1 = dwsize1 >> 2;
|
|
int samples2 = dwsize2 >> 2;
|
|
|
|
const short* src = (const short*)lpbuf1;
|
|
|
|
if (destL == 0)
|
|
{
|
|
while (--samples1 >= 0)
|
|
{
|
|
++src;
|
|
*destR++ = *src++ * g;
|
|
}
|
|
|
|
src = (const short*)lpbuf2;
|
|
|
|
while (--samples2 >= 0)
|
|
{
|
|
++src;
|
|
*destR++ = *src++ * g;
|
|
}
|
|
}
|
|
else if (destR == 0)
|
|
{
|
|
while (--samples1 >= 0)
|
|
{
|
|
*destL++ = *src++ * g;
|
|
++src;
|
|
}
|
|
|
|
src = (const short*)lpbuf2;
|
|
|
|
while (--samples2 >= 0)
|
|
{
|
|
*destL++ = *src++ * g;
|
|
++src;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (--samples1 >= 0)
|
|
{
|
|
*destL++ = *src++ * g;
|
|
*destR++ = *src++ * g;
|
|
}
|
|
|
|
src = (const short*)lpbuf2;
|
|
|
|
while (--samples2 >= 0)
|
|
{
|
|
*destL++ = *src++ * g;
|
|
*destR++ = *src++ * g;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jassertfalse
|
|
}
|
|
|
|
readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer;
|
|
|
|
pInputBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2);
|
|
}
|
|
else
|
|
{
|
|
logError (hr);
|
|
jassertfalse
|
|
}
|
|
|
|
bytesFilled -= bytesPerBuffer;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
class DSoundAudioIODevice : public AudioIODevice,
|
|
public Thread
|
|
{
|
|
public:
|
|
DSoundAudioIODevice (const String& deviceName,
|
|
const int outputDeviceIndex_,
|
|
const int inputDeviceIndex_)
|
|
: AudioIODevice (deviceName, "DirectSound"),
|
|
Thread ("Juce DSound"),
|
|
isOpen_ (false),
|
|
isStarted (false),
|
|
outputDeviceIndex (outputDeviceIndex_),
|
|
inputDeviceIndex (inputDeviceIndex_),
|
|
inChans (4),
|
|
outChans (4),
|
|
numInputBuffers (0),
|
|
numOutputBuffers (0),
|
|
totalSamplesOut (0),
|
|
sampleRate (0.0),
|
|
inputBuffers (0),
|
|
outputBuffers (0),
|
|
callback (0),
|
|
bufferSizeSamples (0)
|
|
{
|
|
if (outputDeviceIndex_ >= 0)
|
|
{
|
|
outChannels.add (TRANS("Left"));
|
|
outChannels.add (TRANS("Right"));
|
|
}
|
|
|
|
if (inputDeviceIndex_ >= 0)
|
|
{
|
|
inChannels.add (TRANS("Left"));
|
|
inChannels.add (TRANS("Right"));
|
|
}
|
|
}
|
|
|
|
~DSoundAudioIODevice()
|
|
{
|
|
close();
|
|
}
|
|
|
|
const StringArray getOutputChannelNames()
|
|
{
|
|
return outChannels;
|
|
}
|
|
|
|
const StringArray getInputChannelNames()
|
|
{
|
|
return inChannels;
|
|
}
|
|
|
|
int getNumSampleRates()
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
double getSampleRate (int index)
|
|
{
|
|
const double samps[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
|
|
|
|
return samps [jlimit (0, 3, index)];
|
|
}
|
|
|
|
int getNumBufferSizesAvailable()
|
|
{
|
|
return 50;
|
|
}
|
|
|
|
int getBufferSizeSamples (int index)
|
|
{
|
|
int n = 64;
|
|
for (int i = 0; i < index; ++i)
|
|
n += (n < 512) ? 32
|
|
: ((n < 1024) ? 64
|
|
: ((n < 2048) ? 128 : 256));
|
|
|
|
return n;
|
|
}
|
|
|
|
int getDefaultBufferSize()
|
|
{
|
|
return 2560;
|
|
}
|
|
|
|
const String open (const BitArray& inputChannels,
|
|
const BitArray& outputChannels,
|
|
double sampleRate,
|
|
int bufferSizeSamples)
|
|
{
|
|
lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
|
|
isOpen_ = lastError.isEmpty();
|
|
|
|
return lastError;
|
|
}
|
|
|
|
void close()
|
|
{
|
|
stop();
|
|
|
|
if (isOpen_)
|
|
{
|
|
closeDevice();
|
|
isOpen_ = false;
|
|
}
|
|
}
|
|
|
|
bool isOpen()
|
|
{
|
|
return isOpen_ && isThreadRunning();
|
|
}
|
|
|
|
int getCurrentBufferSizeSamples()
|
|
{
|
|
return bufferSizeSamples;
|
|
}
|
|
|
|
double getCurrentSampleRate()
|
|
{
|
|
return sampleRate;
|
|
}
|
|
|
|
int getCurrentBitDepth()
|
|
{
|
|
int i, bits = 256;
|
|
|
|
for (i = inChans.size(); --i >= 0;)
|
|
bits = jmin (bits, inChans[i]->bitDepth);
|
|
|
|
for (i = outChans.size(); --i >= 0;)
|
|
bits = jmin (bits, outChans[i]->bitDepth);
|
|
|
|
if (bits > 32)
|
|
bits = 16;
|
|
|
|
return bits;
|
|
}
|
|
|
|
const BitArray getActiveOutputChannels() const
|
|
{
|
|
return enabledOutputs;
|
|
}
|
|
|
|
const BitArray getActiveInputChannels() const
|
|
{
|
|
return enabledInputs;
|
|
}
|
|
|
|
int getOutputLatencyInSamples()
|
|
{
|
|
return (int) (getCurrentBufferSizeSamples() * 1.5);
|
|
}
|
|
|
|
int getInputLatencyInSamples()
|
|
{
|
|
return getOutputLatencyInSamples();
|
|
}
|
|
|
|
void start (AudioIODeviceCallback* call)
|
|
{
|
|
if (isOpen_ && call != 0 && ! isStarted)
|
|
{
|
|
if (! isThreadRunning())
|
|
{
|
|
// something gone wrong and the thread's stopped..
|
|
isOpen_ = false;
|
|
return;
|
|
}
|
|
|
|
call->audioDeviceAboutToStart (this);
|
|
|
|
const ScopedLock sl (startStopLock);
|
|
callback = call;
|
|
isStarted = true;
|
|
}
|
|
}
|
|
|
|
void stop()
|
|
{
|
|
if (isStarted)
|
|
{
|
|
AudioIODeviceCallback* const callbackLocal = callback;
|
|
|
|
{
|
|
const ScopedLock sl (startStopLock);
|
|
isStarted = false;
|
|
}
|
|
|
|
if (callbackLocal != 0)
|
|
callbackLocal->audioDeviceStopped();
|
|
}
|
|
}
|
|
|
|
bool isPlaying()
|
|
{
|
|
return isStarted && isOpen_ && isThreadRunning();
|
|
}
|
|
|
|
const String getLastError()
|
|
{
|
|
return lastError;
|
|
}
|
|
|
|
//==============================================================================
|
|
juce_UseDebuggingNewOperator
|
|
|
|
StringArray inChannels, outChannels;
|
|
int outputDeviceIndex, inputDeviceIndex;
|
|
|
|
private:
|
|
bool isOpen_;
|
|
bool isStarted;
|
|
String lastError;
|
|
|
|
OwnedArray <DSoundInternalInChannel> inChans;
|
|
OwnedArray <DSoundInternalOutChannel> outChans;
|
|
WaitableEvent startEvent;
|
|
|
|
int numInputBuffers, numOutputBuffers, bufferSizeSamples;
|
|
int volatile totalSamplesOut;
|
|
int64 volatile lastBlockTime;
|
|
double sampleRate;
|
|
BitArray enabledInputs, enabledOutputs;
|
|
float** inputBuffers;
|
|
float** outputBuffers;
|
|
|
|
AudioIODeviceCallback* callback;
|
|
CriticalSection startStopLock;
|
|
|
|
DSoundAudioIODevice (const DSoundAudioIODevice&);
|
|
const DSoundAudioIODevice& operator= (const DSoundAudioIODevice&);
|
|
|
|
const String openDevice (const BitArray& inputChannels,
|
|
const BitArray& outputChannels,
|
|
double sampleRate_,
|
|
int bufferSizeSamples_);
|
|
|
|
void closeDevice()
|
|
{
|
|
isStarted = false;
|
|
stopThread (5000);
|
|
|
|
inChans.clear();
|
|
outChans.clear();
|
|
|
|
int i;
|
|
for (i = 0; i < numInputBuffers; ++i)
|
|
juce_free (inputBuffers[i]);
|
|
|
|
delete[] inputBuffers;
|
|
inputBuffers = 0;
|
|
numInputBuffers = 0;
|
|
|
|
for (i = 0; i < numOutputBuffers; ++i)
|
|
juce_free (outputBuffers[i]);
|
|
|
|
delete[] outputBuffers;
|
|
outputBuffers = 0;
|
|
numOutputBuffers = 0;
|
|
}
|
|
|
|
void resync()
|
|
{
|
|
if (! threadShouldExit())
|
|
{
|
|
sleep (5);
|
|
|
|
int i;
|
|
for (i = 0; i < outChans.size(); ++i)
|
|
outChans.getUnchecked(i)->synchronisePosition();
|
|
|
|
for (i = 0; i < inChans.size(); ++i)
|
|
inChans.getUnchecked(i)->synchronisePosition();
|
|
}
|
|
}
|
|
|
|
public:
|
|
void run()
|
|
{
|
|
while (! threadShouldExit())
|
|
{
|
|
if (wait (100))
|
|
break;
|
|
}
|
|
|
|
const int latencyMs = (int) (bufferSizeSamples * 1000.0 / sampleRate);
|
|
const int maxTimeMS = jmax (5, 3 * latencyMs);
|
|
|
|
while (! threadShouldExit())
|
|
{
|
|
int numToDo = 0;
|
|
uint32 startTime = Time::getMillisecondCounter();
|
|
|
|
int i;
|
|
for (i = inChans.size(); --i >= 0;)
|
|
{
|
|
inChans.getUnchecked(i)->doneFlag = false;
|
|
++numToDo;
|
|
}
|
|
|
|
for (i = outChans.size(); --i >= 0;)
|
|
{
|
|
outChans.getUnchecked(i)->doneFlag = false;
|
|
++numToDo;
|
|
}
|
|
|
|
if (numToDo > 0)
|
|
{
|
|
const int maxCount = 3;
|
|
int count = maxCount;
|
|
|
|
for (;;)
|
|
{
|
|
for (i = inChans.size(); --i >= 0;)
|
|
{
|
|
DSoundInternalInChannel* const in = inChans.getUnchecked(i);
|
|
|
|
if ((! in->doneFlag) && in->service())
|
|
{
|
|
in->doneFlag = true;
|
|
--numToDo;
|
|
}
|
|
}
|
|
|
|
for (i = outChans.size(); --i >= 0;)
|
|
{
|
|
DSoundInternalOutChannel* const out = outChans.getUnchecked(i);
|
|
|
|
if ((! out->doneFlag) && out->service())
|
|
{
|
|
out->doneFlag = true;
|
|
--numToDo;
|
|
}
|
|
}
|
|
|
|
if (numToDo <= 0)
|
|
break;
|
|
|
|
if (Time::getMillisecondCounter() > startTime + maxTimeMS)
|
|
{
|
|
resync();
|
|
break;
|
|
}
|
|
|
|
if (--count <= 0)
|
|
{
|
|
Sleep (1);
|
|
count = maxCount;
|
|
}
|
|
|
|
if (threadShouldExit())
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sleep (1);
|
|
}
|
|
|
|
const ScopedLock sl (startStopLock);
|
|
|
|
if (isStarted)
|
|
{
|
|
JUCE_TRY
|
|
{
|
|
callback->audioDeviceIOCallback ((const float**) inputBuffers,
|
|
numInputBuffers,
|
|
outputBuffers,
|
|
numOutputBuffers,
|
|
bufferSizeSamples);
|
|
}
|
|
JUCE_CATCH_EXCEPTION
|
|
|
|
totalSamplesOut += bufferSizeSamples;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < numOutputBuffers; ++i)
|
|
if (outputBuffers[i] != 0)
|
|
zeromem (outputBuffers[i], bufferSizeSamples * sizeof (float));
|
|
|
|
totalSamplesOut = 0;
|
|
sleep (1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//==============================================================================
|
|
class DSoundAudioIODeviceType : public AudioIODeviceType
|
|
{
|
|
public:
|
|
DSoundAudioIODeviceType()
|
|
: AudioIODeviceType (T("DirectSound")),
|
|
hasScanned (false)
|
|
{
|
|
initialiseDSoundFunctions();
|
|
}
|
|
|
|
~DSoundAudioIODeviceType()
|
|
{
|
|
}
|
|
|
|
//==============================================================================
|
|
void scanForDevices()
|
|
{
|
|
hasScanned = true;
|
|
|
|
outputDeviceNames.clear();
|
|
outputGuids.clear();
|
|
inputDeviceNames.clear();
|
|
inputGuids.clear();
|
|
|
|
if (dsDirectSoundEnumerateW != 0)
|
|
{
|
|
dsDirectSoundEnumerateW (outputEnumProcW, this);
|
|
dsDirectSoundCaptureEnumerateW (inputEnumProcW, this);
|
|
}
|
|
}
|
|
|
|
const StringArray getDeviceNames (const bool wantInputNames) const
|
|
{
|
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
|
|
|
return wantInputNames ? inputDeviceNames
|
|
: outputDeviceNames;
|
|
}
|
|
|
|
int getDefaultDeviceIndex (const bool /*forInput*/) const
|
|
{
|
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
|
return 0;
|
|
}
|
|
|
|
int getIndexOfDevice (AudioIODevice* device, const bool asInput) const
|
|
{
|
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
|
|
|
DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device);
|
|
if (d == 0)
|
|
return -1;
|
|
|
|
return asInput ? d->inputDeviceIndex
|
|
: d->outputDeviceIndex;
|
|
}
|
|
|
|
bool hasSeparateInputsAndOutputs() const { return true; }
|
|
|
|
AudioIODevice* createDevice (const String& outputDeviceName,
|
|
const String& inputDeviceName)
|
|
{
|
|
jassert (hasScanned); // need to call scanForDevices() before doing this
|
|
|
|
const int outputIndex = outputDeviceNames.indexOf (outputDeviceName);
|
|
const int inputIndex = inputDeviceNames.indexOf (inputDeviceName);
|
|
|
|
if (outputIndex >= 0 || inputIndex >= 0)
|
|
return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
|
|
: inputDeviceName,
|
|
outputIndex, inputIndex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//==============================================================================
|
|
juce_UseDebuggingNewOperator
|
|
|
|
StringArray outputDeviceNames;
|
|
OwnedArray <GUID> outputGuids;
|
|
|
|
StringArray inputDeviceNames;
|
|
OwnedArray <GUID> inputGuids;
|
|
|
|
private:
|
|
bool hasScanned;
|
|
|
|
//==============================================================================
|
|
BOOL outputEnumProc (LPGUID lpGUID, String desc)
|
|
{
|
|
desc = desc.trim();
|
|
|
|
if (desc.isNotEmpty())
|
|
{
|
|
const String origDesc (desc);
|
|
|
|
int n = 2;
|
|
while (outputDeviceNames.contains (desc))
|
|
desc = origDesc + T(" (") + String (n++) + T(")");
|
|
|
|
outputDeviceNames.add (desc);
|
|
|
|
if (lpGUID != 0)
|
|
outputGuids.add (new GUID (*lpGUID));
|
|
else
|
|
outputGuids.add (0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
|
|
{
|
|
return ((DSoundAudioIODeviceType*) object)
|
|
->outputEnumProc (lpGUID, String (description));
|
|
}
|
|
|
|
static BOOL CALLBACK outputEnumProcA (LPGUID lpGUID, LPCTSTR description, LPCTSTR, LPVOID object)
|
|
{
|
|
return ((DSoundAudioIODeviceType*) object)
|
|
->outputEnumProc (lpGUID, String (description));
|
|
}
|
|
|
|
//==============================================================================
|
|
BOOL CALLBACK inputEnumProc (LPGUID lpGUID, String desc)
|
|
{
|
|
desc = desc.trim();
|
|
|
|
if (desc.isNotEmpty())
|
|
{
|
|
const String origDesc (desc);
|
|
|
|
int n = 2;
|
|
while (inputDeviceNames.contains (desc))
|
|
desc = origDesc + T(" (") + String (n++) + T(")");
|
|
|
|
inputDeviceNames.add (desc);
|
|
|
|
if (lpGUID != 0)
|
|
inputGuids.add (new GUID (*lpGUID));
|
|
else
|
|
inputGuids.add (0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
|
|
{
|
|
return ((DSoundAudioIODeviceType*) object)
|
|
->inputEnumProc (lpGUID, String (description));
|
|
}
|
|
|
|
static BOOL CALLBACK inputEnumProcA (LPGUID lpGUID, LPCTSTR description, LPCTSTR, LPVOID object)
|
|
{
|
|
return ((DSoundAudioIODeviceType*) object)
|
|
->inputEnumProc (lpGUID, String (description));
|
|
}
|
|
|
|
//==============================================================================
|
|
DSoundAudioIODeviceType (const DSoundAudioIODeviceType&);
|
|
const DSoundAudioIODeviceType& operator= (const DSoundAudioIODeviceType&);
|
|
};
|
|
|
|
//==============================================================================
|
|
const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels,
|
|
const BitArray& outputChannels,
|
|
double sampleRate_,
|
|
int bufferSizeSamples_)
|
|
{
|
|
closeDevice();
|
|
totalSamplesOut = 0;
|
|
|
|
sampleRate = sampleRate_;
|
|
|
|
if (bufferSizeSamples_ <= 0)
|
|
bufferSizeSamples_ = 960; // use as a default size if none is set.
|
|
|
|
bufferSizeSamples = bufferSizeSamples_ & ~7;
|
|
|
|
DSoundAudioIODeviceType dlh;
|
|
dlh.scanForDevices();
|
|
|
|
enabledInputs = inputChannels;
|
|
enabledInputs.setRange (inChannels.size(),
|
|
enabledInputs.getHighestBit() + 1 - inChannels.size(),
|
|
false);
|
|
|
|
numInputBuffers = enabledInputs.countNumberOfSetBits();
|
|
inputBuffers = new float* [numInputBuffers + 2];
|
|
zeromem (inputBuffers, sizeof (float*) * numInputBuffers + 2);
|
|
int i, numIns = 0;
|
|
|
|
for (i = 0; i <= enabledInputs.getHighestBit(); i += 2)
|
|
{
|
|
float* left = 0;
|
|
float* right = 0;
|
|
|
|
if (enabledInputs[i])
|
|
left = inputBuffers[numIns++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
|
|
|
|
if (enabledInputs[i + 1])
|
|
right = inputBuffers[numIns++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
|
|
|
|
if (left != 0 || right != 0)
|
|
inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex],
|
|
dlh.inputGuids [inputDeviceIndex],
|
|
(int) sampleRate, bufferSizeSamples,
|
|
left, right));
|
|
}
|
|
|
|
enabledOutputs = outputChannels;
|
|
enabledOutputs.setRange (outChannels.size(),
|
|
enabledOutputs.getHighestBit() + 1 - outChannels.size(),
|
|
false);
|
|
|
|
numOutputBuffers = enabledOutputs.countNumberOfSetBits();
|
|
outputBuffers = new float* [numOutputBuffers + 2];
|
|
zeromem (outputBuffers, sizeof (float*) * numOutputBuffers + 2);
|
|
int numOuts = 0;
|
|
|
|
for (i = 0; i <= enabledOutputs.getHighestBit(); i += 2)
|
|
{
|
|
float* left = 0;
|
|
float* right = 0;
|
|
|
|
if (enabledOutputs[i])
|
|
left = outputBuffers[numOuts++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
|
|
|
|
if (enabledOutputs[i + 1])
|
|
right = outputBuffers[numOuts++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
|
|
|
|
if (left != 0 || right != 0)
|
|
outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex],
|
|
dlh.outputGuids [outputDeviceIndex],
|
|
(int) sampleRate, bufferSizeSamples,
|
|
left, right));
|
|
}
|
|
|
|
String error;
|
|
|
|
// boost our priority while opening the devices to try to get better sync between them
|
|
const int oldThreadPri = GetThreadPriority (GetCurrentThread());
|
|
const int oldProcPri = GetPriorityClass (GetCurrentProcess());
|
|
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
|
SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
|
|
|
for (i = 0; i < outChans.size(); ++i)
|
|
{
|
|
error = outChans[i]->open();
|
|
|
|
if (error.isNotEmpty())
|
|
{
|
|
error = T("Error opening ") + dlh.outputDeviceNames[i]
|
|
+ T(": \"") + error + T("\"");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (error.isEmpty())
|
|
{
|
|
for (i = 0; i < inChans.size(); ++i)
|
|
{
|
|
error = inChans[i]->open();
|
|
|
|
if (error.isNotEmpty())
|
|
{
|
|
error = T("Error opening ") + dlh.inputDeviceNames[i]
|
|
+ T(": \"") + error + T("\"");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (error.isEmpty())
|
|
{
|
|
totalSamplesOut = 0;
|
|
|
|
for (i = 0; i < outChans.size(); ++i)
|
|
outChans.getUnchecked(i)->synchronisePosition();
|
|
|
|
for (i = 0; i < inChans.size(); ++i)
|
|
inChans.getUnchecked(i)->synchronisePosition();
|
|
|
|
startThread (9);
|
|
sleep (10);
|
|
|
|
notify();
|
|
}
|
|
else
|
|
{
|
|
log (error);
|
|
}
|
|
|
|
SetThreadPriority (GetCurrentThread(), oldThreadPri);
|
|
SetPriorityClass (GetCurrentProcess(), oldProcPri);
|
|
|
|
return error;
|
|
}
|
|
|
|
//==============================================================================
|
|
AudioIODeviceType* juce_createAudioIODeviceType_DirectSound()
|
|
{
|
|
return new DSoundAudioIODeviceType();
|
|
}
|
|
|
|
|
|
#undef log
|
|
|
|
#endif
|