diff --git a/default.pedalboard/manifest.ttl b/default.pedalboard/manifest.ttl index c32718632..fcf27c15a 100644 --- a/default.pedalboard/manifest.ttl +++ b/default.pedalboard/manifest.ttl @@ -4,14 +4,6 @@ @prefix lv2: . @prefix pedal: . -_:b1 - ingen:tail ; - ingen:head . - -_:b2 - ingen:tail ; - ingen:head . - lv2:index 0 ; lv2:name "Capture 1" ; @@ -56,8 +48,6 @@ _:b2 pedal:screenshot ; pedal:thumbnail ; ingen:polyphony 1 ; - ingen:arc _:b1 , - _:b2 ; lv2:port , , , diff --git a/default.pedalboard/screenshot.png b/default.pedalboard/screenshot.png index 2e600ba39..8889274b0 100644 Binary files a/default.pedalboard/screenshot.png and b/default.pedalboard/screenshot.png differ diff --git a/default.pedalboard/thumbnail.png b/default.pedalboard/thumbnail.png index e48216d04..ca53251c8 100644 Binary files a/default.pedalboard/thumbnail.png and b/default.pedalboard/thumbnail.png differ diff --git a/html/css/pedals.css b/html/css/pedals.css index 5dc7664a2..aa4221784 100644 --- a/html/css/pedals.css +++ b/html/css/pedals.css @@ -1032,3 +1032,23 @@ body > .mod-settings.mod-window-visible { .plugin-description p::selection { background: #4f2157 } + +#pedalboard-info .js-save.unmodified-changes { + background-color: #883996; +} + +#mod-cloud-terms { + z-index: 100000; +} +#mod-cloud-terms .mod-box { + width: 500px; + left: 0; + right: 0; + margin: -150px auto; +} +#mod-cloud-terms .actions { + text-align: center; +} +#mod-cloud-terms .btn { + margin: 0px 4px; +} diff --git a/html/desktop-pp.html b/html/desktop-pp.html new file mode 100644 index 000000000..a5635d7f2 --- /dev/null +++ b/html/desktop-pp.html @@ -0,0 +1,207 @@ + + + + + + +MOD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+

MOD Desktop - Privacy Policy

+
+
+ +
+
+ +

Introduction

+

+ Thank you for choosing MOD Desktop. Our commitment to your privacy is paramount, and we aim to be transparent about how we handle data related to your use of MOD Desktop. + Protecting your privacy and ensuring you understand our practices is crucial to us. +

+ +

Data Collection and Use

+

+ MOD Desktop is dedicated to enhancing your user experience while respecting your privacy. + To achieve this, we exclusively utilize Matomo analytics for collecting non-personal usage data. + Choosing Matomo is a deliberate decision to ensure we have full control over the collected information, without relying on third-party platforms such as Google, Facebook, or others. + This approach allows us to maintain the highest standards of privacy and data security by keeping this data in-house. +

+

+ We collect data on the following events to understand usage patterns and improve our service: +

+
    +
  • App Open: Tracks each time the application is opened, helping us gauge application usage frequency.
  • +
  • Plugin Load: Records the loading of plugins by name to identify popular plugins and inform our development focus.
  • +
  • Pedalboard Save: Monitors when pedalboards are saved, capturing usage patterns without storing any pedalboard names.
  • +
  • Pedalboard Load: Tracks the loading of pedalboards, similar to saves, without collecting pedalboard names.
  • +
+

+ This collected data is purely for internal use to enhance the MOD Desktop experience and is strictly non-personal. + Our commitment to your privacy means we prioritize the security and integrity of this usage data, ensuring it remains confidential and is used solely for the purposes outlined above. +

+ +

Data Sharing and Disclosure

+

+ We firmly stand by our commitment to not sell, share, or disclose the collected data to any third parties, except as strictly required by law. + Our use of Matomo enables us to uphold this commitment by providing us complete oversight and control over the analytics data, ensuring it is protected from unauthorized access or use. +

+ +

Data Security

+

+ The security of your data is a top priority. + We implement robust security measures to safeguard the non-personal information collected through our analytics. + While we strive for the highest level of security, it’s important to recognize that no method of electronic storage or transmission over the Internet is entirely secure. + We are committed to using commercially acceptable means to protect your data to ensure its security to the best of our ability. +

+ +

Changes to the Privacy Policy

+

+ We reserve the right to modify or update our Privacy Policy at any time. + Such changes will be communicated through our application and/or website, encouraging you to review our Privacy Policy periodically for any updates. +

+ +

Contact information

+

+ Should you have any inquiries or concerns about our Privacy Policy, please do not hesitate to reach out to us through our designated support channels. + We are here to ensure your experience with MOD Desktop is secure, private, and enjoyable. +

+

Owner and Data Controller:

+
    +
  • + Gianfranco Ceccolini
    + MOD Audio UG (haftungsbeschränkt)
    + Ernst-Augustin-Straße 9
    12489 Berlin
    + Germany +
  • +
  • Owner contact email: gdpr@mod.audio
  • +
+
+
+
+
+ + diff --git a/html/desktop-tou.html b/html/desktop-tou.html new file mode 100644 index 000000000..d7db23b68 --- /dev/null +++ b/html/desktop-tou.html @@ -0,0 +1,225 @@ + + + + + + +MOD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+

MOD Desktop - Terms of Use

+
+
+ +
+
+ +

Introduction

+

+ Welcome to MOD Desktop. + These Terms of Use (“Terms”) govern your access to and use of MOD Desktop, an open-source application governed by the GNU Affero General Public License version 3 (AGPL-3.0). + By accessing or using MOD Desktop, you agree to be bound by these Terms and the AGPL-3.0 under which MOD Desktop is licensed. +

+

+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +

+
+
+ +
+ Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +
+ +
+
+ +

Acceptance of Terms

+

+ By using MOD Desktop, you acknowledge that you have read, understood, and agree to be bound by these Terms and the AGPL-3.0. If you do not agree with any part of these Terms, you may not use MOD Desktop. +

+ +

License and Use Rights

+

+ MOD Desktop is provided under the AGPL-3.0 license, which allows you to use, modify, and distribute the software under the same license. + The AGPL-3.0 is designed to ensure that all users have the freedom to use, study, share, and modify the software. + You are encouraged to review the full text of the AGPL-3.0 to understand your rights and obligations under this license. +

+ +

Modifications and Contributions

+
    +
  • Modifications: You may modify MOD Desktop for personal or community use, subject to the AGPL-3.0’s terms. Any modifications must also be made available under the AGPL-3.0.
  • +
  • Contributions: Contributions to MOD Desktop are welcome and encouraged. By submitting contributions, you agree to license them under the AGPL-3.0, allowing them to be freely used, modified, and shared by the community.
  • +
+ +

Prohibited Use

+

+ You may not use MOD Desktop for any unlawful or prohibited purpose. + You agree not to use MOD Desktop in a way that could damage, disable, overburden, or impair the software or interfere with any other party’s use and enjoyment of MOD Desktop. +

+ +

Intellectual Property

+

+ MOD Desktop and its original content, features, and functionality are and will remain the exclusive property of MOD Desktop’s developers and its contributors. + Your use of MOD Desktop does not grant you ownership rights to the software or its content. +

+ +

Disclaimer of Warranties

+

+ MOD Desktop is provided “AS IS,” without warranty of any kind, express or implied. + The developers of MOD Desktop expressly disclaim all warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. +

+ +

Limitation of Liability

+

+ In no event shall the developers or contributors of MOD Desktop be liable for any direct, indirect, incidental, special, consequential, or punitive damages arising out of or in connection with your use of MOD Desktop. +

+ +

Changes to Terms

+

+ We reserve the right to modify or replace these Terms at any time. We will provide notice of any changes by posting the new Terms on the MOD Desktop website or within the application itself. +

+ +

Governing Law

+

+ These Terms will be governed by and interpreted in accordance with the laws of the jurisdiction in which MOD Desktop’s primary developers are located, without regard to its conflict of law provisions. +

+ +

Contact Us

+

+ If you have any questions about these Terms, please contact us through the designated channels provided by MOD Desktop. +

+ +
+
+
+
+ + diff --git a/html/index.html b/html/index.html index 4ddb9ba1e..693c3da68 100644 --- a/html/index.html +++ b/html/index.html @@ -84,6 +84,10 @@ console.log = function() {} } + var CLOUD_TERMS_ACCEPTED = PREFERENCES['cloud-terms-accepted'] === "true"; + var USING_MOD_DESKTOP = {{using_desktop}}; + var USING_MOD_DEVICE = {{using_mod}} && !{{using_desktop}}; + /* var INFO = { "hardware": {}, @@ -162,16 +166,10 @@ $('#mod-bypassRight').hide() } - if ({{using_mod}}) { - desktop.authenticateDevice(function (ok) { - if (ok) { - console.log("MOD authentication succeeded") - desktop.resetPedalboardStats(); - } else { - console.log("MOD authentication failed") - desktop.upgradeWindow.upgradeWindow('setErrored') - } - }) + if (USING_MOD_DEVICE) { + if (CLOUD_TERMS_ACCEPTED) { + desktop.setupDeviceAuthentication() + } } else { desktop.upgradeWindow.upgradeWindow('setErrored') @@ -195,7 +193,7 @@ } desktop.pedalboardBox.pedalboardBox('initViewMode', PREFERENCES['pb-view-mode']) - if ({{using_app}}) { + if ({{using_desktop}}) { desktop.setupApp() } else { if (PREFERENCES['dev-mode'] == "on") { @@ -219,6 +217,36 @@ // end code for ajax loading bar + // start of terms handling + + var terms = $('#mod-cloud-terms') + if (PREFERENCES['cloud-terms-accepted'] !== undefined && (USING_MOD_DEVICE || PREFERENCES['cloud-terms-accepted'] === "true")) { + if (CLOUD_TERMS_ACCEPTED) { + desktop.setupMatomo() + terms.hide() + $('#pedalboard-info').find('.js-cloud').show() + } + terms.hide() + } else { + terms.find('.js-accept').click(function () { + CLOUD_TERMS_ACCEPTED = true + desktop.saveConfigValue("cloud-terms-accepted", true) + desktop.setupMatomo() + if (USING_MOD_DEVICE) { + desktop.setupDeviceAuthentication() + $('#pedalboard-info').find('.js-cloud').show() + } + terms.hide() + }) + terms.find('.js-reject').click(function () { + desktop.saveConfigValue("cloud-terms-accepted", false) + terms.hide() + }) + terms.show() + } + + // end of terms handling + /* $.ajax({ url: '/system/info', @@ -283,6 +311,9 @@ + + + @@ -334,6 +365,58 @@
+{% if using_desktop == 'true' or using_mod == 'true' %} + +
+
+ {% if using_desktop == 'true' %} +

Welcome to MOD Desktop!

+
+

+ We're thrilled to have you on board and can't wait for you to explore all the amazing features we've built for you. + Before you dive in, there's just a little bit of housekeeping to take care of.
+ By using this application you're agreeing to our + Terms of Use + and + Privacy Policy. + They outline how we work together, use your data, and protect your privacy – ensuring a safe and enjoyable experience for everyone.
+ So, take a quick moment to review these documents. + Once you're all set, the world of MOD is yours to explore. + Happy modding!
+

+
+ {% else %} +

Terms of Use for MOD online components

+ × +
+
+

+ (NOTE: THIS AN EXAMPLE TEXT YET TO BE REVIEWED)
+ Your MOD unit can receive firmware updates over the internet, and also download new plugins and pedalboards.
+ The use of these online services is under Terms of Use and Privacy Policy in order to comply with different regulatory laws across the globe.
+

+

+ Please review our + Terms of Use + and + Privacy Policy before continuing.
+ While you can freely use your MOD unit without accepting these terms, you must agree to them in order to enable or perform any online interaction. +

+
+ {% end %} +
+ {% if using_desktop == 'true' %} + + {% else %} + + + {% end %} +
+
+
+ +{% end %} +
@@ -510,7 +593,7 @@

{{fulltitle}}

- +
diff --git a/html/js/desktop.js b/html/js/desktop.js index 1a95cc3b7..b276b90cd 100644 --- a/html/js/desktop.js +++ b/html/js/desktop.js @@ -102,6 +102,9 @@ function Desktop(elements) { this.pedalboardStats = {}; this.resetPedalboardStats = function() { this.pedalboardStatsSuccess = false; + if (! CLOUD_TERMS_ACCEPTED) { + return + } $.ajax({ url: SITEURL + '/pedalboards/stats', type: 'GET', @@ -140,7 +143,7 @@ function Desktop(elements) { data: JSON.stringify(addressing), success: function (resp) { if (resp) { - self.pedalboardModified = true + self.setPedalboardAsModified(true) callback(true) } else { new Bug("Couldn't address parameter, not allowed") @@ -331,7 +334,7 @@ function Desktop(elements) { callback(pedals, '') } - else + else if (CLOUD_TERMS_ACCEPTED) { // NOTE: this part is never called. pedalboard search is always local $.ajax({ @@ -479,6 +482,15 @@ function Desktop(elements) { }) } + this.setPedalboardAsModified = function (modified) { + this.pedalboardModified = modified + if (modified) { + elements.saveButton.addClass('unmodified-changes') + } else { + elements.saveButton.removeClass('unmodified-changes') + } + } + elements.devicesIcon.statusTooltip() this.ccDeviceManager = new ControlChainDeviceManager({ devicesIcon: elements.devicesIcon, @@ -552,7 +564,7 @@ function Desktop(elements) { var source = syncMode === "link" ? "Ableton Link" : "MIDI" new Notification('info', 'BPM addressing removed, incompatible with ' + source + ' sync mode', 8000) } - self.pedalboardModified = true + self.setPedalboardAsModified(true) }, setSyncMode: function(syncMode, callback) { $.ajax({ @@ -595,7 +607,7 @@ function Desktop(elements) { var source = syncMode === "link" ? "Ableton Link" : "MIDI" new Notification('info', 'BPM addressing removed, incompatible with ' + source + ' sync mode', 8000) } - self.pedalboardModified = true + self.setPedalboardAsModified(true) callback(true) } else { new Bug("Couldn't address parameter") @@ -713,6 +725,7 @@ function Desktop(elements) { success: function () { if (callback) { callback(true) + PREFERENCES[key] = value } }, error: function () { @@ -733,10 +746,32 @@ function Desktop(elements) { $('#mod-devices').hide() $('#mod-status').hide() $('#mod-ram').hide() + $('#mod-show-midi-port').hide() $('#pedalboards-library').find('a').hide() $('#pedal-presets-window').find('.js-assign-all').hide() } + this.setupDeviceAuthentication = function () { + self.authenticateDevice(function (ok) { + if (ok) { + console.log("MOD authentication succeeded") + self.resetPedalboardStats(); + } else { + console.log("MOD authentication failed") + self.upgradeWindow.upgradeWindow('setErrored') + } + }) + } + + this.setupMatomo = function() { + var _mtm = window._mtm = window._mtm || []; + _mtm.push({'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'}); + (function() { + var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; + g.async=true; g.src='https://cdn.matomo.cloud/modaudio.matomo.cloud/container_DfEOyKDN.js'; s.parentNode.insertBefore(g,s); + })(); + } + this.effectBox = self.makeEffectBox(elements.effectBox, elements.effectBoxTrigger) this.cloudPluginBox = self.makeCloudPluginBox(elements.cloudPluginBox, @@ -833,6 +868,10 @@ function Desktop(elements) { }, this.loadRemotePedalboard = function (pedalboard_id) { + if (! CLOUD_TERMS_ACCEPTED) { + return + } + self.windowManager.closeWindows(null, true) if (self.cloudAccessToken == null) { @@ -866,7 +905,7 @@ function Desktop(elements) { transfer.reportFinished = function () { self.pedalboardEmpty = false - self.pedalboardModified = true + self.setPedalboardAsModified(true) } transfer.reportError = function (error) { @@ -933,6 +972,7 @@ function Desktop(elements) { self.waitForScreenshot(false, result.bundlepath, function(){}) // all set callback(true, result.bundlepath, result.title) + _paq.push(['trackEvent', 'pedalboard', 'pedalboard-save']) } else { callback(false, "Failed to save") } @@ -998,7 +1038,6 @@ function Desktop(elements) { url: '/snapshot/save', method: 'POST', success: function () { - self.pedalboardModified = true new Notification('info', 'Pedalboard snapshot saved', 2000) }, error: function () { @@ -1026,7 +1065,6 @@ function Desktop(elements) { } self.pedalboardPresetId = resp.id self.pedalboardPresetName = resp.title - self.pedalboardModified = true self.titleBox.text((self.title || 'Untitled') + " - " + resp.title) new Notification('info', 'Pedalboard snapshot saved', 2000) }, @@ -1180,6 +1218,10 @@ function Desktop(elements) { }) } + if (! CLOUD_TERMS_ACCEPTED) { + return + } + if (self.cloudAccessToken == null) { self.authenticateDevice(function (ok) { if (ok && self.cloudAccessToken != null) { @@ -1467,13 +1509,13 @@ Desktop.prototype.makePedalboard = function (el, effectBox) { self.title = '' self.pedalboardBundle = null self.pedalboardEmpty = true - self.pedalboardModified = false self.pedalboardPresetId = 0 self.pedalboardPresetName = '' self.pedalboardDemoPluginsNotified = false self.titleBox.text('Untitled') self.titleBox.addClass("blend") self.transportControls.resetControlsEnabled() + self.setPedalboardAsModified(false) callback(true) }, @@ -1499,7 +1541,7 @@ Desktop.prototype.makePedalboard = function (el, effectBox) { }, pluginParameterChange: function (port, value) { - self.pedalboardModified = true + self.setPedalboardAsModified(true) ws.send(sprintf("param_set %s %f", port, value)) }, @@ -1508,12 +1550,12 @@ Desktop.prototype.makePedalboard = function (el, effectBox) { }, pluginPatchSet: function (instance, uri, valuetype, value) { - self.pedalboardModified = true + self.setPedalboardAsModified(true) ws.send(sprintf("patch_set %s %s %s %s", instance, uri, valuetype, value)) }, pluginMove: function (instance, x, y) { - self.pedalboardModified = true + self.setPedalboardAsModified(true) ws.send(sprintf("plugin_pos %s %f %f", instance, x, y)) }, @@ -1593,7 +1635,7 @@ Desktop.prototype.makePedalboard = function (el, effectBox) { // Bind events el.bind('modified', function () { self.pedalboardEmpty = false - self.pedalboardModified = true + self.setPedalboardAsModified(true) }) /* el.bind('dragStart', function () { @@ -1848,12 +1890,13 @@ Desktop.prototype.loadPedalboard = function (bundlepath, callback) { self.title = resp.name self.pedalboardBundle = bundlepath self.pedalboardEmpty = false - self.pedalboardModified = false self.pedalboardDemoPluginsNotified = false + self.setPedalboardAsModified(false) self.titleBox.text(resp.name); self.titleBox.removeClass("blend"); callback(true) + _paq.push(['trackEvent', 'pedalboard', 'pedalboard-load']); }, error: function () { new Bug("Couldn't load pedalboard") @@ -1887,7 +1930,7 @@ Desktop.prototype.saveCurrentPedalboard = function (asNew, callback) { self.title = title self.pedalboardBundle = errorOrPath self.pedalboardEmpty = false - self.pedalboardModified = false + self.setPedalboardAsModified(false) self.titleBox.text(title + " - " + self.pedalboardPresetName) if (self.previousPedalboardList != null) { diff --git a/html/js/host.js b/html/js/host.js index fc8637b0d..c377e4358 100644 --- a/html/js/host.js +++ b/html/js/host.js @@ -119,6 +119,10 @@ $('document').ready(function() { $("#mod-cpu-stats").html(sprintf("%.1f GHz / %d °C", parseInt(cpufreq)/1000000, parseInt(cputemp)/1000)) + } else if (cpufreq !== "0") { + $("#mod-cpu-stats").html(sprintf("%.1f GHz", parseInt(cpufreq)/1000000)) + } else if (cputemp !== "0") { + $("#mod-cpu-stats").html(sprintf("%d °C", parseInt(cputemp)/1000)) } return } @@ -506,9 +510,9 @@ $('document').ready(function() { success: function (resp) { desktop.pedalboard.pedalboard('scheduleAdapt', true) desktop.pedalboardEmpty = empty && !modified - desktop.pedalboardModified = modified desktop.pedalboardPresetId = snapshotId desktop.pedalboardPresetName = resp.name + desktop.setPedalboardAsModified(modified) if (resp.ok) { desktop.titleBox.text((desktop.title || 'Untitled') + " - " + resp.name) diff --git a/html/js/pedalboard.js b/html/js/pedalboard.js index 7c25d23ec..977c9a241 100644 --- a/html/js/pedalboard.js +++ b/html/js/pedalboard.js @@ -386,6 +386,9 @@ JqueryClass('pedalboard', { width: ui.helper.children().width(), height: ui.helper.children().height() }) + var uri = unescape(ui.draggable.attr('mod-uri')); + var count = self.find('.mod-actions').length; + _paq.push(['trackEvent', 'plugin', 'plugin-add', uri, count]); } }) diff --git a/html/js/snapshot.js b/html/js/snapshot.js index 63b21ab3d..27a94905c 100644 --- a/html/js/snapshot.js +++ b/html/js/snapshot.js @@ -102,7 +102,7 @@ function SnapshotsManager(options) { options.pedalPresetsWindow.find('.js-assign-all').addClass('disabled') } - // Replace options value and text so we can a sequential list 0, 1, 2, etc. + // Replace options value and text so we get a sequential list 1, 2, 3, etc. var i = 0 options.pedalPresetsList.children().each(function(option) { var optionHtml = $(this).html() diff --git a/mod/__init__.py b/mod/__init__.py index bab760f8f..879f63c24 100644 --- a/mod/__init__.py +++ b/mod/__init__.py @@ -59,9 +59,17 @@ def check_environment(): # create temp dirs if not os.path.exists(DOWNLOAD_TMP_DIR): os.makedirs(DOWNLOAD_TMP_DIR) + if os.path.exists(PEDALBOARD_TMP_DIR): - shutil.rmtree(PEDALBOARD_TMP_DIR) - os.makedirs(PEDALBOARD_TMP_DIR) + try: + shutil.rmtree(PEDALBOARD_TMP_DIR) + except OSError: + pass + else: + os.makedirs(PEDALBOARD_TMP_DIR) + + else: + os.makedirs(PEDALBOARD_TMP_DIR) # remove temp files for path in (CAPTURE_PATH, PLAYBACK_PATH, UPDATE_CC_FIRMWARE_FILE): @@ -105,7 +113,7 @@ def check_environment(): if os.path.exists(UPDATE_MOD_OS_HERLPER_FILE): os.remove(UPDATE_MOD_OS_HERLPER_FILE) - os.sync() + os_sync() return True diff --git a/mod/addressings.py b/mod/addressings.py index 7d09d1410..6a25fdfae 100644 --- a/mod/addressings.py +++ b/mod/addressings.py @@ -674,8 +674,12 @@ def registerMappings(self, msg_callback, instances): for actuator_uri, addrs in self.cv_addressings.items(): # pluginData = self._task_get_plugin_data(instance_id) if not self.is_hw_cv_port(actuator_uri): - operational_mode = self._task_get_plugin_cv_port_op_mode(actuator_uri) - msg_callback("add_cv_port %s %s %s" % (actuator_uri, addrs['name'].replace(" ","_"), operational_mode)) + try: + operational_mode = self._task_get_plugin_cv_port_op_mode(actuator_uri) + except KeyError: + pass + else: + msg_callback("add_cv_port %s %s %s" % (actuator_uri, addrs['name'].replace(" ","_"), operational_mode)) # HMI group_mappings = [] #{} if self.addressing_pages else [] diff --git a/mod/host.py b/mod/host.py index ced4be687..16dec2aa0 100644 --- a/mod/host.py +++ b/mod/host.py @@ -402,8 +402,8 @@ def __init__(self, hmi, prefs, msg_callback): # clients at the end of the chain, all managed by mod-host self.jack_hw_capture_prefix = "mod-host:out" if self.descriptor.get('has_noisegate', False) else "system:capture_" - # used for network-manager - self.jack_slave_prefix = "mod-slave" + # used for external connections + self.jack_external_prefix = "mod-external" # used for usb gadget, MUST have "c" or "p" after this prefix self.jack_usbgadget_prefix = "mod-usbgadget_" @@ -551,8 +551,8 @@ def jack_port_appeared(self, name, isOutput): name = charPtrToString(name) isOutput = bool(isOutput) - if name.startswith(self.jack_slave_prefix+":"): - name = name.replace(self.jack_slave_prefix+":","") + if name.startswith(self.jack_external_prefix+":"): + name = name.replace(self.jack_external_prefix+":","") if name.startswith("midi_"): ptype = "midi" elif name.startswith(CV_PREFIX): @@ -563,6 +563,12 @@ def jack_port_appeared(self, name, isOutput): index = 100 + int(name.rsplit("_",1)[-1]) title = name.title().replace(" ","_") self.msg_callback("add_hw_port /graph/%s %s %i %s %i" % (name, ptype, int(isOutput), title, index)) + + if ptype == "audio": + if isOutput: + self.audioportsOut.append(name) + else: + self.audioportsIn.append(name) return if name.startswith(self.jack_usbgadget_prefix): @@ -643,6 +649,13 @@ def jack_port_deleted(self, name): self.msg_callback("remove_hw_port /graph/%s" % (name.split(":",1)[-1])) + if name.startswith(self.jack_external_prefix+":"): + name = name.replace(self.jack_external_prefix+":","") + if name in self.audioportsIn: + self.audioportsIn.remove(name) + if name in self.audioportsOut: + self.audioportsOut.remove(name) + def true_bypass_changed(self, left, right): self.msg_callback("truebypass %i %i" % (left, right)) @@ -1503,7 +1516,7 @@ def initialize_hmi(self, uiConnected, callback): for i in range(startIndex, endIndex): initial_state_data += ' %s %d' % (normalize_for_hw(pedalboards[i]['title']), i + self.pedalboard_index_offset) - def cb_migi_pb_prgch(_): + def cb_midi_pb_prgch(_): midi_pb_prgch = self.profile.get_midi_prgch_channel("pedalboard") if midi_pb_prgch >= 1 and midi_pb_prgch <= 16: self.send_notmodified("monitor_midi_program %d 1" % (midi_pb_prgch-1), @@ -1512,19 +1525,19 @@ def cb_migi_pb_prgch(_): callback(True) def cb_footswitches(_): - self.setNavigateWithFootswitches(True, cb_migi_pb_prgch) + self.setNavigateWithFootswitches(True, cb_midi_pb_prgch) def cb_set_initial_state(_): - cb = cb_footswitches if self.isBankFootswitchNavigationOn() else cb_migi_pb_prgch + cb = cb_footswitches if self.isBankFootswitchNavigationOn() else cb_midi_pb_prgch self.hmi.initial_state(initial_state_data, cb) if self.hmi.initialized: if self.descriptor.get("hmi_bank_navigation", False): self.setNavigateWithFootswitches(False, cb_set_initial_state) else: - self.hmi.initial_state(initial_state_data, cb_migi_pb_prgch) + self.hmi.initial_state(initial_state_data, cb_midi_pb_prgch) else: - cb_migi_pb_prgch(True) + cb_midi_pb_prgch(True) def start_session(self, callback): midi_pb_prgch, midi_ss_prgch = self.profile.get_midi_prgch_channels() @@ -1572,7 +1585,12 @@ def end_session(self, callback): self.send_output_data_ready(None, None) if not self.hmi.initialized: - callback(True) + # typically initialize_hmi takes care of this but without HMI we need to do it manually + midi_pb_prgch, midi_ss_prgch = self.profile.get_midi_prgch_channels() + if midi_pb_prgch >= 1 and midi_pb_prgch <= 16: + self.send_notmodified("monitor_midi_program %d 1" % (midi_pb_prgch-1), callback) + else: + callback(True) return if not self.hmi.connected: callback(True) @@ -2081,7 +2099,7 @@ def report_current_state(self, websocket): ports = get_jack_hardware_ports(False, False) for i in range(len(ports)): name = ports[i] - if name not in midiports and not name.startswith("%s:midi_" % self.jack_slave_prefix): + if name not in midiports and not name.startswith("%s:midi_" % self.jack_external_prefix): continue alias = get_jack_port_alias(name) @@ -2104,7 +2122,7 @@ def report_current_state(self, websocket): ports = get_jack_hardware_ports(False, True) for i in range(len(ports)): name = ports[i] - if name not in midiports and not name.startswith("%s:midi_" % self.jack_slave_prefix): + if name not in midiports and not name.startswith("%s:midi_" % self.jack_external_prefix): continue alias = get_jack_port_alias(name) if alias: @@ -3252,6 +3270,10 @@ def snapshot_load(self, idx, from_hmi, abort_catcher, callback): # callback must be last action callback(True) + def save_snapshots_to_disk(self): + if self.pedalboard_path: + self.save_state_snapshots(self.pedalboard_path) + @gen.coroutine def page_load(self, idx, abort_catcher, callback): if not self.addressings.addressing_pages: @@ -3334,11 +3356,11 @@ def _fix_host_connection_port(self, port): if num in monitorportnums: return "mod-monitor:in_" + num - if data[2].startswith(("audio_from_slave_", - "audio_to_slave_", - "midi_from_slave_", - "midi_to_slave_")): - return "%s:%s" % (self.jack_slave_prefix, data[2]) + if data[2].startswith(("audio_from_external_", + "audio_to_external_", + "midi_from_external_", + "midi_to_external_")): + return "%s:%s" % (self.jack_external_prefix, data[2]) if data[2].startswith("USB_Audio_Capture_"): return "%s:%s" % (self.jack_usbgadget_prefix+"c", data[2]) @@ -3490,10 +3512,11 @@ def load(self, bundlepath, isDefault=False, abort_catcher=None): p.split(":",1)[-1]) for p in get_jack_hardware_ports(False, True)) else: - mappedOldMidiIns = {} - mappedOldMidiOuts = {} - mappedNewMidiIns = {} - mappedNewMidiOuts = {} + mappedOldMidiIns = {} + mappedOldMidiOuts = {} + mappedOldMidiOuts2 = {} + mappedNewMidiIns = {} + mappedNewMidiOuts = {} curmidisymbols = [] for port_symbol, port_alias, _ in self.midiports: @@ -4752,11 +4775,14 @@ def get_free_memory_value(self): def get_system_stats_message(self): memload = self.get_free_memory_value() cpufreq = read_file_contents(self.cpufreqfile, "0") - try: - cputemp = read_file_contents(self.thermalfile, "0") - except OSError: - cputemp = "0" - self.thermalfile = None + cputemp = "0" + + if self.thermalfile is not None: + try: + cputemp = read_file_contents(self.thermalfile, "0") + except OSError: + self.thermalfile = None + return "sys_stats %s %s %s" % (memload, cpufreq, cputemp) def memtimer_callback(self): @@ -6970,7 +6996,7 @@ def profile_apply(self, values, isIntermediate): # skip alsamixer related things on intermediate/boot if not isIntermediate: - apply_mixer_values(values, self.descriptor.get("platform", None)) + apply_mixer_values(values) if self.hmi.initialized: try: diff --git a/mod/profile.py b/mod/profile.py index 1649996e8..d721a0300 100644 --- a/mod/profile.py +++ b/mod/profile.py @@ -14,7 +14,7 @@ from tornado.ioloop import IOLoop from mod import TextFileFlusher, safe_json_load -from mod.settings import APP, DATA_DIR +from mod.settings import DATA_DIR def index_to_filepath(index): return os.path.join(DATA_DIR, "profile{0}.json".format(index)) @@ -24,12 +24,13 @@ def ensure_data_index_valid(data, fallback): if not isinstance(index, int) or index < 1 or index > Profile.NUM_PROFILES: data['index'] = fallback -def apply_mixer_values(values, platform): +def apply_mixer_values(values): if not os.path.exists("/usr/bin/mod-amixer"): return - if os.getenv("MOD_SOUNDCARD", None) is None: + soundcard = os.getenv("MOD_SOUNDCARD", None) + if soundcard is None: return - if platform == "duo": + if soundcard == "MODDUO": os.system("/usr/bin/mod-amixer in 1 dvol %f" % values['input1volume']) os.system("/usr/bin/mod-amixer in 2 dvol %f" % values['input2volume']) os.system("/usr/bin/mod-amixer out 1 dvol %f" % values['output1volume']) @@ -38,7 +39,7 @@ def apply_mixer_values(values, platform): os.system("/usr/bin/mod-amixer hp byp %s" % Profile.value_to_string('headphoneBypass', values['headphoneBypass'])) return - if platform == "duox": + if soundcard == "DUOX": os.system("/usr/bin/mod-amixer in 1 xvol %f" % values['input1volume']) os.system("/usr/bin/mod-amixer in 2 xvol %f" % values['input2volume']) os.system("/usr/bin/mod-amixer out 1 xvol %f" % values['output1volume']) @@ -48,24 +49,25 @@ def apply_mixer_values(values, platform): os.system("/usr/bin/mod-amixer cvexp %s" % Profile.value_to_string('inputMode', values['inputMode'])) os.system("/usr/bin/mod-amixer exppedal %s" % Profile.value_to_string('expPedalMode', values['expPedalMode'])) return - if platform == "dwarf": + if soundcard == "DWARF": os.system("/usr/bin/mod-amixer in 1 xvol %f" % values['input1volume']) os.system("/usr/bin/mod-amixer in 2 xvol %f" % values['input2volume']) os.system("/usr/bin/mod-amixer out 1 xvol %f" % values['output1volume']) os.system("/usr/bin/mod-amixer out 2 xvol %f" % values['output2volume']) os.system("/usr/bin/mod-amixer hp xvol %f" % values['headphoneVolume']) return - if platform is None: - logging.error("[profile] apply_mixer_values called without platform") + if soundcard is None: + logging.error("[profile] apply_mixer_values called without soundcard") else: - logging.error("[profile] apply_mixer_values called with unknown platform %s", platform) + logging.error("[profile] apply_mixer_values called with unknown soundcard %s", soundcard) -def fill_in_mixer_values(data, platform): +def fill_in_mixer_values(data): if not os.path.exists("/usr/bin/mod-amixer"): return - if os.getenv("MOD_SOUNDCARD", None) is None: + soundcard = os.getenv("MOD_SOUNDCARD", None) + if soundcard is None: return - if platform == "duo": + if soundcard == "MODDUO": data['input1volume'] = float(getoutput("/usr/bin/mod-amixer in 1 dvol").strip()) data['input2volume'] = float(getoutput("/usr/bin/mod-amixer in 2 dvol").strip()) data['output1volume'] = float(getoutput("/usr/bin/mod-amixer out 1 dvol").strip()) @@ -74,7 +76,7 @@ def fill_in_mixer_values(data, platform): data['headphoneBypass'] = Profile.string_to_value('headphoneBypass', getoutput("/usr/bin/mod-amixer hp byp").strip()) return - if platform == "duox": + if soundcard == "DUOX": data['input1volume'] = float(getoutput("/usr/bin/mod-amixer in 1 xvol").strip()) data['input2volume'] = float(getoutput("/usr/bin/mod-amixer in 2 xvol").strip()) data['output1volume'] = float(getoutput("/usr/bin/mod-amixer out 1 xvol").strip()) @@ -85,17 +87,17 @@ def fill_in_mixer_values(data, platform): data['expPedalMode'] = Profile.string_to_value('expPedalMode', getoutput("/usr/bin/mod-amixer exppedal").strip()) return - if platform == "dwarf": + if soundcard == "DWARF": data['input1volume'] = float(getoutput("/usr/bin/mod-amixer in 1 xvol").strip()) data['input2volume'] = float(getoutput("/usr/bin/mod-amixer in 2 xvol").strip()) data['output1volume'] = float(getoutput("/usr/bin/mod-amixer out 1 xvol").strip()) data['output1volume'] = float(getoutput("/usr/bin/mod-amixer out 2 xvol").strip()) data['headphoneVolume'] = float(getoutput("/usr/bin/mod-amixer hp xvol").strip()) return - if platform is None: - logging.error("[profile] fill_in_mixer_values called without platform") + if soundcard is None: + logging.error("[profile] fill_in_mixer_values called without soundcard") else: - logging.error("[profile] fill_in_mixer_values called with unknown platform %s", platform) + logging.error("[profile] fill_in_mixer_values called with unknown soundcard %s", soundcard) # The user profile models environmental context. # That is all settings that are related to the physical hookup of the device. @@ -225,10 +227,9 @@ def value_to_string(cls, key, value): return "" def __init__(self, applyFn, hwdescriptor): - self.applyFn = applyFn - self.platform = hwdescriptor.get("platform", None) - self.changed = False - self.values = self.DEFAULTS.copy() + self.applyFn = applyFn + self.changed = False + self.values = self.DEFAULTS.copy() if os.path.exists(self.INTERMEDIATE_PROFILE_PATH): data = safe_json_load(self.INTERMEDIATE_PROFILE_PATH, dict) @@ -241,7 +242,7 @@ def __init__(self, applyFn, hwdescriptor): except IOError: pass - fill_in_mixer_values(self.values, self.platform) + fill_in_mixer_values(self.values) IOLoop.instance().add_callback(self.apply_first) # ----------------------------------------------------------------------------------------------------------------- @@ -411,7 +412,7 @@ def store(self, index): self.values['index'] = index # request and store mixer values - fill_in_mixer_values(self.values, self.platform) + fill_in_mixer_values(self.values) # save intermediate file first with TextFileFlusher(self.INTERMEDIATE_PROFILE_PATH) as fh: diff --git a/mod/screenshot.py b/mod/screenshot.py index de05d917a..ba3c2dd5c 100644 --- a/mod/screenshot.py +++ b/mod/screenshot.py @@ -8,7 +8,7 @@ import logging from tornado.ioloop import IOLoop -from mod.settings import HTML_DIR, DEV_ENVIRONMENT, DEVICE_KEY, CACHE_DIR, APP +from mod.settings import HTML_DIR, DEV_ENVIRONMENT, DEVICE_KEY, CACHE_DIR, DESKTOP def generate_screenshot(bundle_path, callback): @@ -24,7 +24,7 @@ def generate_screenshot(bundle_path, callback): cwd = os.path.abspath(os.path.join(os.path.dirname(__file__), '../')) # running packaged through cxfreeze - if APP and os.path.isfile(sys.argv[0]): + if DESKTOP and os.path.isfile(sys.argv[0]): cmd = [os.path.join(cwd, 'mod-pedalboard'), 'take_screenshot', bundle_path, HTML_DIR, CACHE_DIR] if sys.platform == 'win32': cmd[0] += ".exe" diff --git a/mod/session.py b/mod/session.py index bcd0a25a2..4e7da8f01 100644 --- a/mod/session.py +++ b/mod/session.py @@ -69,6 +69,7 @@ def __init__(self): self.recordhandle = None self.external_ui_timer = None + self.screenshot_needed = False self.screenshot_generator = ScreenshotGenerator() self.websockets = [] @@ -155,10 +156,12 @@ def hmi_reinit_cb(self): # Add a new plugin, starts enabled (ie, not bypassed) def web_add(self, instance, uri, x, y, callback): + self.screenshot_needed = True self.host.add_plugin(instance, uri, x, y, callback) # Remove a plugin def web_remove(self, instance, callback): + self.screenshot_needed = True self.host.remove_plugin(instance, callback) # Address a plugin parameter @@ -181,10 +184,12 @@ def web_set_sync_mode(self, mode, callback): # Connect 2 ports def web_connect(self, port_from, port_to, callback): + self.screenshot_needed = True self.host.connect(port_from, port_to, callback) # Disconnect 2 ports def web_disconnect(self, port_from, port_to, callback): + self.screenshot_needed = True self.host.disconnect(port_from, port_to, callback) # Save the current pedalboard @@ -196,7 +201,10 @@ def web_save_pedalboard(self, title, asNew, callback): if self.hmi.initialized and self.host.descriptor.get('hmi_set_pb_name', False): self.hmi_set_pb_name(newTitle or title) - self.screenshot_generator.schedule_screenshot(bundlepath) + if bundlepath and self.screenshot_needed: + self.screenshot_needed = False + self.screenshot_generator.schedule_screenshot(bundlepath) + return bundlepath, newTitle # Get list of Hardware MIDI devices @@ -334,11 +342,13 @@ def ws_patch_set(self, instance, uri, valuetype, valuedata, ws): # Set a plugin block position within the canvas def ws_plugin_position(self, instance, x, y, ws): + self.screenshot_needed = True self.host.set_position(instance, x, y) self.msg_callback_broadcast("plugin_pos %s %d %d" % (instance, x, y), ws) # set the size of the pedalboard (in 1:1 view, aka "full zoom") def ws_pedalboard_size(self, width, height): + self.screenshot_needed = True self.host.set_pedalboard_size(width, height) def ws_show_external_ui(self, instance): @@ -412,6 +422,7 @@ def msg_callback_broadcast(self, msg, ws2): ws.write_message(msg) def load_pedalboard(self, bundlepath, isDefault): + self.screenshot_needed = False self.host.send_notmodified("feature_enable processing 0") title = self.host.load(bundlepath, isDefault) self.host.send_notmodified("feature_enable processing 1") @@ -429,6 +440,7 @@ def load_pedalboard(self, bundlepath, isDefault): def reset(self, callback): logging.debug("SESSION RESET") + self.screenshot_needed = False self.host.send_notmodified("feature_enable processing 0") def host_callback(resp): diff --git a/mod/settings.py b/mod/settings.py index 2109543fb..498ffb813 100644 --- a/mod/settings.py +++ b/mod/settings.py @@ -12,7 +12,7 @@ # If on, use dev cloud API environment DEV_API = bool(int(os.environ.get('MOD_DEV_API', False))) -APP = bool(int(os.environ.get('MOD_APP', False))) +DESKTOP = bool(int(os.environ.get('MOD_DESKTOP', False))) LOG = int(os.environ.get('MOD_LOG', 0)) API_KEY = os.environ.pop('MOD_API_KEY', None) diff --git a/mod/webserver.py b/mod/webserver.py index a77ee6844..5a48ccc5c 100644 --- a/mod/webserver.py +++ b/mod/webserver.py @@ -28,7 +28,7 @@ haveSignal = False from mod.profile import Profile -from mod.settings import (APP, LOG, DEV_API, +from mod.settings import (DESKTOP, LOG, DEV_API, HTML_DIR, DOWNLOAD_TMP_DIR, DEVICE_KEY, DEVICE_WEBSERVER_PORT, CLOUD_HTTP_ADDRESS, PLUGINS_HTTP_ADDRESS, PEDALBOARDS_HTTP_ADDRESS, CONTROLCHAIN_HTTP_ADDRESS, USER_BANKS_JSON_FILE, @@ -1173,7 +1173,7 @@ def check_origin(self, origin): protocol, domain = match.groups() if protocol not in ("http", "https"): return False - if domain != "mod.audio" and not domain.endswith(".mod.audio"): + if domain != "localhost:8010" and domain != "mod.audio" and not domain.endswith(".mod.audio"): return False return True @@ -1627,6 +1627,7 @@ def post(self, mode): class SnapshotSave(JsonRequestHandler): def post(self): ok = SESSION.host.snapshot_save() + SESSION.host.save_snapshots_to_disk() self.write(ok) class SnapshotSaveAs(JsonRequestHandler): @@ -1639,6 +1640,7 @@ def get(self): yield gen.Task(SESSION.host.hmi_report_ss_name_if_current, idx) + SESSION.host.save_snapshots_to_disk() self.write({ 'ok': idx is not None, 'id': idx, @@ -1658,6 +1660,7 @@ def get(self): yield gen.Task(SESSION.host.hmi_report_ss_name_if_current, idx) + SESSION.host.save_snapshots_to_disk() self.write({ 'ok': ok, 'title': title, @@ -1667,6 +1670,7 @@ class SnapshotRemove(JsonRequestHandler): def get(self): idx = int(self.get_argument('id')) ok = SESSION.host.snapshot_remove(idx) + SESSION.host.save_snapshots_to_disk() self.write(ok) class SnapshotList(JsonRequestHandler): @@ -1833,8 +1837,8 @@ def index(self): 'fulltitle': xhtml_escape(fullpbname), 'titleblend': '' if SESSION.host.pedalboard_name else 'blend', 'dev_api_class': 'dev_api' if DEV_API else '', - 'using_app': 'true' if APP else 'false', - 'using_mod': 'true' if DEVICE_KEY or DEV_HOST else 'false', + 'using_desktop': 'true' if DESKTOP else 'false', + 'using_mod': 'true' if DEVICE_KEY and hwdesc.get('platform', None) is not None else 'false', 'user_name': mod_squeeze(user_id.get("name", "")), 'user_email': mod_squeeze(user_id.get("email", "")), 'favorites': json.dumps(gState.favorites), @@ -1999,11 +2003,14 @@ def post(self): index = freqs.index(cur_freq) + 1 if index >= len(freqs): index = 0 + next_freq = freqs[index] + if cur_freq == next_freq: + return self.write(True) with open("/sys/devices/system/cpu/online", 'r') as fh: num_start, num_end = tuple(int(i) for i in fh.read().strip().split("-")) for num in range(num_start, num_end+1): with open("/sys/devices/system/cpu/cpu%d/cpufreq/scaling_setspeed" % num, 'w') as fh: - fh.write(freqs[index]) + fh.write(next_freq) self.write(True) class SaveSingleConfigValue(JsonRequestHandler): @@ -2495,7 +2502,7 @@ def prepare(isModApp = False): signal(SIGUSR2, signal_recv) set_process_name("mod-ui") - application.listen(DEVICE_WEBSERVER_PORT, address=("localhost" if APP else "0.0.0.0")) + application.listen(DEVICE_WEBSERVER_PORT, address=("127.0.0.1" if DESKTOP else "0.0.0.0")) def checkhost(): if SESSION.host.readsock is None or SESSION.host.writesock is None: diff --git a/modtools/utils.py b/modtools/utils.py index a6d53734e..de3ed8d52 100644 --- a/modtools/utils.py +++ b/modtools/utils.py @@ -616,9 +616,6 @@ class JackData(Structure): utils.file_uri_parse.argtypes = (c_char_p,) utils.file_uri_parse.restype = c_char_p -utils.set_cpu_affinity.argtypes = (c_int,) -utils.set_cpu_affinity.restype = None - utils.init_jack.argtypes = None utils.init_jack.restype = c_bool @@ -918,12 +915,6 @@ def get_bundle_dirname(bundleuri): return bundle -# ------------------------------------------------------------------------------------------------------------ -# helper utilities - -def set_cpu_affinity(cpu): - utils.set_cpu_affinity(cpu) - # ------------------------------------------------------------------------------------------------------------ # jack stuff diff --git a/requirements.txt b/requirements.txt index 53b9822ed..6208e9e00 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ Pillow>=8.2.0 pyserial==3.0 tornado==4.3 -git+https://github.com/dlitz/pycrypto.git@master#egg=pycrypto +pycryptodomex==3.18.0 aggdraw==1.3.11 # for making old tornado python3.10 compatible: diff --git a/utils/Makefile b/utils/Makefile index 27c9ee9b8..ac16c9d1d 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -33,8 +33,8 @@ LDFLAGS += -Wl,-O1,--no-undefined,--strip-all endif endif -ifeq ($(MODAPP),1) -CXXFLAGS += -DMODAPP +ifeq ($(MOD_DESKTOP),1) +CXXFLAGS += -D_MOD_DESKTOP ifneq ($(MACOS)$(WINDOWS),true) LDFLAGS += -Wl,-rpath,'$$ORIGIN/..' endif @@ -58,7 +58,7 @@ JACK_LIBS = $(shell pkg-config --libs jack) endif LILV_CFLAGS = $(shell pkg-config --cflags lilv-0) -Wno-parentheses -LILV_LIBS = $(shell pkg-config --libs lilv-0) +LILV_LIBS = $(shell pkg-config --libs lilv-0) -lm all: build diff --git a/utils/utils.h b/utils/utils.h index 0affa3f04..2f2473d9d 100644 --- a/utils/utils.h +++ b/utils/utils.h @@ -427,9 +427,6 @@ MOD_API const char* const* list_plugins_in_bundle(const char* bundle); // Convert a file URI to a local path string. MOD_API const char* file_uri_parse(const char* fileuri); -// helper utilities -MOD_API void set_cpu_affinity(int cpu); - // jack stuff MOD_API bool init_jack(void); MOD_API void close_jack(void); diff --git a/utils/utils_jack.cpp b/utils/utils_jack.cpp index e9494ad63..e5bf2cd95 100644 --- a/utils/utils_jack.cpp +++ b/utils/utils_jack.cpp @@ -24,8 +24,8 @@ #define ALSA_CONTROL_SPDIF_ENABLE "SPDIF Enable" #define ALSA_CONTROL_MASTER_VOLUME "DAC" -#define JACK_SLAVE_PREFIX "mod-slave" -#define JACK_SLAVE_PREFIX_LEN 9 +#define JACK_EXTERNAL_PREFIX "mod-external" +#define JACK_EXTERNAL_PREFIX_LEN 12 // -------------------------------------------------------------------------------------------------------- @@ -101,7 +101,7 @@ static void JackPortRegistration(jack_port_id_t port_id, int reg, void*) if (const char* const port_name = jack_port_name(port)) { if (strncmp(port_name, "system:midi_", 12) != 0 && - strncmp(port_name, JACK_SLAVE_PREFIX ":", JACK_SLAVE_PREFIX_LEN + 1) != 0 && + strncmp(port_name, JACK_EXTERNAL_PREFIX ":", JACK_EXTERNAL_PREFIX_LEN + 1) != 0 && strncmp(port_name, "nooice", 5) != 0) return; @@ -199,9 +199,12 @@ bool init_jack(void) return true; } -#ifdef MODAPP +#ifdef _MOD_DESKTOP const jack_options_t options = static_cast(JackNoStartServer|JackUseExactName|JackServerName); - jack_client_t* const client = jack_client_open("mod-ui", options, nullptr, "mod-app"); + const char* servername = std::getenv("MOD_DESKTOP_SERVER_NAME"); + if (servername == nullptr) + servername = "mod-desktop"; + jack_client_t* const client = jack_client_open("mod-ui", options, nullptr, servername); #else const jack_options_t options = static_cast(JackNoStartServer|JackUseExactName); jack_client_t* const client = jack_client_open("mod-ui", options, nullptr); diff --git a/utils/utils_lilv.cpp b/utils/utils_lilv.cpp index cd7e96bcd..28aa671fb 100644 --- a/utils/utils_lilv.cpp +++ b/utils/utils_lilv.cpp @@ -186,6 +186,7 @@ static size_t HOMElen = strlen(HOME); // configuration static const bool kAllowRegularCV = getenv("MOD_UI_ALLOW_REGULAR_CV") != nullptr; +static const bool kOnlyShowPluginsWithMODGUI = getenv("MOD_UI_ONLY_SHOW_PLUGINS_WITH_MODGUI") != nullptr; #define PluginInfo_Init { \ false, \ @@ -241,7 +242,7 @@ inline bool contains(const std::unordered_map& map, const std::s return map.find(value) != map.end(); } -inline bool ends_with(const std::string& value, const std::string ending) +inline bool ends_with(const std::string& value, const std::string& ending) { if (ending.size() > value.size()) return false; @@ -266,7 +267,7 @@ inline std::string sha1(const char* const cstring) uint8_t* const hashenc = sha1_result(&s); for (int i=0; igui.resourcesDirectory == nc) + if (kOnlyShowPluginsWithMODGUI && miniInfo->gui.resourcesDirectory == nc) continue; -#endif _get_plugs_mini_ret[curIndex++] = PLUGNFO_Mini[uri]; } } @@ -5958,15 +5957,4 @@ const char* file_uri_parse(const char* const fileuri) return _file_uri_parse_ret != nullptr ? _file_uri_parse_ret : nc; } -void set_cpu_affinity(const int cpu) -{ -#ifdef __linux__ - printf("NOTE: Running pinned to core #%d\n", cpu+1); - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(cpu, &cpuset); - sched_setaffinity(0, sizeof(cpuset), &cpuset); -#endif -} - // --------------------------------------------------------------------------------------------------------