24#include <WebUpdateDetector.h>
30JUCE_IMPLEMENT_SINGLETON(
Mema)
36 m_config = std::make_unique<MemaAppConfiguration>(JUCEAppBasics::AppConfigurationBase::getDefaultConfigFilePath());
37 m_config->addDumper(
this);
40 if (!m_config->isValid())
42 m_config->ResetToDefault();
46 m_config->addWatcher(
this,
true);
50 m_audioDeviceSelectComponent = std::make_unique<AudioSelectComponent>(m_MemaProcessor->getDeviceManager(),
55 false,
false,
false,
false);
56 m_audioDeviceSelectComponent->onAudioDeviceChangedDuringAudioSelection = [=]() {
58 m_MemaProcessor->initializeCtrlValuesToUnity();
62 m_config->triggerWatcherUpdate();
69#elif defined JUCE_ANDROID
74#if defined IGNORE_UPDATES
76 auto updater = JUCEAppBasics::WebUpdateDetector::getInstance();
77 updater->SetReferenceVersion(ProjectInfo::versionString);
78 updater->SetDownloadUpdateWebAddress(
"https://github.com/christianahrens/mema/releases/latest");
79 updater->CheckForNewVersion(
true,
"https://raw.githubusercontent.com/ChristianAhrens/Mema/refs/heads/main/");
86 m_MemaProcessor->editorBeingDeleted(m_MemaProcessor->getActiveEditor());
91 if (m_MemaProcessor && m_MemaProcessor->getDeviceManager())
94 onCpuUsageUpdate(
int(m_MemaProcessor->getDeviceManager()->getCpuUsage() * 100.0));
95 if (onNetworkUsageUpdate)
96 onNetworkUsageUpdate(m_MemaProcessor->getNetworkHealth());
97 if (onServiceDiscoveryTopologyUpdate)
98 onServiceDiscoveryTopologyUpdate(m_MemaProcessor->getDiscoveredServicesTopology());
106 if (
nullptr == m_MemaProcessor->getActiveEditor())
107 m_MemaProcessor->createEditorIfNeeded();
111 jassert(onEditorSizeChangeRequested);
112 editor->onEditorSizeChangeRequested = onEditorSizeChangeRequested;
115 m_MemaProcessor->updateCommanders();
117 return m_MemaProcessor->getActiveEditor();
125 if (m_audioDeviceSelectComponent)
126 return m_audioDeviceSelectComponent.get();
133 onEditorSizeChangeRequested =
nullptr;
134 onCpuUsageUpdate =
nullptr;
135 onNetworkUsageUpdate =
nullptr;
136 onServiceDiscoveryTopologyUpdate =
nullptr;
139 editor->onEditorSizeChangeRequested =
nullptr;
146 auto stateXml = m_config->getConfigState();
151 if (m_MemaUIConfigCache)
160 if (processorConfigState && m_MemaProcessor)
162 m_MemaProcessor->setStateXml(processorConfigState.get());
166 if (uiConfigState && m_MemaProcessor)
168 m_MemaUIConfigCache = std::make_unique<juce::XmlElement>(*uiConfigState);
176 m_MemaProcessor->environmentChanged();
180 editor->lookAndFeelChanged();
187 m_MemaUIConfigCache = std::make_unique<juce::XmlElement>(*uiConfigState);
192 return m_MemaUIConfigCache;
197 m_loadSavefileChooser = std::make_unique<juce::FileChooser>(
198 "Please select the " + juce::JUCEApplication::getInstance()->getApplicationName() +
" configuration file you want to load...",
199 juce::File::getSpecialLocation(juce::File::userHomeDirectory),
202 m_loadSavefileChooser->launchAsync(juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectFiles,
203 [=](
const juce::FileChooser& chooser) {
204 juce::File sourceFile(chooser.getResult());
206 if (!sourceFile.existsAsFile() || !sourceFile.hasReadAccess())
208 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
209 .withIconType(juce::MessageBoxIconType::WarningIcon)
210 .withTitle(
"Loading failed")
211 .withMessage(
"The file " + sourceFile.getFileName() +
" cannot be accessed for reading.")
212 .withButton(
"Ok"),
nullptr);
216 auto config = MemaAppConfiguration::getInstance();
219 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
220 .withIconType(juce::MessageBoxIconType::WarningIcon)
221 .withTitle(
"Loading failed")
222 .withMessage(
"There was an internal error with the configuration.")
223 .withButton(
"Ok"),
nullptr);
227 auto xmlConfig = juce::parseXML(sourceFile);
230 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
231 .withIconType(juce::MessageBoxIconType::WarningIcon)
232 .withTitle(
"Loading failed")
233 .withMessage(
"The file " + sourceFile.getFileName() +
" has invalid contents.")
234 .withButton(
"Ok"),
nullptr);
240 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
241 .withIconType(juce::MessageBoxIconType::WarningIcon)
242 .withTitle(
"Loading failed")
243 .withMessage(
"The file " + sourceFile.getFileName() +
" has invalid contents.")
244 .withButton(
"Ok"),
nullptr);
248 config->SetFlushAndUpdateDisabled();
249 if (!config->resetConfigState(std::move(xmlConfig)))
251 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
252 .withIconType(juce::MessageBoxIconType::WarningIcon)
253 .withTitle(
"Loading failed")
254 .withMessage(
"There was an internal error with applying the configuration.")
255 .withButton(
"Ok"),
nullptr);
256 config->ResetFlushAndUpdateDisabled();
259 config->ResetFlushAndUpdateDisabled();
267 m_loadSavefileChooser = std::make_unique<juce::FileChooser>(
268 "Please select the " + juce::JUCEApplication::getInstance()->getApplicationName() +
" configuration file target you want to save to...",
269 juce::File::getSpecialLocation(juce::File::userHomeDirectory).getChildFile(
270 juce::Time::getCurrentTime().toISO8601(
true).substring(0, 10) +
"_" +
271 juce::JUCEApplication::getInstance()->getApplicationName() +
".config"),
274 m_loadSavefileChooser->launchAsync(juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::canSelectFiles,
275 [=](
const juce::FileChooser& chooser) {
276 juce::File targetFile(chooser.getResult());
279 if (targetFile.getFileExtension() !=
".config")
280 targetFile = targetFile.withFileExtension(
".config");
282 if (!targetFile.hasWriteAccess())
284 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
285 .withIconType(juce::MessageBoxIconType::WarningIcon)
286 .withTitle(
"Saving failed")
287 .withMessage(
"The file " + targetFile.getFileName() +
" cannot be accessed for writing.")
288 .withButton(
"Ok"),
nullptr);
292 auto config = MemaAppConfiguration::getInstance();
295 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
296 .withIconType(juce::MessageBoxIconType::WarningIcon)
297 .withTitle(
"Saving failed")
298 .withMessage(
"There was an internal error with the configuration (0x0).")
299 .withButton(
"Ok"),
nullptr);
303 auto xmlConfig = config->getConfigState();
306 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
307 .withIconType(juce::MessageBoxIconType::WarningIcon)
308 .withTitle(
"Saving failed")
309 .withMessage(
"There was an internal error with the configuration (0x1).")
310 .withButton(
"Ok"),
nullptr);
313 else if (!xmlConfig->writeTo(targetFile))
315 juce::AlertWindow::showAsync(juce::MessageBoxOptions()
316 .withIconType(juce::MessageBoxIconType::WarningIcon)
317 .withTitle(
"Saving failed")
318 .withMessage(
"There was an error when writing the configuration to disk.")
319 .withButton(
"Ok"),
nullptr);
static juce::String getTagName(TagID ID)
@ PROCESSORCONFIG
Audio processor settings.
@ UICONFIG
UI layout and appearance.
bool isValid() override
Returns true when the loaded XML contains all required configuration nodes.
Top-level editor component for the Mema processor — composes the input, crosspoint,...
static constexpr int s_maxChannelCount
Maximum number of input or output channels supported by the routing matrix.
static constexpr int s_minOutputsCount
Minimum number of output channels (always at least 1).
static constexpr int s_minInputsCount
Minimum number of input channels (always at least 1).
void timerCallback() override
Periodic timer callback used to poll CPU usage and trigger deferred dumps.
void setUIConfigState(const std::unique_ptr< juce::XmlElement > &uiConfigState)
Stores a UI configuration state snapshot.
void triggerPromptSaveConfig()
Opens a file chooser dialog to save the configuration file.
void performConfigurationDump() override
Serializes the current configuration to file.
juce::Component * getMemaProcessorEditor()
Returns the main processor editor component.
juce::Component * getDeviceSetupComponent()
Returns the audio device setup component.
void clearUICallbacks()
Clears all UI callback functions.
const std::unique_ptr< juce::XmlElement > & getUIConfigState()
Returns the cached UI configuration state.
void triggerPromptLoadConfig()
Opens a file chooser dialog to load a configuration file.
void onConfigUpdated() override
Reacts to configuration changes and updates internal state.
void propagateLookAndFeelChanged()
Propagates a look-and-feel change to all owned components.