diff --git a/libs/juce/source/modules/juce_audio_basics/juce_module_info b/libs/juce/source/modules/juce_audio_basics/juce_module_info index 3ba936ce..2203c48d 100644 --- a/libs/juce/source/modules/juce_audio_basics/juce_module_info +++ b/libs/juce/source/modules/juce_audio_basics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_basics", "name": "JUCE audio and midi data classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index 71ac41c5..6f6c3747 100644 --- a/libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -155,7 +155,7 @@ MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp) MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, double t) : timeStamp (t) { - const uint8* src = static_cast (srcData); + const uint8* src = static_cast (srcData); unsigned int byte = (unsigned int) *src; if (byte < 0x80) @@ -658,6 +658,36 @@ String MidiMessage::getTextFromTextMetaEvent() const CharPointer_UTF8 (textData + getMetaEventLength())); } +MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) +{ + jassert (type > 0 && type < 16) + + MidiMessage result; + + const size_t textSize = text.text.sizeInBytes() - 1; + + uint8 header[8]; + size_t n = sizeof (header); + + header[--n] = (uint8) (textSize & 0x7f); + + for (size_t i = textSize; (i >>= 7) != 0;) + header[--n] = (uint8) ((i & 0x7f) | 0x80); + + header[--n] = (uint8) type; + header[--n] = 0xff; + + const size_t headerLen = sizeof (header) - n; + + uint8* const dest = result.allocateSpace ((int) (headerLen + textSize)); + result.size = (int) (headerLen + textSize); + + memcpy (dest, header + n, headerLen); + memcpy (dest + headerLen, text.text.getAddress(), textSize); + + return result; +} + bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 3) && (*data == 0xff); } bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 81) && (*data == 0xff); } bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } diff --git a/libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.h b/libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.h index 44eee7c2..4694c550 100644 --- a/libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.h +++ b/libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.h @@ -168,9 +168,7 @@ public: bool isForChannel (int channelNumber) const noexcept; /** Changes the message's midi channel. - This won't do anything for non-channel messages like sysexes. - @param newChannelNumber the channel number to change it to, in the range 1 to 16 */ void setChannel (int newChannelNumber) noexcept; @@ -181,17 +179,13 @@ public: bool isSysEx() const noexcept; /** Returns a pointer to the sysex data inside the message. - If this event isn't a sysex event, it'll return 0. - @see getSysExDataSize */ const uint8* getSysExData() const noexcept; /** Returns the size of the sysex data. - This value excludes the 0xf0 header byte and the 0xf7 at the end. - @see getSysExData */ int getSysExDataSize() const noexcept; @@ -252,15 +246,12 @@ public: bool isNoteOnOrOff() const noexcept; /** Returns the midi note number for note-on and note-off messages. - If the message isn't a note-on or off, the value returned is undefined. - @see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber */ int getNoteNumber() const noexcept; /** Changes the midi note number of a note-on or note-off message. - If the message isn't a note on or off, this will do nothing. */ void setNoteNumber (int newNoteNumber) noexcept; @@ -320,16 +311,12 @@ public: //============================================================================== /** Returns true if the message is a program (patch) change message. - @see getProgramChangeNumber, getGMInstrumentName */ bool isProgramChange() const noexcept; /** Returns the new program number of a program change message. - - If the message isn't a program change, the value returned will be - nonsense. - + If the message isn't a program change, the value returned is undefined. @see isProgramChange, getGMInstrumentName */ int getProgramChangeNumber() const noexcept; @@ -344,7 +331,6 @@ public: //============================================================================== /** Returns true if the message is a pitch-wheel move. - @see getPitchWheelValue, pitchWheel */ bool isPitchWheel() const noexcept; @@ -433,7 +419,6 @@ public: /** Returns the controller number of a controller message. The name of the controller can be looked up using the getControllerName() method. - Note that the value returned is invalid for messages that aren't controller changes. @see isController, getControllerName, getControllerValue @@ -443,7 +428,6 @@ public: /** Returns the controller value from a controller message. A value 0 to 127 is returned to indicate the new controller position. - Note that the value returned is invalid for messages that aren't controller changes. @see isController, getControllerNumber @@ -467,13 +451,11 @@ public: int value) noexcept; /** Checks whether this message is an all-notes-off message. - @see allNotesOff */ bool isAllNotesOff() const noexcept; /** Checks whether this message is an all-sound-off message. - @see allSoundOff */ bool isAllSoundOff() const noexcept; @@ -520,13 +502,11 @@ public: int getMetaEventType() const noexcept; /** Returns a pointer to the data in a meta-event. - @see isMetaEvent, getMetaEventLength */ const uint8* getMetaEventData() const noexcept; /** Returns the length of the data for a meta-event. - @see isMetaEvent, getMetaEventData */ int getMetaEventLength() const noexcept; @@ -539,29 +519,28 @@ public: bool isEndOfTrackMetaEvent() const noexcept; /** Creates an end-of-track meta-event. - @see isEndOfTrackMetaEvent */ static MidiMessage endOfTrack() noexcept; /** Returns true if this is an 'track name' meta-event. - You can use the getTextFromTextMetaEvent() method to get the track's name. */ bool isTrackNameEvent() const noexcept; /** Returns true if this is a 'text' meta-event. - @see getTextFromTextMetaEvent */ bool isTextMetaEvent() const noexcept; /** Returns the text from a text meta-event. - @see isTextMetaEvent */ String getTextFromTextMetaEvent() const; + /** Creates a text meta-event. */ + static MidiMessage textMetaEvent (int type, StringRef text); + //============================================================================== /** Returns true if this is a 'tempo' meta-event. @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote @@ -660,7 +639,6 @@ public: //============================================================================== /** Returns true if this is a midi start event. - @see midiStart */ bool isMidiStart() const noexcept; @@ -669,7 +647,6 @@ public: static MidiMessage midiStart() noexcept; /** Returns true if this is a midi continue event. - @see midiContinue */ bool isMidiContinue() const noexcept; @@ -678,7 +655,6 @@ public: static MidiMessage midiContinue() noexcept; /** Returns true if this is a midi stop event. - @see midiStop */ bool isMidiStop() const noexcept; @@ -687,7 +663,6 @@ public: static MidiMessage midiStop() noexcept; /** Returns true if this is a midi clock event. - @see midiClock, songPositionPointer */ bool isMidiClock() const noexcept; @@ -696,13 +671,11 @@ public: static MidiMessage midiClock() noexcept; /** Returns true if this is a song-position-pointer message. - @see getSongPositionPointerMidiBeat, songPositionPointer */ bool isSongPositionPointer() const noexcept; /** Returns the midi beat-number of a song-position-pointer message. - @see isSongPositionPointer, songPositionPointer */ int getSongPositionPointerMidiBeat() const noexcept; @@ -719,23 +692,18 @@ public: //============================================================================== /** Returns true if this is a quarter-frame midi timecode message. - @see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue */ bool isQuarterFrame() const noexcept; /** Returns the sequence number of a quarter-frame midi timecode message. - This will be a value between 0 and 7. - @see isQuarterFrame, getQuarterFrameValue, quarterFrame */ int getQuarterFrameSequenceNumber() const noexcept; /** Returns the value from a quarter-frame message. - - This will be the lower nybble of the message's data-byte, a value - between 0 and 15 + This will be the lower nybble of the message's data-byte, a value between 0 and 15 */ int getQuarterFrameValue() const noexcept; @@ -747,7 +715,6 @@ public: static MidiMessage quarterFrame (int sequenceNumber, int value) noexcept; /** SMPTE timecode types. - Used by the getFullFrameParameters() and fullFrame() methods. */ enum SmpteTimecodeType @@ -758,8 +725,7 @@ public: fps30 = 3 }; - /** Returns true if this is a full-frame midi timecode message. - */ + /** Returns true if this is a full-frame midi timecode message. */ bool isFullFrame() const noexcept; /** Extracts the timecode information from a full-frame midi timecode message. @@ -773,8 +739,7 @@ public: int& frames, SmpteTimecodeType& timecodeType) const noexcept; - /** Creates a full-frame MTC message. - */ + /** Creates a full-frame MTC message. */ static MidiMessage fullFrame (int hours, int minutes, int seconds, @@ -799,7 +764,6 @@ public: }; /** Checks whether this is an MMC message. - If it is, you can use the getMidiMachineControlCommand() to find out its type. */ bool isMidiMachineControlMessage() const noexcept; diff --git a/libs/juce/source/modules/juce_audio_basics/sources/juce_AudioSource.h b/libs/juce/source/modules/juce_audio_basics/sources/juce_AudioSource.h index e50b2acb..cfbd59fe 100644 --- a/libs/juce/source/modules/juce_audio_basics/sources/juce_AudioSource.h +++ b/libs/juce/source/modules/juce_audio_basics/sources/juce_AudioSource.h @@ -65,7 +65,7 @@ struct JUCE_API AudioSourceChannelInfo Only the samples specified by the startSample and numSamples members of this structure should be affected by the call. - The contents of the buffer when it is passed to the the AudioSource::getNextAudioBlock() + The contents of the buffer when it is passed to the AudioSource::getNextAudioBlock() method can be treated as the input if the source is performing some kind of filter operation, but should be cleared if this is not the case - the clearActiveBufferRegion() is a handy way of doing this. diff --git a/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index c4e0afb6..262469cd 100644 --- a/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -114,7 +114,7 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() { if (availableDeviceTypes.size() == 0) { - OwnedArray types; + OwnedArray types; createAudioDeviceTypes (types); for (int i = 0; i < types.size(); ++i) @@ -127,7 +127,7 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() } } -const OwnedArray & AudioDeviceManager::getAvailableDeviceTypes() +const OwnedArray& AudioDeviceManager::getAvailableDeviceTypes() { scanDevicesIfNeeded(); return availableDeviceTypes; @@ -147,13 +147,13 @@ void AudioDeviceManager::audioDeviceListChanged() } //============================================================================== -static void addIfNotNull (OwnedArray & list, AudioIODeviceType* const device) +static void addIfNotNull (OwnedArray& list, AudioIODeviceType* const device) { if (device != nullptr) list.add (device); } -void AudioDeviceManager::createAudioDeviceTypes (OwnedArray & list) +void AudioDeviceManager::createAudioDeviceTypes (OwnedArray& list) { addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); @@ -181,7 +181,7 @@ void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType) //============================================================================== String AudioDeviceManager::initialise (const int numInputChannelsNeeded, const int numOutputChannelsNeeded, - const XmlElement* const e, + const XmlElement* const xml, const bool selectDefaultDeviceOnFailure, const String& preferredDefaultDeviceName, const AudioDeviceSetup* preferredSetupOptions) @@ -191,106 +191,127 @@ String AudioDeviceManager::initialise (const int numInputChannelsNeeded, numInputChansNeeded = numInputChannelsNeeded; numOutputChansNeeded = numOutputChannelsNeeded; - if (e != nullptr && e->hasTagName ("DEVICESETUP")) - { - lastExplicitSettings = new XmlElement (*e); + if (xml != nullptr && xml->hasTagName ("DEVICESETUP")) + return initialiseFromXML (*xml, selectDefaultDeviceOnFailure, + preferredDefaultDeviceName, preferredSetupOptions); - String error; - AudioDeviceSetup setup; + return initialiseDefault (preferredDefaultDeviceName, preferredSetupOptions); +} - if (preferredSetupOptions != nullptr) - setup = *preferredSetupOptions; +String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName, + const AudioDeviceSetup* preferredSetupOptions) +{ + AudioDeviceSetup setup; - if (e->getStringAttribute ("audioDeviceName").isNotEmpty()) - { - setup.inputDeviceName = setup.outputDeviceName - = e->getStringAttribute ("audioDeviceName"); - } - else + if (preferredSetupOptions != nullptr) + { + setup = *preferredSetupOptions; + } + else if (preferredDefaultDeviceName.isNotEmpty()) + { + for (int j = availableDeviceTypes.size(); --j >= 0;) { - setup.inputDeviceName = e->getStringAttribute ("audioInputDeviceName"); - setup.outputDeviceName = e->getStringAttribute ("audioOutputDeviceName"); - } + AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); - currentDeviceType = e->getStringAttribute ("deviceType"); + const StringArray outs (type->getDeviceNames (false)); - if (findType (currentDeviceType) == nullptr) - { - if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName)) - currentDeviceType = type->getTypeName(); - else if (availableDeviceTypes.size() > 0) - currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); + for (int i = 0; i < outs.size(); ++i) + { + if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) + { + setup.outputDeviceName = outs[i]; + break; + } + } + + const StringArray ins (type->getDeviceNames (true)); + + for (int i = 0; i < ins.size(); ++i) + { + if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) + { + setup.inputDeviceName = ins[i]; + break; + } + } } + } - setup.bufferSize = e->getIntAttribute ("audioDeviceBufferSize"); - setup.sampleRate = e->getDoubleAttribute ("audioDeviceRate"); + insertDefaultDeviceNames (setup); + return setAudioDeviceSetup (setup, false); +} - setup.inputChannels .parseString (e->getStringAttribute ("audioDeviceInChans", "11"), 2); - setup.outputChannels.parseString (e->getStringAttribute ("audioDeviceOutChans", "11"), 2); +String AudioDeviceManager::initialiseFromXML (const XmlElement& xml, + const bool selectDefaultDeviceOnFailure, + const String& preferredDefaultDeviceName, + const AudioDeviceSetup* preferredSetupOptions) +{ + lastExplicitSettings = new XmlElement (xml); - setup.useDefaultInputChannels = ! e->hasAttribute ("audioDeviceInChans"); - setup.useDefaultOutputChannels = ! e->hasAttribute ("audioDeviceOutChans"); + String error; + AudioDeviceSetup setup; - error = setAudioDeviceSetup (setup, true); + if (preferredSetupOptions != nullptr) + setup = *preferredSetupOptions; - midiInsFromXml.clear(); - forEachXmlChildElementWithTagName (*e, c, "MIDIINPUT") - midiInsFromXml.add (c->getStringAttribute ("name")); + if (xml.getStringAttribute ("audioDeviceName").isNotEmpty()) + { + setup.inputDeviceName = setup.outputDeviceName + = xml.getStringAttribute ("audioDeviceName"); + } + else + { + setup.inputDeviceName = xml.getStringAttribute ("audioInputDeviceName"); + setup.outputDeviceName = xml.getStringAttribute ("audioOutputDeviceName"); + } - const StringArray allMidiIns (MidiInput::getDevices()); + currentDeviceType = xml.getStringAttribute ("deviceType"); - for (int i = allMidiIns.size(); --i >= 0;) - setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i])); + if (findType (currentDeviceType) == nullptr) + { + if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName)) + currentDeviceType = type->getTypeName(); + else if (availableDeviceTypes.size() > 0) + currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); + } - if (error.isNotEmpty() && selectDefaultDeviceOnFailure) - error = initialise (numInputChannelsNeeded, numOutputChannelsNeeded, 0, - false, preferredDefaultDeviceName); + setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize"); + setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate"); - setDefaultMidiOutput (e->getStringAttribute ("defaultMidiOutput")); + setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2); + setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2); - return error; - } - else - { - AudioDeviceSetup setup; + setup.useDefaultInputChannels = ! xml.hasAttribute ("audioDeviceInChans"); + setup.useDefaultOutputChannels = ! xml.hasAttribute ("audioDeviceOutChans"); - if (preferredSetupOptions != nullptr) - { - setup = *preferredSetupOptions; - } - else if (preferredDefaultDeviceName.isNotEmpty()) - { - for (int j = availableDeviceTypes.size(); --j >= 0;) - { - AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); + error = setAudioDeviceSetup (setup, true); - const StringArray outs (type->getDeviceNames (false)); + midiInsFromXml.clear(); - for (int i = 0; i < outs.size(); ++i) - { - if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) - { - setup.outputDeviceName = outs[i]; - break; - } - } + forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT") + midiInsFromXml.add (c->getStringAttribute ("name")); - const StringArray ins (type->getDeviceNames (true)); + const StringArray allMidiIns (MidiInput::getDevices()); - for (int i = 0; i < ins.size(); ++i) - { - if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) - { - setup.inputDeviceName = ins[i]; - break; - } - } - } - } + for (int i = allMidiIns.size(); --i >= 0;) + setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i])); - insertDefaultDeviceNames (setup); - return setAudioDeviceSetup (setup, false); - } + if (error.isNotEmpty() && selectDefaultDeviceOnFailure) + error = initialise (numInputChansNeeded, numOutputChansNeeded, + nullptr, false, preferredDefaultDeviceName); + + setDefaultMidiOutput (xml.getStringAttribute ("defaultMidiOutput")); + + return error; +} + +String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNeeded, + int numOutputChannelsNeeded) +{ + lastExplicitSettings = nullptr; + + return initialise (numInputChannelsNeeded, numOutputChannelsNeeded, + nullptr, false, String(), nullptr); } void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const @@ -880,7 +901,7 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) { if (defaultMidiOutputName != deviceName) { - Array oldCallbacks; + Array oldCallbacks; { const ScopedLock sl (audioCallbackLock); @@ -915,7 +936,7 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) void AudioDeviceManager::playTestSound() { { // cunningly nested to swap, unlock and delete in that order. - ScopedPointer oldSound; + ScopedPointer oldSound; { const ScopedLock sl (audioCallbackLock); diff --git a/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h b/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h index d8215932..839edc28 100644 --- a/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -191,7 +191,11 @@ public: const XmlElement* savedState, bool selectDefaultDeviceOnFailure, const String& preferredDefaultDeviceName = String(), - const AudioDeviceSetup* preferredSetupOptions = 0); + const AudioDeviceSetup* preferredSetupOptions = nullptr); + + /** Resets everything to a default device setup, clearing any stored settings. */ + String initialiseWithDefaultDevices (int numInputChannelsNeeded, + int numOutputChannelsNeeded); /** Returns some XML representing the current state of the manager. @@ -383,7 +387,7 @@ public: /** Returns a list of the types of device supported. */ - const OwnedArray & getAvailableDeviceTypes(); + const OwnedArray& getAvailableDeviceTypes(); //============================================================================== /** Creates a list of available types. @@ -394,7 +398,7 @@ public: You can override this if your app needs to do something specific, like avoid using DirectSound devices, etc. */ - virtual void createAudioDeviceTypes (OwnedArray & types); + virtual void createAudioDeviceTypes (OwnedArray& types); /** Adds a new device type to the list of types. The manager will take ownership of the object that is passed-in. @@ -446,30 +450,30 @@ public: private: //============================================================================== - OwnedArray availableDeviceTypes; - OwnedArray lastDeviceTypeConfigs; + OwnedArray availableDeviceTypes; + OwnedArray lastDeviceTypeConfigs; AudioDeviceSetup currentSetup; - ScopedPointer currentAudioDevice; - Array callbacks; + ScopedPointer currentAudioDevice; + Array callbacks; int numInputChansNeeded, numOutputChansNeeded; String currentDeviceType; BigInteger inputChannels, outputChannels; - ScopedPointer lastExplicitSettings; + ScopedPointer lastExplicitSettings; mutable bool listNeedsScanning; bool useInputNames; Atomic inputLevelMeasurementEnabledCount; double inputLevel; - ScopedPointer testSound; + ScopedPointer testSound; int testSoundPosition; AudioSampleBuffer tempBuffer; StringArray midiInsFromXml; - OwnedArray enabledMidiInputs; - Array midiCallbacks; + OwnedArray enabledMidiInputs; + Array midiCallbacks; StringArray midiCallbackDevices; String defaultMidiOutputName; - ScopedPointer defaultMidiOutput; + ScopedPointer defaultMidiOutput; CriticalSection audioCallbackLock, midiCallbackLock; double cpuUsageMs, timeToCpuScale; @@ -500,6 +504,9 @@ private: double chooseBestSampleRate (double preferred) const; int chooseBestBufferSize (int preferred) const; void insertDefaultDeviceNames (AudioDeviceSetup&) const; + String initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup*); + String initialiseFromXML (const XmlElement&, bool selectDefaultDeviceOnFailure, + const String& preferredDefaultDeviceName, const AudioDeviceSetup*); AudioIODeviceType* findType (const String& inputName, const String& outputName); AudioIODeviceType* findType (const String& typeName); diff --git a/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h b/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h index 07e08860..da32b2b4 100644 --- a/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h +++ b/libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h @@ -118,7 +118,7 @@ public: /** A class for receiving events when audio devices are inserted or removed. - You can register a AudioIODeviceType::Listener with an~AudioIODeviceType object + You can register an 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. diff --git a/libs/juce/source/modules/juce_audio_devices/juce_module_info b/libs/juce/source/modules/juce_audio_devices/juce_module_info index ed688117..d6bb87b1 100644 --- a/libs/juce/source/modules/juce_audio_devices/juce_module_info +++ b/libs/juce/source/modules/juce_audio_devices/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_devices", "name": "JUCE audio and midi I/O device classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes to play and record from audio and midi i/o devices.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h b/libs/juce/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h index 435e30c4..4dc70b9b 100644 --- a/libs/juce/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h +++ b/libs/juce/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h @@ -123,6 +123,14 @@ private: { if (pendingBytes > 0 && *d >= 0x80) { + if (*d == 0xf7) + { + *dest++ = *d++; + ++pendingBytes; + --numBytes; + break; + } + if (*d >= 0xfa || *d == 0xf8) { callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); @@ -131,11 +139,15 @@ private: } else { - if (*d == 0xf7) + pendingBytes = 0; + int used = 0; + const MidiMessage m (d, numBytes, used, 0, time); + + if (used > 0) { - *dest++ = *d++; - pendingBytes++; - --numBytes; + callback.handleIncomingMidiMessage (input, m); + numBytes -= used; + d += used; } break; @@ -144,7 +156,7 @@ private: else { *dest++ = *d++; - pendingBytes++; + ++pendingBytes; --numBytes; } } diff --git a/libs/juce/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/libs/juce/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp index 2e57d105..38ad64df 100644 --- a/libs/juce/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp +++ b/libs/juce/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp @@ -502,13 +502,9 @@ public: outputNames.clear(); outputIds.clear(); - if (juce_libjackHandle == nullptr) - { - juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); - - if (juce_libjackHandle == nullptr) - return; - } + if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so.0", RTLD_LAZY); + if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); + if (juce_libjackHandle == nullptr) return; jack_status_t status; diff --git a/libs/juce/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp b/libs/juce/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp index 8f8dd17b..fd5e60c5 100644 --- a/libs/juce/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp +++ b/libs/juce/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp @@ -478,6 +478,12 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) } else { - midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()); + for (int i = 0; i < 50; ++i) + { + if (midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()) != MIDIERR_NOTREADY) + break; + + Sleep (1); + } } } diff --git a/libs/juce/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h b/libs/juce/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h index 01bc16f8..fb9e6a03 100644 --- a/libs/juce/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h +++ b/libs/juce/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h @@ -43,7 +43,7 @@ class JUCE_API AudioSubsectionReader : public AudioFormatReader { public: //============================================================================== - /** Creates a AudioSubsectionReader for a given data source. + /** Creates an AudioSubsectionReader for a given data source. @param sourceReader the source reader from which we'll be taking data @param subsectionStartSample the sample within the source reader which will be diff --git a/libs/juce/source/modules/juce_audio_formats/juce_module_info b/libs/juce/source/modules/juce_audio_formats/juce_module_info index ff68a835..c76f190d 100644 --- a/libs/juce/source/modules/juce_audio_formats/juce_module_info +++ b/libs/juce/source/modules/juce_audio_formats/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_formats", "name": "JUCE audio file format codecs", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes for reading and writing various audio file formats.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp b/libs/juce/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp index b4c696c7..5564cd7e 100644 --- a/libs/juce/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp +++ b/libs/juce/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp @@ -124,8 +124,7 @@ struct AAXClasses case 6: return AAX_eStemFormat_5_1; case 7: return AAX_eStemFormat_7_0_DTS; case 8: return AAX_eStemFormat_7_1_DTS; - - default: jassertfalse; break; // hmm - not a valid number of chans.. + default: jassertfalse; break; } return AAX_eStemFormat_None; @@ -139,17 +138,45 @@ struct AAXClasses case AAX_eStemFormat_Mono: return 1; case AAX_eStemFormat_Stereo: return 2; case AAX_eStemFormat_LCR: return 3; + case AAX_eStemFormat_LCRS: case AAX_eStemFormat_Quad: return 4; case AAX_eStemFormat_5_0: return 5; - case AAX_eStemFormat_5_1: return 6; + case AAX_eStemFormat_5_1: + case AAX_eStemFormat_6_0: return 6; + case AAX_eStemFormat_6_1: + case AAX_eStemFormat_7_0_SDDS: case AAX_eStemFormat_7_0_DTS: return 7; + case AAX_eStemFormat_7_1_SDDS: case AAX_eStemFormat_7_1_DTS: return 8; - default: jassertfalse; break; // hmm - not a valid number of chans.. + default: jassertfalse; break; } return 0; } + static const char* getSpeakerArrangementString (AAX_EStemFormat format) noexcept + { + switch (format) + { + case AAX_eStemFormat_Mono: return "M"; + case AAX_eStemFormat_Stereo: return "L R"; + case AAX_eStemFormat_LCR: return "L C R"; + case AAX_eStemFormat_LCRS: return "L C R S"; + case AAX_eStemFormat_Quad: return "L R Ls Rs"; + case AAX_eStemFormat_5_0: return "L C R Ls Rs"; + case AAX_eStemFormat_5_1: return "L C R Ls Rs LFE"; + case AAX_eStemFormat_6_0: return "L C R Ls Cs Rs"; + case AAX_eStemFormat_6_1: return "L C R Ls Cs Rs LFE"; + case AAX_eStemFormat_7_0_SDDS: return "L Lc C Rc R Ls Rs"; + case AAX_eStemFormat_7_1_SDDS: return "L Lc C Rc R Ls Rs LFE"; + case AAX_eStemFormat_7_0_DTS: return "L C R Lss Rss Lsr Rsr"; + case AAX_eStemFormat_7_1_DTS: return "L C R Lss Rss Lsr Rsr LFE"; + default: break; + } + + return nullptr; + } + //============================================================================== struct JUCELibraryRefCount { @@ -652,6 +679,7 @@ struct AAXClasses void audioProcessorChanged (AudioProcessor* processor) override { + ++mNumPlugInChanges; check (Controller()->SetSignalLatency (processor->getLatencySamples())); } @@ -855,6 +883,9 @@ struct AAXClasses AudioProcessor& audioProcessor = getPluginInstance(); + audioProcessor.setSpeakerArrangement (getSpeakerArrangementString (inputStemFormat), + getSpeakerArrangementString (outputStemFormat)); + audioProcessor.setPlayConfigDetails (numberOfInputChannels, numberOfOutputChannels, sampleRate, lastBufferSize); audioProcessor.prepareToPlay (sampleRate, lastBufferSize); @@ -931,6 +962,13 @@ struct AAXClasses // This value needs to match the RTAS wrapper's Type ID, so that // the host knows that the RTAS/AAX plugins are equivalent. properties->AddProperty (AAX_eProperty_PlugInID_Native, 'jcaa' + channelConfigIndex); + properties->AddProperty (AAX_eProperty_PlugInID_AudioSuite, 'jyaa' + channelConfigIndex); + + #if JucePlugin_AAXDisableMultiMono + properties->AddProperty (AAX_eProperty_Constraint_MultiMonoSupport, false); + #else + properties->AddProperty (AAX_eProperty_Constraint_MultiMonoSupport, true); + #endif check (desc.AddProcessProc_Native (algorithmProcessCallback, properties)); } diff --git a/libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp b/libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp index 10dcb686..40ce8d18 100644 --- a/libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp +++ b/libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp @@ -203,10 +203,8 @@ public: { public: //============================================================================== - JuceCustomUIView (AudioProcessor* const filter_, - JucePlugInProcess* const process_) - : filter (filter_), - process (process_) + JuceCustomUIView (AudioProcessor* ap, JucePlugInProcess* p) + : filter (ap), process (p) { // setting the size in here crashes PT for some reason, so keep it simple.. } @@ -237,7 +235,7 @@ public: r.bottom = editorComp->getHeight(); SetRect (&r); - if ((oldRect.right != r.right) || (oldRect.bottom != r.bottom)) + if (oldRect.right != r.right || oldRect.bottom != r.bottom) startTimer (50); } } @@ -269,8 +267,6 @@ public: #endif wrapper = nullptr; wrapper = new EditorCompWrapper (hostWindow, editorComp, this); - - process->touchAllParameters(); } } else @@ -774,23 +770,13 @@ protected: // xxx is there an RTAS equivalent? } - void touchAllParameters() - { - for (int i = 0; i < juceFilter->getNumParameters(); ++i) - { - audioProcessorParameterChangeGestureBegin (0, i); - audioProcessorParameterChanged (0, i, juceFilter->getParameter (i)); - audioProcessorParameterChangeGestureEnd (0, i); - } - } - public: // Need to use an intermediate class here rather than inheriting from AsyncUpdater, so that it can // be deleted before shutting down juce in our destructor. class InternalAsyncUpdater : public AsyncUpdater { public: - InternalAsyncUpdater (JucePlugInProcess& owner_) : owner (owner_) {} + InternalAsyncUpdater (JucePlugInProcess& p) : owner (p) {} void handleAsyncUpdate() { owner.handleAsyncUpdate(); } private: @@ -927,29 +913,32 @@ public: for (int i = 0; i < numConfigs; ++i) { - CEffectType* const type - = new CEffectTypeRTAS ('jcaa' + i, - JucePlugin_RTASProductId, - JucePlugin_RTASCategory); + if (channelConfigs[i][0] <= 8 && channelConfigs[i][1] <= 8) + { + CEffectType* const type + = new CEffectTypeRTAS ('jcaa' + i, + JucePlugin_RTASProductId, + JucePlugin_RTASCategory); - type->DefineTypeNames (createRTASName().toRawUTF8()); - type->DefineSampleRateSupport (eSupports48kAnd96kAnd192k); + type->DefineTypeNames (createRTASName().toRawUTF8()); + type->DefineSampleRateSupport (eSupports48kAnd96kAnd192k); - type->DefineStemFormats (getFormatForChans (channelConfigs [i][0] != 0 ? channelConfigs [i][0] : channelConfigs [i][1]), - getFormatForChans (channelConfigs [i][1] != 0 ? channelConfigs [i][1] : channelConfigs [i][0])); + type->DefineStemFormats (getFormatForChans (channelConfigs [i][0] != 0 ? channelConfigs [i][0] : channelConfigs [i][1]), + getFormatForChans (channelConfigs [i][1] != 0 ? channelConfigs [i][1] : channelConfigs [i][0])); - #if ! JucePlugin_RTASDisableBypass - type->AddGestalt (pluginGestalt_CanBypass); - #endif + #if ! JucePlugin_RTASDisableBypass + type->AddGestalt (pluginGestalt_CanBypass); + #endif - #if JucePlugin_RTASDisableMultiMono - type->AddGestalt (pluginGestalt_DoesntSupportMultiMono); - #endif + #if JucePlugin_RTASDisableMultiMono + type->AddGestalt (pluginGestalt_DoesntSupportMultiMono); + #endif - type->AddGestalt (pluginGestalt_SupportsVariableQuanta); - type->AttachEffectProcessCreator (createNewProcess); + type->AddGestalt (pluginGestalt_SupportsVariableQuanta); + type->AttachEffectProcessCreator (createNewProcess); - AddEffectType (type); + AddEffectType (type); + } } } diff --git a/libs/juce/source/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h b/libs/juce/source/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h index 6dfcf87c..00976eff 100644 --- a/libs/juce/source/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h +++ b/libs/juce/source/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h @@ -105,7 +105,7 @@ public: const int y = settings->getIntValue ("windowY", -100); if (x != -100 && y != -100) - setBoundsConstrained (Rectangle (x, y, getWidth(), getHeight())); + setBoundsConstrained (juce::Rectangle (x, y, getWidth(), getHeight())); else centreWithSize (getWidth(), getHeight()); } diff --git a/libs/juce/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/libs/juce/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index 82c998e8..01381e05 100644 --- a/libs/juce/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/libs/juce/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -176,7 +176,9 @@ public: toString128 (info.title, p.getParameterName (index)); toString128 (info.shortTitle, p.getParameterName (index, 8)); toString128 (info.units, p.getParameterLabel (index)); - info.stepCount = (Steinberg::int32) p.getParameterNumSteps (index); + + const int numSteps = p.getParameterNumSteps (index); + info.stepCount = (Steinberg::int32) (numSteps > 0 && numSteps < 0x7fffffff ? numSteps - 1 : 0); info.defaultNormalizedValue = p.getParameterDefaultValue (index); info.unitId = Vst::kRootUnitId; info.flags = p.isParameterAutomatable (index) ? Vst::ParameterInfo::kCanAutomate : 0; @@ -752,78 +754,72 @@ public: tresult PLUGIN_API setIoMode (Vst::IoMode) override { return kNotImplemented; } tresult PLUGIN_API getRoutingInfo (Vst::RoutingInfo&, Vst::RoutingInfo&) override { return kNotImplemented; } - tresult PLUGIN_API setState (IBStream* state) override + bool readFromMemoryStream (IBStream* state) const { - if (state != nullptr) + FUnknownPtr s (state); + + if (s != nullptr + && s->getData() != nullptr + && s->getSize() > 0 + && s->getSize() < 1024 * 1024 * 100) // (some hosts seem to return junk for the size) { - // Reset to the beginning of the stream: - if (state->seek (0, IBStream::kIBSeekSet, nullptr) != kResultTrue) - return kResultFalse; + // Adobe Audition CS6 hack to avoid trying to use corrupted streams: + if (getHostType().isAdobeAudition()) + if (s->getSize() >= 5 && memcmp (s->getData(), "VC2!E", 5) == 0) + return kResultFalse; - Steinberg::int64 end = -1; + pluginInstance->setStateInformation (s->getData(), (int) s->getSize()); + return true; + } - if (end < 0) - { - FUnknownPtr s (state); + return false; + } - if (s != nullptr) - s->getStreamSize (end); - } + bool readFromUnknownStream (IBStream* state) const + { + MemoryOutputStream allData; + + { + const size_t bytesPerBlock = 4096; + HeapBlock buffer (bytesPerBlock); - if (end < 0) + for (;;) { - FUnknownPtr s (state); + int32 bytesRead = 0; - if (s != nullptr) + if (state->read (buffer, (int32) bytesPerBlock, &bytesRead) == kResultTrue && bytesRead > 0) { - if (getHostType().isAdobeAudition()) - { - // Adobe Audition CS6 hack to avoid trying to use corrupted streams: - bool failed = true; - - if (const char* const data = s->getData()) - { - if (s->getSize() >= 5 && data[0] != 'V' && data[1] != 'C' - && data[2] != '2' && data[3] != '!' && data[4] != 'E') - { - failed = false; - } - } - else - { - jassertfalse; - } - - if (failed) - return kResultFalse; - } - - end = (Steinberg::int64) s->getSize(); + allData.write (buffer, bytesRead); + continue; } + + break; } + } - if (end <= 0) - return kResultFalse; + const size_t dataSize = allData.getDataSize(); - // Try reading the data, and setting the plugin state: - Steinberg::int32 numBytes = (Steinberg::int32) jmin ((Steinberg::int64) std::numeric_limits::max(), end); + if (dataSize > 0 && dataSize < 0x7fffffff) + { + pluginInstance->setStateInformation (allData.getData(), (int) dataSize); + return true; + } - Array buff; - buff.ensureStorageAllocated ((int) numBytes); - void* buffer = buff.getRawDataPointer(); + return false; + } - if (state->read (buffer, numBytes, &numBytes) == kResultTrue - && buffer != nullptr - && numBytes > 0) - { - pluginInstance->setStateInformation (buffer, (int) numBytes); - return kResultTrue; - } + tresult PLUGIN_API setState (IBStream* state) override + { + if (state == nullptr) + return kInvalidArgument; - return kResultFalse; - } + FUnknownPtr stateRefHolder (state); // just in case the caller hasn't properly ref-counted the stream object - return kInvalidArgument; + if (state->seek (0, IBStream::kIBSeekSet, nullptr) == kResultTrue) + if (readFromMemoryStream (state) || readFromUnknownStream (state)) + return kResultTrue; + + return kResultFalse; } tresult PLUGIN_API getState (IBStream* state) override @@ -1084,7 +1080,7 @@ public: tresult PLUGIN_API process (Vst::ProcessData& data) override { - if (pluginInstance == nullptr || processContext.sampleRate <= 0.0) + if (pluginInstance == nullptr) return kResultFalse; if (data.processContext != nullptr) diff --git a/libs/juce/source/modules/juce_audio_plugin_client/juce_module_info b/libs/juce/source/modules/juce_audio_plugin_client/juce_module_info index 7df78b26..9fa27586 100644 --- a/libs/juce/source/modules/juce_audio_plugin_client/juce_module_info +++ b/libs/juce/source/modules/juce_audio_plugin_client/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_plugin_client", "name": "JUCE audio plugin wrapper classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes for building VST, VST3, RTAS, AAX and AU plugins.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3Common.h b/libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3Common.h index 8b4a4ad6..2c575f31 100644 --- a/libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -27,11 +27,11 @@ //============================================================================== #define JUCE_DECLARE_VST3_COM_REF_METHODS \ - Steinberg::uint32 JUCE_CALLTYPE addRef() override { return (Steinberg::uint32) ++refCount; } \ - Steinberg::uint32 JUCE_CALLTYPE release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; } + Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \ + Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; } #define JUCE_DECLARE_VST3_COM_QUERY_METHODS \ - Steinberg::tresult PLUGIN_API JUCE_CALLTYPE queryInterface (const Steinberg::TUID, void** obj) override \ + Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \ { \ jassertfalse; \ *obj = nullptr; \ @@ -51,6 +51,14 @@ static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexc return Steinberg::kResultOk; \ } +#define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(CommonClassType, SourceClassType) \ + if (doUIDsMatch (iid, CommonClassType::iid)) \ + { \ + addRef(); \ + *obj = (CommonClassType*) static_cast (this); \ + return Steinberg::kResultOk; \ + } + //============================================================================== static juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); } static juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); } @@ -70,19 +78,15 @@ static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept } #if JUCE_WINDOWS -static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; + static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; #else -static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; + static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; #endif //============================================================================== -/** The equivalent numChannels and speaker arrangements should always - match between this function and fillWithCorrespondingSpeakerArrangements(). - - There can only be 1 arrangement per channel count. (i.e.: 4 channels == k31Cine OR k40Cine) - - @see fillWithCorrespondingSpeakerArrangements +/** For the sake of simplicity, there can only be 1 arrangement type per channel count. + i.e.: 4 channels == k31Cine OR k40Cine */ static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept { @@ -116,53 +120,6 @@ static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numC return (Steinberg::Vst::SpeakerArrangement) bi.toInt64(); } -/** The equivalent numChannels and speaker arrangements should always - match between this function and getArrangementForNumChannels(). - - There can only be 1 arrangement per channel count. (i.e.: 4 channels == k31Cine OR k40Cine) - - @see getArrangementForNumChannels -*/ -static void fillWithCorrespondingSpeakerArrangements (Array& destination, - int numChannels) -{ - using namespace Steinberg::Vst::SpeakerArr; - - destination.clearQuick(); - - if (numChannels <= 0) - { - destination.add (kEmpty); - return; - } - - // The order of the arrangement checks must be descending, since most plugins test for - /// the first arrangement to match their number of specified channels. - - if (numChannels > 24) - { - juce::BigInteger bi; - bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true); - destination.add ((Steinberg::Vst::SpeakerArrangement) bi.toInt64()); - } - - if (numChannels >= 24) destination.add ((Steinberg::Vst::SpeakerArrangement) 1929904127); // k222 - if (numChannels >= 14) destination.add (k131); - if (numChannels >= 13) destination.add (k130); - if (numChannels >= 12) destination.add (k111); - if (numChannels >= 11) destination.add (k101); - if (numChannels >= 10) destination.add (k91); - if (numChannels >= 9) destination.add (k90); - if (numChannels >= 8) destination.add (k71CineFullFront); - if (numChannels >= 7) destination.add (k61Cine); - if (numChannels >= 6) destination.add (k51); - if (numChannels >= 5) destination.add (k50); - if (numChannels >= 4) destination.add (k31Cine); - if (numChannels >= 3) destination.add (k30Cine); - if (numChannels >= 2) destination.add (kStereo); - if (numChannels >= 1) destination.add (kMono); -} - //============================================================================== template class ComSmartPtr diff --git a/libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 55361fc2..121d1605 100644 --- a/libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -98,7 +98,7 @@ static void fillDescriptionWith (PluginDescription& description, ObjectType& obj description.version = toString (object.version).trim(); description.category = toString (object.subCategories).trim(); - if (description.manufacturerName.isEmpty()) + if (description.manufacturerName.trim().isEmpty()) description.manufacturerName = toString (object.vendor).trim(); } @@ -459,13 +459,13 @@ public: { *obj = nullptr; - if (! doIdsMatch (cid, iid)) + if (! doUIDsMatch (cid, iid)) { jassertfalse; return kInvalidArgument; } - if (doIdsMatch (cid, Vst::IMessage::iid) && doIdsMatch (iid, Vst::IMessage::iid)) + if (doUIDsMatch (cid, Vst::IMessage::iid) && doUIDsMatch (iid, Vst::IMessage::iid)) { ComSmartPtr m (new Message (*this, attributeList)); messageQueue.add (m); @@ -473,7 +473,7 @@ public: *obj = m; return kResultOk; } - else if (doIdsMatch (cid, Vst::IAttributeList::iid) && doIdsMatch (iid, Vst::IAttributeList::iid)) + else if (doUIDsMatch (cid, Vst::IAttributeList::iid) && doUIDsMatch (iid, Vst::IAttributeList::iid)) { ComSmartPtr l (new AttributeList (this)); l->addRef(); @@ -526,7 +526,7 @@ public: //============================================================================== tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override { - if (doIdsMatch (iid, Vst::IAttributeList::iid)) + if (doUIDsMatch (iid, Vst::IAttributeList::iid)) { *obj = attributeList.get(); return kResultOk; @@ -539,6 +539,7 @@ public: TEST_FOR_AND_RETURN_IF_VALID (Vst::IHostApplication) TEST_FOR_AND_RETURN_IF_VALID (Vst::IParamValueQueue) TEST_FOR_AND_RETURN_IF_VALID (Vst::IUnitHandler) + TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (FUnknown, Vst::IComponentHandler) *obj = nullptr; return kNotImplemented; @@ -550,12 +551,6 @@ private: String appName; VST3PluginInstance* owner; - //============================================================================== - static bool doIdsMatch (const TUID a, const TUID b) noexcept - { - return std::memcmp (a, b, sizeof (TUID)) == 0; - } - //============================================================================== class Message : public Vst::IMessage { @@ -1206,7 +1201,7 @@ public: setOpaque (true); setVisible (true); - view->setFrame (this); + warnOnFailure (view->setFrame (this)); ViewRect rect; warnOnFailure (view->getSize (&rect)); @@ -1215,7 +1210,7 @@ public: ~VST3PluginWindow() { - view->removed(); + warnOnFailure (view->removed()); getAudioProcessor()->editorBeingDeleted (this); #if JUCE_MAC @@ -1273,7 +1268,7 @@ public: } else { - view->getSize (&rect); + warnOnFailure (view->getSize (&rect)); } #if JUCE_WINDOWS @@ -1485,12 +1480,6 @@ public: { using namespace Vst; - const int numInputs = getNumInputChannels(); - const int numOutputs = getNumOutputChannels(); - - // Needed for having the same sample rate in processBlock(); some plugins need this! - setPlayConfigDetails (numInputs, numOutputs, sampleRate, estimatedSamplesPerBlock); - ProcessSetup setup; setup.symbolicSampleSize = kSample32; setup.maxSamplesPerBlock = estimatedSamplesPerBlock; @@ -1504,16 +1493,28 @@ public: editController->setComponentHandler (host); - setStateForAllBusses (true); - Array inArrangements, outArrangements; - fillWithCorrespondingSpeakerArrangements (inArrangements, numInputs); - fillWithCorrespondingSpeakerArrangements (outArrangements, numOutputs); + for (int i = 0; i < numInputAudioBusses; ++i) + inArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (true, true, i).channelCount))); + + for (int i = 0; i < numOutputAudioBusses; ++i) + outArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (false, true, i).channelCount))); warnOnFailure (processor->setBusArrangements (inArrangements.getRawDataPointer(), numInputAudioBusses, outArrangements.getRawDataPointer(), numOutputAudioBusses)); + // Update the num. busses in case the configuration has been modified by the plugin. (May affect number of channels!): + numInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); + numOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); + + // Needed for having the same sample rate in processBlock(); some plugins need this! + setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true), + getNumSingleDirectionChannelsFor (component, false, true), + sampleRate, estimatedSamplesPerBlock); + + setStateForAllBusses (true); + warnOnFailure (component->setActive (true)); warnOnFailure (processor->setProcessing (true)); } @@ -1716,7 +1717,7 @@ public: if (head != nullptr) { - ScopedPointer s (createMemoryStreamForState (*head, "IComponent")); + ComSmartPtr s (createMemoryStreamForState (*head, "IComponent")); if (s != nullptr && component != nullptr) component->setState (s); diff --git a/libs/juce/source/modules/juce_audio_processors/juce_audio_processors.cpp b/libs/juce/source/modules/juce_audio_processors/juce_audio_processors.cpp index 474198a8..78d33876 100644 --- a/libs/juce/source/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/libs/juce/source/modules/juce_audio_processors/juce_audio_processors.cpp @@ -119,15 +119,18 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone { if (NSView* parent = (NSView*) getView()) { - if (NSView* child = [[parent subviews] firstObject]) + if ([[parent subviews] count] > 0) { - NSRect f = [parent frame]; - NSSize newSize = [child frame].size; - - if (f.size.width != newSize.width || f.size.height != newSize.height) + if (NSView* child = [[parent subviews] objectAtIndex: 0]) { - f.size = newSize; - [parent setFrame: f]; + NSRect f = [parent frame]; + NSSize newSize = [child frame].size; + + if (f.size.width != newSize.width || f.size.height != newSize.height) + { + f.size = newSize; + [parent setFrame: f]; + } } } } diff --git a/libs/juce/source/modules/juce_audio_processors/juce_module_info b/libs/juce/source/modules/juce_audio_processors/juce_module_info index 68c07b32..41b6d0e8 100644 --- a/libs/juce/source/modules/juce_audio_processors/juce_module_info +++ b/libs/juce/source/modules/juce_audio_processors/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_processors", "name": "JUCE audio plugin hosting classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes for loading and playing VST, AU, or internally-generated audio processors.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_audio_processors/processors/juce_PluginDescription.h b/libs/juce/source/modules/juce_audio_processors/processors/juce_PluginDescription.h index 2d1c9efd..6361775d 100644 --- a/libs/juce/source/modules/juce_audio_processors/processors/juce_PluginDescription.h +++ b/libs/juce/source/modules/juce_audio_processors/processors/juce_PluginDescription.h @@ -102,7 +102,7 @@ public: /** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */ bool hasSharedContainer; - /** Returns true if the two descriptions refer the the same plug-in. + /** Returns true if the two descriptions refer to the same plug-in. This isn't quite as simple as them just having the same file (because of shell plug-ins). diff --git a/libs/juce/source/modules/juce_audio_utils/juce_module_info b/libs/juce/source/modules/juce_audio_utils/juce_module_info index 0aef3df4..e1a020e2 100644 --- a/libs/juce/source/modules/juce_audio_utils/juce_module_info +++ b/libs/juce/source/modules/juce_audio_utils/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_utils", "name": "JUCE extra audio utility classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes for audio-related GUI and miscellaneous tasks.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_core/containers/juce_NamedValueSet.h b/libs/juce/source/modules/juce_core/containers/juce_NamedValueSet.h index 6043e2ab..1ecc7948 100644 --- a/libs/juce/source/modules/juce_core/containers/juce_NamedValueSet.h +++ b/libs/juce/source/modules/juce_core/containers/juce_NamedValueSet.h @@ -76,14 +76,14 @@ public: /** Changes or adds a named value. @returns true if a value was changed or added; false if the - value was already set the the value passed-in. + value was already set the value passed-in. */ bool set (Identifier name, const var& newValue); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS /** Changes or adds a named value. @returns true if a value was changed or added; false if the - value was already set the the value passed-in. + value was already set the value passed-in. */ bool set (Identifier name, var&& newValue); #endif diff --git a/libs/juce/source/modules/juce_core/juce_core.cpp b/libs/juce/source/modules/juce_core/juce_core.cpp index be6b6ebf..c992d508 100644 --- a/libs/juce/source/modules/juce_core/juce_core.cpp +++ b/libs/juce/source/modules/juce_core/juce_core.cpp @@ -133,7 +133,6 @@ namespace juce #include "network/juce_MACAddress.cpp" #include "network/juce_NamedPipe.cpp" #include "network/juce_Socket.cpp" -#include "network/juce_URL.cpp" #include "network/juce_IPAddress.cpp" #include "streams/juce_BufferedInputStream.cpp" #include "streams/juce_FileInputSource.cpp" @@ -219,5 +218,6 @@ namespace juce #include "threads/juce_ChildProcess.cpp" #include "threads/juce_HighResolutionTimer.cpp" +#include "network/juce_URL.cpp" } diff --git a/libs/juce/source/modules/juce_core/juce_core.h b/libs/juce/source/modules/juce_core/juce_core.h index 953c6adb..557c529e 100644 --- a/libs/juce/source/modules/juce_core/juce_core.h +++ b/libs/juce/source/modules/juce_core/juce_core.h @@ -64,7 +64,7 @@ //============================================================================= /** Config: JUCE_LOG_ASSERTIONS - If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog() + If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog() to write a message when an assertion happens. Enabling it will also leave this turned on in release builds. When it's disabled, diff --git a/libs/juce/source/modules/juce_core/juce_module_info b/libs/juce/source/modules/juce_core/juce_module_info index 41316ea2..798ca98b 100644 --- a/libs/juce/source/modules/juce_core/juce_module_info +++ b/libs/juce/source/modules/juce_core/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_core", "name": "JUCE core classes", - "version": "3.0.2", + "version": "3.0.4", "description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.", "website": "http://www.juce.com/juce", "license": "ISC Permissive", diff --git a/libs/juce/source/modules/juce_core/native/java/JuceAppActivity.java b/libs/juce/source/modules/juce_core/native/java/JuceAppActivity.java index c7ba1789..418b032a 100644 --- a/libs/juce/source/modules/juce_core/native/java/JuceAppActivity.java +++ b/libs/juce/source/modules/juce_core/native/java/JuceAppActivity.java @@ -589,10 +589,34 @@ public final class JuceAppActivity extends Activity //============================================================================== public static class HTTPStream { - public HTTPStream (HttpURLConnection connection_) throws IOException + public HTTPStream (HttpURLConnection connection_, + int[] statusCode, StringBuffer responseHeaders) throws IOException { connection = connection_; - inputStream = new BufferedInputStream (connection.getInputStream()); + + try + { + inputStream = new BufferedInputStream (connection.getInputStream()); + } + catch (IOException e) + { + if (connection.getResponseCode() < org.apache.http.HttpStatus.SC_BAD_REQUEST) + throw e; + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + if (statusCode[0] >= org.apache.http.HttpStatus.SC_BAD_REQUEST) + inputStream = connection.getErrorStream(); + else + inputStream = connection.getInputStream(); + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() @@ -634,30 +658,31 @@ public final class JuceAppActivity extends Activity private long position; } - public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, - String headers, int timeOutMs, - java.lang.StringBuffer responseHeaders) + public static final HTTPStream createHTTPStream (String address, + boolean isPost, byte[] postData, String headers, + int timeOutMs, int[] statusCode, + StringBuffer responseHeaders) { try { - HttpURLConnection connection = (HttpURLConnection) (new URL (address).openConnection()); - + HttpURLConnection connection = (HttpURLConnection) (new URL(address) + .openConnection()); if (connection != null) { try { if (isPost) { - connection.setConnectTimeout (timeOutMs); - connection.setDoOutput (true); - connection.setChunkedStreamingMode (0); - + connection.setRequestMethod("POST"); + connection.setConnectTimeout(timeOutMs); + connection.setDoOutput(true); + connection.setChunkedStreamingMode(0); OutputStream out = connection.getOutputStream(); - out.write (postData); + out.write(postData); out.flush(); } - return new HTTPStream (connection); + return new HTTPStream (connection, statusCode, responseHeaders); } catch (Throwable e) { @@ -665,8 +690,7 @@ public final class JuceAppActivity extends Activity } } } - catch (Throwable e) - {} + catch (Throwable e) {} return null; } diff --git a/libs/juce/source/modules/juce_core/native/juce_android_JNIHelpers.h b/libs/juce/source/modules/juce_core/native/juce_android_JNIHelpers.h index 92f2bbd4..044679e7 100644 --- a/libs/juce/source/modules/juce_core/native/juce_android_JNIHelpers.h +++ b/libs/juce/source/modules/juce_core/native/juce_android_JNIHelpers.h @@ -251,7 +251,7 @@ extern AndroidSystem android; class ThreadLocalJNIEnvHolder { public: - ThreadLocalJNIEnvHolder() + ThreadLocalJNIEnvHolder() noexcept : jvm (nullptr) { zeromem (threads, sizeof (threads)); @@ -269,18 +269,19 @@ public: addEnv (env); } - JNIEnv* attach() + JNIEnv* attach() noexcept { - JNIEnv* env = nullptr; - jvm->AttachCurrentThread (&env, nullptr); - - if (env != nullptr) - addEnv (env); + if (JNIEnv* env = attachToCurrentThread()) + { + SpinLock::ScopedLockType sl (addRemoveLock); + return addEnv (env); + } - return env; + jassertfalse; + return nullptr; } - void detach() + void detach() noexcept { jvm->DetachCurrentThread(); @@ -294,54 +295,61 @@ public: JNIEnv* getOrAttach() noexcept { - JNIEnv* env = get(); + if (JNIEnv* env = get()) + return env; - if (env == nullptr) - env = attach(); + SpinLock::ScopedLockType sl (addRemoveLock); - jassert (env != nullptr); - return env; - } + if (JNIEnv* env = get()) + return env; - JNIEnv* get() const noexcept - { - const pthread_t thisThread = pthread_self(); - - for (int i = 0; i < maxThreads; ++i) - if (threads[i] == thisThread) - return envs[i]; + if (JNIEnv* env = attachToCurrentThread()) + return addEnv (env); return nullptr; } - enum { maxThreads = 32 }; - private: JavaVM* jvm; + enum { maxThreads = 32 }; pthread_t threads [maxThreads]; JNIEnv* envs [maxThreads]; SpinLock addRemoveLock; - void addEnv (JNIEnv* env) + JNIEnv* addEnv (JNIEnv* env) noexcept { - SpinLock::ScopedLockType sl (addRemoveLock); + const pthread_t thisThread = pthread_self(); - if (get() == nullptr) + for (int i = 0; i < maxThreads; ++i) { - const pthread_t thisThread = pthread_self(); - - for (int i = 0; i < maxThreads; ++i) + if (threads[i] == 0) { - if (threads[i] == 0) - { - envs[i] = env; - threads[i] = thisThread; - return; - } + envs[i] = env; + threads[i] = thisThread; + return env; } } jassertfalse; // too many threads! + return nullptr; + } + + JNIEnv* get() const noexcept + { + const pthread_t thisThread = pthread_self(); + + for (int i = 0; i < maxThreads; ++i) + if (threads[i] == thisThread) + return envs[i]; + + return nullptr; + } + + JNIEnv* attachToCurrentThread() + { + JNIEnv* env = nullptr; + jvm->AttachCurrentThread (&env, nullptr); + return env; } }; @@ -357,7 +365,7 @@ extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ METHOD (renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ - STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ + STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ diff --git a/libs/juce/source/modules/juce_core/native/juce_android_Network.cpp b/libs/juce/source/modules/juce_core/native/juce_android_Network.cpp index 40478d4b..1b86deee 100644 --- a/libs/juce/source/modules/juce_core/native/juce_android_Network.cpp +++ b/libs/juce/source/modules/juce_core/native/juce_android_Network.cpp @@ -71,6 +71,7 @@ public: WebInputStream (String address, bool isPost, const MemoryBlock& postData, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers, int timeOutMs, StringPairArray* responseHeaders) + : statusCode (0) { if (! address.contains ("://")) address = "http://" + address; @@ -91,6 +92,9 @@ public: // thread. You'll need to move your networking code to a background thread to keep it happy.. jassert (Thread::getCurrentThread() != nullptr); + jintArray statusCodeArray = env->NewIntArray (1); + jassert (statusCodeArray != 0); + stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity, JuceAppActivity.createHTTPStream, javaString (address).get(), @@ -98,8 +102,14 @@ public: postDataArray, javaString (headers).get(), (jint) timeOutMs, + statusCodeArray, responseHeaderBuffer.get())); + jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); + statusCode = statusCodeElements[0]; + env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); + env->DeleteLocalRef (statusCodeArray); + if (postDataArray != 0) env->DeleteLocalRef (postDataArray); @@ -135,6 +145,8 @@ public: } //============================================================================== + bool isError() const { return stream == nullptr; } + bool isExhausted() override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); } int64 getTotalLength() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; } int64 getPosition() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; } @@ -162,18 +174,8 @@ public: //============================================================================== GlobalRef stream; + int statusCode; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; - -InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, const int timeOutMs, StringPairArray* responseHeaders) -{ - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); - - return wi->stream != 0 ? wi.release() : nullptr; -} diff --git a/libs/juce/source/modules/juce_core/native/juce_linux_Network.cpp b/libs/juce/source/modules/juce_core/native/juce_linux_Network.cpp index a052b2c2..36aeeb80 100644 --- a/libs/juce/source/modules/juce_core/native/juce_linux_Network.cpp +++ b/libs/juce/source/modules/juce_core/native/juce_linux_Network.cpp @@ -72,11 +72,11 @@ public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) - : socketHandle (-1), levelsOfRedirection (0), + : statusCode (0), socketHandle (-1), levelsOfRedirection (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { - createConnection (progressCallback, progressCallbackContext); + statusCode = createConnection (progressCallback, progressCallbackContext); if (responseHeaders != nullptr && ! isError()) { @@ -144,7 +144,7 @@ public: { closeSocket(); position = 0; - createConnection (0, 0); + statusCode = createConnection (0, 0); } skipNextBytes (wantedPos - position); @@ -154,6 +154,8 @@ public: } //============================================================================== + int statusCode; + private: int socketHandle, levelsOfRedirection; StringArray headerLines; @@ -173,7 +175,7 @@ private: levelsOfRedirection = 0; } - void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) + int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { closeSocket(); @@ -189,7 +191,7 @@ private: String hostName, hostPath; int hostPort; if (! decomposeURL (address, hostName, hostPath, hostPort)) - return; + return 0; String serverName, proxyName, proxyPath; int proxyPort = 0; @@ -199,7 +201,7 @@ private: if (proxyURL.startsWithIgnoreCase ("http://")) { if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) - return; + return 0; serverName = proxyName; port = proxyPort; @@ -219,14 +221,14 @@ private: struct addrinfo* result = nullptr; if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) - return; + return 0; socketHandle = socket (result->ai_family, result->ai_socktype, 0); if (socketHandle == -1) { freeaddrinfo (result); - return; + return 0; } int receiveBufferSize = 16384; @@ -241,7 +243,7 @@ private: { closeSocket(); freeaddrinfo (result); - return; + return 0; } freeaddrinfo (result); @@ -254,7 +256,7 @@ private: progressCallback, progressCallbackContext)) { closeSocket(); - return; + return 0; } } @@ -265,15 +267,15 @@ private: { headerLines = StringArray::fromLines (responseHeader); - const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) - .substring (0, 3).getIntValue(); + const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false) + .substring (0, 3).getIntValue(); //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); String location (findHeaderItem (headerLines, "Location:")); - if (statusCode >= 300 && statusCode < 400 + if (status >= 300 && status < 400 && location.isNotEmpty() && location != address) { if (! location.startsWithIgnoreCase ("http://")) @@ -282,18 +284,18 @@ private: if (++levelsOfRedirection <= 3) { address = location; - createConnection (progressCallback, progressCallbackContext); - return; + return createConnection (progressCallback, progressCallbackContext); } } else { levelsOfRedirection = 0; - return; + return status; } } closeSocket(); + return 0; } //============================================================================== @@ -437,14 +439,3 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; - -InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, const int timeOutMs, StringPairArray* responseHeaders) -{ - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); - - return wi->isError() ? nullptr : wi.release(); -} diff --git a/libs/juce/source/modules/juce_core/native/juce_mac_Network.mm b/libs/juce/source/modules/juce_core/native/juce_mac_Network.mm index 75c9ce19..cfb75e8e 100644 --- a/libs/juce/source/modules/juce_core/native/juce_mac_Network.mm +++ b/libs/juce/source/modules/juce_core/native/juce_mac_Network.mm @@ -118,6 +118,7 @@ public: connection (nil), data ([[NSMutableData data] retain]), headers (nil), + statusCode (0), initialised (false), hasFailed (false), hasFinished (false) @@ -202,7 +203,11 @@ public: headers = nil; if ([response isKindOfClass: [NSHTTPURLResponse class]]) - headers = [[((NSHTTPURLResponse*) response) allHeaderFields] retain]; + { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response; + headers = [[httpResponse allHeaderFields] retain]; + statusCode = (int) [httpResponse statusCode]; + } } void didFailWithError (NSError* error) @@ -251,6 +256,7 @@ public: NSURLConnection* connection; NSMutableData* data; NSDictionary* headers; + int statusCode; bool initialised, hasFailed, hasFinished; private: @@ -318,7 +324,7 @@ public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) - : address (address_), headers (headers_), postData (postData_), position (0), + : statusCode (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { JUCE_AUTORELEASEPOOL @@ -327,6 +333,8 @@ public: if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil) { + statusCode = connection->statusCode; + NSEnumerator* enumerator = [connection->headers keyEnumerator]; while (NSString* key = [enumerator nextObject]) @@ -380,6 +388,8 @@ public: return true; } + int statusCode; + private: ScopedPointer connection; String address, headers; @@ -428,14 +438,3 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; - -InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, const int timeOutMs, StringPairArray* responseHeaders) -{ - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); - - return wi->isError() ? nullptr : wi.release(); -} diff --git a/libs/juce/source/modules/juce_core/native/juce_posix_SharedCode.h b/libs/juce/source/modules/juce_core/native/juce_posix_SharedCode.h index 402662a1..ca5768dd 100644 --- a/libs/juce/source/modules/juce_core/native/juce_posix_SharedCode.h +++ b/libs/juce/source/modules/juce_core/native/juce_posix_SharedCode.h @@ -996,6 +996,11 @@ public: ActiveProcess (const StringArray& arguments, int streamFlags) : childPID (0), pipeHandle (0), readHandle (0) { + // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX + // you're trying to launch the .app folder rather than the actual binary inside it?) + jassert ((! arguments[0].containsChar ('/')) + || File::getCurrentWorkingDirectory().getChildFile (arguments[0]).existsAsFile()); + int pipeHandles[2] = { 0 }; if (pipe (pipeHandles) == 0) diff --git a/libs/juce/source/modules/juce_core/native/juce_win32_Network.cpp b/libs/juce/source/modules/juce_core/native/juce_win32_Network.cpp index ef31c021..22e50279 100644 --- a/libs/juce/source/modules/juce_core/native/juce_win32_Network.cpp +++ b/libs/juce/source/modules/juce_core/native/juce_win32_Network.cpp @@ -41,41 +41,50 @@ public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) - : connection (0), request (0), + : statusCode (0), connection (0), request (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { createConnection (progressCallback, progressCallbackContext); - if (responseHeaders != nullptr && ! isError()) + if (! isError()) { - DWORD bufferSizeBytes = 4096; - - for (;;) + if (responseHeaders != nullptr) { - HeapBlock buffer ((size_t) bufferSizeBytes); + DWORD bufferSizeBytes = 4096; - if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) + for (;;) { - StringArray headersArray; - headersArray.addLines (String (reinterpret_cast (buffer.getData()))); + HeapBlock buffer ((size_t) bufferSizeBytes); - for (int i = 0; i < headersArray.size(); ++i) + if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) { - const String& header = headersArray[i]; - const String key (header.upToFirstOccurrenceOf (": ", false, false)); - const String value (header.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue ((*responseHeaders) [key]); + StringArray headersArray; + headersArray.addLines (String (reinterpret_cast (buffer.getData()))); + + for (int i = 0; i < headersArray.size(); ++i) + { + const String& header = headersArray[i]; + const String key (header.upToFirstOccurrenceOf (": ", false, false)); + const String value (header.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue ((*responseHeaders) [key]); - responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + + break; } - break; + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + break; } - - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - break; } + + DWORD status = 0; + DWORD statusSize = sizeof (status); + + if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, 0)) + statusCode = (int) status; } } @@ -145,6 +154,8 @@ public: return true; } + int statusCode; + private: //============================================================================== HINTERNET connection, request; @@ -305,17 +316,6 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; -InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, const int timeOutMs, StringPairArray* responseHeaders) -{ - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); - - return wi->isError() ? nullptr : wi.release(); -} - //============================================================================== struct GetAdaptersInfoHelper diff --git a/libs/juce/source/modules/juce_core/native/juce_win32_SystemStats.cpp b/libs/juce/source/modules/juce_core/native/juce_win32_SystemStats.cpp index 0098b46f..6bac9af0 100644 --- a/libs/juce/source/modules/juce_core/native/juce_win32_SystemStats.cpp +++ b/libs/juce/source/modules/juce_core/native/juce_win32_SystemStats.cpp @@ -45,72 +45,67 @@ void Logger::outputDebugString (const String& text) #pragma intrinsic (__cpuid) #pragma intrinsic (__rdtsc) -String SystemStats::getCpuVendor() +static void callCPUID (int result[4], int infoType) { - int info [4]; - __cpuid (info, 0); - - char v [12]; - memcpy (v, info + 1, 4); - memcpy (v + 4, info + 3, 4); - memcpy (v + 8, info + 2, 4); - - return String (v, 12); + __cpuid (result, infoType); } #else -//============================================================================== -// CPU info functions using old fashioned inline asm... - -static void juce_getCpuVendor (char* const v) +static void callCPUID (int result[4], int infoType) { - int vendor[4] = { 0 }; - #if ! JUCE_MINGW __try #endif { #if JUCE_GCC - unsigned int dummy = 0; - __asm__ ("cpuid" : "=a" (dummy), "=b" (vendor[0]), "=c" (vendor[2]),"=d" (vendor[1]) : "a" (0)); + __asm__ __volatile__ ("cpuid" : "=a" (result[0]), "=b" (result[1]), "=c" (result[2]),"=d" (result[3]) : "a" (infoType)); #else __asm { - mov eax, 0 + mov esi, result + mov eax, infoType + xor ecx, ecx cpuid - mov [vendor], ebx - mov [vendor + 4], edx - mov [vendor + 8], ecx + mov dword ptr [esi + 0], eax + mov dword ptr [esi + 4], ebx + mov dword ptr [esi + 8], ecx + mov dword ptr [esi + 12], edx } #endif } #if ! JUCE_MINGW - __except (EXCEPTION_EXECUTE_HANDLER) - { - } + __except (EXCEPTION_EXECUTE_HANDLER) {} #endif - - memcpy (v, vendor, 16); } +#endif + String SystemStats::getCpuVendor() { - char v [16]; - juce_getCpuVendor (v); - return String (v, 16); -} -#endif + int info[4] = { 0 }; + callCPUID (info, 0); + + char v [12]; + memcpy (v, info + 1, 4); + memcpy (v + 4, info + 3, 4); + memcpy (v + 8, info + 2, 4); + return String (v, 12); +} //============================================================================== void CPUInformation::initialise() noexcept { - hasMMX = IsProcessorFeaturePresent (PF_MMX_INSTRUCTIONS_AVAILABLE) != 0; - hasSSE = IsProcessorFeaturePresent (PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0; - hasSSE2 = IsProcessorFeaturePresent (PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0; - hasSSE3 = IsProcessorFeaturePresent (13 /*PF_SSE3_INSTRUCTIONS_AVAILABLE*/) != 0; - has3DNow = IsProcessorFeaturePresent (7 /*PF_AMD3D_INSTRUCTIONS_AVAILABLE*/) != 0; + int info[4] = { 0 }; + callCPUID (info, 1); + + // NB: IsProcessorFeaturePresent doesn't work on XP + hasMMX = (info[3] & (1 << 23)) != 0; + hasSSE = (info[3] & (1 << 25)) != 0; + hasSSE2 = (info[3] & (1 << 26)) != 0; + hasSSE3 = (info[2] & (1 << 0)) != 0; + has3DNow = (info[1] & (1 << 31)) != 0; SYSTEM_INFO systemInfo; GetNativeSystemInfo (&systemInfo); diff --git a/libs/juce/source/modules/juce_core/network/juce_URL.cpp b/libs/juce/source/modules/juce_core/network/juce_URL.cpp index cf9f2cf6..c957e91a 100644 --- a/libs/juce/source/modules/juce_core/network/juce_URL.cpp +++ b/libs/juce/source/modules/juce_core/network/juce_URL.cpp @@ -63,6 +63,13 @@ URL::URL (const String& u) : url (u) } } +URL::URL (const String& u, int) : url (u) {} + +URL URL::createWithoutParsing (const String& u) +{ + return URL (u, 0); +} + URL::URL (const URL& other) : url (other.url), postData (other.postData), @@ -323,7 +330,8 @@ InputStream* URL::createInputStream (const bool usePostCommand, void* const progressCallbackContext, String headers, const int timeOutMs, - StringPairArray* const responseHeaders) const + StringPairArray* const responseHeaders, + int* statusCode) const { MemoryBlock headersAndPostData; @@ -336,9 +344,15 @@ InputStream* URL::createInputStream (const bool usePostCommand, if (! headers.endsWithChar ('\n')) headers << "\r\n"; - return createNativeStream (toString (! usePostCommand), usePostCommand, headersAndPostData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders); + ScopedPointer wi (new WebInputStream (toString (! usePostCommand), + usePostCommand, headersAndPostData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); + + if (statusCode != nullptr) + *statusCode = wi->statusCode; + + return wi->isError() ? nullptr : wi.release(); } //============================================================================== diff --git a/libs/juce/source/modules/juce_core/network/juce_URL.h b/libs/juce/source/modules/juce_core/network/juce_URL.h index 28d3aac6..ce7676b2 100644 --- a/libs/juce/source/modules/juce_core/network/juce_URL.h +++ b/libs/juce/source/modules/juce_core/network/juce_URL.h @@ -44,7 +44,11 @@ public: /** Creates an empty URL. */ URL(); - /** Creates a URL from a string. */ + /** Creates a URL from a string. + This will parse any embedded parameters after a '?' character and store them + in the list (see getParameterNames etc). If you don't want this to happen, you + can use createWithoutParsing(). + */ URL (const String& url); /** Creates a copy of another URL. */ @@ -243,8 +247,10 @@ public: @param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If a negative number, it will be infinite. Otherwise it specifies a time in milliseconds. - @param responseHeaders if this is non-zero, all the (key, value) pairs received as headers + @param responseHeaders if this is non-null, all the (key, value) pairs received as headers in the response will be stored in this array + @param statusCode if this is non-null, it will get set to the http status code, if one + is known, or 0 if a code isn't available @returns an input stream that the caller must delete, or a null pointer if there was an error trying to open it. */ @@ -253,7 +259,8 @@ public: void* progressCallbackContext = nullptr, String extraHeaders = String(), int connectionTimeOutMs = 0, - StringPairArray* responseHeaders = nullptr) const; + StringPairArray* responseHeaders = nullptr, + int* statusCode = nullptr) const; //============================================================================== @@ -326,18 +333,21 @@ public: */ static String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom); + /** Returns a URL without attempting to remove any embedded parameters from the string. + This may be necessary if you need to create a request that involves both POST + parameters and parameters which are embedded in the URL address itself. + */ + static URL createWithoutParsing (const String& url); + private: //============================================================================== String url, postData; StringArray parameterNames, parameterValues; StringPairArray filesToUpload, mimeTypes; + URL (const String&, int); void addParameter (const String&, const String&); - static InputStream* createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, - void* progressCallbackContext, const String& headers, - const int timeOutMs, StringPairArray* responseHeaders); JUCE_LEAK_DETECTOR (URL) }; diff --git a/libs/juce/source/modules/juce_core/streams/juce_OutputStream.h b/libs/juce/source/modules/juce_core/streams/juce_OutputStream.h index ead633b5..de40888f 100644 --- a/libs/juce/source/modules/juce_core/streams/juce_OutputStream.h +++ b/libs/juce/source/modules/juce_core/streams/juce_OutputStream.h @@ -226,7 +226,7 @@ public: //============================================================================== /** Sets the string that will be written to the stream when the writeNewLine() method is called. - By default this will be set the the value of NewLine::getDefault(). + By default this will be set the value of NewLine::getDefault(). */ void setNewLineString (const String& newLineString); diff --git a/libs/juce/source/modules/juce_core/system/juce_StandardHeader.h b/libs/juce/source/modules/juce_core/system/juce_StandardHeader.h index a24d8f9e..f329be04 100644 --- a/libs/juce/source/modules/juce_core/system/juce_StandardHeader.h +++ b/libs/juce/source/modules/juce_core/system/juce_StandardHeader.h @@ -36,7 +36,7 @@ */ #define JUCE_MAJOR_VERSION 3 #define JUCE_MINOR_VERSION 0 -#define JUCE_BUILDNUMBER 2 +#define JUCE_BUILDNUMBER 4 /** Current Juce version number. diff --git a/libs/juce/source/modules/juce_core/threads/juce_CriticalSection.h b/libs/juce/source/modules/juce_core/threads/juce_CriticalSection.h index 7889b350..60e61dda 100644 --- a/libs/juce/source/modules/juce_core/threads/juce_CriticalSection.h +++ b/libs/juce/source/modules/juce_core/threads/juce_CriticalSection.h @@ -38,6 +38,10 @@ one of these is by using RAII in the form of a local ScopedLock object - have a look through the codebase for many examples of how to do this. + In almost all cases you'll want to declare your CriticalSection as a member variable. + Occasionally you may want to declare one as a static variable, but in that case the usual + C++ static object order-of-construction warnings should be heeded. + @see ScopedLock, ScopedTryLock, ScopedUnlock, SpinLock, ReadWriteLock, Thread, InterProcessLock */ class JUCE_API CriticalSection @@ -157,21 +161,27 @@ private: /** Automatically locks and unlocks a CriticalSection object. - Use one of these as a local variable to provide RAII-based locking of a CriticalSection. + You can use a ScopedLock as a local variable to provide RAII-based locking of a CriticalSection. e.g. @code - CriticalSection myCriticalSection; - - for (;;) + struct MyObject { - const ScopedLock myScopedLock (myCriticalSection); - // myCriticalSection is now locked + CriticalSection objectLock; + + // assuming that this example function will be called by multiple threads + void foo() + { + const ScopedLock myScopedLock (objectLock); + + // objectLock is now locked.. - ...do some stuff... + ...do some thread-safe work here... - // myCriticalSection gets unlocked here. - } + // ..and objectLock gets unlocked here, as myScopedLock goes out of + // scope at the end of the block + } + }; @endcode @see CriticalSection, ScopedUnlock @@ -189,29 +199,29 @@ typedef CriticalSection::ScopedLockType ScopedLock; e.g. @code - CriticalSection myCriticalSection; - - for (;;) + struct MyObject { - const ScopedLock myScopedLock (myCriticalSection); - // myCriticalSection is now locked - - ... do some stuff with it locked .. + CriticalSection objectLock; - while (xyz) + void foo() { - ... do some stuff with it locked .. + { + const ScopedLock myScopedLock (objectLock); - const ScopedUnlock unlocker (myCriticalSection); + // objectLock is now locked.. - // myCriticalSection is now unlocked for the remainder of this block, - // and re-locked at the end. + { + ScopedUnlock myUnlocker (objectLock); - ...do some stuff with it unlocked ... - } + // ..and now unlocked.. + } + + // ..and now locked again.. + } - // myCriticalSection gets unlocked here. - } + // ..and finally unlocked. + } + }; @endcode @see CriticalSection, ScopedLock @@ -225,26 +235,27 @@ typedef CriticalSection::ScopedUnlockType ScopedUnlock; Use one of these as a local variable to control access to a CriticalSection. e.g. @code - CriticalSection myCriticalSection; - for (;;) + struct MyObject { - const ScopedTryLock myScopedTryLock (myCriticalSection); + CriticalSection objectLock; - // Unlike using a ScopedLock, this may fail to actually get the lock, so you - // should test this with the isLocked() method before doing your thread-unsafe - // action.. - if (myScopedTryLock.isLocked()) + void foo() { - ...do some stuff... + const ScopedTryLock myScopedTryLock (objectLock); + + // Unlike using a ScopedLock, this may fail to actually get the lock, so you + // must call the isLocked() method before making any assumptions.. + if (myScopedTryLock.isLocked()) + { + ...safely do some work... + } + else + { + // If we get here, then our attempt at locking failed because another thread had already locked it.. + } } - else - { - ..our attempt at locking failed because another thread had already locked it.. - } - - // myCriticalSection gets unlocked here (if it was locked) - } + }; @endcode @see CriticalSection::tryEnter, ScopedLock, ScopedUnlock, ScopedReadLock diff --git a/libs/juce/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h b/libs/juce/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h index fd09c8cb..dc8929aa 100644 --- a/libs/juce/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h +++ b/libs/juce/source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h @@ -57,7 +57,7 @@ public: struct JUCE_API Options { /** Creates an empty Options structure. - You'll need to fill-in the data memebers appropriately before using this structure. + You'll need to fill-in the data members appropriately before using this structure. */ Options(); diff --git a/libs/juce/source/modules/juce_data_structures/juce_module_info b/libs/juce/source/modules/juce_data_structures/juce_module_info index 41bcd39b..dcf64673 100644 --- a/libs/juce/source/modules/juce_data_structures/juce_module_info +++ b/libs/juce/source/modules/juce_data_structures/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_data_structures", "name": "JUCE data model helper classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes for undo/redo management, and smart data structures.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_events/broadcasters/juce_ActionBroadcaster.h b/libs/juce/source/modules/juce_events/broadcasters/juce_ActionBroadcaster.h index ec727337..8052bed3 100644 --- a/libs/juce/source/modules/juce_events/broadcasters/juce_ActionBroadcaster.h +++ b/libs/juce/source/modules/juce_events/broadcasters/juce_ActionBroadcaster.h @@ -73,7 +73,7 @@ private: class ActionMessage; friend class ActionMessage; - SortedSet actionListeners; + SortedSet actionListeners; CriticalSection actionListenerLock; JUCE_DECLARE_NON_COPYABLE (ActionBroadcaster) diff --git a/libs/juce/source/modules/juce_events/broadcasters/juce_ActionListener.h b/libs/juce/source/modules/juce_events/broadcasters/juce_ActionListener.h index 200f6069..12257f93 100644 --- a/libs/juce/source/modules/juce_events/broadcasters/juce_ActionListener.h +++ b/libs/juce/source/modules/juce_events/broadcasters/juce_ActionListener.h @@ -28,10 +28,7 @@ //============================================================================== /** - Receives callbacks to indicate that some kind of event has occurred. - - Used by various classes, e.g. buttons when they are pressed, to tell listeners - about something that's happened. + Interface class for delivery of events that are sent by an ActionBroadcaster. @see ActionBroadcaster, ChangeListener */ diff --git a/libs/juce/source/modules/juce_events/juce_module_info b/libs/juce/source/modules/juce_events/juce_module_info index e14c5b62..17c0a6d6 100644 --- a/libs/juce/source/modules/juce_events/juce_module_info +++ b/libs/juce/source/modules/juce_events/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_events", "name": "JUCE message and event handling classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes for running an application's main event loop and sending/receiving messages, timers, etc.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_events/messages/juce_NotificationType.h b/libs/juce/source/modules/juce_events/messages/juce_NotificationType.h index 1e23d8b1..c3ecccc6 100644 --- a/libs/juce/source/modules/juce_events/messages/juce_NotificationType.h +++ b/libs/juce/source/modules/juce_events/messages/juce_NotificationType.h @@ -35,7 +35,7 @@ enum NotificationType dontSendNotification = 0, /**< No notification message should be sent. */ sendNotification = 1, /**< Requests a notification message, either synchronous or not. */ sendNotificationSync, /**< Requests a synchronous notification. */ - sendNotificationAsync, /**< Requests a asynchronous notification. */ + sendNotificationAsync, /**< Requests an asynchronous notification. */ }; diff --git a/libs/juce/source/modules/juce_graphics/geometry/juce_Point.h b/libs/juce/source/modules/juce_graphics/geometry/juce_Point.h index 3df567b7..ffe80fe2 100644 --- a/libs/juce/source/modules/juce_graphics/geometry/juce_Point.h +++ b/libs/juce/source/modules/juce_graphics/geometry/juce_Point.h @@ -151,7 +151,8 @@ public: */ FloatType getAngleToPoint (Point other) const noexcept { - return static_cast (std::atan2 (other.x - x, y - other.y)); + return static_cast (std::atan2 (static_cast (other.x - x), + static_cast (y - other.y))); } /** Returns the point that would be reached by rotating this point clockwise @@ -169,8 +170,8 @@ public: */ Point getPointOnCircumference (float radius, float angle) const noexcept { - return Point (static_cast (x + radius * std::sin (angle)), - static_cast (y - radius * std::cos (angle))); + return Point (static_cast (x + radius * std::sin (angle)), + static_cast (y - radius * std::cos (angle))); } /** Taking this point to be the centre of an ellipse, this returns a point on its circumference. @@ -180,8 +181,8 @@ public: */ Point getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept { - return Point (static_cast (x + radiusX * std::sin (angle)), - static_cast (y - radiusY * std::cos (angle))); + return Point (static_cast (x + radiusX * std::sin (angle)), + static_cast (y - radiusY * std::cos (angle))); } /** Returns the dot-product of two points (x1 * x2 + y1 * y2). */ @@ -204,13 +205,13 @@ public: //============================================================================== /** Casts this point to a Point object. */ - Point toInt() const noexcept { return Point (static_cast (x), static_cast (y)); } + Point toInt() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Casts this point to a Point object. */ - Point toFloat() const noexcept { return Point (static_cast (x), static_cast (y)); } + Point toFloat() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Casts this point to a Point object. */ - Point toDouble() const noexcept { return Point (static_cast (x), static_cast (y)); } + Point toDouble() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Returns the point as a string in the form "x, y". */ String toString() const { return String (x) + ", " + String (y); } diff --git a/libs/juce/source/modules/juce_graphics/geometry/juce_Rectangle.h b/libs/juce/source/modules/juce_graphics/geometry/juce_Rectangle.h index 1fc9bd60..6191d1aa 100644 --- a/libs/juce/source/modules/juce_graphics/geometry/juce_Rectangle.h +++ b/libs/juce/source/modules/juce_graphics/geometry/juce_Rectangle.h @@ -174,6 +174,12 @@ public: /** Changes the rectangle's height */ inline void setHeight (const ValueType newHeight) noexcept { h = newHeight; } + /** Changes the position of the rectangle's centre (leaving its size unchanged). */ + inline void setCentre (const ValueType newCentreX, const ValueType newCentreY) noexcept { pos.x = newCentreX - w / (ValueType) 2; pos.y = newCentreY - h / (ValueType) 2; } + + /** Changes the position of the rectangle's centre (leaving its size unchanged). */ + inline void setCentre (const Point newCentre) noexcept { setCentre (newCentre.x, newCentre.y); } + /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ Rectangle withX (const ValueType newX) const noexcept { return Rectangle (newX, pos.y, w, h); } @@ -189,6 +195,10 @@ public: /** Returns a rectangle whose size is the same as this one, but whose top-left position is (0, 0). */ Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } + /** Returns a rectangle with the same size as this one, but a new centre position. */ + Rectangle withCentre (const Point newCentre) const noexcept { return Rectangle (newCentre.x - w / (ValueType) 2, + newCentre.y - h / (ValueType) 2, w, h); } + /** Returns a rectangle which has the same position and height as this one, but with a different width. */ Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } diff --git a/libs/juce/source/modules/juce_graphics/images/juce_Image.cpp b/libs/juce/source/modules/juce_graphics/images/juce_Image.cpp index e3ab8d59..d9aa0c4e 100644 --- a/libs/juce/source/modules/juce_graphics/images/juce_Image.cpp +++ b/libs/juce/source/modules/juce_graphics/images/juce_Image.cpp @@ -31,6 +31,12 @@ ImagePixelData::ImagePixelData (const Image::PixelFormat format, const int w, co ImagePixelData::~ImagePixelData() { + listeners.call (&Listener::imageDataBeingDeleted, this); +} + +void ImagePixelData::sendDataChangeMessage() +{ + listeners.call (&Listener::imageDataChanged, this); } //============================================================================== @@ -69,15 +75,19 @@ public: LowLevelGraphicsContext* createLowLevelContext() override { + sendDataChangeMessage(); return new LowLevelGraphicsSoftwareRenderer (Image (this)); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { bitmap.data = imageData + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; + + if (mode != Image::BitmapData::readOnly) + sendDataChangeMessage(); } ImagePixelData* clone() override @@ -146,6 +156,9 @@ public: void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode); + + if (mode != Image::BitmapData::readOnly) + sendDataChangeMessage(); } ImagePixelData* clone() override diff --git a/libs/juce/source/modules/juce_graphics/images/juce_Image.h b/libs/juce/source/modules/juce_graphics/images/juce_Image.h index 0edab3dc..3580f9f2 100644 --- a/libs/juce/source/modules/juce_graphics/images/juce_Image.h +++ b/libs/juce/source/modules/juce_graphics/images/juce_Image.h @@ -455,6 +455,19 @@ public: typedef ReferenceCountedObjectPtr Ptr; + //============================================================================== + struct Listener + { + virtual ~Listener() {} + + virtual void imageDataChanged (ImagePixelData*) = 0; + virtual void imageDataBeingDeleted (ImagePixelData*) = 0; + }; + + ListenerList listeners; + + void sendDataChangeMessage(); + private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePixelData) }; diff --git a/libs/juce/source/modules/juce_graphics/juce_module_info b/libs/juce/source/modules/juce_graphics/juce_module_info index b4a2f4db..c2a3ec7f 100644 --- a/libs/juce/source/modules/juce_graphics/juce_module_info +++ b/libs/juce/source/modules/juce_graphics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_graphics", "name": "JUCE graphics classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Classes for 2D vector graphics, image loading/saving, font handling, etc.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/libs/juce/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index 7ae0114a..03e47fff 100644 --- a/libs/juce/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/libs/juce/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -52,15 +52,19 @@ public: LowLevelGraphicsContext* createLowLevelContext() override { + sendDataChangeMessage(); return new CoreGraphicsContext (context, height, 1.0f); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { bitmap.data = imageData + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; + + if (mode != Image::BitmapData::readOnly) + sendDataChangeMessage(); } ImagePixelData* clone() override diff --git a/libs/juce/source/modules/juce_graphics/native/juce_mac_Fonts.mm b/libs/juce/source/modules/juce_graphics/native/juce_mac_Fonts.mm index e33acea5..7b1ad706 100644 --- a/libs/juce/source/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/libs/juce/source/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -1230,7 +1230,12 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) { + #if JUCE_CORETEXT_AVAILABLE return new OSXTypeface (data, dataSize); + #else + jassertfalse; // You need CoreText enabled to use this feature! + return nullptr; + #endif } void Typeface::scanFolderForFonts (const File&) diff --git a/libs/juce/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h b/libs/juce/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h index 42a27993..1d6158ed 100644 --- a/libs/juce/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h +++ b/libs/juce/source/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h @@ -290,7 +290,7 @@ public: static ApplicationCommandTarget* findDefaultComponentTarget(); /** Examines this component and all its parents in turn, looking for the first one - which is a ApplicationCommandTarget. + which is an ApplicationCommandTarget. Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them implement that class. diff --git a/libs/juce/source/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h b/libs/juce/source/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h index 62740fe9..d3351aca 100644 --- a/libs/juce/source/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h +++ b/libs/juce/source/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h @@ -29,7 +29,7 @@ //============================================================================== /** Manages and edits a list of keypresses, which it uses to invoke the appropriate - command in a ApplicationCommandManager. + command in an ApplicationCommandManager. Normally, you won't actually create a KeyPressMappingSet directly, because each ApplicationCommandManager contains its own KeyPressMappingSet, so typically diff --git a/libs/juce/source/modules/juce_gui_basics/components/juce_Component.h b/libs/juce/source/modules/juce_gui_basics/components/juce_Component.h index 00ef6ec8..1bc092e0 100644 --- a/libs/juce/source/modules/juce_gui_basics/components/juce_Component.h +++ b/libs/juce/source/modules/juce_gui_basics/components/juce_Component.h @@ -326,12 +326,12 @@ public: void getVisibleArea (RectangleList& result, bool includeSiblings) const; //============================================================================== - /** Returns this component's x coordinate relative the the screen's top-left origin. + /** Returns this component's x coordinate relative the screen's top-left origin. @see getX, localPointToGlobal */ int getScreenX() const; - /** Returns this component's y coordinate relative the the screen's top-left origin. + /** Returns this component's y coordinate relative the screen's top-left origin. @see getY, localPointToGlobal */ int getScreenY() const; diff --git a/libs/juce/source/modules/juce_gui_basics/juce_module_info b/libs/juce/source/modules/juce_gui_basics/juce_module_info index 3dc07ed0..1a80bf49 100644 --- a/libs/juce/source/modules/juce_gui_basics/juce_module_info +++ b/libs/juce/source/modules/juce_gui_basics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_gui_basics", "name": "JUCE GUI core classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Basic user-interface components and related classes.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/libs/juce/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/libs/juce/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 408f752a..85045f7b 100644 --- a/libs/juce/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/libs/juce/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -104,6 +104,9 @@ LookAndFeel_V2::LookAndFeel_V2() ComboBox::backgroundColourId, 0xffffffff, ComboBox::arrowColourId, 0x99000000, + PropertyComponent::backgroundColourId, 0x66ffffff, + PropertyComponent::labelTextColourId, 0xff000000, + TextPropertyComponent::backgroundColourId, 0xffffffff, TextPropertyComponent::textColourId, 0xff000000, TextPropertyComponent::outlineColourId, standardOutlineColour, @@ -1456,6 +1459,8 @@ Label* LookAndFeel_V2::createSliderTextBox (Slider& slider) l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); + l->setColour (TextEditor::highlightColourId, slider.findColour (Slider::textBoxHighlightColourId)); + return l; } @@ -2302,20 +2307,16 @@ void LookAndFeel_V2::drawPropertyPanelSectionHeader (Graphics& g, const String& g.drawText (name, textX, 0, width - textX - 4, height, Justification::centredLeft, true); } -void LookAndFeel_V2::drawPropertyComponentBackground (Graphics& g, int width, int height, - PropertyComponent&) +void LookAndFeel_V2::drawPropertyComponentBackground (Graphics& g, int width, int height, PropertyComponent& component) { - g.setColour (Colour (0x66ffffff)); + g.setColour (component.findColour (PropertyComponent::backgroundColourId)); g.fillRect (0, 0, width, height - 1); } -void LookAndFeel_V2::drawPropertyComponentLabel (Graphics& g, int, int height, - PropertyComponent& component) +void LookAndFeel_V2::drawPropertyComponentLabel (Graphics& g, int, int height, PropertyComponent& component) { - g.setColour (Colours::black); - - if (! component.isEnabled()) - g.setOpacity (0.6f); + g.setColour (component.findColour (PropertyComponent::labelTextColourId) + .withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.6f)); g.setFont (jmin (height, 24) * 0.65f); diff --git a/libs/juce/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/libs/juce/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 0d314db5..3a8ddfa0 100644 --- a/libs/juce/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/libs/juce/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -617,15 +617,19 @@ public: LowLevelGraphicsContext* createLowLevelContext() override { + sendDataChangeMessage(); return new LowLevelGraphicsSoftwareRenderer (Image (this)); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { bitmap.data = imageData + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; + + if (mode != Image::BitmapData::readOnly) + sendDataChangeMessage(); } ImagePixelData* clone() override diff --git a/libs/juce/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/libs/juce/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index ab14b16f..9b368204 100644 --- a/libs/juce/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/libs/juce/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -328,15 +328,19 @@ public: LowLevelGraphicsContext* createLowLevelContext() override { + sendDataChangeMessage(); return new LowLevelGraphicsSoftwareRenderer (Image (this)); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { bitmap.data = imageData + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; + + if (mode != Image::BitmapData::readOnly) + sendDataChangeMessage(); } ImagePixelData* clone() override @@ -3177,10 +3181,22 @@ void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDis } //============================================================================== -static BOOL CALLBACK enumMonitorsProc (HMONITOR, HDC, LPRECT r, LPARAM userInfo) +struct MonitorInfo +{ + MonitorInfo (Rectangle rect, bool main) noexcept : isMain (main), bounds (rect) {} + + Rectangle bounds; + bool isMain; +}; + +static BOOL CALLBACK enumMonitorsProc (HMONITOR hm, HDC, LPRECT r, LPARAM userInfo) { - Array >* const monitorCoords = (Array >*) userInfo; - monitorCoords->add (rectangleFromRECT (*r)); + MONITORINFO info = { 0 }; + info.cbSize = sizeof (info); + GetMonitorInfo (hm, &info); + const bool isMain = (info.dwFlags & 1 /* MONITORINFOF_PRIMARY */) != 0; + ((Array*) userInfo)->add (MonitorInfo (rectangleFromRECT (*r), isMain)); + return TRUE; } @@ -3188,17 +3204,17 @@ void Desktop::Displays::findDisplays (float masterScale) { setDPIAwareness(); - Array > monitors; + Array monitors; EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitors); + if (monitors.size() == 0) + monitors.add (MonitorInfo (rectangleFromRECT (getWindowRect (GetDesktopWindow())), true)); + // make sure the first in the list is the main monitor for (int i = 1; i < monitors.size(); ++i) - if (monitors.getReference(i).getPosition().isOrigin()) + if (monitors.getReference(i).isMain) monitors.swap (i, 0); - if (monitors.size() == 0) - monitors.add (rectangleFromRECT (getWindowRect (GetDesktopWindow()))); - RECT workArea; SystemParametersInfo (SPI_GETWORKAREA, 0, &workArea, 0); @@ -3207,12 +3223,12 @@ void Desktop::Displays::findDisplays (float masterScale) for (int i = 0; i < monitors.size(); ++i) { Display d; - d.userArea = d.totalArea = monitors.getReference(i) / masterScale; - d.isMain = (i == 0); - d.scale = masterScale; - d.dpi = dpi; + d.userArea = d.totalArea = monitors.getReference(i).bounds / masterScale; + d.isMain = monitors.getReference(i).isMain; + d.scale = masterScale; + d.dpi = dpi; - if (i == 0) + if (d.isMain) d.userArea = d.userArea.getIntersection (rectangleFromRECT (workArea) / masterScale); displays.add (d); diff --git a/libs/juce/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h b/libs/juce/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h index de45f329..2fd3d7ad 100644 --- a/libs/juce/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h +++ b/libs/juce/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h @@ -102,6 +102,22 @@ public: /** By default, this just repaints the component. */ void enablementChanged() override; + //============================================================================== + /** A set of colour IDs to use to change the colour of various aspects of the combo box. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + To change the colours of the menu that pops up + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1008300, /**< The background colour to fill the component with. */ + labelTextColourId = 0x1008301, /**< The colour for the property's label text. */ + }; + //============================================================================== /** This abstract base class is implemented by LookAndFeel classes. */ struct JUCE_API LookAndFeelMethods diff --git a/libs/juce/source/modules/juce_gui_basics/widgets/juce_Slider.cpp b/libs/juce/source/modules/juce_gui_basics/widgets/juce_Slider.cpp index c59f9fcc..1a1a73af 100644 --- a/libs/juce/source/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/libs/juce/source/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -375,7 +375,7 @@ public: const double delta = (button == incButton) ? interval : -interval; DragInProgress drag (*this); - setValue (owner.snapValue (getValue() + delta, false), sendNotificationSync); + setValue (owner.snapValue (getValue() + delta, notDragging), sendNotificationSync); } } @@ -394,7 +394,7 @@ public: void labelTextChanged (Label* label) override { - const double newValue = owner.snapValue (owner.getValueFromText (label->getText()), false); + const double newValue = owner.snapValue (owner.getValueFromText (label->getText()), notDragging); if (newValue != (double) currentValue.getValue()) { @@ -873,6 +873,8 @@ public: && ! ((style == LinearBar || style == LinearBarVertical) && e.mouseWasClicked() && valueBox != nullptr && valueBox->isEditable())) { + DragMode dragMode = notDragging; + if (style == Rotary) { handleRotaryDrag (e); @@ -889,21 +891,27 @@ public: } if (isAbsoluteDragMode (e.mods) || (maximum - minimum) / sliderRegionSize < interval) + { + dragMode = notDragging; handleAbsoluteDrag (e); + } else + { + dragMode = velocityDrag; handleVelocityDrag (e); + } } valueWhenLastDragged = jlimit (minimum, maximum, valueWhenLastDragged); if (sliderBeingDragged == 0) { - setValue (owner.snapValue (valueWhenLastDragged, true), + setValue (owner.snapValue (valueWhenLastDragged, dragMode), sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationSync); } else if (sliderBeingDragged == 1) { - setMinValue (owner.snapValue (valueWhenLastDragged, true), + setMinValue (owner.snapValue (valueWhenLastDragged, dragMode), sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationAsync, true); if (e.mods.isShiftDown()) @@ -913,7 +921,7 @@ public: } else if (sliderBeingDragged == 2) { - setMaxValue (owner.snapValue (valueWhenLastDragged, true), + setMaxValue (owner.snapValue (valueWhenLastDragged, dragMode), sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationAsync, true); if (e.mods.isShiftDown()) @@ -994,7 +1002,7 @@ public: delta = -delta; DragInProgress drag (*this); - setValue (owner.snapValue (value + delta, false), sendNotificationSync); + setValue (owner.snapValue (value + delta, notDragging), sendNotificationSync); } return true; @@ -1534,7 +1542,7 @@ double Slider::valueToProportionOfLength (double value) return skew == 1.0 ? n : pow (n, skew); } -double Slider::snapValue (double attemptedValue, const bool) +double Slider::snapValue (double attemptedValue, DragMode) { return attemptedValue; } diff --git a/libs/juce/source/modules/juce_gui_basics/widgets/juce_Slider.h b/libs/juce/source/modules/juce_gui_basics/widgets/juce_Slider.h index f3c2f45b..aeb8aaf4 100644 --- a/libs/juce/source/modules/juce_gui_basics/widgets/juce_Slider.h +++ b/libs/juce/source/modules/juce_gui_basics/widgets/juce_Slider.h @@ -97,6 +97,16 @@ public: TextBoxBelow /**< Puts the text box below the slider, horizontally centred. */ }; + /** Describes the type of mouse-dragging that is happening when a value is being changed. + @see snapValue + */ + enum DragMode + { + notDragging, /**< Dragging is not active. */ + absoluteDrag, /**< The dragging corresponds directly to the value that is displayed. */ + velocityDrag /**< The dragging value change is relative to the velocity of the mouse mouvement. */ + }; + //============================================================================== /** Creates a slider. When created, you can set up the slider's style and range with setSliderStyle(), setRange(), etc. @@ -157,7 +167,7 @@ public: int getMouseDragSensitivity() const noexcept; //============================================================================== - /** Changes the way the the mouse is used when dragging the slider. + /** Changes the way the mouse is used when dragging the slider. If true, this will turn on velocity-sensitive dragging, so that the faster the mouse moves, the bigger the movement to the slider. This @@ -720,12 +730,13 @@ public: a given position, and allows a subclass to sanity-check this value, possibly returning a different value to use instead. - @param attemptedValue the value the user is trying to enter - @param userIsDragging true if the user is dragging with the mouse; false if - they are entering the value using the text box - @returns the value to use instead + @param attemptedValue the value the user is trying to enter + @param dragMode indicates whether the user is dragging with + the mouse; notDragging if they are entering the value + using the text box or other non-dragging interaction + @returns the value to use instead */ - virtual double snapValue (double attemptedValue, bool userIsDragging); + virtual double snapValue (double attemptedValue, DragMode dragMode); //============================================================================== @@ -874,6 +885,7 @@ private: JUCE_DEPRECATED (void setMaxValue (double, bool)); JUCE_DEPRECATED (void setMinAndMaxValues (double, double, bool, bool)); JUCE_DEPRECATED (void setMinAndMaxValues (double, double, bool)); + virtual void snapValue (double, bool) {} #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Slider) diff --git a/libs/juce/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/libs/juce/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp index 097ea896..b31bc491 100644 --- a/libs/juce/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/libs/juce/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -560,7 +560,7 @@ Viewport* TreeView::getViewport() const noexcept void TreeView::clearSelectedItems() { if (rootItem != nullptr) - rootItem->deselectAllRecursively(); + rootItem->deselectAllRecursively (nullptr); } int TreeView::getNumSelectedItems (int maximumDepthToSearchTo) const noexcept @@ -1299,12 +1299,13 @@ bool TreeViewItem::isSelected() const noexcept return selected; } -void TreeViewItem::deselectAllRecursively() +void TreeViewItem::deselectAllRecursively (TreeViewItem* itemToIgnore) { - setSelected (false, false); + if (this != itemToIgnore) + setSelected (false, false); for (int i = 0; i < subItems.size(); ++i) - subItems.getUnchecked(i)->deselectAllRecursively(); + subItems.getUnchecked(i)->deselectAllRecursively (itemToIgnore); } void TreeViewItem::setSelected (const bool shouldBeSelected, @@ -1315,7 +1316,7 @@ void TreeViewItem::setSelected (const bool shouldBeSelected, return; if (deselectOtherItemsFirst) - getTopLevelItem()->deselectAllRecursively(); + getTopLevelItem()->deselectAllRecursively (this); if (shouldBeSelected != selected) { diff --git a/libs/juce/source/modules/juce_gui_basics/widgets/juce_TreeView.h b/libs/juce/source/modules/juce_gui_basics/widgets/juce_TreeView.h index ad6fd51a..dc46c041 100644 --- a/libs/juce/source/modules/juce_gui_basics/widgets/juce_TreeView.h +++ b/libs/juce/source/modules/juce_gui_basics/widgets/juce_TreeView.h @@ -557,7 +557,7 @@ private: TreeViewItem* getDeepestOpenParentItem() noexcept; int getNumRows() const noexcept; TreeViewItem* getItemOnRow (int index) noexcept; - void deselectAllRecursively(); + void deselectAllRecursively (TreeViewItem* itemToIgnore); int countSelectedItemsRecursively (int depth) const noexcept; TreeViewItem* getSelectedItemWithIndex (int index) noexcept; TreeViewItem* getNextVisibleItem (bool recurse) const noexcept; diff --git a/libs/juce/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/libs/juce/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index 1276f43e..d9f4cf28 100644 --- a/libs/juce/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/libs/juce/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -48,8 +48,6 @@ CallOutBox::~CallOutBox() { } -enum { callOutBoxDismissCommandId = 0x4f83a04b }; - //============================================================================== class CallOutBoxCallback : public ModalComponentManager::Callback, private Timer @@ -68,7 +66,7 @@ public: void timerCallback() override { if (! Process::isForegroundProcess()) - callout.postCommandMessage (callOutBoxDismissCommandId); + callout.dismiss(); } ScopedPointer content; @@ -127,7 +125,7 @@ void CallOutBox::inputAttemptWhenModal() // if you click on the area that originally popped-up the callout, you expect it // to get rid of the box, but deleting the box here allows the click to pass through and // probably re-trigger it, so we need to dismiss the box asynchronously to consume the click.. - postCommandMessage (callOutBoxDismissCommandId); + dismiss(); } else { @@ -136,6 +134,8 @@ void CallOutBox::inputAttemptWhenModal() } } +enum { callOutBoxDismissCommandId = 0x4f83a04b }; + void CallOutBox::handleCommandMessage (int commandId) { Component::handleCommandMessage (commandId); @@ -147,6 +147,11 @@ void CallOutBox::handleCommandMessage (int commandId) } } +void CallOutBox::dismiss() +{ + postCommandMessage (callOutBoxDismissCommandId); +} + bool CallOutBox::keyPressed (const KeyPress& key) { if (key.isKeyCode (KeyPress::escapeKey)) diff --git a/libs/juce/source/modules/juce_gui_basics/windows/juce_CallOutBox.h b/libs/juce/source/modules/juce_gui_basics/windows/juce_CallOutBox.h index fab2e6b5..51be2f3e 100644 --- a/libs/juce/source/modules/juce_gui_basics/windows/juce_CallOutBox.h +++ b/libs/juce/source/modules/juce_gui_basics/windows/juce_CallOutBox.h @@ -118,6 +118,11 @@ public: const Rectangle& areaToPointTo, Component* parentComponent); + /** Posts a message which will dismiss the callout box asynchronously. + NB: it's safe to call this method from any thread. + */ + void dismiss(); + //============================================================================== /** This abstract base class is implemented by LookAndFeel classes. */ struct JUCE_API LookAndFeelMethods diff --git a/libs/juce/source/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/libs/juce/source/modules/juce_gui_basics/windows/juce_ComponentPeer.h index 600425c6..9cdc8075 100644 --- a/libs/juce/source/modules/juce_gui_basics/windows/juce_ComponentPeer.h +++ b/libs/juce/source/modules/juce_gui_basics/windows/juce_ComponentPeer.h @@ -102,7 +102,7 @@ public: /** Returns the raw handle to whatever kind of window is being used. On windows, this is probably a HWND, on the mac, it's likely to be a WindowRef, - but rememeber there's no guarantees what you'll get back. + but remember there's no guarantees what you'll get back. */ virtual void* getNativeHandle() const = 0; diff --git a/libs/juce/source/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp b/libs/juce/source/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp index 82d12aa9..559bcab8 100644 --- a/libs/juce/source/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp +++ b/libs/juce/source/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp @@ -25,12 +25,13 @@ ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, const bool hasProgressBar, const bool hasCancelButton, - const int timeOutMsWhenCancelling_, + const int cancellingTimeOutMs, const String& cancelButtonText, Component* componentToCentreAround) - : Thread ("Juce Progress Window"), - progress (0.0), - timeOutMsWhenCancelling (timeOutMsWhenCancelling_) + : Thread ("ThreadWithProgressWindow"), + progress (0.0), + timeOutMsWhenCancelling (cancellingTimeOutMs), + wasCancelledByUser (false) { alertWindow = LookAndFeel::getDefaultLookAndFeel() .createAlertWindow (title, String(), @@ -52,8 +53,7 @@ ThreadWithProgressWindow::~ThreadWithProgressWindow() stopThread (timeOutMsWhenCancelling); } -#if JUCE_MODAL_LOOPS_PERMITTED -bool ThreadWithProgressWindow::runThread (const int priority) +void ThreadWithProgressWindow::launchThread (int priority) { jassert (MessageManager::getInstance()->isThisTheMessageThread()); @@ -65,15 +65,8 @@ bool ThreadWithProgressWindow::runThread (const int priority) alertWindow->setMessage (message); } - const bool finishedNaturally = alertWindow->runModalLoop() != 0; - - stopThread (timeOutMsWhenCancelling); - - alertWindow->setVisible (false); - - return finishedNaturally; + alertWindow->enterModalState(); } -#endif void ThreadWithProgressWindow::setProgress (const double newProgress) { @@ -88,15 +81,34 @@ void ThreadWithProgressWindow::setStatusMessage (const String& newStatusMessage) void ThreadWithProgressWindow::timerCallback() { - if (! isThreadRunning()) + bool threadStillRunning = isThreadRunning(); + + if (! (threadStillRunning && alertWindow->isCurrentlyModal())) { - // thread has finished normally.. + stopTimer(); + stopThread (timeOutMsWhenCancelling); alertWindow->exitModalState (1); alertWindow->setVisible (false); + + wasCancelledByUser = threadStillRunning; + threadComplete (threadStillRunning); + return; // (this may be deleted now) } - else - { - const ScopedLock sl (messageLock); - alertWindow->setMessage (message); - } + + const ScopedLock sl (messageLock); + alertWindow->setMessage (message); +} + +void ThreadWithProgressWindow::threadComplete (bool) {} + +#if JUCE_MODAL_LOOPS_PERMITTED +bool ThreadWithProgressWindow::runThread (const int priority) +{ + launchThread (priority); + + while (isTimerRunning()) + MessageManager::getInstance()->runDispatchLoopUntil (5); + + return ! wasCancelledByUser; } +#endif diff --git a/libs/juce/source/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h b/libs/juce/source/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h index 7bf85228..ec68244a 100644 --- a/libs/juce/source/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h +++ b/libs/juce/source/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h @@ -111,6 +111,7 @@ public: ~ThreadWithProgressWindow(); //============================================================================== + #if JUCE_MODAL_LOOPS_PERMITTED /** Starts the thread and waits for it to finish. This will start the thread, make the dialog box appear, and wait until either @@ -123,6 +124,18 @@ public: @returns true if the thread finished normally; false if the user pressed cancel */ bool runThread (int threadPriority = 5); + #endif + + /** Starts the thread and returns. + + This will start the thread and make the dialog box appear in a modal state. When + the thread finishes normally, or the cancel button is pressed, the window will be + hidden and the threadComplete() method will be called. + + @param threadPriority the priority to use when starting the thread - see + Thread::startThread() for values + */ + void launchThread (int threadPriority = 5); /** The thread should call this periodically to update the position of the progress bar. @@ -137,15 +150,22 @@ public: /** Returns the AlertWindow that is being used. */ AlertWindow* getAlertWindow() const noexcept { return alertWindow; } + //============================================================================== + /** This method is called (on the message thread) when the operation has finished. + You may choose to use this callback to delete the ThreadWithProgressWindow object. + */ + virtual void threadComplete (bool userPressedCancel); + private: //============================================================================== void timerCallback() override; double progress; - ScopedPointer alertWindow; + ScopedPointer alertWindow; String message; CriticalSection messageLock; const int timeOutMsWhenCancelling; + bool wasCancelledByUser; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadWithProgressWindow) }; diff --git a/libs/juce/source/modules/juce_gui_extra/juce_module_info b/libs/juce/source/modules/juce_gui_extra/juce_module_info index 9837cb40..4d13ceb0 100644 --- a/libs/juce/source/modules/juce_gui_extra/juce_module_info +++ b/libs/juce/source/modules/juce_gui_extra/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_gui_extra", "name": "JUCE extended GUI classes", - "version": "3.0.2", + "version": "3.0.4", "description": "Miscellaneous GUI classes for specialised tasks.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial",