1
0
Fork 0
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:
Julian Storer 2011-06-22 10:50:46 +01:00
parent 9537821a9f
commit 59f78f4cbe
18 changed files with 544 additions and 237 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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()
{

View file

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

View file

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

View file

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

View file

@ -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.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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