diff --git a/BREAKING-CHANGES.txt b/BREAKING-CHANGES.txt index 7ffb08a11e..3ecabf0c40 100644 --- a/BREAKING-CHANGES.txt +++ b/BREAKING-CHANGES.txt @@ -4,6 +4,26 @@ JUCE breaking changes develop ======= +Change +------ +AudioFrameRate::frameRate is now a class type instead of an enum. + +Possible Issues +--------------- +Code that read the old enum value will not compile. + +Workaround +---------- +Call frameRate.getType() to fetch the old enum type. Alternatively, use the new +getBaseRate(), isDrop(), isPullDown(), and getEffectiveRate() functions. The +new functions provide a more accurate description of the host's frame rate. + +Rationale +--------- +The old enum-based interface was not flexible enough to describe all the frame +rates that might be reported by a plugin host. + + Change ------ FlexItem::alignSelf now defaults to "autoAlign" rather than "stretch". diff --git a/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h b/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h index 37a3d53783..0e83537ad1 100644 --- a/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h +++ b/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h @@ -60,6 +60,98 @@ public: fpsUnknown = 99 }; + /** More descriptive frame rate type. */ + class JUCE_API FrameRate + { + public: + /** Creates a frame rate with a base rate of 0. */ + FrameRate() = default; + + /** Creates a FrameRate instance from a FrameRateType. */ + FrameRate (FrameRateType type) : FrameRate (fromType (type)) {} + + /** Gets the FrameRateType that matches the state of this FrameRate. + + Returns fpsUnknown if this FrameRate cannot be represented by any of the + other enum fields. + */ + FrameRateType getType() const + { + switch (base) + { + case 24: return pulldown ? fps23976 : fps24; + case 25: return fps25; + case 30: return pulldown ? (drop ? fps2997drop : fps2997) + : (drop ? fps30drop : fps30); + case 60: return drop ? fps60drop : fps60; + } + + return fpsUnknown; + } + + /** Returns the plain rate, without taking pulldown into account. */ + int getBaseRate() const { return base; } + + /** Returns true if drop-frame timecode is in use. */ + bool isDrop() const { return drop; } + + /** Returns true if the effective framerate is actually equal to the base rate divided by 1.001 */ + bool isPullDown() const { return pulldown; } + + /** Returns the actual rate described by this object, taking pulldown into account. */ + double getEffectiveRate() const { return pulldown ? (double) base / 1.001 : (double) base; } + + /** Returns a copy of this object with the specified base rate. */ + FrameRate withBaseRate (int x) const { return with (&FrameRate::base, x); } + + /** Returns a copy of this object with drop frames enabled or disabled, as specified. */ + FrameRate withDrop (bool x = true) const { return with (&FrameRate::drop, x); } + + /** Returns a copy of this object with pulldown enabled or disabled, as specified. */ + FrameRate withPullDown (bool x = true) const { return with (&FrameRate::pulldown, x); } + + /** Returns true if this instance is equal to other. */ + bool operator== (const FrameRate& other) const + { + const auto tie = [] (const FrameRate& x) { return std::tie (x.base, x.drop, x.pulldown); }; + return tie (*this) == tie (other); + } + + /** Returns true if this instance is not equal to other. */ + bool operator!= (const FrameRate& other) const { return ! (*this == other); } + + private: + static FrameRate fromType (FrameRateType type) + { + switch (type) + { + case fps23976: return FrameRate().withBaseRate (24).withPullDown(); + case fps24: return FrameRate().withBaseRate (24); + case fps25: return FrameRate().withBaseRate (25); + case fps2997: return FrameRate().withBaseRate (30).withPullDown(); + case fps30: return FrameRate().withBaseRate (30); + case fps2997drop: return FrameRate().withBaseRate (30).withDrop().withPullDown(); + case fps30drop: return FrameRate().withBaseRate (30).withDrop(); + case fps60: return FrameRate().withBaseRate (60); + case fps60drop: return FrameRate().withBaseRate (60).withDrop(); + case fpsUnknown: break; + } + + return {}; + } + + template + FrameRate with (Member&& member, Value&& value) const + { + auto copy = *this; + copy.*member = std::forward (value); + return copy; + } + + int base = 0; + bool drop = false, pulldown = false; + }; + //============================================================================== /** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method. */ @@ -95,7 +187,7 @@ public: double ppqPositionOfLastBarStart = 0; /** The video frame rate, if applicable. */ - FrameRateType frameRate = FrameRateType::fps23976; + FrameRate frameRate = FrameRateType::fps23976; /** True if the transport is currently playing. */ bool isPlaying = false; @@ -124,7 +216,7 @@ public: //============================================================================== bool operator== (const CurrentPositionInfo& other) const noexcept { - auto tie = [] (const CurrentPositionInfo& i) + const auto tie = [] (const CurrentPositionInfo& i) { return std::tie (i.timeInSamples, i.ppqPosition, diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index aef1dd4b8b..59298c4cbd 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -271,20 +271,9 @@ static void toProcessContext (Vst::ProcessContext& context, AudioPlayHead* playH context.cycleStartMusic = position.ppqLoopStart; context.cycleEndMusic = position.ppqLoopEnd; - switch (position.frameRate) - { - case AudioPlayHead::fps23976: fr.framesPerSecond = 24; fr.flags = FrameRate::kPullDownRate; break; - case AudioPlayHead::fps24: fr.framesPerSecond = 24; fr.flags = 0; break; - case AudioPlayHead::fps25: fr.framesPerSecond = 25; fr.flags = 0; break; - case AudioPlayHead::fps2997: fr.framesPerSecond = 30; fr.flags = FrameRate::kPullDownRate; break; - case AudioPlayHead::fps2997drop: fr.framesPerSecond = 30; fr.flags = FrameRate::kPullDownRate | FrameRate::kDropRate; break; - case AudioPlayHead::fps30: fr.framesPerSecond = 30; fr.flags = 0; break; - case AudioPlayHead::fps30drop: fr.framesPerSecond = 30; fr.flags = FrameRate::kDropRate; break; - case AudioPlayHead::fps60: fr.framesPerSecond = 60; fr.flags = 0; break; - case AudioPlayHead::fps60drop: fr.framesPerSecond = 60; fr.flags = FrameRate::kDropRate; break; - case AudioPlayHead::fpsUnknown: break; - default: jassertfalse; break; // New frame rate? - } + context.frameRate.framesPerSecond = (Steinberg::uint32) position.frameRate.getBaseRate(); + context.frameRate.flags = (Steinberg::uint32) ((position.frameRate.isDrop() ? FrameRate::kDropRate : 0) + | (position.frameRate.isPullDown() ? FrameRate::kPullDownRate : 0)); if (position.isPlaying) context.state |= ProcessContext::kPlaying; if (position.isRecording) context.state |= ProcessContext::kRecording; diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index cd8db0dbf9..a8dda5d4ef 100644 --- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -2396,21 +2396,29 @@ private: else vstHostTime.flags &= ~Vst2::kVstTransportChanged; - switch (position.frameRate) + struct OptionalFrameRate { - case AudioPlayHead::fps24: setHostTimeFrameRate (Vst2::kVstSmpte24fps, 24.0, position.timeInSeconds); break; - case AudioPlayHead::fps25: setHostTimeFrameRate (Vst2::kVstSmpte25fps, 25.0, position.timeInSeconds); break; - case AudioPlayHead::fps30: setHostTimeFrameRate (Vst2::kVstSmpte30fps, 30.0, position.timeInSeconds); break; - case AudioPlayHead::fps60: setHostTimeFrameRate (Vst2::kVstSmpte60fps, 60.0, position.timeInSeconds); break; + bool valid; + Vst2::VstInt32 rate; + }; - case AudioPlayHead::fps23976: setHostTimeFrameRateDrop (Vst2::kVstSmpte239fps, 24.0, position.timeInSeconds); break; - case AudioPlayHead::fps2997: setHostTimeFrameRateDrop (Vst2::kVstSmpte2997fps, 30.0, position.timeInSeconds); break; - case AudioPlayHead::fps2997drop: setHostTimeFrameRateDrop (Vst2::kVstSmpte2997dfps, 30.0, position.timeInSeconds); break; - case AudioPlayHead::fps30drop: setHostTimeFrameRateDrop (Vst2::kVstSmpte30dfps, 30.0, position.timeInSeconds); break; - case AudioPlayHead::fps60drop: setHostTimeFrameRateDrop (Vst2::kVstSmpte599fps, 60.0, position.timeInSeconds); break; - case AudioPlayHead::fpsUnknown: - default: break; - } + const auto optionalFrameRate = [&fr = position.frameRate]() -> OptionalFrameRate + { + switch (fr.getBaseRate()) + { + case 24: return { true, fr.isPullDown() ? Vst2::kVstSmpte239fps : Vst2::kVstSmpte24fps }; + case 25: return { true, fr.isPullDown() ? Vst2::kVstSmpte249fps : Vst2::kVstSmpte25fps }; + case 30: return { true, fr.isPullDown() ? (fr.isDrop() ? Vst2::kVstSmpte2997dfps : Vst2::kVstSmpte2997fps) + : (fr.isDrop() ? Vst2::kVstSmpte30dfps : Vst2::kVstSmpte30fps) }; + case 60: return { true, fr.isPullDown() ? Vst2::kVstSmpte599fps : Vst2::kVstSmpte60fps }; + } + + return { false, Vst2::VstSmpteFrameRate{} }; + }(); + + vstHostTime.flags |= optionalFrameRate.valid ? Vst2::kVstSmpteValid : 0; + vstHostTime.smpteFrameRate = optionalFrameRate.rate; + vstHostTime.smpteOffset = (int32) (position.timeInSeconds * 80.0 * position.frameRate.getEffectiveRate() + 0.5); if (position.isLooping) { @@ -2506,18 +2514,6 @@ private: } //============================================================================== - void setHostTimeFrameRate (long frameRateIndex, double frameRate, double currentTime) noexcept - { - vstHostTime.flags |= Vst2::kVstSmpteValid; - vstHostTime.smpteFrameRate = (int32) frameRateIndex; - vstHostTime.smpteOffset = (int32) (currentTime * 80.0 * frameRate + 0.5); - } - - void setHostTimeFrameRateDrop (long frameRateIndex, double frameRate, double currentTime) noexcept - { - setHostTimeFrameRate (frameRateIndex, frameRate * 1000.0 / 1001.0, currentTime); - } - bool restoreProgramSettings (const fxProgram* const prog) { if (compareMagic (prog->chunkMagic, "CcnK")