mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Sorted out some mac window maximising and ordering peculiarities. Fix for introjucer Xcode project generation. Added AudioIODeviceType::Listener class for monitoring audio device insertion/removal events (implemented for CoreAudio and WASAPI). Made TextEditor update its Value when it loses focus.
This commit is contained in:
parent
9537821a9f
commit
59f78f4cbe
18 changed files with 544 additions and 237 deletions
|
|
@ -725,7 +725,7 @@ private:
|
|||
const Identifier propertyName (o.getPropertyName(j));
|
||||
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 = val.quoted();
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ public:
|
|||
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());
|
||||
LPCOLESTR name = nameCopy.toUTF16();
|
||||
|
|
@ -312,7 +312,7 @@ public:
|
|||
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;
|
||||
const String nameCopy (methodName.toString());
|
||||
|
|
|
|||
|
|
@ -572,7 +572,7 @@ public:
|
|||
DBG ("num NP wrapper objs: " + String (--numDOWNP));
|
||||
}
|
||||
|
||||
const var getProperty (const var::identifier& propertyName) const
|
||||
var getProperty (const var::identifier& propertyName) const
|
||||
{
|
||||
NPVariant result;
|
||||
VOID_TO_NPVARIANT (result);
|
||||
|
|
@ -610,8 +610,8 @@ public:
|
|||
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)
|
||||
{
|
||||
var returnVal;
|
||||
|
|
|
|||
|
|
@ -26751,6 +26751,9 @@ void AudioDeviceManager::createDeviceTypesIfNeeded()
|
|||
|
||||
if (availableDeviceTypes.size() > 0)
|
||||
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;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::audioDeviceListChanged()
|
||||
{
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
static void addIfNotNull (OwnedArray <AudioIODeviceType>& list, AudioIODeviceType* const device)
|
||||
{
|
||||
if (device != nullptr)
|
||||
|
|
@ -27528,6 +27536,11 @@ void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput*
|
|||
owner->handleIncomingMidiMessageInt (source, message);
|
||||
}
|
||||
|
||||
void AudioDeviceManager::CallbackHandler::audioDeviceListChanged()
|
||||
{
|
||||
owner->audioDeviceListChanged();
|
||||
}
|
||||
|
||||
void AudioDeviceManager::playTestSound()
|
||||
{
|
||||
{ // 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
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; }
|
||||
#endif
|
||||
|
|
@ -55142,14 +55163,18 @@ void TextEditor::setText (const String& newText,
|
|||
}
|
||||
}
|
||||
|
||||
Value& TextEditor::getTextValue()
|
||||
void TextEditor::updateValueFromText()
|
||||
{
|
||||
if (valueTextNeedsUpdating)
|
||||
{
|
||||
valueTextNeedsUpdating = false;
|
||||
textValue = getText();
|
||||
}
|
||||
}
|
||||
|
||||
Value& TextEditor::getTextValue()
|
||||
{
|
||||
updateValueFromText();
|
||||
return textValue;
|
||||
}
|
||||
|
||||
|
|
@ -56082,6 +56107,7 @@ void TextEditor::handleCommandMessage (const int commandId)
|
|||
break;
|
||||
|
||||
case TextEditorDefs::focusLossMessageId:
|
||||
updateValueFromText();
|
||||
listeners.callChecked (checker, &TextEditorListener::textEditorFocusLost, (TextEditor&) *this);
|
||||
break;
|
||||
|
||||
|
|
@ -75025,6 +75051,14 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputSelectorComponentListBox);
|
||||
};
|
||||
|
||||
struct AudioDeviceSetupDetails
|
||||
{
|
||||
AudioDeviceManager* manager;
|
||||
int minNumInputChannels, maxNumInputChannels;
|
||||
int minNumOutputChannels, maxNumOutputChannels;
|
||||
bool useStereoPairs;
|
||||
};
|
||||
|
||||
class AudioDeviceSettingsPanel : public Component,
|
||||
public ChangeListener,
|
||||
public ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug)
|
||||
|
|
@ -75032,7 +75066,7 @@ class AudioDeviceSettingsPanel : public Component,
|
|||
{
|
||||
public:
|
||||
AudioDeviceSettingsPanel (AudioIODeviceType* type_,
|
||||
AudioIODeviceType::DeviceSetupDetails& setup_,
|
||||
AudioDeviceSetupDetails& setup_,
|
||||
const bool hideAdvancedOptionsWithButton)
|
||||
: type (type_),
|
||||
setup (setup_)
|
||||
|
|
@ -75302,7 +75336,7 @@ public:
|
|||
|
||||
private:
|
||||
AudioIODeviceType* const type;
|
||||
const AudioIODeviceType::DeviceSetupDetails setup;
|
||||
const AudioDeviceSetupDetails setup;
|
||||
|
||||
ScopedPointer<ComboBox> outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown;
|
||||
ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel;
|
||||
|
|
@ -75487,7 +75521,7 @@ public:
|
|||
audioOutputType
|
||||
};
|
||||
|
||||
ChannelSelectorListBox (const AudioIODeviceType::DeviceSetupDetails& setup_,
|
||||
ChannelSelectorListBox (const AudioDeviceSetupDetails& setup_,
|
||||
const BoxType type_,
|
||||
const String& noItemsMessage_)
|
||||
: ListBox (String::empty, nullptr),
|
||||
|
|
@ -75630,7 +75664,7 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
const AudioIODeviceType::DeviceSetupDetails setup;
|
||||
const AudioDeviceSetupDetails setup;
|
||||
const BoxType type;
|
||||
const String noItemsMessage;
|
||||
StringArray items;
|
||||
|
|
@ -75900,7 +75934,7 @@ void AudioDeviceSelectorComponent::updateAllControls()
|
|||
|
||||
if (type != nullptr)
|
||||
{
|
||||
AudioIODeviceType::DeviceSetupDetails details;
|
||||
AudioDeviceSetupDetails details;
|
||||
details.manager = &deviceManager;
|
||||
details.minNumInputChannels = minInputChannels;
|
||||
details.maxNumInputChannels = maxInputChannels;
|
||||
|
|
@ -246925,13 +246959,52 @@ void PlatformUtilities::beep()
|
|||
// compiled on its own).
|
||||
#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 broadcastId = WM_APP + 0x4403;
|
||||
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;
|
||||
|
||||
|
|
@ -247149,34 +247222,14 @@ void MessageManager::doPlatformSpecificInitialisation()
|
|||
{
|
||||
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()
|
||||
{
|
||||
DestroyWindow (juce_messageWindowHandle);
|
||||
UnregisterClass (getMessageWindowClassName(), 0);
|
||||
messageWindow = nullptr;
|
||||
|
||||
OleUninitialize();
|
||||
}
|
||||
|
||||
|
|
@ -260473,8 +260526,6 @@ public:
|
|||
|
||||
bool initialise()
|
||||
{
|
||||
double defaultSampleRateIn = 0, defaultSampleRateOut = 0;
|
||||
int minBufferSizeIn = 0, defaultBufferSizeIn = 0, minBufferSizeOut = 0, defaultBufferSizeOut = 0;
|
||||
latencyIn = latencyOut = 0;
|
||||
Array <double> ratesIn, ratesOut;
|
||||
|
||||
|
|
@ -260800,12 +260851,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType
|
|||
public:
|
||||
WASAPIAudioIODeviceType()
|
||||
: AudioIODeviceType ("Windows Audio"),
|
||||
deviceChangeCatcher (_T("Windows Audio"), (WNDPROC) deviceChangeEventCallback),
|
||||
hasScanned (false)
|
||||
{
|
||||
}
|
||||
|
||||
~WASAPIAudioIODeviceType()
|
||||
{
|
||||
SetWindowLongPtr (deviceChangeCatcher.getHWND(), GWLP_USERDATA, (LONG_PTR) this);
|
||||
}
|
||||
|
||||
void scanForDevices()
|
||||
|
|
@ -260817,68 +260866,8 @@ public:
|
|||
outputDeviceIds.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
|
||||
|
|
@ -260935,6 +260924,7 @@ public:
|
|||
StringArray inputDeviceNames, inputDeviceIds;
|
||||
|
||||
private:
|
||||
HiddenMessageWindow deviceChangeCatcher;
|
||||
bool hasScanned;
|
||||
|
||||
static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture)
|
||||
|
|
@ -260947,7 +260937,7 @@ private:
|
|||
WCHAR* deviceId = nullptr;
|
||||
if (check (dev->GetId (&deviceId)))
|
||||
{
|
||||
s = String (deviceId);
|
||||
s = deviceId;
|
||||
CoTaskMemFree (deviceId);
|
||||
}
|
||||
|
||||
|
|
@ -260957,6 +260947,104 @@ private:
|
|||
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);
|
||||
};
|
||||
|
||||
|
|
@ -277275,10 +277363,10 @@ public:
|
|||
view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(),
|
||||
(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();
|
||||
createGLBuffers();
|
||||
}
|
||||
|
|
@ -280726,6 +280814,7 @@ public:
|
|||
virtual void drawRect (NSRect r);
|
||||
|
||||
virtual bool canBecomeKeyWindow();
|
||||
virtual void becomeKeyWindow();
|
||||
virtual bool windowShouldClose();
|
||||
|
||||
virtual void redirectMovedOrResized();
|
||||
|
|
@ -281186,7 +281275,7 @@ END_JUCE_NAMESPACE
|
|||
[super becomeKeyWindow];
|
||||
|
||||
if (owner != nullptr)
|
||||
owner->grabFocus();
|
||||
owner->becomeKeyWindow();
|
||||
}
|
||||
|
||||
- (BOOL) windowShouldClose: (id) window
|
||||
|
|
@ -282097,6 +282186,12 @@ bool NSViewComponentPeer::canBecomeKeyWindow()
|
|||
return (getStyleFlags() & JUCE_NAMESPACE::ComponentPeer::windowIgnoresKeyPresses) == 0;
|
||||
}
|
||||
|
||||
void NSViewComponentPeer::becomeKeyWindow()
|
||||
{
|
||||
handleBroughtToFront();
|
||||
grabFocus();
|
||||
}
|
||||
|
||||
bool NSViewComponentPeer::windowShouldClose()
|
||||
{
|
||||
if (! isValidPeer (this))
|
||||
|
|
@ -283188,10 +283283,10 @@ public:
|
|||
view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(),
|
||||
(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();
|
||||
createGLBuffers();
|
||||
}
|
||||
|
|
@ -286984,6 +287079,22 @@ public:
|
|||
: AudioIODeviceType ("CoreAudio"),
|
||||
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()
|
||||
|
|
@ -287156,6 +287267,18 @@ private:
|
|||
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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 53
|
||||
#define JUCE_BUILDNUMBER 104
|
||||
#define JUCE_BUILDNUMBER 105
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
@ -39887,14 +39887,32 @@ public:
|
|||
virtual AudioIODevice* createDevice (const String& outputDeviceName,
|
||||
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. */
|
||||
virtual ~AudioIODeviceType();
|
||||
|
||||
|
|
@ -39918,8 +39936,12 @@ public:
|
|||
protected:
|
||||
explicit AudioIODeviceType (const String& typeName);
|
||||
|
||||
/** Synchronously calls all the registered device list change listeners. */
|
||||
void callDeviceChangeListeners();
|
||||
|
||||
private:
|
||||
String typeName;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType);
|
||||
};
|
||||
|
|
@ -41733,13 +41755,15 @@ private:
|
|||
double cpuUsageMs, timeToCpuScale;
|
||||
|
||||
class CallbackHandler : public AudioIODeviceCallback,
|
||||
public MidiInputCallback
|
||||
public MidiInputCallback,
|
||||
public AudioIODeviceType::Listener
|
||||
{
|
||||
public:
|
||||
void audioDeviceIOCallback (const float**, int, float**, int, int);
|
||||
void audioDeviceAboutToStart (AudioIODevice*);
|
||||
void audioDeviceStopped();
|
||||
void handleIncomingMidiMessage (MidiInput*, const MidiMessage&);
|
||||
void audioDeviceListChanged();
|
||||
|
||||
AudioDeviceManager* owner;
|
||||
};
|
||||
|
|
@ -41752,6 +41776,7 @@ private:
|
|||
void audioDeviceAboutToStartInt (AudioIODevice*);
|
||||
void audioDeviceStoppedInt();
|
||||
void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&);
|
||||
void audioDeviceListChanged();
|
||||
|
||||
String restartDevice (int blockSizeToUse, double sampleRateToUse,
|
||||
const BigInteger& ins, const BigInteger& outs);
|
||||
|
|
@ -41764,7 +41789,7 @@ private:
|
|||
void deleteCurrentDevice();
|
||||
double chooseBestSampleRate (double preferred) const;
|
||||
int chooseBestBufferSize (int preferred) const;
|
||||
void insertDefaultDeviceNames (AudioDeviceSetup& setup) const;
|
||||
void insertDefaultDeviceNames (AudioDeviceSetup&) const;
|
||||
|
||||
AudioIODeviceType* findType (const String& inputName, const String& outputName);
|
||||
|
||||
|
|
@ -52451,6 +52476,7 @@ private:
|
|||
void remove (const Range<int>& range, UndoManager* um, int caretPositionToMoveTo);
|
||||
void getCharPosition (int index, float& x, float& y, float& lineHeight) const;
|
||||
void updateCaretPosition();
|
||||
void updateValueFromText();
|
||||
void textWasChangedByValue();
|
||||
int indexAtPosition (float x, float y);
|
||||
int findWordBreakAfter (int position) const;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ void AudioDeviceManager::createDeviceTypesIfNeeded()
|
|||
|
||||
if (availableDeviceTypes.size() > 0)
|
||||
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;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::audioDeviceListChanged()
|
||||
{
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static void addIfNotNull (OwnedArray <AudioIODeviceType>& list, AudioIODeviceType* const device)
|
||||
{
|
||||
|
|
@ -869,6 +877,11 @@ void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput*
|
|||
owner->handleIncomingMidiMessageInt (source, message);
|
||||
}
|
||||
|
||||
void AudioDeviceManager::CallbackHandler::audioDeviceListChanged()
|
||||
{
|
||||
owner->audioDeviceListChanged();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDeviceManager::playTestSound()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -476,13 +476,15 @@ private:
|
|||
|
||||
//==============================================================================
|
||||
class CallbackHandler : public AudioIODeviceCallback,
|
||||
public MidiInputCallback
|
||||
public MidiInputCallback,
|
||||
public AudioIODeviceType::Listener
|
||||
{
|
||||
public:
|
||||
void audioDeviceIOCallback (const float**, int, float**, int, int);
|
||||
void audioDeviceAboutToStart (AudioIODevice*);
|
||||
void audioDeviceStopped();
|
||||
void handleIncomingMidiMessage (MidiInput*, const MidiMessage&);
|
||||
void audioDeviceListChanged();
|
||||
|
||||
AudioDeviceManager* owner;
|
||||
};
|
||||
|
|
@ -495,6 +497,7 @@ private:
|
|||
void audioDeviceAboutToStartInt (AudioIODevice*);
|
||||
void audioDeviceStoppedInt();
|
||||
void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&);
|
||||
void audioDeviceListChanged();
|
||||
|
||||
String restartDevice (int blockSizeToUse, double sampleRateToUse,
|
||||
const BigInteger& ins, const BigInteger& outs);
|
||||
|
|
@ -507,7 +510,7 @@ private:
|
|||
void deleteCurrentDevice();
|
||||
double chooseBestSampleRate (double preferred) const;
|
||||
int chooseBestBufferSize (int preferred) const;
|
||||
void insertDefaultDeviceNames (AudioDeviceSetup& setup) const;
|
||||
void insertDefaultDeviceNames (AudioDeviceSetup&) const;
|
||||
|
||||
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
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; }
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__
|
||||
|
||||
#include "juce_AudioIODevice.h"
|
||||
#include "../../events/juce_ListenerList.h"
|
||||
class AudioDeviceManager;
|
||||
|
||||
|
||||
|
|
@ -119,14 +120,32 @@ public:
|
|||
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. */
|
||||
virtual ~AudioIODeviceType();
|
||||
|
|
@ -152,8 +171,12 @@ public:
|
|||
protected:
|
||||
explicit AudioIODeviceType (const String& typeName);
|
||||
|
||||
/** Synchronously calls all the registered device list change listeners. */
|
||||
void callDeviceChangeListeners();
|
||||
|
||||
private:
|
||||
String typeName;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 53
|
||||
#define JUCE_BUILDNUMBER 104
|
||||
#define JUCE_BUILDNUMBER 105
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
|
|||
|
|
@ -1270,14 +1270,18 @@ void TextEditor::setText (const String& newText,
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
Value& TextEditor::getTextValue()
|
||||
void TextEditor::updateValueFromText()
|
||||
{
|
||||
if (valueTextNeedsUpdating)
|
||||
{
|
||||
valueTextNeedsUpdating = false;
|
||||
textValue = getText();
|
||||
}
|
||||
}
|
||||
|
||||
Value& TextEditor::getTextValue()
|
||||
{
|
||||
updateValueFromText();
|
||||
return textValue;
|
||||
}
|
||||
|
||||
|
|
@ -2222,6 +2226,7 @@ void TextEditor::handleCommandMessage (const int commandId)
|
|||
break;
|
||||
|
||||
case TextEditorDefs::focusLossMessageId:
|
||||
updateValueFromText();
|
||||
listeners.callChecked (checker, &TextEditorListener::textEditorFocusLost, (TextEditor&) *this);
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -685,6 +685,7 @@ private:
|
|||
void remove (const Range<int>& range, UndoManager* um, int caretPositionToMoveTo);
|
||||
void getCharPosition (int index, float& x, float& y, float& lineHeight) const;
|
||||
void updateCaretPosition();
|
||||
void updateValueFromText();
|
||||
void textWasChangedByValue();
|
||||
int indexAtPosition (float x, float y);
|
||||
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,
|
||||
public ChangeListener,
|
||||
|
|
@ -205,7 +215,7 @@ class AudioDeviceSettingsPanel : public Component,
|
|||
{
|
||||
public:
|
||||
AudioDeviceSettingsPanel (AudioIODeviceType* type_,
|
||||
AudioIODeviceType::DeviceSetupDetails& setup_,
|
||||
AudioDeviceSetupDetails& setup_,
|
||||
const bool hideAdvancedOptionsWithButton)
|
||||
: type (type_),
|
||||
setup (setup_)
|
||||
|
|
@ -475,7 +485,7 @@ public:
|
|||
|
||||
private:
|
||||
AudioIODeviceType* const type;
|
||||
const AudioIODeviceType::DeviceSetupDetails setup;
|
||||
const AudioDeviceSetupDetails setup;
|
||||
|
||||
ScopedPointer<ComboBox> outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown;
|
||||
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 String& noItemsMessage_)
|
||||
: ListBox (String::empty, nullptr),
|
||||
|
|
@ -804,7 +814,7 @@ public:
|
|||
|
||||
private:
|
||||
//==============================================================================
|
||||
const AudioIODeviceType::DeviceSetupDetails setup;
|
||||
const AudioDeviceSetupDetails setup;
|
||||
const BoxType type;
|
||||
const String noItemsMessage;
|
||||
StringArray items;
|
||||
|
|
@ -1076,7 +1086,7 @@ void AudioDeviceSelectorComponent::updateAllControls()
|
|||
|
||||
if (type != nullptr)
|
||||
{
|
||||
AudioIODeviceType::DeviceSetupDetails details;
|
||||
AudioDeviceSetupDetails details;
|
||||
details.manager = &deviceManager;
|
||||
details.minNumInputChannels = minInputChannels;
|
||||
details.maxNumInputChannels = maxInputChannels;
|
||||
|
|
|
|||
|
|
@ -1052,6 +1052,22 @@ public:
|
|||
: AudioIODeviceType ("CoreAudio"),
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ public:
|
|||
virtual void drawRect (NSRect r);
|
||||
|
||||
virtual bool canBecomeKeyWindow();
|
||||
virtual void becomeKeyWindow();
|
||||
virtual bool windowShouldClose();
|
||||
|
||||
virtual void redirectMovedOrResized();
|
||||
|
|
@ -681,7 +682,7 @@ END_JUCE_NAMESPACE
|
|||
[super becomeKeyWindow];
|
||||
|
||||
if (owner != nullptr)
|
||||
owner->grabFocus();
|
||||
owner->becomeKeyWindow();
|
||||
}
|
||||
|
||||
- (BOOL) windowShouldClose: (id) window
|
||||
|
|
@ -1601,6 +1602,12 @@ bool NSViewComponentPeer::canBecomeKeyWindow()
|
|||
return (getStyleFlags() & JUCE_NAMESPACE::ComponentPeer::windowIgnoresKeyPresses) == 0;
|
||||
}
|
||||
|
||||
void NSViewComponentPeer::becomeKeyWindow()
|
||||
{
|
||||
handleBroughtToFront();
|
||||
grabFocus();
|
||||
}
|
||||
|
||||
bool NSViewComponentPeer::windowShouldClose()
|
||||
{
|
||||
if (! isValidPeer (this))
|
||||
|
|
|
|||
|
|
@ -427,10 +427,10 @@ public:
|
|||
view.frame = CGRectMake ((CGFloat) bounds.getX(), (CGFloat) bounds.getY(),
|
||||
(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();
|
||||
createGLBuffers();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,14 +28,55 @@
|
|||
#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 broadcastId = WM_APP + 0x4403;
|
||||
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;
|
||||
|
||||
|
|
@ -259,34 +300,14 @@ void MessageManager::doPlatformSpecificInitialisation()
|
|||
{
|
||||
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()
|
||||
{
|
||||
DestroyWindow (juce_messageWindowHandle);
|
||||
UnregisterClass (getMessageWindowClassName(), 0);
|
||||
messageWindow = nullptr;
|
||||
|
||||
OleUninitialize();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -632,8 +632,6 @@ public:
|
|||
|
||||
bool initialise()
|
||||
{
|
||||
double defaultSampleRateIn = 0, defaultSampleRateOut = 0;
|
||||
int minBufferSizeIn = 0, defaultBufferSizeIn = 0, minBufferSizeOut = 0, defaultBufferSizeOut = 0;
|
||||
latencyIn = latencyOut = 0;
|
||||
Array <double> ratesIn, ratesOut;
|
||||
|
||||
|
|
@ -965,12 +963,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType
|
|||
public:
|
||||
WASAPIAudioIODeviceType()
|
||||
: AudioIODeviceType ("Windows Audio"),
|
||||
deviceChangeCatcher (_T("Windows Audio"), (WNDPROC) deviceChangeEventCallback),
|
||||
hasScanned (false)
|
||||
{
|
||||
}
|
||||
|
||||
~WASAPIAudioIODeviceType()
|
||||
{
|
||||
SetWindowLongPtr (deviceChangeCatcher.getHWND(), GWLP_USERDATA, (LONG_PTR) this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -983,68 +979,8 @@ public:
|
|||
outputDeviceIds.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
|
||||
|
|
@ -1102,6 +1038,7 @@ public:
|
|||
StringArray inputDeviceNames, inputDeviceIds;
|
||||
|
||||
private:
|
||||
HiddenMessageWindow deviceChangeCatcher;
|
||||
bool hasScanned;
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -1115,7 +1052,7 @@ private:
|
|||
WCHAR* deviceId = nullptr;
|
||||
if (check (dev->GetId (&deviceId)))
|
||||
{
|
||||
s = String (deviceId);
|
||||
s = deviceId;
|
||||
CoTaskMemFree (deviceId);
|
||||
}
|
||||
|
||||
|
|
@ -1125,6 +1062,106 @@ private:
|
|||
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);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue