| @@ -37,15 +37,16 @@ ApplicationCommandManager* commandManager = nullptr; | |||||
| MainWindow::MainWindow() | MainWindow::MainWindow() | ||||
| : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), | : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), | ||||
| Colour::greyLevel (0.6f), | Colour::greyLevel (0.6f), | ||||
| DocumentWindow::allButtons) | |||||
| DocumentWindow::allButtons, | |||||
| false) | |||||
| { | { | ||||
| setUsingNativeTitleBar (true); | setUsingNativeTitleBar (true); | ||||
| setContentOwned (new ProjectContentComponent(), false); | setContentOwned (new ProjectContentComponent(), false); | ||||
| #if ! JUCE_MAC | |||||
| #if ! JUCE_MAC | |||||
| JucerApplication* app = static_cast<JucerApplication*> (JUCEApplication::getInstance()); | JucerApplication* app = static_cast<JucerApplication*> (JUCEApplication::getInstance()); | ||||
| setMenuBar (app->menuModel); | setMenuBar (app->menuModel); | ||||
| #endif | |||||
| #endif | |||||
| setResizable (true, false); | setResizable (true, false); | ||||
| @@ -77,6 +78,11 @@ MainWindow::MainWindow() | |||||
| //getPeer()->setCurrentRenderingEngine (0); | //getPeer()->setCurrentRenderingEngine (0); | ||||
| getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | ||||
| setVisible (true); | |||||
| addToDesktop(); | |||||
| getContentComponent()->grabKeyboardFocus(); | |||||
| } | } | ||||
| MainWindow::~MainWindow() | MainWindow::~MainWindow() | ||||
| @@ -80492,7 +80492,7 @@ void ResizableWindow::initialise (const bool shouldAddToDesktop) | |||||
| lastNonFullScreenPos.setBounds (50, 50, 256, 256); | lastNonFullScreenPos.setBounds (50, 50, 256, 256); | ||||
| if (shouldAddToDesktop) | if (shouldAddToDesktop) | ||||
| Component::addToDesktop (ResizableWindow::getDesktopWindowStyleFlags()); | |||||
| addToDesktop(); | |||||
| } | } | ||||
| int ResizableWindow::getDesktopWindowStyleFlags() const | int ResizableWindow::getDesktopWindowStyleFlags() const | ||||
| @@ -80505,6 +80505,11 @@ int ResizableWindow::getDesktopWindowStyleFlags() const | |||||
| return styleFlags; | return styleFlags; | ||||
| } | } | ||||
| void ResizableWindow::addToDesktop() | |||||
| { | |||||
| Component::addToDesktop (ResizableWindow::getDesktopWindowStyleFlags()); | |||||
| } | |||||
| void ResizableWindow::clearContentComponent() | void ResizableWindow::clearContentComponent() | ||||
| { | { | ||||
| if (ownsContentComponent) | if (ownsContentComponent) | ||||
| @@ -247265,6 +247270,38 @@ void MessageManager::doPlatformSpecificShutdown() | |||||
| OleUninitialize(); | 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 | #endif | ||||
| /*** End of inlined file: juce_win32_Messaging.cpp ***/ | /*** End of inlined file: juce_win32_Messaging.cpp ***/ | ||||
| @@ -256845,8 +256882,6 @@ namespace ASIODebugging | |||||
| class ASIOAudioIODevice; | class ASIOAudioIODevice; | ||||
| static ASIOAudioIODevice* volatile currentASIODev[3] = { 0 }; | static ASIOAudioIODevice* volatile currentASIODev[3] = { 0 }; | ||||
| static const int maxASIOChannels = 160; | |||||
| class JUCE_API ASIOAudioIODevice : public AudioIODevice, | class JUCE_API ASIOAudioIODevice : public AudioIODevice, | ||||
| private Timer | private Timer | ||||
| { | { | ||||
| @@ -257141,30 +257176,7 @@ public: | |||||
| const int totalBuffers = numActiveInputChans + numActiveOutputChans; | 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"); | log ("disposing buffers"); | ||||
| err = asioObject->disposeBuffers(); | err = asioObject->disposeBuffers(); | ||||
| @@ -257197,7 +257209,7 @@ public: | |||||
| Array <int> types; | Array <int> types; | ||||
| currentBitDepth = 16; | currentBitDepth = 16; | ||||
| for (i = 0; i < jmin ((int) totalNumInputChans, maxASIOChannels); ++i) | |||||
| for (i = 0; i < jmin ((int) totalNumInputChans, (int) maxASIOChannels); ++i) | |||||
| { | { | ||||
| if (inputChannels[i]) | if (inputChannels[i]) | ||||
| { | { | ||||
| @@ -257224,7 +257236,7 @@ public: | |||||
| jassert (numActiveInputChans == n); | jassert (numActiveInputChans == n); | ||||
| n = 0; | n = 0; | ||||
| for (i = 0; i < jmin ((int) totalNumOutputChans, maxASIOChannels); ++i) | |||||
| for (i = 0; i < jmin ((int) totalNumOutputChans, (int) maxASIOChannels); ++i) | |||||
| { | { | ||||
| if (outputChannels[i]) | if (outputChannels[i]) | ||||
| { | { | ||||
| @@ -257518,6 +257530,8 @@ private: | |||||
| AudioIODeviceCallback* volatile currentCallback; | AudioIODeviceCallback* volatile currentCallback; | ||||
| CriticalSection callbackLock; | CriticalSection callbackLock; | ||||
| enum { maxASIOChannels = 160 }; | |||||
| ASIOBufferInfo bufferInfos [maxASIOChannels]; | ASIOBufferInfo bufferInfos [maxASIOChannels]; | ||||
| float* inBuffers [maxASIOChannels]; | float* inBuffers [maxASIOChannels]; | ||||
| float* outBuffers [maxASIOChannels]; | float* outBuffers [maxASIOChannels]; | ||||
| @@ -257749,30 +257763,7 @@ private: | |||||
| ++numChans; | ++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)); | log ("creating buffers (dummy): " + String (numChans) + ", " + String ((int) preferredSize)); | ||||
| @@ -258028,64 +258019,47 @@ private: | |||||
| asioObject->outputReady(); | 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) | switch (selector) | ||||
| { | { | ||||
| @@ -258805,7 +258779,7 @@ namespace | |||||
| class DSoundInternalOutChannel | class DSoundInternalOutChannel | ||||
| { | { | ||||
| public: | public: | ||||
| DSoundInternalOutChannel (const String& name_, LPGUID guid_, int rate, | |||||
| DSoundInternalOutChannel (const String& name_, const GUID& guid_, int rate, | |||||
| int bufferSize, float* left, float* right) | int bufferSize, float* left, float* right) | ||||
| : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), | : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), | ||||
| bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), | bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), | ||||
| @@ -258854,7 +258828,7 @@ public: | |||||
| HRESULT hr = E_NOINTERFACE; | HRESULT hr = E_NOINTERFACE; | ||||
| if (dsDirectSoundCreate != 0) | if (dsDirectSoundCreate != 0) | ||||
| hr = dsDirectSoundCreate (guid, &pDirectSound, 0); | |||||
| hr = dsDirectSoundCreate (&guid, &pDirectSound, 0); | |||||
| if (hr == S_OK) | if (hr == S_OK) | ||||
| { | { | ||||
| @@ -259093,7 +259067,7 @@ public: | |||||
| private: | private: | ||||
| String name; | String name; | ||||
| LPGUID guid; | |||||
| GUID guid; | |||||
| int sampleRate, bufferSizeSamples; | int sampleRate, bufferSizeSamples; | ||||
| float* leftBuffer; | float* leftBuffer; | ||||
| float* rightBuffer; | float* rightBuffer; | ||||
| @@ -259115,7 +259089,7 @@ private: | |||||
| struct DSoundInternalInChannel | struct DSoundInternalInChannel | ||||
| { | { | ||||
| public: | public: | ||||
| DSoundInternalInChannel (const String& name_, LPGUID guid_, int rate, | |||||
| DSoundInternalInChannel (const String& name_, const GUID& guid_, int rate, | |||||
| int bufferSize, float* left, float* right) | int bufferSize, float* left, float* right) | ||||
| : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), | : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), | ||||
| bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), | bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), | ||||
| @@ -259173,7 +259147,7 @@ public: | |||||
| HRESULT hr = E_NOINTERFACE; | HRESULT hr = E_NOINTERFACE; | ||||
| if (dsDirectSoundCaptureCreate != 0) | if (dsDirectSoundCaptureCreate != 0) | ||||
| hr = dsDirectSoundCaptureCreate (guid, &pDirectSoundCapture, 0); | |||||
| hr = dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, 0); | |||||
| logError (hr); | logError (hr); | ||||
| @@ -259349,7 +259323,7 @@ public: | |||||
| private: | private: | ||||
| String name; | String name; | ||||
| LPGUID guid; | |||||
| GUID guid; | |||||
| int sampleRate, bufferSizeSamples; | int sampleRate, bufferSizeSamples; | ||||
| float* leftBuffer; | float* leftBuffer; | ||||
| float* rightBuffer; | float* rightBuffer; | ||||
| @@ -259668,23 +259642,16 @@ public: | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice); | 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(); | outputDeviceNames.clear(); | ||||
| outputGuids.clear(); | |||||
| inputDeviceNames.clear(); | inputDeviceNames.clear(); | ||||
| outputGuids.clear(); | |||||
| inputGuids.clear(); | inputGuids.clear(); | ||||
| if (dsDirectSoundEnumerateW != 0) | 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: | private: | ||||
| bool hasScanned; | |||||
| BOOL outputEnumProc (LPGUID lpGUID, String desc) | |||||
| static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids) | |||||
| { | { | ||||
| desc = desc.trim(); | desc = desc.trim(); | ||||
| @@ -259753,68 +259679,28 @@ private: | |||||
| const String origDesc (desc); | const String origDesc (desc); | ||||
| int n = 2; | int n = 2; | ||||
| while (outputDeviceNames.contains (desc)) | |||||
| while (names.contains (desc)) | |||||
| desc = origDesc + " (" + String (n++) + ")"; | 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; | 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) | 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, | String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | ||||
| @@ -259831,8 +259717,8 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | |||||
| bufferSizeSamples = bufferSizeSamples_ & ~7; | bufferSizeSamples = bufferSizeSamples_ & ~7; | ||||
| DSoundAudioIODeviceType dlh; | |||||
| dlh.scanForDevices(); | |||||
| DSoundDeviceList dlh; | |||||
| dlh.scan(); | |||||
| enabledInputs = inputChannels; | enabledInputs = inputChannels; | ||||
| enabledInputs.setRange (inChannels.size(), | enabledInputs.setRange (inChannels.size(), | ||||
| @@ -259945,6 +259831,88 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | |||||
| return error; | 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() | AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() | ||||
| { | { | ||||
| return new DSoundAudioIODeviceType(); | return new DSoundAudioIODeviceType(); | ||||
| @@ -260879,15 +260847,15 @@ private: | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice); | ||||
| }; | }; | ||||
| class WASAPIAudioIODeviceType : public AudioIODeviceType | |||||
| class WASAPIAudioIODeviceType : public AudioIODeviceType, | |||||
| private DeviceChangeDetector | |||||
| { | { | ||||
| public: | public: | ||||
| WASAPIAudioIODeviceType() | WASAPIAudioIODeviceType() | ||||
| : AudioIODeviceType ("Windows Audio"), | : AudioIODeviceType ("Windows Audio"), | ||||
| deviceChangeCatcher (_T("Windows Audio"), (WNDPROC) deviceChangeEventCallback), | |||||
| DeviceChangeDetector (L"Windows Audio"), | |||||
| hasScanned (false) | hasScanned (false) | ||||
| { | { | ||||
| SetWindowLongPtr (deviceChangeCatcher.getHWND(), GWLP_USERDATA, (LONG_PTR) this); | |||||
| } | } | ||||
| void scanForDevices() | void scanForDevices() | ||||
| @@ -260957,7 +260925,6 @@ public: | |||||
| StringArray inputDeviceNames, inputDeviceIds; | StringArray inputDeviceNames, inputDeviceIds; | ||||
| private: | private: | ||||
| HiddenMessageWindow deviceChangeCatcher; | |||||
| bool hasScanned; | bool hasScanned; | ||||
| static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture) | static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture) | ||||
| @@ -261045,21 +261012,7 @@ private: | |||||
| outputDeviceNames.appendNumbersToDuplicates (false, false); | 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; | StringArray newOutNames, newInNames, newOutIds, newInIds; | ||||
| scan (newOutNames, newInNames, newOutIds, newInIds); | scan (newOutNames, newInNames, newOutIds, newInIds); | ||||
| @@ -57521,6 +57521,9 @@ public: | |||||
| */ | */ | ||||
| void setMinimised (bool shouldMinimise); | 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. | /** 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 | 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); | lastNonFullScreenPos.setBounds (50, 50, 256, 256); | ||||
| if (shouldAddToDesktop) | if (shouldAddToDesktop) | ||||
| Component::addToDesktop (ResizableWindow::getDesktopWindowStyleFlags()); | |||||
| addToDesktop(); | |||||
| } | } | ||||
| int ResizableWindow::getDesktopWindowStyleFlags() const | int ResizableWindow::getDesktopWindowStyleFlags() const | ||||
| @@ -103,6 +103,11 @@ int ResizableWindow::getDesktopWindowStyleFlags() const | |||||
| return styleFlags; | return styleFlags; | ||||
| } | } | ||||
| void ResizableWindow::addToDesktop() | |||||
| { | |||||
| Component::addToDesktop (ResizableWindow::getDesktopWindowStyleFlags()); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void ResizableWindow::clearContentComponent() | void ResizableWindow::clearContentComponent() | ||||
| { | { | ||||
| @@ -200,6 +200,9 @@ public: | |||||
| */ | */ | ||||
| void setMinimised (bool shouldMinimise); | 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. | /** Returns a string which encodes the window's current size and position. | ||||
| @@ -75,8 +75,6 @@ namespace ASIODebugging | |||||
| class ASIOAudioIODevice; | class ASIOAudioIODevice; | ||||
| static ASIOAudioIODevice* volatile currentASIODev[3] = { 0 }; | static ASIOAudioIODevice* volatile currentASIODev[3] = { 0 }; | ||||
| static const int maxASIOChannels = 160; | |||||
| //============================================================================== | //============================================================================== | ||||
| class JUCE_API ASIOAudioIODevice : public AudioIODevice, | class JUCE_API ASIOAudioIODevice : public AudioIODevice, | ||||
| @@ -373,30 +371,7 @@ public: | |||||
| const int totalBuffers = numActiveInputChans + numActiveOutputChans; | 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"); | log ("disposing buffers"); | ||||
| err = asioObject->disposeBuffers(); | err = asioObject->disposeBuffers(); | ||||
| @@ -429,7 +404,7 @@ public: | |||||
| Array <int> types; | Array <int> types; | ||||
| currentBitDepth = 16; | currentBitDepth = 16; | ||||
| for (i = 0; i < jmin ((int) totalNumInputChans, maxASIOChannels); ++i) | |||||
| for (i = 0; i < jmin ((int) totalNumInputChans, (int) maxASIOChannels); ++i) | |||||
| { | { | ||||
| if (inputChannels[i]) | if (inputChannels[i]) | ||||
| { | { | ||||
| @@ -456,7 +431,7 @@ public: | |||||
| jassert (numActiveInputChans == n); | jassert (numActiveInputChans == n); | ||||
| n = 0; | n = 0; | ||||
| for (i = 0; i < jmin ((int) totalNumOutputChans, maxASIOChannels); ++i) | |||||
| for (i = 0; i < jmin ((int) totalNumOutputChans, (int) maxASIOChannels); ++i) | |||||
| { | { | ||||
| if (outputChannels[i]) | if (outputChannels[i]) | ||||
| { | { | ||||
| @@ -750,6 +725,8 @@ private: | |||||
| AudioIODeviceCallback* volatile currentCallback; | AudioIODeviceCallback* volatile currentCallback; | ||||
| CriticalSection callbackLock; | CriticalSection callbackLock; | ||||
| enum { maxASIOChannels = 160 }; | |||||
| ASIOBufferInfo bufferInfos [maxASIOChannels]; | ASIOBufferInfo bufferInfos [maxASIOChannels]; | ||||
| float* inBuffers [maxASIOChannels]; | float* inBuffers [maxASIOChannels]; | ||||
| float* outBuffers [maxASIOChannels]; | float* outBuffers [maxASIOChannels]; | ||||
| @@ -982,31 +959,7 @@ private: | |||||
| ++numChans; | ++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)); | 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) | switch (selector) | ||||
| { | { | ||||
| @@ -228,7 +228,7 @@ namespace | |||||
| class DSoundInternalOutChannel | class DSoundInternalOutChannel | ||||
| { | { | ||||
| public: | public: | ||||
| DSoundInternalOutChannel (const String& name_, LPGUID guid_, int rate, | |||||
| DSoundInternalOutChannel (const String& name_, const GUID& guid_, int rate, | |||||
| int bufferSize, float* left, float* right) | int bufferSize, float* left, float* right) | ||||
| : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), | : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), | ||||
| bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), | bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), | ||||
| @@ -277,7 +277,7 @@ public: | |||||
| HRESULT hr = E_NOINTERFACE; | HRESULT hr = E_NOINTERFACE; | ||||
| if (dsDirectSoundCreate != 0) | if (dsDirectSoundCreate != 0) | ||||
| hr = dsDirectSoundCreate (guid, &pDirectSound, 0); | |||||
| hr = dsDirectSoundCreate (&guid, &pDirectSound, 0); | |||||
| if (hr == S_OK) | if (hr == S_OK) | ||||
| { | { | ||||
| @@ -516,7 +516,7 @@ public: | |||||
| private: | private: | ||||
| String name; | String name; | ||||
| LPGUID guid; | |||||
| GUID guid; | |||||
| int sampleRate, bufferSizeSamples; | int sampleRate, bufferSizeSamples; | ||||
| float* leftBuffer; | float* leftBuffer; | ||||
| float* rightBuffer; | float* rightBuffer; | ||||
| @@ -539,7 +539,7 @@ private: | |||||
| struct DSoundInternalInChannel | struct DSoundInternalInChannel | ||||
| { | { | ||||
| public: | public: | ||||
| DSoundInternalInChannel (const String& name_, LPGUID guid_, int rate, | |||||
| DSoundInternalInChannel (const String& name_, const GUID& guid_, int rate, | |||||
| int bufferSize, float* left, float* right) | int bufferSize, float* left, float* right) | ||||
| : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), | : bitDepth (16), name (name_), guid (guid_), sampleRate (rate), | ||||
| bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), | bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right), | ||||
| @@ -597,7 +597,7 @@ public: | |||||
| HRESULT hr = E_NOINTERFACE; | HRESULT hr = E_NOINTERFACE; | ||||
| if (dsDirectSoundCaptureCreate != 0) | if (dsDirectSoundCaptureCreate != 0) | ||||
| hr = dsDirectSoundCaptureCreate (guid, &pDirectSoundCapture, 0); | |||||
| hr = dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, 0); | |||||
| logError (hr); | logError (hr); | ||||
| @@ -773,7 +773,7 @@ public: | |||||
| private: | private: | ||||
| String name; | String name; | ||||
| LPGUID guid; | |||||
| GUID guid; | |||||
| int sampleRate, bufferSizeSamples; | int sampleRate, bufferSizeSamples; | ||||
| float* leftBuffer; | float* leftBuffer; | ||||
| float* rightBuffer; | float* rightBuffer; | ||||
| @@ -1094,26 +1094,17 @@ public: | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice); | 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(); | outputDeviceNames.clear(); | ||||
| outputGuids.clear(); | |||||
| inputDeviceNames.clear(); | inputDeviceNames.clear(); | ||||
| outputGuids.clear(); | |||||
| inputGuids.clear(); | inputGuids.clear(); | ||||
| if (dsDirectSoundEnumerateW != 0) | 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: | private: | ||||
| bool hasScanned; | |||||
| //============================================================================== | |||||
| BOOL outputEnumProc (LPGUID lpGUID, String desc) | |||||
| static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids) | |||||
| { | { | ||||
| desc = desc.trim(); | desc = desc.trim(); | ||||
| @@ -1184,70 +1132,28 @@ private: | |||||
| const String origDesc (desc); | const String origDesc (desc); | ||||
| int n = 2; | int n = 2; | ||||
| while (outputDeviceNames.contains (desc)) | |||||
| while (names.contains (desc)) | |||||
| desc = origDesc + " (" + String (n++) + ")"; | 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; | 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) | 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; | bufferSizeSamples = bufferSizeSamples_ & ~7; | ||||
| DSoundAudioIODeviceType dlh; | |||||
| dlh.scanForDevices(); | |||||
| DSoundDeviceList dlh; | |||||
| dlh.scan(); | |||||
| enabledInputs = inputChannels; | enabledInputs = inputChannels; | ||||
| enabledInputs.setRange (inChannels.size(), | enabledInputs.setRange (inChannels.size(), | ||||
| @@ -1379,6 +1285,91 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, | |||||
| return error; | 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() | AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() | ||||
| { | { | ||||
| @@ -344,4 +344,37 @@ void MessageManager::doPlatformSpecificShutdown() | |||||
| OleUninitialize(); | 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 | #endif | ||||
| @@ -958,15 +958,15 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| class WASAPIAudioIODeviceType : public AudioIODeviceType | |||||
| class WASAPIAudioIODeviceType : public AudioIODeviceType, | |||||
| private DeviceChangeDetector | |||||
| { | { | ||||
| public: | public: | ||||
| WASAPIAudioIODeviceType() | WASAPIAudioIODeviceType() | ||||
| : AudioIODeviceType ("Windows Audio"), | : AudioIODeviceType ("Windows Audio"), | ||||
| deviceChangeCatcher (_T("Windows Audio"), (WNDPROC) deviceChangeEventCallback), | |||||
| DeviceChangeDetector (L"Windows Audio"), | |||||
| hasScanned (false) | hasScanned (false) | ||||
| { | { | ||||
| SetWindowLongPtr (deviceChangeCatcher.getHWND(), GWLP_USERDATA, (LONG_PTR) this); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -1038,7 +1038,6 @@ public: | |||||
| StringArray inputDeviceNames, inputDeviceIds; | StringArray inputDeviceNames, inputDeviceIds; | ||||
| private: | private: | ||||
| HiddenMessageWindow deviceChangeCatcher; | |||||
| bool hasScanned; | 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; | StringArray newOutNames, newInNames, newOutIds, newInIds; | ||||
| scan (newOutNames, newInNames, newOutIds, newInIds); | scan (newOutNames, newInNames, newOutIds, newInIds); | ||||