| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE examples.
 -    Copyright (c) 2022 - Raw Material Software Limited
 - 
 -    The code included in this file is provided under the terms of the ISC license
 -    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 -    To use, copy, modify, and/or distribute this software for any purpose with or
 -    without fee is hereby granted provided that the above copyright notice and
 -    this permission notice appear in all copies.
 - 
 -    THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 -    WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 -    PURPOSE, ARE DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - /*******************************************************************************
 -  The block below describes the properties of this PIP. A PIP is a short snippet
 -  of code that can be read by the Projucer and used to generate a JUCE project.
 - 
 -  BEGIN_JUCE_PIP_METADATA
 - 
 -  name:             ReaperEmbeddedViewDemo
 -  version:          1.0.0
 -  vendor:           JUCE
 -  website:          http://juce.com
 -  description:      An audio plugin which embeds a secondary view in VST2 and
 -                    VST3 formats in REAPER
 - 
 -  dependencies:     juce_audio_basics, juce_audio_devices, juce_audio_formats,
 -                    juce_audio_plugin_client, juce_audio_processors,
 -                    juce_audio_utils, juce_core, juce_data_structures,
 -                    juce_events, juce_graphics, juce_gui_basics, juce_gui_extra
 -  exporters:        xcode_mac, vs2022, linux_make
 - 
 -  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 - 
 -  type:             AudioProcessor
 -  mainClass:        ReaperEmbeddedViewDemo
 - 
 -  useLocalCopy:     1
 - 
 -  END_JUCE_PIP_METADATA
 - 
 - *******************************************************************************/
 - 
 - /*  This demo shows how to use the VST2ClientExtensions and VST3ClientExtensions
 -     classes to provide extended functionality in compatible VST/VST3 hosts.
 - 
 -     If this project is built as a VST or VST3 plugin and loaded in REAPER
 -     6.29 or higher, it will provide an embedded level meter in the track
 -     control panel. To enable the embedded view, right-click on the plugin
 -     and select "Show embedded UI in TCP".
 - 
 -     The plugin's editor also include a button which can be used to toggle
 -     all inserts on and off.
 - */
 - 
 - #pragma once
 - 
 - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wshadow-field-in-constructor",
 -                                      "-Wnon-virtual-dtor")
 - 
 - #include <pluginterfaces/base/ftypes.h>
 - #include <pluginterfaces/base/funknown.h>
 - #include <pluginterfaces/vst/ivsthostapplication.h>
 - #include <pluginterfaces/vst2.x/aeffect.h>
 - 
 - JUCE_END_IGNORE_WARNINGS_GCC_LIKE
 - 
 - namespace reaper
 - {
 -     JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant",
 -                                          "-Wunused-parameter",
 -                                          "-Wnon-virtual-dtor")
 -     JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100)
 - 
 -     using namespace Steinberg;
 -     using INT_PTR = pointer_sized_int;
 -     using uint32 = Steinberg::uint32;
 - 
 -     #include "extern/reaper_plugin_fx_embed.h"
 -     #include "extern/reaper_vst3_interfaces.h"
 - 
 -     //==============================================================================
 -     /*  These should live in a file which is guaranteed to be compiled only once
 -         (i.e. a .cpp file, normally). This demo is a bit special, because we know
 -         that this header will only be included in a single translation unit.
 -      */
 -     DEF_CLASS_IID (IReaperHostApplication)
 -     DEF_CLASS_IID (IReaperUIEmbedInterface)
 - 
 -     JUCE_END_IGNORE_WARNINGS_MSVC
 -     JUCE_END_IGNORE_WARNINGS_GCC_LIKE
 - }
 - 
 - //==============================================================================
 - struct EmbeddedViewListener
 - {
 -     virtual ~EmbeddedViewListener() = default;
 -     virtual Steinberg::TPtrInt handledEmbeddedUIMessage (int msg,
 -                                                          Steinberg::TPtrInt parm2,
 -                                                          Steinberg::TPtrInt parm3) = 0;
 - 
 -     virtual void setGlobalBypassFunction (void (*) (int)) = 0;
 - };
 - 
 - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnon-virtual-dtor")
 - 
 - //==============================================================================
 - class EmbeddedUI : public reaper::IReaperUIEmbedInterface
 - {
 - public:
 -     explicit EmbeddedUI (EmbeddedViewListener& demo) : listener (demo) {}
 - 
 -     Steinberg::TPtrInt embed_message (int msg,
 -                                       Steinberg::TPtrInt parm2,
 -                                       Steinberg::TPtrInt parm3) override
 -     {
 -         return listener.handledEmbeddedUIMessage (msg, parm2, parm3);
 -     }
 - 
 -     Steinberg::uint32 PLUGIN_API addRef() override   { return ++refCount; }
 -     Steinberg::uint32 PLUGIN_API release() override  { return --refCount; }
 - 
 -     Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID tuid, void** obj) override
 -     {
 -         if (std::memcmp (tuid, iid, sizeof (Steinberg::TUID)) == 0)
 -         {
 -             ++refCount;
 -             *obj = this;
 -             return Steinberg::kResultOk;
 -         }
 - 
 -         *obj = nullptr;
 -         return Steinberg::kNoInterface;
 -     }
 - 
 - private:
 -     EmbeddedViewListener& listener;
 -     std::atomic<Steinberg::uint32> refCount { 1 };
 - };
 - 
 - JUCE_END_IGNORE_WARNINGS_GCC_LIKE
 - 
 - class VST2Extensions : public VST2ClientExtensions
 - {
 - public:
 -     explicit VST2Extensions (EmbeddedViewListener& l)
 -         : listener (l) {}
 - 
 -     pointer_sized_int handleVstPluginCanDo (int32, pointer_sized_int, void* ptr, float) override
 -     {
 -         if (auto* str = static_cast<const char*> (ptr))
 -             for (auto* key : { "hasCockosEmbeddedUI", "hasCockosExtensions" })
 -                 if (strcmp (str, key) == 0)
 -                     return (pointer_sized_int) 0xbeef0000;
 - 
 -         return 0;
 -     }
 - 
 -     pointer_sized_int handleVstManufacturerSpecific (int32 index,
 -                                                      pointer_sized_int value,
 -                                                      void* ptr,
 -                                                      float opt) override
 -     {
 -         // The docstring at the top of reaper_plugin_fx_embed.h specifies
 -         // that the index will always be effEditDraw, which is now deprecated.
 -         if (index != __effEditDrawDeprecated)
 -             return 0;
 - 
 -         return (pointer_sized_int) listener.handledEmbeddedUIMessage ((int) opt,
 -                                                                       (Steinberg::TPtrInt) value,
 -                                                                       (Steinberg::TPtrInt) ptr);
 -     }
 - 
 -     void handleVstHostCallbackAvailable (std::function<VstHostCallbackType>&& hostcb) override
 -     {
 -         char functionName[] = "BypassFxAllTracks";
 -         listener.setGlobalBypassFunction (reinterpret_cast<void (*) (int)> (hostcb ((int32_t) 0xdeadbeef, (int32_t) 0xdeadf00d, 0, functionName, 0.0)));
 -     }
 - 
 - private:
 -     EmbeddedViewListener& listener;
 - };
 - 
 - class VST3Extensions : public VST3ClientExtensions
 - {
 - public:
 -     explicit VST3Extensions (EmbeddedViewListener& l)
 -         : listener (l) {}
 - 
 -     int32_t queryIEditController (const Steinberg::TUID tuid, void** obj) override
 -     {
 -         if (embeddedUi.queryInterface (tuid, obj) == Steinberg::kResultOk)
 -             return Steinberg::kResultOk;
 - 
 -         *obj = nullptr;
 -         return Steinberg::kNoInterface;
 -     }
 - 
 -     void setIHostApplication (Steinberg::FUnknown* ptr) override
 -     {
 -         if (ptr == nullptr)
 -             return;
 - 
 -         void* objPtr = nullptr;
 - 
 -         if (ptr->queryInterface (reaper::IReaperHostApplication::iid, &objPtr) == Steinberg::kResultOk)
 -         {
 -             if (void* fnPtr = static_cast<reaper::IReaperHostApplication*> (objPtr)->getReaperApi ("BypassFxAllTracks"))
 -                 listener.setGlobalBypassFunction (reinterpret_cast<void (*) (int)> (fnPtr));
 -         }
 -     }
 - 
 - private:
 -     EmbeddedViewListener& listener;
 -     EmbeddedUI embeddedUi { listener };
 - };
 - 
 - //==============================================================================
 - class Editor : public AudioProcessorEditor
 - {
 - public:
 -     explicit Editor (AudioProcessor& proc,
 -                      AudioParameterFloat& param,
 -                      void (*globalBypass) (int))
 -         : AudioProcessorEditor (proc), attachment (param, slider)
 -     {
 -         addAndMakeVisible (slider);
 -         addAndMakeVisible (bypassButton);
 - 
 -         // Clicking will bypass *everything*
 -         bypassButton.onClick = [globalBypass] { if (globalBypass != nullptr) globalBypass (-1); };
 - 
 -         setSize (300, 80);
 -     }
 - 
 -     void resized() override
 -     {
 -         auto b = getLocalBounds();
 -         slider.setBounds (b.removeFromTop (40));
 -         bypassButton.setBounds (b);
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         g.fillAll (Colours::darkgrey);
 -     }
 - 
 - private:
 -     Slider slider;
 -     TextButton bypassButton { "global bypass" };
 -     SliderParameterAttachment attachment;
 - };
 - 
 - //==============================================================================
 - class ReaperEmbeddedViewDemo  : public AudioProcessor,
 -                                 private EmbeddedViewListener,
 -                                 private Timer
 - {
 - public:
 -     ReaperEmbeddedViewDemo()
 -     {
 -         addParameter (gain = new AudioParameterFloat ({ "gain", 1 }, "Gain", 0.0f, 1.0f, 0.5f));
 -         startTimerHz (60);
 -     }
 - 
 -     void prepareToPlay (double, int) override {}
 -     void reset() override {}
 - 
 -     void releaseResources() override {}
 - 
 -     void processBlock (AudioBuffer<float>&  audio, MidiBuffer&) override { processBlockImpl (audio); }
 -     void processBlock (AudioBuffer<double>& audio, MidiBuffer&) override { processBlockImpl (audio); }
 - 
 -     //==============================================================================
 -     AudioProcessorEditor* createEditor() override { return new Editor (*this, *gain, globalBypassFn); }
 -     bool hasEditor() const override               { return true;   }
 - 
 -     //==============================================================================
 -     const String getName() const override { return "ReaperEmbeddedViewDemo"; }
 - 
 -     bool acceptsMidi()  const override { return false; }
 -     bool producesMidi() const override { return false; }
 -     bool isMidiEffect() const override { return false; }
 - 
 -     double getTailLengthSeconds() const override { return 0.0; }
 - 
 -     //==============================================================================
 -     int getNumPrograms()    override { return 1; }
 -     int getCurrentProgram() override { return 0; }
 -     void setCurrentProgram (int) override {}
 -     const String getProgramName (int) override { return "None"; }
 - 
 -     void changeProgramName (int, const String&) override {}
 - 
 -     //==============================================================================
 -     void getStateInformation (MemoryBlock& destData) override
 -     {
 -         MemoryOutputStream (destData, true).writeFloat (*gain);
 -     }
 - 
 -     void setStateInformation (const void* data, int sizeInBytes) override
 -     {
 -         gain->setValueNotifyingHost (MemoryInputStream (data,
 -                                                         static_cast<size_t> (sizeInBytes),
 -                                                         false).readFloat());
 -     }
 - 
 -     VST2ClientExtensions* getVST2ClientExtensions() override { return &vst2Extensions; }
 -     VST3ClientExtensions* getVST3ClientExtensions() override { return &vst3Extensions; }
 - 
 - private:
 -     template <typename Float>
 -     void processBlockImpl (AudioBuffer<Float>& audio)
 -     {
 -         audio.applyGain (*gain);
 - 
 -         const auto minMax = audio.findMinMax (0, 0, audio.getNumSamples());
 -         const auto newMax = (float) std::max (std::abs (minMax.getStart()), std::abs (minMax.getEnd()));
 - 
 -         auto loaded = storedLevel.load();
 -         while (loaded < newMax && ! storedLevel.compare_exchange_weak (loaded, newMax)) {}
 -     }
 - 
 -     void timerCallback() override
 -     {
 -         levelToDraw = std::max (levelToDraw * 0.95f, storedLevel.exchange (0.0f));
 -     }
 - 
 -     Steinberg::TPtrInt getSizeInfo (reaper::REAPER_FXEMBED_SizeHints* sizeHints)
 -     {
 -         if (sizeHints == nullptr)
 -             return 0;
 - 
 -         sizeHints->preferred_aspect = 1 << 16;
 -         sizeHints->minimum_aspect   = 1 << 16;
 -         sizeHints->min_height = sizeHints->min_width = 50;
 -         sizeHints->max_height = sizeHints->max_width = 1000;
 -         return 1;
 -     }
 - 
 -     Steinberg::TPtrInt doPaint (reaper::REAPER_FXEMBED_IBitmap* bitmap,
 -                                 reaper::REAPER_FXEMBED_DrawInfo* drawInfo)
 -     {
 -         if (bitmap == nullptr || drawInfo == nullptr || bitmap->getWidth() <= 0 || bitmap->getHeight() <= 0)
 -             return 0;
 - 
 -         Image img (juce::Image::PixelFormat::ARGB, bitmap->getWidth(), bitmap->getHeight(), true);
 -         Graphics g (img);
 - 
 -         g.fillAll (Colours::black);
 - 
 -         const auto bounds = g.getClipBounds();
 -         const auto corner = 3.0f;
 - 
 -         g.setColour (Colours::darkgrey);
 -         g.fillRoundedRectangle (bounds.withSizeKeepingCentre (20, bounds.getHeight() - 6).toFloat(),
 -                                 corner);
 - 
 -         const auto minDb = -50.0f;
 -         const auto maxDb = 6.0f;
 -         const auto levelInDb = Decibels::gainToDecibels (levelToDraw, minDb);
 -         const auto fractionOfHeight = jmap (levelInDb, minDb, maxDb, 0.0f, 1.0f);
 -         const auto trackBounds = bounds.withSizeKeepingCentre (16, bounds.getHeight() - 10).toFloat();
 - 
 -         g.setColour (Colours::black);
 -         const auto zeroDbIndicatorY = trackBounds.proportionOfHeight (jmap (0.0f,
 -                                                                             minDb,
 -                                                                             maxDb,
 -                                                                             0.0f,
 -                                                                             1.0f));
 -         g.drawHorizontalLine ((int) (trackBounds.getBottom() - zeroDbIndicatorY),
 -                               trackBounds.getX(),
 -                               trackBounds.getRight());
 - 
 -         g.setGradientFill (ColourGradient (Colours::darkgreen,
 -                                            { 0.0f, (float) bounds.getHeight() },
 -                                            Colours::darkred,
 -                                            { 0.0f, 0.0f },
 -                                            false));
 - 
 -         g.fillRoundedRectangle (trackBounds.withHeight (trackBounds.proportionOfHeight (fractionOfHeight))
 -                                            .withBottomY (trackBounds.getBottom()),
 -                                 corner);
 - 
 -         Image::BitmapData imgData { img, Image::BitmapData::readOnly };
 -         const auto pixelsWidth = imgData.pixelStride * imgData.width;
 - 
 -         auto* px = bitmap->getBits();
 -         const auto rowSpan = bitmap->getRowSpan();
 -         const auto numRows = bitmap->getHeight();
 - 
 -         for (int y = 0; y < numRows; ++y)
 -             std::memcpy (px + (y * rowSpan), imgData.getLinePointer (y), (size_t) pixelsWidth);
 - 
 -         return 1;
 -     }
 - 
 -     Steinberg::TPtrInt handledEmbeddedUIMessage (int msg,
 -                                                  Steinberg::TPtrInt parm2,
 -                                                  Steinberg::TPtrInt parm3) override
 -     {
 -         switch (msg)
 -         {
 -             case REAPER_FXEMBED_WM_IS_SUPPORTED:
 -                 return 1;
 - 
 -             case REAPER_FXEMBED_WM_PAINT:
 -                 return doPaint (reinterpret_cast<reaper::REAPER_FXEMBED_IBitmap*> (parm2),
 -                                 reinterpret_cast<reaper::REAPER_FXEMBED_DrawInfo*> (parm3));
 - 
 -             case REAPER_FXEMBED_WM_GETMINMAXINFO:
 -                 return getSizeInfo (reinterpret_cast<reaper::REAPER_FXEMBED_SizeHints*> (parm3));
 - 
 -             // Implementing mouse behaviour is left as an exercise for the reaper, I mean reader
 -             case REAPER_FXEMBED_WM_CREATE:          break;
 -             case REAPER_FXEMBED_WM_DESTROY:         break;
 -             case REAPER_FXEMBED_WM_SETCURSOR:       break;
 -             case REAPER_FXEMBED_WM_MOUSEMOVE:       break;
 -             case REAPER_FXEMBED_WM_LBUTTONDOWN:     break;
 -             case REAPER_FXEMBED_WM_LBUTTONUP:       break;
 -             case REAPER_FXEMBED_WM_LBUTTONDBLCLK:   break;
 -             case REAPER_FXEMBED_WM_RBUTTONDOWN:     break;
 -             case REAPER_FXEMBED_WM_RBUTTONUP:       break;
 -             case REAPER_FXEMBED_WM_RBUTTONDBLCLK:   break;
 -             case REAPER_FXEMBED_WM_MOUSEWHEEL:      break;
 -         }
 - 
 -         return 0;
 -     }
 - 
 -     void setGlobalBypassFunction (void (*fn) (int)) override { globalBypassFn = fn; }
 - 
 -     AudioParameterFloat* gain = nullptr;
 -     void (*globalBypassFn) (int) = nullptr;
 - 
 -     std::atomic<float> storedLevel { 0.0f };
 -     float levelToDraw = 0.0f;
 - 
 -     VST2Extensions vst2Extensions { *this };
 -     VST3Extensions vst3Extensions { *this };
 - };
 
 
  |