|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2015 - ROLI Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found 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.juce.com for more information.
-
- ==============================================================================
- */
-
- } // (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;
- };
-
- #undef INTERFACE
- }
-
- namespace juce
- {
-
- //==============================================================================
- namespace DSoundLogging
- {
- String getErrorMessage (HRESULT hr)
- {
- const char* result = nullptr;
-
- 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;
- }
-
- //==============================================================================
- #if JUCE_DIRECTSOUND_LOGGING
- static void logMessage (String message)
- {
- message = "DSOUND: " + message;
- DBG (message);
- Logger::writeToLog (message);
- }
-
- static void logError (HRESULT hr, int lineNum)
- {
- if (FAILED (hr))
- {
- String error ("Error at line ");
- error << lineNum << ": " << getErrorMessage (hr);
- logMessage (error);
- }
- }
-
- #define JUCE_DS_LOG(a) DSoundLogging::logMessage(a);
- #define JUCE_DS_LOG_ERROR(a) DSoundLogging::logError(a, __LINE__);
- #else
- #define JUCE_DS_LOG(a)
- #define JUCE_DS_LOG_ERROR(a)
- #endif
- }
-
- //==============================================================================
- namespace
- {
- #define DSOUND_FUNCTION(functionName, params) \
- typedef HRESULT (WINAPI *type##functionName) params; \
- static type##functionName ds##functionName = nullptr;
-
- #define DSOUND_FUNCTION_LOAD(functionName) \
- ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \
- jassert (ds##functionName != nullptr);
-
- 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))
-
- void initialiseDSoundFunctions()
- {
- if (dsDirectSoundCreate == nullptr)
- {
- HMODULE h = LoadLibraryA ("dsound.dll");
-
- DSOUND_FUNCTION_LOAD (DirectSoundCreate)
- DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate)
- DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW)
- DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW)
- }
- }
-
- // the overall size of buffer used is this value x the block size
- enum { blocksPerOverallBuffer = 16 };
- }
-
- //==============================================================================
- class DSoundInternalOutChannel
- {
- public:
- DSoundInternalOutChannel (const String& name_, const GUID& guid_, int rate,
- int bufferSize, float* left, float* right)
- : bitDepth (16), name (name_), guid (guid_), sampleRate (rate),
- bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right),
- pDirectSound (nullptr), pOutputBuffer (nullptr)
- {
- }
-
- ~DSoundInternalOutChannel()
- {
- close();
- }
-
- void close()
- {
- if (pOutputBuffer != nullptr)
- {
- JUCE_DS_LOG ("closing output: " + name);
- HRESULT hr = pOutputBuffer->Stop();
- JUCE_DS_LOG_ERROR (hr); ignoreUnused (hr);
-
- pOutputBuffer->Release();
- pOutputBuffer = nullptr;
- }
-
- if (pDirectSound != nullptr)
- {
- pDirectSound->Release();
- pDirectSound = nullptr;
- }
- }
-
- String open()
- {
- JUCE_DS_LOG ("opening output: " + name + " rate=" + String (sampleRate)
- + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
-
- pDirectSound = nullptr;
- pOutputBuffer = nullptr;
- writeOffset = 0;
-
- String error;
- HRESULT hr = E_NOINTERFACE;
-
- if (dsDirectSoundCreate != nullptr)
- hr = dsDirectSoundCreate (&guid, &pDirectSound, nullptr);
-
- if (SUCCEEDED (hr))
- {
- bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
- totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15;
- const int numChannels = 2;
-
- hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */);
- JUCE_DS_LOG_ERROR (hr);
-
- if (SUCCEEDED (hr))
- {
- IDirectSoundBuffer* pPrimaryBuffer;
-
- DSBUFFERDESC primaryDesc = { 0 };
- primaryDesc.dwSize = sizeof (DSBUFFERDESC);
- primaryDesc.dwFlags = 1 /* DSBCAPS_PRIMARYBUFFER */;
- primaryDesc.dwBufferBytes = 0;
- primaryDesc.lpwfxFormat = 0;
-
- JUCE_DS_LOG ("co-op level set");
- hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0);
- JUCE_DS_LOG_ERROR (hr);
-
- if (SUCCEEDED (hr))
- {
- WAVEFORMATEX wfFormat;
- wfFormat.wFormatTag = WAVE_FORMAT_PCM;
- wfFormat.nChannels = (unsigned short) numChannels;
- wfFormat.nSamplesPerSec = (DWORD) 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);
- JUCE_DS_LOG_ERROR (hr);
-
- if (SUCCEEDED (hr))
- {
- DSBUFFERDESC secondaryDesc = { 0 };
- secondaryDesc.dwSize = sizeof (DSBUFFERDESC);
- secondaryDesc.dwFlags = 0x8000 /* DSBCAPS_GLOBALFOCUS */
- | 0x10000 /* DSBCAPS_GETCURRENTPOSITION2 */;
- secondaryDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer;
- secondaryDesc.lpwfxFormat = &wfFormat;
-
- hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0);
- JUCE_DS_LOG_ERROR (hr);
-
- if (SUCCEEDED (hr))
- {
- JUCE_DS_LOG ("buffer created");
-
- DWORD dwDataLen;
- unsigned char* pDSBuffData;
-
- hr = pOutputBuffer->Lock (0, (DWORD) totalBytesPerBuffer,
- (LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0);
- JUCE_DS_LOG_ERROR (hr);
-
- if (SUCCEEDED (hr))
- {
- zeromem (pDSBuffData, dwDataLen);
-
- hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0);
-
- if (SUCCEEDED (hr))
- {
- hr = pOutputBuffer->SetCurrentPosition (0);
-
- if (SUCCEEDED (hr))
- {
- hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */);
-
- if (SUCCEEDED (hr))
- return String();
- }
- }
- }
- }
- }
- }
- }
- }
-
- error = DSoundLogging::getErrorMessage (hr);
- close();
- return error;
- }
-
- void synchronisePosition()
- {
- if (pOutputBuffer != nullptr)
- {
- 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 (SUCCEEDED (hr))
- break;
-
- JUCE_DS_LOG_ERROR (hr);
- jassertfalse;
- return true;
- }
-
- int playWriteGap = (int) (writeCursor - playCursor);
- if (playWriteGap < 0)
- playWriteGap += totalBytesPerBuffer;
-
- int bytesEmpty = (int) (playCursor - writeOffset);
- if (bytesEmpty < 0)
- bytesEmpty += totalBytesPerBuffer;
-
- if (bytesEmpty > (totalBytesPerBuffer - playWriteGap))
- {
- writeOffset = writeCursor;
- bytesEmpty = totalBytesPerBuffer - playWriteGap;
- }
-
- if (bytesEmpty >= bytesPerBuffer)
- {
- int* buf1 = nullptr;
- int* buf2 = nullptr;
- DWORD dwSize1 = 0;
- DWORD dwSize2 = 0;
-
- HRESULT hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer,
- (void**) &buf1, &dwSize1,
- (void**) &buf2, &dwSize2, 0);
-
- if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
- {
- pOutputBuffer->Restore();
-
- hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer,
- (void**) &buf1, &dwSize1,
- (void**) &buf2, &dwSize2, 0);
- }
-
- if (SUCCEEDED (hr))
- {
- if (bitDepth == 16)
- {
- const float* left = leftBuffer;
- const float* right = rightBuffer;
- int samples1 = (int) (dwSize1 >> 2);
- int samples2 = (int) (dwSize2 >> 2);
-
- if (left == nullptr)
- {
- for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (0, *right++);
- for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (0, *right++);
- }
- else if (right == nullptr)
- {
- for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, 0);
- for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, 0);
- }
- else
- {
- for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, *right++);
- for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, *right++);
- }
- }
- else
- {
- jassertfalse;
- }
-
- writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer;
-
- pOutputBuffer->Unlock (buf1, dwSize1, buf2, dwSize2);
- }
- else
- {
- jassertfalse;
- JUCE_DS_LOG_ERROR (hr);
- }
-
- bytesEmpty -= bytesPerBuffer;
- return true;
- }
- else
- {
- return false;
- }
- }
-
- int bitDepth;
- bool doneFlag;
-
- private:
- String name;
- GUID guid;
- int sampleRate, bufferSizeSamples;
- float* leftBuffer;
- float* rightBuffer;
-
- IDirectSound* pDirectSound;
- IDirectSoundBuffer* pOutputBuffer;
- DWORD writeOffset;
- int totalBytesPerBuffer, bytesPerBuffer;
- unsigned int lastPlayCursor;
-
- static inline int convertInputValues (const float l, const float r) noexcept
- {
- return jlimit (-32768, 32767, roundToInt (32767.0f * r)) << 16
- | (0xffff & jlimit (-32768, 32767, roundToInt (32767.0f * l)));
- }
-
- JUCE_DECLARE_NON_COPYABLE (DSoundInternalOutChannel)
- };
-
- //==============================================================================
- struct DSoundInternalInChannel
- {
- public:
- DSoundInternalInChannel (const String& name_, const GUID& guid_, int rate,
- int bufferSize, float* left, float* right)
- : bitDepth (16), name (name_), guid (guid_), sampleRate (rate),
- bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right),
- pDirectSound (nullptr), pDirectSoundCapture (nullptr), pInputBuffer (nullptr)
- {
- }
-
- ~DSoundInternalInChannel()
- {
- close();
- }
-
- void close()
- {
- if (pInputBuffer != nullptr)
- {
- JUCE_DS_LOG ("closing input: " + name);
- HRESULT hr = pInputBuffer->Stop();
- JUCE_DS_LOG_ERROR (hr); ignoreUnused (hr);
-
- pInputBuffer->Release();
- pInputBuffer = nullptr;
- }
-
- if (pDirectSoundCapture != nullptr)
- {
- pDirectSoundCapture->Release();
- pDirectSoundCapture = nullptr;
- }
-
- if (pDirectSound != nullptr)
- {
- pDirectSound->Release();
- pDirectSound = nullptr;
- }
- }
-
- String open()
- {
- JUCE_DS_LOG ("opening input: " + name
- + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
-
- pDirectSound = nullptr;
- pDirectSoundCapture = nullptr;
- pInputBuffer = nullptr;
- readOffset = 0;
- totalBytesPerBuffer = 0;
-
- HRESULT hr = dsDirectSoundCaptureCreate != nullptr
- ? dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, nullptr)
- : E_NOINTERFACE;
-
- if (SUCCEEDED (hr))
- {
- const int numChannels = 2;
- bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
- totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15;
-
- WAVEFORMATEX wfFormat;
- wfFormat.wFormatTag = WAVE_FORMAT_PCM;
- wfFormat.nChannels = (unsigned short)numChannels;
- wfFormat.nSamplesPerSec = (DWORD) 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 = { 0 };
- captureDesc.dwSize = sizeof (DSCBUFFERDESC);
- captureDesc.dwFlags = 0;
- captureDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer;
- captureDesc.lpwfxFormat = &wfFormat;
-
- JUCE_DS_LOG ("object created");
- hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0);
-
- if (SUCCEEDED (hr))
- {
- hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */);
-
- if (SUCCEEDED (hr))
- return String();
- }
- }
-
- JUCE_DS_LOG_ERROR (hr);
- const String error (DSoundLogging::getErrorMessage (hr));
- close();
-
- return error;
- }
-
- void synchronisePosition()
- {
- if (pInputBuffer != nullptr)
- {
- DWORD capturePos;
- pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*) &readOffset);
- }
- }
-
- bool service()
- {
- if (pInputBuffer == 0)
- return true;
-
- DWORD capturePos, readPos;
- HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos);
- JUCE_DS_LOG_ERROR (hr);
-
- if (FAILED (hr))
- return true;
-
- int bytesFilled = (int) (readPos - readOffset);
- if (bytesFilled < 0)
- bytesFilled += totalBytesPerBuffer;
-
- if (bytesFilled >= bytesPerBuffer)
- {
- short* buf1 = nullptr;
- short* buf2 = nullptr;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
-
- hr = pInputBuffer->Lock ((DWORD) readOffset, (DWORD) bytesPerBuffer,
- (void**) &buf1, &dwsize1,
- (void**) &buf2, &dwsize2, 0);
-
- if (SUCCEEDED (hr))
- {
- if (bitDepth == 16)
- {
- const float g = 1.0f / 32768.0f;
-
- float* destL = leftBuffer;
- float* destR = rightBuffer;
- int samples1 = (int) (dwsize1 >> 2);
- int samples2 = (int) (dwsize2 >> 2);
-
- if (destL == nullptr)
- {
- for (const short* src = buf1; --samples1 >= 0;) { ++src; *destR++ = *src++ * g; }
- for (const short* src = buf2; --samples2 >= 0;) { ++src; *destR++ = *src++ * g; }
- }
- else if (destR == nullptr)
- {
- for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; ++src; }
- for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; ++src; }
- }
- else
- {
- for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; }
- for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; }
- }
- }
- else
- {
- jassertfalse;
- }
-
- readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer;
-
- pInputBuffer->Unlock (buf1, dwsize1, buf2, dwsize2);
- }
- else
- {
- JUCE_DS_LOG_ERROR (hr);
- jassertfalse;
- }
-
- bytesFilled -= bytesPerBuffer;
-
- return true;
- }
- else
- {
- return false;
- }
- }
-
- unsigned int readOffset;
- int bytesPerBuffer, totalBytesPerBuffer;
- int bitDepth;
- bool doneFlag;
-
- private:
- String name;
- GUID guid;
- int sampleRate, bufferSizeSamples;
- float* leftBuffer;
- float* rightBuffer;
-
- IDirectSound* pDirectSound;
- IDirectSoundCapture* pDirectSoundCapture;
- IDirectSoundCaptureBuffer* pInputBuffer;
-
- JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel)
- };
-
- //==============================================================================
- class DSoundAudioIODevice : public AudioIODevice,
- public Thread
- {
- public:
- DSoundAudioIODevice (const String& deviceName,
- const int outputDeviceIndex_,
- const int inputDeviceIndex_)
- : AudioIODevice (deviceName, "DirectSound"),
- Thread ("Juce DSound"),
- outputDeviceIndex (outputDeviceIndex_),
- inputDeviceIndex (inputDeviceIndex_),
- isOpen_ (false),
- isStarted (false),
- bufferSizeSamples (0),
- sampleRate (0.0),
- callback (nullptr)
- {
- if (outputDeviceIndex_ >= 0)
- {
- outChannels.add (TRANS("Left"));
- outChannels.add (TRANS("Right"));
- }
-
- if (inputDeviceIndex_ >= 0)
- {
- inChannels.add (TRANS("Left"));
- inChannels.add (TRANS("Right"));
- }
- }
-
- ~DSoundAudioIODevice()
- {
- close();
- }
-
- String open (const BigInteger& inputChannels,
- const BigInteger& outputChannels,
- double newSampleRate, int newBufferSize) override
- {
- lastError = openDevice (inputChannels, outputChannels, newSampleRate, newBufferSize);
- isOpen_ = lastError.isEmpty();
-
- return lastError;
- }
-
- void close() override
- {
- stop();
-
- if (isOpen_)
- {
- closeDevice();
- isOpen_ = false;
- }
- }
-
- bool isOpen() override { return isOpen_ && isThreadRunning(); }
- int getCurrentBufferSizeSamples() override { return bufferSizeSamples; }
- double getCurrentSampleRate() override { return sampleRate; }
- BigInteger getActiveOutputChannels() const override { return enabledOutputs; }
- BigInteger getActiveInputChannels() const override { return enabledInputs; }
- int getOutputLatencyInSamples() override { return (int) (getCurrentBufferSizeSamples() * 1.5); }
- int getInputLatencyInSamples() override { return getOutputLatencyInSamples(); }
- StringArray getOutputChannelNames() override { return outChannels; }
- StringArray getInputChannelNames() override { return inChannels; }
-
- Array<double> getAvailableSampleRates() override
- {
- static const double rates[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
- return Array<double> (rates, numElementsInArray (rates));
- }
-
- Array<int> getAvailableBufferSizes() override
- {
- Array<int> r;
- int n = 64;
-
- for (int i = 0; i < 50; ++i)
- {
- r.add (n);
- n += (n < 512) ? 32
- : ((n < 1024) ? 64
- : ((n < 2048) ? 128 : 256));
- }
-
- return r;
- }
-
- int getDefaultBufferSize() override { return 2560; }
-
- int getCurrentBitDepth() override
- {
- int bits = 256;
-
- for (int i = inChans.size(); --i >= 0;)
- bits = jmin (bits, inChans[i]->bitDepth);
-
- for (int i = outChans.size(); --i >= 0;)
- bits = jmin (bits, outChans[i]->bitDepth);
-
- if (bits > 32)
- bits = 16;
-
- return bits;
- }
-
- void start (AudioIODeviceCallback* call) override
- {
- if (isOpen_ && call != nullptr && ! 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() override
- {
- if (isStarted)
- {
- AudioIODeviceCallback* const callbackLocal = callback;
-
- {
- const ScopedLock sl (startStopLock);
- isStarted = false;
- }
-
- if (callbackLocal != nullptr)
- callbackLocal->audioDeviceStopped();
- }
- }
-
- bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); }
- String getLastError() override { return lastError; }
-
- //==============================================================================
- StringArray inChannels, outChannels;
- int outputDeviceIndex, inputDeviceIndex;
-
- private:
- bool isOpen_;
- bool isStarted;
- String lastError;
-
- OwnedArray<DSoundInternalInChannel> inChans;
- OwnedArray<DSoundInternalOutChannel> outChans;
- WaitableEvent startEvent;
-
- int bufferSizeSamples;
- double sampleRate;
- BigInteger enabledInputs, enabledOutputs;
- AudioSampleBuffer inputBuffers, outputBuffers;
-
- AudioIODeviceCallback* callback;
- CriticalSection startStopLock;
-
- String openDevice (const BigInteger& inputChannels,
- const BigInteger& outputChannels,
- double sampleRate_, int bufferSizeSamples_);
-
- void closeDevice()
- {
- isStarted = false;
- stopThread (5000);
-
- inChans.clear();
- outChans.clear();
- inputBuffers.setSize (1, 1);
- outputBuffers.setSize (1, 1);
- }
-
- void resync()
- {
- if (! threadShouldExit())
- {
- sleep (5);
-
- for (int i = 0; i < outChans.size(); ++i)
- outChans.getUnchecked(i)->synchronisePosition();
-
- for (int i = 0; i < inChans.size(); ++i)
- inChans.getUnchecked(i)->synchronisePosition();
- }
- }
-
- public:
- void run() override
- {
- 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();
-
- for (int i = inChans.size(); --i >= 0;)
- {
- inChans.getUnchecked(i)->doneFlag = false;
- ++numToDo;
- }
-
- for (int i = outChans.size(); --i >= 0;)
- {
- outChans.getUnchecked(i)->doneFlag = false;
- ++numToDo;
- }
-
- if (numToDo > 0)
- {
- const int maxCount = 3;
- int count = maxCount;
-
- for (;;)
- {
- for (int i = inChans.size(); --i >= 0;)
- {
- DSoundInternalInChannel* const in = inChans.getUnchecked(i);
-
- if ((! in->doneFlag) && in->service())
- {
- in->doneFlag = true;
- --numToDo;
- }
- }
-
- for (int 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)
- {
- callback->audioDeviceIOCallback (inputBuffers.getArrayOfReadPointers(), inputBuffers.getNumChannels(),
- outputBuffers.getArrayOfWritePointers(), outputBuffers.getNumChannels(),
- bufferSizeSamples);
- }
- else
- {
- outputBuffers.clear();
- sleep (1);
- }
- }
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice)
- };
-
- //==============================================================================
- struct DSoundDeviceList
- {
- StringArray outputDeviceNames, inputDeviceNames;
- Array<GUID> outputGuids, inputGuids;
-
- void scan()
- {
- outputDeviceNames.clear();
- inputDeviceNames.clear();
- outputGuids.clear();
- inputGuids.clear();
-
- if (dsDirectSoundEnumerateW != 0)
- {
- dsDirectSoundEnumerateW (outputEnumProcW, this);
- dsDirectSoundCaptureEnumerateW (inputEnumProcW, this);
- }
- }
-
- bool operator!= (const DSoundDeviceList& other) const noexcept
- {
- return outputDeviceNames != other.outputDeviceNames
- || inputDeviceNames != other.inputDeviceNames
- || outputGuids != other.outputGuids
- || inputGuids != other.inputGuids;
- }
-
- private:
- static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids)
- {
- desc = desc.trim();
-
- if (desc.isNotEmpty())
- {
- const String origDesc (desc);
-
- int n = 2;
- while (names.contains (desc))
- desc = origDesc + " (" + String (n++) + ")";
-
- names.add (desc);
- guids.add (lpGUID != nullptr ? *lpGUID : GUID());
- }
-
- return TRUE;
- }
-
- BOOL outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); }
- BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); }
-
- static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
- {
- return static_cast<DSoundDeviceList*> (object)->outputEnumProc (lpGUID, description);
- }
-
- static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
- {
- return static_cast<DSoundDeviceList*> (object)->inputEnumProc (lpGUID, description);
- }
- };
-
- //==============================================================================
- String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels,
- const BigInteger& outputChannels,
- double sampleRate_, int bufferSizeSamples_)
- {
- closeDevice();
-
- sampleRate = sampleRate_;
-
- if (bufferSizeSamples_ <= 0)
- bufferSizeSamples_ = 960; // use as a default size if none is set.
-
- bufferSizeSamples = bufferSizeSamples_ & ~7;
-
- DSoundDeviceList dlh;
- dlh.scan();
-
- enabledInputs = inputChannels;
- enabledInputs.setRange (inChannels.size(),
- enabledInputs.getHighestBit() + 1 - inChannels.size(),
- false);
-
- inputBuffers.setSize (jmax (1, enabledInputs.countNumberOfSetBits()), bufferSizeSamples);
- inputBuffers.clear();
- int numIns = 0;
-
- for (int i = 0; i <= enabledInputs.getHighestBit(); i += 2)
- {
- float* left = enabledInputs[i] ? inputBuffers.getWritePointer (numIns++) : nullptr;
- float* right = enabledInputs[i + 1] ? inputBuffers.getWritePointer (numIns++) : nullptr;
-
- if (left != nullptr || right != nullptr)
- 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);
-
- outputBuffers.setSize (jmax (1, enabledOutputs.countNumberOfSetBits()), bufferSizeSamples);
- outputBuffers.clear();
- int numOuts = 0;
-
- for (int i = 0; i <= enabledOutputs.getHighestBit(); i += 2)
- {
- float* left = enabledOutputs[i] ? outputBuffers.getWritePointer (numOuts++) : nullptr;
- float* right = enabledOutputs[i + 1] ? outputBuffers.getWritePointer (numOuts++) : nullptr;
-
- if (left != nullptr || right != nullptr)
- 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 DWORD oldProcPri = GetPriorityClass (GetCurrentProcess());
- SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
- SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
-
- for (int i = 0; i < outChans.size(); ++i)
- {
- error = outChans[i]->open();
-
- if (error.isNotEmpty())
- {
- error = "Error opening " + dlh.outputDeviceNames[i] + ": \"" + error + "\"";
- break;
- }
- }
-
- if (error.isEmpty())
- {
- for (int i = 0; i < inChans.size(); ++i)
- {
- error = inChans[i]->open();
-
- if (error.isNotEmpty())
- {
- error = "Error opening " + dlh.inputDeviceNames[i] + ": \"" + error + "\"";
- break;
- }
- }
- }
-
- if (error.isEmpty())
- {
- for (int i = 0; i < outChans.size(); ++i)
- outChans.getUnchecked(i)->synchronisePosition();
-
- for (int i = 0; i < inChans.size(); ++i)
- inChans.getUnchecked(i)->synchronisePosition();
-
- startThread (9);
- sleep (10);
-
- notify();
- }
- else
- {
- JUCE_DS_LOG ("Opening failed: " + error);
- }
-
- SetThreadPriority (GetCurrentThread(), oldThreadPri);
- SetPriorityClass (GetCurrentProcess(), oldProcPri);
-
- return error;
- }
-
- //==============================================================================
- class DSoundAudioIODeviceType : public AudioIODeviceType,
- private DeviceChangeDetector
- {
- public:
- DSoundAudioIODeviceType()
- : AudioIODeviceType ("DirectSound"),
- DeviceChangeDetector (L"DirectSound"),
- hasScanned (false)
- {
- initialiseDSoundFunctions();
- }
-
- void scanForDevices()
- {
- hasScanned = true;
- deviceList.scan();
- }
-
- StringArray getDeviceNames (bool wantInputNames) const
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
-
- return wantInputNames ? deviceList.inputDeviceNames
- : deviceList.outputDeviceNames;
- }
-
- int getDefaultDeviceIndex (bool /*forInput*/) const
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
- return 0;
- }
-
- int getIndexOfDevice (AudioIODevice* device, bool asInput) const
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
-
- if (DSoundAudioIODevice* const d = dynamic_cast<DSoundAudioIODevice*> (device))
- return asInput ? d->inputDeviceIndex
- : d->outputDeviceIndex;
-
- return -1;
- }
-
- 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 = deviceList.outputDeviceNames.indexOf (outputDeviceName);
- const int inputIndex = deviceList.inputDeviceNames.indexOf (inputDeviceName);
-
- if (outputIndex >= 0 || inputIndex >= 0)
- return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
- : inputDeviceName,
- outputIndex, inputIndex);
-
- return nullptr;
- }
-
- private:
- DSoundDeviceList deviceList;
- bool hasScanned;
-
- void systemDeviceChanged() override
- {
- DSoundDeviceList newList;
- newList.scan();
-
- if (newList != deviceList)
- {
- deviceList = newList;
- callDeviceChangeListeners();
- }
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType)
- };
-
- //==============================================================================
- AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
- {
- return new DSoundAudioIODeviceType();
- }
|