@@ -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); | ||||