|
- /*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-7 by Raw Material Software ltd.
-
- ------------------------------------------------------------------------------
-
- JUCE can be redistributed and/or modified under the terms of the
- GNU General Public License, as published by the Free Software Foundation;
- either version 2 of the License, or (at your option) any later version.
-
- 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.
-
- You should have received a copy of the GNU General Public License
- along with JUCE; if not, visit www.gnu.org/licenses or write to the
- Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- Boston, MA 02111-1307 USA
-
- ------------------------------------------------------------------------------
-
- If you'd like to release a closed-source product which uses JUCE, commercial
- licenses are also available: visit www.rawmaterialsoftware.com/juce for
- more information.
-
- ==============================================================================
- */
-
- #include "win32_headers.h"
- #include "../../../src/juce_core/basics/juce_StandardHeader.h"
-
- //==============================================================================
- 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
-
- #include "../../../src/juce_appframework/audio/devices/juce_AudioIODeviceType.h"
- #include "../../../src/juce_appframework/application/juce_Application.h"
- #include "../../../src/juce_core/threads/juce_Thread.h"
- #include "../../../src/juce_core/basics/juce_Singleton.h"
- #include "../../../src/juce_core/basics/juce_Time.h"
- #include "../../../src/juce_core/containers/juce_OwnedArray.h"
- #include "../../../src/juce_core/text/juce_LocalisedStrings.h"
-
-
- 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(), 3 /* DSSCL_EXCLUSIVE */);
- 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;
- HRESULT hr = pOutputBuffer->GetCurrentPosition (&playCursor, &writeCursor);
-
- if (hr != S_OK)
- {
- 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 == 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;
- }
- }
- };
-
- //==============================================================================
- static int findBestMatchForName (const String& name, const StringArray& names)
- {
- int i = names.indexOf (name);
-
- if (i >= 0)
- return i;
-
- StringArray tokens1;
- tokens1.addTokens (name, T(" :-"), 0);
-
- int bestResult = -1;
- int bestNumMatches = 1;
-
- for (i = 0; i < names.size(); ++i)
- {
- StringArray tokens2;
- tokens2.addTokens (names[i], T(" :-"), 0);
-
- int matches = 0;
-
- for (int j = tokens1.size(); --j >= 0;)
- if (tokens2.contains (tokens1 [j]))
- ++matches;
-
- if (matches > bestNumMatches)
- bestResult = i;
- }
-
- return bestResult;
- }
-
- class DSoundAudioIODevice : public AudioIODevice,
- public Thread
- {
- public:
- DSoundAudioIODevice (const String& deviceName,
- const int index,
- const int inputIndex_)
- : AudioIODevice (deviceName, "DirectSound"),
- Thread ("Juce DSound"),
- isOpen_ (false),
- isStarted (false),
- deviceIndex (index),
- inputIndex (inputIndex_),
- inChans (4),
- outChans (4),
- numInputBuffers (0),
- numOutputBuffers (0),
- totalSamplesOut (0),
- sampleRate (0.0),
- inputBuffers (0),
- outputBuffers (0),
- callback (0)
- {
- }
-
- ~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)
- {
- BitArray ins, outs;
-
- if (deviceIndex >= 0)
- {
- if (outputChannels[0])
- outs.setBit (2 * deviceIndex);
-
- if (outputChannels[1])
- outs.setBit (2 * deviceIndex + 1);
-
- if (inputIndex >= 0)
- {
- if (inputChannels[0])
- ins.setBit (2 * inputIndex);
-
- if (inputChannels[1])
- ins.setBit (2 * inputIndex + 1);
- }
- }
- else
- {
- ins = inputChannels;
- outs = outputChannels;
- }
-
- lastError = openDevice (ins, outs, 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;
-
- private:
- bool isOpen_;
- bool isStarted;
- String lastError;
-
- int deviceIndex, inputIndex;
- 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()
- {
- int i;
- for (i = outChans.size(); --i >= 0;)
- outChans.getUnchecked(i)->close();
-
- for (i = inChans.size(); --i >= 0;)
- inChans.getUnchecked(i)->close();
-
- if (threadShouldExit())
- return;
-
- // 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 = outChans.size(); --i >= 0;)
- outChans.getUnchecked(i)->open();
-
- for (i = inChans.size(); --i >= 0;)
- inChans.getUnchecked(i)->open();
-
- if (! threadShouldExit())
- {
- sleep (5);
-
- for (i = 0; i < outChans.size(); ++i)
- outChans.getUnchecked(i)->synchronisePosition();
-
- for (i = 0; i < inChans.size(); ++i)
- inChans.getUnchecked(i)->synchronisePosition();
- }
-
- SetThreadPriority (GetCurrentThread(), oldThreadPri);
- SetPriorityClass (GetCurrentProcess(), oldProcPri);
- }
-
- 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 preferInputNames) const
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
-
- return preferInputNames ? inputDeviceNames
- : outputDeviceNames;
- }
-
- const String getDefaultDeviceName (const bool preferInputNames,
- const int /*numInputChannelsNeeded*/,
- const int /*numOutputChannelsNeeded*/) const
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
-
- return getDeviceNames (preferInputNames) [0];
- }
-
- AudioIODevice* createDevice (const String& deviceName)
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
-
- if (deviceName.isEmpty() || deviceName.equalsIgnoreCase (T("DirectSound")))
- {
- DSoundAudioIODevice* device = new DSoundAudioIODevice (deviceName, -1, -1);
-
- int i;
- for (i = 0; i < outputDeviceNames.size(); ++i)
- {
- device->outChannels.add (outputDeviceNames[i] + TRANS(" (left)"));
- device->outChannels.add (outputDeviceNames[i] + TRANS(" (right)"));
- }
-
- for (i = 0; i < inputDeviceNames.size(); ++i)
- {
- device->inChannels.add (inputDeviceNames[i] + TRANS(" (left)"));
- device->inChannels.add (inputDeviceNames[i] + TRANS(" (right)"));
- }
-
- return device;
- }
- else if (outputDeviceNames.contains (deviceName)
- || inputDeviceNames.contains (deviceName))
- {
- int outputIndex = outputDeviceNames.indexOf (deviceName);
- int inputIndex = findBestMatchForName (deviceName, inputDeviceNames);
-
- if (outputIndex < 0)
- {
- // using an input device name instead..
- inputIndex = inputDeviceNames.indexOf (deviceName);
- outputIndex = jmax (0, findBestMatchForName (deviceName, outputDeviceNames));
- }
-
- DSoundAudioIODevice* device = new DSoundAudioIODevice (deviceName, outputIndex, inputIndex);
-
- device->outChannels.add (TRANS("Left"));
- device->outChannels.add (TRANS("Right"));
-
- if (inputIndex >= 0)
- {
- device->inChannels.add (TRANS("Left"));
- device->inChannels.add (TRANS("Right"));
- }
-
- return device;
- }
-
- 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&);
- };
-
- //==============================================================================
- AudioIODeviceType* juce_createDefaultAudioIODeviceType()
- {
- return new DSoundAudioIODeviceType();
- }
-
- //==============================================================================
- const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels,
- const BitArray& outputChannels,
- double sampleRate_,
- int bufferSizeSamples_)
- {
- closeDevice();
- totalSamplesOut = 0;
- enabledInputs.clear();
- enabledOutputs.clear();
-
- 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;
- numInputBuffers = inputChannels.countNumberOfSetBits();
- inputBuffers = new float* [numInputBuffers + 2];
- zeromem (inputBuffers, sizeof (inputBuffers));
- int i, numIns = 0;
-
- for (i = 0; i < inputChannels.getHighestBit(); i += 2)
- {
- float* left = 0;
- float* right = 0;
-
- if (inputChannels[i])
- left = inputBuffers[numIns++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
-
- if (inputChannels[i + 1])
- right = inputBuffers[numIns++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
-
- if (left != 0 || right != 0)
- inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [i / 2],
- dlh.inputGuids [i / 2],
- (int) sampleRate, bufferSizeSamples,
- left, right));
- }
-
- enabledOutputs = outputChannels;
- numOutputBuffers = outputChannels.countNumberOfSetBits();
- outputBuffers = new float* [numOutputBuffers + 2];
- zeromem (outputBuffers, sizeof (outputBuffers));
- int numOuts = 0;
-
- for (i = 0; i < outputChannels.getHighestBit(); i += 2)
- {
- float* left = 0;
- float* right = 0;
-
- if (inputChannels[i])
- left = outputBuffers[numOuts++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
-
- if (inputChannels[i + 1])
- right = outputBuffers[numOuts++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
-
- if (left != 0 || right != 0)
- outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[i / 2],
- dlh.outputGuids [i / 2],
- (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;
- }
-
- #undef log
-
- END_JUCE_NAMESPACE
|