From 59acfeaafc1f1bf055d988a31f8d1b0d6bf8b425 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 19 Jan 2013 22:32:45 +0000 Subject: [PATCH] Misc --- source/backend/carla_backend.hpp | 6 +- source/carla_backend.py | 10 - source/carla_shared.py | 325 ++++++++++++++++++++++++++- source/discovery/carla-discovery.cpp | 6 + source/includes/lv2_rdf.hpp | 23 +- source/utils/carla_utils.hpp | 2 +- 6 files changed, 344 insertions(+), 28 deletions(-) diff --git a/source/backend/carla_backend.hpp b/source/backend/carla_backend.hpp index 5de31b04e..af35e8500 100644 --- a/source/backend/carla_backend.hpp +++ b/source/backend/carla_backend.hpp @@ -601,13 +601,13 @@ struct CustomData { ~CustomData() { if (type) - free((void*)type); + ::free((void*)type); if (key) - free((void*)key); + ::free((void*)key); if (value) - free((void*)value); + ::free((void*)value); } }; diff --git a/source/carla_backend.py b/source/carla_backend.py index 5e64e7c13..a760d01a7 100644 --- a/source/carla_backend.py +++ b/source/carla_backend.py @@ -35,16 +35,6 @@ except: print("LRDF Support not available (LADSPA-RDF will be disabled)") haveLRDF = False -# ------------------------------------------------------------------------------------------------------------ -# Convert a ctypes c_char_p into a python string - -def cString(value): - if not value: - return "" - if isinstance(value, str): - return value - return value.decode("utf-8", errors="ignore") - # ------------------------------------------------------------------------------------------------------------ # Convert a ctypes struct into a python dict diff --git a/source/carla_shared.py b/source/carla_shared.py index 488fb8307..233910f2a 100644 --- a/source/carla_shared.py +++ b/source/carla_shared.py @@ -25,17 +25,18 @@ import sys from codecs import open as codecopen from copy import deepcopy #from decimal import Decimal -from PyQt4.QtCore import qWarning +from PyQt4.QtCore import pyqtSlot, qWarning, SIGNAL, SLOT #pyqtSlot, qFatal, Qt, QSettings, QTimer -#from PyQt4.QtGui import QColor, QCursor, QDialog, QFontMetrics, QFrame, QGraphicsScene, QInputDialog, QLinearGradient, QMenu, QPainter, QPainterPath, QVBoxLayout, QWidget +from PyQt4.QtGui import QDialog, QWidget +#from PyQt4.QtGui import QColor, QCursor, QFontMetrics, QFrame, QGraphicsScene, QInputDialog, QLinearGradient, QMenu, QPainter, QPainterPath, QVBoxLayout #from PyQt4.QtXml import QDomDocument # ------------------------------------------------------------------------------------------------------------ # Imports (Custom) -#import ui_carla_about +import ui_carla_about #import ui_carla_edit -#import ui_carla_parameter +import ui_carla_parameter #import ui_carla_plugin # ------------------------------------------------------------------------------------------------------------ @@ -134,6 +135,16 @@ else: is64bit = False #bool(platform.architecture()[0] == "64bit" and sys.maxsize > 2**32) +# ------------------------------------------------------------------------------------------------------------ +# Convert a ctypes c_char_p into a python string + +def cString(value): + if not value: + return "" + if isinstance(value, str): + return value + return value.decode("utf-8", errors="ignore") + # ------------------------------------------------------------------------------------------------------------ # Check if a value is a number (float support) @@ -371,6 +382,102 @@ CarlaSaveState = { 'chunk': None } +# ------------------------------------------------------------------------------------------------------------ +# Static MIDI CC list + +MIDI_CC_LIST = ( + #"0x00 Bank Select", + "0x01 Modulation", + "0x02 Breath", + "0x03 (Undefined)", + "0x04 Foot", + "0x05 Portamento", + #"0x06 (Data Entry MSB)", + "0x07 Volume", + "0x08 Balance", + "0x09 (Undefined)", + "0x0A Pan", + "0x0B Expression", + "0x0C FX Control 1", + "0x0D FX Control 2", + "0x0E (Undefined)", + "0x0F (Undefined)", + "0x10 General Purpose 1", + "0x11 General Purpose 2", + "0x12 General Purpose 3", + "0x13 General Purpose 4", + "0x14 (Undefined)", + "0x15 (Undefined)", + "0x16 (Undefined)", + "0x17 (Undefined)", + "0x18 (Undefined)", + "0x19 (Undefined)", + "0x1A (Undefined)", + "0x1B (Undefined)", + "0x1C (Undefined)", + "0x1D (Undefined)", + "0x1E (Undefined)", + "0x1F (Undefined)", + #"0x20 *Bank Select", + #"0x21 *Modulation", + #"0x22 *Breath", + #"0x23 *(Undefined)", + #"0x24 *Foot", + #"0x25 *Portamento", + #"0x26 *(Data Entry MSB)", + #"0x27 *Volume", + #"0x28 *Balance", + #"0x29 *(Undefined)", + #"0x2A *Pan", + #"0x2B *Expression", + #"0x2C *FX *Control 1", + #"0x2D *FX *Control 2", + #"0x2E *(Undefined)", + #"0x2F *(Undefined)", + #"0x30 *General Purpose 1", + #"0x31 *General Purpose 2", + #"0x32 *General Purpose 3", + #"0x33 *General Purpose 4", + #"0x34 *(Undefined)", + #"0x35 *(Undefined)", + #"0x36 *(Undefined)", + #"0x37 *(Undefined)", + #"0x38 *(Undefined)", + #"0x39 *(Undefined)", + #"0x3A *(Undefined)", + #"0x3B *(Undefined)", + #"0x3C *(Undefined)", + #"0x3D *(Undefined)", + #"0x3E *(Undefined)", + #"0x3F *(Undefined)", + #"0x40 Damper On/Off", # <63 off, >64 on + #"0x41 Portamento On/Off", # <63 off, >64 on + #"0x42 Sostenuto On/Off", # <63 off, >64 on + #"0x43 Soft Pedal On/Off", # <63 off, >64 on + #"0x44 Legato Footswitch", # <63 Normal, >64 Legato + #"0x45 Hold 2", # <63 off, >64 on + "0x46 Control 1 [Variation]", + "0x47 Control 2 [Timbre]", + "0x48 Control 3 [Release]", + "0x49 Control 4 [Attack]", + "0x4A Control 5 [Brightness]", + "0x4B Control 6 [Decay]", + "0x4C Control 7 [Vib Rate]", + "0x4D Control 8 [Vib Depth]", + "0x4E Control 9 [Vib Delay]", + "0x4F Control 10 [Undefined]", + "0x50 General Purpose 5", + "0x51 General Purpose 6", + "0x52 General Purpose 7", + "0x53 General Purpose 8", + "0x54 Portamento Control", + "0x5B FX 1 Depth [Reverb]", + "0x5C FX 2 Depth [Tremolo]", + "0x5D FX 3 Depth [Chorus]", + "0x5E FX 4 Depth [Detune]", + "0x5F FX 5 Depth [Phaser]" + ) + # ------------------------------------------------------------------------------------------------------------ # Carla XML helpers @@ -518,3 +625,213 @@ def xmlSafeString(string, toXml): return string.replace("&", "&").replace("<","<").replace(">",">").replace("'","'").replace("\"",""") else: return string.replace("&", "&").replace("<","<").replace(">",">").replace("'","'").replace(""","\"") + +# ------------------------------------------------------------------------------------------------------------ +# Carla About dialog + +class CarlaAboutW(QDialog, ui_carla_about.Ui_CarlaAboutW): + def __init__(self, parent): + QDialog.__init__(self, parent) + self.setupUi(self) + + oscTxt = self.tr(" - OSC Bridge Version") if Carla.isControl else "" + + self.l_about.setText(self.tr("" + "
Version %s" + "
Carla is a Multi-Plugin Host for JACK%s.
" + "
Copyright (C) 2011-2012 falkTX
" + "" % (VERSION, oscTxt))) + + if Carla.isControl: + self.l_extended.setVisible(False) # TODO - write about this special OSC version + self.tabWidget.removeTab(1) + self.tabWidget.removeTab(1) + + else: + self.l_extended.setText(cString(Carla.host.get_extended_license_text())) + self.le_osc_url.setText(cString(Carla.host.get_host_osc_url()) if Carla.host.is_engine_running() else self.tr("(Engine not running)")) + + self.l_osc_cmds.setText( + " /set_active \n" + " /set_drywet \n" + " /set_volume \n" + " /set_balance_left \n" + " /set_balance_right \n" + " /set_panning \n" + " /set_parameter_value \n" + " /set_parameter_midi_cc \n" + " /set_parameter_midi_channel \n" + " /set_program \n" + " /set_midi_program \n" + " /note_on \n" + " /note_off \n" + ) + + self.l_example.setText("/Carla/2/set_parameter_value 5 1.0") + self.l_example_help.setText("(as in this example, \"2\" is the plugin number and \"5\" the parameter)") + + self.l_ladspa.setText(self.tr("Everything! (Including LRDF)")) + self.l_dssi.setText(self.tr("Everything! (Including CustomData/Chunks)")) + self.l_lv2.setText(self.tr("About 95% complete (using custom extensions).
" + "Implemented Feature/Extensions:" + "
    " + "
  • http://lv2plug.in/ns/ext/atom
  • " + "
  • http://lv2plug.in/ns/ext/buf-size
  • " + "
  • http://lv2plug.in/ns/ext/data-access
  • " + #"
  • http://lv2plug.in/ns/ext/dynmanifest
  • " + "
  • http://lv2plug.in/ns/ext/event
  • " + "
  • http://lv2plug.in/ns/ext/instance-access
  • " + "
  • http://lv2plug.in/ns/ext/log
  • " + "
  • http://lv2plug.in/ns/ext/midi
  • " + "
  • http://lv2plug.in/ns/ext/options
  • " + #"
  • http://lv2plug.in/ns/ext/parameters
  • " + "
  • http://lv2plug.in/ns/ext/patch
  • " + #"
  • http://lv2plug.in/ns/ext/port-groups
  • " + "
  • http://lv2plug.in/ns/ext/port-props
  • " + #"
  • http://lv2plug.in/ns/ext/presets
  • " + "
  • http://lv2plug.in/ns/ext/state
  • " + "
  • http://lv2plug.in/ns/ext/time
  • " + "
  • http://lv2plug.in/ns/ext/uri-map
  • " + "
  • http://lv2plug.in/ns/ext/urid
  • " + "
  • http://lv2plug.in/ns/ext/worker
  • " + "
  • http://lv2plug.in/ns/extensions/ui
  • " + "
  • http://lv2plug.in/ns/extensions/units
  • " + "
  • http://kxstudio.sf.net/ns/lv2ext/external-ui
  • " + "
  • http://kxstudio.sf.net/ns/lv2ext/programs
  • " + "
  • http://kxstudio.sf.net/ns/lv2ext/rtmempool
  • " + "
  • http://ll-plugins.nongnu.org/lv2/ext/midimap
  • " + "
  • http://ll-plugins.nongnu.org/lv2/ext/miditype
  • " + "
")) + self.l_vst.setText(self.tr("

About 85% complete (missing vst bank/presets and some minor stuff)

")) + + def done(self, r): + QDialog.done(self, r) + self.close() + +# ------------------------------------------------------------------------------------------------------------ +# Plugin Parameter + +class PluginParameter(QWidget, ui_carla_parameter.Ui_PluginParameter): + def __init__(self, parent, pInfo, pluginId, tabIndex): + QWidget.__init__(self, parent) + self.setupUi(self) + + pType = pInfo['type'] + pHints = pInfo['hints'] + + self.m_midiCC = -1 + self.m_midiChannel = 1 + self.m_pluginId = pluginId + self.m_parameterId = pInfo['index'] + self.m_tabIndex = tabIndex + + self.label.setText(pInfo['name']) + + for MIDI_CC in MIDI_CC_LIST: + self.combo.addItem(MIDI_CC) + + if pType == PARAMETER_INPUT: + self.widget.set_minimum(pInfo['minimum']) + self.widget.set_maximum(pInfo['maximum']) + self.widget.set_default(pInfo['default']) + self.widget.set_value(pInfo['current'], False) + self.widget.set_label(pInfo['unit']) + self.widget.set_step(pInfo['step']) + self.widget.set_step_small(pInfo['stepSmall']) + self.widget.set_step_large(pInfo['stepLarge']) + self.widget.set_scalepoints(pInfo['scalepoints'], bool(pHints & PARAMETER_USES_SCALEPOINTS)) + + if not pHints & PARAMETER_IS_ENABLED: + self.widget.set_read_only(True) + self.combo.setEnabled(False) + self.sb_channel.setEnabled(False) + + elif not pHints & PARAMETER_IS_AUTOMABLE: + self.combo.setEnabled(False) + self.sb_channel.setEnabled(False) + + elif pType == PARAMETER_OUTPUT: + self.widget.set_minimum(pInfo['minimum']) + self.widget.set_maximum(pInfo['maximum']) + self.widget.set_value(pInfo['current'], False) + self.widget.set_label(pInfo['unit']) + self.widget.set_read_only(True) + + if not pHints & PARAMETER_IS_AUTOMABLE: + self.combo.setEnabled(False) + self.sb_channel.setEnabled(False) + + else: + self.widget.setVisible(False) + self.combo.setVisible(False) + self.sb_channel.setVisible(False) + + self.set_parameter_midi_cc(pInfo['midiCC']) + self.set_parameter_midi_channel(pInfo['midiChannel']) + + self.connect(self.widget, SIGNAL("valueChanged(double)"), SLOT("slot_valueChanged(double)")) + self.connect(self.sb_channel, SIGNAL("valueChanged(int)"), SLOT("slot_midiChannelChanged(int)")) + self.connect(self.combo, SIGNAL("currentIndexChanged(int)"), SLOT("slot_midiCcChanged(int)")) + + #if force_parameters_style: + #self.widget.force_plastique_style() + + if pHints & PARAMETER_USES_CUSTOM_TEXT: + self.widget.set_text_call(self.textCallBack) + + self.widget.updateAll() + + def setDefaultValue(self, value): + self.widget.set_default(value) + + def set_parameter_value(self, value, send=True): + self.widget.set_value(value, send) + + def set_parameter_midi_cc(self, cc): + self.m_midiCC = cc + self.set_MIDI_CC_in_ComboBox(cc) + + def set_parameter_midi_channel(self, channel): + self.m_midiChannel = channel+1 + self.sb_channel.setValue(channel+1) + + def set_MIDI_CC_in_ComboBox(self, cc): + for i in range(len(MIDI_CC_LIST)): + ccText = MIDI_CC_LIST[i].split(" ")[0] + if int(ccText, 16) == cc: + ccIndex = i + break + else: + ccIndex = -1 + + self.combo.setCurrentIndex(ccIndex+1) + + def tabIndex(self): + return self.m_tabIndex + + def textCallBack(self): + return cString(Carla.host.get_parameter_text(self.m_pluginId, self.m_parameterId)) + + @pyqtSlot(float) + def slot_valueChanged(self, value): + self.emit(SIGNAL("valueChanged(int, double)"), self.m_parameterId, value) + + @pyqtSlot(int) + def slot_midiCcChanged(self, ccIndex): + if ccIndex <= 0: + cc = -1 + else: + ccText = MIDI_CC_LIST[ccIndex - 1].split(" ")[0] + cc = int(ccText, 16) + + if self.m_midiCC != cc: + self.emit(SIGNAL("midiCcChanged(int, int)"), self.m_parameterId, cc) + + self.m_midiCC = cc + + @pyqtSlot(int) + def slot_midiChannelChanged(self, channel): + if self.m_midiChannel != channel: + self.emit(SIGNAL("midiChannelChanged(int, int)"), self.m_parameterId, channel) + + self.m_midiChannel = channel diff --git a/source/discovery/carla-discovery.cpp b/source/discovery/carla-discovery.cpp index 722001f90..89033c8cb 100644 --- a/source/discovery/carla-discovery.cpp +++ b/source/discovery/carla-discovery.cpp @@ -721,6 +721,12 @@ void do_lv2_check(const char* const bundle, const bool init) URIs.append(QString(uri)); } + if (URIs.count() == 0) + { + DISCOVERY_OUT("warning", "LV2 Bundle doesn't provide any plugins"); + return; + } + // Get & check every plugin-instance for (int i=0; i < URIs.count(); i++) { diff --git a/source/includes/lv2_rdf.hpp b/source/includes/lv2_rdf.hpp index 173071e4c..8fc5d8f66 100644 --- a/source/includes/lv2_rdf.hpp +++ b/source/includes/lv2_rdf.hpp @@ -119,19 +119,22 @@ struct LV2_Type { #define LV2_IS_PORT_UNIT_SEMITONE(x) ((x) == LV2_PORT_UNIT_SEMITONE) // Port Types -#define LV2_PORT_INPUT 0x01 -#define LV2_PORT_OUTPUT 0x02 -#define LV2_PORT_CONTROL 0x04 -#define LV2_PORT_AUDIO 0x08 -#define LV2_PORT_CV 0x10 -#define LV2_PORT_ATOM 0x20 -#define LV2_PORT_ATOM_SEQUENCE (0x40 | LV2_PORT_ATOM) -#define LV2_PORT_EVENT 0x80 -#define LV2_PORT_MIDI_LL 0x100 +#define LV2_PORT_INPUT 0x001 +#define LV2_PORT_OUTPUT 0x002 +#define LV2_PORT_CONTROL 0x004 +#define LV2_PORT_AUDIO 0x008 +#define LV2_PORT_CV 0x010 +#define LV2_PORT_ATOM 0x020 +#define LV2_PORT_ATOM_SEQUENCE (0x040 | LV2_PORT_ATOM) +#define LV2_PORT_ATOM_URID (0x080 | LV2_PORT_ATOM) +#define LV2_PORT_EVENT 0x100 +#define LV2_PORT_MIDI_LL 0x200 // Port Data Types #define LV2_PORT_DATA_MIDI_EVENT 0x1000 -#define LV2_PORT_DATA_PATCH_MESSAGE 0x2000 +#define LV2_PORT_DATA_OBJECT 0x2000 +#define LV2_PORT_DATA_PATCH_MESSAGE 0x4000 +#define LV2_PORT_DATA_TIME 0x8000 #define LV2_IS_PORT_INPUT(x) ((x) & LV2_PORT_INPUT) #define LV2_IS_PORT_OUTPUT(x) ((x) & LV2_PORT_OUTPUT) diff --git a/source/utils/carla_utils.hpp b/source/utils/carla_utils.hpp index 855bf45c2..948d3ec11 100644 --- a/source/utils/carla_utils.hpp +++ b/source/utils/carla_utils.hpp @@ -178,7 +178,7 @@ class CarlaMutex { public: CarlaMutex() - : pmutex(PTHREAD_MUTEX_INITIALIZER) + //: pmutex PTHREAD_MUTEX_INITIALIZER { pthread_mutex_init(&pmutex, nullptr); }