migrating to the latest JUCE version
This commit is contained in:
		
							
								
								
									
										1449
									
								
								deps/juce/examples/Plugins/ARAPluginDemo.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1449
									
								
								deps/juce/examples/Plugins/ARAPluginDemo.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										954
									
								
								deps/juce/examples/Plugins/AUv3SynthPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										954
									
								
								deps/juce/examples/Plugins/AUv3SynthPluginDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,477 +1,477 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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:                  AUv3SynthPlugin
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           AUv3 synthesiser audio plugin.
 | 
			
		||||
 | 
			
		||||
 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, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:           JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:                  AudioProcessor
 | 
			
		||||
 mainClass:             AUv3SynthProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginIsSynth, pluginWantsMidiIn
 | 
			
		||||
 extraPluginFormats:    AUv3
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MaterialLookAndFeel : public LookAndFeel_V4
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MaterialLookAndFeel()
 | 
			
		||||
    {
 | 
			
		||||
        setColour (ResizableWindow::backgroundColourId, windowBackgroundColour);
 | 
			
		||||
        setColour (TextButton::buttonOnColourId,        brightButtonColour);
 | 
			
		||||
        setColour (TextButton::buttonColourId,          disabledButtonColour);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void drawButtonBackground (Graphics& g,
 | 
			
		||||
                               Button& button,
 | 
			
		||||
                               const Colour& /*backgroundColour*/,
 | 
			
		||||
                               bool /*isMouseOverButton*/,
 | 
			
		||||
                               bool isButtonDown) override
 | 
			
		||||
    {
 | 
			
		||||
        auto buttonRect = button.getLocalBounds().toFloat();
 | 
			
		||||
 | 
			
		||||
        if (isButtonDown)
 | 
			
		||||
            g.setColour (brightButtonColour.withAlpha (0.7f));
 | 
			
		||||
        else if (! button.isEnabled())
 | 
			
		||||
            g.setColour (disabledButtonColour);
 | 
			
		||||
        else
 | 
			
		||||
            g.setColour (brightButtonColour);
 | 
			
		||||
 | 
			
		||||
        g.fillRoundedRectangle (buttonRect, 5.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void drawButtonText (Graphics& g, TextButton& button, bool isMouseOverButton, bool isButtonDown) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (isMouseOverButton, isButtonDown);
 | 
			
		||||
 | 
			
		||||
        Font font (getTextButtonFont (button, button.getHeight()));
 | 
			
		||||
        g.setFont (font);
 | 
			
		||||
 | 
			
		||||
        if (button.isEnabled())
 | 
			
		||||
            g.setColour (Colours::white);
 | 
			
		||||
        else
 | 
			
		||||
            g.setColour (backgroundColour);
 | 
			
		||||
 | 
			
		||||
        g.drawFittedText (button.getButtonText(), 0, 0,
 | 
			
		||||
                          button.getWidth(),
 | 
			
		||||
                          button.getHeight(),
 | 
			
		||||
                          Justification::centred, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void drawLinearSlider (Graphics& g, int x, int y, int width, int height,
 | 
			
		||||
                           float sliderPos, float minSliderPos, float maxSliderPos,
 | 
			
		||||
                           const Slider::SliderStyle style, Slider& slider) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (style, minSliderPos, maxSliderPos);
 | 
			
		||||
 | 
			
		||||
        auto r = Rectangle<int> (x + haloRadius, y, width - (haloRadius * 2), height);
 | 
			
		||||
        auto backgroundBar = r.withSizeKeepingCentre(r.getWidth(), 2);
 | 
			
		||||
 | 
			
		||||
        sliderPos = (sliderPos - minSliderPos) / static_cast<float> (width);
 | 
			
		||||
 | 
			
		||||
        auto knobPos = static_cast<int> (sliderPos * (float) r.getWidth());
 | 
			
		||||
 | 
			
		||||
        g.setColour (sliderActivePart);
 | 
			
		||||
        g.fillRect (backgroundBar.removeFromLeft (knobPos));
 | 
			
		||||
 | 
			
		||||
        g.setColour (sliderInactivePart);
 | 
			
		||||
        g.fillRect (backgroundBar);
 | 
			
		||||
 | 
			
		||||
        if (slider.isMouseOverOrDragging())
 | 
			
		||||
        {
 | 
			
		||||
            auto haloBounds = r.withTrimmedLeft (knobPos - haloRadius)
 | 
			
		||||
                               .withWidth (haloRadius * 2)
 | 
			
		||||
                               .withSizeKeepingCentre (haloRadius * 2, haloRadius * 2);
 | 
			
		||||
 | 
			
		||||
            g.setColour (sliderActivePart.withAlpha (0.5f));
 | 
			
		||||
            g.fillEllipse (haloBounds.toFloat());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto knobRadius = slider.isMouseOverOrDragging() ? knobActiveRadius : knobInActiveRadius;
 | 
			
		||||
        auto knobBounds = r.withTrimmedLeft (knobPos - knobRadius)
 | 
			
		||||
                           .withWidth (knobRadius * 2)
 | 
			
		||||
                           .withSizeKeepingCentre (knobRadius * 2, knobRadius * 2);
 | 
			
		||||
 | 
			
		||||
        g.setColour (sliderActivePart);
 | 
			
		||||
        g.fillEllipse (knobBounds.toFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Font getTextButtonFont (TextButton& button, int buttonHeight) override
 | 
			
		||||
    {
 | 
			
		||||
        return LookAndFeel_V3::getTextButtonFont (button, buttonHeight).withHeight (buttonFontSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Font getLabelFont (Label& label) override
 | 
			
		||||
    {
 | 
			
		||||
        return LookAndFeel_V3::getLabelFont (label).withHeight (labelFontSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        labelFontSize  = 12,
 | 
			
		||||
        buttonFontSize = 15
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        knobActiveRadius   = 12,
 | 
			
		||||
        knobInActiveRadius = 8,
 | 
			
		||||
        haloRadius         = 18
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const Colour windowBackgroundColour = Colour (0xff262328);
 | 
			
		||||
    const Colour backgroundColour       = Colour (0xff4d4d4d);
 | 
			
		||||
    const Colour brightButtonColour     = Colour (0xff80cbc4);
 | 
			
		||||
    const Colour disabledButtonColour   = Colour (0xffe4e4e4);
 | 
			
		||||
    const Colour sliderInactivePart     = Colour (0xff545d62);
 | 
			
		||||
    const Colour sliderActivePart       = Colour (0xff80cbc4);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class AUv3SynthEditor   : public AudioProcessorEditor,
 | 
			
		||||
                          private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AUv3SynthEditor (AudioProcessor& processorIn)
 | 
			
		||||
        : AudioProcessorEditor (processorIn),
 | 
			
		||||
          roomSizeSlider (Slider::LinearHorizontal, Slider::NoTextBox)
 | 
			
		||||
    {
 | 
			
		||||
        setLookAndFeel (&materialLookAndFeel);
 | 
			
		||||
 | 
			
		||||
        roomSizeSlider.setValue (getParameterValue ("roomSize"), NotificationType::dontSendNotification);
 | 
			
		||||
 | 
			
		||||
        recordButton.onClick = [this] { startRecording(); };
 | 
			
		||||
        addAndMakeVisible (recordButton);
 | 
			
		||||
 | 
			
		||||
        roomSizeSlider.onValueChange = [this] { setParameterValue ("roomSize", (float) roomSizeSlider.getValue()); };
 | 
			
		||||
        roomSizeSlider.setRange (0.0, 1.0);
 | 
			
		||||
        addAndMakeVisible (roomSizeSlider);
 | 
			
		||||
 | 
			
		||||
        if (auto fileStream = createAssetInputStream ("proaudio.path"))
 | 
			
		||||
        {
 | 
			
		||||
            Path proAudioPath;
 | 
			
		||||
            proAudioPath.loadPathFromStream (*fileStream);
 | 
			
		||||
            proAudioIcon.setPath (proAudioPath);
 | 
			
		||||
            addAndMakeVisible (proAudioIcon);
 | 
			
		||||
 | 
			
		||||
            auto proAudioIconColour = findColour (TextButton::buttonOnColourId);
 | 
			
		||||
            proAudioIcon.setFill (FillType (proAudioIconColour));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setSize (600, 400);
 | 
			
		||||
        startTimer (100);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~AUv3SynthEditor() override
 | 
			
		||||
    {
 | 
			
		||||
        setLookAndFeel (nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        auto guiElementAreaHeight = r.getHeight() / 3;
 | 
			
		||||
 | 
			
		||||
        proAudioIcon.setTransformToFit (r.removeFromLeft (proportionOfWidth (0.25))
 | 
			
		||||
                                         .withSizeKeepingCentre (guiElementAreaHeight, guiElementAreaHeight)
 | 
			
		||||
                                         .toFloat(),
 | 
			
		||||
                                        RectanglePlacement::fillDestination);
 | 
			
		||||
 | 
			
		||||
        auto margin = guiElementAreaHeight / 4;
 | 
			
		||||
        r.reduce (margin, margin);
 | 
			
		||||
 | 
			
		||||
        auto buttonHeight = guiElementAreaHeight - margin;
 | 
			
		||||
 | 
			
		||||
        recordButton  .setBounds (r.removeFromTop (guiElementAreaHeight).withSizeKeepingCentre (r.getWidth(), buttonHeight));
 | 
			
		||||
        roomSizeSlider.setBounds (r.removeFromTop (guiElementAreaHeight).withSizeKeepingCentre (r.getWidth(), buttonHeight));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void startRecording()
 | 
			
		||||
    {
 | 
			
		||||
        recordButton.setEnabled (false);
 | 
			
		||||
        setParameterValue ("isRecording", 1.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        auto isRecordingNow = (getParameterValue ("isRecording") >= 0.5f);
 | 
			
		||||
 | 
			
		||||
        recordButton.setEnabled (! isRecordingNow);
 | 
			
		||||
        roomSizeSlider.setValue (getParameterValue ("roomSize"), NotificationType::dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorParameter* getParameter (const String& paramId)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* audioProcessor = getAudioProcessor())
 | 
			
		||||
        {
 | 
			
		||||
            auto& params = audioProcessor->getParameters();
 | 
			
		||||
 | 
			
		||||
            for (auto p : params)
 | 
			
		||||
            {
 | 
			
		||||
                if (auto* param = dynamic_cast<AudioProcessorParameterWithID*> (p))
 | 
			
		||||
                {
 | 
			
		||||
                    if (param->paramID == paramId)
 | 
			
		||||
                        return param;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    float getParameterValue (const String& paramId)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* param = getParameter (paramId))
 | 
			
		||||
            return param->getValue();
 | 
			
		||||
 | 
			
		||||
        return 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setParameterValue (const String& paramId, float value)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* param = getParameter (paramId))
 | 
			
		||||
            param->setValueNotifyingHost (value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MaterialLookAndFeel materialLookAndFeel;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    TextButton recordButton { "Record" };
 | 
			
		||||
    Slider roomSizeSlider;
 | 
			
		||||
    DrawablePath proAudioIcon;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AUv3SynthEditor)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class AUv3SynthProcessor : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    AUv3SynthProcessor ()
 | 
			
		||||
        : AudioProcessor (BusesProperties().withOutput ("Output", AudioChannelSet::stereo(), true)),
 | 
			
		||||
          currentRecording (1, 1), currentProgram (0)
 | 
			
		||||
    {
 | 
			
		||||
        // initialize parameters
 | 
			
		||||
        addParameter (isRecordingParam = new AudioParameterBool  ("isRecording", "Is Recording", false));
 | 
			
		||||
        addParameter (roomSizeParam    = new AudioParameterFloat ("roomSize", "Room Size", 0.0f, 1.0f, 0.5f));
 | 
			
		||||
 | 
			
		||||
        formatManager.registerBasicFormats();
 | 
			
		||||
 | 
			
		||||
        for (auto i = 0; i < maxNumVoices; ++i)
 | 
			
		||||
            synth.addVoice (new SamplerVoice());
 | 
			
		||||
 | 
			
		||||
        loadNewSample (createAssetInputStream ("singing.ogg"), "ogg");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        return (layouts.getMainOutputChannels() <= 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void prepareToPlay (double sampleRate, int estimatedMaxSizeOfBuffer) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (estimatedMaxSizeOfBuffer);
 | 
			
		||||
 | 
			
		||||
        lastSampleRate = sampleRate;
 | 
			
		||||
 | 
			
		||||
        currentRecording.setSize (1, static_cast<int> (std::ceil (maxDurationOfRecording * lastSampleRate)));
 | 
			
		||||
        samplesRecorded = 0;
 | 
			
		||||
 | 
			
		||||
        synth.setCurrentPlaybackSampleRate (lastSampleRate);
 | 
			
		||||
        reverb.setSampleRate (lastSampleRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
 | 
			
		||||
    {
 | 
			
		||||
        Reverb::Parameters reverbParameters;
 | 
			
		||||
        reverbParameters.roomSize = roomSizeParam->get();
 | 
			
		||||
 | 
			
		||||
        reverb.setParameters (reverbParameters);
 | 
			
		||||
        synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
 | 
			
		||||
 | 
			
		||||
        if (getMainBusNumOutputChannels() == 1)
 | 
			
		||||
            reverb.processMono (buffer.getWritePointer (0), buffer.getNumSamples());
 | 
			
		||||
        else if (getMainBusNumOutputChannels() == 2)
 | 
			
		||||
            reverb.processStereo (buffer.getWritePointer (0), buffer.getWritePointer (1), buffer.getNumSamples());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void releaseResources() override                                            { currentRecording.setSize (1, 1); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool acceptsMidi() const override                                           { return true; }
 | 
			
		||||
    bool producesMidi() const override                                          { return false; }
 | 
			
		||||
    double getTailLengthSeconds() const override                                { return 0.0; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override                               { return new AUv3SynthEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                                             { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                                       { return "AUv3 Synth"; }
 | 
			
		||||
    int getNumPrograms() override                                               { return 4; }
 | 
			
		||||
    int getCurrentProgram() override                                            { return currentProgram; }
 | 
			
		||||
    void setCurrentProgram (int index) override                                 { currentProgram = index; }
 | 
			
		||||
 | 
			
		||||
    const String getProgramName (int index) override
 | 
			
		||||
    {
 | 
			
		||||
        switch (index)
 | 
			
		||||
        {
 | 
			
		||||
            case 0:  return "Piano";
 | 
			
		||||
            case 1:  return "Singing";
 | 
			
		||||
            case 2:  return "Pinched Balloon";
 | 
			
		||||
            case 3:  return "Gazeebo";
 | 
			
		||||
            default: break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return "<Unknown>";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void changeProgramName (int /*index*/, const String& /*name*/) override     {}
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void getStateInformation (MemoryBlock& destData) override
 | 
			
		||||
    {
 | 
			
		||||
        MemoryOutputStream stream (destData, true);
 | 
			
		||||
 | 
			
		||||
        stream.writeFloat (*isRecordingParam);
 | 
			
		||||
        stream.writeFloat (*roomSizeParam);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int sizeInBytes) override
 | 
			
		||||
    {
 | 
			
		||||
        MemoryInputStream stream (data, static_cast<size_t> (sizeInBytes), false);
 | 
			
		||||
 | 
			
		||||
        isRecordingParam->setValueNotifyingHost (stream.readFloat());
 | 
			
		||||
        roomSizeParam->setValueNotifyingHost    (stream.readFloat());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void loadNewSampleBinary (const void* data, int dataSize, const char* format)
 | 
			
		||||
    {
 | 
			
		||||
        auto soundBuffer = std::make_unique<MemoryInputStream> (data, static_cast<std::size_t> (dataSize), false);
 | 
			
		||||
        loadNewSample (std::move (soundBuffer), format);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void loadNewSample (std::unique_ptr<InputStream> soundBuffer, const char* format)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<AudioFormatReader> formatReader (formatManager.findFormatForFileExtension (format)->createReaderFor (soundBuffer.release(), true));
 | 
			
		||||
 | 
			
		||||
        BigInteger midiNotes;
 | 
			
		||||
        midiNotes.setRange (0, 126, true);
 | 
			
		||||
        SynthesiserSound::Ptr newSound = new SamplerSound ("Voice", *formatReader, midiNotes, 0x40, 0.0, 0.0, 10.0);
 | 
			
		||||
        synth.removeSound (0);
 | 
			
		||||
        sound = newSound;
 | 
			
		||||
        synth.addSound (sound);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void swapSamples()
 | 
			
		||||
    {
 | 
			
		||||
        MemoryBlock mb;
 | 
			
		||||
        auto* stream = new MemoryOutputStream (mb, true);
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_ptr<AudioFormatWriter> writer (formatManager.findFormatForFileExtension ("wav")->createWriterFor (stream, lastSampleRate, 1, 16,
 | 
			
		||||
                                                                                                                          StringPairArray(), 0));
 | 
			
		||||
            writer->writeFromAudioSampleBuffer (currentRecording, 0, currentRecording.getNumSamples());
 | 
			
		||||
            writer->flush();
 | 
			
		||||
            stream->flush();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        loadNewSampleBinary (mb.getData(), static_cast<int> (mb.getSize()), "wav");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static constexpr int maxNumVoices = 5;
 | 
			
		||||
    static constexpr double maxDurationOfRecording = 1.0;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioFormatManager formatManager;
 | 
			
		||||
 | 
			
		||||
    int samplesRecorded;
 | 
			
		||||
    double lastSampleRate;
 | 
			
		||||
    AudioBuffer<float> currentRecording;
 | 
			
		||||
 | 
			
		||||
    Reverb reverb;
 | 
			
		||||
    Synthesiser synth;
 | 
			
		||||
    SynthesiserSound::Ptr sound;
 | 
			
		||||
 | 
			
		||||
    AudioParameterBool* isRecordingParam;
 | 
			
		||||
    AudioParameterFloat* roomSizeParam;
 | 
			
		||||
 | 
			
		||||
    int currentProgram;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AUv3SynthProcessor)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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:                  AUv3SynthPlugin
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           AUv3 synthesiser audio plugin.
 | 
			
		||||
 | 
			
		||||
 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, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:           JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:                  AudioProcessor
 | 
			
		||||
 mainClass:             AUv3SynthProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginIsSynth, pluginWantsMidiIn
 | 
			
		||||
 extraPluginFormats:    AUv3
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MaterialLookAndFeel : public LookAndFeel_V4
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MaterialLookAndFeel()
 | 
			
		||||
    {
 | 
			
		||||
        setColour (ResizableWindow::backgroundColourId, windowBackgroundColour);
 | 
			
		||||
        setColour (TextButton::buttonOnColourId,        brightButtonColour);
 | 
			
		||||
        setColour (TextButton::buttonColourId,          disabledButtonColour);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void drawButtonBackground (Graphics& g,
 | 
			
		||||
                               Button& button,
 | 
			
		||||
                               const Colour& /*backgroundColour*/,
 | 
			
		||||
                               bool /*isMouseOverButton*/,
 | 
			
		||||
                               bool isButtonDown) override
 | 
			
		||||
    {
 | 
			
		||||
        auto buttonRect = button.getLocalBounds().toFloat();
 | 
			
		||||
 | 
			
		||||
        if (isButtonDown)
 | 
			
		||||
            g.setColour (brightButtonColour.withAlpha (0.7f));
 | 
			
		||||
        else if (! button.isEnabled())
 | 
			
		||||
            g.setColour (disabledButtonColour);
 | 
			
		||||
        else
 | 
			
		||||
            g.setColour (brightButtonColour);
 | 
			
		||||
 | 
			
		||||
        g.fillRoundedRectangle (buttonRect, 5.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void drawButtonText (Graphics& g, TextButton& button, bool isMouseOverButton, bool isButtonDown) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (isMouseOverButton, isButtonDown);
 | 
			
		||||
 | 
			
		||||
        Font font (getTextButtonFont (button, button.getHeight()));
 | 
			
		||||
        g.setFont (font);
 | 
			
		||||
 | 
			
		||||
        if (button.isEnabled())
 | 
			
		||||
            g.setColour (Colours::white);
 | 
			
		||||
        else
 | 
			
		||||
            g.setColour (backgroundColour);
 | 
			
		||||
 | 
			
		||||
        g.drawFittedText (button.getButtonText(), 0, 0,
 | 
			
		||||
                          button.getWidth(),
 | 
			
		||||
                          button.getHeight(),
 | 
			
		||||
                          Justification::centred, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void drawLinearSlider (Graphics& g, int x, int y, int width, int height,
 | 
			
		||||
                           float sliderPos, float minSliderPos, float maxSliderPos,
 | 
			
		||||
                           const Slider::SliderStyle style, Slider& slider) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (style, minSliderPos, maxSliderPos);
 | 
			
		||||
 | 
			
		||||
        auto r = Rectangle<int> (x + haloRadius, y, width - (haloRadius * 2), height);
 | 
			
		||||
        auto backgroundBar = r.withSizeKeepingCentre(r.getWidth(), 2);
 | 
			
		||||
 | 
			
		||||
        sliderPos = (sliderPos - minSliderPos) / static_cast<float> (width);
 | 
			
		||||
 | 
			
		||||
        auto knobPos = static_cast<int> (sliderPos * (float) r.getWidth());
 | 
			
		||||
 | 
			
		||||
        g.setColour (sliderActivePart);
 | 
			
		||||
        g.fillRect (backgroundBar.removeFromLeft (knobPos));
 | 
			
		||||
 | 
			
		||||
        g.setColour (sliderInactivePart);
 | 
			
		||||
        g.fillRect (backgroundBar);
 | 
			
		||||
 | 
			
		||||
        if (slider.isMouseOverOrDragging())
 | 
			
		||||
        {
 | 
			
		||||
            auto haloBounds = r.withTrimmedLeft (knobPos - haloRadius)
 | 
			
		||||
                               .withWidth (haloRadius * 2)
 | 
			
		||||
                               .withSizeKeepingCentre (haloRadius * 2, haloRadius * 2);
 | 
			
		||||
 | 
			
		||||
            g.setColour (sliderActivePart.withAlpha (0.5f));
 | 
			
		||||
            g.fillEllipse (haloBounds.toFloat());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto knobRadius = slider.isMouseOverOrDragging() ? knobActiveRadius : knobInActiveRadius;
 | 
			
		||||
        auto knobBounds = r.withTrimmedLeft (knobPos - knobRadius)
 | 
			
		||||
                           .withWidth (knobRadius * 2)
 | 
			
		||||
                           .withSizeKeepingCentre (knobRadius * 2, knobRadius * 2);
 | 
			
		||||
 | 
			
		||||
        g.setColour (sliderActivePart);
 | 
			
		||||
        g.fillEllipse (knobBounds.toFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Font getTextButtonFont (TextButton& button, int buttonHeight) override
 | 
			
		||||
    {
 | 
			
		||||
        return LookAndFeel_V3::getTextButtonFont (button, buttonHeight).withHeight (buttonFontSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Font getLabelFont (Label& label) override
 | 
			
		||||
    {
 | 
			
		||||
        return LookAndFeel_V3::getLabelFont (label).withHeight (labelFontSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        labelFontSize  = 12,
 | 
			
		||||
        buttonFontSize = 15
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        knobActiveRadius   = 12,
 | 
			
		||||
        knobInActiveRadius = 8,
 | 
			
		||||
        haloRadius         = 18
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const Colour windowBackgroundColour = Colour (0xff262328);
 | 
			
		||||
    const Colour backgroundColour       = Colour (0xff4d4d4d);
 | 
			
		||||
    const Colour brightButtonColour     = Colour (0xff80cbc4);
 | 
			
		||||
    const Colour disabledButtonColour   = Colour (0xffe4e4e4);
 | 
			
		||||
    const Colour sliderInactivePart     = Colour (0xff545d62);
 | 
			
		||||
    const Colour sliderActivePart       = Colour (0xff80cbc4);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class AUv3SynthEditor   : public AudioProcessorEditor,
 | 
			
		||||
                          private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AUv3SynthEditor (AudioProcessor& processorIn)
 | 
			
		||||
        : AudioProcessorEditor (processorIn),
 | 
			
		||||
          roomSizeSlider (Slider::LinearHorizontal, Slider::NoTextBox)
 | 
			
		||||
    {
 | 
			
		||||
        setLookAndFeel (&materialLookAndFeel);
 | 
			
		||||
 | 
			
		||||
        roomSizeSlider.setValue (getParameterValue ("roomSize"), NotificationType::dontSendNotification);
 | 
			
		||||
 | 
			
		||||
        recordButton.onClick = [this] { startRecording(); };
 | 
			
		||||
        addAndMakeVisible (recordButton);
 | 
			
		||||
 | 
			
		||||
        roomSizeSlider.onValueChange = [this] { setParameterValue ("roomSize", (float) roomSizeSlider.getValue()); };
 | 
			
		||||
        roomSizeSlider.setRange (0.0, 1.0);
 | 
			
		||||
        addAndMakeVisible (roomSizeSlider);
 | 
			
		||||
 | 
			
		||||
        if (auto fileStream = createAssetInputStream ("proaudio.path"))
 | 
			
		||||
        {
 | 
			
		||||
            Path proAudioPath;
 | 
			
		||||
            proAudioPath.loadPathFromStream (*fileStream);
 | 
			
		||||
            proAudioIcon.setPath (proAudioPath);
 | 
			
		||||
            addAndMakeVisible (proAudioIcon);
 | 
			
		||||
 | 
			
		||||
            auto proAudioIconColour = findColour (TextButton::buttonOnColourId);
 | 
			
		||||
            proAudioIcon.setFill (FillType (proAudioIconColour));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setSize (600, 400);
 | 
			
		||||
        startTimer (100);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~AUv3SynthEditor() override
 | 
			
		||||
    {
 | 
			
		||||
        setLookAndFeel (nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        auto guiElementAreaHeight = r.getHeight() / 3;
 | 
			
		||||
 | 
			
		||||
        proAudioIcon.setTransformToFit (r.removeFromLeft (proportionOfWidth (0.25))
 | 
			
		||||
                                         .withSizeKeepingCentre (guiElementAreaHeight, guiElementAreaHeight)
 | 
			
		||||
                                         .toFloat(),
 | 
			
		||||
                                        RectanglePlacement::fillDestination);
 | 
			
		||||
 | 
			
		||||
        auto margin = guiElementAreaHeight / 4;
 | 
			
		||||
        r.reduce (margin, margin);
 | 
			
		||||
 | 
			
		||||
        auto buttonHeight = guiElementAreaHeight - margin;
 | 
			
		||||
 | 
			
		||||
        recordButton  .setBounds (r.removeFromTop (guiElementAreaHeight).withSizeKeepingCentre (r.getWidth(), buttonHeight));
 | 
			
		||||
        roomSizeSlider.setBounds (r.removeFromTop (guiElementAreaHeight).withSizeKeepingCentre (r.getWidth(), buttonHeight));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void startRecording()
 | 
			
		||||
    {
 | 
			
		||||
        recordButton.setEnabled (false);
 | 
			
		||||
        setParameterValue ("isRecording", 1.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        auto isRecordingNow = (getParameterValue ("isRecording") >= 0.5f);
 | 
			
		||||
 | 
			
		||||
        recordButton.setEnabled (! isRecordingNow);
 | 
			
		||||
        roomSizeSlider.setValue (getParameterValue ("roomSize"), NotificationType::dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorParameter* getParameter (const String& paramId)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* audioProcessor = getAudioProcessor())
 | 
			
		||||
        {
 | 
			
		||||
            auto& params = audioProcessor->getParameters();
 | 
			
		||||
 | 
			
		||||
            for (auto p : params)
 | 
			
		||||
            {
 | 
			
		||||
                if (auto* param = dynamic_cast<AudioProcessorParameterWithID*> (p))
 | 
			
		||||
                {
 | 
			
		||||
                    if (param->paramID == paramId)
 | 
			
		||||
                        return param;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    float getParameterValue (const String& paramId)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* param = getParameter (paramId))
 | 
			
		||||
            return param->getValue();
 | 
			
		||||
 | 
			
		||||
        return 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setParameterValue (const String& paramId, float value)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* param = getParameter (paramId))
 | 
			
		||||
            param->setValueNotifyingHost (value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MaterialLookAndFeel materialLookAndFeel;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    TextButton recordButton { "Record" };
 | 
			
		||||
    Slider roomSizeSlider;
 | 
			
		||||
    DrawablePath proAudioIcon;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AUv3SynthEditor)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class AUv3SynthProcessor : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    AUv3SynthProcessor ()
 | 
			
		||||
        : AudioProcessor (BusesProperties().withOutput ("Output", AudioChannelSet::stereo(), true)),
 | 
			
		||||
          currentRecording (1, 1), currentProgram (0)
 | 
			
		||||
    {
 | 
			
		||||
        // initialize parameters
 | 
			
		||||
        addParameter (isRecordingParam = new AudioParameterBool  ({ "isRecording", 1 }, "Is Recording", false));
 | 
			
		||||
        addParameter (roomSizeParam    = new AudioParameterFloat ({ "roomSize", 1 }, "Room Size", 0.0f, 1.0f, 0.5f));
 | 
			
		||||
 | 
			
		||||
        formatManager.registerBasicFormats();
 | 
			
		||||
 | 
			
		||||
        for (auto i = 0; i < maxNumVoices; ++i)
 | 
			
		||||
            synth.addVoice (new SamplerVoice());
 | 
			
		||||
 | 
			
		||||
        loadNewSample (createAssetInputStream ("singing.ogg"), "ogg");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        return (layouts.getMainOutputChannels() <= 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void prepareToPlay (double sampleRate, int estimatedMaxSizeOfBuffer) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (estimatedMaxSizeOfBuffer);
 | 
			
		||||
 | 
			
		||||
        lastSampleRate = sampleRate;
 | 
			
		||||
 | 
			
		||||
        currentRecording.setSize (1, static_cast<int> (std::ceil (maxDurationOfRecording * lastSampleRate)));
 | 
			
		||||
        samplesRecorded = 0;
 | 
			
		||||
 | 
			
		||||
        synth.setCurrentPlaybackSampleRate (lastSampleRate);
 | 
			
		||||
        reverb.setSampleRate (lastSampleRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
 | 
			
		||||
    {
 | 
			
		||||
        Reverb::Parameters reverbParameters;
 | 
			
		||||
        reverbParameters.roomSize = roomSizeParam->get();
 | 
			
		||||
 | 
			
		||||
        reverb.setParameters (reverbParameters);
 | 
			
		||||
        synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
 | 
			
		||||
 | 
			
		||||
        if (getMainBusNumOutputChannels() == 1)
 | 
			
		||||
            reverb.processMono (buffer.getWritePointer (0), buffer.getNumSamples());
 | 
			
		||||
        else if (getMainBusNumOutputChannels() == 2)
 | 
			
		||||
            reverb.processStereo (buffer.getWritePointer (0), buffer.getWritePointer (1), buffer.getNumSamples());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void releaseResources() override                                            { currentRecording.setSize (1, 1); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool acceptsMidi() const override                                           { return true; }
 | 
			
		||||
    bool producesMidi() const override                                          { return false; }
 | 
			
		||||
    double getTailLengthSeconds() const override                                { return 0.0; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override                               { return new AUv3SynthEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                                             { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                                       { return "AUv3 Synth"; }
 | 
			
		||||
    int getNumPrograms() override                                               { return 4; }
 | 
			
		||||
    int getCurrentProgram() override                                            { return currentProgram; }
 | 
			
		||||
    void setCurrentProgram (int index) override                                 { currentProgram = index; }
 | 
			
		||||
 | 
			
		||||
    const String getProgramName (int index) override
 | 
			
		||||
    {
 | 
			
		||||
        switch (index)
 | 
			
		||||
        {
 | 
			
		||||
            case 0:  return "Piano";
 | 
			
		||||
            case 1:  return "Singing";
 | 
			
		||||
            case 2:  return "Pinched Balloon";
 | 
			
		||||
            case 3:  return "Gazeebo";
 | 
			
		||||
            default: break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return "<Unknown>";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void changeProgramName (int /*index*/, const String& /*name*/) override     {}
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void getStateInformation (MemoryBlock& destData) override
 | 
			
		||||
    {
 | 
			
		||||
        MemoryOutputStream stream (destData, true);
 | 
			
		||||
 | 
			
		||||
        stream.writeFloat (*isRecordingParam);
 | 
			
		||||
        stream.writeFloat (*roomSizeParam);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int sizeInBytes) override
 | 
			
		||||
    {
 | 
			
		||||
        MemoryInputStream stream (data, static_cast<size_t> (sizeInBytes), false);
 | 
			
		||||
 | 
			
		||||
        isRecordingParam->setValueNotifyingHost (stream.readFloat());
 | 
			
		||||
        roomSizeParam->setValueNotifyingHost    (stream.readFloat());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void loadNewSampleBinary (const void* data, int dataSize, const char* format)
 | 
			
		||||
    {
 | 
			
		||||
        auto soundBuffer = std::make_unique<MemoryInputStream> (data, static_cast<std::size_t> (dataSize), false);
 | 
			
		||||
        loadNewSample (std::move (soundBuffer), format);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void loadNewSample (std::unique_ptr<InputStream> soundBuffer, const char* format)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<AudioFormatReader> formatReader (formatManager.findFormatForFileExtension (format)->createReaderFor (soundBuffer.release(), true));
 | 
			
		||||
 | 
			
		||||
        BigInteger midiNotes;
 | 
			
		||||
        midiNotes.setRange (0, 126, true);
 | 
			
		||||
        SynthesiserSound::Ptr newSound = new SamplerSound ("Voice", *formatReader, midiNotes, 0x40, 0.0, 0.0, 10.0);
 | 
			
		||||
        synth.removeSound (0);
 | 
			
		||||
        sound = newSound;
 | 
			
		||||
        synth.addSound (sound);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void swapSamples()
 | 
			
		||||
    {
 | 
			
		||||
        MemoryBlock mb;
 | 
			
		||||
        auto* stream = new MemoryOutputStream (mb, true);
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_ptr<AudioFormatWriter> writer (formatManager.findFormatForFileExtension ("wav")->createWriterFor (stream, lastSampleRate, 1, 16,
 | 
			
		||||
                                                                                                                          StringPairArray(), 0));
 | 
			
		||||
            writer->writeFromAudioSampleBuffer (currentRecording, 0, currentRecording.getNumSamples());
 | 
			
		||||
            writer->flush();
 | 
			
		||||
            stream->flush();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        loadNewSampleBinary (mb.getData(), static_cast<int> (mb.getSize()), "wav");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static constexpr int maxNumVoices = 5;
 | 
			
		||||
    static constexpr double maxDurationOfRecording = 1.0;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioFormatManager formatManager;
 | 
			
		||||
 | 
			
		||||
    int samplesRecorded;
 | 
			
		||||
    double lastSampleRate;
 | 
			
		||||
    AudioBuffer<float> currentRecording;
 | 
			
		||||
 | 
			
		||||
    Reverb reverb;
 | 
			
		||||
    Synthesiser synth;
 | 
			
		||||
    SynthesiserSound::Ptr sound;
 | 
			
		||||
 | 
			
		||||
    AudioParameterBool* isRecordingParam;
 | 
			
		||||
    AudioParameterFloat* roomSizeParam;
 | 
			
		||||
 | 
			
		||||
    int currentProgram;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AUv3SynthProcessor)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										332
									
								
								deps/juce/examples/Plugins/ArpeggiatorPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										332
									
								
								deps/juce/examples/Plugins/ArpeggiatorPluginDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,166 +1,166 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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:                  ArpeggiatorPlugin
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           Arpeggiator audio plugin.
 | 
			
		||||
 | 
			
		||||
 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, vs2019
 | 
			
		||||
 | 
			
		||||
 moduleFlags:           JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:                  AudioProcessor
 | 
			
		||||
 mainClass:             Arpeggiator
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginWantsMidiIn, pluginProducesMidiOut, pluginIsMidiEffectPlugin
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class Arpeggiator  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Arpeggiator()
 | 
			
		||||
        : AudioProcessor (BusesProperties()) // add no audio buses at all
 | 
			
		||||
    {
 | 
			
		||||
        addParameter (speed = new AudioParameterFloat ("speed", "Arpeggiator Speed", 0.0, 1.0, 0.5));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double sampleRate, int samplesPerBlock) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
        notes.clear();
 | 
			
		||||
        currentNote = 0;
 | 
			
		||||
        lastNoteValue = -1;
 | 
			
		||||
        time = 0;
 | 
			
		||||
        rate = static_cast<float> (sampleRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi) override
 | 
			
		||||
    {
 | 
			
		||||
        // the audio buffer in a midi effect will have zero channels!
 | 
			
		||||
        jassert (buffer.getNumChannels() == 0);
 | 
			
		||||
 | 
			
		||||
        // however we use the buffer to get timing information
 | 
			
		||||
        auto numSamples = buffer.getNumSamples();
 | 
			
		||||
 | 
			
		||||
        // get note duration
 | 
			
		||||
        auto noteDuration = static_cast<int> (std::ceil (rate * 0.25f * (0.1f + (1.0f - (*speed)))));
 | 
			
		||||
 | 
			
		||||
        for (const auto metadata : midi)
 | 
			
		||||
        {
 | 
			
		||||
            const auto msg = metadata.getMessage();
 | 
			
		||||
            if      (msg.isNoteOn())  notes.add (msg.getNoteNumber());
 | 
			
		||||
            else if (msg.isNoteOff()) notes.removeValue (msg.getNoteNumber());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        midi.clear();
 | 
			
		||||
 | 
			
		||||
        if ((time + numSamples) >= noteDuration)
 | 
			
		||||
        {
 | 
			
		||||
            auto offset = jmax (0, jmin ((int) (noteDuration - time), numSamples - 1));
 | 
			
		||||
 | 
			
		||||
            if (lastNoteValue > 0)
 | 
			
		||||
            {
 | 
			
		||||
                midi.addEvent (MidiMessage::noteOff (1, lastNoteValue), offset);
 | 
			
		||||
                lastNoteValue = -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (notes.size() > 0)
 | 
			
		||||
            {
 | 
			
		||||
                currentNote = (currentNote + 1) % notes.size();
 | 
			
		||||
                lastNoteValue = notes[currentNote];
 | 
			
		||||
                midi.addEvent (MidiMessage::noteOn  (1, lastNoteValue, (uint8) 127), offset);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        time = (time + numSamples) % noteDuration;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isMidiEffect() const override                     { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override          { return new GenericAudioProcessorEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                        { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                  { return "Arpeggiator"; }
 | 
			
		||||
 | 
			
		||||
    bool acceptsMidi() const override                      { return true; }
 | 
			
		||||
    bool producesMidi() const override                     { return true; }
 | 
			
		||||
    double getTailLengthSeconds() const override           { return 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 (*speed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int sizeInBytes) override
 | 
			
		||||
    {
 | 
			
		||||
        speed->setValueNotifyingHost (MemoryInputStream (data, static_cast<size_t> (sizeInBytes), false).readFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioParameterFloat* speed;
 | 
			
		||||
    int currentNote, lastNoteValue;
 | 
			
		||||
    int time;
 | 
			
		||||
    float rate;
 | 
			
		||||
    SortedSet<int> notes;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Arpeggiator)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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:                  ArpeggiatorPlugin
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           Arpeggiator audio plugin.
 | 
			
		||||
 | 
			
		||||
 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
 | 
			
		||||
 | 
			
		||||
 moduleFlags:           JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:                  AudioProcessor
 | 
			
		||||
 mainClass:             Arpeggiator
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginWantsMidiIn, pluginProducesMidiOut, pluginIsMidiEffectPlugin
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class Arpeggiator  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Arpeggiator()
 | 
			
		||||
        : AudioProcessor (BusesProperties()) // add no audio buses at all
 | 
			
		||||
    {
 | 
			
		||||
        addParameter (speed = new AudioParameterFloat ({ "speed", 1 }, "Arpeggiator Speed", 0.0, 1.0, 0.5));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double sampleRate, int samplesPerBlock) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
        notes.clear();
 | 
			
		||||
        currentNote = 0;
 | 
			
		||||
        lastNoteValue = -1;
 | 
			
		||||
        time = 0;
 | 
			
		||||
        rate = static_cast<float> (sampleRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi) override
 | 
			
		||||
    {
 | 
			
		||||
        // A pure MIDI plugin shouldn't be provided any audio data
 | 
			
		||||
        jassert (buffer.getNumChannels() == 0);
 | 
			
		||||
 | 
			
		||||
        // however we use the buffer to get timing information
 | 
			
		||||
        auto numSamples = buffer.getNumSamples();
 | 
			
		||||
 | 
			
		||||
        // get note duration
 | 
			
		||||
        auto noteDuration = static_cast<int> (std::ceil (rate * 0.25f * (0.1f + (1.0f - (*speed)))));
 | 
			
		||||
 | 
			
		||||
        for (const auto metadata : midi)
 | 
			
		||||
        {
 | 
			
		||||
            const auto msg = metadata.getMessage();
 | 
			
		||||
            if      (msg.isNoteOn())  notes.add (msg.getNoteNumber());
 | 
			
		||||
            else if (msg.isNoteOff()) notes.removeValue (msg.getNoteNumber());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        midi.clear();
 | 
			
		||||
 | 
			
		||||
        if ((time + numSamples) >= noteDuration)
 | 
			
		||||
        {
 | 
			
		||||
            auto offset = jmax (0, jmin ((int) (noteDuration - time), numSamples - 1));
 | 
			
		||||
 | 
			
		||||
            if (lastNoteValue > 0)
 | 
			
		||||
            {
 | 
			
		||||
                midi.addEvent (MidiMessage::noteOff (1, lastNoteValue), offset);
 | 
			
		||||
                lastNoteValue = -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (notes.size() > 0)
 | 
			
		||||
            {
 | 
			
		||||
                currentNote = (currentNote + 1) % notes.size();
 | 
			
		||||
                lastNoteValue = notes[currentNote];
 | 
			
		||||
                midi.addEvent (MidiMessage::noteOn  (1, lastNoteValue, (uint8) 127), offset);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        time = (time + numSamples) % noteDuration;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isMidiEffect() const override                     { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override          { return new GenericAudioProcessorEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                        { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                  { return "Arpeggiator"; }
 | 
			
		||||
 | 
			
		||||
    bool acceptsMidi() const override                      { return true; }
 | 
			
		||||
    bool producesMidi() const override                     { return true; }
 | 
			
		||||
    double getTailLengthSeconds() const override           { return 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 (*speed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int sizeInBytes) override
 | 
			
		||||
    {
 | 
			
		||||
        speed->setValueNotifyingHost (MemoryInputStream (data, static_cast<size_t> (sizeInBytes), false).readFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioParameterFloat* speed;
 | 
			
		||||
    int currentNote, lastNoteValue;
 | 
			
		||||
    int time;
 | 
			
		||||
    float rate;
 | 
			
		||||
    SortedSet<int> notes;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Arpeggiator)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1338
									
								
								deps/juce/examples/Plugins/AudioPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1338
									
								
								deps/juce/examples/Plugins/AudioPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										8
									
								
								deps/juce/examples/Plugins/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								deps/juce/examples/Plugins/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@@ -1,15 +1,15 @@
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
#
 | 
			
		||||
#  This file is part of the JUCE library.
 | 
			
		||||
#  Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
#  Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
#
 | 
			
		||||
#  JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
#  licensing.
 | 
			
		||||
#
 | 
			
		||||
#  By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
#  Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
#  By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
#  Agreement and JUCE Privacy Policy.
 | 
			
		||||
#
 | 
			
		||||
#  End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
#  End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
#  Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
#
 | 
			
		||||
#  Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4284
									
								
								deps/juce/examples/Plugins/DSPModulePluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4284
									
								
								deps/juce/examples/Plugins/DSPModulePluginDemo.h
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										244
									
								
								deps/juce/examples/Plugins/GainPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										244
									
								
								deps/juce/examples/Plugins/GainPluginDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,122 +1,122 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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:             GainPlugin
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Gain audio plugin.
 | 
			
		||||
 | 
			
		||||
 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, vs2019
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             AudioProcessor
 | 
			
		||||
 mainClass:        GainProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class GainProcessor  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    GainProcessor()
 | 
			
		||||
        : AudioProcessor (BusesProperties().withInput  ("Input",  AudioChannelSet::stereo())
 | 
			
		||||
                                           .withOutput ("Output", AudioChannelSet::stereo()))
 | 
			
		||||
    {
 | 
			
		||||
        addParameter (gain = new AudioParameterFloat ("gain", "Gain", 0.0f, 1.0f, 0.5f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double, int) override {}
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        buffer.applyGain (*gain);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<double>& buffer, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        buffer.applyGain ((float) *gain);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override          { return new GenericAudioProcessorEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                        { return true;   }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                  { return "Gain PlugIn"; }
 | 
			
		||||
    bool acceptsMidi() const override                      { return false; }
 | 
			
		||||
    bool producesMidi() const override                     { return false; }
 | 
			
		||||
    double getTailLengthSeconds() const override           { return 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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        const auto& mainInLayout  = layouts.getChannelSet (true,  0);
 | 
			
		||||
        const auto& mainOutLayout = layouts.getChannelSet (false, 0);
 | 
			
		||||
 | 
			
		||||
        return (mainInLayout == mainOutLayout && (! mainInLayout.isDisabled()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioParameterFloat* gain;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GainProcessor)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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:             GainPlugin
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Gain audio plugin.
 | 
			
		||||
 | 
			
		||||
 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
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             AudioProcessor
 | 
			
		||||
 mainClass:        GainProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class GainProcessor  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    GainProcessor()
 | 
			
		||||
        : AudioProcessor (BusesProperties().withInput  ("Input",  AudioChannelSet::stereo())
 | 
			
		||||
                                           .withOutput ("Output", AudioChannelSet::stereo()))
 | 
			
		||||
    {
 | 
			
		||||
        addParameter (gain = new AudioParameterFloat ({ "gain", 1 }, "Gain", 0.0f, 1.0f, 0.5f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double, int) override {}
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        buffer.applyGain (*gain);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<double>& buffer, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        buffer.applyGain ((float) *gain);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override          { return new GenericAudioProcessorEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                        { return true;   }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                  { return "Gain PlugIn"; }
 | 
			
		||||
    bool acceptsMidi() const override                      { return false; }
 | 
			
		||||
    bool producesMidi() const override                     { return false; }
 | 
			
		||||
    double getTailLengthSeconds() const override           { return 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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        const auto& mainInLayout  = layouts.getChannelSet (true,  0);
 | 
			
		||||
        const auto& mainOutLayout = layouts.getChannelSet (false, 0);
 | 
			
		||||
 | 
			
		||||
        return (mainInLayout == mainOutLayout && (! mainInLayout.isDisabled()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioParameterFloat* gain;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GainProcessor)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										588
									
								
								deps/juce/examples/Plugins/HostPluginDemo.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										588
									
								
								deps/juce/examples/Plugins/HostPluginDemo.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,588 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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:                  HostPluginDemo
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           Plugin that can host other plugins
 | 
			
		||||
 | 
			
		||||
 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
 | 
			
		||||
                        JUCE_PLUGINHOST_LV2=1
 | 
			
		||||
                        JUCE_PLUGINHOST_VST3=1
 | 
			
		||||
                        JUCE_PLUGINHOST_VST=0
 | 
			
		||||
                        JUCE_PLUGINHOST_AU=1
 | 
			
		||||
 | 
			
		||||
 type:                  AudioProcessor
 | 
			
		||||
 mainClass:             HostAudioProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginIsSynth, pluginWantsMidiIn, pluginProducesMidiOut,
 | 
			
		||||
                        pluginEditorRequiresKeys
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
enum class EditorStyle { thisWindow, newWindow };
 | 
			
		||||
 | 
			
		||||
class HostAudioProcessorImpl : public AudioProcessor,
 | 
			
		||||
                               private ChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    HostAudioProcessorImpl()
 | 
			
		||||
        : AudioProcessor (BusesProperties().withInput  ("Input",  AudioChannelSet::stereo(), true)
 | 
			
		||||
                                           .withOutput ("Output", AudioChannelSet::stereo(), true))
 | 
			
		||||
    {
 | 
			
		||||
        appProperties.setStorageParameters ([&]
 | 
			
		||||
        {
 | 
			
		||||
            PropertiesFile::Options opt;
 | 
			
		||||
            opt.applicationName = getName();
 | 
			
		||||
            opt.commonToAllUsers = false;
 | 
			
		||||
            opt.doNotSave = false;
 | 
			
		||||
            opt.filenameSuffix = ".props";
 | 
			
		||||
            opt.ignoreCaseOfKeyNames = false;
 | 
			
		||||
            opt.storageFormat = PropertiesFile::StorageFormat::storeAsXML;
 | 
			
		||||
            opt.osxLibrarySubFolder = "Application Support";
 | 
			
		||||
            return opt;
 | 
			
		||||
        }());
 | 
			
		||||
 | 
			
		||||
        pluginFormatManager.addDefaultFormats();
 | 
			
		||||
 | 
			
		||||
        if (auto savedPluginList = appProperties.getUserSettings()->getXmlValue ("pluginList"))
 | 
			
		||||
            pluginList.recreateFromXml (*savedPluginList);
 | 
			
		||||
 | 
			
		||||
        MessageManagerLock lock;
 | 
			
		||||
        pluginList.addChangeListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        const auto& mainOutput = layouts.getMainOutputChannelSet();
 | 
			
		||||
        const auto& mainInput  = layouts.getMainInputChannelSet();
 | 
			
		||||
 | 
			
		||||
        if (! mainInput.isDisabled() && mainInput != mainOutput)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (mainOutput.size() > 2)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void prepareToPlay (double sr, int bs) override
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
 | 
			
		||||
        active = true;
 | 
			
		||||
 | 
			
		||||
        if (inner != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            inner->setRateAndBufferSizeDetails (sr, bs);
 | 
			
		||||
            inner->prepareToPlay (sr, bs);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void releaseResources() override
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
 | 
			
		||||
        active = false;
 | 
			
		||||
 | 
			
		||||
        if (inner != nullptr)
 | 
			
		||||
            inner->releaseResources();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void reset() override
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
 | 
			
		||||
        if (inner != nullptr)
 | 
			
		||||
            inner->reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // In this example, we don't actually pass any audio through the inner processor.
 | 
			
		||||
    // In a 'real' plugin, we'd need to add some synchronisation to ensure that the inner
 | 
			
		||||
    // plugin instance was never modified (deleted, replaced etc.) during a call to processBlock.
 | 
			
		||||
    void processBlock (AudioBuffer<float>&, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        jassert (! isUsingDoublePrecision());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<double>&, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        jassert (isUsingDoublePrecision());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool hasEditor() const override                                   { return false; }
 | 
			
		||||
    AudioProcessorEditor* createEditor() override                     { return nullptr; }
 | 
			
		||||
 | 
			
		||||
    const String getName() const override                             { return "HostPluginDemo"; }
 | 
			
		||||
    bool acceptsMidi() const override                                 { return true; }
 | 
			
		||||
    bool producesMidi() const override                                { return true; }
 | 
			
		||||
    double getTailLengthSeconds() const override                      { return 0.0; }
 | 
			
		||||
 | 
			
		||||
    int getNumPrograms() override                                     { return 0; }
 | 
			
		||||
    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
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
 | 
			
		||||
        XmlElement xml ("state");
 | 
			
		||||
 | 
			
		||||
        if (inner != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            xml.setAttribute (editorStyleTag, (int) editorStyle);
 | 
			
		||||
            xml.addChildElement (inner->getPluginDescription().createXml().release());
 | 
			
		||||
            xml.addChildElement ([this]
 | 
			
		||||
            {
 | 
			
		||||
                MemoryBlock innerState;
 | 
			
		||||
                inner->getStateInformation (innerState);
 | 
			
		||||
 | 
			
		||||
                auto stateNode = std::make_unique<XmlElement> (innerStateTag);
 | 
			
		||||
                stateNode->addTextElement (innerState.toBase64Encoding());
 | 
			
		||||
                return stateNode.release();
 | 
			
		||||
            }());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const auto text = xml.toString();
 | 
			
		||||
        destData.replaceAll (text.toRawUTF8(), text.getNumBytesAsUTF8());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int sizeInBytes) override
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
 | 
			
		||||
        auto xml = XmlDocument::parse (String (CharPointer_UTF8 (static_cast<const char*> (data)), (size_t) sizeInBytes));
 | 
			
		||||
 | 
			
		||||
        if (auto* pluginNode = xml->getChildByName ("PLUGIN"))
 | 
			
		||||
        {
 | 
			
		||||
            PluginDescription pd;
 | 
			
		||||
            pd.loadFromXml (*pluginNode);
 | 
			
		||||
 | 
			
		||||
            MemoryBlock innerState;
 | 
			
		||||
            innerState.fromBase64Encoding (xml->getChildElementAllSubText (innerStateTag, {}));
 | 
			
		||||
 | 
			
		||||
            setNewPlugin (pd,
 | 
			
		||||
                          (EditorStyle) xml->getIntAttribute (editorStyleTag, 0),
 | 
			
		||||
                          innerState);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setNewPlugin (const PluginDescription& pd, EditorStyle where, const MemoryBlock& mb = {})
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
 | 
			
		||||
        const auto callback = [this, where, mb] (std::unique_ptr<AudioPluginInstance> instance, const String& error)
 | 
			
		||||
        {
 | 
			
		||||
            if (error.isNotEmpty())
 | 
			
		||||
            {
 | 
			
		||||
                NativeMessageBox::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                                       "Plugin Load Failed",
 | 
			
		||||
                                                       error,
 | 
			
		||||
                                                       nullptr,
 | 
			
		||||
                                                       nullptr);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            inner = std::move (instance);
 | 
			
		||||
            editorStyle = where;
 | 
			
		||||
 | 
			
		||||
            if (inner != nullptr && ! mb.isEmpty())
 | 
			
		||||
                inner->setStateInformation (mb.getData(), (int) mb.getSize());
 | 
			
		||||
 | 
			
		||||
            // In a 'real' plugin, we'd also need to set the bus configuration of the inner plugin.
 | 
			
		||||
            // One possibility would be to match the bus configuration of the wrapper plugin, but
 | 
			
		||||
            // the inner plugin isn't guaranteed to support the same layout. Alternatively, we
 | 
			
		||||
            // could try to apply a reasonably similar layout, and maintain a mapping between the
 | 
			
		||||
            // inner/outer channel layouts.
 | 
			
		||||
            //
 | 
			
		||||
            // In any case, it is essential that the inner plugin is told about the bus
 | 
			
		||||
            // configuration that will be used. The AudioBuffer passed to the inner plugin must also
 | 
			
		||||
            // exactly match this layout.
 | 
			
		||||
 | 
			
		||||
            if (active)
 | 
			
		||||
            {
 | 
			
		||||
                inner->setRateAndBufferSizeDetails (getSampleRate(), getBlockSize());
 | 
			
		||||
                inner->prepareToPlay (getSampleRate(), getBlockSize());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            NullCheckedInvocation::invoke (pluginChanged);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        pluginFormatManager.createPluginInstanceAsync (pd, getSampleRate(), getBlockSize(), callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void clearPlugin()
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
 | 
			
		||||
        inner = nullptr;
 | 
			
		||||
        NullCheckedInvocation::invoke (pluginChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isPluginLoaded() const
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
        return inner != nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AudioProcessorEditor> createInnerEditor() const
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock sl (innerMutex);
 | 
			
		||||
        return rawToUniquePtr (inner->hasEditor() ? inner->createEditorIfNeeded() : nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EditorStyle getEditorStyle() const noexcept { return editorStyle; }
 | 
			
		||||
 | 
			
		||||
    ApplicationProperties appProperties;
 | 
			
		||||
    AudioPluginFormatManager pluginFormatManager;
 | 
			
		||||
    KnownPluginList pluginList;
 | 
			
		||||
    std::function<void()> pluginChanged;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    CriticalSection innerMutex;
 | 
			
		||||
    std::unique_ptr<AudioPluginInstance> inner;
 | 
			
		||||
    EditorStyle editorStyle = EditorStyle{};
 | 
			
		||||
    bool active = false;
 | 
			
		||||
 | 
			
		||||
    static constexpr const char* innerStateTag = "inner_state";
 | 
			
		||||
    static constexpr const char* editorStyleTag = "editor_style";
 | 
			
		||||
 | 
			
		||||
    void changeListenerCallback (ChangeBroadcaster* source) override
 | 
			
		||||
    {
 | 
			
		||||
        if (source != &pluginList)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (auto savedPluginList = pluginList.createXml())
 | 
			
		||||
        {
 | 
			
		||||
            appProperties.getUserSettings()->setValue ("pluginList", savedPluginList.get());
 | 
			
		||||
            appProperties.saveIfNeeded();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
constexpr const char* HostAudioProcessorImpl::innerStateTag;
 | 
			
		||||
constexpr const char* HostAudioProcessorImpl::editorStyleTag;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
constexpr auto margin = 10;
 | 
			
		||||
 | 
			
		||||
static void doLayout (Component* main, Component& bottom, int bottomHeight, Rectangle<int> bounds)
 | 
			
		||||
{
 | 
			
		||||
    Grid grid;
 | 
			
		||||
    grid.setGap (Grid::Px { margin });
 | 
			
		||||
    grid.templateColumns = { Grid::TrackInfo { Grid::Fr { 1 } } };
 | 
			
		||||
    grid.templateRows = { Grid::TrackInfo { Grid::Fr { 1 } },
 | 
			
		||||
                          Grid::TrackInfo { Grid::Px { bottomHeight }} };
 | 
			
		||||
    grid.items = { GridItem { main }, GridItem { bottom }.withMargin ({ 0, margin, margin, margin }) };
 | 
			
		||||
    grid.performLayout (bounds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PluginLoaderComponent : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    template <typename Callback>
 | 
			
		||||
    PluginLoaderComponent (AudioPluginFormatManager& manager,
 | 
			
		||||
                           KnownPluginList& list,
 | 
			
		||||
                           Callback&& callback)
 | 
			
		||||
        : pluginListComponent (manager, list, {}, {})
 | 
			
		||||
    {
 | 
			
		||||
        pluginListComponent.getTableListBox().setMultipleSelectionEnabled (false);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (pluginListComponent);
 | 
			
		||||
        addAndMakeVisible (buttons);
 | 
			
		||||
 | 
			
		||||
        const auto getCallback = [this, &list, callback = std::forward<Callback> (callback)] (EditorStyle style)
 | 
			
		||||
        {
 | 
			
		||||
            return [this, &list, callback, style]
 | 
			
		||||
            {
 | 
			
		||||
                const auto index = pluginListComponent.getTableListBox().getSelectedRow();
 | 
			
		||||
                const auto& types = list.getTypes();
 | 
			
		||||
 | 
			
		||||
                if (isPositiveAndBelow (index, types.size()))
 | 
			
		||||
                    NullCheckedInvocation::invoke (callback, types.getReference (index), style);
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        buttons.thisWindowButton.onClick = getCallback (EditorStyle::thisWindow);
 | 
			
		||||
        buttons.newWindowButton .onClick = getCallback (EditorStyle::newWindow);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        doLayout (&pluginListComponent, buttons, 80, getLocalBounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct Buttons : public Component
 | 
			
		||||
    {
 | 
			
		||||
        Buttons()
 | 
			
		||||
        {
 | 
			
		||||
            label.setJustificationType (Justification::centred);
 | 
			
		||||
 | 
			
		||||
            addAndMakeVisible (label);
 | 
			
		||||
            addAndMakeVisible (thisWindowButton);
 | 
			
		||||
            addAndMakeVisible (newWindowButton);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void resized() override
 | 
			
		||||
        {
 | 
			
		||||
            Grid vertical;
 | 
			
		||||
            vertical.autoFlow = Grid::AutoFlow::row;
 | 
			
		||||
            vertical.setGap (Grid::Px { margin });
 | 
			
		||||
            vertical.autoRows = vertical.autoColumns = Grid::TrackInfo { Grid::Fr { 1 } };
 | 
			
		||||
            vertical.items.insertMultiple (0, GridItem{}, 2);
 | 
			
		||||
            vertical.performLayout (getLocalBounds());
 | 
			
		||||
 | 
			
		||||
            label.setBounds (vertical.items[0].currentBounds.toNearestInt());
 | 
			
		||||
 | 
			
		||||
            Grid grid;
 | 
			
		||||
            grid.autoFlow = Grid::AutoFlow::column;
 | 
			
		||||
            grid.setGap (Grid::Px { margin });
 | 
			
		||||
            grid.autoRows = grid.autoColumns = Grid::TrackInfo { Grid::Fr { 1 } };
 | 
			
		||||
            grid.items = { GridItem { thisWindowButton },
 | 
			
		||||
                           GridItem { newWindowButton } };
 | 
			
		||||
 | 
			
		||||
            grid.performLayout (vertical.items[1].currentBounds.toNearestInt());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Label label { "", "Select a plugin from the list, then display it using the buttons below." };
 | 
			
		||||
        TextButton thisWindowButton { "Open In This Window" };
 | 
			
		||||
        TextButton newWindowButton { "Open In New Window" };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    PluginListComponent pluginListComponent;
 | 
			
		||||
    Buttons buttons;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class PluginEditorComponent : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    template <typename Callback>
 | 
			
		||||
    PluginEditorComponent (std::unique_ptr<AudioProcessorEditor> editorIn, Callback&& onClose)
 | 
			
		||||
        : editor (std::move (editorIn))
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (editor.get());
 | 
			
		||||
        addAndMakeVisible (closeButton);
 | 
			
		||||
 | 
			
		||||
        childBoundsChanged (editor.get());
 | 
			
		||||
 | 
			
		||||
        closeButton.onClick = std::forward<Callback> (onClose);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setScaleFactor (float scale)
 | 
			
		||||
    {
 | 
			
		||||
        if (editor != nullptr)
 | 
			
		||||
            editor->setScaleFactor (scale);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        doLayout (editor.get(), closeButton, buttonHeight, getLocalBounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void childBoundsChanged (Component* child) override
 | 
			
		||||
    {
 | 
			
		||||
        if (child != editor.get())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        const auto size = editor != nullptr ? editor->getLocalBounds()
 | 
			
		||||
                                            : Rectangle<int>();
 | 
			
		||||
 | 
			
		||||
        setSize (size.getWidth(), margin + buttonHeight + size.getHeight());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static constexpr auto buttonHeight = 40;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AudioProcessorEditor> editor;
 | 
			
		||||
    TextButton closeButton { "Close Plugin" };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ScaledDocumentWindow : public DocumentWindow
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ScaledDocumentWindow (Colour bg, float scale)
 | 
			
		||||
        : DocumentWindow ("Editor", bg, 0), desktopScale (scale) {}
 | 
			
		||||
 | 
			
		||||
    float getDesktopScaleFactor() const override { return Desktop::getInstance().getGlobalScaleFactor() * desktopScale; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    float desktopScale = 1.0f;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class HostAudioProcessorEditor  : public AudioProcessorEditor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    explicit HostAudioProcessorEditor (HostAudioProcessorImpl& owner)
 | 
			
		||||
        : AudioProcessorEditor (owner),
 | 
			
		||||
          hostProcessor (owner),
 | 
			
		||||
          loader (owner.pluginFormatManager,
 | 
			
		||||
                  owner.pluginList,
 | 
			
		||||
                  [&owner] (const PluginDescription& pd,
 | 
			
		||||
                            EditorStyle editorStyle)
 | 
			
		||||
                  {
 | 
			
		||||
                      owner.setNewPlugin (pd, editorStyle);
 | 
			
		||||
                  }),
 | 
			
		||||
          scopedCallback (owner.pluginChanged, [this] { pluginChanged(); })
 | 
			
		||||
    {
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
        setResizable (false, false);
 | 
			
		||||
        addAndMakeVisible (closeButton);
 | 
			
		||||
        addAndMakeVisible (loader);
 | 
			
		||||
 | 
			
		||||
        hostProcessor.pluginChanged();
 | 
			
		||||
 | 
			
		||||
        closeButton.onClick = [this] { clearPlugin(); };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId).darker());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        closeButton.setBounds (getLocalBounds().withSizeKeepingCentre (200, buttonHeight));
 | 
			
		||||
        loader.setBounds (getLocalBounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void childBoundsChanged (Component* child) override
 | 
			
		||||
    {
 | 
			
		||||
        if (child != editor.get())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        const auto size = editor != nullptr ? editor->getLocalBounds()
 | 
			
		||||
                                            : Rectangle<int>();
 | 
			
		||||
 | 
			
		||||
        setSize (size.getWidth(), size.getHeight());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setScaleFactor (float scale) override
 | 
			
		||||
    {
 | 
			
		||||
        currentScaleFactor = scale;
 | 
			
		||||
        AudioProcessorEditor::setScaleFactor (scale);
 | 
			
		||||
 | 
			
		||||
        const auto posted = MessageManager::callAsync ([ref = SafePointer<HostAudioProcessorEditor> (this), scale]
 | 
			
		||||
        {
 | 
			
		||||
            if (auto* r = ref.getComponent())
 | 
			
		||||
                if (auto* e = r->currentEditorComponent)
 | 
			
		||||
                    e->setScaleFactor (scale);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        jassertquiet (posted);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void pluginChanged()
 | 
			
		||||
    {
 | 
			
		||||
        loader.setVisible (! hostProcessor.isPluginLoaded());
 | 
			
		||||
        closeButton.setVisible (hostProcessor.isPluginLoaded());
 | 
			
		||||
 | 
			
		||||
        if (hostProcessor.isPluginLoaded())
 | 
			
		||||
        {
 | 
			
		||||
            auto editorComponent = std::make_unique<PluginEditorComponent> (hostProcessor.createInnerEditor(), [this]
 | 
			
		||||
            {
 | 
			
		||||
                const auto posted = MessageManager::callAsync ([this] { clearPlugin(); });
 | 
			
		||||
                jassertquiet (posted);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            editorComponent->setScaleFactor (currentScaleFactor);
 | 
			
		||||
            currentEditorComponent = editorComponent.get();
 | 
			
		||||
 | 
			
		||||
            editor = [&]() -> std::unique_ptr<Component>
 | 
			
		||||
            {
 | 
			
		||||
                switch (hostProcessor.getEditorStyle())
 | 
			
		||||
                {
 | 
			
		||||
                    case EditorStyle::thisWindow:
 | 
			
		||||
                        addAndMakeVisible (editorComponent.get());
 | 
			
		||||
                        setSize (editorComponent->getWidth(), editorComponent->getHeight());
 | 
			
		||||
                        return std::move (editorComponent);
 | 
			
		||||
 | 
			
		||||
                    case EditorStyle::newWindow:
 | 
			
		||||
                        const auto bg = getLookAndFeel().findColour (ResizableWindow::backgroundColourId).darker();
 | 
			
		||||
                        auto window = std::make_unique<ScaledDocumentWindow> (bg, currentScaleFactor);
 | 
			
		||||
                        window->setAlwaysOnTop (true);
 | 
			
		||||
                        window->setContentOwned (editorComponent.release(), true);
 | 
			
		||||
                        window->centreAroundComponent (this, window->getWidth(), window->getHeight());
 | 
			
		||||
                        window->setVisible (true);
 | 
			
		||||
                        return window;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                jassertfalse;
 | 
			
		||||
                return nullptr;
 | 
			
		||||
            }();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            editor = nullptr;
 | 
			
		||||
            setSize (500, 500);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void clearPlugin()
 | 
			
		||||
    {
 | 
			
		||||
        currentEditorComponent = nullptr;
 | 
			
		||||
        editor = nullptr;
 | 
			
		||||
        hostProcessor.clearPlugin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static constexpr auto buttonHeight = 30;
 | 
			
		||||
 | 
			
		||||
    HostAudioProcessorImpl& hostProcessor;
 | 
			
		||||
    PluginLoaderComponent loader;
 | 
			
		||||
    std::unique_ptr<Component> editor;
 | 
			
		||||
    PluginEditorComponent* currentEditorComponent = nullptr;
 | 
			
		||||
    ScopedValueSetter<std::function<void()>> scopedCallback;
 | 
			
		||||
    TextButton closeButton { "Close Plugin" };
 | 
			
		||||
    float currentScaleFactor = 1.0f;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class HostAudioProcessor : public HostAudioProcessorImpl
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    bool hasEditor() const override { return true; }
 | 
			
		||||
    AudioProcessorEditor* createEditor() override { return new HostAudioProcessorEditor (*this); }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,529 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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:               InterAppAudioEffectPlugin
 | 
			
		||||
 version:            1.0.0
 | 
			
		||||
 vendor:             JUCE
 | 
			
		||||
 website:            http://juce.com
 | 
			
		||||
 description:        Inter-app audio effect plugin.
 | 
			
		||||
 | 
			
		||||
 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_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:        JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:               AudioProcessor
 | 
			
		||||
 mainClass:          IAAEffectProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:       1
 | 
			
		||||
 | 
			
		||||
 extraPluginFormats: IAA
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// A very simple decaying meter.
 | 
			
		||||
class SimpleMeter  : public Component,
 | 
			
		||||
                     private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    SimpleMeter()
 | 
			
		||||
    {
 | 
			
		||||
        startTimerHz (30);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (Colours::transparentBlack);
 | 
			
		||||
 | 
			
		||||
        auto area = g.getClipBounds();
 | 
			
		||||
        g.setColour (getLookAndFeel().findColour (Slider::thumbColourId));
 | 
			
		||||
        g.fillRoundedRectangle (area.toFloat(), 6.0);
 | 
			
		||||
 | 
			
		||||
        auto unfilledHeight = area.getHeight() * (1.0 - level);
 | 
			
		||||
        g.reduceClipRegion (area.getX(), area.getY(),
 | 
			
		||||
                            area.getWidth(), (int) unfilledHeight);
 | 
			
		||||
        g.setColour (getLookAndFeel().findColour (Slider::trackColourId));
 | 
			
		||||
        g.fillRoundedRectangle (area.toFloat(), 6.0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    // Called from the audio thread.
 | 
			
		||||
    void update (float newLevel)
 | 
			
		||||
    {
 | 
			
		||||
        // We don't care if maxLevel gets set to zero (in timerCallback) between the
 | 
			
		||||
        // load and the assignment.
 | 
			
		||||
        maxLevel = jmax (maxLevel.load(), newLevel);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        auto callbackLevel = maxLevel.exchange (0.0);
 | 
			
		||||
 | 
			
		||||
        float decayFactor = 0.95f;
 | 
			
		||||
 | 
			
		||||
        if (callbackLevel > level)
 | 
			
		||||
            level = callbackLevel;
 | 
			
		||||
        else if (level > 0.001)
 | 
			
		||||
            level *= decayFactor;
 | 
			
		||||
        else
 | 
			
		||||
            level = 0;
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::atomic<float> maxLevel {0.0};
 | 
			
		||||
    float level = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleMeter)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// A simple Inter-App Audio plug-in with a gain control and some meters.
 | 
			
		||||
class IAAEffectProcessor  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    IAAEffectProcessor()
 | 
			
		||||
         : AudioProcessor (BusesProperties()
 | 
			
		||||
                           .withInput  ("Input",  AudioChannelSet::stereo(), true)
 | 
			
		||||
                           .withOutput ("Output", AudioChannelSet::stereo(), true)),
 | 
			
		||||
           parameters (*this, nullptr, "InterAppAudioEffect",
 | 
			
		||||
                       { std::make_unique<AudioParameterFloat> ("gain", "Gain", NormalisableRange<float> (0.0f, 1.0f), 1.0f / 3.14f) })
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double, int) override
 | 
			
		||||
    {
 | 
			
		||||
        previousGain = *parameters.getRawParameterValue ("gain");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        if (layouts.getMainInputChannels() > 2)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        float gain = *parameters.getRawParameterValue ("gain");
 | 
			
		||||
 | 
			
		||||
        auto totalNumInputChannels  = getTotalNumInputChannels();
 | 
			
		||||
        auto totalNumOutputChannels = getTotalNumOutputChannels();
 | 
			
		||||
 | 
			
		||||
        auto numSamples = buffer.getNumSamples();
 | 
			
		||||
 | 
			
		||||
        for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
 | 
			
		||||
            buffer.clear (i, 0, buffer.getNumSamples());
 | 
			
		||||
 | 
			
		||||
        // Apply the gain to the samples using a ramp to avoid discontinuities in
 | 
			
		||||
        // the audio between processed buffers.
 | 
			
		||||
        for (auto channel = 0; channel < totalNumInputChannels; ++channel)
 | 
			
		||||
        {
 | 
			
		||||
            buffer.applyGainRamp (channel, 0, numSamples, previousGain, gain);
 | 
			
		||||
            auto newLevel = buffer.getMagnitude (channel, 0, numSamples);
 | 
			
		||||
 | 
			
		||||
            meterListeners.call ([=] (MeterListener& l) { l.handleNewMeterValue (channel, newLevel); });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        previousGain = gain;
 | 
			
		||||
 | 
			
		||||
        // Now ask the host for the current time so we can store it to be displayed later.
 | 
			
		||||
        updateCurrentTimeInfoFromHost (lastPosInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override
 | 
			
		||||
    {
 | 
			
		||||
        return new IAAEffectEditor (*this, parameters);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool hasEditor() const override                                    { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                              { return "InterAppAudioEffectPlugin"; }
 | 
			
		||||
    bool acceptsMidi() const override                                  { return false; }
 | 
			
		||||
    bool producesMidi() 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
 | 
			
		||||
    {
 | 
			
		||||
        if (auto xml = parameters.state.createXml())
 | 
			
		||||
            copyXmlToBinary (*xml, destData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int sizeInBytes) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto xmlState = getXmlFromBinary (data, sizeInBytes))
 | 
			
		||||
            if (xmlState->hasTagName (parameters.state.getType()))
 | 
			
		||||
                parameters.state = ValueTree::fromXml (*xmlState);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool updateCurrentTimeInfoFromHost (AudioPlayHead::CurrentPositionInfo& posInfo)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* ph = getPlayHead())
 | 
			
		||||
        {
 | 
			
		||||
            AudioPlayHead::CurrentPositionInfo newTime;
 | 
			
		||||
 | 
			
		||||
            if (ph->getCurrentPosition (newTime))
 | 
			
		||||
            {
 | 
			
		||||
                posInfo = newTime;  // Successfully got the current time from the host.
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If the host fails to provide the current time, we'll just reset our copy to a default.
 | 
			
		||||
        lastPosInfo.resetToDefault();
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Allow an IAAAudioProcessorEditor to register as a listener to receive new
 | 
			
		||||
    // meter values directly from the audio thread.
 | 
			
		||||
    struct MeterListener
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~MeterListener() {}
 | 
			
		||||
 | 
			
		||||
        virtual void handleNewMeterValue (int, float) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void addMeterListener    (MeterListener& listener) { meterListeners.add    (&listener); }
 | 
			
		||||
    void removeMeterListener (MeterListener& listener) { meterListeners.remove (&listener); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class IAAEffectEditor  : public AudioProcessorEditor,
 | 
			
		||||
                             private IAAEffectProcessor::MeterListener,
 | 
			
		||||
                             private Timer
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        IAAEffectEditor (IAAEffectProcessor& p,
 | 
			
		||||
                         AudioProcessorValueTreeState& vts)
 | 
			
		||||
            : AudioProcessorEditor (p),
 | 
			
		||||
              iaaEffectProcessor (p),
 | 
			
		||||
              parameters (vts)
 | 
			
		||||
        {
 | 
			
		||||
            // Register for meter value updates.
 | 
			
		||||
            iaaEffectProcessor.addMeterListener (*this);
 | 
			
		||||
 | 
			
		||||
            gainSlider.setSliderStyle (Slider::SliderStyle::LinearVertical);
 | 
			
		||||
            gainSlider.setTextBoxStyle (Slider::TextEntryBoxPosition::TextBoxAbove, false, 60, 20);
 | 
			
		||||
            addAndMakeVisible (gainSlider);
 | 
			
		||||
 | 
			
		||||
            for (auto& meter : meters)
 | 
			
		||||
                addAndMakeVisible (meter);
 | 
			
		||||
 | 
			
		||||
            // Configure all the graphics for the transport control.
 | 
			
		||||
 | 
			
		||||
            transportText.setFont (Font (Font::getDefaultMonospacedFontName(), 18.0f, Font::plain));
 | 
			
		||||
            transportText.setJustificationType (Justification::topLeft);
 | 
			
		||||
            addChildComponent (transportText);
 | 
			
		||||
 | 
			
		||||
            Path rewindShape;
 | 
			
		||||
            rewindShape.addRectangle (0.0, 0.0, 5.0, buttonSize);
 | 
			
		||||
            rewindShape.addTriangle (0.0, buttonSize * 0.5f, buttonSize, 0.0, buttonSize, buttonSize);
 | 
			
		||||
            rewindButton.setShape (rewindShape, true, true, false);
 | 
			
		||||
            rewindButton.onClick = [this]
 | 
			
		||||
            {
 | 
			
		||||
                if (transportControllable())
 | 
			
		||||
                    iaaEffectProcessor.getPlayHead()->transportRewind();
 | 
			
		||||
            };
 | 
			
		||||
            addChildComponent (rewindButton);
 | 
			
		||||
 | 
			
		||||
            Path playShape;
 | 
			
		||||
            playShape.addTriangle (0.0, 0.0, 0.0, buttonSize, buttonSize, buttonSize / 2);
 | 
			
		||||
            playButton.setShape (playShape, true, true, false);
 | 
			
		||||
            playButton.onClick = [this]
 | 
			
		||||
            {
 | 
			
		||||
                if (transportControllable())
 | 
			
		||||
                    iaaEffectProcessor.getPlayHead()->transportPlay (! lastPosInfo.isPlaying);
 | 
			
		||||
            };
 | 
			
		||||
            addChildComponent (playButton);
 | 
			
		||||
 | 
			
		||||
            Path recordShape;
 | 
			
		||||
            recordShape.addEllipse (0.0, 0.0, buttonSize, buttonSize);
 | 
			
		||||
            recordButton.setShape (recordShape, true, true, false);
 | 
			
		||||
            recordButton.onClick = [this]
 | 
			
		||||
            {
 | 
			
		||||
                if (transportControllable())
 | 
			
		||||
                    iaaEffectProcessor.getPlayHead()->transportRecord (! lastPosInfo.isRecording);
 | 
			
		||||
            };
 | 
			
		||||
            addChildComponent (recordButton);
 | 
			
		||||
 | 
			
		||||
            // Configure the switch to host button.
 | 
			
		||||
 | 
			
		||||
            switchToHostButtonLabel.setFont (Font (Font::getDefaultMonospacedFontName(), 18.0f, Font::plain));
 | 
			
		||||
            switchToHostButtonLabel.setJustificationType (Justification::centredRight);
 | 
			
		||||
            switchToHostButtonLabel.setText ("Switch to\nhost app:", dontSendNotification);
 | 
			
		||||
            addChildComponent (switchToHostButtonLabel);
 | 
			
		||||
 | 
			
		||||
            switchToHostButton.onClick = [this]
 | 
			
		||||
            {
 | 
			
		||||
                if (transportControllable())
 | 
			
		||||
                {
 | 
			
		||||
                    PluginHostType hostType;
 | 
			
		||||
                    hostType.switchToHostApplication();
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            addChildComponent (switchToHostButton);
 | 
			
		||||
 | 
			
		||||
            auto screenSize = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
 | 
			
		||||
            setSize (screenSize.getWidth(), screenSize.getHeight());
 | 
			
		||||
 | 
			
		||||
            resized();
 | 
			
		||||
 | 
			
		||||
            startTimerHz (60);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ~IAAEffectEditor() override
 | 
			
		||||
        {
 | 
			
		||||
            iaaEffectProcessor.removeMeterListener (*this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        void paint (Graphics& g) override
 | 
			
		||||
        {
 | 
			
		||||
            g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void resized() override
 | 
			
		||||
        {
 | 
			
		||||
            auto area = getBounds().reduced (20);
 | 
			
		||||
 | 
			
		||||
            gainSlider.setBounds (area.removeFromLeft (60));
 | 
			
		||||
 | 
			
		||||
            for (auto& meter : meters)
 | 
			
		||||
            {
 | 
			
		||||
                area.removeFromLeft (10);
 | 
			
		||||
                meter.setBounds (area.removeFromLeft (20));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            area.removeFromLeft (20);
 | 
			
		||||
            transportText.setBounds (area.removeFromTop (120));
 | 
			
		||||
 | 
			
		||||
            auto navigationArea = area.removeFromTop ((int) buttonSize);
 | 
			
		||||
            rewindButton.setTopLeftPosition (navigationArea.getPosition());
 | 
			
		||||
            navigationArea.removeFromLeft ((int) buttonSize + 10);
 | 
			
		||||
            playButton.setTopLeftPosition (navigationArea.getPosition());
 | 
			
		||||
            navigationArea.removeFromLeft ((int) buttonSize + 10);
 | 
			
		||||
            recordButton.setTopLeftPosition (navigationArea.getPosition());
 | 
			
		||||
 | 
			
		||||
            area.removeFromTop (30);
 | 
			
		||||
 | 
			
		||||
            auto appSwitchArea = area.removeFromTop ((int) buttonSize);
 | 
			
		||||
            switchToHostButtonLabel.setBounds (appSwitchArea.removeFromLeft (100));
 | 
			
		||||
            appSwitchArea.removeFromLeft (5);
 | 
			
		||||
            switchToHostButton.setBounds (appSwitchArea.removeFromLeft ((int) buttonSize));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        // Called from the audio thread.
 | 
			
		||||
        void handleNewMeterValue (int channel, float value) override
 | 
			
		||||
        {
 | 
			
		||||
            meters[(size_t) channel].update (value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        void timerCallback () override
 | 
			
		||||
        {
 | 
			
		||||
            auto timeInfoSuccess = iaaEffectProcessor.updateCurrentTimeInfoFromHost (lastPosInfo);
 | 
			
		||||
            transportText.setVisible (timeInfoSuccess);
 | 
			
		||||
 | 
			
		||||
            if (timeInfoSuccess)
 | 
			
		||||
                updateTransportTextDisplay();
 | 
			
		||||
 | 
			
		||||
            updateTransportButtonsDisplay();
 | 
			
		||||
 | 
			
		||||
            updateSwitchToHostDisplay();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        bool transportControllable()
 | 
			
		||||
        {
 | 
			
		||||
            auto processorPlayHead = iaaEffectProcessor.getPlayHead();
 | 
			
		||||
            return processorPlayHead != nullptr && processorPlayHead->canControlTransport();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        // quick-and-dirty function to format a timecode string
 | 
			
		||||
        String timeToTimecodeString (double seconds)
 | 
			
		||||
        {
 | 
			
		||||
            auto millisecs = roundToInt (seconds * 1000.0);
 | 
			
		||||
            auto absMillisecs = std::abs (millisecs);
 | 
			
		||||
 | 
			
		||||
            return String::formatted ("%02d:%02d:%02d.%03d",
 | 
			
		||||
                                      millisecs / 360000,
 | 
			
		||||
                                      (absMillisecs / 60000) % 60,
 | 
			
		||||
                                      (absMillisecs / 1000) % 60,
 | 
			
		||||
                                      absMillisecs % 1000);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // A quick-and-dirty function to format a bars/beats string.
 | 
			
		||||
        String quarterNotePositionToBarsBeatsString (double quarterNotes, int numerator, int denominator)
 | 
			
		||||
        {
 | 
			
		||||
            if (numerator == 0 || denominator == 0)
 | 
			
		||||
                return "1|1|000";
 | 
			
		||||
 | 
			
		||||
            auto quarterNotesPerBar = (numerator * 4 / denominator);
 | 
			
		||||
            auto beats  = (fmod (quarterNotes, quarterNotesPerBar) / quarterNotesPerBar) * numerator;
 | 
			
		||||
 | 
			
		||||
            auto bar    = ((int) quarterNotes) / quarterNotesPerBar + 1;
 | 
			
		||||
            auto beat   = ((int) beats) + 1;
 | 
			
		||||
            auto ticks  = ((int) (fmod (beats, 1.0) * 960.0 + 0.5));
 | 
			
		||||
 | 
			
		||||
            return String::formatted ("%d|%d|%03d", bar, beat, ticks);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void updateTransportTextDisplay()
 | 
			
		||||
        {
 | 
			
		||||
            MemoryOutputStream displayText;
 | 
			
		||||
 | 
			
		||||
            displayText << "[" << SystemStats::getJUCEVersion() << "]\n"
 | 
			
		||||
                        << String (lastPosInfo.bpm, 2) << " bpm\n"
 | 
			
		||||
                        << lastPosInfo.timeSigNumerator << '/' << lastPosInfo.timeSigDenominator << "\n"
 | 
			
		||||
                        << timeToTimecodeString (lastPosInfo.timeInSeconds) << "\n"
 | 
			
		||||
                        << quarterNotePositionToBarsBeatsString (lastPosInfo.ppqPosition,
 | 
			
		||||
                                                     lastPosInfo.timeSigNumerator,
 | 
			
		||||
                                                     lastPosInfo.timeSigDenominator) << "\n";
 | 
			
		||||
 | 
			
		||||
            if (lastPosInfo.isRecording)
 | 
			
		||||
                displayText << "(recording)";
 | 
			
		||||
            else if (lastPosInfo.isPlaying)
 | 
			
		||||
                displayText << "(playing)";
 | 
			
		||||
 | 
			
		||||
            transportText.setText (displayText.toString(), dontSendNotification);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void updateTransportButtonsDisplay()
 | 
			
		||||
        {
 | 
			
		||||
            auto visible = iaaEffectProcessor.getPlayHead() != nullptr
 | 
			
		||||
                        && iaaEffectProcessor.getPlayHead()->canControlTransport();
 | 
			
		||||
 | 
			
		||||
            if (rewindButton.isVisible() != visible)
 | 
			
		||||
            {
 | 
			
		||||
                rewindButton.setVisible (visible);
 | 
			
		||||
                playButton.setVisible   (visible);
 | 
			
		||||
                recordButton.setVisible (visible);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (visible)
 | 
			
		||||
            {
 | 
			
		||||
                auto playColour = lastPosInfo.isPlaying ? Colours::green : defaultButtonColour;
 | 
			
		||||
                playButton.setColours (playColour, playColour, playColour);
 | 
			
		||||
                playButton.repaint();
 | 
			
		||||
 | 
			
		||||
                auto recordColour = lastPosInfo.isRecording ? Colours::red : defaultButtonColour;
 | 
			
		||||
                recordButton.setColours (recordColour, recordColour, recordColour);
 | 
			
		||||
                recordButton.repaint();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void updateSwitchToHostDisplay()
 | 
			
		||||
        {
 | 
			
		||||
            PluginHostType hostType;
 | 
			
		||||
            auto visible = hostType.isInterAppAudioConnected();
 | 
			
		||||
 | 
			
		||||
            if (switchToHostButtonLabel.isVisible() != visible)
 | 
			
		||||
            {
 | 
			
		||||
                switchToHostButtonLabel.setVisible (visible);
 | 
			
		||||
                switchToHostButton.setVisible (visible);
 | 
			
		||||
 | 
			
		||||
                if (visible)
 | 
			
		||||
                {
 | 
			
		||||
                    auto icon = hostType.getHostIcon ((int) buttonSize);
 | 
			
		||||
                    switchToHostButton.setImages(false, true, true,
 | 
			
		||||
                                                 icon, 1.0, Colours::transparentBlack,
 | 
			
		||||
                                                 icon, 1.0, Colours::transparentBlack,
 | 
			
		||||
                                                 icon, 1.0, Colours::transparentBlack);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IAAEffectProcessor& iaaEffectProcessor;
 | 
			
		||||
        AudioProcessorValueTreeState& parameters;
 | 
			
		||||
 | 
			
		||||
        const float buttonSize = 30.0f;
 | 
			
		||||
        const Colour defaultButtonColour = Colours::darkgrey;
 | 
			
		||||
        ShapeButton rewindButton {"Rewind", defaultButtonColour, defaultButtonColour, defaultButtonColour};
 | 
			
		||||
        ShapeButton playButton   {"Play",   defaultButtonColour, defaultButtonColour, defaultButtonColour};
 | 
			
		||||
        ShapeButton recordButton {"Record", defaultButtonColour, defaultButtonColour, defaultButtonColour};
 | 
			
		||||
 | 
			
		||||
        Slider gainSlider;
 | 
			
		||||
        AudioProcessorValueTreeState::SliderAttachment gainAttachment = { parameters, "gain", gainSlider };
 | 
			
		||||
 | 
			
		||||
        std::array<SimpleMeter, 2> meters;
 | 
			
		||||
 | 
			
		||||
        ImageButton switchToHostButton;
 | 
			
		||||
        Label transportText, switchToHostButtonLabel;
 | 
			
		||||
 | 
			
		||||
        AudioPlayHead::CurrentPositionInfo lastPosInfo;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IAAEffectEditor)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorValueTreeState parameters;
 | 
			
		||||
    float previousGain = 0.0f;
 | 
			
		||||
 | 
			
		||||
    // This keeps a copy of the last set of timing info that was acquired during an
 | 
			
		||||
    // audio callback - the UI component will display this.
 | 
			
		||||
    AudioPlayHead::CurrentPositionInfo lastPosInfo;
 | 
			
		||||
 | 
			
		||||
    ListenerList<MeterListener> meterListeners;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IAAEffectProcessor)
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										696
									
								
								deps/juce/examples/Plugins/MidiLoggerPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										696
									
								
								deps/juce/examples/Plugins/MidiLoggerPluginDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,345 +1,351 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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:                  MIDILogger
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           Logs incoming MIDI messages.
 | 
			
		||||
 | 
			
		||||
 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, vs2019, linux_make
 | 
			
		||||
 | 
			
		||||
 moduleFlags:           JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:                  AudioProcessor
 | 
			
		||||
 mainClass:             MidiLoggerPluginDemoProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginWantsMidiIn, pluginProducesMidiOut, pluginIsMidiEffectPlugin
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <iterator>
 | 
			
		||||
 | 
			
		||||
class MidiQueue
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    void push (const MidiBuffer& buffer)
 | 
			
		||||
    {
 | 
			
		||||
        for (const auto metadata : buffer)
 | 
			
		||||
            fifo.write (1).forEach ([&] (int dest) { messages[(size_t) dest] = metadata.getMessage(); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename OutputIt>
 | 
			
		||||
    void pop (OutputIt out)
 | 
			
		||||
    {
 | 
			
		||||
        fifo.read (fifo.getNumReady()).forEach ([&] (int source) { *out++ = messages[(size_t) source]; });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static constexpr auto queueSize = 1 << 14;
 | 
			
		||||
    AbstractFifo fifo { queueSize };
 | 
			
		||||
    std::vector<MidiMessage> messages = std::vector<MidiMessage> (queueSize);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Stores the last N messages. Safe to access from the message thread only.
 | 
			
		||||
class MidiListModel
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    template <typename It>
 | 
			
		||||
    void addMessages (It begin, It end)
 | 
			
		||||
    {
 | 
			
		||||
        const auto numNewMessages = (int) std::distance (begin, end);
 | 
			
		||||
        const auto numToAdd = juce::jmin (numToStore, numNewMessages);
 | 
			
		||||
        const auto numToRemove = jmax (0, (int) messages.size() + numToAdd - numToStore);
 | 
			
		||||
        messages.erase (messages.begin(), std::next (messages.begin(), numToRemove));
 | 
			
		||||
        messages.insert (messages.end(), std::prev (end, numToAdd), end);
 | 
			
		||||
 | 
			
		||||
        if (onChange != nullptr)
 | 
			
		||||
            onChange();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void clear()
 | 
			
		||||
    {
 | 
			
		||||
        messages.clear();
 | 
			
		||||
 | 
			
		||||
        if (onChange != nullptr)
 | 
			
		||||
            onChange();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const MidiMessage& operator[] (size_t ind) const     { return messages[ind]; }
 | 
			
		||||
 | 
			
		||||
    size_t size() const                                  { return messages.size(); }
 | 
			
		||||
 | 
			
		||||
    std::function<void()> onChange;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static constexpr auto numToStore = 1000;
 | 
			
		||||
    std::vector<MidiMessage> messages;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MidiTable  : public Component,
 | 
			
		||||
                   private TableListBoxModel
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    MidiTable (MidiListModel& m)
 | 
			
		||||
        : messages (m)
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (table);
 | 
			
		||||
 | 
			
		||||
        table.setModel (this);
 | 
			
		||||
        table.setClickingTogglesRowSelection (false);
 | 
			
		||||
        table.setHeader ([&]
 | 
			
		||||
        {
 | 
			
		||||
            auto header = std::make_unique<TableHeaderComponent>();
 | 
			
		||||
            header->addColumn ("Message", messageColumn, 200, 30, -1, TableHeaderComponent::notSortable);
 | 
			
		||||
            header->addColumn ("Channel", channelColumn, 100, 30, -1, TableHeaderComponent::notSortable);
 | 
			
		||||
            header->addColumn ("Data",    dataColumn,    200, 30, -1, TableHeaderComponent::notSortable);
 | 
			
		||||
            return header;
 | 
			
		||||
        }());
 | 
			
		||||
 | 
			
		||||
        messages.onChange = [&] { table.updateContent(); };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~MidiTable() override { messages.onChange = nullptr; }
 | 
			
		||||
 | 
			
		||||
    void resized() override { table.setBounds (getLocalBounds()); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        messageColumn = 1,
 | 
			
		||||
        channelColumn,
 | 
			
		||||
        dataColumn
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    int getNumRows() override          { return (int) messages.size(); }
 | 
			
		||||
 | 
			
		||||
    void paintRowBackground (Graphics&, int, int, int, bool) override {}
 | 
			
		||||
    void paintCell (Graphics&, int, int, int, int, bool)     override {}
 | 
			
		||||
 | 
			
		||||
    Component* refreshComponentForCell (int rowNumber,
 | 
			
		||||
                                        int columnId,
 | 
			
		||||
                                        bool,
 | 
			
		||||
                                        Component* existingComponentToUpdate) override
 | 
			
		||||
    {
 | 
			
		||||
        delete existingComponentToUpdate;
 | 
			
		||||
 | 
			
		||||
        const auto index = (int) messages.size() - 1 - rowNumber;
 | 
			
		||||
        const auto message = messages[(size_t) index];
 | 
			
		||||
 | 
			
		||||
        return new Label ({}, [&]
 | 
			
		||||
        {
 | 
			
		||||
            switch (columnId)
 | 
			
		||||
            {
 | 
			
		||||
                case messageColumn: return getEventString (message);
 | 
			
		||||
                case channelColumn: return String (message.getChannel());
 | 
			
		||||
                case dataColumn:    return getDataString (message);
 | 
			
		||||
                default:            break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            jassertfalse;
 | 
			
		||||
            return String();
 | 
			
		||||
        }());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getEventString (const MidiMessage& m)
 | 
			
		||||
    {
 | 
			
		||||
        if (m.isNoteOn())           return "Note on";
 | 
			
		||||
        if (m.isNoteOff())          return "Note off";
 | 
			
		||||
        if (m.isProgramChange())    return "Program change";
 | 
			
		||||
        if (m.isPitchWheel())       return "Pitch wheel";
 | 
			
		||||
        if (m.isAftertouch())       return "Aftertouch";
 | 
			
		||||
        if (m.isChannelPressure())  return "Channel pressure";
 | 
			
		||||
        if (m.isAllNotesOff())      return "All notes off";
 | 
			
		||||
        if (m.isAllSoundOff())      return "All sound off";
 | 
			
		||||
        if (m.isMetaEvent())        return "Meta event";
 | 
			
		||||
 | 
			
		||||
        if (m.isController())
 | 
			
		||||
        {
 | 
			
		||||
            const auto* name = MidiMessage::getControllerName (m.getControllerNumber());
 | 
			
		||||
            return "Controller " + (name == nullptr ? String (m.getControllerNumber()) : String (name));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return String::toHexString (m.getRawData(), m.getRawDataSize());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getDataString (const MidiMessage& m)
 | 
			
		||||
    {
 | 
			
		||||
        if (m.isNoteOn())           return MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) + " Velocity " + String (m.getVelocity());
 | 
			
		||||
        if (m.isNoteOff())          return MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) + " Velocity " + String (m.getVelocity());
 | 
			
		||||
        if (m.isProgramChange())    return String (m.getProgramChangeNumber());
 | 
			
		||||
        if (m.isPitchWheel())       return String (m.getPitchWheelValue());
 | 
			
		||||
        if (m.isAftertouch())       return MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) +  ": " + String (m.getAfterTouchValue());
 | 
			
		||||
        if (m.isChannelPressure())  return String (m.getChannelPressureValue());
 | 
			
		||||
        if (m.isController())       return String (m.getControllerValue());
 | 
			
		||||
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MidiListModel& messages;
 | 
			
		||||
    TableListBox table;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MidiLoggerPluginDemoProcessor  : public AudioProcessor,
 | 
			
		||||
                                       private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    MidiLoggerPluginDemoProcessor()
 | 
			
		||||
        : AudioProcessor (getBusesLayout())
 | 
			
		||||
    {
 | 
			
		||||
        state.addChild ({ "uiState", { { "width",  500 }, { "height", 300 } }, {} }, -1, nullptr);
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~MidiLoggerPluginDemoProcessor() override { stopTimer(); }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& audio,  MidiBuffer& midi) override { process (audio, midi); }
 | 
			
		||||
    void processBlock (AudioBuffer<double>& audio, MidiBuffer& midi) override { process (audio, midi); }
 | 
			
		||||
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout&) const override           { return true; }
 | 
			
		||||
    bool isMidiEffect() const override                                        { return true; }
 | 
			
		||||
    bool hasEditor() const override                                           { return true; }
 | 
			
		||||
    AudioProcessorEditor* createEditor() override                             { return new Editor (*this); }
 | 
			
		||||
 | 
			
		||||
    const String getName() const override                                     { return "MIDI Logger"; }
 | 
			
		||||
    bool acceptsMidi() const override                                         { return true; }
 | 
			
		||||
    bool producesMidi() const override                                        { return true; }
 | 
			
		||||
    double getTailLengthSeconds() const override                              { return 0.0; }
 | 
			
		||||
 | 
			
		||||
    int getNumPrograms() override                                             { return 0; }
 | 
			
		||||
    int getCurrentProgram() override                                          { return 0; }
 | 
			
		||||
    void setCurrentProgram (int) override                                     {}
 | 
			
		||||
    const String getProgramName (int) override                                { return "None"; }
 | 
			
		||||
    void changeProgramName (int, const String&) override                      {}
 | 
			
		||||
 | 
			
		||||
    void prepareToPlay (double, int) override                                 {}
 | 
			
		||||
    void releaseResources() override                                          {}
 | 
			
		||||
 | 
			
		||||
    void getStateInformation (MemoryBlock& destData) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto xmlState = state.createXml())
 | 
			
		||||
            copyXmlToBinary (*xmlState, destData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int size) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto xmlState = getXmlFromBinary (data, size))
 | 
			
		||||
            state = ValueTree::fromXml (*xmlState);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    class Editor  : public AudioProcessorEditor,
 | 
			
		||||
                    private Value::Listener
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        explicit Editor (MidiLoggerPluginDemoProcessor& ownerIn)
 | 
			
		||||
            : AudioProcessorEditor (ownerIn),
 | 
			
		||||
              owner (ownerIn),
 | 
			
		||||
              table (owner.model)
 | 
			
		||||
        {
 | 
			
		||||
            addAndMakeVisible (table);
 | 
			
		||||
            addAndMakeVisible (clearButton);
 | 
			
		||||
 | 
			
		||||
            setResizable (true, true);
 | 
			
		||||
            lastUIWidth .referTo (owner.state.getChildWithName ("uiState").getPropertyAsValue ("width",  nullptr));
 | 
			
		||||
            lastUIHeight.referTo (owner.state.getChildWithName ("uiState").getPropertyAsValue ("height", nullptr));
 | 
			
		||||
            setSize (lastUIWidth.getValue(), lastUIHeight.getValue());
 | 
			
		||||
 | 
			
		||||
            lastUIWidth. addListener (this);
 | 
			
		||||
            lastUIHeight.addListener (this);
 | 
			
		||||
 | 
			
		||||
            clearButton.onClick = [&] { owner.model.clear(); };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void paint (Graphics& g) override
 | 
			
		||||
        {
 | 
			
		||||
            g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void resized() override
 | 
			
		||||
        {
 | 
			
		||||
            auto bounds = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
            clearButton.setBounds (bounds.removeFromBottom (30).withSizeKeepingCentre (50, 24));
 | 
			
		||||
            table.setBounds (bounds);
 | 
			
		||||
 | 
			
		||||
            lastUIWidth  = getWidth();
 | 
			
		||||
            lastUIHeight = getHeight();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        void valueChanged (Value&) override
 | 
			
		||||
        {
 | 
			
		||||
            setSize (lastUIWidth.getValue(), lastUIHeight.getValue());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MidiLoggerPluginDemoProcessor& owner;
 | 
			
		||||
 | 
			
		||||
        MidiTable table;
 | 
			
		||||
        TextButton clearButton { "Clear" };
 | 
			
		||||
 | 
			
		||||
        Value lastUIWidth, lastUIHeight;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<MidiMessage> messages;
 | 
			
		||||
        queue.pop (std::back_inserter (messages));
 | 
			
		||||
        model.addMessages (messages.begin(), messages.end());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename Element>
 | 
			
		||||
    void process (AudioBuffer<Element>& audio, MidiBuffer& midi)
 | 
			
		||||
    {
 | 
			
		||||
        audio.clear();
 | 
			
		||||
        queue.push (midi);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static BusesProperties getBusesLayout()
 | 
			
		||||
    {
 | 
			
		||||
        // Live doesn't like to load midi-only plugins, so we add an audio output there.
 | 
			
		||||
        return PluginHostType().isAbletonLive() ? BusesProperties().withOutput ("out", AudioChannelSet::stereo())
 | 
			
		||||
                                                : BusesProperties();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ValueTree state { "state" };
 | 
			
		||||
    MidiQueue queue;
 | 
			
		||||
    MidiListModel model; // The data to show in the UI. We keep it around in the processor so that
 | 
			
		||||
                         // the view is persistent even when the plugin UI is closed and reopened.
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiLoggerPluginDemoProcessor)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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:                  MIDILogger
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           Logs incoming MIDI messages.
 | 
			
		||||
 | 
			
		||||
 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:             MidiLoggerPluginDemoProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginWantsMidiIn, pluginProducesMidiOut, pluginIsMidiEffectPlugin
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <iterator>
 | 
			
		||||
 | 
			
		||||
class MidiQueue
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    void push (const MidiBuffer& buffer)
 | 
			
		||||
    {
 | 
			
		||||
        for (const auto metadata : buffer)
 | 
			
		||||
            fifo.write (1).forEach ([&] (int dest) { messages[(size_t) dest] = metadata.getMessage(); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename OutputIt>
 | 
			
		||||
    void pop (OutputIt out)
 | 
			
		||||
    {
 | 
			
		||||
        fifo.read (fifo.getNumReady()).forEach ([&] (int source) { *out++ = messages[(size_t) source]; });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static constexpr auto queueSize = 1 << 14;
 | 
			
		||||
    AbstractFifo fifo { queueSize };
 | 
			
		||||
    std::vector<MidiMessage> messages = std::vector<MidiMessage> (queueSize);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Stores the last N messages. Safe to access from the message thread only.
 | 
			
		||||
class MidiListModel
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    template <typename It>
 | 
			
		||||
    void addMessages (It begin, It end)
 | 
			
		||||
    {
 | 
			
		||||
        if (begin == end)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        const auto numNewMessages = (int) std::distance (begin, end);
 | 
			
		||||
        const auto numToAdd = juce::jmin (numToStore, numNewMessages);
 | 
			
		||||
        const auto numToRemove = jmax (0, (int) messages.size() + numToAdd - numToStore);
 | 
			
		||||
        messages.erase (messages.begin(), std::next (messages.begin(), numToRemove));
 | 
			
		||||
        messages.insert (messages.end(), std::prev (end, numToAdd), end);
 | 
			
		||||
 | 
			
		||||
        if (onChange != nullptr)
 | 
			
		||||
            onChange();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void clear()
 | 
			
		||||
    {
 | 
			
		||||
        messages.clear();
 | 
			
		||||
 | 
			
		||||
        if (onChange != nullptr)
 | 
			
		||||
            onChange();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const MidiMessage& operator[] (size_t ind) const     { return messages[ind]; }
 | 
			
		||||
 | 
			
		||||
    size_t size() const                                  { return messages.size(); }
 | 
			
		||||
 | 
			
		||||
    std::function<void()> onChange;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static constexpr auto numToStore = 1000;
 | 
			
		||||
    std::vector<MidiMessage> messages;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MidiTable  : public Component,
 | 
			
		||||
                   private TableListBoxModel
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    MidiTable (MidiListModel& m)
 | 
			
		||||
        : messages (m)
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (table);
 | 
			
		||||
 | 
			
		||||
        table.setModel (this);
 | 
			
		||||
        table.setClickingTogglesRowSelection (false);
 | 
			
		||||
        table.setHeader ([&]
 | 
			
		||||
        {
 | 
			
		||||
            auto header = std::make_unique<TableHeaderComponent>();
 | 
			
		||||
            header->addColumn ("Message", messageColumn, 200, 30, -1, TableHeaderComponent::notSortable);
 | 
			
		||||
            header->addColumn ("Time",    timeColumn,    100, 30, -1, TableHeaderComponent::notSortable);
 | 
			
		||||
            header->addColumn ("Channel", channelColumn, 100, 30, -1, TableHeaderComponent::notSortable);
 | 
			
		||||
            header->addColumn ("Data",    dataColumn,    200, 30, -1, TableHeaderComponent::notSortable);
 | 
			
		||||
            return header;
 | 
			
		||||
        }());
 | 
			
		||||
 | 
			
		||||
        messages.onChange = [&] { table.updateContent(); };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~MidiTable() override { messages.onChange = nullptr; }
 | 
			
		||||
 | 
			
		||||
    void resized() override { table.setBounds (getLocalBounds()); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        messageColumn = 1,
 | 
			
		||||
        timeColumn,
 | 
			
		||||
        channelColumn,
 | 
			
		||||
        dataColumn
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    int getNumRows() override          { return (int) messages.size(); }
 | 
			
		||||
 | 
			
		||||
    void paintRowBackground (Graphics&, int, int, int, bool) override {}
 | 
			
		||||
    void paintCell (Graphics&, int, int, int, int, bool)     override {}
 | 
			
		||||
 | 
			
		||||
    Component* refreshComponentForCell (int rowNumber,
 | 
			
		||||
                                        int columnId,
 | 
			
		||||
                                        bool,
 | 
			
		||||
                                        Component* existingComponentToUpdate) override
 | 
			
		||||
    {
 | 
			
		||||
        delete existingComponentToUpdate;
 | 
			
		||||
 | 
			
		||||
        const auto index = (int) messages.size() - 1 - rowNumber;
 | 
			
		||||
        const auto message = messages[(size_t) index];
 | 
			
		||||
 | 
			
		||||
        return new Label ({}, [&]
 | 
			
		||||
        {
 | 
			
		||||
            switch (columnId)
 | 
			
		||||
            {
 | 
			
		||||
                case messageColumn: return getEventString (message);
 | 
			
		||||
                case timeColumn:    return String (message.getTimeStamp());
 | 
			
		||||
                case channelColumn: return String (message.getChannel());
 | 
			
		||||
                case dataColumn:    return getDataString (message);
 | 
			
		||||
                default:            break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            jassertfalse;
 | 
			
		||||
            return String();
 | 
			
		||||
        }());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getEventString (const MidiMessage& m)
 | 
			
		||||
    {
 | 
			
		||||
        if (m.isNoteOn())           return "Note on";
 | 
			
		||||
        if (m.isNoteOff())          return "Note off";
 | 
			
		||||
        if (m.isProgramChange())    return "Program change";
 | 
			
		||||
        if (m.isPitchWheel())       return "Pitch wheel";
 | 
			
		||||
        if (m.isAftertouch())       return "Aftertouch";
 | 
			
		||||
        if (m.isChannelPressure())  return "Channel pressure";
 | 
			
		||||
        if (m.isAllNotesOff())      return "All notes off";
 | 
			
		||||
        if (m.isAllSoundOff())      return "All sound off";
 | 
			
		||||
        if (m.isMetaEvent())        return "Meta event";
 | 
			
		||||
 | 
			
		||||
        if (m.isController())
 | 
			
		||||
        {
 | 
			
		||||
            const auto* name = MidiMessage::getControllerName (m.getControllerNumber());
 | 
			
		||||
            return "Controller " + (name == nullptr ? String (m.getControllerNumber()) : String (name));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return String::toHexString (m.getRawData(), m.getRawDataSize());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getDataString (const MidiMessage& m)
 | 
			
		||||
    {
 | 
			
		||||
        if (m.isNoteOn())           return MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) + " Velocity " + String (m.getVelocity());
 | 
			
		||||
        if (m.isNoteOff())          return MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) + " Velocity " + String (m.getVelocity());
 | 
			
		||||
        if (m.isProgramChange())    return String (m.getProgramChangeNumber());
 | 
			
		||||
        if (m.isPitchWheel())       return String (m.getPitchWheelValue());
 | 
			
		||||
        if (m.isAftertouch())       return MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) +  ": " + String (m.getAfterTouchValue());
 | 
			
		||||
        if (m.isChannelPressure())  return String (m.getChannelPressureValue());
 | 
			
		||||
        if (m.isController())       return String (m.getControllerValue());
 | 
			
		||||
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MidiListModel& messages;
 | 
			
		||||
    TableListBox table;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MidiLoggerPluginDemoProcessor  : public AudioProcessor,
 | 
			
		||||
                                       private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    MidiLoggerPluginDemoProcessor()
 | 
			
		||||
        : AudioProcessor (getBusesLayout())
 | 
			
		||||
    {
 | 
			
		||||
        state.addChild ({ "uiState", { { "width",  600 }, { "height", 300 } }, {} }, -1, nullptr);
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~MidiLoggerPluginDemoProcessor() override { stopTimer(); }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& audio,  MidiBuffer& midi) override { process (audio, midi); }
 | 
			
		||||
    void processBlock (AudioBuffer<double>& audio, MidiBuffer& midi) override { process (audio, midi); }
 | 
			
		||||
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout&) const override           { return true; }
 | 
			
		||||
    bool isMidiEffect() const override                                        { return true; }
 | 
			
		||||
    bool hasEditor() const override                                           { return true; }
 | 
			
		||||
    AudioProcessorEditor* createEditor() override                             { return new Editor (*this); }
 | 
			
		||||
 | 
			
		||||
    const String getName() const override                                     { return "MIDI Logger"; }
 | 
			
		||||
    bool acceptsMidi() const override                                         { return true; }
 | 
			
		||||
    bool producesMidi() const override                                        { return true; }
 | 
			
		||||
    double getTailLengthSeconds() const override                              { return 0.0; }
 | 
			
		||||
 | 
			
		||||
    int getNumPrograms() override                                             { return 0; }
 | 
			
		||||
    int getCurrentProgram() override                                          { return 0; }
 | 
			
		||||
    void setCurrentProgram (int) override                                     {}
 | 
			
		||||
    const String getProgramName (int) override                                { return "None"; }
 | 
			
		||||
    void changeProgramName (int, const String&) override                      {}
 | 
			
		||||
 | 
			
		||||
    void prepareToPlay (double, int) override                                 {}
 | 
			
		||||
    void releaseResources() override                                          {}
 | 
			
		||||
 | 
			
		||||
    void getStateInformation (MemoryBlock& destData) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto xmlState = state.createXml())
 | 
			
		||||
            copyXmlToBinary (*xmlState, destData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int size) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto xmlState = getXmlFromBinary (data, size))
 | 
			
		||||
            state = ValueTree::fromXml (*xmlState);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    class Editor  : public AudioProcessorEditor,
 | 
			
		||||
                    private Value::Listener
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        explicit Editor (MidiLoggerPluginDemoProcessor& ownerIn)
 | 
			
		||||
            : AudioProcessorEditor (ownerIn),
 | 
			
		||||
              owner (ownerIn),
 | 
			
		||||
              table (owner.model)
 | 
			
		||||
        {
 | 
			
		||||
            addAndMakeVisible (table);
 | 
			
		||||
            addAndMakeVisible (clearButton);
 | 
			
		||||
 | 
			
		||||
            setResizable (true, true);
 | 
			
		||||
            lastUIWidth .referTo (owner.state.getChildWithName ("uiState").getPropertyAsValue ("width",  nullptr));
 | 
			
		||||
            lastUIHeight.referTo (owner.state.getChildWithName ("uiState").getPropertyAsValue ("height", nullptr));
 | 
			
		||||
            setSize (lastUIWidth.getValue(), lastUIHeight.getValue());
 | 
			
		||||
 | 
			
		||||
            lastUIWidth. addListener (this);
 | 
			
		||||
            lastUIHeight.addListener (this);
 | 
			
		||||
 | 
			
		||||
            clearButton.onClick = [&] { owner.model.clear(); };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void paint (Graphics& g) override
 | 
			
		||||
        {
 | 
			
		||||
            g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void resized() override
 | 
			
		||||
        {
 | 
			
		||||
            auto bounds = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
            clearButton.setBounds (bounds.removeFromBottom (30).withSizeKeepingCentre (50, 24));
 | 
			
		||||
            table.setBounds (bounds);
 | 
			
		||||
 | 
			
		||||
            lastUIWidth  = getWidth();
 | 
			
		||||
            lastUIHeight = getHeight();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        void valueChanged (Value&) override
 | 
			
		||||
        {
 | 
			
		||||
            setSize (lastUIWidth.getValue(), lastUIHeight.getValue());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MidiLoggerPluginDemoProcessor& owner;
 | 
			
		||||
 | 
			
		||||
        MidiTable table;
 | 
			
		||||
        TextButton clearButton { "Clear" };
 | 
			
		||||
 | 
			
		||||
        Value lastUIWidth, lastUIHeight;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<MidiMessage> messages;
 | 
			
		||||
        queue.pop (std::back_inserter (messages));
 | 
			
		||||
        model.addMessages (messages.begin(), messages.end());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename Element>
 | 
			
		||||
    void process (AudioBuffer<Element>& audio, MidiBuffer& midi)
 | 
			
		||||
    {
 | 
			
		||||
        audio.clear();
 | 
			
		||||
        queue.push (midi);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static BusesProperties getBusesLayout()
 | 
			
		||||
    {
 | 
			
		||||
        // Live doesn't like to load midi-only plugins, so we add an audio output there.
 | 
			
		||||
        return PluginHostType().isAbletonLive() ? BusesProperties().withOutput ("out", AudioChannelSet::stereo())
 | 
			
		||||
                                                : BusesProperties();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ValueTree state { "state" };
 | 
			
		||||
    MidiQueue queue;
 | 
			
		||||
    MidiListModel model; // The data to show in the UI. We keep it around in the processor so that
 | 
			
		||||
                         // the view is persistent even when the plugin UI is closed and reopened.
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiLoggerPluginDemoProcessor)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										409
									
								
								deps/juce/examples/Plugins/MultiOutSynthPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										409
									
								
								deps/juce/examples/Plugins/MultiOutSynthPluginDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,201 +1,208 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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:                  MultiOutSynthPlugin
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           Multi-out synthesiser audio plugin.
 | 
			
		||||
 | 
			
		||||
 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, vs2019
 | 
			
		||||
 | 
			
		||||
 moduleFlags:           JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:                  AudioProcessor
 | 
			
		||||
 mainClass:             MultiOutSynth
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginIsSynth, pluginWantsMidiIn
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MultiOutSynth  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        maxMidiChannel    = 16,
 | 
			
		||||
        maxNumberOfVoices = 5
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MultiOutSynth()
 | 
			
		||||
        : AudioProcessor (BusesProperties()
 | 
			
		||||
                          .withOutput ("Output #1",  AudioChannelSet::stereo(), true)
 | 
			
		||||
                          .withOutput ("Output #2",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #3",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #4",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #5",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #6",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #7",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #8",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #9",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #10", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #11", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #12", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #13", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #14", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #15", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #16", AudioChannelSet::stereo(), false))
 | 
			
		||||
    {
 | 
			
		||||
        // initialize other stuff (not related to buses)
 | 
			
		||||
        formatManager.registerBasicFormats();
 | 
			
		||||
 | 
			
		||||
        for (int midiChannel = 0; midiChannel < maxMidiChannel; ++midiChannel)
 | 
			
		||||
        {
 | 
			
		||||
            synth.add (new Synthesiser());
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < maxNumberOfVoices; ++i)
 | 
			
		||||
                synth[midiChannel]->addVoice (new SamplerVoice());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        loadNewSample (createAssetInputStream ("singing.ogg"), "ogg");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool canAddBus    (bool isInput) const override   { return ! isInput; }
 | 
			
		||||
    bool canRemoveBus (bool isInput) const override   { return ! isInput; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double newSampleRate, int samplesPerBlock) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
        for (auto* s : synth)
 | 
			
		||||
            s->setCurrentPlaybackSampleRate (newSampleRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiBuffer) override
 | 
			
		||||
    {
 | 
			
		||||
        auto busCount = getBusCount (false);
 | 
			
		||||
 | 
			
		||||
        for (auto busNr = 0; busNr < busCount; ++busNr)
 | 
			
		||||
        {
 | 
			
		||||
            if (synth.size() <= busNr)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            auto midiChannelBuffer = filterMidiMessagesForChannel (midiBuffer, busNr + 1);
 | 
			
		||||
            auto audioBusBuffer = getBusBuffer (buffer, false, busNr);
 | 
			
		||||
 | 
			
		||||
            synth [busNr]->renderNextBlock (audioBusBuffer, midiChannelBuffer, 0, audioBusBuffer.getNumSamples());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override          { return new GenericAudioProcessorEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                        { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                  { return "Multi Out Synth PlugIn"; }
 | 
			
		||||
    bool acceptsMidi() const override                      { return false; }
 | 
			
		||||
    bool producesMidi() const override                     { return false; }
 | 
			
		||||
    double getTailLengthSeconds() const override           { return 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   {}
 | 
			
		||||
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layout) const override
 | 
			
		||||
    {
 | 
			
		||||
        for (const auto& bus : layout.outputBuses)
 | 
			
		||||
            if (bus != AudioChannelSet::stereo())
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
        return layout.inputBuses.isEmpty() && 1 <= layout.outputBuses.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void getStateInformation (MemoryBlock&) override {}
 | 
			
		||||
    void setStateInformation (const void*, int) override {}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static MidiBuffer filterMidiMessagesForChannel (const MidiBuffer& input, int channel)
 | 
			
		||||
    {
 | 
			
		||||
        MidiBuffer output;
 | 
			
		||||
 | 
			
		||||
        for (const auto metadata : input)
 | 
			
		||||
        {
 | 
			
		||||
            const auto message = metadata.getMessage();
 | 
			
		||||
 | 
			
		||||
            if (message.getChannel() == channel)
 | 
			
		||||
                output.addEvent (message, metadata.samplePosition);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return output;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void loadNewSample (std::unique_ptr<InputStream> soundBuffer, const char* format)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<AudioFormatReader> formatReader (formatManager.findFormatForFileExtension (format)->createReaderFor (soundBuffer.release(), true));
 | 
			
		||||
 | 
			
		||||
        BigInteger midiNotes;
 | 
			
		||||
        midiNotes.setRange (0, 126, true);
 | 
			
		||||
        SynthesiserSound::Ptr newSound = new SamplerSound ("Voice", *formatReader, midiNotes, 0x40, 0.0, 0.0, 10.0);
 | 
			
		||||
 | 
			
		||||
        for (auto* s : synth)
 | 
			
		||||
            s->removeSound (0);
 | 
			
		||||
 | 
			
		||||
        sound = newSound;
 | 
			
		||||
 | 
			
		||||
        for (auto* s : synth)
 | 
			
		||||
            s->addSound (sound);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioFormatManager formatManager;
 | 
			
		||||
    OwnedArray<Synthesiser> synth;
 | 
			
		||||
    SynthesiserSound::Ptr sound;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiOutSynth)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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:                  MultiOutSynthPlugin
 | 
			
		||||
 version:               1.0.0
 | 
			
		||||
 vendor:                JUCE
 | 
			
		||||
 website:               http://juce.com
 | 
			
		||||
 description:           Multi-out synthesiser audio plugin.
 | 
			
		||||
 | 
			
		||||
 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
 | 
			
		||||
 | 
			
		||||
 moduleFlags:           JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:                  AudioProcessor
 | 
			
		||||
 mainClass:             MultiOutSynth
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:          1
 | 
			
		||||
 | 
			
		||||
 pluginCharacteristics: pluginIsSynth, pluginWantsMidiIn
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MultiOutSynth  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        maxMidiChannel    = 16,
 | 
			
		||||
        maxNumberOfVoices = 5
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MultiOutSynth()
 | 
			
		||||
        : AudioProcessor (BusesProperties()
 | 
			
		||||
                          .withOutput ("Output #1",  AudioChannelSet::stereo(), true)
 | 
			
		||||
                          .withOutput ("Output #2",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #3",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #4",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #5",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #6",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #7",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #8",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #9",  AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #10", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #11", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #12", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #13", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #14", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #15", AudioChannelSet::stereo(), false)
 | 
			
		||||
                          .withOutput ("Output #16", AudioChannelSet::stereo(), false))
 | 
			
		||||
    {
 | 
			
		||||
        // initialize other stuff (not related to buses)
 | 
			
		||||
        formatManager.registerBasicFormats();
 | 
			
		||||
 | 
			
		||||
        for (int midiChannel = 0; midiChannel < maxMidiChannel; ++midiChannel)
 | 
			
		||||
        {
 | 
			
		||||
            synth.add (new Synthesiser());
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < maxNumberOfVoices; ++i)
 | 
			
		||||
                synth[midiChannel]->addVoice (new SamplerVoice());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        loadNewSample (createAssetInputStream ("singing.ogg"), "ogg");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool canAddBus    (bool isInput) const override   { return ! isInput; }
 | 
			
		||||
    bool canRemoveBus (bool isInput) const override   { return ! isInput; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double newSampleRate, int samplesPerBlock) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (samplesPerBlock);
 | 
			
		||||
 | 
			
		||||
        for (auto* s : synth)
 | 
			
		||||
            s->setCurrentPlaybackSampleRate (newSampleRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiBuffer) override
 | 
			
		||||
    {
 | 
			
		||||
        auto busCount = getBusCount (false);
 | 
			
		||||
 | 
			
		||||
        for (auto busNr = 0; busNr < busCount; ++busNr)
 | 
			
		||||
        {
 | 
			
		||||
            if (synth.size() <= busNr)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            auto midiChannelBuffer = filterMidiMessagesForChannel (midiBuffer, busNr + 1);
 | 
			
		||||
            auto audioBusBuffer = getBusBuffer (buffer, false, busNr);
 | 
			
		||||
 | 
			
		||||
            // Voices add to the contents of the buffer. Make sure the buffer is clear before
 | 
			
		||||
            // rendering, just in case the host left old data in the buffer.
 | 
			
		||||
            audioBusBuffer.clear();
 | 
			
		||||
 | 
			
		||||
            synth [busNr]->renderNextBlock (audioBusBuffer, midiChannelBuffer, 0, audioBusBuffer.getNumSamples());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override          { return new GenericAudioProcessorEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                        { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                  { return "Multi Out Synth PlugIn"; }
 | 
			
		||||
    bool acceptsMidi() const override                      { return false; }
 | 
			
		||||
    bool producesMidi() const override                     { return false; }
 | 
			
		||||
    double getTailLengthSeconds() const override           { return 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   {}
 | 
			
		||||
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layout) const override
 | 
			
		||||
    {
 | 
			
		||||
        const auto& outputs = layout.outputBuses;
 | 
			
		||||
 | 
			
		||||
        return layout.inputBuses.isEmpty()
 | 
			
		||||
            && 1 <= outputs.size()
 | 
			
		||||
            && std::all_of (outputs.begin(), outputs.end(), [] (const auto& bus)
 | 
			
		||||
               {
 | 
			
		||||
                   return bus == AudioChannelSet::stereo();
 | 
			
		||||
               });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void getStateInformation (MemoryBlock&) override {}
 | 
			
		||||
    void setStateInformation (const void*, int) override {}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static MidiBuffer filterMidiMessagesForChannel (const MidiBuffer& input, int channel)
 | 
			
		||||
    {
 | 
			
		||||
        MidiBuffer output;
 | 
			
		||||
 | 
			
		||||
        for (const auto metadata : input)
 | 
			
		||||
        {
 | 
			
		||||
            const auto message = metadata.getMessage();
 | 
			
		||||
 | 
			
		||||
            if (message.getChannel() == channel)
 | 
			
		||||
                output.addEvent (message, metadata.samplePosition);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return output;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void loadNewSample (std::unique_ptr<InputStream> soundBuffer, const char* format)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<AudioFormatReader> formatReader (formatManager.findFormatForFileExtension (format)->createReaderFor (soundBuffer.release(), true));
 | 
			
		||||
 | 
			
		||||
        BigInteger midiNotes;
 | 
			
		||||
        midiNotes.setRange (0, 126, true);
 | 
			
		||||
        SynthesiserSound::Ptr newSound = new SamplerSound ("Voice", *formatReader, midiNotes, 0x40, 0.0, 0.0, 10.0);
 | 
			
		||||
 | 
			
		||||
        for (auto* s : synth)
 | 
			
		||||
            s->removeSound (0);
 | 
			
		||||
 | 
			
		||||
        sound = newSound;
 | 
			
		||||
 | 
			
		||||
        for (auto* s : synth)
 | 
			
		||||
            s->addSound (sound);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioFormatManager formatManager;
 | 
			
		||||
    OwnedArray<Synthesiser> synth;
 | 
			
		||||
    SynthesiserSound::Ptr sound;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiOutSynth)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										302
									
								
								deps/juce/examples/Plugins/NoiseGatePluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										302
									
								
								deps/juce/examples/Plugins/NoiseGatePluginDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,151 +1,151 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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:             NoiseGatePlugin
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Noise gate audio plugin.
 | 
			
		||||
 | 
			
		||||
 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, vs2019
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             AudioProcessor
 | 
			
		||||
 mainClass:        NoiseGate
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class NoiseGate  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    NoiseGate()
 | 
			
		||||
        : AudioProcessor (BusesProperties().withInput  ("Input",     AudioChannelSet::stereo())
 | 
			
		||||
                                           .withOutput ("Output",    AudioChannelSet::stereo())
 | 
			
		||||
                                           .withInput  ("Sidechain", AudioChannelSet::stereo()))
 | 
			
		||||
    {
 | 
			
		||||
        addParameter (threshold = new AudioParameterFloat ("threshold", "Threshold", 0.0f, 1.0f, 0.5f));
 | 
			
		||||
        addParameter (alpha     = new AudioParameterFloat ("alpha",     "Alpha",     0.0f, 1.0f, 0.8f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        // the sidechain can take any layout, the main bus needs to be the same on the input and output
 | 
			
		||||
        return layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet()
 | 
			
		||||
                 && ! layouts.getMainInputChannelSet().isDisabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double, int) override { lowPassCoeff = 0.0f; sampleCountDown = 0; }
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        auto mainInputOutput = getBusBuffer (buffer, true, 0);
 | 
			
		||||
        auto sideChainInput  = getBusBuffer (buffer, true, 1);
 | 
			
		||||
 | 
			
		||||
        auto alphaCopy     = alpha->get();
 | 
			
		||||
        auto thresholdCopy = threshold->get();
 | 
			
		||||
 | 
			
		||||
        for (int j = 0; j < buffer.getNumSamples(); ++j)
 | 
			
		||||
        {
 | 
			
		||||
            auto mixedSamples = 0.0f;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < sideChainInput.getNumChannels(); ++i)
 | 
			
		||||
                mixedSamples += sideChainInput.getReadPointer (i)[j];
 | 
			
		||||
 | 
			
		||||
            mixedSamples /= static_cast<float> (sideChainInput.getNumChannels());
 | 
			
		||||
            lowPassCoeff = (alphaCopy * lowPassCoeff) + ((1.0f - alphaCopy) * mixedSamples);
 | 
			
		||||
 | 
			
		||||
            if (lowPassCoeff >= thresholdCopy)
 | 
			
		||||
                sampleCountDown = (int) getSampleRate();
 | 
			
		||||
 | 
			
		||||
            // very in-effective way of doing this
 | 
			
		||||
            for (int i = 0; i < mainInputOutput.getNumChannels(); ++i)
 | 
			
		||||
                *mainInputOutput.getWritePointer (i, j) = sampleCountDown > 0 ? *mainInputOutput.getReadPointer (i, j) : 0.0f;
 | 
			
		||||
 | 
			
		||||
            if (sampleCountDown > 0)
 | 
			
		||||
                --sampleCountDown;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override            { return new GenericAudioProcessorEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                          { return true; }
 | 
			
		||||
    const String getName() const override                    { return "NoiseGate"; }
 | 
			
		||||
    bool acceptsMidi() const override                        { return false; }
 | 
			
		||||
    bool producesMidi() 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     {}
 | 
			
		||||
    bool isVST2() const noexcept                             { return (wrapperType == wrapperType_VST); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void getStateInformation (MemoryBlock& destData) override
 | 
			
		||||
    {
 | 
			
		||||
        MemoryOutputStream stream (destData, true);
 | 
			
		||||
 | 
			
		||||
        stream.writeFloat (*threshold);
 | 
			
		||||
        stream.writeFloat (*alpha);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int sizeInBytes) override
 | 
			
		||||
    {
 | 
			
		||||
        MemoryInputStream stream (data, static_cast<size_t> (sizeInBytes), false);
 | 
			
		||||
 | 
			
		||||
        threshold->setValueNotifyingHost (stream.readFloat());
 | 
			
		||||
        alpha->setValueNotifyingHost     (stream.readFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioParameterFloat* threshold;
 | 
			
		||||
    AudioParameterFloat* alpha;
 | 
			
		||||
    int sampleCountDown;
 | 
			
		||||
 | 
			
		||||
    float lowPassCoeff;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NoiseGate)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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:             NoiseGatePlugin
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Noise gate audio plugin.
 | 
			
		||||
 | 
			
		||||
 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
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             AudioProcessor
 | 
			
		||||
 mainClass:        NoiseGate
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class NoiseGate  : public AudioProcessor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    NoiseGate()
 | 
			
		||||
        : AudioProcessor (BusesProperties().withInput  ("Input",     AudioChannelSet::stereo())
 | 
			
		||||
                                           .withOutput ("Output",    AudioChannelSet::stereo())
 | 
			
		||||
                                           .withInput  ("Sidechain", AudioChannelSet::stereo()))
 | 
			
		||||
    {
 | 
			
		||||
        addParameter (threshold = new AudioParameterFloat ({ "threshold", 1 }, "Threshold", 0.0f, 1.0f, 0.5f));
 | 
			
		||||
        addParameter (alpha     = new AudioParameterFloat ({ "alpha",     1 }, "Alpha",     0.0f, 1.0f, 0.8f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        // the sidechain can take any layout, the main bus needs to be the same on the input and output
 | 
			
		||||
        return layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet()
 | 
			
		||||
                 && ! layouts.getMainInputChannelSet().isDisabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double, int) override { lowPassCoeff = 0.0f; sampleCountDown = 0; }
 | 
			
		||||
    void releaseResources() override {}
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        auto mainInputOutput = getBusBuffer (buffer, true, 0);
 | 
			
		||||
        auto sideChainInput  = getBusBuffer (buffer, true, 1);
 | 
			
		||||
 | 
			
		||||
        auto alphaCopy     = alpha->get();
 | 
			
		||||
        auto thresholdCopy = threshold->get();
 | 
			
		||||
 | 
			
		||||
        for (int j = 0; j < buffer.getNumSamples(); ++j)
 | 
			
		||||
        {
 | 
			
		||||
            auto mixedSamples = 0.0f;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < sideChainInput.getNumChannels(); ++i)
 | 
			
		||||
                mixedSamples += sideChainInput.getReadPointer (i)[j];
 | 
			
		||||
 | 
			
		||||
            mixedSamples /= static_cast<float> (sideChainInput.getNumChannels());
 | 
			
		||||
            lowPassCoeff = (alphaCopy * lowPassCoeff) + ((1.0f - alphaCopy) * mixedSamples);
 | 
			
		||||
 | 
			
		||||
            if (lowPassCoeff >= thresholdCopy)
 | 
			
		||||
                sampleCountDown = (int) getSampleRate();
 | 
			
		||||
 | 
			
		||||
            // very in-effective way of doing this
 | 
			
		||||
            for (int i = 0; i < mainInputOutput.getNumChannels(); ++i)
 | 
			
		||||
                *mainInputOutput.getWritePointer (i, j) = sampleCountDown > 0 ? *mainInputOutput.getReadPointer (i, j) : 0.0f;
 | 
			
		||||
 | 
			
		||||
            if (sampleCountDown > 0)
 | 
			
		||||
                --sampleCountDown;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override            { return new GenericAudioProcessorEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override                          { return true; }
 | 
			
		||||
    const String getName() const override                    { return "NoiseGate"; }
 | 
			
		||||
    bool acceptsMidi() const override                        { return false; }
 | 
			
		||||
    bool producesMidi() 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     {}
 | 
			
		||||
    bool isVST2() const noexcept                             { return (wrapperType == wrapperType_VST); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void getStateInformation (MemoryBlock& destData) override
 | 
			
		||||
    {
 | 
			
		||||
        MemoryOutputStream stream (destData, true);
 | 
			
		||||
 | 
			
		||||
        stream.writeFloat (*threshold);
 | 
			
		||||
        stream.writeFloat (*alpha);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setStateInformation (const void* data, int sizeInBytes) override
 | 
			
		||||
    {
 | 
			
		||||
        MemoryInputStream stream (data, static_cast<size_t> (sizeInBytes), false);
 | 
			
		||||
 | 
			
		||||
        threshold->setValueNotifyingHost (stream.readFloat());
 | 
			
		||||
        alpha->setValueNotifyingHost     (stream.readFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioParameterFloat* threshold;
 | 
			
		||||
    AudioParameterFloat* alpha;
 | 
			
		||||
    int sampleCountDown;
 | 
			
		||||
 | 
			
		||||
    float lowPassCoeff;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NoiseGate)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,423 +1,423 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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, vs2019, linux_make
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             AudioProcessor
 | 
			
		||||
 mainClass:        ReaperEmbeddedViewDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*  This demo shows how to use the VSTCallbackHandler 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 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,
 | 
			
		||||
                                public VSTCallbackHandler,
 | 
			
		||||
                                public VST3ClientExtensions,
 | 
			
		||||
                                private EmbeddedViewListener,
 | 
			
		||||
                                private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ReaperEmbeddedViewDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addParameter (gain = new AudioParameterFloat ("gain", "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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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"))
 | 
			
		||||
                globalBypassFn = reinterpret_cast<void (*) (int)> (fnPtr);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pointer_sized_int handleVstPluginCanDo (int32, pointer_sized_int, void* ptr, float) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* str = static_cast<const char*> (ptr))
 | 
			
		||||
        {
 | 
			
		||||
            if (strcmp (str, "hasCockosEmbeddedUI") == 0)
 | 
			
		||||
                return 0xbeef0000;
 | 
			
		||||
 | 
			
		||||
            if (strcmp (str, "hasCockosExtensions") == 0)
 | 
			
		||||
                return 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) handledEmbeddedUIMessage ((int) opt,
 | 
			
		||||
                                                             (Steinberg::TPtrInt) value,
 | 
			
		||||
                                                             (Steinberg::TPtrInt) ptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleVstHostCallbackAvailable (std::function<VstHostCallbackType>&& hostcb) override
 | 
			
		||||
    {
 | 
			
		||||
        char functionName[] = "BypassFxAllTracks";
 | 
			
		||||
        globalBypassFn = reinterpret_cast<void (*) (int)> (hostcb ((int32_t) 0xdeadbeef, (int32_t) 0xdeadf00d, 0, functionName, 0.0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    AudioParameterFloat* gain = nullptr;
 | 
			
		||||
    void (*globalBypassFn) (int) = nullptr;
 | 
			
		||||
    EmbeddedUI embeddedUi { *this };
 | 
			
		||||
 | 
			
		||||
    std::atomic<float> storedLevel { 0.0f };
 | 
			
		||||
    float levelToDraw = 0.0f;
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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 VSTCallbackHandler 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 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,
 | 
			
		||||
                                public VSTCallbackHandler,
 | 
			
		||||
                                public VST3ClientExtensions,
 | 
			
		||||
                                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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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"))
 | 
			
		||||
                globalBypassFn = reinterpret_cast<void (*) (int)> (fnPtr);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pointer_sized_int handleVstPluginCanDo (int32, pointer_sized_int, void* ptr, float) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* str = static_cast<const char*> (ptr))
 | 
			
		||||
        {
 | 
			
		||||
            if (strcmp (str, "hasCockosEmbeddedUI") == 0)
 | 
			
		||||
                return 0xbeef0000;
 | 
			
		||||
 | 
			
		||||
            if (strcmp (str, "hasCockosExtensions") == 0)
 | 
			
		||||
                return 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) handledEmbeddedUIMessage ((int) opt,
 | 
			
		||||
                                                             (Steinberg::TPtrInt) value,
 | 
			
		||||
                                                             (Steinberg::TPtrInt) ptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleVstHostCallbackAvailable (std::function<VstHostCallbackType>&& hostcb) override
 | 
			
		||||
    {
 | 
			
		||||
        char functionName[] = "BypassFxAllTracks";
 | 
			
		||||
        globalBypassFn = reinterpret_cast<void (*) (int)> (hostcb ((int32_t) 0xdeadbeef, (int32_t) 0xdeadf00d, 0, functionName, 0.0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    AudioParameterFloat* gain = nullptr;
 | 
			
		||||
    void (*globalBypassFn) (int) = nullptr;
 | 
			
		||||
    EmbeddedUI embeddedUi { *this };
 | 
			
		||||
 | 
			
		||||
    std::atomic<float> storedLevel { 0.0f };
 | 
			
		||||
    float levelToDraw = 0.0f;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5213
									
								
								deps/juce/examples/Plugins/SamplerPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5213
									
								
								deps/juce/examples/Plugins/SamplerPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										798
									
								
								deps/juce/examples/Plugins/SurroundPluginDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										798
									
								
								deps/juce/examples/Plugins/SurroundPluginDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,337 +1,461 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - 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:             SurroundPlugin
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Surround audio plugin.
 | 
			
		||||
 | 
			
		||||
 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, vs2019, linux_make
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             AudioProcessor
 | 
			
		||||
 mainClass:        SurroundProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ChannelClickListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~ChannelClickListener() {}
 | 
			
		||||
    virtual void channelButtonClicked (int channelIndex) = 0;
 | 
			
		||||
    virtual bool isChannelActive (int channelIndex) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class SurroundEditor : public AudioProcessorEditor,
 | 
			
		||||
                       private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    SurroundEditor (AudioProcessor& parent)
 | 
			
		||||
        : AudioProcessorEditor (parent),
 | 
			
		||||
          currentChannelLayout (AudioChannelSet::disabled()),
 | 
			
		||||
          layoutTitle          ("LayoutTitleLabel", getLayoutName())
 | 
			
		||||
    {
 | 
			
		||||
        layoutTitle.setJustificationType (Justification::centred);
 | 
			
		||||
        addAndMakeVisible (layoutTitle);
 | 
			
		||||
        addAndMakeVisible (noChannelsLabel);
 | 
			
		||||
 | 
			
		||||
        setSize (600, 100);
 | 
			
		||||
 | 
			
		||||
        lastSuspended = ! getAudioProcessor()->isSuspended();
 | 
			
		||||
        timerCallback();
 | 
			
		||||
        startTimer (500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        layoutTitle.setBounds (r.removeFromBottom (16));
 | 
			
		||||
 | 
			
		||||
        noChannelsLabel.setBounds (r);
 | 
			
		||||
 | 
			
		||||
        if (channelButtons.size() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            auto buttonWidth = r.getWidth() / channelButtons.size();
 | 
			
		||||
            for (auto channelButton : channelButtons)
 | 
			
		||||
                channelButton->setBounds (r.removeFromLeft (buttonWidth));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateButton (Button* btn)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* textButton = dynamic_cast<TextButton*> (btn))
 | 
			
		||||
        {
 | 
			
		||||
            auto channelIndex = channelButtons.indexOf (textButton);
 | 
			
		||||
 | 
			
		||||
            if (auto* listener = dynamic_cast<ChannelClickListener*> (getAudioProcessor()))
 | 
			
		||||
                listener->channelButtonClicked (channelIndex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateGUI()
 | 
			
		||||
    {
 | 
			
		||||
        const auto& channelSet = getAudioProcessor()->getChannelLayoutOfBus (false, 0);
 | 
			
		||||
 | 
			
		||||
        if (channelSet != currentChannelLayout)
 | 
			
		||||
        {
 | 
			
		||||
            currentChannelLayout = channelSet;
 | 
			
		||||
 | 
			
		||||
            layoutTitle.setText (currentChannelLayout.getDescription(), NotificationType::dontSendNotification);
 | 
			
		||||
            channelButtons.clear();
 | 
			
		||||
            activeChannels.resize (currentChannelLayout.size());
 | 
			
		||||
 | 
			
		||||
            if (currentChannelLayout == AudioChannelSet::disabled())
 | 
			
		||||
            {
 | 
			
		||||
                noChannelsLabel.setVisible (true);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                auto numChannels = currentChannelLayout.size();
 | 
			
		||||
 | 
			
		||||
                for (auto i = 0; i < numChannels; ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    auto channelName =
 | 
			
		||||
                    AudioChannelSet::getAbbreviatedChannelTypeName (currentChannelLayout.getTypeOfChannel (i));
 | 
			
		||||
 | 
			
		||||
                    TextButton* newButton;
 | 
			
		||||
                    channelButtons.add (newButton = new TextButton (channelName, channelName));
 | 
			
		||||
 | 
			
		||||
                    newButton->onClick = [this, newButton] { updateButton (newButton); };
 | 
			
		||||
                    addAndMakeVisible (newButton);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                noChannelsLabel.setVisible (false);
 | 
			
		||||
                resized();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (auto* listener = dynamic_cast<ChannelClickListener*> (getAudioProcessor()))
 | 
			
		||||
            {
 | 
			
		||||
                auto   activeColour = getLookAndFeel().findColour (Slider::thumbColourId);
 | 
			
		||||
                auto inactiveColour = getLookAndFeel().findColour (Slider::trackColourId);
 | 
			
		||||
 | 
			
		||||
                for (auto i = 0; i < activeChannels.size(); ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    auto isActive = listener->isChannelActive (i);
 | 
			
		||||
                    activeChannels.getReference (i) = isActive;
 | 
			
		||||
                    channelButtons[i]->setColour (TextButton::buttonColourId, isActive ? activeColour : inactiveColour);
 | 
			
		||||
                    channelButtons[i]->repaint();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    String getLayoutName() const
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* p = getAudioProcessor())
 | 
			
		||||
            return p->getChannelLayoutOfBus (false, 0).getDescription();
 | 
			
		||||
 | 
			
		||||
        return "Unknown";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        if (getAudioProcessor()->isSuspended() != lastSuspended)
 | 
			
		||||
        {
 | 
			
		||||
            lastSuspended = getAudioProcessor()->isSuspended();
 | 
			
		||||
            updateGUI();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (! lastSuspended)
 | 
			
		||||
        {
 | 
			
		||||
            if (auto* listener = dynamic_cast<ChannelClickListener*> (getAudioProcessor()))
 | 
			
		||||
            {
 | 
			
		||||
                auto   activeColour = getLookAndFeel().findColour (Slider::thumbColourId);
 | 
			
		||||
                auto inactiveColour = getLookAndFeel().findColour (Slider::trackColourId);
 | 
			
		||||
 | 
			
		||||
                for (auto i = 0; i < activeChannels.size(); ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    auto isActive = listener->isChannelActive (i);
 | 
			
		||||
                    if (activeChannels.getReference (i) != isActive)
 | 
			
		||||
                    {
 | 
			
		||||
                        activeChannels.getReference (i) = isActive;
 | 
			
		||||
                        channelButtons[i]->setColour (TextButton::buttonColourId, isActive ? activeColour : inactiveColour);
 | 
			
		||||
                        channelButtons[i]->repaint();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    AudioChannelSet currentChannelLayout;
 | 
			
		||||
    Label noChannelsLabel { "noChannelsLabel", "Input disabled" },
 | 
			
		||||
          layoutTitle;
 | 
			
		||||
    OwnedArray<TextButton> channelButtons;
 | 
			
		||||
    Array<bool> activeChannels;
 | 
			
		||||
 | 
			
		||||
    bool lastSuspended;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class SurroundProcessor  : public AudioProcessor,
 | 
			
		||||
                           public ChannelClickListener,
 | 
			
		||||
                           private AsyncUpdater
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    SurroundProcessor()
 | 
			
		||||
        : AudioProcessor(BusesProperties().withInput  ("Input",  AudioChannelSet::stereo())
 | 
			
		||||
                                          .withOutput ("Output", AudioChannelSet::stereo()))
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void prepareToPlay (double sampleRate, int samplesPerBlock) override
 | 
			
		||||
    {
 | 
			
		||||
        channelClicked = 0;
 | 
			
		||||
        sampleOffset = static_cast<int> (std::ceil (sampleRate));
 | 
			
		||||
 | 
			
		||||
        auto numChannels = getChannelCountOfBus (true, 0);
 | 
			
		||||
        channelActive.resize (numChannels);
 | 
			
		||||
        alphaCoeffs  .resize (numChannels);
 | 
			
		||||
        reset();
 | 
			
		||||
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
 | 
			
		||||
        ignoreUnused (samplesPerBlock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void releaseResources() override { reset(); }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
 | 
			
		||||
    {
 | 
			
		||||
        for (auto ch = 0; ch < buffer.getNumChannels(); ++ch)
 | 
			
		||||
        {
 | 
			
		||||
            auto& channelTime = channelActive.getReference (ch);
 | 
			
		||||
            auto& alpha       = alphaCoeffs  .getReference (ch);
 | 
			
		||||
 | 
			
		||||
            for (auto j = 0; j < buffer.getNumSamples(); ++j)
 | 
			
		||||
            {
 | 
			
		||||
                auto sample = buffer.getReadPointer (ch)[j];
 | 
			
		||||
                alpha = (0.8f * alpha) + (0.2f * sample);
 | 
			
		||||
 | 
			
		||||
                if (std::abs (alpha) >= 0.1f)
 | 
			
		||||
                    channelTime = static_cast<int> (getSampleRate() / 2.0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            channelTime = jmax (0, channelTime - buffer.getNumSamples());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto fillSamples = jmin (static_cast<int> (std::ceil (getSampleRate())) - sampleOffset,
 | 
			
		||||
                                 buffer.getNumSamples());
 | 
			
		||||
 | 
			
		||||
        if (isPositiveAndBelow (channelClicked, buffer.getNumChannels()))
 | 
			
		||||
        {
 | 
			
		||||
            auto* channelBuffer = buffer.getWritePointer (channelClicked);
 | 
			
		||||
            auto freq = (float) (440.0 / getSampleRate());
 | 
			
		||||
 | 
			
		||||
            for (auto i = 0; i < fillSamples; ++i)
 | 
			
		||||
                channelBuffer[i] += std::sin (MathConstants<float>::twoPi * freq * static_cast<float> (sampleOffset++));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using AudioProcessor::processBlock;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AudioProcessorEditor* createEditor() override { return new SurroundEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override               { return true; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        return ((! layouts.getMainInputChannelSet() .isDiscreteLayout())
 | 
			
		||||
             && (! layouts.getMainOutputChannelSet().isDiscreteLayout())
 | 
			
		||||
             && (layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet())
 | 
			
		||||
             && (! layouts.getMainInputChannelSet().isDisabled()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void reset() override
 | 
			
		||||
    {
 | 
			
		||||
        for (auto& channel : channelActive)
 | 
			
		||||
            channel = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                  { return "Surround PlugIn"; }
 | 
			
		||||
    bool acceptsMidi() const override                      { return false; }
 | 
			
		||||
    bool producesMidi() const override                     { return false; }
 | 
			
		||||
    double getTailLengthSeconds() const override           { return 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&) override       {}
 | 
			
		||||
    void setStateInformation (const void*, int) override   {}
 | 
			
		||||
 | 
			
		||||
    void channelButtonClicked (int channelIndex) override
 | 
			
		||||
    {
 | 
			
		||||
        channelClicked = channelIndex;
 | 
			
		||||
        sampleOffset = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isChannelActive (int channelIndex) override
 | 
			
		||||
    {
 | 
			
		||||
        return channelActive[channelIndex] > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleAsyncUpdate() override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* editor = getActiveEditor())
 | 
			
		||||
            if (auto* surroundEditor = dynamic_cast<SurroundEditor*> (editor))
 | 
			
		||||
                surroundEditor->updateGUI();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Array<int> channelActive;
 | 
			
		||||
    Array<float> alphaCoeffs;
 | 
			
		||||
    int channelClicked;
 | 
			
		||||
    int sampleOffset;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SurroundProcessor)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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:             SurroundPlugin
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Surround audio plugin.
 | 
			
		||||
 | 
			
		||||
 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:        SurroundProcessor
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ProcessorWithLevels : public AudioProcessor,
 | 
			
		||||
                            private AsyncUpdater,
 | 
			
		||||
                            private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ProcessorWithLevels()
 | 
			
		||||
        : AudioProcessor (BusesProperties().withInput  ("Input", AudioChannelSet::stereo())
 | 
			
		||||
                                           .withInput  ("Aux", AudioChannelSet::stereo(), false)
 | 
			
		||||
                                           .withOutput ("Output", AudioChannelSet::stereo())
 | 
			
		||||
                                           .withOutput ("Aux", AudioChannelSet::stereo(), false))
 | 
			
		||||
    {
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
        applyBusLayouts (getBusesLayout());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~ProcessorWithLevels() override
 | 
			
		||||
    {
 | 
			
		||||
        stopTimer();
 | 
			
		||||
        cancelPendingUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void prepareToPlay (double, int) override
 | 
			
		||||
    {
 | 
			
		||||
        samplesToPlay = (int) getSampleRate();
 | 
			
		||||
        reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processBlock (AudioBuffer<float>&  audio, MidiBuffer&) override { processAudio (audio); }
 | 
			
		||||
    void processBlock (AudioBuffer<double>& audio, MidiBuffer&) override { processAudio (audio); }
 | 
			
		||||
 | 
			
		||||
    void releaseResources() override { reset(); }
 | 
			
		||||
 | 
			
		||||
    float getLevel (int bus, int channel) const
 | 
			
		||||
    {
 | 
			
		||||
        return readableLevels[(size_t) getChannelIndexInProcessBlockBuffer (true, bus, channel)];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
 | 
			
		||||
    {
 | 
			
		||||
        const auto isSetValid = [] (const AudioChannelSet& set)
 | 
			
		||||
        {
 | 
			
		||||
            return ! set.isDisabled()
 | 
			
		||||
                   && ! (set.isDiscreteLayout() && set.getChannelIndexForType (AudioChannelSet::discreteChannel0) == -1);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return isSetValid (layouts.getMainOutputChannelSet())
 | 
			
		||||
               && isSetValid (layouts.getMainInputChannelSet());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void reset() override
 | 
			
		||||
    {
 | 
			
		||||
        channelClicked = 0;
 | 
			
		||||
        samplesPlayed = samplesToPlay;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool applyBusLayouts (const BusesLayout& layouts) override
 | 
			
		||||
    {
 | 
			
		||||
        // Some very badly-behaved hosts will call this during processing!
 | 
			
		||||
        const SpinLock::ScopedLockType lock (levelMutex);
 | 
			
		||||
 | 
			
		||||
        const auto result = AudioProcessor::applyBusLayouts (layouts);
 | 
			
		||||
 | 
			
		||||
        size_t numInputChannels = 0;
 | 
			
		||||
 | 
			
		||||
        for (auto i = 0; i < getBusCount (true); ++i)
 | 
			
		||||
            numInputChannels += (size_t) getBus (true, i)->getLastEnabledLayout().size();
 | 
			
		||||
 | 
			
		||||
        incomingLevels = readableLevels = std::vector<float> (numInputChannels, 0.0f);
 | 
			
		||||
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    const String getName() const override                  { return "Surround PlugIn"; }
 | 
			
		||||
    bool acceptsMidi() const override                      { return false; }
 | 
			
		||||
    bool producesMidi() const override                     { return false; }
 | 
			
		||||
    double getTailLengthSeconds() const override           { return 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&) override       {}
 | 
			
		||||
    void setStateInformation (const void*, int) override   {}
 | 
			
		||||
 | 
			
		||||
    void channelButtonClicked (int bus, int channelIndex)
 | 
			
		||||
    {
 | 
			
		||||
        channelClicked = getChannelIndexInProcessBlockBuffer (false, bus, channelIndex);
 | 
			
		||||
        samplesPlayed = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::function<void()> updateEditor;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void handleAsyncUpdate() override
 | 
			
		||||
    {
 | 
			
		||||
        NullCheckedInvocation::invoke (updateEditor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename Float>
 | 
			
		||||
    void processAudio (AudioBuffer<Float>& audio)
 | 
			
		||||
    {
 | 
			
		||||
        {
 | 
			
		||||
            SpinLock::ScopedTryLockType lock (levelMutex);
 | 
			
		||||
 | 
			
		||||
            if (lock.isLocked())
 | 
			
		||||
            {
 | 
			
		||||
                const auto numInputChannels = (size_t) getTotalNumInputChannels();
 | 
			
		||||
 | 
			
		||||
                for (size_t i = 0; i < numInputChannels; ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    const auto minMax = audio.findMinMax ((int) i, 0, audio.getNumSamples());
 | 
			
		||||
                    const auto newMax = (float) std::max (std::abs (minMax.getStart()), std::abs (minMax.getEnd()));
 | 
			
		||||
 | 
			
		||||
                    auto& toUpdate = incomingLevels[i];
 | 
			
		||||
                    toUpdate = jmax (toUpdate, newMax);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        audio.clear (0, audio.getNumSamples());
 | 
			
		||||
 | 
			
		||||
        auto fillSamples = jmin (samplesToPlay - samplesPlayed, audio.getNumSamples());
 | 
			
		||||
 | 
			
		||||
        if (isPositiveAndBelow (channelClicked, audio.getNumChannels()))
 | 
			
		||||
        {
 | 
			
		||||
            auto* channelBuffer = audio.getWritePointer (channelClicked);
 | 
			
		||||
            auto freq = (float) (440.0 / getSampleRate());
 | 
			
		||||
 | 
			
		||||
            for (auto i = 0; i < fillSamples; ++i)
 | 
			
		||||
                channelBuffer[i] += std::sin (MathConstants<float>::twoPi * freq * (float) samplesPlayed++);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        const SpinLock::ScopedLockType lock (levelMutex);
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < readableLevels.size(); ++i)
 | 
			
		||||
            readableLevels[i] = std::max (readableLevels[i] * 0.95f, std::exchange (incomingLevels[i], 0.0f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SpinLock levelMutex;
 | 
			
		||||
    std::vector<float> incomingLevels;
 | 
			
		||||
    std::vector<float> readableLevels;
 | 
			
		||||
 | 
			
		||||
    int channelClicked;
 | 
			
		||||
    int samplesPlayed;
 | 
			
		||||
    int samplesToPlay;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
const Colour textColour = Colours::white.withAlpha (0.8f);
 | 
			
		||||
 | 
			
		||||
inline void drawBackground (Component& comp, Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    g.setColour (comp.getLookAndFeel().findColour (ResizableWindow::backgroundColourId).darker (0.8f));
 | 
			
		||||
    g.fillRoundedRectangle (comp.getLocalBounds().toFloat(), 4.0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void configureLabel (Label& label, const AudioProcessor::Bus* layout)
 | 
			
		||||
{
 | 
			
		||||
    const auto text = layout != nullptr
 | 
			
		||||
                          ? (layout->getName() + ": " + layout->getCurrentLayout().getDescription())
 | 
			
		||||
                          : "";
 | 
			
		||||
    label.setText (text, dontSendNotification);
 | 
			
		||||
    label.setJustificationType (Justification::centred);
 | 
			
		||||
    label.setColour (Label::textColourId, textColour);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class InputBusViewer : public Component,
 | 
			
		||||
                       private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    InputBusViewer (ProcessorWithLevels& proc, int busNumber)
 | 
			
		||||
        : processor (proc),
 | 
			
		||||
          bus (busNumber)
 | 
			
		||||
    {
 | 
			
		||||
        configureLabel (layoutName, processor.getBus (true, bus));
 | 
			
		||||
        addAndMakeVisible (layoutName);
 | 
			
		||||
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~InputBusViewer() override
 | 
			
		||||
    {
 | 
			
		||||
        stopTimer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        drawBackground (*this, g);
 | 
			
		||||
 | 
			
		||||
        auto* layout = processor.getBus (true, bus);
 | 
			
		||||
 | 
			
		||||
        if (layout == nullptr)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        const auto channelSet = layout->getCurrentLayout();
 | 
			
		||||
        const auto numChannels = channelSet.size();
 | 
			
		||||
 | 
			
		||||
        Grid grid;
 | 
			
		||||
 | 
			
		||||
        grid.autoFlow = Grid::AutoFlow::column;
 | 
			
		||||
        grid.autoColumns = grid.autoRows = Grid::TrackInfo (Grid::Fr (1));
 | 
			
		||||
        grid.items.insertMultiple (0, GridItem(), numChannels);
 | 
			
		||||
        grid.performLayout (getLocalBounds());
 | 
			
		||||
 | 
			
		||||
        const auto minDb = -50.0f;
 | 
			
		||||
        const auto maxDb = 6.0f;
 | 
			
		||||
 | 
			
		||||
        for (auto i = 0; i < numChannels; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            g.setColour (Colours::orange.darker());
 | 
			
		||||
 | 
			
		||||
            const auto levelInDb = Decibels::gainToDecibels (processor.getLevel (bus, i), minDb);
 | 
			
		||||
            const auto fractionOfHeight = jmap (levelInDb, minDb, maxDb, 0.0f, 1.0f);
 | 
			
		||||
            const auto bounds = grid.items[i].currentBounds;
 | 
			
		||||
            const auto trackBounds = bounds.withSizeKeepingCentre (16, bounds.getHeight() - 10).toFloat();
 | 
			
		||||
            g.fillRect (trackBounds.withHeight (trackBounds.proportionOfHeight (fractionOfHeight)).withBottomY (trackBounds.getBottom()));
 | 
			
		||||
 | 
			
		||||
            g.setColour (textColour);
 | 
			
		||||
 | 
			
		||||
            g.drawText (channelSet.getAbbreviatedChannelTypeName (channelSet.getTypeOfChannel (i)),
 | 
			
		||||
                        bounds,
 | 
			
		||||
                        Justification::centredBottom);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        layoutName.setBounds (getLocalBounds().removeFromTop (20));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getNumChannels() const
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* b = processor.getBus (true, bus))
 | 
			
		||||
            return b->getCurrentLayout().size();
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void timerCallback() override { repaint(); }
 | 
			
		||||
 | 
			
		||||
    ProcessorWithLevels& processor;
 | 
			
		||||
    int bus = 0;
 | 
			
		||||
    Label layoutName;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OutputBusViewer : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    OutputBusViewer (ProcessorWithLevels& proc, int busNumber)
 | 
			
		||||
        : processor (proc),
 | 
			
		||||
          bus (busNumber)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = processor.getBus (false, bus);
 | 
			
		||||
 | 
			
		||||
        configureLabel (layoutName, layout);
 | 
			
		||||
        addAndMakeVisible (layoutName);
 | 
			
		||||
 | 
			
		||||
        if (layout == nullptr)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        const auto& channelSet = layout->getCurrentLayout();
 | 
			
		||||
 | 
			
		||||
        const auto numChannels = channelSet.size();
 | 
			
		||||
 | 
			
		||||
        for (auto i = 0; i < numChannels; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            const auto channelName = channelSet.getAbbreviatedChannelTypeName (channelSet.getTypeOfChannel (i));
 | 
			
		||||
 | 
			
		||||
            channelButtons.emplace_back (channelName, channelName);
 | 
			
		||||
 | 
			
		||||
            auto& newButton = channelButtons.back();
 | 
			
		||||
            newButton.onClick = [&proc = processor, bus = bus, i] { proc.channelButtonClicked (bus, i); };
 | 
			
		||||
            addAndMakeVisible (newButton);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        drawBackground (*this, g);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto b = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        layoutName.setBounds (b.removeFromBottom (20));
 | 
			
		||||
 | 
			
		||||
        Grid grid;
 | 
			
		||||
        grid.autoFlow = Grid::AutoFlow::column;
 | 
			
		||||
        grid.autoColumns = grid.autoRows = Grid::TrackInfo (Grid::Fr (1));
 | 
			
		||||
 | 
			
		||||
        for (auto& channelButton : channelButtons)
 | 
			
		||||
            grid.items.add (GridItem (channelButton));
 | 
			
		||||
 | 
			
		||||
        grid.performLayout (b.reduced (2));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getNumChannels() const
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* b = processor.getBus (false, bus))
 | 
			
		||||
            return b->getCurrentLayout().size();
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ProcessorWithLevels& processor;
 | 
			
		||||
    int bus = 0;
 | 
			
		||||
    Label layoutName;
 | 
			
		||||
    std::list<TextButton> channelButtons;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class SurroundEditor : public AudioProcessorEditor
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    explicit SurroundEditor (ProcessorWithLevels& parent)
 | 
			
		||||
        : AudioProcessorEditor (parent),
 | 
			
		||||
          customProcessor (parent),
 | 
			
		||||
          scopedUpdateEditor (customProcessor.updateEditor, [this] { updateGUI(); })
 | 
			
		||||
    {
 | 
			
		||||
        updateGUI();
 | 
			
		||||
        setResizable (true, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds();
 | 
			
		||||
        doLayout (inputViewers, r.removeFromTop (proportionOfHeight (0.5f)));
 | 
			
		||||
        doLayout (outputViewers, r);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    template <typename Range>
 | 
			
		||||
    void doLayout (Range& range, Rectangle<int> bounds) const
 | 
			
		||||
    {
 | 
			
		||||
        FlexBox fb;
 | 
			
		||||
 | 
			
		||||
        for (auto& viewer : range)
 | 
			
		||||
        {
 | 
			
		||||
            if (viewer.getNumChannels() != 0)
 | 
			
		||||
            {
 | 
			
		||||
                fb.items.add (FlexItem (viewer)
 | 
			
		||||
                                  .withFlex ((float) viewer.getNumChannels())
 | 
			
		||||
                                  .withMargin (4.0f));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fb.performLayout (bounds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateGUI()
 | 
			
		||||
    {
 | 
			
		||||
        inputViewers.clear();
 | 
			
		||||
        outputViewers.clear();
 | 
			
		||||
 | 
			
		||||
        const auto inputBuses = getAudioProcessor()->getBusCount (true);
 | 
			
		||||
 | 
			
		||||
        for (auto i = 0; i < inputBuses; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            inputViewers.emplace_back (customProcessor, i);
 | 
			
		||||
            addAndMakeVisible (inputViewers.back());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const auto outputBuses = getAudioProcessor()->getBusCount (false);
 | 
			
		||||
 | 
			
		||||
        for (auto i = 0; i < outputBuses; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            outputViewers.emplace_back (customProcessor, i);
 | 
			
		||||
            addAndMakeVisible (outputViewers.back());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const auto channels = jmax (processor.getTotalNumInputChannels(),
 | 
			
		||||
                                    processor.getTotalNumOutputChannels());
 | 
			
		||||
        setSize (jmax (150, channels * 40), 200);
 | 
			
		||||
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ProcessorWithLevels& customProcessor;
 | 
			
		||||
    ScopedValueSetter<std::function<void()>> scopedUpdateEditor;
 | 
			
		||||
    std::list<InputBusViewer> inputViewers;
 | 
			
		||||
    std::list<OutputBusViewer> outputViewers;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct SurroundProcessor  : public ProcessorWithLevels
 | 
			
		||||
{
 | 
			
		||||
    AudioProcessorEditor* createEditor() override { return new SurroundEditor (*this); }
 | 
			
		||||
    bool hasEditor() const override               { return true; }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,140 +1,140 @@
 | 
			
		||||
#ifndef _REAPER_PLUGIN_FX_EMBED_H_
 | 
			
		||||
#define _REAPER_PLUGIN_FX_EMBED_H_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * to support via VST2: canDo("hasCockosEmbeddedUI") should return 0xbeef0000
 | 
			
		||||
 * dispatcher will be called with opcode=effVendorSpecific, index=effEditDraw, value=parm2, ptr=(void*)(INT_PTR)parm3, opt=message (REAPER_FXEMBED_WM_*)
 | 
			
		||||
 *
 | 
			
		||||
 * to support via VST3: IController should support IReaperUIEmbedInterface, see reaper_vst3_interfaces.h
 | 
			
		||||
 *
 | 
			
		||||
 * to support via LV2: todo
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// these alias to win32's WM_*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_IS_SUPPORTED                 0x0000
 | 
			
		||||
/* return 1 if embedding is supported and available
 | 
			
		||||
 * return -1 if embedding is supported and unavailable
 | 
			
		||||
 * return 0 if embedding is not supported
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_CREATE                       0x0001 // called when embedding begins (return value ignored)
 | 
			
		||||
#define REAPER_FXEMBED_WM_DESTROY                      0x0002 // called when embedding ends (return value ignored)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct REAPER_FXEMBED_DrawInfo // alias of REAPER_inline_positioninfo
 | 
			
		||||
{
 | 
			
		||||
  int context;        // 0=unknown (v6.23 and earlier), 1=TCP, 2=MCP
 | 
			
		||||
  int dpi;            // 0=unknown (v6.23 and earlier), otherwise 24.8 fixed point (256=100%)
 | 
			
		||||
  int mousewheel_amt; // for REAPER_FXEMBED_WM_MOUSEWHEEL, 120 = step, typically
 | 
			
		||||
  double _res2;
 | 
			
		||||
 | 
			
		||||
  int width, height;
 | 
			
		||||
  int mouse_x, mouse_y;
 | 
			
		||||
 | 
			
		||||
  int flags; // REAPER_FXEMBED_DRAWINFO_FLAG_PAINT_OPTIONAL etc
 | 
			
		||||
  int _res3;
 | 
			
		||||
 | 
			
		||||
  void *spare[6];
 | 
			
		||||
} REAPER_FXEMBED_DrawInfo;
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_DRAWINFO_FLAG_PAINT_OPTIONAL 1
 | 
			
		||||
#define REAPER_FXEMBED_DRAWINFO_FLAG_LBUTTON_CAPTURED 0x10000
 | 
			
		||||
#define REAPER_FXEMBED_DRAWINFO_FLAG_RBUTTON_CAPTURED 0x20000
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_PAINT                        0x000F
 | 
			
		||||
/*
 | 
			
		||||
 * draw embedded UI.
 | 
			
		||||
 * parm2: REAPER_FXEMBED_IBitmap * to draw into. note
 | 
			
		||||
 * parm3: REAPER_FXEMBED_DrawInfo *
 | 
			
		||||
 *
 | 
			
		||||
 * if flags has REAPER_FXEMBED_DRAWINFO_FLAG_PAINT_OPTIONAL set, update is optional. if no change since last draw, return 0.
 | 
			
		||||
 * if flags has REAPER_FXEMBED_DRAWINFO_FLAG_LBUTTON_CAPTURED set, left mouse button is down and captured
 | 
			
		||||
 * if flags has REAPER_FXEMBED_DRAWINFO_FLAG_RBUTTON_CAPTURED set, right mouse button is down and captured
 | 
			
		||||
 *
 | 
			
		||||
 * HiDPI:
 | 
			
		||||
 * if REAPER_FXEMBED_IBitmap::Extended(REAPER_FXEMBED_EXT_GET_ADVISORY_SCALING,NULL) returns nonzero, then it is a 24.8 scalefactor for UI drawing
 | 
			
		||||
 *
 | 
			
		||||
 * return 1 if drawing occurred, 0 otherwise.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_SETCURSOR                    0x0020 // parm3: REAPER_FXEMBED_DrawInfo*. set mouse cursor and return REAPER_FXEMBED_RETNOTIFY_HANDLED, or return 0.
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_GETMINMAXINFO                0x0024
 | 
			
		||||
/*
 | 
			
		||||
 * get size hints. parm3 = (REAPER_FXEMBED_SizeHints*). return 1 if supported
 | 
			
		||||
 * note that these are just hints, the actual size may vary
 | 
			
		||||
 */
 | 
			
		||||
typedef struct REAPER_FXEMBED_SizeHints { // alias to MINMAXINFO
 | 
			
		||||
  int preferred_aspect; // 16.16 fixed point (65536 = 1:1, 32768 = 1:2, etc)
 | 
			
		||||
  int minimum_aspect;   // 16.16 fixed point
 | 
			
		||||
 | 
			
		||||
  int _res1, _res2, _res3, _res4;
 | 
			
		||||
 | 
			
		||||
  int min_width, min_height;
 | 
			
		||||
  int max_width, max_height;
 | 
			
		||||
} REAPER_FXEMBED_SizeHints;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * mouse messages
 | 
			
		||||
 * parm3 = (REAPER_FXEMBED_DrawInfo*)
 | 
			
		||||
 * capture is automatically set on mouse down, released on mouse up
 | 
			
		||||
 * when not captured, will always receive a mousemove when exiting the window
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_MOUSEMOVE                    0x0200
 | 
			
		||||
#define REAPER_FXEMBED_WM_LBUTTONDOWN                  0x0201
 | 
			
		||||
#define REAPER_FXEMBED_WM_LBUTTONUP                    0x0202
 | 
			
		||||
#define REAPER_FXEMBED_WM_LBUTTONDBLCLK                0x0203
 | 
			
		||||
#define REAPER_FXEMBED_WM_RBUTTONDOWN                  0x0204
 | 
			
		||||
#define REAPER_FXEMBED_WM_RBUTTONUP                    0x0205
 | 
			
		||||
#define REAPER_FXEMBED_WM_RBUTTONDBLCLK                0x0206
 | 
			
		||||
#define REAPER_FXEMBED_WM_MOUSEWHEEL                   0x020A
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* REAPER_FXEMBED_WM_SETCURSOR should return REAPER_FXEMBED_RETNOTIFY_HANDLED if a cursor was set
 | 
			
		||||
 */
 | 
			
		||||
#define REAPER_FXEMBED_RETNOTIFY_HANDLED    0x0000001
 | 
			
		||||
 | 
			
		||||
/* if the mouse messages return with REAPER_FXEMBED_RETNOTIFY_INVALIDATE set, a non-optional
 | 
			
		||||
 * redraw is initiated (generally sooner than the next timer-based redraw)
 | 
			
		||||
 */
 | 
			
		||||
#define REAPER_FXEMBED_RETNOTIFY_INVALIDATE 0x1000000
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * bitmap interface
 | 
			
		||||
 * this is an alias of LICE_IBitmap etc from WDL/lice/lice.h
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#define REAPER_FXEMBED_RGBA(r,g,b,a) (((b)&0xff)|(((g)&0xff)<<8)|(((r)&0xff)<<16)|(((a)&0xff)<<24))
 | 
			
		||||
#define REAPER_FXEMBED_GETB(v) ((v)&0xff)
 | 
			
		||||
#define REAPER_FXEMBED_GETG(v) (((v)>>8)&0xff)
 | 
			
		||||
#define REAPER_FXEMBED_GETR(v) (((v)>>16)&0xff)
 | 
			
		||||
#define REAPER_FXEMBED_GETA(v) (((v)>>24)&0xff)
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
class REAPER_FXEMBED_IBitmap // alias of LICE_IBitmap
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  virtual ~REAPER_FXEMBED_IBitmap() { }
 | 
			
		||||
 | 
			
		||||
  virtual unsigned int *getBits()=0;
 | 
			
		||||
  virtual int getWidth()=0;
 | 
			
		||||
  virtual int getHeight()=0;
 | 
			
		||||
  virtual int getRowSpan()=0; // includes any off-bitmap data. this is in sizeof(unsigned int) units, not bytes.
 | 
			
		||||
  virtual bool isFlipped() { return false;  }
 | 
			
		||||
  virtual bool resize(int w, int h)=0;
 | 
			
		||||
 | 
			
		||||
  virtual void *getDC() { return 0; } // do not use
 | 
			
		||||
 | 
			
		||||
  virtual INT_PTR Extended(int id, void* data) { return 0; }
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_EXT_GET_ADVISORY_SCALING 0x2003 // data ignored, returns .8 fixed point. returns 0 if unscaled
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef _REAPER_PLUGIN_FX_EMBED_H_
 | 
			
		||||
#define _REAPER_PLUGIN_FX_EMBED_H_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * to support via VST2: canDo("hasCockosEmbeddedUI") should return 0xbeef0000
 | 
			
		||||
 * dispatcher will be called with opcode=effVendorSpecific, index=effEditDraw, value=parm2, ptr=(void*)(INT_PTR)parm3, opt=message (REAPER_FXEMBED_WM_*)
 | 
			
		||||
 *
 | 
			
		||||
 * to support via VST3: IController should support IReaperUIEmbedInterface, see reaper_vst3_interfaces.h
 | 
			
		||||
 *
 | 
			
		||||
 * to support via LV2: todo
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// these alias to win32's WM_*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_IS_SUPPORTED                 0x0000
 | 
			
		||||
/* return 1 if embedding is supported and available
 | 
			
		||||
 * return -1 if embedding is supported and unavailable
 | 
			
		||||
 * return 0 if embedding is not supported
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_CREATE                       0x0001 // called when embedding begins (return value ignored)
 | 
			
		||||
#define REAPER_FXEMBED_WM_DESTROY                      0x0002 // called when embedding ends (return value ignored)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct REAPER_FXEMBED_DrawInfo // alias of REAPER_inline_positioninfo
 | 
			
		||||
{
 | 
			
		||||
  int context;        // 0=unknown (v6.23 and earlier), 1=TCP, 2=MCP
 | 
			
		||||
  int dpi;            // 0=unknown (v6.23 and earlier), otherwise 24.8 fixed point (256=100%)
 | 
			
		||||
  int mousewheel_amt; // for REAPER_FXEMBED_WM_MOUSEWHEEL, 120 = step, typically
 | 
			
		||||
  double _res2;
 | 
			
		||||
 | 
			
		||||
  int width, height;
 | 
			
		||||
  int mouse_x, mouse_y;
 | 
			
		||||
 | 
			
		||||
  int flags; // REAPER_FXEMBED_DRAWINFO_FLAG_PAINT_OPTIONAL etc
 | 
			
		||||
  int _res3;
 | 
			
		||||
 | 
			
		||||
  void *spare[6];
 | 
			
		||||
} REAPER_FXEMBED_DrawInfo;
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_DRAWINFO_FLAG_PAINT_OPTIONAL 1
 | 
			
		||||
#define REAPER_FXEMBED_DRAWINFO_FLAG_LBUTTON_CAPTURED 0x10000
 | 
			
		||||
#define REAPER_FXEMBED_DRAWINFO_FLAG_RBUTTON_CAPTURED 0x20000
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_PAINT                        0x000F
 | 
			
		||||
/*
 | 
			
		||||
 * draw embedded UI.
 | 
			
		||||
 * parm2: REAPER_FXEMBED_IBitmap * to draw into. note
 | 
			
		||||
 * parm3: REAPER_FXEMBED_DrawInfo *
 | 
			
		||||
 *
 | 
			
		||||
 * if flags has REAPER_FXEMBED_DRAWINFO_FLAG_PAINT_OPTIONAL set, update is optional. if no change since last draw, return 0.
 | 
			
		||||
 * if flags has REAPER_FXEMBED_DRAWINFO_FLAG_LBUTTON_CAPTURED set, left mouse button is down and captured
 | 
			
		||||
 * if flags has REAPER_FXEMBED_DRAWINFO_FLAG_RBUTTON_CAPTURED set, right mouse button is down and captured
 | 
			
		||||
 *
 | 
			
		||||
 * HiDPI:
 | 
			
		||||
 * if REAPER_FXEMBED_IBitmap::Extended(REAPER_FXEMBED_EXT_GET_ADVISORY_SCALING,NULL) returns nonzero, then it is a 24.8 scalefactor for UI drawing
 | 
			
		||||
 *
 | 
			
		||||
 * return 1 if drawing occurred, 0 otherwise.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_SETCURSOR                    0x0020 // parm3: REAPER_FXEMBED_DrawInfo*. set mouse cursor and return REAPER_FXEMBED_RETNOTIFY_HANDLED, or return 0.
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_GETMINMAXINFO                0x0024
 | 
			
		||||
/*
 | 
			
		||||
 * get size hints. parm3 = (REAPER_FXEMBED_SizeHints*). return 1 if supported
 | 
			
		||||
 * note that these are just hints, the actual size may vary
 | 
			
		||||
 */
 | 
			
		||||
typedef struct REAPER_FXEMBED_SizeHints { // alias to MINMAXINFO
 | 
			
		||||
  int preferred_aspect; // 16.16 fixed point (65536 = 1:1, 32768 = 1:2, etc)
 | 
			
		||||
  int minimum_aspect;   // 16.16 fixed point
 | 
			
		||||
 | 
			
		||||
  int _res1, _res2, _res3, _res4;
 | 
			
		||||
 | 
			
		||||
  int min_width, min_height;
 | 
			
		||||
  int max_width, max_height;
 | 
			
		||||
} REAPER_FXEMBED_SizeHints;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * mouse messages
 | 
			
		||||
 * parm3 = (REAPER_FXEMBED_DrawInfo*)
 | 
			
		||||
 * capture is automatically set on mouse down, released on mouse up
 | 
			
		||||
 * when not captured, will always receive a mousemove when exiting the window
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_WM_MOUSEMOVE                    0x0200
 | 
			
		||||
#define REAPER_FXEMBED_WM_LBUTTONDOWN                  0x0201
 | 
			
		||||
#define REAPER_FXEMBED_WM_LBUTTONUP                    0x0202
 | 
			
		||||
#define REAPER_FXEMBED_WM_LBUTTONDBLCLK                0x0203
 | 
			
		||||
#define REAPER_FXEMBED_WM_RBUTTONDOWN                  0x0204
 | 
			
		||||
#define REAPER_FXEMBED_WM_RBUTTONUP                    0x0205
 | 
			
		||||
#define REAPER_FXEMBED_WM_RBUTTONDBLCLK                0x0206
 | 
			
		||||
#define REAPER_FXEMBED_WM_MOUSEWHEEL                   0x020A
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* REAPER_FXEMBED_WM_SETCURSOR should return REAPER_FXEMBED_RETNOTIFY_HANDLED if a cursor was set
 | 
			
		||||
 */
 | 
			
		||||
#define REAPER_FXEMBED_RETNOTIFY_HANDLED    0x0000001
 | 
			
		||||
 | 
			
		||||
/* if the mouse messages return with REAPER_FXEMBED_RETNOTIFY_INVALIDATE set, a non-optional
 | 
			
		||||
 * redraw is initiated (generally sooner than the next timer-based redraw)
 | 
			
		||||
 */
 | 
			
		||||
#define REAPER_FXEMBED_RETNOTIFY_INVALIDATE 0x1000000
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * bitmap interface
 | 
			
		||||
 * this is an alias of LICE_IBitmap etc from WDL/lice/lice.h
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#define REAPER_FXEMBED_RGBA(r,g,b,a) (((b)&0xff)|(((g)&0xff)<<8)|(((r)&0xff)<<16)|(((a)&0xff)<<24))
 | 
			
		||||
#define REAPER_FXEMBED_GETB(v) ((v)&0xff)
 | 
			
		||||
#define REAPER_FXEMBED_GETG(v) (((v)>>8)&0xff)
 | 
			
		||||
#define REAPER_FXEMBED_GETR(v) (((v)>>16)&0xff)
 | 
			
		||||
#define REAPER_FXEMBED_GETA(v) (((v)>>24)&0xff)
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
class REAPER_FXEMBED_IBitmap // alias of LICE_IBitmap
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  virtual ~REAPER_FXEMBED_IBitmap() { }
 | 
			
		||||
 | 
			
		||||
  virtual unsigned int *getBits()=0;
 | 
			
		||||
  virtual int getWidth()=0;
 | 
			
		||||
  virtual int getHeight()=0;
 | 
			
		||||
  virtual int getRowSpan()=0; // includes any off-bitmap data. this is in sizeof(unsigned int) units, not bytes.
 | 
			
		||||
  virtual bool isFlipped() { return false;  }
 | 
			
		||||
  virtual bool resize(int w, int h)=0;
 | 
			
		||||
 | 
			
		||||
  virtual void *getDC() { return 0; } // do not use
 | 
			
		||||
 | 
			
		||||
  virtual INT_PTR Extended(int id, void* data) { return 0; }
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define REAPER_FXEMBED_EXT_GET_ADVISORY_SCALING 0x2003 // data ignored, returns .8 fixed point. returns 0 if unscaled
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +1,31 @@
 | 
			
		||||
#ifndef _REAPER_VST3_INTERFACES_H_
 | 
			
		||||
#define _REAPER_VST3_INTERFACES_H_
 | 
			
		||||
 | 
			
		||||
class IReaperHostApplication : public FUnknown // available from IHostApplication in REAPER v5.02+
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  // Gets a REAPER Extension API function by name, returns NULL is failed
 | 
			
		||||
  virtual void* PLUGIN_API getReaperApi(CStringA funcname) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void* PLUGIN_API getReaperParent(uint32 w) = 0; // get parent track(=1), take(=2), project(=3), fxdsp(=4), trackchan(=5)
 | 
			
		||||
 | 
			
		||||
  // Multi-purpose function, returns NULL if unsupported
 | 
			
		||||
  virtual void* PLUGIN_API reaperExtended(uint32 call, void *parm1, void *parm2, void *parm3) = 0;
 | 
			
		||||
 | 
			
		||||
  static const FUID iid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DECLARE_CLASS_IID (IReaperHostApplication, 0x79655E36, 0x77EE4267, 0xA573FEF7, 0x4912C27C)
 | 
			
		||||
 | 
			
		||||
class IReaperUIEmbedInterface : public FUnknown // supported by REAPER v6.24+, queried from plug-in IController
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
    // note: VST2 uses CanDo "hasCockosEmbeddedUI"==0xbeef0000, then opcode=effVendorSpecific, index=effEditDraw, opt=(float)msg, value=parm2, ptr=parm3
 | 
			
		||||
    // see reaper_plugin_fx_embed.h
 | 
			
		||||
  virtual Steinberg::TPtrInt embed_message(int msg, Steinberg::TPtrInt parm2, Steinberg::TPtrInt parm3) = 0;
 | 
			
		||||
 | 
			
		||||
  static const FUID iid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DECLARE_CLASS_IID (IReaperUIEmbedInterface, 0x049bf9e7, 0xbc74ead0, 0xc4101e86, 0x7f725981)
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef _REAPER_VST3_INTERFACES_H_
 | 
			
		||||
#define _REAPER_VST3_INTERFACES_H_
 | 
			
		||||
 | 
			
		||||
class IReaperHostApplication : public FUnknown // available from IHostApplication in REAPER v5.02+
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  // Gets a REAPER Extension API function by name, returns NULL is failed
 | 
			
		||||
  virtual void* PLUGIN_API getReaperApi(CStringA funcname) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void* PLUGIN_API getReaperParent(uint32 w) = 0; // get parent track(=1), take(=2), project(=3), fxdsp(=4), trackchan(=5)
 | 
			
		||||
 | 
			
		||||
  // Multi-purpose function, returns NULL if unsupported
 | 
			
		||||
  virtual void* PLUGIN_API reaperExtended(uint32 call, void *parm1, void *parm2, void *parm3) = 0;
 | 
			
		||||
 | 
			
		||||
  static const FUID iid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DECLARE_CLASS_IID (IReaperHostApplication, 0x79655E36, 0x77EE4267, 0xA573FEF7, 0x4912C27C)
 | 
			
		||||
 | 
			
		||||
class IReaperUIEmbedInterface : public FUnknown // supported by REAPER v6.24+, queried from plug-in IController
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
    // note: VST2 uses CanDo "hasCockosEmbeddedUI"==0xbeef0000, then opcode=effVendorSpecific, index=effEditDraw, opt=(float)msg, value=parm2, ptr=parm3
 | 
			
		||||
    // see reaper_plugin_fx_embed.h
 | 
			
		||||
  virtual Steinberg::TPtrInt embed_message(int msg, Steinberg::TPtrInt parm2, Steinberg::TPtrInt parm3) = 0;
 | 
			
		||||
 | 
			
		||||
  static const FUID iid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DECLARE_CLASS_IID (IReaperUIEmbedInterface, 0x049bf9e7, 0xbc74ead0, 0xc4101e86, 0x7f725981)
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user