| @@ -37,15 +37,16 @@ ApplicationCommandManager* commandManager = nullptr; | |||
| MainWindow::MainWindow() | |||
| : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), | |||
| Colour::greyLevel (0.6f), | |||
| DocumentWindow::allButtons) | |||
| DocumentWindow::allButtons, | |||
| false) | |||
| { | |||
| setUsingNativeTitleBar (true); | |||
| setContentOwned (new ProjectContentComponent(), false); | |||
| #if ! JUCE_MAC | |||
| #if ! JUCE_MAC | |||
| JucerApplication* app = static_cast<JucerApplication*> (JUCEApplication::getInstance()); | |||
| setMenuBar (app->menuModel); | |||
| #endif | |||
| #endif | |||
| setResizable (true, false); | |||
| @@ -77,6 +78,11 @@ MainWindow::MainWindow() | |||
| //getPeer()->setCurrentRenderingEngine (0); | |||
| getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | |||
| setVisible (true); | |||
| addToDesktop(); | |||
| getContentComponent()->grabKeyboardFocus(); | |||
| } | |||
| MainWindow::~MainWindow() | |||
| @@ -80492,7 +80492,7 @@ void ResizableWindow::initialise (const bool shouldAddToDesktop) | |||
| lastNonFullScreenPos.setBounds (50, 50, 256, 256); | |||
| if (shouldAddToDesktop) | |||
| Component::addToDesktop (ResizableWindow::getDesktopWindowStyleFlags()); | |||
| addToDesktop(); | |||
| } | |||
| int ResizableWindow::getDesktopWindowStyleFlags() const | |||
| @@ -80505,6 +80505,11 @@ int ResizableWindow::getDesktopWindowStyleFlags() const | |||
| return styleFlags; | |||
| } | |||
| void ResizableWindow::addToDesktop() | |||
| { | |||
| Component::addToDesktop (ResizableWindow::getDesktopWindowStyleFlags()); | |||
| } | |||
| void ResizableWindow::clearContentComponent() | |||
| { | |||
| if (ownsContentComponent) | |||
| @@ -247265,6 +247270,38 @@ void MessageManager::doPlatformSpecificShutdown() | |||
| OleUninitialize(); | |||
| } | |||
| class DeviceChangeDetector // (Used by various audio classes) | |||
| { | |||
| public: | |||
| DeviceChangeDetector (const wchar_t* const name) | |||
| : messageWindow (name, (WNDPROC) deviceChangeEventCallback) | |||
| { | |||
| SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this); | |||
| } | |||
| virtual ~DeviceChangeDetector() {} | |||
| protected: | |||
| virtual void systemDeviceChanged() = 0; | |||
| private: | |||
| HiddenMessageWindow messageWindow; | |||
| static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message, | |||
| const WPARAM wParam, const LPARAM lParam) | |||
| { | |||
| if (message == WM_DEVICECHANGE | |||
| && (wParam == 0x8000 /*DBT_DEVICEARRIVAL*/ | |||
| || wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/ | |||
| || wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/)) | |||
| { | |||
| ((DeviceChangeDetector*) GetWindowLongPtr (h, GWLP_USERDATA))->systemDeviceChanged(); | |||
| } | |||
| return DefWindowProc (h, message, wParam, lParam); | |||
| } | |||
| }; | |||
| #endif | |||
| /*** End of inlined file: juce_win32_Messaging.cpp ***/ | |||
| @@ -256845,8 +256882,6 @@ namespace ASIODebugging | |||
| class ASIOAudioIODevice; | |||
| static ASIOAudioIODevice* volatile currentASIODev[3] = { 0 }; | |||
| static const int maxASIOChannels = 160; | |||
| class JUCE_API ASIOAudioIODevice : public AudioIODevice, | |||
| private Timer | |||
| { | |||
| @@ -257141,30 +257176,7 @@ public: | |||
| const int totalBuffers = numActiveInputChans + numActiveOutputChans; | |||
| callbacks.sampleRateDidChange = &sampleRateChangedCallback; | |||
| if (currentASIODev[0] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback0; | |||
| callbacks.asioMessage = &asioMessagesCallback0; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback0; | |||
| } | |||
| else if (currentASIODev[1] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback1; | |||
| callbacks.asioMessage = &asioMessagesCallback1; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback1; | |||
| } | |||
| else if (currentASIODev[2] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback2; | |||
| callbacks.asioMessage = &asioMessagesCallback2; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback2; | |||
| } | |||
| else | |||
| { | |||
| jassertfalse; | |||
| } | |||
| setCallbackFunctions(); | |||
| log ("disposing buffers"); | |||
| err = asioObject->disposeBuffers(); | |||
| @@ -257197,7 +257209,7 @@ public: | |||
| Array <int> types; | |||
| currentBitDepth = 16; | |||
| for (i = 0; i < jmin ((int) totalNumInputChans, maxASIOChannels); ++i) | |||
| for (i = 0; i < jmin ((int) totalNumInputChans, (int) maxASIOChannels); ++i) | |||
| { | |||
| if (inputChannels[i]) | |||
| { | |||
| @@ -257224,7 +257236,7 @@ public: | |||
| jassert (numActiveInputChans == n); | |||
| n = 0; | |||
| for (i = 0; i < jmin ((int) totalNumOutputChans, maxASIOChannels); ++i) | |||
| for (i = 0; i < jmin ((int) totalNumOutputChans, (int) maxASIOChannels); ++i) | |||
| { | |||
| if (outputChannels[i]) | |||
| { | |||
| @@ -257518,6 +257530,8 @@ private: | |||
| AudioIODeviceCallback* volatile currentCallback; | |||
| CriticalSection callbackLock; | |||
| enum { maxASIOChannels = 160 }; | |||
| ASIOBufferInfo bufferInfos [maxASIOChannels]; | |||
| float* inBuffers [maxASIOChannels]; | |||
| float* outBuffers [maxASIOChannels]; | |||
| @@ -257749,30 +257763,7 @@ private: | |||
| ++numChans; | |||
| } | |||
| callbacks.sampleRateDidChange = &sampleRateChangedCallback; | |||
| if (currentASIODev[0] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback0; | |||
| callbacks.asioMessage = &asioMessagesCallback0; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback0; | |||
| } | |||
| else if (currentASIODev[1] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback1; | |||
| callbacks.asioMessage = &asioMessagesCallback1; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback1; | |||
| } | |||
| else if (currentASIODev[2] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback2; | |||
| callbacks.asioMessage = &asioMessagesCallback2; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback2; | |||
| } | |||
| else | |||
| { | |||
| jassertfalse; | |||
| } | |||
| setCallbackFunctions(); | |||
| log ("creating buffers (dummy): " + String (numChans) + ", " + String ((int) preferredSize)); | |||
| @@ -258028,64 +258019,47 @@ private: | |||
| asioObject->outputReady(); | |||
| } | |||
| static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback0 (ASIOTime*, long index, long) | |||
| { | |||
| if (currentASIODev[0] != nullptr) | |||
| currentASIODev[0]->callback (index); | |||
| return nullptr; | |||
| } | |||
| static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback1 (ASIOTime*, long index, long) | |||
| template <int deviceIndex> | |||
| struct ASIOCallbackFunctions | |||
| { | |||
| if (currentASIODev[1] != nullptr) | |||
| currentASIODev[1]->callback (index); | |||
| return nullptr; | |||
| } | |||
| static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback2 (ASIOTime*, long index, long) | |||
| { | |||
| if (currentASIODev[2] != nullptr) | |||
| currentASIODev[2]->callback (index); | |||
| return nullptr; | |||
| } | |||
| static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback (ASIOTime*, long index, long) | |||
| { | |||
| if (currentASIODev[deviceIndex] != nullptr) | |||
| currentASIODev[deviceIndex]->callback (index); | |||
| static void JUCE_ASIOCALLBACK bufferSwitchCallback0 (long index, long) | |||
| { | |||
| if (currentASIODev[0] != nullptr) | |||
| currentASIODev[0]->callback (index); | |||
| } | |||
| return nullptr; | |||
| } | |||
| static void JUCE_ASIOCALLBACK bufferSwitchCallback1 (long index, long) | |||
| { | |||
| if (currentASIODev[1] != nullptr) | |||
| currentASIODev[1]->callback (index); | |||
| } | |||
| static void JUCE_ASIOCALLBACK bufferSwitchCallback (long index, long) | |||
| { | |||
| if (currentASIODev[deviceIndex] != nullptr) | |||
| currentASIODev[deviceIndex]->callback (index); | |||
| } | |||
| static void JUCE_ASIOCALLBACK bufferSwitchCallback2 (long index, long) | |||
| { | |||
| if (currentASIODev[2] != nullptr) | |||
| currentASIODev[2]->callback (index); | |||
| } | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, void*, double*) | |||
| { | |||
| return ASIOAudioIODevice::asioMessagesCallback (selector, value, deviceIndex); | |||
| } | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback0 (long selector, long value, void*, double*) | |||
| { | |||
| return asioMessagesCallback (selector, value, 0); | |||
| } | |||
| static void setCallbacks (ASIOCallbacks& callbacks) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback; | |||
| callbacks.asioMessage = &asioMessagesCallback; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; | |||
| } | |||
| }; | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback1 (long selector, long value, void*, double*) | |||
| void setCallbackFunctions() | |||
| { | |||
| return asioMessagesCallback (selector, value, 1); | |||
| } | |||
| callbacks.sampleRateDidChange = &sampleRateChangedCallback; | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback2 (long selector, long value, void*, double*) | |||
| { | |||
| return asioMessagesCallback (selector, value, 2); | |||
| if (currentASIODev[0] == this) ASIOCallbackFunctions<0>::setCallbacks (callbacks); | |||
| else if (currentASIODev[1] == this) ASIOCallbackFunctions<1>::setCallbacks (callbacks); | |||
| else if (currentASIODev[2] == this) ASIOCallbackFunctions<2>::setCallbacks (callbacks); | |||
| else jassertfalse; | |||
| } | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, const int deviceIndex) | |||
| static long asioMessagesCallback (long selector, long value, const int deviceIndex) | |||
| { | |||
| switch (selector) | |||
| { | |||
| @@ -258805,7 +258779,7 @@ namespace | |||
| class DSoundInternalOutChannel | |||
| { | |||
| public: | |||
| DSoundInternalOutChannel (const String& name_, LPGUID guid_, int rate, | |||
| 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), | |||
| @@ -258854,7 +258828,7 @@ public: | |||
| HRESULT hr = E_NOINTERFACE; | |||
| if (dsDirectSoundCreate != 0) | |||
| hr = dsDirectSoundCreate (guid, &pDirectSound, 0); | |||
| hr = dsDirectSoundCreate (&guid, &pDirectSound, 0); | |||
| if (hr == S_OK) | |||
| { | |||
| @@ -259093,7 +259067,7 @@ public: | |||
| private: | |||
| String name; | |||
| LPGUID guid; | |||
| GUID guid; | |||
| int sampleRate, bufferSizeSamples; | |||
| float* leftBuffer; | |||
| float* rightBuffer; | |||
| @@ -259115,7 +259089,7 @@ private: | |||
| struct DSoundInternalInChannel | |||
| { | |||
| public: | |||
| DSoundInternalInChannel (const String& name_, LPGUID guid_, int rate, | |||
| 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), | |||
| @@ -259173,7 +259147,7 @@ public: | |||
| HRESULT hr = E_NOINTERFACE; | |||
| if (dsDirectSoundCaptureCreate != 0) | |||
| hr = dsDirectSoundCaptureCreate (guid, &pDirectSoundCapture, 0); | |||
| hr = dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, 0); | |||
| logError (hr); | |||
| @@ -259349,7 +259323,7 @@ public: | |||
| private: | |||
| String name; | |||
| LPGUID guid; | |||
| GUID guid; | |||
| int sampleRate, bufferSizeSamples; | |||
| float* leftBuffer; | |||
| float* rightBuffer; | |||
| @@ -259668,23 +259642,16 @@ public: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice); | |||
| }; | |||
| class DSoundAudioIODeviceType : public AudioIODeviceType | |||
| struct DSoundDeviceList | |||
| { | |||
| public: | |||
| DSoundAudioIODeviceType() | |||
| : AudioIODeviceType ("DirectSound"), | |||
| hasScanned (false) | |||
| { | |||
| initialiseDSoundFunctions(); | |||
| } | |||
| StringArray outputDeviceNames, inputDeviceNames; | |||
| Array<GUID> outputGuids, inputGuids; | |||
| void scanForDevices() | |||
| void scan() | |||
| { | |||
| hasScanned = true; | |||
| outputDeviceNames.clear(); | |||
| outputGuids.clear(); | |||
| inputDeviceNames.clear(); | |||
| outputGuids.clear(); | |||
| inputGuids.clear(); | |||
| if (dsDirectSoundEnumerateW != 0) | |||
| @@ -259694,57 +259661,16 @@ public: | |||
| } | |||
| } | |||
| StringArray getDeviceNames (bool wantInputNames) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return wantInputNames ? inputDeviceNames | |||
| : outputDeviceNames; | |||
| } | |||
| int getDefaultDeviceIndex (bool /*forInput*/) const | |||
| bool operator!= (const DSoundDeviceList& other) const noexcept | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return 0; | |||
| return outputDeviceNames != other.outputDeviceNames | |||
| || inputDeviceNames != other.inputDeviceNames | |||
| || outputGuids != other.outputGuids | |||
| || inputGuids != other.inputGuids; | |||
| } | |||
| int getIndexOfDevice (AudioIODevice* device, 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 nullptr; | |||
| } | |||
| StringArray outputDeviceNames, inputDeviceNames; | |||
| OwnedArray <GUID> outputGuids, inputGuids; | |||
| private: | |||
| bool hasScanned; | |||
| BOOL outputEnumProc (LPGUID lpGUID, String desc) | |||
| static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids) | |||
| { | |||
| desc = desc.trim(); | |||
| @@ -259753,68 +259679,28 @@ private: | |||
| const String origDesc (desc); | |||
| int n = 2; | |||
| while (outputDeviceNames.contains (desc)) | |||
| while (names.contains (desc)) | |||
| desc = origDesc + " (" + String (n++) + ")"; | |||
| outputDeviceNames.add (desc); | |||
| if (lpGUID != 0) | |||
| outputGuids.add (new GUID (*lpGUID)); | |||
| else | |||
| outputGuids.add (nullptr); | |||
| names.add (desc); | |||
| guids.add (lpGUID != nullptr ? *lpGUID : GUID()); | |||
| } | |||
| return TRUE; | |||
| } | |||
| static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object) | |||
| { | |||
| return ((DSoundAudioIODeviceType*) object) | |||
| ->outputEnumProc (lpGUID, String (description)); | |||
| } | |||
| 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 outputEnumProcA (LPGUID lpGUID, LPCTSTR description, LPCTSTR, LPVOID object) | |||
| static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, 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 + " (" + String (n++) + ")"; | |||
| inputDeviceNames.add (desc); | |||
| if (lpGUID != 0) | |||
| inputGuids.add (new GUID (*lpGUID)); | |||
| else | |||
| inputGuids.add (nullptr); | |||
| } | |||
| return TRUE; | |||
| return static_cast<DSoundDeviceList*> (object)->outputEnumProc (lpGUID, description); | |||
| } | |||
| static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object) | |||
| { | |||
| return ((DSoundAudioIODeviceType*) object) | |||
| ->inputEnumProc (lpGUID, String (description)); | |||
| return static_cast<DSoundDeviceList*> (object)->inputEnumProc (lpGUID, description); | |||
| } | |||
| static BOOL CALLBACK inputEnumProcA (LPGUID lpGUID, LPCTSTR description, LPCTSTR, LPVOID object) | |||
| { | |||
| return ((DSoundAudioIODeviceType*) object) | |||
| ->inputEnumProc (lpGUID, String (description)); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType); | |||
| }; | |||
| String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | |||
| @@ -259831,8 +259717,8 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | |||
| bufferSizeSamples = bufferSizeSamples_ & ~7; | |||
| DSoundAudioIODeviceType dlh; | |||
| dlh.scanForDevices(); | |||
| DSoundDeviceList dlh; | |||
| dlh.scan(); | |||
| enabledInputs = inputChannels; | |||
| enabledInputs.setRange (inChannels.size(), | |||
| @@ -259945,6 +259831,88 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | |||
| 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 | |||
| 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 = 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() | |||
| { | |||
| 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(); | |||
| @@ -260879,15 +260847,15 @@ private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice); | |||
| }; | |||
| class WASAPIAudioIODeviceType : public AudioIODeviceType | |||
| class WASAPIAudioIODeviceType : public AudioIODeviceType, | |||
| private DeviceChangeDetector | |||
| { | |||
| public: | |||
| WASAPIAudioIODeviceType() | |||
| : AudioIODeviceType ("Windows Audio"), | |||
| deviceChangeCatcher (_T("Windows Audio"), (WNDPROC) deviceChangeEventCallback), | |||
| DeviceChangeDetector (L"Windows Audio"), | |||
| hasScanned (false) | |||
| { | |||
| SetWindowLongPtr (deviceChangeCatcher.getHWND(), GWLP_USERDATA, (LONG_PTR) this); | |||
| } | |||
| void scanForDevices() | |||
| @@ -260957,7 +260925,6 @@ public: | |||
| StringArray inputDeviceNames, inputDeviceIds; | |||
| private: | |||
| HiddenMessageWindow deviceChangeCatcher; | |||
| bool hasScanned; | |||
| static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture) | |||
| @@ -261045,21 +261012,7 @@ private: | |||
| outputDeviceNames.appendNumbersToDuplicates (false, false); | |||
| } | |||
| static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message, | |||
| const WPARAM wParam, const LPARAM lParam) | |||
| { | |||
| if (message == WM_DEVICECHANGE | |||
| && (wParam == 0x8000 /*DBT_DEVICEARRIVAL*/ | |||
| || wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/ | |||
| || wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/)) | |||
| { | |||
| ((WASAPIAudioIODeviceType*) GetWindowLongPtr (h, GWLP_USERDATA))->handleDeviceChange(); | |||
| } | |||
| return DefWindowProc (h, message, wParam, lParam); | |||
| } | |||
| void handleDeviceChange() | |||
| void systemDeviceChanged() | |||
| { | |||
| StringArray newOutNames, newInNames, newOutIds, newInIds; | |||
| scan (newOutNames, newInNames, newOutIds, newInIds); | |||
| @@ -57521,6 +57521,9 @@ public: | |||
| */ | |||
| void setMinimised (bool shouldMinimise); | |||
| /** Adds the window to the desktop using the default flags. */ | |||
| void addToDesktop(); | |||
| /** Returns a string which encodes the window's current size and position. | |||
| This string will encapsulate the window's size, position, and whether it's | |||
| @@ -90,7 +90,7 @@ void ResizableWindow::initialise (const bool shouldAddToDesktop) | |||
| lastNonFullScreenPos.setBounds (50, 50, 256, 256); | |||
| if (shouldAddToDesktop) | |||
| Component::addToDesktop (ResizableWindow::getDesktopWindowStyleFlags()); | |||
| addToDesktop(); | |||
| } | |||
| int ResizableWindow::getDesktopWindowStyleFlags() const | |||
| @@ -103,6 +103,11 @@ int ResizableWindow::getDesktopWindowStyleFlags() const | |||
| return styleFlags; | |||
| } | |||
| void ResizableWindow::addToDesktop() | |||
| { | |||
| Component::addToDesktop (ResizableWindow::getDesktopWindowStyleFlags()); | |||
| } | |||
| //============================================================================== | |||
| void ResizableWindow::clearContentComponent() | |||
| { | |||
| @@ -200,6 +200,9 @@ public: | |||
| */ | |||
| void setMinimised (bool shouldMinimise); | |||
| /** Adds the window to the desktop using the default flags. */ | |||
| void addToDesktop(); | |||
| //============================================================================== | |||
| /** Returns a string which encodes the window's current size and position. | |||
| @@ -75,8 +75,6 @@ namespace ASIODebugging | |||
| class ASIOAudioIODevice; | |||
| static ASIOAudioIODevice* volatile currentASIODev[3] = { 0 }; | |||
| static const int maxASIOChannels = 160; | |||
| //============================================================================== | |||
| class JUCE_API ASIOAudioIODevice : public AudioIODevice, | |||
| @@ -373,30 +371,7 @@ public: | |||
| const int totalBuffers = numActiveInputChans + numActiveOutputChans; | |||
| callbacks.sampleRateDidChange = &sampleRateChangedCallback; | |||
| if (currentASIODev[0] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback0; | |||
| callbacks.asioMessage = &asioMessagesCallback0; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback0; | |||
| } | |||
| else if (currentASIODev[1] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback1; | |||
| callbacks.asioMessage = &asioMessagesCallback1; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback1; | |||
| } | |||
| else if (currentASIODev[2] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback2; | |||
| callbacks.asioMessage = &asioMessagesCallback2; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback2; | |||
| } | |||
| else | |||
| { | |||
| jassertfalse; | |||
| } | |||
| setCallbackFunctions(); | |||
| log ("disposing buffers"); | |||
| err = asioObject->disposeBuffers(); | |||
| @@ -429,7 +404,7 @@ public: | |||
| Array <int> types; | |||
| currentBitDepth = 16; | |||
| for (i = 0; i < jmin ((int) totalNumInputChans, maxASIOChannels); ++i) | |||
| for (i = 0; i < jmin ((int) totalNumInputChans, (int) maxASIOChannels); ++i) | |||
| { | |||
| if (inputChannels[i]) | |||
| { | |||
| @@ -456,7 +431,7 @@ public: | |||
| jassert (numActiveInputChans == n); | |||
| n = 0; | |||
| for (i = 0; i < jmin ((int) totalNumOutputChans, maxASIOChannels); ++i) | |||
| for (i = 0; i < jmin ((int) totalNumOutputChans, (int) maxASIOChannels); ++i) | |||
| { | |||
| if (outputChannels[i]) | |||
| { | |||
| @@ -750,6 +725,8 @@ private: | |||
| AudioIODeviceCallback* volatile currentCallback; | |||
| CriticalSection callbackLock; | |||
| enum { maxASIOChannels = 160 }; | |||
| ASIOBufferInfo bufferInfos [maxASIOChannels]; | |||
| float* inBuffers [maxASIOChannels]; | |||
| float* outBuffers [maxASIOChannels]; | |||
| @@ -982,31 +959,7 @@ private: | |||
| ++numChans; | |||
| } | |||
| callbacks.sampleRateDidChange = &sampleRateChangedCallback; | |||
| if (currentASIODev[0] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback0; | |||
| callbacks.asioMessage = &asioMessagesCallback0; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback0; | |||
| } | |||
| else if (currentASIODev[1] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback1; | |||
| callbacks.asioMessage = &asioMessagesCallback1; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback1; | |||
| } | |||
| else if (currentASIODev[2] == this) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback2; | |||
| callbacks.asioMessage = &asioMessagesCallback2; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback2; | |||
| } | |||
| else | |||
| { | |||
| jassertfalse; | |||
| } | |||
| setCallbackFunctions(); | |||
| log ("creating buffers (dummy): " + String (numChans) + ", " + String ((int) preferredSize)); | |||
| @@ -1264,65 +1217,48 @@ private: | |||
| } | |||
| //============================================================================== | |||
| static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback0 (ASIOTime*, long index, long) | |||
| { | |||
| if (currentASIODev[0] != nullptr) | |||
| currentASIODev[0]->callback (index); | |||
| return nullptr; | |||
| } | |||
| static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback1 (ASIOTime*, long index, long) | |||
| template <int deviceIndex> | |||
| struct ASIOCallbackFunctions | |||
| { | |||
| if (currentASIODev[1] != nullptr) | |||
| currentASIODev[1]->callback (index); | |||
| return nullptr; | |||
| } | |||
| static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback2 (ASIOTime*, long index, long) | |||
| { | |||
| if (currentASIODev[2] != nullptr) | |||
| currentASIODev[2]->callback (index); | |||
| return nullptr; | |||
| } | |||
| static ASIOTime* JUCE_ASIOCALLBACK bufferSwitchTimeInfoCallback (ASIOTime*, long index, long) | |||
| { | |||
| if (currentASIODev[deviceIndex] != nullptr) | |||
| currentASIODev[deviceIndex]->callback (index); | |||
| static void JUCE_ASIOCALLBACK bufferSwitchCallback0 (long index, long) | |||
| { | |||
| if (currentASIODev[0] != nullptr) | |||
| currentASIODev[0]->callback (index); | |||
| } | |||
| return nullptr; | |||
| } | |||
| static void JUCE_ASIOCALLBACK bufferSwitchCallback1 (long index, long) | |||
| { | |||
| if (currentASIODev[1] != nullptr) | |||
| currentASIODev[1]->callback (index); | |||
| } | |||
| static void JUCE_ASIOCALLBACK bufferSwitchCallback (long index, long) | |||
| { | |||
| if (currentASIODev[deviceIndex] != nullptr) | |||
| currentASIODev[deviceIndex]->callback (index); | |||
| } | |||
| static void JUCE_ASIOCALLBACK bufferSwitchCallback2 (long index, long) | |||
| { | |||
| if (currentASIODev[2] != nullptr) | |||
| currentASIODev[2]->callback (index); | |||
| } | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, void*, double*) | |||
| { | |||
| return ASIOAudioIODevice::asioMessagesCallback (selector, value, deviceIndex); | |||
| } | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback0 (long selector, long value, void*, double*) | |||
| { | |||
| return asioMessagesCallback (selector, value, 0); | |||
| } | |||
| static void setCallbacks (ASIOCallbacks& callbacks) | |||
| { | |||
| callbacks.bufferSwitch = &bufferSwitchCallback; | |||
| callbacks.asioMessage = &asioMessagesCallback; | |||
| callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; | |||
| } | |||
| }; | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback1 (long selector, long value, void*, double*) | |||
| void setCallbackFunctions() | |||
| { | |||
| return asioMessagesCallback (selector, value, 1); | |||
| } | |||
| callbacks.sampleRateDidChange = &sampleRateChangedCallback; | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback2 (long selector, long value, void*, double*) | |||
| { | |||
| return asioMessagesCallback (selector, value, 2); | |||
| if (currentASIODev[0] == this) ASIOCallbackFunctions<0>::setCallbacks (callbacks); | |||
| else if (currentASIODev[1] == this) ASIOCallbackFunctions<1>::setCallbacks (callbacks); | |||
| else if (currentASIODev[2] == this) ASIOCallbackFunctions<2>::setCallbacks (callbacks); | |||
| else jassertfalse; | |||
| } | |||
| //============================================================================== | |||
| static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, const int deviceIndex) | |||
| static long asioMessagesCallback (long selector, long value, const int deviceIndex) | |||
| { | |||
| switch (selector) | |||
| { | |||
| @@ -228,7 +228,7 @@ namespace | |||
| class DSoundInternalOutChannel | |||
| { | |||
| public: | |||
| DSoundInternalOutChannel (const String& name_, LPGUID guid_, int rate, | |||
| 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), | |||
| @@ -277,7 +277,7 @@ public: | |||
| HRESULT hr = E_NOINTERFACE; | |||
| if (dsDirectSoundCreate != 0) | |||
| hr = dsDirectSoundCreate (guid, &pDirectSound, 0); | |||
| hr = dsDirectSoundCreate (&guid, &pDirectSound, 0); | |||
| if (hr == S_OK) | |||
| { | |||
| @@ -516,7 +516,7 @@ public: | |||
| private: | |||
| String name; | |||
| LPGUID guid; | |||
| GUID guid; | |||
| int sampleRate, bufferSizeSamples; | |||
| float* leftBuffer; | |||
| float* rightBuffer; | |||
| @@ -539,7 +539,7 @@ private: | |||
| struct DSoundInternalInChannel | |||
| { | |||
| public: | |||
| DSoundInternalInChannel (const String& name_, LPGUID guid_, int rate, | |||
| 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), | |||
| @@ -597,7 +597,7 @@ public: | |||
| HRESULT hr = E_NOINTERFACE; | |||
| if (dsDirectSoundCaptureCreate != 0) | |||
| hr = dsDirectSoundCaptureCreate (guid, &pDirectSoundCapture, 0); | |||
| hr = dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, 0); | |||
| logError (hr); | |||
| @@ -773,7 +773,7 @@ public: | |||
| private: | |||
| String name; | |||
| LPGUID guid; | |||
| GUID guid; | |||
| int sampleRate, bufferSizeSamples; | |||
| float* leftBuffer; | |||
| float* rightBuffer; | |||
| @@ -1094,26 +1094,17 @@ public: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice); | |||
| }; | |||
| //============================================================================== | |||
| class DSoundAudioIODeviceType : public AudioIODeviceType | |||
| struct DSoundDeviceList | |||
| { | |||
| public: | |||
| DSoundAudioIODeviceType() | |||
| : AudioIODeviceType ("DirectSound"), | |||
| hasScanned (false) | |||
| { | |||
| initialiseDSoundFunctions(); | |||
| } | |||
| StringArray outputDeviceNames, inputDeviceNames; | |||
| Array<GUID> outputGuids, inputGuids; | |||
| //============================================================================== | |||
| void scanForDevices() | |||
| void scan() | |||
| { | |||
| hasScanned = true; | |||
| outputDeviceNames.clear(); | |||
| outputGuids.clear(); | |||
| inputDeviceNames.clear(); | |||
| outputGuids.clear(); | |||
| inputGuids.clear(); | |||
| if (dsDirectSoundEnumerateW != 0) | |||
| @@ -1123,59 +1114,16 @@ public: | |||
| } | |||
| } | |||
| StringArray getDeviceNames (bool wantInputNames) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return wantInputNames ? inputDeviceNames | |||
| : outputDeviceNames; | |||
| } | |||
| int getDefaultDeviceIndex (bool /*forInput*/) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return 0; | |||
| } | |||
| int getIndexOfDevice (AudioIODevice* device, bool asInput) const | |||
| bool operator!= (const DSoundDeviceList& other) const noexcept | |||
| { | |||
| 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; | |||
| return outputDeviceNames != other.outputDeviceNames | |||
| || inputDeviceNames != other.inputDeviceNames | |||
| || outputGuids != other.outputGuids | |||
| || inputGuids != other.inputGuids; | |||
| } | |||
| 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 nullptr; | |||
| } | |||
| //============================================================================== | |||
| StringArray outputDeviceNames, inputDeviceNames; | |||
| OwnedArray <GUID> outputGuids, inputGuids; | |||
| private: | |||
| bool hasScanned; | |||
| //============================================================================== | |||
| BOOL outputEnumProc (LPGUID lpGUID, String desc) | |||
| static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids) | |||
| { | |||
| desc = desc.trim(); | |||
| @@ -1184,70 +1132,28 @@ private: | |||
| const String origDesc (desc); | |||
| int n = 2; | |||
| while (outputDeviceNames.contains (desc)) | |||
| while (names.contains (desc)) | |||
| desc = origDesc + " (" + String (n++) + ")"; | |||
| outputDeviceNames.add (desc); | |||
| if (lpGUID != 0) | |||
| outputGuids.add (new GUID (*lpGUID)); | |||
| else | |||
| outputGuids.add (nullptr); | |||
| names.add (desc); | |||
| guids.add (lpGUID != nullptr ? *lpGUID : GUID()); | |||
| } | |||
| 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 outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); } | |||
| BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); } | |||
| //============================================================================== | |||
| BOOL CALLBACK inputEnumProc (LPGUID lpGUID, String desc) | |||
| static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object) | |||
| { | |||
| desc = desc.trim(); | |||
| if (desc.isNotEmpty()) | |||
| { | |||
| const String origDesc (desc); | |||
| int n = 2; | |||
| while (inputDeviceNames.contains (desc)) | |||
| desc = origDesc + " (" + String (n++) + ")"; | |||
| inputDeviceNames.add (desc); | |||
| if (lpGUID != 0) | |||
| inputGuids.add (new GUID (*lpGUID)); | |||
| else | |||
| inputGuids.add (nullptr); | |||
| } | |||
| return TRUE; | |||
| return static_cast<DSoundDeviceList*> (object)->outputEnumProc (lpGUID, description); | |||
| } | |||
| 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)); | |||
| return static_cast<DSoundDeviceList*> (object)->inputEnumProc (lpGUID, description); | |||
| } | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType); | |||
| }; | |||
| //============================================================================== | |||
| @@ -1265,8 +1171,8 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | |||
| bufferSizeSamples = bufferSizeSamples_ & ~7; | |||
| DSoundAudioIODeviceType dlh; | |||
| dlh.scanForDevices(); | |||
| DSoundDeviceList dlh; | |||
| dlh.scan(); | |||
| enabledInputs = inputChannels; | |||
| enabledInputs.setRange (inChannels.size(), | |||
| @@ -1379,6 +1285,91 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | |||
| 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 | |||
| 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 = 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() | |||
| { | |||
| DSoundDeviceList newList; | |||
| newList.scan(); | |||
| if (newList != deviceList) | |||
| { | |||
| deviceList = newList; | |||
| callDeviceChangeListeners(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType); | |||
| }; | |||
| //============================================================================== | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() | |||
| { | |||
| @@ -344,4 +344,37 @@ void MessageManager::doPlatformSpecificShutdown() | |||
| OleUninitialize(); | |||
| } | |||
| //============================================================================== | |||
| class DeviceChangeDetector // (Used by various audio classes) | |||
| { | |||
| public: | |||
| DeviceChangeDetector (const wchar_t* const name) | |||
| : messageWindow (name, (WNDPROC) deviceChangeEventCallback) | |||
| { | |||
| SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this); | |||
| } | |||
| virtual ~DeviceChangeDetector() {} | |||
| protected: | |||
| virtual void systemDeviceChanged() = 0; | |||
| private: | |||
| HiddenMessageWindow messageWindow; | |||
| static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message, | |||
| const WPARAM wParam, const LPARAM lParam) | |||
| { | |||
| if (message == WM_DEVICECHANGE | |||
| && (wParam == 0x8000 /*DBT_DEVICEARRIVAL*/ | |||
| || wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/ | |||
| || wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/)) | |||
| { | |||
| ((DeviceChangeDetector*) GetWindowLongPtr (h, GWLP_USERDATA))->systemDeviceChanged(); | |||
| } | |||
| return DefWindowProc (h, message, wParam, lParam); | |||
| } | |||
| }; | |||
| #endif | |||
| @@ -958,15 +958,15 @@ private: | |||
| //============================================================================== | |||
| class WASAPIAudioIODeviceType : public AudioIODeviceType | |||
| class WASAPIAudioIODeviceType : public AudioIODeviceType, | |||
| private DeviceChangeDetector | |||
| { | |||
| public: | |||
| WASAPIAudioIODeviceType() | |||
| : AudioIODeviceType ("Windows Audio"), | |||
| deviceChangeCatcher (_T("Windows Audio"), (WNDPROC) deviceChangeEventCallback), | |||
| DeviceChangeDetector (L"Windows Audio"), | |||
| hasScanned (false) | |||
| { | |||
| SetWindowLongPtr (deviceChangeCatcher.getHWND(), GWLP_USERDATA, (LONG_PTR) this); | |||
| } | |||
| //============================================================================== | |||
| @@ -1038,7 +1038,6 @@ public: | |||
| StringArray inputDeviceNames, inputDeviceIds; | |||
| private: | |||
| HiddenMessageWindow deviceChangeCatcher; | |||
| bool hasScanned; | |||
| //============================================================================== | |||
| @@ -1129,21 +1128,7 @@ private: | |||
| } | |||
| //============================================================================== | |||
| static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message, | |||
| const WPARAM wParam, const LPARAM lParam) | |||
| { | |||
| if (message == WM_DEVICECHANGE | |||
| && (wParam == 0x8000 /*DBT_DEVICEARRIVAL*/ | |||
| || wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/ | |||
| || wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/)) | |||
| { | |||
| ((WASAPIAudioIODeviceType*) GetWindowLongPtr (h, GWLP_USERDATA))->handleDeviceChange(); | |||
| } | |||
| return DefWindowProc (h, message, wParam, lParam); | |||
| } | |||
| void handleDeviceChange() | |||
| void systemDeviceChanged() | |||
| { | |||
| StringArray newOutNames, newInNames, newOutIds, newInIds; | |||
| scan (newOutNames, newInNames, newOutIds, newInIds); | |||