diff --git a/ear-production-suite-plugins/lib/CMakeLists.txt b/ear-production-suite-plugins/lib/CMakeLists.txt index 54355b6e0..0bd54d044 100644 --- a/ear-production-suite-plugins/lib/CMakeLists.txt +++ b/ear-production-suite-plugins/lib/CMakeLists.txt @@ -10,7 +10,7 @@ set(EAR_PROTO_INPUT protobuf_generate_cpp(EAR_PROTO_SRC EAR_PROTO_HDR ${EAR_PROTO_INPUT}) -add_library(ear-plugin-base STATIC +set(EAR_BASE_SOURCES src/communication/commands.cpp src/communication/metadata_sender.cpp src/communication/direct_speakers_metadata_sender.cpp @@ -56,7 +56,7 @@ add_library(ear-plugin-base STATIC ${EAR_PROTO_SRC} src/restored_pending_store.cpp ) -set(EAR_BASE_HEADERS +set(EAR_BASE_HEADERS include/binaural_monitoring_audio_processor.hpp include/binaural_monitoring_backend.hpp include/communication/commands.hpp @@ -131,10 +131,13 @@ set(EAR_BASE_HEADERS include/scene_store.hpp include/communication/metadata_thread.hpp include/auto_mode_controller.hpp + include/reaper_integration.hpp + include/reaper_vst3_interfaces.h ${EPS_SHARED_DIR}/helper/container_helpers.hpp ${EPS_SHARED_DIR}/speaker_setups.hpp ) +add_library(ear-plugin-base STATIC ${EAR_BASE_HEADERS} ${EAR_BASE_SOURCES}) set_source_files_properties(${EAR_PROTO_INPUT} PROPERTIES HEADER_ONLY TRUE) source_group("Header Files" FILES ${EAR_BASE_HEADERS}) target_sources(ear-plugin-base PRIVATE ${EAR_PROTO_INPUT} ${EAR_BASE_HEADERS}) diff --git a/ear-production-suite-plugins/lib/include/reaper_integration.hpp b/ear-production-suite-plugins/lib/include/reaper_integration.hpp new file mode 100644 index 000000000..d1c04fb3c --- /dev/null +++ b/ear-production-suite-plugins/lib/include/reaper_integration.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include "reaper_vst3_interfaces.h" +#include + +inline int DetermineChannelCount(IReaperHostApplication* reaperHostPtr) { + if (!reaperHostPtr) { + return MAX_DAW_CHANNELS; + } + auto getAppVersionPtr = reaperHostPtr->getReaperApi("GetAppVersion"); + if (!getAppVersionPtr) { + return MAX_DAW_CHANNELS; + } + return GetReaperChannelCount(getAppVersionPtr); +} + +// When a plugin is initialised, it should register itself with the extension using this function +// A callback function should be provided as an argument, +// which the extension will use to return XML data to set the plugin state +inline bool registerPluginLoadWithExtension( + IReaperHostApplication* reaperHostPtr, + std::function callback) { + if (!reaperHostPtr) return false; + void registerPluginLoadSig(std::function); + auto registerPluginLoadPtr = reaperHostPtr->getReaperApi("registerPluginLoad"); + if (!registerPluginLoadPtr) return false; + auto registerPluginLoad = reinterpret_cast(registerPluginLoadPtr); + registerPluginLoad(callback); + return true; +} + +// Queries the extension to get a unique ID for a plugin instance +inline uint32_t requestInstanceIdFromExtension( + IReaperHostApplication* reaperHostPtr) { + if(!reaperHostPtr) return 0; + uint32_t requestInputInstanceIdSig(); + auto requestInputInstanceIdPtr = reaperHostPtr->getReaperApi("requestInputInstanceId"); + if (!requestInputInstanceIdPtr) return 0; + auto requestInputInstanceId = reinterpret_cast(requestInputInstanceIdPtr); + return requestInputInstanceId(); +} diff --git a/ear-production-suite-plugins/lib/src/store_metadata.cpp b/ear-production-suite-plugins/lib/src/store_metadata.cpp index 93c176843..ff3aaae71 100644 --- a/ear-production-suite-plugins/lib/src/store_metadata.cpp +++ b/ear-production-suite-plugins/lib/src/store_metadata.cpp @@ -391,7 +391,7 @@ void Metadata::setElementOrder(const ProgrammeInternalId &progId, const std::vec } // This is horribly inefficient algorithm wise (O(n^2)?). -// OTOH, order.size() is always <= 64 so a linear vector search probably still wins over a map. (it may fit in cache.) +// OTOH, order.size() is probably <= 128 (unless asset reuse and huge project) so a linear vector search probably still wins over a map. (it may fit in cache.) // If this ends up profiling slow, try changing it. // This should only be called rarely (on a routing change in auto mode or an element move in the GUI), // so it's not on a hot path. diff --git a/ear-production-suite-plugins/plugins/binaural_monitoring/src/binaural_monitoring_plugin_processor.cpp b/ear-production-suite-plugins/plugins/binaural_monitoring/src/binaural_monitoring_plugin_processor.cpp index 5ef87a5ab..429e61026 100644 --- a/ear-production-suite-plugins/plugins/binaural_monitoring/src/binaural_monitoring_plugin_processor.cpp +++ b/ear-production-suite-plugins/plugins/binaural_monitoring/src/binaural_monitoring_plugin_processor.cpp @@ -6,6 +6,7 @@ #include "variable_block_adapter.hpp" #include #include +#include #define DEFAULT_OSC_PORT 8000 @@ -108,8 +109,8 @@ EarBinauralMonitoringAudioProcessor::EarBinauralMonitoringAudioProcessor() bypass_->setValueNotifyingHost(bypass_->get()); }; - backend_ = - std::make_unique(nullptr, 64); + backend_ = std::make_unique( + nullptr, MAX_DAW_CHANNELS); connector_ = std::make_unique(this); connector_->setListenerOrientationInstance(backend_->listenerOrientation); @@ -158,7 +159,7 @@ EarBinauralMonitoringAudioProcessor::EarBinauralMonitoringAudioProcessor() std::lock_guard lock(processorMutex_); processor_ = std::make_unique( - 64, 64, 64, 48000, 512, + MAX_DAW_CHANNELS, MAX_DAW_CHANNELS, MAX_DAW_CHANNELS, 48000, 512, bearDataFilePath); // Used to verify if BEAR can be initialised - can't // get SR and block size in ctor. Made assumption - // prepareToPlay will be called with correct values @@ -218,7 +219,7 @@ void EarBinauralMonitoringAudioProcessor::timerCallback() { juce::AudioProcessor::BusesProperties EarBinauralMonitoringAudioProcessor::_getBusProperties() { auto ret = BusesProperties().withInput( - "Input", AudioChannelSet::discreteChannels(64), true); + "Input", AudioChannelSet::discreteChannels(MAX_DAW_CHANNELS), true); ret = ret.withOutput("Left Ear", AudioChannelSet::mono(), true); ret = ret.withOutput("Right Ear", AudioChannelSet::mono(), true); ret = ret.withOutput("(Unused)", AudioChannelSet::discreteChannels(62), true); @@ -307,7 +308,9 @@ void EarBinauralMonitoringAudioProcessor::prepareToPlay(double sampleRate, if (!processor_ || !processor_->configMatches(sampleRate, samplesPerBlock)) { processor_ = std::make_unique( - 64, 64, 64, sampleRate, samplesPerBlock, bearDataFilePath); + MAX_DAW_CHANNELS, MAX_DAW_CHANNELS, MAX_DAW_CHANNELS, + sampleRate, samplesPerBlock, + bearDataFilePath); } } @@ -323,7 +326,8 @@ bool EarBinauralMonitoringAudioProcessor::isBusesLayoutSupported( if(layouts.inputBuses.size() != 1) return false; - if(layouts.inputBuses[0] != AudioChannelSet::discreteChannels(64)) + if (layouts.inputBuses[0] != + AudioChannelSet::discreteChannels(MAX_DAW_CHANNELS)) return false; if(layouts.outputBuses.size() != 3) diff --git a/ear-production-suite-plugins/plugins/direct_speakers/CMakeLists.txt b/ear-production-suite-plugins/plugins/direct_speakers/CMakeLists.txt index 59cee205c..9f54a60e9 100644 --- a/ear-production-suite-plugins/plugins/direct_speakers/CMakeLists.txt +++ b/ear-production-suite-plugins/plugins/direct_speakers/CMakeLists.txt @@ -30,6 +30,7 @@ set(HEADERS_DIRECT_SPEAKERS_INPUT ${EPS_SHARED_DIR}/components/onboarding.hpp ${EPS_SHARED_DIR}/components/onboarding_slide.hpp ${EPS_SHARED_DIR}/components/overlay.hpp + ${EPS_SHARED_DIR}/components/routing_info_icon.hpp ${EPS_SHARED_DIR}/components/segment_progress_bar.hpp ${EPS_SHARED_DIR}/components/speaker_layer.hpp ${EPS_SHARED_DIR}/components/version_label.hpp diff --git a/ear-production-suite-plugins/plugins/direct_speakers/src/channel_meter_layout.hpp b/ear-production-suite-plugins/plugins/direct_speakers/src/channel_meter_layout.hpp index 7e71f66e3..35538c97c 100644 --- a/ear-production-suite-plugins/plugins/direct_speakers/src/channel_meter_layout.hpp +++ b/ear-production-suite-plugins/plugins/direct_speakers/src/channel_meter_layout.hpp @@ -60,6 +60,9 @@ class ChannelMeterLayout : public Component { if(speakerLevels_.size() > 18) meterRows = 4; auto metersBoxHeight = area.getHeight() / meterRows; + // 6 meters require 337 px - centre them with left padding + int leftPad = (area.getWidth() - 337) / 2; + if (leftPad > 0) area.removeFromLeft(leftPad); auto metersBoxArea = area.removeFromTop(metersBoxHeight); channelMeterBox1to6_->setBounds(metersBoxArea.reduced(0, 5)); diff --git a/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_component.hpp b/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_component.hpp index 79d910cd9..44aaaa4bc 100644 --- a/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_component.hpp +++ b/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_component.hpp @@ -2,6 +2,7 @@ #include "JuceHeader.h" +#include "components/look_and_feel/tooltips.hpp" #include "components/ear_combo_box.hpp" #include "components/onboarding.hpp" #include "components/overlay.hpp" @@ -42,6 +43,10 @@ class DirectSpeakersComponent : public Component, propertiesFileLock( std::make_unique("EPS_preferences")), propertiesFile(getPropertiesFile(propertiesFileLock.get())) { + + tooltipWindow.setLookAndFeel(&tooltipLookAndFeel); + tooltipWindow.setOpaque(false); + header->setText(" Direct Speakers"); onBoardingButton->setButtonText("?"); @@ -68,6 +73,7 @@ class DirectSpeakersComponent : public Component, addChildComponent(onBoardingOverlay.get()); //addAndMakeVisible(metadataValueBox.get()); + mainValueBox->showRoutingTooltip(p->getNumDawChannels() < 128); addAndMakeVisible(mainValueBox.get()); addAndMakeVisible(channelMetersBox.get()); @@ -110,8 +116,10 @@ class DirectSpeakersComponent : public Component, headingArea.removeFromRight(39).removeFromBottom(39)); header->setBounds(headingArea); - auto leftColumn = area.withTrimmedRight(area.getWidth() / 2); - auto rightColumn = area.withTrimmedLeft(area.getWidth() / 2); + auto leftColumnWidth = area.getWidth() / 2; + if (leftColumnWidth < 385) leftColumnWidth = 385; + auto leftColumn = area.removeFromLeft(leftColumnWidth); + auto rightColumn = area; // left column mainValueBox->setBounds(leftColumn.removeFromTop(223).reduced(5, 5)); @@ -161,6 +169,10 @@ class DirectSpeakersComponent : public Component, private: DirectSpeakersAudioProcessor* p_; + + TooltipWindow tooltipWindow{this}; + TooltipLookAndFeel tooltipLookAndFeel; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DirectSpeakersComponent) }; diff --git a/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_frontend_connector.cpp b/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_frontend_connector.cpp index 36d542033..fac02a63b 100644 --- a/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_frontend_connector.cpp +++ b/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_frontend_connector.cpp @@ -5,6 +5,7 @@ #include "detail/weak_ptr_helpers.hpp" #include "speaker_setups.hpp" +#include namespace { String routingLayoutDescriptionAt(int position, int layoutSizeFixed) { @@ -186,8 +187,9 @@ void DirectSpeakersJuceFrontendConnector::setSpeakerSetup( speakerSetupByIndex(cachedSpeakerSetupIndex_).speakers.size(); routingComboBoxLocked->clearEntries(); auto layoutSizeFixed = layoutSize != 0 ? layoutSize - 1 : layoutSize; - for (int i = 1; i + layoutSizeFixed <= 64; ++i) { - routingComboBoxLocked->addTextEntry(routingLayoutDescriptionAt(i, layoutSizeFixed)); + for (int i = 1; i + layoutSizeFixed <= MAX_DAW_CHANNELS; ++i) { + auto entry = routingComboBoxLocked->addTextEntry(routingLayoutDescriptionAt(i, layoutSizeFixed), i); + entry->setSelectable(i + layoutSizeFixed <= p_->getNumDawChannels()); } routingComboBoxLocked->selectEntry(cachedRouting_, sendNotification); } diff --git a/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_plugin_processor.cpp b/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_plugin_processor.cpp index 6c8157cf7..56f8c169c 100644 --- a/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_plugin_processor.cpp +++ b/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_plugin_processor.cpp @@ -4,9 +4,7 @@ #include "components/non_automatable_parameter.hpp" #include "direct_speakers_plugin_editor.hpp" #include "direct_speakers_frontend_connector.hpp" - -void registerPluginLoadSig(std::function); -uint32_t requestInputInstanceIdSig(); +#include "reaper_integration.hpp" using namespace ear::plugin; @@ -24,7 +22,7 @@ DirectSpeakersAudioProcessor::DirectSpeakersAudioProcessor() addParameter(routing_ = new ui::NonAutomatedParameter( "routing", "Routing", - -1, 63, -1)); + -1, MAX_DAW_CHANNELS-1, -1)); addParameter(packFormatIdValue_ = new ui::NonAutomatedParameter( "packFormatIdValue", "PackFormat ID Value", @@ -197,20 +195,15 @@ void DirectSpeakersAudioProcessor::setIHostApplication(Steinberg::FUnknown * unk VST3ClientExtensions::setIHostApplication(unknown); if(reaperHost) { - auto requestInputInstanceIdPtr = reaperHost->getReaperApi("requestInputInstanceId"); - if(requestInputInstanceIdPtr) { - auto requestInputInstanceId = reinterpret_cast(requestInputInstanceIdPtr); - uint32_t inputInstanceId = requestInputInstanceId(); - inputInstanceId_->internalSetIntAndNotifyHost(inputInstanceId); - } + inputInstanceId_->internalSetIntAndNotifyHost( + requestInstanceIdFromExtension(reaperHost)); - auto registerPluginLoadPtr = reaperHost->getReaperApi("registerPluginLoad"); - if(registerPluginLoadPtr) { - auto registerPluginLoad = reinterpret_cast(registerPluginLoadPtr); - registerPluginLoad([this](std::string const& xmlState) { - this->extensionSetState(xmlState); - }); - } + registerPluginLoadWithExtension(reaperHost, + [this](std::string const& xmlState) { + this->extensionSetState(xmlState); + }); + + numDawChannels_ = DetermineChannelCount(reaperHost); } } diff --git a/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_plugin_processor.hpp b/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_plugin_processor.hpp index 0c67d507e..d5b49fce5 100644 --- a/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_plugin_processor.hpp +++ b/ear-production-suite-plugins/plugins/direct_speakers/src/direct_speakers_plugin_processor.hpp @@ -6,6 +6,7 @@ #include "components/level_meter_calculator.hpp" #include "reaper_vst3_interfaces.h" #include "components/read_only_audio_parameter_int.hpp" +#include #include @@ -72,6 +73,8 @@ class DirectSpeakersAudioProcessor : public AudioProcessor, public VST3ClientExt void setIHostApplication(Steinberg::FUnknown *unknown) override; void extensionSetState(std::string const& xmlState); + int getNumDawChannels() { return numDawChannels_; } + private: IReaperHostApplication* reaperHost{ nullptr }; ear::plugin::communication::ConnectionId connectionId_; @@ -87,6 +90,7 @@ class DirectSpeakersAudioProcessor : public AudioProcessor, public VST3ClientExt std::unique_ptr backend_; int samplerate_; + int numDawChannels_{MAX_DAW_CHANNELS}; std::shared_ptr levelMeter_; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DirectSpeakersAudioProcessor) diff --git a/ear-production-suite-plugins/plugins/direct_speakers/src/value_box_main.hpp b/ear-production-suite-plugins/plugins/direct_speakers/src/value_box_main.hpp index 938d05417..cacb77fbb 100644 --- a/ear-production-suite-plugins/plugins/direct_speakers/src/value_box_main.hpp +++ b/ear-production-suite-plugins/plugins/direct_speakers/src/value_box_main.hpp @@ -4,6 +4,7 @@ #include "components/ear_combo_box.hpp" #include "components/ear_name_text_editor.hpp" +#include "components/routing_info_icon.hpp" #include "components/look_and_feel/colours.hpp" #include "components/look_and_feel/fonts.hpp" @@ -20,6 +21,7 @@ class ValueBoxMain : public Component { speakerSetupLabel_(std::make_unique