From 2ca8881b76a25d77ad2036065439ade7526a779a Mon Sep 17 00:00:00 2001 From: AndyMc <8242017+AndyMcProducer@users.noreply.github.com> Date: Fri, 17 Apr 2026 12:24:40 +0100 Subject: [PATCH 01/12] Implement Remote MOGG Multitrack Playback and Control - Synchronized MOGG metadata (isMogg, stemCount) across remote peers. - Added MoggMixerButton to remote peer UI. - Implemented MoggMixerWindow for remote peer stem control (gain, mute, solo). - Supported MIDI Learn for remote MOGG mixer controls. - Automatic multichannel send mode when sending MOGG files. - Added Full Mix master group for MOGG playback. --- CMakeLists.txt | 2 +- Source/ChannelGroupsView.cpp | 347 ++++++++++----- Source/ChannelGroupsView.h | 8 +- Source/MoggMixerView.h | 383 +++++++++++++++++ Source/SonobusPluginEditor.cpp | 66 ++- Source/SonobusPluginEditor.h | 6 + Source/SonobusPluginProcessor.cpp | 588 +++++++++++++++++++++----- Source/SonobusPluginProcessor.h | 89 +++- Source/Soundboard.h | 4 +- Source/SoundboardChannelProcessor.cpp | 2 +- 10 files changed, 1249 insertions(+), 246 deletions(-) create mode 100644 Source/MoggMixerView.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ddd78e86..0ec1b9fa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -447,7 +447,7 @@ function(sono_add_custom_plugin_target target_name product_name formats is_instr JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 JUCE_USE_WINDOWS_MEDIA_FORMAT=1 JUCE_LOAD_CURL_SYMBOLS_LAZILY=1 - JUCE_ASIO=1 + JUCE_ASIO=0 JUCE_WASAPI=1 JUCE_DIRECTSOUND=0 JUCE_USE_ANDROID_OBOE=1 diff --git a/Source/ChannelGroupsView.cpp b/Source/ChannelGroupsView.cpp index 5a7bd6172..c46f1ca1d 100644 --- a/Source/ChannelGroupsView.cpp +++ b/Source/ChannelGroupsView.cpp @@ -514,9 +514,10 @@ void ChannelGroupMonitorEffectsView::updateStateForInput() reverbSendView->getHeaderComponent()->setVisible(false); } } - else if (groupIndex == -2) { + else if (groupIndex <= -100) { // file playback - if (processor.getFilePlaybackMonitorDelayParams(monDelayParams)) { + int fidx = -(groupIndex + 100); + if (processor.getFilePlaybackMonitorDelayParams(fidx, monDelayParams)) { delayView->updateParams(monDelayParams); } @@ -641,10 +642,11 @@ void ChannelGroupMonitorEffectsView::monitorDelayParamsChanged(MonitorDelayView processor.getMetronomeMonitorDelayParams(eparam); wason = eparam.enabled; processor.setMetronomeMonitorDelayParams(params); - } else if (groupIndex == -2) { - processor.getFilePlaybackMonitorDelayParams(eparam); + } else if (groupIndex <= -100) { + int fidx = -(groupIndex + 100); + processor.getFilePlaybackMonitorDelayParams(fidx, eparam); wason = eparam.enabled; - processor.setFilePlaybackMonitorDelayParams(params); + processor.setFilePlaybackMonitorDelayParams(fidx, params); } else if (groupIndex == -3) { eparam = processor.getSoundboardProcessor()->getMonitorDelayParams(); wason = eparam.enabled; @@ -671,10 +673,12 @@ void ChannelGroupMonitorEffectsView::monitorDelayParamsChanged(MonitorDelayView processor.setMetronomeMonitorDelayParams(eparam); } - processor.getFilePlaybackMonitorDelayParams(eparam); - if (eparam.delayTimeMs != deltimems) { - eparam.delayTimeMs = deltimems; - processor.setFilePlaybackMonitorDelayParams(eparam); + for (int fidx=0; fidx < processor.getFilePlaybackGroupCount(); ++fidx) { + processor.getFilePlaybackMonitorDelayParams(fidx, eparam); + if (eparam.delayTimeMs != deltimems) { + eparam.delayTimeMs = deltimems; + processor.setFilePlaybackMonitorDelayParams(fidx, eparam); + } } eparam = processor.getSoundboardProcessor()->getMonitorDelayParams(); @@ -712,10 +716,11 @@ void ChannelGroupMonitorEffectsView::effectsHeaderClicked(EffectsBaseView *comp) processor.getMetronomeMonitorDelayParams(params); params.enabled = !params.enabled; processor.setMetronomeMonitorDelayParams(params); - } else if (groupIndex == -2) { - processor.getFilePlaybackMonitorDelayParams(params); + } else if (groupIndex <= -100) { + int fidx = -(groupIndex + 100); + processor.getFilePlaybackMonitorDelayParams(fidx, params); params.enabled = !params.enabled; - processor.setFilePlaybackMonitorDelayParams(params); + processor.setFilePlaybackMonitorDelayParams(fidx, params); } else if (groupIndex == -3) { params = processor.getSoundboardProcessor()->getMonitorDelayParams(); @@ -917,6 +922,7 @@ void ChannelGroupView::resized() ChannelGroupsView::ChannelGroupsView(SonobusAudioProcessor& proc, bool peerMode, int peerIndex) : Component("pcv"), addLnf(20), processor(proc), mPeerMode(peerMode), mPeerIndex(peerIndex) { + processor.addMidiLearnListener(this); mutedTextColor = Colour::fromFloatRGBA(0.8, 0.5, 0.2, 1.0); regularTextColor = Colour(0xa0eeeeee);; //Colour(0xc0eeeeee); dimTextColor = Colour(0xa0aaaaaa); //Colour(0xc0aaaaaa); @@ -999,11 +1005,68 @@ ChannelGroupsView::ChannelGroupsView(SonobusAudioProcessor& proc, bool peerMode, ChannelGroupsView::~ChannelGroupsView() { + processor.removeMidiLearnListener(this); if (mEffectsView) { mEffectsView->removeListener(this); } } +// Called on the message thread whenever a MIDI mapping changes +void ChannelGroupsView::midiMappingChanged() +{ + // Refresh button labels to reflect new mapping status + MessageManager::callAsync([this]() { + updateChannelViews(); + }); +} + +void ChannelGroupsView::showFileStemMidiMenu(Component* source, + SonobusAudioProcessor::MidiTargetType type, + int stemIdx) +{ + using MidiTargetType = SonobusAudioProcessor::MidiTargetType; + + int ccNum = -1, midiCh = 0; + bool hasMapped = processor.getMidiMapping(type, stemIdx, ccNum, midiCh); + + String controlName; + switch (type) { + case MidiTargetType::MidiTarget_FileStemGain: controlName = "Fader " + String(stemIdx+1); break; + case MidiTargetType::MidiTarget_FileStemMute: controlName = "Mute " + String(stemIdx+1); break; + case MidiTargetType::MidiTarget_FileStemSolo: controlName = "Solo " + String(stemIdx+1); break; + default: controlName = "Control"; break; + } + + PopupMenu m; + if (processor.isMidiLearning() && + processor.getMidiLearnTargetType() == type && + processor.getMidiLearnTargetData() == stemIdx) { + m.addItem(1, String(TRANS("Cancel MIDI Learn")), true, false); + } else { + m.addItem(1, String(TRANS("MIDI Learn: ")) + controlName, true, false); + } + + if (hasMapped) { + m.addItem(2, String(TRANS("Clear MIDI Assignment")) + + " (CC " + String(ccNum) + ", Ch " + String(midiCh) + ")", true, false); + } + + m.showMenuAsync(PopupMenu::Options().withTargetComponent(source), + [this, type, stemIdx, hasMapped](int result) { + if (result == 1) { + if (processor.isMidiLearning()) { + processor.stopMidiLearn(); + } else { + processor.startMidiLearn(type, stemIdx); + // Show a brief indication — update tooltip of source + // (the mapping will be applied on next incoming CC) + } + } else if (result == 2 && hasMapped) { + processor.clearMidiMapping(type, stemIdx); + } + }); +} + void ChannelGroupsView::configLevelSlider(Slider * slider, bool monmode) { //slider->setTextValueSuffix(" dB"); @@ -1352,6 +1415,13 @@ ChannelGroupView * ChannelGroupsView::createChannelGroupView(bool first) pvf->panSlider->setLookAndFeel(&pvf->panSliderLNF); pvf->singlePanner = true; + pvf->moggButton = std::make_unique("MOGG"); + pvf->moggButton->addListener(this); + pvf->moggButton->setTooltip(TRANS("Open MOGG multitrack mixer")); + pvf->moggButton->setColour(TextButton::buttonColourId, Colour(0xFF553366)); + pvf->moggButton->setColour(TextButton::buttonOnColourId, Colour(0xFF774499)); + pvf->moggButton->setConnectedEdges(Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnTop | Button::ConnectedOnBottom); + std::unique_ptr destimg(Drawable::createFromImageData(BinaryData::chevron_forward_svg, BinaryData::chevron_forward_svgSize)); std::unique_ptr linkimg(Drawable::createFromImageData(BinaryData::link_svg, BinaryData::link_svgSize)); std::unique_ptr trirightimg(Drawable::createFromImageData(BinaryData::expand_arrow_inactive_svg, BinaryData::expand_arrow_inactive_svgSize)); @@ -1506,9 +1576,7 @@ void ChannelGroupsView::setMetersActive(bool flag) if (mMetChannelView) { mMetChannelView->meter->setRefreshRateHz(rate); } - if (mFileChannelView) { - mFileChannelView->meter->setRefreshRateHz(rate); - } + for (auto v : mFileChannelViews) { v->meter->setRefreshRateHz(rate); } if (mSoundboardChannelView) { mSoundboardChannelView->meter->setRefreshRateHz(rate); } @@ -1706,83 +1774,80 @@ void ChannelGroupsView::rebuildChannelViews(bool notify) } - if (!mFileChannelView) { - mFileChannelView.reset(createChannelGroupView(true)); - mFileChannelView->nameLabel->setEditable(false); - mFileChannelView->nameLabel->setText(TRANS("File Playback"), dontSendNotification); - mFileChannelView->nameLabel->setColour(Label::backgroundColourId, Colours::transparentBlack); - mFileChannelView->nameLabel->setColour(Label::outlineColourId, Colours::transparentBlack); - //mFileChannelView->nameLabel->setColour(TextEditor::backgroundColourId, Colours::transparentBlack); - //mFileChannelView->nameLabel->setColour(TextEditor::outlineColourId, Colours::transparentBlack); - //mFileChannelView->nameLabel->setReadOnly(true); + int fpgCnt = processor.getFilePlaybackGroupCount(); + while (mFileChannelViews.size() > fpgCnt) { + mFileChannelViews.removeLast(); + } + while (mFileChannelViews.size() < fpgCnt) { + int idx = mFileChannelViews.size(); + auto v = createChannelGroupView(true); + mFileChannelViews.add(v); + + v->nameLabel->setEditable(false); + v->nameLabel->setText(fpgCnt == 1 ? TRANS("File Playback") : TRANS("File pb ") + String(idx+1), dontSendNotification); + v->nameLabel->setColour(Label::backgroundColourId, Colours::transparentBlack); + v->nameLabel->setColour(Label::outlineColourId, Colours::transparentBlack); + + if (idx == 0) { + v->linkButton->setClickingTogglesState(true); + std::unique_ptr grpimg(Drawable::createFromImageData(BinaryData::send_group_small_svg, BinaryData::send_group_small_svgSize)); + v->linkButton->setTitle(TRANS("Send File Playback")); + v->linkButton->setImages(grpimg.get()); + v->linkButton->setClickingTogglesState(true); + mFileSendAttachment = std::make_unique (processor.getValueTreeState(), SonobusAudioProcessor::paramSendFileAudio, *v->linkButton); + v->linkButton->setButtonStyle(DrawableButton::ButtonStyle::ImageOnButtonBackground); + v->linkButton->setForegroundImageRatio(1.0f); + v->linkButton->setColour(TextButton::buttonOnColourId, Colour::fromFloatRGBA(0.2, 0.5, 0.7, 0.65)); + v->linkButton->setColour(TextButton::buttonColourId, Colours::transparentBlack); + v->linkButton->setTooltip(TRANS("Send File Playback to All")); + } else { + v->linkButton->setVisible(false); + } - mFileChannelView->linkButton->setClickingTogglesState(true); - std::unique_ptr grpimg(Drawable::createFromImageData(BinaryData::send_group_small_svg, BinaryData::send_group_small_svgSize)); - mFileChannelView->linkButton->setTitle(TRANS("Send File Playback")); - mFileChannelView->linkButton->setImages(grpimg.get()); - mFileChannelView->linkButton->setClickingTogglesState(true); - mFileSendAttachment = std::make_unique (processor.getValueTreeState(), SonobusAudioProcessor::paramSendFileAudio, *mFileChannelView->linkButton); - mFileChannelView->linkButton->setButtonStyle(DrawableButton::ButtonStyle::ImageOnButtonBackground); - mFileChannelView->linkButton->setForegroundImageRatio(1.0f); - mFileChannelView->linkButton->setColour(TextButton::buttonOnColourId, Colour::fromFloatRGBA(0.2, 0.5, 0.7, 0.65)); - mFileChannelView->linkButton->setColour(TextButton::buttonColourId, Colours::transparentBlack); - mFileChannelView->linkButton->setTooltip(TRANS("Send File Playback to All")); - - - mFileChannelView->soloButton->onClick = [this]() { + v->soloButton->onClick = [this, idx]() { + processor.setFilePlaybackSoloed(idx, mFileChannelViews[idx]->soloButton->getToggleState()); }; - mFileChannelView->muteButton->onClick = [this]() { + v->muteButton->onClick = [this, idx]() { + processor.setFilePlaybackMuted(idx, mFileChannelViews[idx]->muteButton->getToggleState()); }; - - mFileChannelView->fxButton->onClick = [this]() { - /* - if (!effectsCalloutBox) { - showEffects(0, true, mFileChannelView->fxButton.get()); - } else { - showEffects(0, false); - } - */ + v->fxButton->onClick = [this]() { }; - mFileChannelView->monfxButton->onClick = [this]() { + v->monfxButton->onClick = [this, idx]() { if (!monEffectsCalloutBox) { - showMonitorEffects(-2, true, mFileChannelView->monfxButton.get()); + showMonitorEffects(-100 - idx, true, mFileChannelViews[idx]->monfxButton.get()); } else { - showMonitorEffects(-2, false); + showMonitorEffects(-100 - idx, false); } }; - mFileChannelView->destButton->onClick = [this]() { - // when shown it will be for the first one - showDestSelectionMenu(mFileChannelView->destButton.get(), -2); + v->destButton->onClick = [this, idx]() { + showDestSelectionMenu(mFileChannelViews[idx]->destButton.get(), -100 - idx); }; - mFileChannelView->levelSlider->onValueChange = [this]() { - processor.setFilePlaybackGain(mFileChannelView->levelSlider->getValue()); + v->levelSlider->onValueChange = [this, idx]() { + processor.setFilePlaybackGain(idx, mFileChannelViews[idx]->levelSlider->getValue()); }; + // Right-click on fader is handled via ChannelGroupsView::mouseDown - //mFileChannelView->panSlider->onValueChange = [this]() { - // processor.setFilePlaybackPan(mFileChannelView->panSlider->getValue()); - //}; - - mFileChannelView->monitorSlider->onValueChange = [this]() { - processor.setFilePlaybackMonitor(mFileChannelView->monitorSlider->getValue()); + v->monitorSlider->onValueChange = [this, idx]() { + processor.setFilePlaybackMonitor(idx, mFileChannelViews[idx]->monitorSlider->getValue()); }; - setupChildren(mFileChannelView.get()); - - mFileChannelView->muteButton->setVisible(false); - mFileChannelView->soloButton->setVisible(false); - mFileChannelView->fxButton->setVisible(false); - mFileChannelView->panSlider->setVisible(false); + // Right-click MIDI learn on mute & solo (override their onClick with right-click detection via mouseDown) + // We use a lambda approach via a helper CustomButton subclass; simplest is to store index and re-use mouseDown. + // We will handle this in ChannelGroupsView::mouseDown by checking the component pointer. - mFileChannelView->premeter->setVisible(false); - mFileChannelView->premeter->setRefreshRateHz(0); - //mFileChannelView->premeter->setVisible(false); - //mFileChannelView->premeter->setRefreshRateHz(0); + setupChildren(v); + v->muteButton->setVisible(true); + v->soloButton->setVisible(true); + v->fxButton->setVisible(false); + v->panSlider->setVisible(false); + v->premeter->setVisible(false); + v->premeter->setRefreshRateHz(0); } if (!mSoundboardChannelView) { mSoundboardChannelView.reset(createChannelGroupView(true)); @@ -1881,6 +1946,10 @@ void ChannelGroupsView::setupChildren(ChannelGroupView * pvf) pvf->addAndMakeVisible(pvf->soloButton.get()); pvf->addAndMakeVisible(pvf->levelSlider.get()); pvf->addChildComponent(pvf->monitorSlider.get()); + // Allow this ChannelGroupsView to see mouse events on child controls (for right-click MIDI learn) + pvf->levelSlider->addMouseListener(this, false); + pvf->muteButton->addMouseListener(this, false); + pvf->soloButton->addMouseListener(this, false); //pvf->addAndMakeVisible(pvf->levelLabel.get()); pvf->addAndMakeVisible(pvf->panLabel.get()); pvf->addAndMakeVisible(pvf->nameLabel.get()); @@ -1896,6 +1965,8 @@ void ChannelGroupsView::setupChildren(ChannelGroupView * pvf) pvf->addAndMakeVisible(pvf->monfxButton.get()); pvf->addAndMakeVisible(pvf->panSlider.get()); + pvf->addChildComponent(pvf->moggButton.get()); + pvf->moggButton->addMouseListener(this, false); pvf->panSlider->setPopupDisplayEnabled(true, true, dw); @@ -2058,6 +2129,16 @@ void ChannelGroupsView::updateLayoutForRemotePeer(bool notify) pvf->inbox.items.add(FlexItem(5, 3)); pvf->inbox.items.add(FlexItem(mutebuttwidth, minitemheight, *pvf->fxButton).withMargin(0).withFlex(0)); + if (i < 0 && processor.getRemotePeerIsMogg(mPeerIndex)) { + pvf->inbox.items.add(FlexItem(5, 3)); + pvf->inbox.items.add(FlexItem(40, minitemheight, *pvf->moggButton).withMargin(0).withFlex(0)); + } + } + else { + if (i < 0 && processor.getRemotePeerIsMogg(mPeerIndex)) { + pvf->inbox.items.add(FlexItem(3, 3)); + pvf->inbox.items.add(FlexItem(40, minitemheight, *pvf->moggButton).withMargin(0).withFlex(0)); + } } pvf->inbox.items.add(FlexItem(3, 3)); pvf->inbox.items.add(FlexItem(minSliderWidth, minitemheight, *pvf->levelSlider).withMargin(0).withFlex(1)); @@ -2390,13 +2471,13 @@ void ChannelGroupsView::updateLayoutForInput(bool notify) if (i == mChannelViews.size()) { pvf = mMetChannelView.get(); ismetorfileorsoundboard = true; - } else if (i == mChannelViews.size() + 1) { - pvf = mFileChannelView.get(); - auto numfilechan = processor.getFilePlaybackMeterSource().getNumChannels(); - mainmeterwidth = numfilechan * (numfilechan > 2 ? 6 : meterwidth); + } else if (i >= mChannelViews.size() + 1 && i < mChannelViews.size() + 1 + mFileChannelViews.size()) { + int fidx = i - (mChannelViews.size() + 1); + pvf = mFileChannelViews[fidx]; + // meter logic adjusted below maybe? ismetorfileorsoundboard = true; } - else if (i > mChannelViews.size()) { + else if (i >= mChannelViews.size() + 1 + mFileChannelViews.size()) { pvf = mSoundboardChannelView.get(); auto numsoundboardchan = processor.getSoundboardProcessor()->getMeterSource().getNumChannels(); mainmeterwidth = numsoundboardchan * (numsoundboardchan > 2 ? 6 : meterwidth); @@ -2665,10 +2746,10 @@ void ChannelGroupsView::applyToAllSliders(std::function & routin routine(mMainChannelView->monitorSlider.get()); } - if (mFileChannelView) { - routine(mFileChannelView->levelSlider.get()); - routine(mFileChannelView->panSlider.get()); - routine(mFileChannelView->monitorSlider.get()); + for (auto v : mFileChannelViews) { + routine(v->levelSlider.get()); + routine(v->panSlider.get()); + routine(v->monitorSlider.get()); } if (mMetChannelView) { @@ -2750,32 +2831,33 @@ void ChannelGroupsView::updateInputModeChannelViews(int specific) } - if (mFileChannelView) { + for (int i=0; i < mFileChannelViews.size(); i++) { + auto v = mFileChannelViews[i]; String desttext; int destcnt, deststart; - processor.getFilePlaybackDestStartAndCount(deststart, destcnt); + processor.getFilePlaybackDestStartAndCount(i, deststart, destcnt); if (destcnt == 1) { desttext << deststart + 1; } else { desttext << deststart + 1 << "-" << deststart+destcnt; } - mFileChannelView->destButton->setButtonText(desttext); - mFileChannelView->monitorSlider->setVisible(true); - mFileChannelView->monitorSlider->setValue(processor.getFilePlaybackMonitor(), dontSendNotification); - mFileChannelView->levelSlider->setValue(processor.getFilePlaybackGain(), dontSendNotification); - mFileChannelView->panSlider->setVisible(false); - mFileChannelView->panLabel->setVisible(false); - //mFileChannelView->panSlider->setValue(processor.getFilPlaybackPan(), dontSendNotification); - mFileChannelView->showDivider = true; - mFileChannelView->nameEditor->setVisible(false); + v->destButton->setButtonText(desttext); + v->monitorSlider->setVisible(true); + v->monitorSlider->setValue(processor.getFilePlaybackMonitor(i), dontSendNotification); + v->levelSlider->setValue(processor.getFilePlaybackGain(i), dontSendNotification); + v->muteButton->setToggleState(processor.getFilePlaybackMuted(i), dontSendNotification); + v->soloButton->setToggleState(processor.getFilePlaybackSoloed(i), dontSendNotification); + v->panSlider->setVisible(false); + v->panLabel->setVisible(false); + v->showDivider = true; + v->nameEditor->setVisible(false); - mFileChannelView->meter->setMeterSource (&processor.getFilePlaybackMeterSource()); - mFileChannelView->meter->setSelectedChannel(0); + v->meter->setMeterSource (&processor.getFilePlaybackMeterSource()); + v->meter->setSelectedChannel(i); SonoAudio::DelayParams eparams; - processor.getFilePlaybackMonitorDelayParams(eparams); - mFileChannelView->monfxButton->setToggleState(eparams.enabled, dontSendNotification); - + processor.getFilePlaybackMonitorDelayParams(i, eparams); + v->monfxButton->setToggleState(eparams.enabled, dontSendNotification); } if (mSoundboardChannelView) { @@ -2982,6 +3064,9 @@ void ChannelGroupsView::updatePeerModeChannelViews(int specific) String username = processor.getRemotePeerUserName(mPeerIndex); mMainChannelView->nameLabel->setText(username, dontSendNotification); + + bool isMogg = processor.getRemotePeerIsMogg(mPeerIndex); + mMainChannelView->moggButton->setVisible(isMogg); if (username.length() > 32) { mMainChannelView->nameLabel->setTooltip(username); } else { @@ -3297,7 +3382,7 @@ void ChannelGroupsView::updateMonDelayButton() processor.getMetronomeMonitorDelayParams(metparams); DelayParams fileparams; - processor.getFilePlaybackMonitorDelayParams(fileparams); + processor.getFilePlaybackMonitorDelayParams(0, fileparams); DelayParams sbparams = processor.getSoundboardProcessor()->getMonitorDelayParams(); @@ -3328,7 +3413,7 @@ void ChannelGroupsView::toggleAllMonitorDelay() processor.getMetronomeMonitorDelayParams(metparams); DelayParams fileparams; - processor.getFilePlaybackMonitorDelayParams(fileparams); + processor.getFilePlaybackMonitorDelayParams(0, fileparams); DelayParams sbparams = processor.getSoundboardProcessor()->getMonitorDelayParams(); @@ -3352,7 +3437,9 @@ void ChannelGroupsView::toggleAllMonitorDelay() processor.setMetronomeMonitorDelayParams(metparams); fileparams.enabled = !doDisable; - processor.setFilePlaybackMonitorDelayParams(fileparams); + for (int fi=0; fi < processor.getFilePlaybackGroupCount(); ++fi) { + processor.setFilePlaybackMonitorDelayParams(fi, fileparams); + } sbparams.enabled = !doDisable; processor.getSoundboardProcessor()->setMonitorDelayParams(sbparams); @@ -3425,14 +3512,18 @@ void ChannelGroupsView::buttonClicked (Button* buttonThatWasClicked) break; } - else if (pvf->destButton.get() == buttonThatWasClicked) { - //processor.setRemotePeerChannelMuted (mPeerIndex, changroup, buttonThatWasClicked->getToggleState()); - //updateChannelViews(); - showDestSelectionMenu(buttonThatWasClicked, i); break; } + else if (pvf->moggButton.get() == buttonThatWasClicked) { + // Open remote MOGG mixer + auto editor = findParentComponentOfClass(); + if (editor) { + editor->showMoggMixer(mPeerIndex); + } + break; + } else if (pvf->soloButton.get() == buttonThatWasClicked) { if (ModifierKeys::currentModifiers.isAltDown()) { // exclusive solo this one @@ -4364,11 +4455,29 @@ void ChannelGroupsView::mouseDown (const MouseEvent& event) return; } } - if (mFileChannelView) { - if (event.eventComponent == mFileChannelView->meter.get()) { + for (int fv=0; fv < mFileChannelViews.size(); ++fv) { + auto v = mFileChannelViews[fv]; + if (event.eventComponent == v->meter.get()) { clearClipIndicators(); return; } + if (event.mods.isRightButtonDown()) { + if (event.eventComponent == v->muteButton.get()) { + showFileStemMidiMenu(v->muteButton.get(), + SonobusAudioProcessor::MidiTarget_FileStemMute, fv); + return; + } + if (event.eventComponent == v->soloButton.get()) { + showFileStemMidiMenu(v->soloButton.get(), + SonobusAudioProcessor::MidiTarget_FileStemSolo, fv); + return; + } + if (event.eventComponent == v->levelSlider.get()) { + showFileStemMidiMenu(v->levelSlider.get(), + SonobusAudioProcessor::MidiTarget_FileStemGain, fv); + return; + } + } } if (mSoundboardChannelView) { if (event.eventComponent == mSoundboardChannelView->meter.get()) { @@ -4518,11 +4627,11 @@ void ChannelGroupsView::clearClipIndicators() mMetChannelView->meter->clearMaxLevelDisplay(-1); } - if (mFileChannelView) { - mFileChannelView->premeter->clearClipIndicator(); - mFileChannelView->premeter->clearMaxLevelDisplay(-1); - mFileChannelView->meter->clearClipIndicator(); - mFileChannelView->meter->clearMaxLevelDisplay(-1); + for (auto v : mFileChannelViews) { + v->premeter->clearClipIndicator(); + v->premeter->clearMaxLevelDisplay(-1); + v->meter->clearClipIndicator(); + v->meter->clearMaxLevelDisplay(-1); } if (mSoundboardChannelView) { @@ -4545,8 +4654,14 @@ void ChannelGroupsView::showDestSelectionMenu(Component * source, int index) if (index == -1) { pvf = mMetChannelView.get(); ismet = true; - } else if (index == -2) { - pvf = mFileChannelView.get(); + } else if (index <= -100) { + // index mapping: -100 - fileindex + int fileIdx = -(index + 100); + if (fileIdx >= 0 && fileIdx < mFileChannelViews.size()) { + pvf = mFileChannelViews[fileIdx]; + } else { + pvf = mFileChannelViews[0]; + } isfile = true; } else if (index == -3) { pvf = mSoundboardChannelView.get(); @@ -4580,7 +4695,8 @@ void ChannelGroupsView::showDestSelectionMenu(Component * source, int index) processor.getMetronomeChannelDestStartAndCount(destst, destcnt); } else if (isfile) { - processor.getFilePlaybackDestStartAndCount(destst, destcnt); + int fidx = -(index + 100); + processor.getFilePlaybackDestStartAndCount(fidx, destst, destcnt); chcnt = processor.getFilePlaybackMeterSource().getNumChannels(); maxchcnt = chcnt; chcnt = jmin(2, chcnt, totalouts); @@ -4686,7 +4802,8 @@ void ChannelGroupsView::showDestSelectionMenu(Component * source, int index) safeThis->processor.setMetronomeChannelDestStartAndCount(dclitem->startIndex, dclitem->count); } else if (isfile) { - safeThis->processor.setFilePlaybackDestStartAndCount(dclitem->startIndex, dclitem->count); + int fidx = -(changroup + 100); + safeThis->processor.setFilePlaybackDestStartAndCount(fidx, dclitem->startIndex, dclitem->count); } else if (issoundboard) { safeThis->processor.getSoundboardProcessor()->setDestStartAndCount(dclitem->startIndex, dclitem->count); diff --git a/Source/ChannelGroupsView.h b/Source/ChannelGroupsView.h index f3fe3bb3a..62aa334dc 100644 --- a/Source/ChannelGroupsView.h +++ b/Source/ChannelGroupsView.h @@ -255,6 +255,7 @@ class ChannelGroupView : public Component { std::unique_ptr soloButton; std::unique_ptr fxButton; std::unique_ptr monfxButton; + std::unique_ptr moggButton; std::unique_ptr