Browse Source

Update juce

tags/1.9.7
falkTX 9 years ago
parent
commit
c0190a23a8
17 changed files with 597 additions and 259 deletions
  1. +2
    -2
      data/copy-juce-carla
  2. +6
    -2
      source/modules/AppConfig.h
  3. +1
    -1
      source/modules/juce_audio_basics/mpe/juce_MPENote.cpp
  4. +14
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp
  5. +6
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEZone.h
  6. +46
    -3
      source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp
  7. +38
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h
  8. +376
    -195
      source/modules/juce_audio_devices/native/juce_ios_Audio.cpp
  9. +7
    -0
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h
  10. +49
    -19
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  11. +7
    -5
      source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  12. +3
    -0
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  13. +3
    -1
      source/modules/juce_core/native/juce_BasicNativeHeaders.h
  14. +0
    -6
      source/modules/juce_core/text/juce_CharPointer_UTF8.h
  15. +37
    -22
      source/modules/juce_core/text/juce_CharacterFunctions.h
  16. +1
    -2
      source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp
  17. +1
    -1
      source/modules/juce_gui_basics/menus/juce_PopupMenu.h

+ 2
- 2
data/copy-juce-carla View File

@@ -2,8 +2,8 @@


set -e set -e


JUCE_MODULES_DIR="/home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO-Ports/libs/juce/source/modules/"
CARLA_MODULES_DIR="/home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/"
JUCE_MODULES_DIR="/home/falktx/Projects/FOSS/GIT-mine/DISTRHO-Ports/libs/juce/source/modules/"
CARLA_MODULES_DIR="/home/falktx/Projects/FOSS/GIT-mine/Carla/source/modules/"


MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics juce_gui_extra") MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics juce_gui_extra")




+ 6
- 2
source/modules/AppConfig.h View File

@@ -319,13 +319,17 @@
Enabling this flag means that DirectWrite will be used when available for font Enabling this flag means that DirectWrite will be used when available for font
management and layout. management and layout.
*/ */
#define JUCE_USE_DIRECTWRITE 1
#define JUCE_USE_DIRECTWRITE 0
#define JUCE_INCLUDE_PNGLIB_CODE 1 #define JUCE_INCLUDE_PNGLIB_CODE 1
#define JUCE_INCLUDE_JPEGLIB_CODE 1 #define JUCE_INCLUDE_JPEGLIB_CODE 1
#define USE_COREGRAPHICS_RENDERING 1
#ifdef APPCONFIG_OS_MAC
# define USE_COREGRAPHICS_RENDERING 1
#else
# define USE_COREGRAPHICS_RENDERING 0
#endif
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// juce_gui_basics // juce_gui_basics


+ 1
- 1
source/modules/juce_audio_basics/mpe/juce_MPENote.cpp View File

@@ -71,7 +71,7 @@ MPENote::MPENote() noexcept
//============================================================================== //==============================================================================
bool MPENote::isValid() const noexcept bool MPENote::isValid() const noexcept
{ {
return midiChannel > 0 && midiChannel <= 16 && initialNote >= 0 && initialNote <= 127;
return midiChannel > 0 && midiChannel <= 16 && initialNote < 128;
} }
//============================================================================== //==============================================================================


+ 14
- 0
source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp View File

@@ -144,6 +144,20 @@ bool MPEZone::truncateToFit (MPEZone other) noexcept
return true; return true;
} }
//==========================================================================
bool MPEZone::operator== (const MPEZone& other) const noexcept
{
return masterChannel == other.masterChannel
&& numNoteChannels == other.numNoteChannels
&& perNotePitchbendRange == other.perNotePitchbendRange
&& masterPitchbendRange == other.masterPitchbendRange;
}
bool MPEZone::operator!= (const MPEZone& other) const noexcept
{
return ! operator== (other);
}
//============================================================================== //==============================================================================
//============================================================================== //==============================================================================
#if JUCE_UNIT_TESTS #if JUCE_UNIT_TESTS


+ 6
- 0
source/modules/juce_audio_basics/mpe/juce_MPEZone.h View File

@@ -120,6 +120,12 @@ struct JUCE_API MPEZone
*/ */
bool truncateToFit (MPEZone zoneToAvoid) noexcept; bool truncateToFit (MPEZone zoneToAvoid) noexcept;
/** @returns true if this zone is equal to the one passed in. */
bool operator== (const MPEZone& other) const noexcept;
/** @returns true if this zone is not equal to the one passed in. */
bool operator!= (const MPEZone& other) const noexcept;
private: private:
//========================================================================== //==========================================================================
int masterChannel; int masterChannel;


+ 46
- 3
source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp View File

@@ -26,6 +26,17 @@ MPEZoneLayout::MPEZoneLayout() noexcept
{ {
} }
MPEZoneLayout::MPEZoneLayout (const MPEZoneLayout& other)
: zones (other.zones)
{
}
MPEZoneLayout& MPEZoneLayout::operator= (const MPEZoneLayout& other)
{
zones = other.zones;
return *this;
}
//============================================================================== //==============================================================================
bool MPEZoneLayout::addZone (MPEZone newZone) bool MPEZoneLayout::addZone (MPEZone newZone)
{ {
@@ -46,6 +57,7 @@ bool MPEZoneLayout::addZone (MPEZone newZone)
} }
zones.add (newZone); zones.add (newZone);
listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this);
return noOtherZonesModified; return noOtherZonesModified;
} }
@@ -66,6 +78,7 @@ MPEZone* MPEZoneLayout::getZoneByIndex (int index) const noexcept
void MPEZoneLayout::clearAllZones() void MPEZoneLayout::clearAllZones()
{ {
zones.clear(); zones.clear();
listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this);
} }
//============================================================================== //==============================================================================
@@ -106,12 +119,23 @@ void MPEZoneLayout::processPitchbendRangeRpnMessage (MidiRPNMessage rpn)
{ {
if (MPEZone* zone = getZoneByFirstNoteChannel (rpn.channel)) if (MPEZone* zone = getZoneByFirstNoteChannel (rpn.channel))
{ {
zone->setPerNotePitchbendRange (rpn.value);
return;
if (zone->getPerNotePitchbendRange() != rpn.value)
{
zone->setPerNotePitchbendRange (rpn.value);
listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this);
return;
}
} }
if (MPEZone* zone = getZoneByMasterChannel (rpn.channel)) if (MPEZone* zone = getZoneByMasterChannel (rpn.channel))
zone->setMasterPitchbendRange (rpn.value);
{
if (zone->getMasterPitchbendRange() != rpn.value)
{
zone->setMasterPitchbendRange (rpn.value);
listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this);
return;
}
}
} }
//============================================================================== //==============================================================================
@@ -162,6 +186,25 @@ MPEZone* MPEZoneLayout::getZoneByNoteChannel (int channel) const noexcept
return nullptr; return nullptr;
} }
//==============================================================================
void MPEZoneLayout::addListener (Listener* const listenerToAdd) noexcept
{
listeners.add (listenerToAdd);
}
void MPEZoneLayout::removeListener (Listener* const listenerToRemove) noexcept
{
listeners.remove (listenerToRemove);
}
MPEZoneLayout::Listener::Listener()
{
}
MPEZoneLayout::Listener::~Listener()
{
}
//============================================================================== //==============================================================================
//============================================================================== //==============================================================================
#if JUCE_UNIT_TESTS #if JUCE_UNIT_TESTS


+ 38
- 0
source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h View File

@@ -47,6 +47,16 @@ public:
*/ */
MPEZoneLayout() noexcept; MPEZoneLayout() noexcept;
/** Copy constuctor.
This will not copy the listeners registered to the MPEZoneLayout.
*/
MPEZoneLayout (const MPEZoneLayout& other);
/** Copy assignment operator.
This will not copy the listeners registered to the MPEZoneLayout.
*/
MPEZoneLayout& operator= (const MPEZoneLayout& other);
/** Adds a new MPE zone to the layout. /** Adds a new MPE zone to the layout.
@param newZone The zone to add. @param newZone The zone to add.
@@ -115,10 +125,38 @@ public:
*/ */
MPEZone* getZoneByNoteChannel (int midiChannel) const noexcept; MPEZone* getZoneByNoteChannel (int midiChannel) const noexcept;
//==========================================================================
/** Listener class. Derive from this class to allow your class to be
notified about changes to the zone layout.
*/
class Listener
{
public:
/** Constructor. */
Listener();
/** Destructor. */
virtual ~Listener();
/** Implement this callback to be notified about any changes to this
MPEZoneLayout. Will be called whenever a zone is added, zones are
removed, or any zone's master or note pitchbend ranges change.
*/
virtual void zoneLayoutChanged (const MPEZoneLayout& layout) = 0;
};
//==========================================================================
/** Adds a listener. */
void addListener (Listener* const listenerToAdd) noexcept;
/** Removes a listener. */
void removeListener (Listener* const listenerToRemove) noexcept;
private: private:
//========================================================================== //==========================================================================
Array<MPEZone> zones; Array<MPEZone> zones;
MidiRPNDetector rpnDetector; MidiRPNDetector rpnDetector;
ListenerList<Listener> listeners;
void processRpnMessage (MidiRPNMessage); void processRpnMessage (MidiRPNMessage);
void processZoneLayoutRpnMessage (MidiRPNMessage); void processZoneLayoutRpnMessage (MidiRPNMessage);


+ 376
- 195
source/modules/juce_audio_devices/native/juce_ios_Audio.cpp View File

@@ -22,56 +22,256 @@
============================================================================== ==============================================================================
*/ */
class iOSAudioIODevice : public AudioIODevice
class iOSAudioIODevice;
//==================================================================================================
struct AudioSessionHolder
{ {
public:
iOSAudioIODevice (const String& deviceName)
: AudioIODevice (deviceName, "Audio"),
actualBufferSize (0),
isRunning (false),
audioUnit (0),
callback (nullptr),
floatData (1, 2)
AudioSessionHolder();
~AudioSessionHolder();
void handleStatusChange (bool enabled, const char* reason) const;
void handleRouteChange (const char* reason) const;
Array<iOSAudioIODevice*> activeDevices;
id nativeSession;
};
static const char* getRoutingChangeReason (AVAudioSessionRouteChangeReason reason) noexcept
{
switch (reason)
{
case AVAudioSessionRouteChangeReasonNewDeviceAvailable: return "New device available";
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: return "Old device unavailable";
case AVAudioSessionRouteChangeReasonCategoryChange: return "Category change";
case AVAudioSessionRouteChangeReasonOverride: return "Override";
case AVAudioSessionRouteChangeReasonWakeFromSleep: return "Wake from sleep";
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: return "No suitable route for category";
case AVAudioSessionRouteChangeReasonRouteConfigurationChange: return "Route configuration change";
case AVAudioSessionRouteChangeReasonUnknown:
default: return "Unknown";
}
}
bool getNotificationValueForKey (NSNotification* notification, NSString* key, NSUInteger& value) noexcept
{
if (notification != nil)
{
if (NSDictionary* userInfo = [notification userInfo])
{
if (NSNumber* number = [userInfo objectForKey: key])
{
value = [number unsignedIntegerValue];
return true;
}
}
}
jassertfalse;
return false;
}
} // juce namespace
//==================================================================================================
@interface iOSAudioSessionNative : NSObject
{
@private
juce::AudioSessionHolder* audioSessionHolder;
};
- (id) init: (juce::AudioSessionHolder*) holder;
- (void) dealloc;
- (void) audioSessionDidChangeInterruptionType: (NSNotification*) notification;
- (void) handleMediaServicesReset;
- (void) handleMediaServicesLost;
- (void) handleRouteChange: (NSNotification*) notification;
@end
@implementation iOSAudioSessionNative
- (id) init: (juce::AudioSessionHolder*) holder
{
self = [super init];
if (self != nil)
{
audioSessionHolder = holder;
auto session = [AVAudioSession sharedInstance];
auto centre = [NSNotificationCenter defaultCenter];
[centre addObserver: self
selector: @selector (audioSessionDidChangeInterruptionType:)
name: AVAudioSessionInterruptionNotification
object: session];
[centre addObserver: self
selector: @selector (handleMediaServicesLost)
name: AVAudioSessionMediaServicesWereLostNotification
object: session];
[centre addObserver: self
selector: @selector (handleMediaServicesReset)
name: AVAudioSessionMediaServicesWereResetNotification
object: session];
[centre addObserver: self
selector: @selector (handleRouteChange:)
name: AVAudioSessionRouteChangeNotification
object: session];
}
else
{ {
getSessionHolder().activeDevices.add (this);
jassertfalse;
}
return self;
}
numInputChannels = 2;
numOutputChannels = 2;
preferredBufferSize = 0;
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
[super dealloc];
}
- (void) audioSessionDidChangeInterruptionType: (NSNotification*) notification
{
NSUInteger value;
updateDeviceInfo();
if (juce::getNotificationValueForKey (notification, AVAudioSessionInterruptionTypeKey, value))
{
switch ((AVAudioSessionInterruptionType) value)
{
case AVAudioSessionInterruptionTypeBegan:
audioSessionHolder->handleStatusChange (false, "AVAudioSessionInterruptionTypeBegan");
break;
case AVAudioSessionInterruptionTypeEnded:
audioSessionHolder->handleStatusChange (true, "AVAudioSessionInterruptionTypeEnded");
break;
// No default so the code doesn't compile if this enum is extended.
}
}
}
- (void) handleMediaServicesReset
{
audioSessionHolder->handleStatusChange (true, "AVAudioSessionMediaServicesWereResetNotification");
}
- (void) handleMediaServicesLost
{
audioSessionHolder->handleStatusChange (false, "AVAudioSessionMediaServicesWereLostNotification");
}
- (void) handleRouteChange: (NSNotification*) notification
{
NSUInteger value;
if (juce::getNotificationValueForKey (notification, AVAudioSessionRouteChangeReasonKey, value))
audioSessionHolder->handleRouteChange (juce::getRoutingChangeReason ((AVAudioSessionRouteChangeReason) value));
}
@end
//==================================================================================================
namespace juce {
#ifndef JUCE_IOS_AUDIO_LOGGING
#define JUCE_IOS_AUDIO_LOGGING 0
#endif
#if JUCE_IOS_AUDIO_LOGGING
#define JUCE_IOS_AUDIO_LOG(x) DBG(x)
#else
#define JUCE_IOS_AUDIO_LOG(x)
#endif
static void logNSError (NSError* e)
{
if (e != nil)
{
JUCE_IOS_AUDIO_LOG ("iOS Audio error: " << [e.localizedDescription UTF8String]);
jassertfalse;
}
}
#define JUCE_NSERROR_CHECK(X) { NSError* error = nil; X; logNSError (error); }
//==================================================================================================
class iOSAudioIODevice : public AudioIODevice
{
public:
iOSAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, "Audio")
{
sessionHolder->activeDevices.add (this);
updateSampleRateAndAudioInput();
} }
~iOSAudioIODevice() ~iOSAudioIODevice()
{ {
getSessionHolder().activeDevices.removeFirstMatchingValue (this);
sessionHolder->activeDevices.removeFirstMatchingValue (this);
close(); close();
} }
StringArray getOutputChannelNames() override StringArray getOutputChannelNames() override
{ {
StringArray s;
s.add ("Left");
s.add ("Right");
return s;
return { "Left", "Right" };
} }
StringArray getInputChannelNames() override StringArray getInputChannelNames() override
{ {
StringArray s;
if (audioInputIsAvailable) if (audioInputIsAvailable)
{
s.add ("Left");
s.add ("Right");
}
return s;
return { "Left", "Right" };
return {};
}
static void setAudioSessionActive (bool enabled)
{
JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setActive: enabled
error: &error]);
}
static double trySampleRate (double rate)
{
auto session = [AVAudioSession sharedInstance];
JUCE_NSERROR_CHECK ([session setPreferredSampleRate: rate
error: &error]);
return session.sampleRate;
} }
Array<double> getAvailableSampleRates() override Array<double> getAvailableSampleRates() override
{ {
// can't find a good way to actually ask the device for which of these it supports..
static const double rates[] = { 8000.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0 };
return Array<double> (rates, numElementsInArray (rates));
Array<double> rates;
// Important: the supported audio sample rates change on the iPhone 6S
// depending on whether the headphones are plugged in or not!
setAudioSessionActive (true);
const double lowestRate = trySampleRate (4000);
const double highestRate = trySampleRate (192000);
for (double rate = lowestRate; rate <= highestRate; rate += 1000)
{
const double supportedRate = trySampleRate (rate);
rates.addIfNotAlreadyThere (supportedRate);
rate = jmax (rate, supportedRate);
}
for (auto r : rates)
{
ignoreUnused (r);
JUCE_IOS_AUDIO_LOG ("available rate = " + String (r, 0) + "Hz");
}
return rates;
} }
Array<int> getAvailableBufferSizes() override Array<int> getAvailableBufferSizes() override
@@ -84,7 +284,7 @@ public:
return r; return r;
} }
int getDefaultBufferSize() override { return 1024; }
int getDefaultBufferSize() override { return 256; }
String open (const BigInteger& inputChannelsWanted, String open (const BigInteger& inputChannelsWanted,
const BigInteger& outputChannelsWanted, const BigInteger& outputChannelsWanted,
@@ -93,7 +293,8 @@ public:
close(); close();
lastError.clear(); lastError.clear();
preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
preferredBufferSize = bufferSize <= 0 ? getDefaultBufferSize()
: bufferSize;
// xxx set up channel mapping // xxx set up channel mapping
@@ -107,34 +308,39 @@ public:
numInputChannels = activeInputChans.countNumberOfSetBits(); numInputChannels = activeInputChans.countNumberOfSetBits();
monoInputChannelNumber = activeInputChans.findNextSetBit (0); monoInputChannelNumber = activeInputChans.findNextSetBit (0);
AudioSessionSetActive (true);
setAudioSessionActive (true);
if (numInputChannels > 0 && audioInputIsAvailable)
{
setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_PlayAndRecord);
setSessionUInt32Property (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, 1);
}
else
{
setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback);
}
// Set the session category & options:
auto session = [AVAudioSession sharedInstance];
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this);
const bool useInputs = (numInputChannels > 0 && audioInputIsAvailable);
fixAudioRouteIfSetToReceiver();
NSString* category = (useInputs ? AVAudioSessionCategoryPlayAndRecord : AVAudioSessionCategoryPlayback);
NSUInteger options = AVAudioSessionCategoryOptionMixWithOthers; // Alternatively AVAudioSessionCategoryOptionDuckOthers
if (useInputs) // These options are only valid for category = PlayAndRecord
options |= (AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowBluetooth);
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, targetSampleRate);
updateDeviceInfo();
JUCE_NSERROR_CHECK ([session setCategory: category
withOptions: options
error: &error]);
setSessionFloat32Property (kAudioSessionProperty_PreferredHardwareIOBufferDuration, preferredBufferSize / sampleRate);
fixAudioRouteIfSetToReceiver();
// Set the sample rate
trySampleRate (targetSampleRate);
updateSampleRateAndAudioInput();
updateCurrentBufferSize(); updateCurrentBufferSize();
prepareFloatBuffers (actualBufferSize); prepareFloatBuffers (actualBufferSize);
isRunning = true; isRunning = true;
routingChanged (nullptr); // creates and starts the AU
handleRouteChange ("Started AudioUnit");
lastError = (audioUnit != 0 ? "" : "Couldn't open the device");
setAudioSessionActive (true);
lastError = audioUnit != 0 ? "" : "Couldn't open the device";
return lastError; return lastError;
} }
@@ -144,10 +350,7 @@ public:
{ {
isRunning = false; isRunning = false;
setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback);
AudioSessionRemovePropertyListenerWithUserData (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this);
AudioSessionSetActive (false);
setAudioSessionActive (false);
if (audioUnit != 0) if (audioUnit != 0)
{ {
@@ -157,24 +360,17 @@ public:
} }
} }
bool isOpen() override { return isRunning; }
bool isOpen() override { return isRunning; }
int getCurrentBufferSizeSamples() override { return actualBufferSize; }
double getCurrentSampleRate() override { return sampleRate; }
int getCurrentBitDepth() override { return 16; }
int getCurrentBufferSizeSamples() override { return actualBufferSize; }
double getCurrentSampleRate() override { return sampleRate; }
int getCurrentBitDepth() override { return 16; }
BigInteger getActiveOutputChannels() const override { return activeOutputChans; } BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
BigInteger getActiveInputChannels() const override { return activeInputChans; } BigInteger getActiveInputChannels() const override { return activeInputChans; }
int getOutputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareOutputLatency); }
int getInputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareInputLatency); }
int getLatency (AudioSessionPropertyID propID)
{
Float32 latency = 0;
getSessionProperty (propID, latency);
return roundToInt (latency * getCurrentSampleRate());
}
int getOutputLatencyInSamples() override { return roundToInt (getCurrentSampleRate() * [AVAudioSession sharedInstance].outputLatency); }
int getInputLatencyInSamples() override { return roundToInt (getCurrentSampleRate() * [AVAudioSession sharedInstance].inputLatency); }
void start (AudioIODeviceCallback* newCallback) override void start (AudioIODeviceCallback* newCallback) override
{ {
@@ -210,23 +406,82 @@ public:
bool setAudioPreprocessingEnabled (bool enable) override bool setAudioPreprocessingEnabled (bool enable) override
{ {
return setSessionUInt32Property (kAudioSessionProperty_Mode, enable ? kAudioSessionMode_Default
: kAudioSessionMode_Measurement);
auto session = [AVAudioSession sharedInstance];
NSString* mode = (enable ? AVAudioSessionModeMeasurement
: AVAudioSessionModeDefault);
JUCE_NSERROR_CHECK ([session setMode: mode
error: &error]);
return session.mode == mode;
}
void invokeAudioDeviceErrorCallback (const String& reason)
{
const ScopedLock sl (callbackLock);
if (callback != nullptr)
callback->audioDeviceError (reason);
}
void handleStatusChange (bool enabled, const char* reason)
{
JUCE_IOS_AUDIO_LOG ("handleStatusChange: enabled: " << (int) enabled << ", reason: " << reason);
isRunning = enabled;
setAudioSessionActive (enabled);
if (enabled)
AudioOutputUnitStart (audioUnit);
else
AudioOutputUnitStop (audioUnit);
if (! enabled)
invokeAudioDeviceErrorCallback (reason);
}
void handleRouteChange (const char* reason)
{
JUCE_IOS_AUDIO_LOG ("handleRouteChange: reason: " << reason);
fixAudioRouteIfSetToReceiver();
if (isRunning)
{
invokeAudioDeviceErrorCallback (reason);
updateSampleRateAndAudioInput();
updateCurrentBufferSize();
createAudioUnit();
setAudioSessionActive (true);
if (audioUnit != 0)
{
UInt32 formatSize = sizeof (format);
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize);
AudioOutputUnitStart (audioUnit);
}
if (callback)
callback->audioDeviceAboutToStart (this);
}
} }
private: private:
//================================================================================================== //==================================================================================================
SharedResourcePointer<AudioSessionHolder> sessionHolder;
CriticalSection callbackLock; CriticalSection callbackLock;
Float64 sampleRate;
int numInputChannels, numOutputChannels;
int preferredBufferSize, actualBufferSize;
bool isRunning;
NSTimeInterval sampleRate = 0;
int numInputChannels = 2, numOutputChannels = 2;
int preferredBufferSize = 0, actualBufferSize = 0;
bool isRunning = false;
String lastError; String lastError;
AudioStreamBasicDescription format; AudioStreamBasicDescription format;
AudioUnit audioUnit;
UInt32 audioInputIsAvailable;
AudioIODeviceCallback* callback;
AudioUnit audioUnit {};
bool audioInputIsAvailable = false;
AudioIODeviceCallback* callback = nullptr;
BigInteger activeOutputChans, activeInputChans; BigInteger activeOutputChans, activeInputChans;
AudioSampleBuffer floatData; AudioSampleBuffer floatData;
@@ -299,7 +554,7 @@ private:
callback->audioDeviceIOCallback ((const float**) inputChannels, numInputChannels, callback->audioDeviceIOCallback ((const float**) inputChannels, numInputChannels,
outputChannels, numOutputChannels, (int) numFrames); outputChannels, numOutputChannels, (int) numFrames);
short* shortData = (short*) data->mBuffers[0].mData;
short* const shortData = (short*) data->mBuffers[0].mData;
int n = 0; int n = 0;
if (numOutputChannels >= 2) if (numOutputChannels >= 2)
@@ -332,119 +587,31 @@ private:
return err; return err;
} }
void updateDeviceInfo()
void updateSampleRateAndAudioInput()
{ {
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate);
getSessionProperty (kAudioSessionProperty_AudioInputAvailable, audioInputIsAvailable);
auto session = [AVAudioSession sharedInstance];
sampleRate = session.sampleRate;
audioInputIsAvailable = session.isInputAvailable;
JUCE_IOS_AUDIO_LOG ("AVAudioSession: sampleRate: " << sampleRate << "Hz, audioInputAvailable: " << (int) audioInputIsAvailable);
} }
void updateCurrentBufferSize() void updateCurrentBufferSize()
{ {
Float32 bufferDuration = sampleRate > 0 ? (Float32) (preferredBufferSize / sampleRate) : 0.0f;
getSessionProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, bufferDuration);
actualBufferSize = (int) (sampleRate * bufferDuration + 0.5);
}
void routingChanged (const void* propertyValue)
{
if (! isRunning)
return;
if (propertyValue != nullptr)
{
CFDictionaryRef routeChangeDictionary = (CFDictionaryRef) propertyValue;
CFNumberRef routeChangeReasonRef = (CFNumberRef) CFDictionaryGetValue (routeChangeDictionary,
CFSTR (kAudioSession_AudioRouteChangeKey_Reason));
SInt32 routeChangeReason;
CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
auto session = [AVAudioSession sharedInstance];
NSTimeInterval bufferDuration = sampleRate > 0 ? (NSTimeInterval) (preferredBufferSize / sampleRate) : 0.0;
JUCE_NSERROR_CHECK ([session setPreferredIOBufferDuration: bufferDuration
error: &error]);
if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable)
{
const ScopedLock sl (callbackLock);
if (callback != nullptr)
callback->audioDeviceError ("Old device unavailable");
}
}
updateDeviceInfo();
createAudioUnit();
AudioSessionSetActive (true);
if (audioUnit != 0)
{
UInt32 formatSize = sizeof (format);
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize);
updateCurrentBufferSize();
AudioOutputUnitStart (audioUnit);
}
}
//==================================================================================================
struct AudioSessionHolder
{
AudioSessionHolder()
{
AudioSessionInitialize (0, 0, interruptionListenerCallback, this);
}
static void interruptionListenerCallback (void* client, UInt32 interruptionType)
{
const Array<iOSAudioIODevice*>& activeDevices = static_cast<AudioSessionHolder*> (client)->activeDevices;
for (int i = activeDevices.size(); --i >= 0;)
activeDevices.getUnchecked(i)->interruptionListener (interruptionType);
}
Array<iOSAudioIODevice*> activeDevices;
};
static AudioSessionHolder& getSessionHolder()
{
static AudioSessionHolder audioSessionHolder;
return audioSessionHolder;
}
void interruptionListener (const UInt32 interruptionType)
{
if (interruptionType == kAudioSessionBeginInterruption)
{
isRunning = false;
AudioOutputUnitStop (audioUnit);
AudioSessionSetActive (false);
const ScopedLock sl (callbackLock);
if (callback != nullptr)
callback->audioDeviceError ("iOS audio session interruption");
}
if (interruptionType == kAudioSessionEndInterruption)
{
isRunning = true;
AudioSessionSetActive (true);
AudioOutputUnitStart (audioUnit);
const ScopedLock sl (callbackLock);
if (callback != nullptr)
callback->audioDeviceError ("iOS audio session resumed");
}
bufferDuration = session.IOBufferDuration;
actualBufferSize = roundToInt (sampleRate * bufferDuration);
} }
//================================================================================================== //==================================================================================================
static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data)
{ {
return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data);
}
static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue)
{
static_cast<iOSAudioIODevice*> (client)->routingChanged (propertyValue);
return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data);
} }
//================================================================================================== //==================================================================================================
@@ -515,31 +682,28 @@ private:
// to make it loud. Needed because by default when using an input + output, the output is kept quiet. // to make it loud. Needed because by default when using an input + output, the output is kept quiet.
static void fixAudioRouteIfSetToReceiver() static void fixAudioRouteIfSetToReceiver()
{ {
CFStringRef audioRoute = 0;
if (getSessionProperty (kAudioSessionProperty_AudioRoute, audioRoute) == noErr)
{
NSString* route = (NSString*) audioRoute;
auto session = [AVAudioSession sharedInstance];
auto route = session.currentRoute;
//DBG ("audio route: " + nsStringToJuce (route));
for (AVAudioSessionPortDescription* port in route.inputs)
{
ignoreUnused (port);
JUCE_IOS_AUDIO_LOG ("AVAudioSession: input: " << [port.description UTF8String]);
}
if ([route hasPrefix: @"Receiver"])
setSessionUInt32Property (kAudioSessionProperty_OverrideAudioRoute, kAudioSessionOverrideAudioRoute_Speaker);
for (AVAudioSessionPortDescription* port in route.outputs)
{
JUCE_IOS_AUDIO_LOG ("AVAudioSession: output: " << [port.description UTF8String]);
CFRelease (audioRoute);
if ([port.portName isEqualToString: @"Receiver"])
{
JUCE_NSERROR_CHECK ([session overrideOutputAudioPort: AVAudioSessionPortOverrideSpeaker
error: &error]);
setAudioSessionActive (true);
}
} }
} }
template <typename Type>
static OSStatus getSessionProperty (AudioSessionPropertyID propID, Type& result) noexcept
{
UInt32 valueSize = sizeof (result);
return AudioSessionGetProperty (propID, &valueSize, &result);
}
static bool setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
static bool setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
static bool setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice)
}; };
@@ -559,8 +723,7 @@ public:
AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName)
{ {
if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
return new iOSAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
: inputDeviceName);
return new iOSAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName);
return nullptr; return nullptr;
} }
@@ -574,3 +737,21 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio()
{ {
return new iOSAudioIODeviceType(); return new iOSAudioIODeviceType();
} }
//==================================================================================================
AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; }
AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; }
void AudioSessionHolder::handleStatusChange (bool enabled, const char* reason) const
{
for (auto device: activeDevices)
device->handleStatusChange (enabled, reason);
}
void AudioSessionHolder::handleRouteChange (const char* reason) const
{
for (auto device: activeDevices)
device->handleRouteChange (reason);
}
#undef JUCE_NSERROR_CHECK

+ 7
- 0
source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h View File

@@ -53,3 +53,10 @@ private:
}; };
#endif #endif
//==============================================================================
enum
{
/** Custom AudioUnit property used to indicate MPE support */
kAudioUnitProperty_SupportsMPE = 75001
};

+ 49
- 19
source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -445,12 +445,7 @@ public:
(int) (numOutputBusChannels * numOutputBusses), (int) (numOutputBusChannels * numOutputBusses),
(double) newSampleRate, estimatedSamplesPerBlock); (double) newSampleRate, estimatedSamplesPerBlock);
Float64 latencySecs = 0.0;
UInt32 latencySize = sizeof (latencySecs);
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
0, &latencySecs, &latencySize);
setLatencySamples (roundToInt (latencySecs * newSampleRate));
updateLatency();
{ {
AudioStreamBasicDescription stream; AudioStreamBasicDescription stream;
@@ -889,6 +884,16 @@ public:
} }
} }
void updateLatency()
{
Float64 latencySecs = 0.0;
UInt32 latencySize = sizeof (latencySecs);
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
0, &latencySecs, &latencySize);
setLatencySamples (roundToInt (latencySecs * getSampleRate()));
}
void handleIncomingMidiMessage (void*, const MidiMessage& message) void handleIncomingMidiMessage (void*, const MidiMessage& message)
{ {
const ScopedLock sl (midiInLock); const ScopedLock sl (midiInLock);
@@ -993,22 +998,24 @@ private:
AUEventListenerAddEventType (eventListenerRef, nullptr, &event); AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
} }
// Add a listener for program changes
AudioUnitEvent event;
event.mArgument.mProperty.mAudioUnit = audioUnit;
event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_PresentPreset;
event.mArgument.mProperty.mScope = kAudioUnitScope_Global;
event.mArgument.mProperty.mElement = 0;
event.mEventType = kAudioUnitEvent_PropertyChange;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
// Add a listener for parameter list changes
event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_ParameterList;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
addPropertyChangeListener (kAudioUnitProperty_PresentPreset);
addPropertyChangeListener (kAudioUnitProperty_ParameterList);
addPropertyChangeListener (kAudioUnitProperty_Latency);
} }
} }
void addPropertyChangeListener (AudioUnitPropertyID type) const
{
AudioUnitEvent event;
event.mEventType = kAudioUnitEvent_PropertyChange;
event.mArgument.mProperty.mPropertyID = type;
event.mArgument.mProperty.mAudioUnit = audioUnit;
event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_PresentPreset;
event.mArgument.mProperty.mScope = kAudioUnitScope_Global;
event.mArgument.mProperty.mElement = 0;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
}
void eventCallback (const AudioUnitEvent& event, AudioUnitParameterValue newValue) void eventCallback (const AudioUnitEvent& event, AudioUnitParameterValue newValue)
{ {
switch (event.mEventType) switch (event.mEventType)
@@ -1040,6 +1047,8 @@ private:
updateHostDisplay(); updateHostDisplay();
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_PresentPreset) else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_PresentPreset)
sendAllParametersChangedEvents(); sendAllParametersChangedEvents();
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_Latency)
updateLatency();
break; break;
} }
@@ -1329,6 +1338,27 @@ private:
return false; return false;
} }
bool supportsMPE() const override
{
UInt32 dataSize = 0;
Boolean isWritable = false;
if (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_SupportsMPE,
kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr
&& dataSize == sizeof (UInt32))
{
UInt32 result = 0;
if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportsMPE,
kAudioUnitScope_Global, 0, &result, &dataSize) == noErr)
{
return result > 0;
}
}
return false;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance)
}; };


+ 7
- 5
source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -868,10 +868,10 @@ public:
if (getVstCategory() != kPlugCategShell) // (workaround for Waves 5 plugins which crash during this call) if (getVstCategory() != kPlugCategShell) // (workaround for Waves 5 plugins which crash during this call)
updateStoredProgramNames(); updateStoredProgramNames();
wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0;
wantsMidiMessages = pluginCanDo ("receiveVstMidiEvent") > 0;
#if JUCE_MAC && JUCE_SUPPORT_CARBON #if JUCE_MAC && JUCE_SUPPORT_CARBON
usesCocoaNSView = (dispatch (effCanDo, 0, 0, (void*) "hasCockosViewAsConfig", 0) & (int) 0xffff0000) == 0xbeef0000;
usesCocoaNSView = (pluginCanDo ("hasCockosViewAsConfig") & (int) 0xffff0000) == 0xbeef0000;
#endif #endif
setLatencySamples (effect->initialDelay); setLatencySamples (effect->initialDelay);
@@ -910,10 +910,13 @@ public:
} }
bool acceptsMidi() const override { return wantsMidiMessages; } bool acceptsMidi() const override { return wantsMidiMessages; }
bool producesMidi() const override { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; }
bool producesMidi() const override { return pluginCanDo ("sendVstMidiEvent") > 0; }
bool supportsMPE() const override { return pluginCanDo ("MPE") > 0; }
VstPlugCategory getVstCategory() const noexcept { return (VstPlugCategory) dispatch (effGetPlugCategory, 0, 0, 0, 0); } VstPlugCategory getVstCategory() const noexcept { return (VstPlugCategory) dispatch (effGetPlugCategory, 0, 0, 0, 0); }
int pluginCanDo (const char* text) const { return (int) dispatch (effCanDo, 0, 0, (void*) text, 0); }
//============================================================================== //==============================================================================
void prepareToPlay (double rate, int samplesPerBlockExpected) override void prepareToPlay (double rate, int samplesPerBlockExpected) override
{ {
@@ -930,8 +933,7 @@ public:
if (initialised) if (initialised)
{ {
wantsMidiMessages = wantsMidiMessages
|| (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0);
wantsMidiMessages = wantsMidiMessages || (pluginCanDo ("receiveVstMidiEvent") > 0);
if (wantsMidiMessages) if (wantsMidiMessages)
midiEventsToSend.ensureSize (256); midiEventsToSend.ensureSize (256);


+ 3
- 0
source/modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -465,6 +465,9 @@ public:
/** Returns true if the processor produces midi messages. */ /** Returns true if the processor produces midi messages. */
virtual bool producesMidi() const = 0; virtual bool producesMidi() const = 0;
/** Returns true if the processor supports MPE. */
virtual bool supportsMPE() const { return false; }
//============================================================================== //==============================================================================
/** This returns a critical section that will automatically be locked while the host /** This returns a critical section that will automatically be locked while the host
is calling the processBlock() method. is calling the processBlock() method.


+ 3
- 1
source/modules/juce_core/native/juce_BasicNativeHeaders.h View File

@@ -114,7 +114,9 @@
#if JUCE_MINGW #if JUCE_MINGW
#include <basetyps.h> #include <basetyps.h>
#include <sys/time.h> #include <sys/time.h>
#define alloca(x) __builtin_alloca((x))
#ifndef alloca
#define alloca __builtin_alloca
#endif
#else #else
#include <crtdbg.h> #include <crtdbg.h>
#include <comutil.h> #include <comutil.h>


+ 0
- 6
source/modules/juce_core/text/juce_CharPointer_UTF8.h View File

@@ -420,13 +420,7 @@ public:
/** Compares this string with another one. */ /** Compares this string with another one. */
int compareIgnoreCase (const CharPointer_UTF8 other) const noexcept int compareIgnoreCase (const CharPointer_UTF8 other) const noexcept
{ {
#if JUCE_MINGW || (JUCE_WINDOWS && JUCE_CLANG)
return CharacterFunctions::compareIgnoreCase (*this, other); return CharacterFunctions::compareIgnoreCase (*this, other);
#elif JUCE_WINDOWS
return stricmp (data, other.data);
#else
return strcasecmp (data, other.data);
#endif
} }
/** Compares this string with another one, up to a specified number of characters. */ /** Compares this string with another one, up to a specified number of characters. */


+ 37
- 22
source/modules/juce_core/text/juce_CharacterFunctions.h View File

@@ -391,18 +391,28 @@ public:
dest.writeNull(); dest.writeNull();
} }
/** Compares two characters. */
static inline int compare (juce_wchar char1, juce_wchar char2) noexcept
{
if (int diff = static_cast<int> (char1) - static_cast<int> (char2))
return diff < 0 ? -1 : 1;
return 0;
}
/** Compares two null-terminated character strings. */ /** Compares two null-terminated character strings. */
template <typename CharPointerType1, typename CharPointerType2> template <typename CharPointerType1, typename CharPointerType2>
static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept
{ {
for (;;) for (;;)
{ {
const int c1 = (int) s1.getAndAdvance();
const int c2 = (int) s2.getAndAdvance();
const int diff = c1 - c2;
const juce_wchar c1 = s1.getAndAdvance();
if (int diff = compare (c1, s2.getAndAdvance()))
return diff;
if (diff != 0) return diff < 0 ? -1 : 1;
if (c1 == 0) break;
if (c1 == 0)
break;
} }
return 0; return 0;
@@ -414,31 +424,37 @@ public:
{ {
while (--maxChars >= 0) while (--maxChars >= 0)
{ {
const int c1 = (int) s1.getAndAdvance();
const int c2 = (int) s2.getAndAdvance();
const int diff = c1 - c2;
const juce_wchar c1 = s1.getAndAdvance();
if (diff != 0) return diff < 0 ? -1 : 1;
if (c1 == 0) break;
if (int diff = compare (c1, s2.getAndAdvance()))
return diff;
if (c1 == 0)
break;
} }
return 0; return 0;
} }
/** Compares two characters, using a case-independant match. */
static inline int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept
{
return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0;
}
/** Compares two null-terminated character strings, using a case-independant match. */ /** Compares two null-terminated character strings, using a case-independant match. */
template <typename CharPointerType1, typename CharPointerType2> template <typename CharPointerType1, typename CharPointerType2>
static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept
{ {
for (;;) for (;;)
{ {
const int c1 = (int) s1.toUpperCase();
const int c2 = (int) s2.toUpperCase();
const int diff = c1 - c2;
const juce_wchar c1 = s1.getAndAdvance();
if (diff != 0) return diff < 0 ? -1 : 1;
if (c1 == 0) break;
if (int diff = compareIgnoreCase (c1, s2.getAndAdvance()))
return diff;
++s1; ++s2;
if (c1 == 0)
break;
} }
return 0; return 0;
@@ -450,14 +466,13 @@ public:
{ {
while (--maxChars >= 0) while (--maxChars >= 0)
{ {
const int c1 = (int) s1.toUpperCase();
const int c2 = (int) s2.toUpperCase();
const int diff = c1 - c2;
const juce_wchar c1 = s1.getAndAdvance();
if (diff != 0) return diff < 0 ? -1 : 1;
if (c1 == 0) break;
if (int diff = compareIgnoreCase (c1, s2.getAndAdvance()))
return diff;
++s1; ++s2;
if (c1 == 0)
break;
} }
return 0; return 0;


+ 1
- 2
source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp View File

@@ -248,7 +248,7 @@ public:
setAlwaysOnTop (true); setAlwaysOnTop (true);
setLookAndFeel (parent != nullptr ? &(parent->getLookAndFeel()) setLookAndFeel (parent != nullptr ? &(parent->getLookAndFeel())
: menu.lookAndFeel);
: menu.lookAndFeel.get());
setOpaque (getLookAndFeel().findColour (PopupMenu::backgroundColourId).isOpaque() setOpaque (getLookAndFeel().findColour (PopupMenu::backgroundColourId).isOpaque()
|| ! Desktop::canUseSemiTransparentWindows()); || ! Desktop::canUseSemiTransparentWindows());
@@ -1234,7 +1234,6 @@ private:
//============================================================================== //==============================================================================
PopupMenu::PopupMenu() PopupMenu::PopupMenu()
: lookAndFeel (nullptr)
{ {
} }


+ 1
- 1
source/modules/juce_gui_basics/menus/juce_PopupMenu.h View File

@@ -614,7 +614,7 @@ private:
friend class MenuBarComponent; friend class MenuBarComponent;
OwnedArray<Item> items; OwnedArray<Item> items;
LookAndFeel* lookAndFeel;
WeakReference<LookAndFeel> lookAndFeel;
Component* createWindow (const Options&, ApplicationCommandManager**) const; Component* createWindow (const Options&, ApplicationCommandManager**) const;
int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool); int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool);


Loading…
Cancel
Save