| @@ -725,7 +725,7 @@ private: | |||||
| const Identifier propertyName (o.getPropertyName(j)); | const Identifier propertyName (o.getPropertyName(j)); | ||||
| String val (o.getProperty (propertyName).toString()); | String val (o.getProperty (propertyName).toString()); | ||||
| if (val.isEmpty() || (val.containsAnyOf (" \t;<>()=,-\r\n") | |||||
| if (val.isEmpty() || (val.containsAnyOf (" \t;<>()=,&+-_\r\n") | |||||
| && ! (val.trimStart().startsWithChar ('(') | && ! (val.trimStart().startsWithChar ('(') | ||||
| || val.trimStart().startsWithChar ('{')))) | || val.trimStart().startsWithChar ('{')))) | ||||
| val = val.quoted(); | val = val.quoted(); | ||||
| @@ -233,7 +233,7 @@ public: | |||||
| log ("num IDispatch wrapper objs: " + String (--numDOWID)); | log ("num IDispatch wrapper objs: " + String (--numDOWID)); | ||||
| } | } | ||||
| const var getProperty (const Identifier& propertyName) const | |||||
| var getProperty (const Identifier& propertyName) const | |||||
| { | { | ||||
| const String nameCopy (propertyName.toString()); | const String nameCopy (propertyName.toString()); | ||||
| LPCOLESTR name = nameCopy.toUTF16(); | LPCOLESTR name = nameCopy.toUTF16(); | ||||
| @@ -312,7 +312,7 @@ public: | |||||
| return source->GetIDsOfNames (IID_NULL, (LPOLESTR*) &name, 1, 0, &id) == S_OK; | return source->GetIDsOfNames (IID_NULL, (LPOLESTR*) &name, 1, 0, &id) == S_OK; | ||||
| } | } | ||||
| const var invokeMethod (const Identifier& methodName, const var* parameters, int numParameters) | |||||
| var invokeMethod (const Identifier& methodName, const var* parameters, int numParameters) | |||||
| { | { | ||||
| var returnValue; | var returnValue; | ||||
| const String nameCopy (methodName.toString()); | const String nameCopy (methodName.toString()); | ||||
| @@ -572,7 +572,7 @@ public: | |||||
| DBG ("num NP wrapper objs: " + String (--numDOWNP)); | DBG ("num NP wrapper objs: " + String (--numDOWNP)); | ||||
| } | } | ||||
| const var getProperty (const var::identifier& propertyName) const | |||||
| var getProperty (const var::identifier& propertyName) const | |||||
| { | { | ||||
| NPVariant result; | NPVariant result; | ||||
| VOID_TO_NPVARIANT (result); | VOID_TO_NPVARIANT (result); | ||||
| @@ -610,8 +610,8 @@ public: | |||||
| return browser.hasmethod (npp, source, getIdentifierFromString (methodName)); | return browser.hasmethod (npp, source, getIdentifierFromString (methodName)); | ||||
| } | } | ||||
| const var invokeMethod (const var::identifier& methodName, | |||||
| const var* parameters, | |||||
| var invokeMethod (const var::identifier& methodName, | |||||
| const var* parameters, | |||||
| int numParameters) | int numParameters) | ||||
| { | { | ||||
| var returnVal; | var returnVal; | ||||
| @@ -26751,6 +26751,9 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() | |||||
| if (availableDeviceTypes.size() > 0) | if (availableDeviceTypes.size() > 0) | ||||
| currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | ||||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||||
| availableDeviceTypes.getUnchecked(i)->addListener (&callbackHandler); | |||||
| } | } | ||||
| } | } | ||||
| @@ -26760,6 +26763,11 @@ const OwnedArray <AudioIODeviceType>& AudioDeviceManager::getAvailableDeviceType | |||||
| return availableDeviceTypes; | return availableDeviceTypes; | ||||
| } | } | ||||
| void AudioDeviceManager::audioDeviceListChanged() | |||||
| { | |||||
| sendChangeMessage(); | |||||
| } | |||||
| static void addIfNotNull (OwnedArray <AudioIODeviceType>& list, AudioIODeviceType* const device) | static void addIfNotNull (OwnedArray <AudioIODeviceType>& list, AudioIODeviceType* const device) | ||||
| { | { | ||||
| if (device != nullptr) | if (device != nullptr) | ||||
| @@ -27528,6 +27536,11 @@ void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput* | |||||
| owner->handleIncomingMidiMessageInt (source, message); | owner->handleIncomingMidiMessageInt (source, message); | ||||
| } | } | ||||
| void AudioDeviceManager::CallbackHandler::audioDeviceListChanged() | |||||
| { | |||||
| owner->audioDeviceListChanged(); | |||||
| } | |||||
| void AudioDeviceManager::playTestSound() | void AudioDeviceManager::playTestSound() | ||||
| { | { | ||||
| { // cunningly nested to swap, unlock and delete in that order. | { // cunningly nested to swap, unlock and delete in that order. | ||||
| @@ -27632,6 +27645,14 @@ AudioIODeviceType::~AudioIODeviceType() | |||||
| { | { | ||||
| } | } | ||||
| void AudioIODeviceType::addListener (Listener* l) { listeners.add (l); } | |||||
| void AudioIODeviceType::removeListener (Listener* l) { listeners.remove (l); } | |||||
| void AudioIODeviceType::callDeviceChangeListeners() | |||||
| { | |||||
| listeners.call (&AudioIODeviceType::Listener::audioDeviceListChanged); | |||||
| } | |||||
| #if ! JUCE_MAC | #if ! JUCE_MAC | ||||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } | AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } | ||||
| #endif | #endif | ||||
| @@ -55142,14 +55163,18 @@ void TextEditor::setText (const String& newText, | |||||
| } | } | ||||
| } | } | ||||
| Value& TextEditor::getTextValue() | |||||
| void TextEditor::updateValueFromText() | |||||
| { | { | ||||
| if (valueTextNeedsUpdating) | if (valueTextNeedsUpdating) | ||||
| { | { | ||||
| valueTextNeedsUpdating = false; | valueTextNeedsUpdating = false; | ||||
| textValue = getText(); | textValue = getText(); | ||||
| } | } | ||||
| } | |||||
| Value& TextEditor::getTextValue() | |||||
| { | |||||
| updateValueFromText(); | |||||
| return textValue; | return textValue; | ||||
| } | } | ||||
| @@ -56082,6 +56107,7 @@ void TextEditor::handleCommandMessage (const int commandId) | |||||
| break; | break; | ||||
| case TextEditorDefs::focusLossMessageId: | case TextEditorDefs::focusLossMessageId: | ||||
| updateValueFromText(); | |||||
| listeners.callChecked (checker, &TextEditorListener::textEditorFocusLost, (TextEditor&) *this); | listeners.callChecked (checker, &TextEditorListener::textEditorFocusLost, (TextEditor&) *this); | ||||
| break; | break; | ||||
| @@ -75025,6 +75051,14 @@ private: | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputSelectorComponentListBox); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputSelectorComponentListBox); | ||||
| }; | }; | ||||
| struct AudioDeviceSetupDetails | |||||
| { | |||||
| AudioDeviceManager* manager; | |||||
| int minNumInputChannels, maxNumInputChannels; | |||||
| int minNumOutputChannels, maxNumOutputChannels; | |||||
| bool useStereoPairs; | |||||
| }; | |||||
| class AudioDeviceSettingsPanel : public Component, | class AudioDeviceSettingsPanel : public Component, | ||||
| public ChangeListener, | public ChangeListener, | ||||
| public ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug) | public ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug) | ||||
| @@ -75032,7 +75066,7 @@ class AudioDeviceSettingsPanel : public Component, | |||||
| { | { | ||||
| public: | public: | ||||
| AudioDeviceSettingsPanel (AudioIODeviceType* type_, | AudioDeviceSettingsPanel (AudioIODeviceType* type_, | ||||
| AudioIODeviceType::DeviceSetupDetails& setup_, | |||||
| AudioDeviceSetupDetails& setup_, | |||||
| const bool hideAdvancedOptionsWithButton) | const bool hideAdvancedOptionsWithButton) | ||||
| : type (type_), | : type (type_), | ||||
| setup (setup_) | setup (setup_) | ||||
| @@ -75302,7 +75336,7 @@ public: | |||||
| private: | private: | ||||
| AudioIODeviceType* const type; | AudioIODeviceType* const type; | ||||
| const AudioIODeviceType::DeviceSetupDetails setup; | |||||
| const AudioDeviceSetupDetails setup; | |||||
| ScopedPointer<ComboBox> outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown; | ScopedPointer<ComboBox> outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown; | ||||
| ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel; | ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel; | ||||
| @@ -75487,7 +75521,7 @@ public: | |||||
| audioOutputType | audioOutputType | ||||
| }; | }; | ||||
| ChannelSelectorListBox (const AudioIODeviceType::DeviceSetupDetails& setup_, | |||||
| ChannelSelectorListBox (const AudioDeviceSetupDetails& setup_, | |||||
| const BoxType type_, | const BoxType type_, | ||||
| const String& noItemsMessage_) | const String& noItemsMessage_) | ||||
| : ListBox (String::empty, nullptr), | : ListBox (String::empty, nullptr), | ||||
| @@ -75630,7 +75664,7 @@ public: | |||||
| private: | private: | ||||
| const AudioIODeviceType::DeviceSetupDetails setup; | |||||
| const AudioDeviceSetupDetails setup; | |||||
| const BoxType type; | const BoxType type; | ||||
| const String noItemsMessage; | const String noItemsMessage; | ||||
| StringArray items; | StringArray items; | ||||
| @@ -75900,7 +75934,7 @@ void AudioDeviceSelectorComponent::updateAllControls() | |||||
| if (type != nullptr) | if (type != nullptr) | ||||
| { | { | ||||
| AudioIODeviceType::DeviceSetupDetails details; | |||||
| AudioDeviceSetupDetails details; | |||||
| details.manager = &deviceManager; | details.manager = &deviceManager; | ||||
| details.minNumInputChannels = minInputChannels; | details.minNumInputChannels = minInputChannels; | ||||
| details.maxNumInputChannels = maxInputChannels; | details.maxNumInputChannels = maxInputChannels; | ||||
| @@ -246925,13 +246959,52 @@ void PlatformUtilities::beep() | |||||
| // compiled on its own). | // compiled on its own). | ||||
| #if JUCE_INCLUDED_FILE | #if JUCE_INCLUDED_FILE | ||||
| class HiddenMessageWindow | |||||
| { | |||||
| public: | |||||
| HiddenMessageWindow (const TCHAR* const messageWindowName, WNDPROC wndProc) | |||||
| { | |||||
| String className ("JUCE_"); | |||||
| className << String::toHexString (Time::getHighResolutionTicks()); | |||||
| HMODULE moduleHandle = (HMODULE) PlatformUtilities::getCurrentModuleInstanceHandle(); | |||||
| WNDCLASSEX wc = { 0 }; | |||||
| wc.cbSize = sizeof (wc); | |||||
| wc.lpfnWndProc = wndProc; | |||||
| wc.cbWndExtra = 4; | |||||
| wc.hInstance = moduleHandle; | |||||
| wc.lpszClassName = className.toWideCharPointer(); | |||||
| atom = RegisterClassEx (&wc); | |||||
| jassert (atom != 0); | |||||
| hwnd = CreateWindow (getClassNameFromAtom(), messageWindowName, | |||||
| 0, 0, 0, 0, 0, 0, 0, moduleHandle, 0); | |||||
| jassert (hwnd != 0); | |||||
| } | |||||
| ~HiddenMessageWindow() | |||||
| { | |||||
| DestroyWindow (hwnd); | |||||
| UnregisterClass (getClassNameFromAtom(), 0); | |||||
| } | |||||
| inline HWND getHWND() const noexcept { return hwnd; } | |||||
| private: | |||||
| ATOM atom; | |||||
| HWND hwnd; | |||||
| LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) MAKELONG (atom, 0); } | |||||
| }; | |||||
| static const unsigned int specialId = WM_APP + 0x4400; | static const unsigned int specialId = WM_APP + 0x4400; | ||||
| static const unsigned int broadcastId = WM_APP + 0x4403; | static const unsigned int broadcastId = WM_APP + 0x4403; | ||||
| static const unsigned int specialCallbackId = WM_APP + 0x4402; | static const unsigned int specialCallbackId = WM_APP + 0x4402; | ||||
| static const TCHAR* const messageWindowName = _T("JUCEWindow"); | |||||
| static ATOM messageWindowClassAtom = 0; | |||||
| static LPCTSTR getMessageWindowClassName() noexcept { return (LPCTSTR) MAKELONG (messageWindowClassAtom, 0); } | |||||
| static const TCHAR messageWindowName[] = _T("JUCEWindow"); | |||||
| static ScopedPointer<HiddenMessageWindow> messageWindow; | |||||
| HWND juce_messageWindowHandle = 0; | HWND juce_messageWindowHandle = 0; | ||||
| @@ -247149,34 +247222,14 @@ void MessageManager::doPlatformSpecificInitialisation() | |||||
| { | { | ||||
| OleInitialize (0); | OleInitialize (0); | ||||
| // this name has to be different for each app/dll instance because otherwise | |||||
| // poor old Win32 can get a bit confused (even despite it not being a process-global | |||||
| // window class). | |||||
| String className ("JUCEcs_"); | |||||
| className << (int) (Time::getHighResolutionTicks() & 0x7fffffff); | |||||
| HMODULE moduleHandle = (HMODULE) PlatformUtilities::getCurrentModuleInstanceHandle(); | |||||
| WNDCLASSEX wc = { 0 }; | |||||
| wc.cbSize = sizeof (wc); | |||||
| wc.lpfnWndProc = (WNDPROC) juce_MessageWndProc; | |||||
| wc.cbWndExtra = 4; | |||||
| wc.hInstance = moduleHandle; | |||||
| wc.lpszClassName = className.toWideCharPointer(); | |||||
| messageWindowClassAtom = RegisterClassEx (&wc); | |||||
| jassert (messageWindowClassAtom != 0); | |||||
| juce_messageWindowHandle = CreateWindow (getMessageWindowClassName(), messageWindowName, | |||||
| 0, 0, 0, 0, 0, 0, 0, moduleHandle, 0); | |||||
| jassert (juce_messageWindowHandle != 0); | |||||
| messageWindow = new HiddenMessageWindow (messageWindowName, (WNDPROC) juce_MessageWndProc); | |||||
| juce_messageWindowHandle = messageWindow->getHWND(); | |||||
| } | } | ||||
| void MessageManager::doPlatformSpecificShutdown() | void MessageManager::doPlatformSpecificShutdown() | ||||
| { | { | ||||
| DestroyWindow (juce_messageWindowHandle); | |||||
| UnregisterClass (getMessageWindowClassName(), 0); | |||||
| messageWindow = nullptr; | |||||
| OleUninitialize(); | OleUninitialize(); | ||||
| } | } | ||||
| @@ -260473,8 +260526,6 @@ public: | |||||
| bool initialise() | bool initialise() | ||||
| { | { | ||||
| double defaultSampleRateIn = 0, defaultSampleRateOut = 0; | |||||
| int minBufferSizeIn = 0, defaultBufferSizeIn = 0, minBufferSizeOut = 0, defaultBufferSizeOut = 0; | |||||
| latencyIn = latencyOut = 0; | latencyIn = latencyOut = 0; | ||||
| Array <double> ratesIn, ratesOut; | Array <double> ratesIn, ratesOut; | ||||
| @@ -260800,12 +260851,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType | |||||
| public: | public: | ||||
| WASAPIAudioIODeviceType() | WASAPIAudioIODeviceType() | ||||
| : AudioIODeviceType ("Windows Audio"), | : AudioIODeviceType ("Windows Audio"), | ||||
| deviceChangeCatcher (_T("Windows Audio"), (WNDPROC) deviceChangeEventCallback), | |||||
| hasScanned (false) | hasScanned (false) | ||||
| { | { | ||||
| } | |||||
| ~WASAPIAudioIODeviceType() | |||||
| { | |||||
| SetWindowLongPtr (deviceChangeCatcher.getHWND(), GWLP_USERDATA, (LONG_PTR) this); | |||||
| } | } | ||||
| void scanForDevices() | void scanForDevices() | ||||
| @@ -260817,68 +260866,8 @@ public: | |||||
| outputDeviceIds.clear(); | outputDeviceIds.clear(); | ||||
| inputDeviceIds.clear(); | inputDeviceIds.clear(); | ||||
| ComSmartPtr <IMMDeviceEnumerator> enumerator; | |||||
| if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) | |||||
| return; | |||||
| const String defaultRenderer = getDefaultEndpoint (enumerator, false); | |||||
| const String defaultCapture = getDefaultEndpoint (enumerator, true); | |||||
| ComSmartPtr <IMMDeviceCollection> deviceCollection; | |||||
| UINT32 numDevices = 0; | |||||
| if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())) | |||||
| && check (deviceCollection->GetCount (&numDevices)))) | |||||
| return; | |||||
| for (UINT32 i = 0; i < numDevices; ++i) | |||||
| { | |||||
| ComSmartPtr <IMMDevice> device; | |||||
| if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress()))) | |||||
| continue; | |||||
| const String deviceId (getDeviceID (device)); | |||||
| DWORD state = 0; | |||||
| if (! check (device->GetState (&state))) | |||||
| continue; | |||||
| if (state != DEVICE_STATE_ACTIVE) | |||||
| continue; | |||||
| String name; | |||||
| { | |||||
| ComSmartPtr <IPropertyStore> properties; | |||||
| if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress()))) | |||||
| continue; | |||||
| PROPVARIANT value; | |||||
| PropVariantInit (&value); | |||||
| if (check (properties->GetValue (PKEY_Device_FriendlyName, &value))) | |||||
| name = value.pwszVal; | |||||
| PropVariantClear (&value); | |||||
| } | |||||
| const EDataFlow flow = getDataFlow (device); | |||||
| if (flow == eRender) | |||||
| { | |||||
| const int index = (deviceId == defaultRenderer) ? 0 : -1; | |||||
| outputDeviceIds.insert (index, deviceId); | |||||
| outputDeviceNames.insert (index, name); | |||||
| } | |||||
| else if (flow == eCapture) | |||||
| { | |||||
| const int index = (deviceId == defaultCapture) ? 0 : -1; | |||||
| inputDeviceIds.insert (index, deviceId); | |||||
| inputDeviceNames.insert (index, name); | |||||
| } | |||||
| } | |||||
| inputDeviceNames.appendNumbersToDuplicates (false, false); | |||||
| outputDeviceNames.appendNumbersToDuplicates (false, false); | |||||
| scan (outputDeviceNames, inputDeviceNames, | |||||
| outputDeviceIds, inputDeviceIds); | |||||
| } | } | ||||
| StringArray getDeviceNames (bool wantInputNames) const | StringArray getDeviceNames (bool wantInputNames) const | ||||
| @@ -260935,6 +260924,7 @@ 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) | ||||
| @@ -260947,7 +260937,7 @@ private: | |||||
| WCHAR* deviceId = nullptr; | WCHAR* deviceId = nullptr; | ||||
| if (check (dev->GetId (&deviceId))) | if (check (dev->GetId (&deviceId))) | ||||
| { | { | ||||
| s = String (deviceId); | |||||
| s = deviceId; | |||||
| CoTaskMemFree (deviceId); | CoTaskMemFree (deviceId); | ||||
| } | } | ||||
| @@ -260957,6 +260947,104 @@ private: | |||||
| return s; | return s; | ||||
| } | } | ||||
| void scan (StringArray& outputDeviceNames, | |||||
| StringArray& inputDeviceNames, | |||||
| StringArray& outputDeviceIds, | |||||
| StringArray& inputDeviceIds) | |||||
| { | |||||
| ComSmartPtr <IMMDeviceEnumerator> enumerator; | |||||
| if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) | |||||
| return; | |||||
| const String defaultRenderer (getDefaultEndpoint (enumerator, false)); | |||||
| const String defaultCapture (getDefaultEndpoint (enumerator, true)); | |||||
| ComSmartPtr <IMMDeviceCollection> deviceCollection; | |||||
| UINT32 numDevices = 0; | |||||
| if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())) | |||||
| && check (deviceCollection->GetCount (&numDevices)))) | |||||
| return; | |||||
| for (UINT32 i = 0; i < numDevices; ++i) | |||||
| { | |||||
| ComSmartPtr <IMMDevice> device; | |||||
| if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress()))) | |||||
| continue; | |||||
| DWORD state = 0; | |||||
| if (! (check (device->GetState (&state)) && state == DEVICE_STATE_ACTIVE)) | |||||
| continue; | |||||
| const String deviceId (getDeviceID (device)); | |||||
| String name; | |||||
| { | |||||
| ComSmartPtr <IPropertyStore> properties; | |||||
| if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress()))) | |||||
| continue; | |||||
| PROPVARIANT value; | |||||
| PropVariantInit (&value); | |||||
| if (check (properties->GetValue (PKEY_Device_FriendlyName, &value))) | |||||
| name = value.pwszVal; | |||||
| PropVariantClear (&value); | |||||
| } | |||||
| const EDataFlow flow = getDataFlow (device); | |||||
| if (flow == eRender) | |||||
| { | |||||
| const int index = (deviceId == defaultRenderer) ? 0 : -1; | |||||
| outputDeviceIds.insert (index, deviceId); | |||||
| outputDeviceNames.insert (index, name); | |||||
| } | |||||
| else if (flow == eCapture) | |||||
| { | |||||
| const int index = (deviceId == defaultCapture) ? 0 : -1; | |||||
| inputDeviceIds.insert (index, deviceId); | |||||
| inputDeviceNames.insert (index, name); | |||||
| } | |||||
| } | |||||
| inputDeviceNames.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*/)) | |||||
| { | |||||
| ((WASAPIAudioIODeviceType*) GetWindowLongPtr (h, GWLP_USERDATA))->handleDeviceChange(); | |||||
| } | |||||
| return DefWindowProc (h, message, wParam, lParam); | |||||
| } | |||||
| void handleDeviceChange() | |||||
| { | |||||
| StringArray newOutNames, newInNames, newOutIds, newInIds; | |||||
| scan (newOutNames, newInNames, newOutIds, newInIds); | |||||
| if (newOutNames != outputDeviceNames | |||||
| || newInNames != inputDeviceNames | |||||
| || newOutIds != outputDeviceIds | |||||
| || newInIds != inputDeviceIds) | |||||
| { | |||||
| hasScanned = true; | |||||
| outputDeviceNames = newOutNames; | |||||
| inputDeviceNames = newInNames; | |||||
| outputDeviceIds = newOutIds; | |||||
| inputDeviceIds = newInIds; | |||||
| callDeviceChangeListeners(); | |||||
| } | |||||
| } | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType); | ||||
| }; | }; | ||||
| @@ -277275,10 +277363,10 @@ public: | |||||
| view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(), | view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(), | ||||
| (CGFloat) bounds.getWidth(), (CGFloat) bounds.getHeight()); | (CGFloat) bounds.getWidth(), (CGFloat) bounds.getHeight()); | ||||
| if (lastWidth != w || lastHeight != h) | |||||
| if (lastWidth != bounds.getWidth() || lastHeight != bounds.getHeight()) | |||||
| { | { | ||||
| lastWidth = w; | |||||
| lastHeight = h; | |||||
| lastWidth = bounds.getWidth(); | |||||
| lastHeight = bounds.getHeight(); | |||||
| freeGLBuffers(); | freeGLBuffers(); | ||||
| createGLBuffers(); | createGLBuffers(); | ||||
| } | } | ||||
| @@ -280726,6 +280814,7 @@ public: | |||||
| virtual void drawRect (NSRect r); | virtual void drawRect (NSRect r); | ||||
| virtual bool canBecomeKeyWindow(); | virtual bool canBecomeKeyWindow(); | ||||
| virtual void becomeKeyWindow(); | |||||
| virtual bool windowShouldClose(); | virtual bool windowShouldClose(); | ||||
| virtual void redirectMovedOrResized(); | virtual void redirectMovedOrResized(); | ||||
| @@ -281186,7 +281275,7 @@ END_JUCE_NAMESPACE | |||||
| [super becomeKeyWindow]; | [super becomeKeyWindow]; | ||||
| if (owner != nullptr) | if (owner != nullptr) | ||||
| owner->grabFocus(); | |||||
| owner->becomeKeyWindow(); | |||||
| } | } | ||||
| - (BOOL) windowShouldClose: (id) window | - (BOOL) windowShouldClose: (id) window | ||||
| @@ -282097,6 +282186,12 @@ bool NSViewComponentPeer::canBecomeKeyWindow() | |||||
| return (getStyleFlags() & JUCE_NAMESPACE::ComponentPeer::windowIgnoresKeyPresses) == 0; | return (getStyleFlags() & JUCE_NAMESPACE::ComponentPeer::windowIgnoresKeyPresses) == 0; | ||||
| } | } | ||||
| void NSViewComponentPeer::becomeKeyWindow() | |||||
| { | |||||
| handleBroughtToFront(); | |||||
| grabFocus(); | |||||
| } | |||||
| bool NSViewComponentPeer::windowShouldClose() | bool NSViewComponentPeer::windowShouldClose() | ||||
| { | { | ||||
| if (! isValidPeer (this)) | if (! isValidPeer (this)) | ||||
| @@ -283188,10 +283283,10 @@ public: | |||||
| view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(), | view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(), | ||||
| (CGFloat) bounds.getWidth(), (CGFloat) bounds.getHeight()); | (CGFloat) bounds.getWidth(), (CGFloat) bounds.getHeight()); | ||||
| if (lastWidth != w || lastHeight != h) | |||||
| if (lastWidth != bounds.getWidth() || lastHeight != bounds.getHeight()) | |||||
| { | { | ||||
| lastWidth = w; | |||||
| lastHeight = h; | |||||
| lastWidth = bounds.getWidth(); | |||||
| lastHeight = bounds.getHeight(); | |||||
| freeGLBuffers(); | freeGLBuffers(); | ||||
| createGLBuffers(); | createGLBuffers(); | ||||
| } | } | ||||
| @@ -286984,6 +287079,22 @@ public: | |||||
| : AudioIODeviceType ("CoreAudio"), | : AudioIODeviceType ("CoreAudio"), | ||||
| hasScanned (false) | hasScanned (false) | ||||
| { | { | ||||
| AudioObjectPropertyAddress pa; | |||||
| pa.mSelector = kAudioHardwarePropertyDevices; | |||||
| pa.mScope = kAudioObjectPropertyScopeWildcard; | |||||
| pa.mElement = kAudioObjectPropertyElementWildcard; | |||||
| AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); | |||||
| } | |||||
| ~CoreAudioIODeviceType() | |||||
| { | |||||
| AudioObjectPropertyAddress pa; | |||||
| pa.mSelector = kAudioHardwarePropertyDevices; | |||||
| pa.mScope = kAudioObjectPropertyScopeWildcard; | |||||
| pa.mElement = kAudioObjectPropertyElementWildcard; | |||||
| AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); | |||||
| } | } | ||||
| void scanForDevices() | void scanForDevices() | ||||
| @@ -287156,6 +287267,18 @@ private: | |||||
| return total; | return total; | ||||
| } | } | ||||
| void audioDeviceListChanged() | |||||
| { | |||||
| scanForDevices(); | |||||
| callDeviceChangeListeners(); | |||||
| } | |||||
| static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) | |||||
| { | |||||
| static_cast <CoreAudioIODeviceType*> (clientData)->audioDeviceListChanged(); | |||||
| return noErr; | |||||
| } | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType); | ||||
| }; | }; | ||||
| @@ -73,7 +73,7 @@ namespace JuceDummyNamespace {} | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 53 | #define JUCE_MINOR_VERSION 53 | ||||
| #define JUCE_BUILDNUMBER 104 | |||||
| #define JUCE_BUILDNUMBER 105 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -39887,14 +39887,32 @@ public: | |||||
| virtual AudioIODevice* createDevice (const String& outputDeviceName, | virtual AudioIODevice* createDevice (const String& outputDeviceName, | ||||
| const String& inputDeviceName) = 0; | const String& inputDeviceName) = 0; | ||||
| struct DeviceSetupDetails | |||||
| /** | |||||
| A class for receiving events when audio devices are inserted or removed. | |||||
| You can register a AudioIODeviceType::Listener with an~AudioIODeviceType object | |||||
| using the AudioIODeviceType::addListener() method, and it will be called when | |||||
| devices of that type are added or removed. | |||||
| @see AudioIODeviceType::addListener, AudioIODeviceType::removeListener | |||||
| */ | |||||
| class Listener | |||||
| { | { | ||||
| AudioDeviceManager* manager; | |||||
| int minNumInputChannels, maxNumInputChannels; | |||||
| int minNumOutputChannels, maxNumOutputChannels; | |||||
| bool useStereoPairs; | |||||
| public: | |||||
| virtual ~Listener() {} | |||||
| /** Called when the list of available audio devices changes. */ | |||||
| virtual void audioDeviceListChanged() = 0; | |||||
| }; | }; | ||||
| /** Adds a listener that will be called when this type of device is added or | |||||
| removed from the system. | |||||
| */ | |||||
| void addListener (Listener* listener); | |||||
| /** Removes a listener that was previously added with addListener(). */ | |||||
| void removeListener (Listener* listener); | |||||
| /** Destructor. */ | /** Destructor. */ | ||||
| virtual ~AudioIODeviceType(); | virtual ~AudioIODeviceType(); | ||||
| @@ -39918,8 +39936,12 @@ public: | |||||
| protected: | protected: | ||||
| explicit AudioIODeviceType (const String& typeName); | explicit AudioIODeviceType (const String& typeName); | ||||
| /** Synchronously calls all the registered device list change listeners. */ | |||||
| void callDeviceChangeListeners(); | |||||
| private: | private: | ||||
| String typeName; | String typeName; | ||||
| ListenerList<Listener> listeners; | |||||
| JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType); | JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType); | ||||
| }; | }; | ||||
| @@ -41733,13 +41755,15 @@ private: | |||||
| double cpuUsageMs, timeToCpuScale; | double cpuUsageMs, timeToCpuScale; | ||||
| class CallbackHandler : public AudioIODeviceCallback, | class CallbackHandler : public AudioIODeviceCallback, | ||||
| public MidiInputCallback | |||||
| public MidiInputCallback, | |||||
| public AudioIODeviceType::Listener | |||||
| { | { | ||||
| public: | public: | ||||
| void audioDeviceIOCallback (const float**, int, float**, int, int); | void audioDeviceIOCallback (const float**, int, float**, int, int); | ||||
| void audioDeviceAboutToStart (AudioIODevice*); | void audioDeviceAboutToStart (AudioIODevice*); | ||||
| void audioDeviceStopped(); | void audioDeviceStopped(); | ||||
| void handleIncomingMidiMessage (MidiInput*, const MidiMessage&); | void handleIncomingMidiMessage (MidiInput*, const MidiMessage&); | ||||
| void audioDeviceListChanged(); | |||||
| AudioDeviceManager* owner; | AudioDeviceManager* owner; | ||||
| }; | }; | ||||
| @@ -41752,6 +41776,7 @@ private: | |||||
| void audioDeviceAboutToStartInt (AudioIODevice*); | void audioDeviceAboutToStartInt (AudioIODevice*); | ||||
| void audioDeviceStoppedInt(); | void audioDeviceStoppedInt(); | ||||
| void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&); | void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&); | ||||
| void audioDeviceListChanged(); | |||||
| String restartDevice (int blockSizeToUse, double sampleRateToUse, | String restartDevice (int blockSizeToUse, double sampleRateToUse, | ||||
| const BigInteger& ins, const BigInteger& outs); | const BigInteger& ins, const BigInteger& outs); | ||||
| @@ -41764,7 +41789,7 @@ private: | |||||
| void deleteCurrentDevice(); | void deleteCurrentDevice(); | ||||
| double chooseBestSampleRate (double preferred) const; | double chooseBestSampleRate (double preferred) const; | ||||
| int chooseBestBufferSize (int preferred) const; | int chooseBestBufferSize (int preferred) const; | ||||
| void insertDefaultDeviceNames (AudioDeviceSetup& setup) const; | |||||
| void insertDefaultDeviceNames (AudioDeviceSetup&) const; | |||||
| AudioIODeviceType* findType (const String& inputName, const String& outputName); | AudioIODeviceType* findType (const String& inputName, const String& outputName); | ||||
| @@ -52451,6 +52476,7 @@ private: | |||||
| void remove (const Range<int>& range, UndoManager* um, int caretPositionToMoveTo); | void remove (const Range<int>& range, UndoManager* um, int caretPositionToMoveTo); | ||||
| void getCharPosition (int index, float& x, float& y, float& lineHeight) const; | void getCharPosition (int index, float& x, float& y, float& lineHeight) const; | ||||
| void updateCaretPosition(); | void updateCaretPosition(); | ||||
| void updateValueFromText(); | |||||
| void textWasChangedByValue(); | void textWasChangedByValue(); | ||||
| int indexAtPosition (float x, float y); | int indexAtPosition (float x, float y); | ||||
| int findWordBreakAfter (int position) const; | int findWordBreakAfter (int position) const; | ||||
| @@ -85,6 +85,9 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() | |||||
| if (availableDeviceTypes.size() > 0) | if (availableDeviceTypes.size() > 0) | ||||
| currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | ||||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||||
| availableDeviceTypes.getUnchecked(i)->addListener (&callbackHandler); | |||||
| } | } | ||||
| } | } | ||||
| @@ -94,6 +97,11 @@ const OwnedArray <AudioIODeviceType>& AudioDeviceManager::getAvailableDeviceType | |||||
| return availableDeviceTypes; | return availableDeviceTypes; | ||||
| } | } | ||||
| void AudioDeviceManager::audioDeviceListChanged() | |||||
| { | |||||
| sendChangeMessage(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| static void addIfNotNull (OwnedArray <AudioIODeviceType>& list, AudioIODeviceType* const device) | static void addIfNotNull (OwnedArray <AudioIODeviceType>& list, AudioIODeviceType* const device) | ||||
| { | { | ||||
| @@ -869,6 +877,11 @@ void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput* | |||||
| owner->handleIncomingMidiMessageInt (source, message); | owner->handleIncomingMidiMessageInt (source, message); | ||||
| } | } | ||||
| void AudioDeviceManager::CallbackHandler::audioDeviceListChanged() | |||||
| { | |||||
| owner->audioDeviceListChanged(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void AudioDeviceManager::playTestSound() | void AudioDeviceManager::playTestSound() | ||||
| { | { | ||||
| @@ -476,13 +476,15 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| class CallbackHandler : public AudioIODeviceCallback, | class CallbackHandler : public AudioIODeviceCallback, | ||||
| public MidiInputCallback | |||||
| public MidiInputCallback, | |||||
| public AudioIODeviceType::Listener | |||||
| { | { | ||||
| public: | public: | ||||
| void audioDeviceIOCallback (const float**, int, float**, int, int); | void audioDeviceIOCallback (const float**, int, float**, int, int); | ||||
| void audioDeviceAboutToStart (AudioIODevice*); | void audioDeviceAboutToStart (AudioIODevice*); | ||||
| void audioDeviceStopped(); | void audioDeviceStopped(); | ||||
| void handleIncomingMidiMessage (MidiInput*, const MidiMessage&); | void handleIncomingMidiMessage (MidiInput*, const MidiMessage&); | ||||
| void audioDeviceListChanged(); | |||||
| AudioDeviceManager* owner; | AudioDeviceManager* owner; | ||||
| }; | }; | ||||
| @@ -495,6 +497,7 @@ private: | |||||
| void audioDeviceAboutToStartInt (AudioIODevice*); | void audioDeviceAboutToStartInt (AudioIODevice*); | ||||
| void audioDeviceStoppedInt(); | void audioDeviceStoppedInt(); | ||||
| void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&); | void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&); | ||||
| void audioDeviceListChanged(); | |||||
| String restartDevice (int blockSizeToUse, double sampleRateToUse, | String restartDevice (int blockSizeToUse, double sampleRateToUse, | ||||
| const BigInteger& ins, const BigInteger& outs); | const BigInteger& ins, const BigInteger& outs); | ||||
| @@ -507,7 +510,7 @@ private: | |||||
| void deleteCurrentDevice(); | void deleteCurrentDevice(); | ||||
| double chooseBestSampleRate (double preferred) const; | double chooseBestSampleRate (double preferred) const; | ||||
| int chooseBestBufferSize (int preferred) const; | int chooseBestBufferSize (int preferred) const; | ||||
| void insertDefaultDeviceNames (AudioDeviceSetup& setup) const; | |||||
| void insertDefaultDeviceNames (AudioDeviceSetup&) const; | |||||
| AudioIODeviceType* findType (const String& inputName, const String& outputName); | AudioIODeviceType* findType (const String& inputName, const String& outputName); | ||||
| @@ -40,6 +40,16 @@ AudioIODeviceType::~AudioIODeviceType() | |||||
| { | { | ||||
| } | } | ||||
| //============================================================================== | |||||
| void AudioIODeviceType::addListener (Listener* l) { listeners.add (l); } | |||||
| void AudioIODeviceType::removeListener (Listener* l) { listeners.remove (l); } | |||||
| void AudioIODeviceType::callDeviceChangeListeners() | |||||
| { | |||||
| listeners.call (&AudioIODeviceType::Listener::audioDeviceListChanged); | |||||
| } | |||||
| //============================================================================== | |||||
| #if ! JUCE_MAC | #if ! JUCE_MAC | ||||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } | AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } | ||||
| #endif | #endif | ||||
| @@ -27,6 +27,7 @@ | |||||
| #define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ | #define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ | ||||
| #include "juce_AudioIODevice.h" | #include "juce_AudioIODevice.h" | ||||
| #include "../../events/juce_ListenerList.h" | |||||
| class AudioDeviceManager; | class AudioDeviceManager; | ||||
| @@ -119,14 +120,32 @@ public: | |||||
| const String& inputDeviceName) = 0; | const String& inputDeviceName) = 0; | ||||
| //============================================================================== | //============================================================================== | ||||
| struct DeviceSetupDetails | |||||
| /** | |||||
| A class for receiving events when audio devices are inserted or removed. | |||||
| You can register a AudioIODeviceType::Listener with an~AudioIODeviceType object | |||||
| using the AudioIODeviceType::addListener() method, and it will be called when | |||||
| devices of that type are added or removed. | |||||
| @see AudioIODeviceType::addListener, AudioIODeviceType::removeListener | |||||
| */ | |||||
| class Listener | |||||
| { | { | ||||
| AudioDeviceManager* manager; | |||||
| int minNumInputChannels, maxNumInputChannels; | |||||
| int minNumOutputChannels, maxNumOutputChannels; | |||||
| bool useStereoPairs; | |||||
| public: | |||||
| virtual ~Listener() {} | |||||
| /** Called when the list of available audio devices changes. */ | |||||
| virtual void audioDeviceListChanged() = 0; | |||||
| }; | }; | ||||
| /** Adds a listener that will be called when this type of device is added or | |||||
| removed from the system. | |||||
| */ | |||||
| void addListener (Listener* listener); | |||||
| /** Removes a listener that was previously added with addListener(). */ | |||||
| void removeListener (Listener* listener); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Destructor. */ | /** Destructor. */ | ||||
| virtual ~AudioIODeviceType(); | virtual ~AudioIODeviceType(); | ||||
| @@ -152,8 +171,12 @@ public: | |||||
| protected: | protected: | ||||
| explicit AudioIODeviceType (const String& typeName); | explicit AudioIODeviceType (const String& typeName); | ||||
| /** Synchronously calls all the registered device list change listeners. */ | |||||
| void callDeviceChangeListeners(); | |||||
| private: | private: | ||||
| String typeName; | String typeName; | ||||
| ListenerList<Listener> listeners; | |||||
| JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType); | JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType); | ||||
| }; | }; | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 53 | #define JUCE_MINOR_VERSION 53 | ||||
| #define JUCE_BUILDNUMBER 104 | |||||
| #define JUCE_BUILDNUMBER 105 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -1270,14 +1270,18 @@ void TextEditor::setText (const String& newText, | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| Value& TextEditor::getTextValue() | |||||
| void TextEditor::updateValueFromText() | |||||
| { | { | ||||
| if (valueTextNeedsUpdating) | if (valueTextNeedsUpdating) | ||||
| { | { | ||||
| valueTextNeedsUpdating = false; | valueTextNeedsUpdating = false; | ||||
| textValue = getText(); | textValue = getText(); | ||||
| } | } | ||||
| } | |||||
| Value& TextEditor::getTextValue() | |||||
| { | |||||
| updateValueFromText(); | |||||
| return textValue; | return textValue; | ||||
| } | } | ||||
| @@ -2222,6 +2226,7 @@ void TextEditor::handleCommandMessage (const int commandId) | |||||
| break; | break; | ||||
| case TextEditorDefs::focusLossMessageId: | case TextEditorDefs::focusLossMessageId: | ||||
| updateValueFromText(); | |||||
| listeners.callChecked (checker, &TextEditorListener::textEditorFocusLost, (TextEditor&) *this); | listeners.callChecked (checker, &TextEditorListener::textEditorFocusLost, (TextEditor&) *this); | ||||
| break; | break; | ||||
| @@ -685,6 +685,7 @@ private: | |||||
| void remove (const Range<int>& range, UndoManager* um, int caretPositionToMoveTo); | void remove (const Range<int>& range, UndoManager* um, int caretPositionToMoveTo); | ||||
| void getCharPosition (int index, float& x, float& y, float& lineHeight) const; | void getCharPosition (int index, float& x, float& y, float& lineHeight) const; | ||||
| void updateCaretPosition(); | void updateCaretPosition(); | ||||
| void updateValueFromText(); | |||||
| void textWasChangedByValue(); | void textWasChangedByValue(); | ||||
| int indexAtPosition (float x, float y); | int indexAtPosition (float x, float y); | ||||
| int findWordBreakAfter (int position) const; | int findWordBreakAfter (int position) const; | ||||
| @@ -197,6 +197,16 @@ private: | |||||
| }; | }; | ||||
| //============================================================================== | |||||
| struct AudioDeviceSetupDetails | |||||
| { | |||||
| AudioDeviceManager* manager; | |||||
| int minNumInputChannels, maxNumInputChannels; | |||||
| int minNumOutputChannels, maxNumOutputChannels; | |||||
| bool useStereoPairs; | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| class AudioDeviceSettingsPanel : public Component, | class AudioDeviceSettingsPanel : public Component, | ||||
| public ChangeListener, | public ChangeListener, | ||||
| @@ -205,7 +215,7 @@ class AudioDeviceSettingsPanel : public Component, | |||||
| { | { | ||||
| public: | public: | ||||
| AudioDeviceSettingsPanel (AudioIODeviceType* type_, | AudioDeviceSettingsPanel (AudioIODeviceType* type_, | ||||
| AudioIODeviceType::DeviceSetupDetails& setup_, | |||||
| AudioDeviceSetupDetails& setup_, | |||||
| const bool hideAdvancedOptionsWithButton) | const bool hideAdvancedOptionsWithButton) | ||||
| : type (type_), | : type (type_), | ||||
| setup (setup_) | setup (setup_) | ||||
| @@ -475,7 +485,7 @@ public: | |||||
| private: | private: | ||||
| AudioIODeviceType* const type; | AudioIODeviceType* const type; | ||||
| const AudioIODeviceType::DeviceSetupDetails setup; | |||||
| const AudioDeviceSetupDetails setup; | |||||
| ScopedPointer<ComboBox> outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown; | ScopedPointer<ComboBox> outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown; | ||||
| ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel; | ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel; | ||||
| @@ -661,7 +671,7 @@ public: | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| ChannelSelectorListBox (const AudioIODeviceType::DeviceSetupDetails& setup_, | |||||
| ChannelSelectorListBox (const AudioDeviceSetupDetails& setup_, | |||||
| const BoxType type_, | const BoxType type_, | ||||
| const String& noItemsMessage_) | const String& noItemsMessage_) | ||||
| : ListBox (String::empty, nullptr), | : ListBox (String::empty, nullptr), | ||||
| @@ -804,7 +814,7 @@ public: | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| const AudioIODeviceType::DeviceSetupDetails setup; | |||||
| const AudioDeviceSetupDetails setup; | |||||
| const BoxType type; | const BoxType type; | ||||
| const String noItemsMessage; | const String noItemsMessage; | ||||
| StringArray items; | StringArray items; | ||||
| @@ -1076,7 +1086,7 @@ void AudioDeviceSelectorComponent::updateAllControls() | |||||
| if (type != nullptr) | if (type != nullptr) | ||||
| { | { | ||||
| AudioIODeviceType::DeviceSetupDetails details; | |||||
| AudioDeviceSetupDetails details; | |||||
| details.manager = &deviceManager; | details.manager = &deviceManager; | ||||
| details.minNumInputChannels = minInputChannels; | details.minNumInputChannels = minInputChannels; | ||||
| details.maxNumInputChannels = maxInputChannels; | details.maxNumInputChannels = maxInputChannels; | ||||
| @@ -1052,6 +1052,22 @@ public: | |||||
| : AudioIODeviceType ("CoreAudio"), | : AudioIODeviceType ("CoreAudio"), | ||||
| hasScanned (false) | hasScanned (false) | ||||
| { | { | ||||
| AudioObjectPropertyAddress pa; | |||||
| pa.mSelector = kAudioHardwarePropertyDevices; | |||||
| pa.mScope = kAudioObjectPropertyScopeWildcard; | |||||
| pa.mElement = kAudioObjectPropertyElementWildcard; | |||||
| AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); | |||||
| } | |||||
| ~CoreAudioIODeviceType() | |||||
| { | |||||
| AudioObjectPropertyAddress pa; | |||||
| pa.mSelector = kAudioHardwarePropertyDevices; | |||||
| pa.mScope = kAudioObjectPropertyScopeWildcard; | |||||
| pa.mElement = kAudioObjectPropertyElementWildcard; | |||||
| AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -1226,6 +1242,18 @@ private: | |||||
| return total; | return total; | ||||
| } | } | ||||
| void audioDeviceListChanged() | |||||
| { | |||||
| scanForDevices(); | |||||
| callDeviceChangeListeners(); | |||||
| } | |||||
| static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) | |||||
| { | |||||
| static_cast <CoreAudioIODeviceType*> (clientData)->audioDeviceListChanged(); | |||||
| return noErr; | |||||
| } | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType); | ||||
| }; | }; | ||||
| @@ -210,6 +210,7 @@ public: | |||||
| virtual void drawRect (NSRect r); | virtual void drawRect (NSRect r); | ||||
| virtual bool canBecomeKeyWindow(); | virtual bool canBecomeKeyWindow(); | ||||
| virtual void becomeKeyWindow(); | |||||
| virtual bool windowShouldClose(); | virtual bool windowShouldClose(); | ||||
| virtual void redirectMovedOrResized(); | virtual void redirectMovedOrResized(); | ||||
| @@ -681,7 +682,7 @@ END_JUCE_NAMESPACE | |||||
| [super becomeKeyWindow]; | [super becomeKeyWindow]; | ||||
| if (owner != nullptr) | if (owner != nullptr) | ||||
| owner->grabFocus(); | |||||
| owner->becomeKeyWindow(); | |||||
| } | } | ||||
| - (BOOL) windowShouldClose: (id) window | - (BOOL) windowShouldClose: (id) window | ||||
| @@ -1601,6 +1602,12 @@ bool NSViewComponentPeer::canBecomeKeyWindow() | |||||
| return (getStyleFlags() & JUCE_NAMESPACE::ComponentPeer::windowIgnoresKeyPresses) == 0; | return (getStyleFlags() & JUCE_NAMESPACE::ComponentPeer::windowIgnoresKeyPresses) == 0; | ||||
| } | } | ||||
| void NSViewComponentPeer::becomeKeyWindow() | |||||
| { | |||||
| handleBroughtToFront(); | |||||
| grabFocus(); | |||||
| } | |||||
| bool NSViewComponentPeer::windowShouldClose() | bool NSViewComponentPeer::windowShouldClose() | ||||
| { | { | ||||
| if (! isValidPeer (this)) | if (! isValidPeer (this)) | ||||
| @@ -427,10 +427,10 @@ public: | |||||
| view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(), | view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(), | ||||
| (CGFloat) bounds.getWidth(), (CGFloat) bounds.getHeight()); | (CGFloat) bounds.getWidth(), (CGFloat) bounds.getHeight()); | ||||
| if (lastWidth != w || lastHeight != h) | |||||
| if (lastWidth != bounds.getWidth() || lastHeight != bounds.getHeight()) | |||||
| { | { | ||||
| lastWidth = w; | |||||
| lastHeight = h; | |||||
| lastWidth = bounds.getWidth(); | |||||
| lastHeight = bounds.getHeight(); | |||||
| freeGLBuffers(); | freeGLBuffers(); | ||||
| createGLBuffers(); | createGLBuffers(); | ||||
| } | } | ||||
| @@ -28,14 +28,55 @@ | |||||
| #if JUCE_INCLUDED_FILE | #if JUCE_INCLUDED_FILE | ||||
| //============================================================================== | |||||
| class HiddenMessageWindow | |||||
| { | |||||
| public: | |||||
| HiddenMessageWindow (const TCHAR* const messageWindowName, WNDPROC wndProc) | |||||
| { | |||||
| String className ("JUCE_"); | |||||
| className << String::toHexString (Time::getHighResolutionTicks()); | |||||
| HMODULE moduleHandle = (HMODULE) PlatformUtilities::getCurrentModuleInstanceHandle(); | |||||
| WNDCLASSEX wc = { 0 }; | |||||
| wc.cbSize = sizeof (wc); | |||||
| wc.lpfnWndProc = wndProc; | |||||
| wc.cbWndExtra = 4; | |||||
| wc.hInstance = moduleHandle; | |||||
| wc.lpszClassName = className.toWideCharPointer(); | |||||
| atom = RegisterClassEx (&wc); | |||||
| jassert (atom != 0); | |||||
| hwnd = CreateWindow (getClassNameFromAtom(), messageWindowName, | |||||
| 0, 0, 0, 0, 0, 0, 0, moduleHandle, 0); | |||||
| jassert (hwnd != 0); | |||||
| } | |||||
| ~HiddenMessageWindow() | |||||
| { | |||||
| DestroyWindow (hwnd); | |||||
| UnregisterClass (getClassNameFromAtom(), 0); | |||||
| } | |||||
| inline HWND getHWND() const noexcept { return hwnd; } | |||||
| private: | |||||
| ATOM atom; | |||||
| HWND hwnd; | |||||
| LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) MAKELONG (atom, 0); } | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| static const unsigned int specialId = WM_APP + 0x4400; | static const unsigned int specialId = WM_APP + 0x4400; | ||||
| static const unsigned int broadcastId = WM_APP + 0x4403; | static const unsigned int broadcastId = WM_APP + 0x4403; | ||||
| static const unsigned int specialCallbackId = WM_APP + 0x4402; | static const unsigned int specialCallbackId = WM_APP + 0x4402; | ||||
| static const TCHAR* const messageWindowName = _T("JUCEWindow"); | |||||
| static ATOM messageWindowClassAtom = 0; | |||||
| static LPCTSTR getMessageWindowClassName() noexcept { return (LPCTSTR) MAKELONG (messageWindowClassAtom, 0); } | |||||
| static const TCHAR messageWindowName[] = _T("JUCEWindow"); | |||||
| static ScopedPointer<HiddenMessageWindow> messageWindow; | |||||
| HWND juce_messageWindowHandle = 0; | HWND juce_messageWindowHandle = 0; | ||||
| @@ -259,34 +300,14 @@ void MessageManager::doPlatformSpecificInitialisation() | |||||
| { | { | ||||
| OleInitialize (0); | OleInitialize (0); | ||||
| // this name has to be different for each app/dll instance because otherwise | |||||
| // poor old Win32 can get a bit confused (even despite it not being a process-global | |||||
| // window class). | |||||
| String className ("JUCEcs_"); | |||||
| className << (int) (Time::getHighResolutionTicks() & 0x7fffffff); | |||||
| HMODULE moduleHandle = (HMODULE) PlatformUtilities::getCurrentModuleInstanceHandle(); | |||||
| WNDCLASSEX wc = { 0 }; | |||||
| wc.cbSize = sizeof (wc); | |||||
| wc.lpfnWndProc = (WNDPROC) juce_MessageWndProc; | |||||
| wc.cbWndExtra = 4; | |||||
| wc.hInstance = moduleHandle; | |||||
| wc.lpszClassName = className.toWideCharPointer(); | |||||
| messageWindowClassAtom = RegisterClassEx (&wc); | |||||
| jassert (messageWindowClassAtom != 0); | |||||
| juce_messageWindowHandle = CreateWindow (getMessageWindowClassName(), messageWindowName, | |||||
| 0, 0, 0, 0, 0, 0, 0, moduleHandle, 0); | |||||
| jassert (juce_messageWindowHandle != 0); | |||||
| messageWindow = new HiddenMessageWindow (messageWindowName, (WNDPROC) juce_MessageWndProc); | |||||
| juce_messageWindowHandle = messageWindow->getHWND(); | |||||
| } | } | ||||
| void MessageManager::doPlatformSpecificShutdown() | void MessageManager::doPlatformSpecificShutdown() | ||||
| { | { | ||||
| DestroyWindow (juce_messageWindowHandle); | |||||
| UnregisterClass (getMessageWindowClassName(), 0); | |||||
| messageWindow = nullptr; | |||||
| OleUninitialize(); | OleUninitialize(); | ||||
| } | } | ||||
| @@ -632,8 +632,6 @@ public: | |||||
| bool initialise() | bool initialise() | ||||
| { | { | ||||
| double defaultSampleRateIn = 0, defaultSampleRateOut = 0; | |||||
| int minBufferSizeIn = 0, defaultBufferSizeIn = 0, minBufferSizeOut = 0, defaultBufferSizeOut = 0; | |||||
| latencyIn = latencyOut = 0; | latencyIn = latencyOut = 0; | ||||
| Array <double> ratesIn, ratesOut; | Array <double> ratesIn, ratesOut; | ||||
| @@ -965,12 +963,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType | |||||
| public: | public: | ||||
| WASAPIAudioIODeviceType() | WASAPIAudioIODeviceType() | ||||
| : AudioIODeviceType ("Windows Audio"), | : AudioIODeviceType ("Windows Audio"), | ||||
| deviceChangeCatcher (_T("Windows Audio"), (WNDPROC) deviceChangeEventCallback), | |||||
| hasScanned (false) | hasScanned (false) | ||||
| { | { | ||||
| } | |||||
| ~WASAPIAudioIODeviceType() | |||||
| { | |||||
| SetWindowLongPtr (deviceChangeCatcher.getHWND(), GWLP_USERDATA, (LONG_PTR) this); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -983,68 +979,8 @@ public: | |||||
| outputDeviceIds.clear(); | outputDeviceIds.clear(); | ||||
| inputDeviceIds.clear(); | inputDeviceIds.clear(); | ||||
| ComSmartPtr <IMMDeviceEnumerator> enumerator; | |||||
| if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) | |||||
| return; | |||||
| const String defaultRenderer = getDefaultEndpoint (enumerator, false); | |||||
| const String defaultCapture = getDefaultEndpoint (enumerator, true); | |||||
| ComSmartPtr <IMMDeviceCollection> deviceCollection; | |||||
| UINT32 numDevices = 0; | |||||
| if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())) | |||||
| && check (deviceCollection->GetCount (&numDevices)))) | |||||
| return; | |||||
| for (UINT32 i = 0; i < numDevices; ++i) | |||||
| { | |||||
| ComSmartPtr <IMMDevice> device; | |||||
| if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress()))) | |||||
| continue; | |||||
| const String deviceId (getDeviceID (device)); | |||||
| DWORD state = 0; | |||||
| if (! check (device->GetState (&state))) | |||||
| continue; | |||||
| if (state != DEVICE_STATE_ACTIVE) | |||||
| continue; | |||||
| String name; | |||||
| { | |||||
| ComSmartPtr <IPropertyStore> properties; | |||||
| if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress()))) | |||||
| continue; | |||||
| PROPVARIANT value; | |||||
| PropVariantInit (&value); | |||||
| if (check (properties->GetValue (PKEY_Device_FriendlyName, &value))) | |||||
| name = value.pwszVal; | |||||
| PropVariantClear (&value); | |||||
| } | |||||
| const EDataFlow flow = getDataFlow (device); | |||||
| if (flow == eRender) | |||||
| { | |||||
| const int index = (deviceId == defaultRenderer) ? 0 : -1; | |||||
| outputDeviceIds.insert (index, deviceId); | |||||
| outputDeviceNames.insert (index, name); | |||||
| } | |||||
| else if (flow == eCapture) | |||||
| { | |||||
| const int index = (deviceId == defaultCapture) ? 0 : -1; | |||||
| inputDeviceIds.insert (index, deviceId); | |||||
| inputDeviceNames.insert (index, name); | |||||
| } | |||||
| } | |||||
| inputDeviceNames.appendNumbersToDuplicates (false, false); | |||||
| outputDeviceNames.appendNumbersToDuplicates (false, false); | |||||
| scan (outputDeviceNames, inputDeviceNames, | |||||
| outputDeviceIds, inputDeviceIds); | |||||
| } | } | ||||
| StringArray getDeviceNames (bool wantInputNames) const | StringArray getDeviceNames (bool wantInputNames) const | ||||
| @@ -1102,6 +1038,7 @@ public: | |||||
| StringArray inputDeviceNames, inputDeviceIds; | StringArray inputDeviceNames, inputDeviceIds; | ||||
| private: | private: | ||||
| HiddenMessageWindow deviceChangeCatcher; | |||||
| bool hasScanned; | bool hasScanned; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -1115,7 +1052,7 @@ private: | |||||
| WCHAR* deviceId = nullptr; | WCHAR* deviceId = nullptr; | ||||
| if (check (dev->GetId (&deviceId))) | if (check (dev->GetId (&deviceId))) | ||||
| { | { | ||||
| s = String (deviceId); | |||||
| s = deviceId; | |||||
| CoTaskMemFree (deviceId); | CoTaskMemFree (deviceId); | ||||
| } | } | ||||
| @@ -1125,6 +1062,106 @@ private: | |||||
| return s; | return s; | ||||
| } | } | ||||
| //============================================================================== | |||||
| void scan (StringArray& outputDeviceNames, | |||||
| StringArray& inputDeviceNames, | |||||
| StringArray& outputDeviceIds, | |||||
| StringArray& inputDeviceIds) | |||||
| { | |||||
| ComSmartPtr <IMMDeviceEnumerator> enumerator; | |||||
| if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) | |||||
| return; | |||||
| const String defaultRenderer (getDefaultEndpoint (enumerator, false)); | |||||
| const String defaultCapture (getDefaultEndpoint (enumerator, true)); | |||||
| ComSmartPtr <IMMDeviceCollection> deviceCollection; | |||||
| UINT32 numDevices = 0; | |||||
| if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())) | |||||
| && check (deviceCollection->GetCount (&numDevices)))) | |||||
| return; | |||||
| for (UINT32 i = 0; i < numDevices; ++i) | |||||
| { | |||||
| ComSmartPtr <IMMDevice> device; | |||||
| if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress()))) | |||||
| continue; | |||||
| DWORD state = 0; | |||||
| if (! (check (device->GetState (&state)) && state == DEVICE_STATE_ACTIVE)) | |||||
| continue; | |||||
| const String deviceId (getDeviceID (device)); | |||||
| String name; | |||||
| { | |||||
| ComSmartPtr <IPropertyStore> properties; | |||||
| if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress()))) | |||||
| continue; | |||||
| PROPVARIANT value; | |||||
| PropVariantInit (&value); | |||||
| if (check (properties->GetValue (PKEY_Device_FriendlyName, &value))) | |||||
| name = value.pwszVal; | |||||
| PropVariantClear (&value); | |||||
| } | |||||
| const EDataFlow flow = getDataFlow (device); | |||||
| if (flow == eRender) | |||||
| { | |||||
| const int index = (deviceId == defaultRenderer) ? 0 : -1; | |||||
| outputDeviceIds.insert (index, deviceId); | |||||
| outputDeviceNames.insert (index, name); | |||||
| } | |||||
| else if (flow == eCapture) | |||||
| { | |||||
| const int index = (deviceId == defaultCapture) ? 0 : -1; | |||||
| inputDeviceIds.insert (index, deviceId); | |||||
| inputDeviceNames.insert (index, name); | |||||
| } | |||||
| } | |||||
| inputDeviceNames.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*/)) | |||||
| { | |||||
| ((WASAPIAudioIODeviceType*) GetWindowLongPtr (h, GWLP_USERDATA))->handleDeviceChange(); | |||||
| } | |||||
| return DefWindowProc (h, message, wParam, lParam); | |||||
| } | |||||
| void handleDeviceChange() | |||||
| { | |||||
| StringArray newOutNames, newInNames, newOutIds, newInIds; | |||||
| scan (newOutNames, newInNames, newOutIds, newInIds); | |||||
| if (newOutNames != outputDeviceNames | |||||
| || newInNames != inputDeviceNames | |||||
| || newOutIds != outputDeviceIds | |||||
| || newInIds != inputDeviceIds) | |||||
| { | |||||
| hasScanned = true; | |||||
| outputDeviceNames = newOutNames; | |||||
| inputDeviceNames = newInNames; | |||||
| outputDeviceIds = newOutIds; | |||||
| inputDeviceIds = newInIds; | |||||
| callDeviceChangeListeners(); | |||||
| } | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType); | ||||
| }; | }; | ||||