31#include <CustomLookAndFeel.h>
32#include <WebUpdateDetector.h>
41 m_config = std::make_unique<UmsciAppConfiguration>(JUCEAppBasics::AppConfigurationBase::getDefaultConfigFilePath());
42 m_config->addDumper(
this);
45 if (!m_config->isValid())
47 m_config->ResetToDefault();
51 m_controlComponent = std::make_unique<UmsciControlComponent>();
52 addAndMakeVisible(m_controlComponent.get());
54 m_discoverHintComponent = std::make_unique<UmsciDiscoveringHintComponent>();
55 addAndMakeVisible(m_discoverHintComponent.get());
57 m_connectingComponent = std::make_unique<UmsciConnectingComponent>();
58 addAndMakeVisible(m_connectingComponent.get());
61 m_connectingComponent->setVisible(
false);
62 m_controlComponent->setVisible(
false);
63 m_discoverHintComponent->setVisible(
true);
65 m_aboutComponent = std::make_unique<AboutComponent>(BinaryData::UmsciRect_png, BinaryData::UmsciRect_pngSize);
66 m_aboutButton = std::make_unique<juce::DrawableButton>(
"About", juce::DrawableButton::ButtonStyle::ImageFitted);
67 m_aboutButton->setTooltip(juce::String(
"About") + juce::JUCEApplication::getInstance()->getApplicationName());
68 m_aboutButton->onClick = [
this] {
69 juce::PopupMenu aboutMenu;
70 aboutMenu.addCustomItem(1, std::make_unique<CustomAboutItem>(m_aboutComponent.get(), juce::Rectangle<int>(250, 250)),
nullptr, juce::String(
"Info about") + juce::JUCEApplication::getInstance()->getApplicationName());
71 aboutMenu.showMenuAsync(juce::PopupMenu::Options());
73 m_aboutButton->setAlwaysOnTop(
true);
74 m_aboutButton->setColour(juce::DrawableButton::ColourIds::backgroundColourId, juce::Colours::transparentBlack);
75 m_aboutButton->setColour(juce::DrawableButton::ColourIds::backgroundOnColourId, juce::Colours::transparentBlack);
76 addAndMakeVisible(m_aboutButton.get());
80 auto fullscreenShortCutHint = std::string(
" (Ctrl+F11)");
82 auto fullscreenShortCutHint = std::string(
" (Cmd+Ctrl+F)");
116#if JUCE_WINDOWS || JUCE_MAC
121 m_settingsButton = std::make_unique<juce::DrawableButton>(
"Settings", juce::DrawableButton::ButtonStyle::ImageFitted);
122 m_settingsButton->setTooltip(juce::String(
"Settings for ") + juce::JUCEApplication::getInstance()->getApplicationName());
123 m_settingsButton->onClick = [
this] {
124 juce::PopupMenu lookAndFeelSubMenu;
126 lookAndFeelSubMenu.addItem(i, m_settingsItems[i].first,
true, m_settingsItems[i].second == 1);
128 juce::PopupMenu controlColourSubMenu;
130 controlColourSubMenu.addItem(i, m_settingsItems[i].first,
true, m_settingsItems[i].second == 1);
132 juce::PopupMenu controlSizeSubMenu;
134 controlSizeSubMenu.addItem(i, m_settingsItems[i].first,
true, m_settingsItems[i].second == 1);
136 juce::PopupMenu settingsMenu;
137 settingsMenu.addSubMenu(
"LookAndFeel", lookAndFeelSubMenu);
138 settingsMenu.addSubMenu(
"Control colour", controlColourSubMenu);
139 settingsMenu.addSubMenu(
"Control size", controlSizeSubMenu);
140 settingsMenu.addSeparator();
144#if JUCE_WINDOWS || JUCE_MAC
145 settingsMenu.addSeparator();
148 settingsMenu.showMenuAsync(juce::PopupMenu::Options(), [=](
int selectedId) {
149 handleSettingsMenuResult(selectedId);
151 m_config->triggerConfigurationDump();
154 m_settingsButton->setAlwaysOnTop(
true);
155 m_settingsButton->setColour(juce::DrawableButton::ColourIds::backgroundColourId, juce::Colours::transparentBlack);
156 m_settingsButton->setColour(juce::DrawableButton::ColourIds::backgroundOnColourId, juce::Colours::transparentBlack);
157 addAndMakeVisible(m_settingsButton.get());
159 m_connectionToggleButton = std::make_unique<juce::DrawableButton>(
"ConnectionToggle", juce::DrawableButton::ButtonStyle::ImageFitted);
160 m_connectionToggleButton->setTooltip(
"Toggle connection to device.");
161 m_connectionToggleButton->onClick = [
this] {
162 if (m_connectionToggleButton->getToggleState())
163 DeviceController::getInstance()->connect();
166 DeviceController::getInstance()->disconnect();
167 m_controlComponent->resetData();
173 m_config->triggerConfigurationDump();
175 m_connectionToggleButton->setAlwaysOnTop(
true);
176 m_connectionToggleButton->setClickingTogglesState(
true);
177 m_connectionToggleButton->setColour(juce::DrawableButton::ColourIds::backgroundColourId, juce::Colours::transparentBlack);
178 m_connectionToggleButton->setColour(juce::DrawableButton::ColourIds::backgroundOnColourId, juce::Colours::transparentBlack);
179 addAndMakeVisible(m_connectionToggleButton.get());
181 if (juce::JUCEApplication::getInstance()->getCommandLineParameters().contains(
"--noconfigui"))
183 m_aboutButton->setVisible(
false);
184 m_settingsButton->setVisible(
false);
185 m_connectionToggleButton->setVisible(
false);
188 m_upmixSnapshotStoreButton = std::make_unique<juce::DrawableButton>(
"SnapshotStore", juce::DrawableButton::ButtonStyle::ImageFitted);
189 m_upmixSnapshotStoreButton->setColour(juce::DrawableButton::ColourIds::backgroundColourId, juce::Colours::transparentBlack);
190 m_upmixSnapshotStoreButton->setColour(juce::DrawableButton::ColourIds::backgroundOnColourId, juce::Colours::transparentBlack);
191 m_upmixSnapshotStoreButton->setTooltip(
"Store upmix indicator state");
192 m_upmixSnapshotStoreButton->onClick = [
this] {
193 if (m_controlComponent)
195 m_upmixSnapshot = UpmixSnapshot{
196 m_controlComponent->getUpmixRot(),
197 m_controlComponent->getUpmixTrans(),
198 m_controlComponent->getUpmixHeightTrans(),
199 m_controlComponent->getUpmixAngleStretch(),
200 m_controlComponent->getUpmixOffsetX(),
201 m_controlComponent->getUpmixOffsetY()
203 m_upmixSnapshotRecallButton->setEnabled(
true);
205 m_config->triggerConfigurationDump();
208 m_upmixSnapshotStoreButton->setAlwaysOnTop(
true);
209 m_upmixSnapshotStoreButton->setVisible(
false);
210 addAndMakeVisible(m_upmixSnapshotStoreButton.get());
212 m_upmixSnapshotRecallButton = std::make_unique<juce::DrawableButton>(
"SnapshotRecall", juce::DrawableButton::ButtonStyle::ImageFitted);
213 m_upmixSnapshotRecallButton->setColour(juce::DrawableButton::ColourIds::backgroundColourId, juce::Colours::transparentBlack);
214 m_upmixSnapshotRecallButton->setColour(juce::DrawableButton::ColourIds::backgroundOnColourId, juce::Colours::transparentBlack);
215 m_upmixSnapshotRecallButton->setTooltip(
"Recall upmix indicator state");
216 m_upmixSnapshotRecallButton->onClick = [
this] {
217 if (m_controlComponent && m_upmixSnapshot.has_value())
219 const auto& s = *m_upmixSnapshot;
220 m_controlComponent->setUpmixTransform(s.rot, s.scale, s.heightScale, s.angleStretch);
221 m_controlComponent->setUpmixOffset(s.offsetX, s.offsetY);
222 if (m_controlComponent->getUpmixLiveMode())
223 m_controlComponent->triggerUpmixTransformApplied();
225 m_controlComponent->triggerUpmixFlashCheck();
227 m_config->triggerConfigurationDump();
230 m_upmixSnapshotRecallButton->setAlwaysOnTop(
true);
231 m_upmixSnapshotRecallButton->setEnabled(
false);
232 m_upmixSnapshotRecallButton->setVisible(
false);
233 addAndMakeVisible(m_upmixSnapshotRecallButton.get());
235 m_controlComponent->onUpmixTransformChanged = [
this]() {
237 m_config->triggerConfigurationDump();
241 auto noconfigui = juce::JUCEApplication::getInstance()->getCommandLineParameters().contains(
"--noconfigui");
245 m_connectionToggleButton->setToggleState(
false, juce::dontSendNotification);
246 m_connectingComponent->setVisible(
false);
248 m_controlComponent->setVisible(
false);
249 m_discoverHintComponent->setVisible(
true);
250 m_upmixSnapshotStoreButton->setVisible(
false);
251 m_upmixSnapshotRecallButton->setVisible(
false);
254 m_connectionToggleButton->setToggleState(
true, juce::dontSendNotification);
255 m_controlComponent->setVisible(
false);
256 m_discoverHintComponent->setVisible(
false);
257 m_connectingComponent->setVisible(
true);
259 m_upmixSnapshotStoreButton->setVisible(
false);
260 m_upmixSnapshotRecallButton->setVisible(
false);
263 m_connectionToggleButton->setToggleState(
true, juce::dontSendNotification);
264 m_controlComponent->setVisible(
false);
265 m_discoverHintComponent->setVisible(
false);
266 m_connectingComponent->setVisible(
true);
268 m_upmixSnapshotStoreButton->setVisible(
false);
269 m_upmixSnapshotRecallButton->setVisible(
false);
272 m_connectionToggleButton->setToggleState(
true, juce::dontSendNotification);
273 m_connectingComponent->setVisible(
true);
275 m_discoverHintComponent->setVisible(
false);
276 m_controlComponent->setVisible(
false);
277 m_upmixSnapshotStoreButton->setVisible(
false);
278 m_upmixSnapshotRecallButton->setVisible(
false);
281 m_connectionToggleButton->setToggleState(
true, juce::dontSendNotification);
282 m_connectingComponent->setVisible(
false);
283 m_discoverHintComponent->setVisible(
false);
284 m_controlComponent->setVisible(
true);
287 m_upmixSnapshotStoreButton->setVisible(
true);
288 m_upmixSnapshotRecallButton->setVisible(
true);
296#ifdef RUN_MESSAGE_TESTS
304#define IGNORE_UPDATES
305#elif defined JUCE_ANDROID
307#define IGNORE_UPDATES
310#if defined IGNORE_UPDATES
312 auto noUpdates = juce::JUCEApplication::getInstance()->getCommandLineParameters().contains(
"--noupdates");
315 auto updater = JUCEAppBasics::WebUpdateDetector::getInstance();
316 updater->SetReferenceVersion(ProjectInfo::versionString);
317 updater->SetDownloadUpdateWebAddress(
"https://github.com/christianahrens/umsci/releases/latest");
318 updater->CheckForNewVersion(
true,
"https://raw.githubusercontent.com/ChristianAhrens/Umsci/refs/heads/main/");
324 m_midiController = std::make_unique<MidiController>();
326 applyUpmixParamValue(param, domainValue);
329 m_oscController = std::make_unique<OscController>();
331 applyUpmixParamValue(param, domainValue);
335 m_config->addWatcher(
this);
338 setWantsKeyboardFocus(
true);
346 JUCEAppBasics::iOS_utils::initialise([
this] {
347 juce::MessageManager::callAsync([
this] {
resized(); });
353 JUCEAppBasics::iOS_utils::deinitialise();
358 auto safety = JUCEAppBasics::iOS_utils::getDeviceSafetyMargins();
359 auto safeBounds = getLocalBounds();
360 safeBounds.removeFromTop(safety._top);
361 safeBounds.removeFromBottom(safety._bottom);
362 safeBounds.removeFromLeft(safety._left);
363 safeBounds.removeFromRight(safety._right);
365 m_controlComponent->setBounds(safeBounds);
366 m_connectingComponent->setBounds(safeBounds);
367 m_discoverHintComponent->setBounds(safeBounds);
369 if (!juce::JUCEApplication::getInstance()->getCommandLineParameters().contains(
"--noconfigui"))
371 auto leftButtons = safeBounds.removeFromLeft(36);
372 auto rightButtons = safeBounds.removeFromLeft(36);
373 m_aboutButton->setBounds(leftButtons.removeFromTop(35).removeFromBottom(30));
374 m_settingsButton->setBounds(leftButtons.removeFromTop(35).removeFromBottom(30));
375 m_connectionToggleButton->setBounds(rightButtons.removeFromTop(35).removeFromBottom(30));
377 m_upmixSnapshotStoreButton->setBounds(leftButtons.removeFromBottom(35).removeFromRight(30).removeFromTop(30));
378 m_upmixSnapshotRecallButton->setBounds(leftButtons.removeFromBottom(35).removeFromRight(30).removeFromTop(30));
384 g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
389 auto aboutButtonDrawable = juce::Drawable::createFromSVG(*juce::XmlDocument::parse(BinaryData::question_mark_24dp_svg).get());
390 aboutButtonDrawable->replaceColour(juce::Colours::black, getLookAndFeel().findColour(juce::TextButton::ColourIds::textColourOnId));
391 m_aboutButton->setImages(aboutButtonDrawable.get());
393 auto settingsDrawable = juce::Drawable::createFromSVG(*juce::XmlDocument::parse(BinaryData::settings_24dp_svg).get());
394 settingsDrawable->replaceColour(juce::Colours::black, getLookAndFeel().findColour(juce::TextButton::ColourIds::textColourOnId));
395 m_settingsButton->setImages(settingsDrawable.get());
397 auto connectedDrawable = juce::Drawable::createFromSVG(*juce::XmlDocument::parse(BinaryData::link_off_24dp_svg).get());
398 connectedDrawable->replaceColour(juce::Colours::black, getLookAndFeel().findColour(juce::TextButton::ColourIds::textColourOnId));
399 auto disconnectedDrawable = juce::Drawable::createFromSVG(*juce::XmlDocument::parse(BinaryData::link_24dp_svg).get());
400 disconnectedDrawable->replaceColour(juce::Colours::black, getLookAndFeel().findColour(juce::TextButton::ColourIds::textColourOffId));
401 if (m_connectionToggleButton->getToggleState())
402 m_connectionToggleButton->setImages(connectedDrawable.get());
404 m_connectionToggleButton->setImages(disconnectedDrawable.get());
406 auto snapshotStoreDrawable = juce::Drawable::createFromSVG(*juce::XmlDocument::parse(BinaryData::variable_add_24dp_svg).get());
407 snapshotStoreDrawable->replaceColour(juce::Colours::black, getLookAndFeel().findColour(juce::TextButton::ColourIds::textColourOnId));
408 m_upmixSnapshotStoreButton->setImages(snapshotStoreDrawable.get());
410 auto snapshotRecallDrawable = juce::Drawable::createFromSVG(*juce::XmlDocument::parse(BinaryData::variable_insert_24dp_svg).get());
411 snapshotRecallDrawable->replaceColour(juce::Colours::black, getLookAndFeel().findColour(juce::TextButton::ColourIds::textColourOnId));
412 m_upmixSnapshotRecallButton->setImages(snapshotRecallDrawable.get());
414 applyControlColour();
420 handleSettingsMenuResult(option);
423void MainComponent::handleSettingsMenuResult(
int selectedId)
427 else if (UmsciSettingsOption::LookAndFeel_First <= selectedId && UmsciSettingsOption::LookAndFeel_Last >= selectedId)
428 handleSettingsLookAndFeelMenuResult(selectedId);
429 else if (UmsciSettingsOption::ControlColour_First <= selectedId && UmsciSettingsOption::ControlColour_Last >= selectedId)
430 handleSettingsControlColourMenuResult(selectedId);
432 showConnectionSettings();
434 handleSettingsFullscreenModeToggleResult();
435 else if (UmsciSettingsOption::ControlFormat_First <= selectedId && UmsciSettingsOption::ControlFormat_Last >= selectedId)
436 handleSettingsControlFormatMenuResult(selectedId);
440 showExternalControlSettings();
441 else if (UmsciSettingsOption::ControlSize_First <= selectedId && UmsciSettingsOption::ControlSize_Last >= selectedId)
442 handleSettingsControlSizeMenuResult(selectedId);
447void MainComponent::handleSettingsControlFormatMenuResult(
int selectedId)
450 std::function<void(
int,
int,
int,
int,
int,
int,
int,
int,
int,
int)> setSettingsItemsCheckState = [=](
int a,
int b,
int c,
int d,
int e,
int f,
int g,
int h,
int i,
int j) {
466 setSettingsItemsCheckState(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
467 if (m_controlComponent)
468 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::stereo());
471 setSettingsItemsCheckState(0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
472 if (m_controlComponent)
473 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::createLRS());
476 setSettingsItemsCheckState(0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
477 if (m_controlComponent)
478 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::createLCRS());
481 setSettingsItemsCheckState(0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
482 if (m_controlComponent)
483 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::create5point0());
486 setSettingsItemsCheckState(0, 0, 0, 0, 1, 0, 0, 0, 0, 0);
487 if (m_controlComponent)
488 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::create5point1());
491 setSettingsItemsCheckState(0, 0, 0, 0, 0, 1, 0, 0, 0, 0);
492 if (m_controlComponent)
493 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::create5point1point2());
496 setSettingsItemsCheckState(0, 0, 0, 0, 0, 0, 1, 0, 0, 0);
497 if (m_controlComponent)
498 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::create7point0());
501 setSettingsItemsCheckState(0, 0, 0, 0, 0, 0, 0, 1, 0, 0);
502 if (m_controlComponent)
503 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::create7point1());
506 setSettingsItemsCheckState(0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
507 if (m_controlComponent)
508 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::create7point1point4());
511 setSettingsItemsCheckState(0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
512 if (m_controlComponent)
513 m_controlComponent->setUpmixChannelConfiguration(juce::AudioChannelSet::create9point1point6());
523void MainComponent::handleSettingsControlSizeMenuResult(
int selectedId)
525 std::function<void(
int,
int,
int)> setSettingsItemsCheckState = [=](
int s,
int m,
int l) {
534 setSettingsItemsCheckState(1, 0, 0);
535 if (m_controlComponent)
539 setSettingsItemsCheckState(0, 1, 0);
540 if (m_controlComponent)
544 setSettingsItemsCheckState(0, 0, 1);
545 if (m_controlComponent)
554void MainComponent::handleSettingsLookAndFeelMenuResult(
int selectedId)
557 std::function<void(
int,
int,
int)> setSettingsItemsCheckState = [=](
int a,
int b,
int c) {
566 setSettingsItemsCheckState(1, 0, 0);
571 setSettingsItemsCheckState(0, 1, 0);
576 setSettingsItemsCheckState(0, 0, 1);
586void MainComponent::handleSettingsControlColourMenuResult(
int selectedId)
589 std::function<void(
int,
int,
int,
int,
int)> setSettingsItemsCheckState = [=](
int green,
int red,
int blue,
int pink,
int laser) {
600 setSettingsItemsCheckState(1, 0, 0, 0, 0);
601 setControlColour(juce::Colours::forestgreen);
604 setSettingsItemsCheckState(0, 1, 0, 0, 0);
605 setControlColour(juce::Colours::orangered);
608 setSettingsItemsCheckState(0, 0, 1, 0, 0);
609 setControlColour(juce::Colours::dodgerblue);
612 setSettingsItemsCheckState(0, 0, 0, 1, 0);
613 setControlColour(juce::Colours::deeppink);
616 setSettingsItemsCheckState(0, 0, 0, 0, 1);
617 setControlColour(juce::Colour(0xd1, 0xff, 0x4f));
625void MainComponent::handleSettingsFullscreenModeToggleResult()
627 toggleFullscreenMode();
630void MainComponent::toggleFullscreenMode()
637void MainComponent::showConnectionSettings()
639 m_messageBox = std::make_unique<juce::AlertWindow>(
640 "Control connection settings",
641 "Info: This machine uses IP " + juce::IPAddress::getLocalAddress().toString(),
642 juce::MessageBoxIconType::NoIcon);
644 auto currentOCP1connPar = DeviceController::getInstance()->getConnectionParameters();
646 m_messageBox->addTextBlock(
"\nOCA/OCP.1 connection parameters:");
648 m_zeroconfDiscoverComboComponent = std::make_unique<UmsciZeroconfDiscoverComboComponent>();
649 m_zeroconfDiscoverComboComponent->setSize(10, 26);
650 m_zeroconfDiscoverComboComponent->onServiceSelected = [
this](
const ZeroconfSearcher::ZeroconfSearcher::ServiceInfo& service) {
651 if (
auto* ed = m_messageBox->getTextEditor(
"Device IP"))
652 ed->setText(juce::String(service.ip), juce::sendNotification);
653 if (
auto* ed = m_messageBox->getTextEditor(
"Device port"))
654 ed->setText(juce::String(service.port), juce::sendNotification);
655 auto matrixSizeIt = service.txtRecords.find(
"db_matrixSize");
656 if (matrixSizeIt != service.txtRecords.end())
657 if (
auto* ed = m_messageBox->getTextEditor(
"Device IO size"))
658 ed->setText(juce::String(matrixSizeIt->second), juce::sendNotification);
660 m_messageBox->addCustomComponent(m_zeroconfDiscoverComboComponent.get());
662 m_messageBox->addTextEditor(
"Device IP", std::get<0>(currentOCP1connPar).toString(),
"OCP.1 IP");
663 m_messageBox->addTextEditor(
"Device port", juce::String(std::get<1>(currentOCP1connPar)),
"OCP.1 port");
664 m_messageBox->addTextEditor(
"Device IO size", juce::String(m_controlComponent->getOcp1IOSize().first) +
"x" + juce::String(m_controlComponent->getOcp1IOSize().second),
"OCP.1 IOSize");
666 m_messageBox->addButton(
"Cancel", 0, juce::KeyPress(juce::KeyPress::escapeKey));
667 m_messageBox->addButton(
"Ok", 1, juce::KeyPress(juce::KeyPress::returnKey));
668 m_messageBox->enterModalState(
true, juce::ModalCallbackFunction::create([=](
int returnValue) {
669 if (returnValue == 1)
671 auto ocp1IP = juce::IPAddress(m_messageBox->getTextEditorContents(
"Device IP"));
672 auto ocp1Port = m_messageBox->getTextEditorContents(
"Device port").getIntValue();
673 auto ocp1IOsize = m_messageBox->getTextEditorContents(
"Device IO size");
675 if (m_connectingComponent)
676 m_connectingComponent->setConnectionParameters(ocp1IP, ocp1Port);
678 m_controlComponent->setOcp1IOSize({ ocp1IOsize.upToFirstOccurrenceOf(
"x",
false,
true).getIntValue(), ocp1IOsize.fromLastOccurrenceOf(
"x",
false,
true).getIntValue() });
679 DeviceController::getInstance()->setConnectionParameters(ocp1IP, ocp1Port);
682 m_config->triggerConfigurationDump();
685 m_zeroconfDiscoverComboComponent.reset();
686 m_messageBox.reset();
690void MainComponent::showUpmixSettings()
692 m_messageBox = std::make_unique<juce::AlertWindow>(
693 "Upmix control settings",
694 "Configure the upmix overlay control settings.",
695 juce::MessageBoxIconType::NoIcon);
697 juce::StringArray formatItems;
698 int currentFormatIndex = 0;
701 formatItems.add(m_settingsItems[i].first);
702 if (m_settingsItems[i].second == 1)
705 m_messageBox->addComboBox(
"Control format", formatItems,
"Channel format");
706 if (
auto* combo = m_messageBox->getComboBoxComponent(
"Control format"))
707 combo->setSelectedItemIndex(currentFormatIndex, juce::dontSendNotification);
709 juce::StringArray liveModeItems;
710 liveModeItems.add(
"Manual (double-click to apply)");
711 liveModeItems.add(
"Live (apply changes immediately)");
712 m_messageBox->addComboBox(
"Live mode", liveModeItems,
"Control mode");
713 if (
auto* combo = m_messageBox->getComboBoxComponent(
"Live mode"))
714 combo->setSelectedItemIndex(m_controlComponent->getUpmixLiveMode() ? 1 : 0,
715 juce::dontSendNotification);
717 juce::StringArray shapeItems;
718 shapeItems.add(
"Circle");
719 shapeItems.add(
"Rectangle");
720 m_messageBox->addComboBox(
"Shape", shapeItems,
"Indicator shape");
721 if (
auto* combo = m_messageBox->getComboBoxComponent(
"Shape"))
723 juce::dontSendNotification);
725 m_messageBox->addTextEditor(
"Start soundobject ID",
726 juce::String(m_controlComponent->getUpmixSourceStartId()),
727 "First soundobject");
729 juce::StringArray showSourcesItems;
730 showSourcesItems.add(
"All");
731 showSourcesItems.add(
"Upmix controlled only");
732 m_messageBox->addComboBox(
"Show sources", showSourcesItems,
"Visible soundobjects");
733 if (
auto* combo = m_messageBox->getComboBoxComponent(
"Show sources"))
734 combo->setSelectedItemIndex(m_controlComponent->getShowAllSources() ? 0 : 1,
735 juce::dontSendNotification);
737 m_messageBox->addButton(
"Cancel", 0, juce::KeyPress(juce::KeyPress::escapeKey));
738 m_messageBox->addButton(
"Ok", 1, juce::KeyPress(juce::KeyPress::returnKey));
739 m_messageBox->enterModalState(
true, juce::ModalCallbackFunction::create([=](
int returnValue) {
740 if (returnValue == 1)
742 if (
auto* combo = m_messageBox->getComboBoxComponent(
"Control format"))
744 if (
auto* combo = m_messageBox->getComboBoxComponent(
"Live mode"))
745 m_controlComponent->setUpmixLiveMode(combo->getSelectedItemIndex() == 1);
746 if (
auto* combo = m_messageBox->getComboBoxComponent(
"Shape"))
747 m_controlComponent->setUpmixShape(combo->getSelectedItemIndex() == 1
750 auto startId = m_messageBox->getTextEditorContents(
"Start soundobject ID").getIntValue();
751 m_controlComponent->setUpmixSourceStartId(startId);
752 if (
auto* combo = m_messageBox->getComboBoxComponent(
"Show sources"))
753 m_controlComponent->setShowAllSources(combo->getSelectedItemIndex() == 0);
755 m_config->triggerConfigurationDump();
757 m_messageBox.reset();
761void MainComponent::setControlColour(
const juce::Colour& controlColour)
763 m_controlColour = controlColour;
767 if (m_connectingComponent)
768 m_connectingComponent->lookAndFeelChanged();
771void MainComponent::applyControlColour()
773 auto customLookAndFeel =
dynamic_cast<JUCEAppBasics::CustomLookAndFeel*
>(&getLookAndFeel());
774 if (customLookAndFeel)
776 switch (customLookAndFeel->getPaletteStyle())
778 case JUCEAppBasics::CustomLookAndFeel::PS_Light:
779 getLookAndFeel().setColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringPeakColourId, m_controlColour.brighter());
780 getLookAndFeel().setColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId, m_controlColour);
782 case JUCEAppBasics::CustomLookAndFeel::PS_Dark:
784 getLookAndFeel().setColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringPeakColourId, m_controlColour.darker());
785 getLookAndFeel().setColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId, m_controlColour);
794 if (key == juce::KeyPress(juce::KeyPress::F11Key, juce::ModifierKeys::ctrlModifier, 0) ||
795 key == juce::KeyPress(
'f', juce::ModifierKeys::commandModifier | juce::ModifierKeys::ctrlModifier, 0))
797 toggleFullscreenMode();
808 if (m_controlComponent)
810 auto controlConfigXmlElement = m_controlComponent->createStateXml();
811 if (controlConfigXmlElement)
817 auto params = DeviceController::getInstance()->getConnectionParameters();
829 if (m_settingsItems[i].second == 1)
830 lookAndFeelXmlElmement->addTextElement(juce::String(i));
832 visuConfigXmlElement->addChildElement(lookAndFeelXmlElmement.release());
837 if (m_settingsItems[i].second == 1)
838 controlColourXmlElmement->addTextElement(juce::String(i));
840 visuConfigXmlElement->addChildElement(controlColourXmlElmement.release());
845 if (m_settingsItems[i].second == 1)
846 controlFormatXmlElmement->addTextElement(juce::String(i));
848 visuConfigXmlElement->addChildElement(controlFormatXmlElmement.release());
853 if (m_settingsItems[i].second == 1)
854 controlSizeXmlElement->addTextElement(juce::String(i));
856 visuConfigXmlElement->addChildElement(controlSizeXmlElement.release());
861 auto upmixConfigXmlElement = std::make_unique<juce::XmlElement>(
863 upmixConfigXmlElement->setAttribute(
865 (m_controlComponent ? m_controlComponent->getUpmixSourceStartId() : 1));
866 upmixConfigXmlElement->setAttribute(
868 (m_controlComponent ? (m_controlComponent->getUpmixLiveMode() ? 1 : 0) : 0));
869 upmixConfigXmlElement->setAttribute(
872 m_controlComponent ? m_controlComponent->getUpmixShape()
874 upmixConfigXmlElement->setAttribute(
876 (m_controlComponent ? (m_controlComponent->getShowAllSources() ? 1 : 0) : 1));
877 upmixConfigXmlElement->addTextElement(UpmixSnapshot{
878 m_controlComponent ? m_controlComponent->getUpmixRot() : 0.0f,
879 m_controlComponent ? m_controlComponent->getUpmixTrans() : 1.0f,
880 m_controlComponent ? m_controlComponent->getUpmixHeightTrans() : 0.6f,
881 m_controlComponent ? m_controlComponent->getUpmixAngleStretch() : 1.0f,
882 m_controlComponent ? m_controlComponent->getUpmixOffsetX() : 0.0f,
883 m_controlComponent ? m_controlComponent->getUpmixOffsetY() : 0.0f
886 m_config->setConfigState(std::move(upmixConfigXmlElement),
890 if (m_upmixSnapshot.has_value())
892 const auto& s = *m_upmixSnapshot;
893 auto snapshotXmlElement = std::make_unique<juce::XmlElement>(
895 snapshotXmlElement->addTextElement(s.toString());
896 m_config->setConfigState(std::move(snapshotXmlElement),
910 auto extCtrlXmlElement = std::make_unique<juce::XmlElement>(
913 auto midiDeviceXmlElement = std::make_unique<juce::XmlElement>(
915 midiDeviceXmlElement->addTextElement(m_midiController->getDeviceIdentifier());
916 extCtrlXmlElement->addChildElement(midiDeviceXmlElement.release());
920 auto assiXmlElement = std::make_unique<juce::XmlElement>(
922 assiXmlElement->addTextElement(m_midiController->getAssignment(
924 extCtrlXmlElement->addChildElement(assiXmlElement.release());
927 auto oscPortXmlElement = std::make_unique<juce::XmlElement>(
929 oscPortXmlElement->addTextElement(juce::String(m_oscController->getPort()));
930 extCtrlXmlElement->addChildElement(oscPortXmlElement.release());
943 auto addrXmlElement = std::make_unique<juce::XmlElement>(
945 addrXmlElement->addTextElement(m_oscController->getAddress(
947 extCtrlXmlElement->addChildElement(addrXmlElement.release());
950 m_config->setConfigState(std::move(extCtrlXmlElement),
959 if (controlConfigState && m_controlComponent)
960 m_controlComponent->setStateXml(controlConfigState.get());
964 if (connectionConfigState)
970 if (m_connectingComponent)
971 m_connectingComponent->setConnectionParameters(ocp1IP, ocp1Port);
973 auto ocp1ConParams = DeviceController::getInstance()->getConnectionParameters();
974 if (std::get<0>(ocp1ConParams) != ocp1IP || std::get<1>(ocp1ConParams) != ocp1Port)
976 DeviceController::getInstance()->setConnectionParameters(ocp1IP, ocp1Port);
979 if (ocp1ConnectionEnabled != m_connectionToggleButton->getToggleState())
981 m_connectionToggleButton->setToggleState(ocp1ConnectionEnabled, juce::dontSendNotification);
982 if (ocp1ConnectionEnabled)
983 DeviceController::getInstance()->connect();
985 DeviceController::getInstance()->disconnect();
994 if (lookAndFeelXmlElement)
996 auto lookAndFeelSettingsOptionId = lookAndFeelXmlElement->getAllSubText().getIntValue();
997 handleSettingsLookAndFeelMenuResult(lookAndFeelSettingsOptionId);
1001 if (controlColourXmlElement)
1003 auto controlColourSettingsOptionId = controlColourXmlElement->getAllSubText().getIntValue();
1004 handleSettingsControlColourMenuResult(controlColourSettingsOptionId);
1008 if (controlFormatXmlElement)
1010 auto controlFormatSettingsOptionId = controlFormatXmlElement->getAllSubText().getIntValue();
1011 handleSettingsControlFormatMenuResult(controlFormatSettingsOptionId);
1015 if (controlSizeXmlElement)
1017 auto controlSizeSettingsOptionId = controlSizeXmlElement->getAllSubText().getIntValue();
1018 handleSettingsControlSizeMenuResult(controlSizeSettingsOptionId);
1023 auto upmixConfigState = m_config->getConfigState(
1025 if (upmixConfigState && m_controlComponent)
1027 auto startId = upmixConfigState->getIntAttribute(
1029 m_controlComponent->setUpmixSourceStartId(startId);
1030 auto showAllSources = upmixConfigState->getIntAttribute(
1032 m_controlComponent->setShowAllSources(showAllSources);
1033 auto liveMode = upmixConfigState->getIntAttribute(
1035 m_controlComponent->setUpmixLiveMode(liveMode);
1037 upmixConfigState->getStringAttribute(
1039 m_controlComponent->setUpmixShape(upmixShape);
1040 auto upmixParams = UpmixSnapshot::fromString(upmixConfigState->getAllSubText());
1041 m_controlComponent->setUpmixTransform(upmixParams.rot, upmixParams.scale,
1042 upmixParams.heightScale, upmixParams.angleStretch);
1043 m_controlComponent->setUpmixOffset(upmixParams.offsetX, upmixParams.offsetY);
1047 auto upmixSnapshotState = m_config->getConfigState(
1049 if (upmixSnapshotState)
1051 m_upmixSnapshot = UpmixSnapshot::fromString(upmixSnapshotState->getAllSubText());
1052 if (m_upmixSnapshotRecallButton)
1053 m_upmixSnapshotRecallButton->setEnabled(
true);
1057 auto extCtrlState = m_config->getConfigState(
1061 if (
auto* midiDevXml = extCtrlState->getChildByName(
1063 m_midiController->openDevice(midiDevXml->getAllSubText());
1076 if (
auto* assiXml = extCtrlState->getChildByName(
1079 auto hexStr = assiXml->getAllSubText();
1080 if (hexStr.isNotEmpty())
1082 JUCEAppBasics::MidiCommandRangeAssignment assi;
1083 assi.deserializeFromHexString(hexStr);
1084 m_midiController->setAssignment(
1090 if (
auto* portXml = extCtrlState->getChildByName(
1092 m_oscController->openPort(portXml->getAllSubText().getIntValue());
1105 if (
auto* addrXml = extCtrlState->getChildByName(
1108 auto addr = addrXml->getAllSubText();
1109 if (addr.isNotEmpty())
1110 m_oscController->setAddress(
1120 return juce::Desktop::getInstance().getKioskModeComponent() !=
nullptr;
1122 if (
auto* topLevel = getTopLevelComponent())
1123 if (
auto* peer = topLevel->getPeer())
1124 return peer->isFullScreen();
1131void MainComponent::showExternalControlSettings()
1133 m_messageBox = std::make_unique<juce::AlertWindow>(
1134 "External control...",
1135 "Configure MIDI and OSC assignments for upmix transform parameters.",
1136 juce::MessageBoxIconType::NoIcon);
1138 m_externalControlComponent = std::make_unique<UmsciExternalControlComponent>();
1139 m_externalControlComponent->setMidiInputDeviceIdentifier(m_midiController->getDeviceIdentifier());
1141 m_externalControlComponent->setMidiAssi(
1144 m_externalControlComponent->setOscInputPort(m_oscController->getPort());
1146 m_externalControlComponent->setOscAddr(
1149 m_messageBox->addCustomComponent(m_externalControlComponent.get());
1151 m_messageBox->addButton(
"Cancel", 0, juce::KeyPress(juce::KeyPress::escapeKey));
1152 m_messageBox->addButton(
"Ok", 1, juce::KeyPress(juce::KeyPress::returnKey));
1153 m_messageBox->enterModalState(
true, juce::ModalCallbackFunction::create([=](
int returnValue) {
1154 if (returnValue == 1)
1156 m_midiController->openDevice(m_externalControlComponent->getMidiInputDeviceIdentifier());
1158 m_midiController->setAssignment(
1160 m_externalControlComponent->getMidiAssi(
1162 m_oscController->openPort(m_externalControlComponent->getOscInputPort());
1164 m_oscController->setAddress(
1166 m_externalControlComponent->getOscAddr(
1169 m_config->triggerConfigurationDump();
1171 m_externalControlComponent.reset();
1172 m_messageBox.reset();
1179 if (!m_controlComponent)
1185 m_controlComponent->setUpmixTransform(domainValue,
1186 m_controlComponent->getUpmixTrans(),
1187 m_controlComponent->getUpmixHeightTrans(),
1188 m_controlComponent->getUpmixAngleStretch());
1191 m_controlComponent->setUpmixTransform(m_controlComponent->getUpmixRot(),
1193 m_controlComponent->getUpmixHeightTrans(),
1194 m_controlComponent->getUpmixAngleStretch());
1197 m_controlComponent->setUpmixTransform(m_controlComponent->getUpmixRot(),
1198 m_controlComponent->getUpmixTrans(),
1200 m_controlComponent->getUpmixAngleStretch());
1203 m_controlComponent->setUpmixTransform(m_controlComponent->getUpmixRot(),
1204 m_controlComponent->getUpmixTrans(),
1205 m_controlComponent->getUpmixHeightTrans(),
1209 m_controlComponent->setUpmixOffset(domainValue, m_controlComponent->getUpmixOffsetY());
1212 m_controlComponent->setUpmixOffset(m_controlComponent->getUpmixOffsetX(), domainValue);
1221 m_controlComponent->triggerUpmixTransformApplied();
State
Represents the logical phase of the OCP.1 connection.
@ Connected
All subscriptions confirmed and all initial values received.
@ Connecting
TCP connect in progress; timer retries until success.
@ GetValues
GetValue responses being collected.
@ Subscribing
AddSubscription commands sent, awaiting acknowledgements.
@ Disconnected
No TCP connection exists; no resources are allocated.
void paint(juce::Graphics &g) override
void performConfigurationDump() override
UmsciAppConfiguration::Dumper — serialises all current settings to XML.
void applySettingsOption(const UmsciSettingsOption &option)
Applies a single settings option (called from both menu handlers and config restore).
std::function< void(int, bool)> onPaletteStyleChange
Fired when the look-and-feel palette changes, so the host application (if this is used as a plugin) c...
void lookAndFeelChanged() override
UmsciSettingsOption
Enumerates every user-selectable setting exposed via the settings menu.
@ UpmixSettings
Opens the upmix settings dialog.
@ ControlFormat_7point1point4
@ ControlSize_L
Large icons.
@ ControlSize_M
Medium icons.
@ FullscreenWindowMode
Toggles fullscreen / windowed.
@ LookAndFeel_Light
Force light colour scheme.
@ ControlFormat_5point1point2
@ ControlFormat_9point1point6
@ ControlColour_Laser
Bright laser-style highlight.
@ ExternalControlSettings
Opens the external (MIDI) control settings dialog.
@ ConnectionSettings
Opens the connection settings dialog.
@ LookAndFeel_FollowHost
Inherit host application L&F.
@ ControlColour_Green
Green source icons.
@ LookAndFeel_Dark
Force dark colour scheme.
@ ControlSize_S
Small icons.
bool keyPressed(const juce::KeyPress &key) override
Handles the Escape key (exits fullscreen) and F key (toggles fullscreen).
void onConfigUpdated() override
UmsciAppConfiguration::Watcher — called when the config XML changes on disk.
bool isFullscreenEnabled()
std::function< void(bool)> onSetFullscreenWindow
Fired when the user requests fullscreen mode; the host window applies it.
~MainComponent() override
@ IP
String: target device IP address.
@ UPMIXLIVEMODE
Boolean: follow live DS100 source positions.
@ ENABLED
Boolean: whether the connection is active.
@ UPMIXSHOWALLSOURCES
Boolean: render all sources or only upmix group.
@ UPMIXSHAPE
String: "Circle" or "Rectangle".
@ PORT
Integer: target OCP.1 port (default 50014).
@ UPMIXSOURCESTARTID
Integer: 1-based first DS100 channel for upmix inputs.
static juce::String getTagName(TagID ID)
static juce::String getAttributeName(AttributeID ID)
TagID
Enumerates every XML element tag used in the config file.
@ MIDI_UPMIXOFFSETX
MIDI assignment for upmix X offset.
@ MIDI_UPMIXHEIGHTSCALE
MIDI assignment for upmix height translation.
@ CONTROLCONFIG
Control-component settings.
@ OSC_UPMIXHEIGHTSCALE
OSC address for upmix height translation.
@ CONNECTIONCONFIG
OCP.1 connection parameters.
@ OSC_UPMIXANGLESTRETCH
OSC address for upmix angle stretch.
@ MIDIINPUTDEVICE
Selected MIDI input device identifier.
@ UPMIXSNAPSHOTCONFIG
Upmix snapshot container (optional; absent = no snapshot stored).
@ CONTROLCOLOUR
Source/speaker icon colour.
@ OSC_UPMIXOFFSETX
OSC address for upmix X offset.
@ OSC_UPMIXROT
OSC address for upmix rotation.
@ MIDI_UPMIXSCALE
MIDI assignment for upmix translation (radial scale).
@ EXTERNALCONTROLCONFIG
External (MIDI) control assignments container.
@ CONTROLSIZE
Icon size (S/M/L).
@ MIDI_UPMIXOFFSETY
MIDI assignment for upmix Y offset.
@ CONTROLFORMAT
Upmix channel format.
@ MIDI_UPMIXANGLESTRETCH
MIDI assignment for upmix angle stretch.
@ OSC_UPMIXOFFSETY
OSC address for upmix Y offset.
@ OSCINPUTPORT
OSC UDP listen port.
@ MIDI_UPMIXROT
MIDI assignment for upmix rotation.
@ OSC_UPMIXSCALE
OSC address for upmix translation (radial scale).
@ LOOKANDFEEL
Look-and-feel variant.
@ UPMIXCONFIG
Upmix behaviour settings.
@ VISUCONFIG
Visual appearance settings.
@ Subscribing
AddSubscription commands sent, waiting for ACKs.
@ Connecting
TCP connect in progress.
@ Reading
GetValue responses being collected (DeviceController::GetValues state).
UpmixMidiParam
Identifies each controllable upmix transform parameter.
@ UpmixMidiParam_OffsetX
m_upmixOffsetX — ring centre X offset.
@ UpmixMidiParam_HeightTranslation
m_upmixHeightTrans — height ring fraction.
@ UpmixMidiParam_Translation
m_upmixTrans — radial scale factor.
@ UpmixMidiParam_OffsetY
m_upmixOffsetY — ring centre Y offset.
@ UpmixMidiParam_Rotation
m_upmixRot — ring rotation (−π – +π rad).
@ UpmixMidiParam_AngleStretch
m_upmixAngleStretch — angular spread.
The top layer of the UmsciControlComponent stack — renders an interactive upmix speaker ring and lets...
static IndicatorShape getShapeForName(const juce::String &name)
static juce::String getShapeName(IndicatorShape shape)