Browse Source

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.

tags/2021-05-28
Julian Storer 14 years ago
parent
commit
59f78f4cbe
18 changed files with 544 additions and 237 deletions
  1. +1
    -1
      extras/Introjucer/Source/Project/jucer_ProjectExport_XCode.h
  2. +2
    -2
      extras/browser plugins/wrapper/juce_ActiveX_GlueCode.cpp
  3. +3
    -3
      extras/browser plugins/wrapper/juce_NPAPI_GlueCode.cpp
  4. +232
    -109
      juce_amalgamated.cpp
  5. +34
    -8
      juce_amalgamated.h
  6. +13
    -0
      src/audio/devices/juce_AudioDeviceManager.cpp
  7. +5
    -2
      src/audio/devices/juce_AudioDeviceManager.h
  8. +10
    -0
      src/audio/devices/juce_AudioIODeviceType.cpp
  9. +28
    -5
      src/audio/devices/juce_AudioIODeviceType.h
  10. +1
    -1
      src/core/juce_StandardHeader.h
  11. +6
    -1
      src/gui/components/controls/juce_TextEditor.cpp
  12. +1
    -0
      src/gui/components/controls/juce_TextEditor.h
  13. +15
    -5
      src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp
  14. +28
    -0
      src/native/mac/juce_mac_CoreAudio.cpp
  15. +8
    -1
      src/native/mac/juce_mac_NSViewComponentPeer.mm
  16. +3
    -3
      src/native/mac/juce_mac_OpenGLComponent.mm
  17. +48
    -27
      src/native/windows/juce_win32_Messaging.cpp
  18. +106
    -69
      src/native/windows/juce_win32_WASAPI.cpp

+ 1
- 1
extras/Introjucer/Source/Project/jucer_ProjectExport_XCode.h 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();


+ 2
- 2
extras/browser plugins/wrapper/juce_ActiveX_GlueCode.cpp 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());


+ 3
- 3
extras/browser plugins/wrapper/juce_NPAPI_GlueCode.cpp 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;


+ 232
- 109
juce_amalgamated.cpp 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);
};



+ 34
- 8
juce_amalgamated.h 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;


+ 13
- 0
src/audio/devices/juce_AudioDeviceManager.cpp 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()
{


+ 5
- 2
src/audio/devices/juce_AudioDeviceManager.h 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);


+ 10
- 0
src/audio/devices/juce_AudioIODeviceType.cpp 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


+ 28
- 5
src/audio/devices/juce_AudioIODeviceType.h 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);
};


+ 1
- 1
src/core/juce_StandardHeader.h 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.


+ 6
- 1
src/gui/components/controls/juce_TextEditor.cpp 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;


+ 1
- 0
src/gui/components/controls/juce_TextEditor.h 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;


+ 15
- 5
src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp 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;


+ 28
- 0
src/native/mac/juce_mac_CoreAudio.cpp 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);
};


+ 8
- 1
src/native/mac/juce_mac_NSViewComponentPeer.mm 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))


+ 3
- 3
src/native/mac/juce_mac_OpenGLComponent.mm 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();
}


+ 48
- 27
src/native/windows/juce_win32_Messaging.cpp 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();
}


+ 106
- 69
src/native/windows/juce_win32_WASAPI.cpp 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);
};


Loading…
Cancel
Save