Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

carla_control.py 38KB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla OSC controller
  4. # Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as
  8. # published by the Free Software Foundation; either version 2 of
  9. # the License, or any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the GPL.txt file
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. from PyQt4.QtCore import QLibrary
  20. from PyQt4.QtGui import QApplication, QInputDialog, QMainWindow
  21. from liblo import make_method, Address, ServerError, ServerThread
  22. from liblo import send as lo_send
  23. from liblo import TCP as LO_TCP
  24. from liblo import UDP as LO_UDP
  25. # ------------------------------------------------------------------------------------------------------------
  26. # Imports (Custom)
  27. import ui_carla_control
  28. from carla_shared import *
  29. global lo_target, lo_targetName
  30. lo_target = None
  31. lo_targetName = ""
  32. Carla.isControl = True
  33. Carla.isLocal = False
  34. # ------------------------------------------------------------------------------------------------------------
  35. # Helper class
  36. class ControlPluginInfo(object):
  37. __slots__ = [
  38. 'pluginInfo',
  39. 'pluginRealName',
  40. 'audioCountInfo',
  41. 'midiCountInfo',
  42. 'parameterCountInfo',
  43. 'parameterInfoS',
  44. 'parameterDataS',
  45. 'parameterRangeS',
  46. 'parameterValueS',
  47. 'programCount',
  48. 'programCurrent',
  49. 'programNameS',
  50. 'midiProgramCount',
  51. 'midiProgramCurrent',
  52. 'midiProgramDataS',
  53. 'peaks'
  54. ]
  55. # ------------------------------------------------------------------------------------------------------------
  56. # Python Object class compatible to 'Host' on the Carla Backend code
  57. class Host(object):
  58. def __init__(self):
  59. object.__init__(self)
  60. self.fPluginsInfo = []
  61. def _add(self, pluginId):
  62. if len(self.fPluginsInfo) != pluginId:
  63. return
  64. info = ControlPluginInfo()
  65. info.pluginInfo = CarlaPluginInfo
  66. info.pluginRealName = None
  67. info.audioCountInfo = CarlaPortCountInfo
  68. info.midiCountInfo = CarlaPortCountInfo
  69. info.parameterCountInfo = CarlaPortCountInfo
  70. info.parameterInfoS = []
  71. info.parameterDataS = []
  72. info.parameterRangeS = []
  73. info.parameterValueS = []
  74. info.programCount = 0
  75. info.programCurrent = -1
  76. info.programNameS = []
  77. info.midiProgramCount = 0
  78. info.midiProgramCurrent = -1
  79. info.midiProgramDataS = []
  80. info.peaks = [0.0, 0.0, 0.0, 0.0]
  81. self.fPluginsInfo.append(info)
  82. def _set_pluginInfo(self, index, info):
  83. self.fPluginsInfo[index].pluginInfo = info
  84. def _set_pluginRealName(self, index, realName):
  85. self.fPluginsInfo[index].pluginRealName = realName
  86. def _set_audioCountInfo(self, index, info):
  87. self.fPluginsInfo[index].audioCountInfo = info
  88. def _set_midiCountInfo(self, index, info):
  89. self.fPluginsInfo[index].midiCountInfo = info
  90. def _set_parameterCountInfo(self, index, info):
  91. self.fPluginsInfo[index].parameterCountInfo = info
  92. # clear
  93. self.fPluginsInfo[index].parameterInfoS = []
  94. self.fPluginsInfo[index].parameterDataS = []
  95. self.fPluginsInfo[index].parameterRangeS = []
  96. self.fPluginsInfo[index].parameterValueS = []
  97. # add placeholders
  98. for x in range(info['total']):
  99. self.fPluginsInfo[index].parameterInfoS.append(CarlaParameterInfo)
  100. self.fPluginsInfo[index].parameterDataS.append(ParameterData)
  101. self.fPluginsInfo[index].parameterRangeS.append(ParameterRanges)
  102. self.fPluginsInfo[index].parameterValueS.append(0.0)
  103. def _set_programCount(self, index, count):
  104. self.fPluginsInfo[index].programCount = count
  105. # clear
  106. self.fPluginsInfo[index].programNameS = []
  107. # add placeholders
  108. for x in range(count):
  109. self.fPluginsInfo[index].programNameS.append(None)
  110. def _set_midiProgramCount(self, index, count):
  111. self.fPluginsInfo[index].midiProgramCount = count
  112. # clear
  113. self.fPluginsInfo[index].midiProgramDataS = []
  114. # add placeholders
  115. for x in range(count):
  116. self.fPluginsInfo[index].midiProgramDataS.append(MidiProgramData)
  117. def _set_parameterInfoS(self, index, paramIndex, data):
  118. if paramIndex < self.fPluginsInfo[index].parameterCountInfo['total']:
  119. self.fPluginsInfo[index].parameterInfoS[paramIndex] = data
  120. def _set_parameterDataS(self, index, paramIndex, data):
  121. if paramIndex < self.fPluginsInfo[index].parameterCountInfo['total']:
  122. self.fPluginsInfo[index].parameterDataS[paramIndex] = data
  123. def _set_parameterRangeS(self, index, paramIndex, data):
  124. if paramIndex < self.fPluginsInfo[index].parameterCountInfo['total']:
  125. self.fPluginsInfo[index].parameterRangeS[paramIndex] = data
  126. def _set_parameterValueS(self, index, paramIndex, value):
  127. if paramIndex < self.fPluginsInfo[index].parameterCountInfo['total']:
  128. self.fPluginsInfo[index].parameterValueS[paramIndex] = value
  129. def _set_parameterDefaultValue(self, index, paramIndex, value):
  130. if paramIndex < self.fPluginsInfo[index].parameterCountInfo['total']:
  131. self.fPluginsInfo[index].parameterRangeS[paramIndex]['def'] = value
  132. def _set_parameterMidiCC(self, index, paramIndex, cc):
  133. if paramIndex < self.fPluginsInfo[index].parameterCountInfo['total']:
  134. self.fPluginsInfo[index].parameterDataS[paramIndex]['midiCC'] = cc
  135. def _set_parameterMidiChannel(self, index, paramIndex, channel):
  136. if paramIndex < self.fPluginsInfo[index].parameterCountInfo['total']:
  137. self.fPluginsInfo[index].parameterDataS[paramIndex]['midiChannel'] = channel
  138. def _set_currentProgram(self, index, pIndex):
  139. self.fPluginsInfo[index].programCurrent = pIndex
  140. def _set_currentMidiProgram(self, index, mpIndex):
  141. self.fPluginsInfo[index].midiProgramCurrent = mpIndex
  142. def _set_programNameS(self, index, pIndex, data):
  143. if pIndex < self.fPluginsInfo[index].programCount:
  144. self.fPluginsInfo[index].programNameS[pIndex] = data
  145. def _set_midiProgramDataS(self, index, mpIndex, data):
  146. if mpIndex < self.fPluginsInfo[index].midiProgramCount:
  147. self.fPluginsInfo[index].midiProgramDataS[mpIndex] = data
  148. def _set_peaks(self, index, in1, in2, out1, out2):
  149. self.fPluginsInfo[index].peaks = [in1, in2, out1, out2]
  150. # get_extended_license_text
  151. # get_supported_file_types
  152. # get_engine_driver_count
  153. # get_engine_driver_name
  154. # get_engine_driver_device_names
  155. # get_internal_plugin_count
  156. # get_internal_plugin_info
  157. # engine_init
  158. # engine_close
  159. # engine_idle
  160. # is_engine_running
  161. # set_engine_about_to_close
  162. # set_engine_callback
  163. # set_engine_option
  164. # load_filename
  165. # load_project
  166. # save_project
  167. # patchbay_connect
  168. # patchbay_disconnect
  169. # patchbay_refresh
  170. # transport_play
  171. # transport_pause
  172. # transport_relocate
  173. # get_current_transport_frame
  174. # get_transport_info
  175. # add_plugin
  176. # remove_plugin
  177. # remove_all_plugins
  178. # rename_plugin
  179. # clone_plugin
  180. # replace_plugin
  181. # switch_plugins
  182. # load_plugin_state
  183. # save_plugin_state
  184. def get_plugin_info(self, pluginId):
  185. return self.fPluginsInfo[pluginId].pluginInfo
  186. def get_audio_port_count_info(self, pluginId):
  187. return self.fPluginsInfo[pluginId].audioCountInfo
  188. def get_midi_port_count_info(self, pluginId):
  189. return self.fPluginsInfo[pluginId].midiCountInfo
  190. def get_parameter_count_info(self, pluginId):
  191. return self.fPluginsInfo[pluginId].parameterCountInfo
  192. def get_parameter_info(self, pluginId, parameterId):
  193. return self.fPluginsInfo[pluginId].parameterInfoS[parameterId]
  194. def get_parameter_scalepoint_info(self, pluginId, parameterId, scalepoint_id):
  195. return CarlaScalePointInfo
  196. def get_parameter_data(self, pluginId, parameterId):
  197. return self.fPluginsInfo[pluginId].parameterDataS[parameterId]
  198. def get_parameter_ranges(self, pluginId, parameterId):
  199. return self.fPluginsInfo[pluginId].parameterRangeS[parameterId]
  200. def get_midi_program_data(self, pluginId, midiProgramId):
  201. return self.fPluginsInfo[pluginId].midiProgramDataS[midiProgramId]
  202. def get_custom_data(self, pluginId, custom_data_id):
  203. return CustomData
  204. def get_chunk_data(self, pluginId):
  205. return None
  206. def get_parameter_count(self, pluginId):
  207. return self.fPluginsInfo[pluginId].parameterCountInfo['total']
  208. def get_program_count(self, pluginId):
  209. return self.fPluginsInfo[pluginId].programCount
  210. def get_midi_program_count(self, pluginId):
  211. return self.fPluginsInfo[pluginId].midiProgramCount
  212. def get_custom_data_count(self, pluginId):
  213. return 0
  214. def get_parameter_text(self, pluginId, parameterId):
  215. return None
  216. def get_program_name(self, pluginId, programId):
  217. return self.fPluginsInfo[pluginId].programNameS[programId]
  218. def get_midi_program_name(self, pluginId, midiProgramId):
  219. return self.fPluginsInfo[pluginId].midiProgramDataS[midiProgramId]['label']
  220. def get_real_plugin_name(self, pluginId):
  221. return self.fPluginsInfo[pluginId].pluginRealName
  222. def get_current_program_index(self, pluginId):
  223. return self.fPluginsInfo[pluginId].programCurrent
  224. def get_current_midi_program_index(self, pluginId):
  225. return self.fPluginsInfo[pluginId].midiProgramCurrent
  226. def get_default_parameter_value(self, pluginId, parameterId):
  227. return self.fPluginsInfo[pluginId].parameterRangeS[parameterId]['def']
  228. def get_current_parameter_value(self, pluginId, parameterId):
  229. return self.fPluginsInfo[pluginId].parameterValueS[parameterId]
  230. def get_input_peak_value(self, pluginId, portId):
  231. return self.fPluginsInfo[pluginId].peaks[portId-1]
  232. def get_output_peak_value(self, pluginId, portId):
  233. return self.fPluginsInfo[pluginId].peaks[portId+1]
  234. def set_option(self, pluginId, option, yesNo):
  235. global to_target, lo_targetName
  236. lo_path = "/%s/%i/set_option" % (lo_targetName, pluginId)
  237. lo_send(lo_target, lo_path, option, yesNo)
  238. def set_active(self, pluginId, onoff):
  239. global to_target, lo_targetName
  240. lo_path = "/%s/%i/set_active" % (lo_targetName, pluginId)
  241. lo_send(lo_target, lo_path, 1 if onoff else 0)
  242. def set_drywet(self, pluginId, value):
  243. global to_target, lo_targetName
  244. lo_path = "/%s/%i/set_drywet" % (lo_targetName, pluginId)
  245. lo_send(lo_target, lo_path, value)
  246. def set_volume(self, pluginId, value):
  247. global to_target, lo_targetName
  248. lo_path = "/%s/%i/set_volume" % (lo_targetName, pluginId)
  249. lo_send(lo_target, lo_path, value)
  250. def set_balance_left(self, pluginId, value):
  251. global to_target, lo_targetName
  252. lo_path = "/%s/%i/set_balance_left" % (lo_targetName, pluginId)
  253. lo_send(lo_target, lo_path, value)
  254. def set_balance_right(self, pluginId, value):
  255. global to_target, lo_targetName
  256. lo_path = "/%s/%i/set_balance_right" % (lo_targetName, pluginId)
  257. lo_send(lo_target, lo_path, value)
  258. def set_panning(self, pluginId, value):
  259. global to_target, lo_targetName
  260. lo_path = "/%s/%i/set_panning" % (lo_targetName, pluginId)
  261. lo_send(lo_target, lo_path, value)
  262. def set_ctrl_channel(self, pluginId, channel):
  263. global to_target, lo_targetName
  264. lo_path = "/%s/%i/set_ctrl_channel" % (lo_targetName, pluginId)
  265. lo_send(lo_target, lo_path, channel)
  266. def set_parameter_value(self, pluginId, parameterId, value):
  267. global to_target, lo_targetName
  268. lo_path = "/%s/%i/set_parameter_value" % (lo_targetName, pluginId)
  269. lo_send(lo_target, lo_path, parameterId, value)
  270. def set_parameter_midi_cc(self, pluginId, parameterId, midi_cc):
  271. global to_target, lo_targetName
  272. lo_path = "/%s/%i/set_parameter_midi_cc" % (lo_targetName, pluginId)
  273. lo_send(lo_target, lo_path, parameterId, midi_cc)
  274. def set_parameter_midi_channel(self, pluginId, parameterId, channel):
  275. global to_target, lo_targetName
  276. lo_path = "/%s/%i/set_parameter_midi_channel" % (lo_targetName, pluginId)
  277. lo_send(lo_target, lo_path, parameterId, channel)
  278. def set_program(self, pluginId, programId):
  279. global to_target, lo_targetName
  280. lo_path = "/%s/%i/set_program" % (lo_targetName, pluginId)
  281. lo_send(lo_target, lo_path, programId)
  282. def set_midi_program(self, pluginId, midiProgramId):
  283. global to_target, lo_targetName
  284. lo_path = "/%s/%i/set_midi_program" % (lo_targetName, pluginId)
  285. lo_send(lo_target, lo_path, midiProgramId)
  286. # set_custom_data
  287. # set_chunk_data
  288. # prepare_for_save
  289. def send_midi_note(self, pluginId, channel, note, velocity):
  290. global to_target, lo_targetName
  291. if velocity:
  292. lo_path = "/%s/%i/note_on" % (lo_targetName, pluginId)
  293. lo_send(lo_target, lo_path, channel, note, velocity)
  294. else:
  295. lo_path = "/%s/%i/note_off" % (lo_targetName, pluginId)
  296. lo_send(lo_target, lo_path, channel, note)
  297. # show_gui
  298. # get_buffer_size
  299. # get_sample_rate
  300. # get_last_error
  301. # get_host_osc_url
  302. # nsm_announce
  303. # nsm_reply_open
  304. # nsm_reply_save
  305. # ------------------------------------------------------------------------------------------------------------
  306. # OSC Control server
  307. class ControlServer(ServerThread):
  308. def __init__(self, parent, mode):
  309. ServerThread.__init__(self, 8087, mode)
  310. self.fParent = parent
  311. def getFullURL(self):
  312. return "%scarla-control" % self.get_url()
  313. @make_method('/carla-control/add_plugin_start', 'is')
  314. def add_plugin_start_callback(self, path, args):
  315. pluginId, pluginName = args
  316. self.fParent.emit(SIGNAL("AddPluginStart(int, QString)"), pluginId, pluginName)
  317. @make_method('/carla-control/add_plugin_end', 'i')
  318. def add_plugin_end_callback(self, path, args):
  319. pluginId, = args
  320. self.fParent.emit(SIGNAL("AddPluginEnd(int)"), pluginId)
  321. @make_method('/carla-control/remove_plugin', 'i')
  322. def remove_plugin_callback(self, path, args):
  323. pluginId, = args
  324. self.fParent.emit(SIGNAL("RemovePlugin(int)"), pluginId)
  325. @make_method('/carla-control/set_plugin_data', 'iiiissssh')
  326. def set_plugin_data_callback(self, path, args):
  327. pluginId, ptype, category, hints, realName, label, maker, copyright_, uniqueId = args
  328. self.fParent.emit(SIGNAL("SetPluginData(int, int, int, int, QString, QString, QString, QString, int)"), pluginId, ptype, category, hints, realName, label, maker, copyright_, uniqueId)
  329. @make_method('/carla-control/set_plugin_ports', 'iiiiiiii')
  330. def set_plugin_ports_callback(self, path, args):
  331. pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals = args
  332. self.fParent.emit(SIGNAL("SetPluginPorts(int, int, int, int, int, int, int, int)"), pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals)
  333. @make_method('/carla-control/set_parameter_data', 'iiiissf')
  334. def set_parameter_data_callback(self, path, args):
  335. pluginId, index, type_, hints, name, label, current = args
  336. self.fParent.emit(SIGNAL("SetParameterData(int, int, int, int, QString, QString, double)"), pluginId, index, type_, hints, name, label, current)
  337. @make_method('/carla-control/set_parameter_ranges', 'iiffffff')
  338. def set_parameter_ranges_callback(self, path, args):
  339. pluginId, index, min_, max_, def_, step, stepSmall, stepLarge = args
  340. self.fParent.emit(SIGNAL("SetParameterRanges(int, int, double, double, double, double, double, double)"), pluginId, index, min_, max_, def_, step, stepSmall, stepLarge)
  341. @make_method('/carla-control/set_parameter_midi_cc', 'iii')
  342. def set_parameter_midi_cc_callback(self, path, args):
  343. pluginId, index, cc = args
  344. self.fParent.emit(SIGNAL("SetParameterMidiCC(int, int, int)"), pluginId, index, cc)
  345. @make_method('/carla-control/set_parameter_midi_channel', 'iii')
  346. def set_parameter_midi_channel_callback(self, path, args):
  347. pluginId, index, channel = args
  348. self.fParent.emit(SIGNAL("SetParameterMidiChannel(int, int, int)"), pluginId, index, channel)
  349. @make_method('/carla-control/set_parameter_value', 'iif')
  350. def set_parameter_value_callback(self, path, args):
  351. pluginId, index, value = args
  352. self.fParent.emit(SIGNAL("SetParameterValue(int, int, double)"), pluginId, index, value)
  353. @make_method('/carla-control/set_default_value', 'iif')
  354. def set_default_value_callback(self, path, args):
  355. pluginId, index, value = args
  356. self.fParent.emit(SIGNAL("SetDefaultValue(int, int, double)"), pluginId, index, value)
  357. @make_method('/carla-control/set_program', 'ii')
  358. def set_program_callback(self, path, args):
  359. pluginId, index = args
  360. self.fParent.emit(SIGNAL("SetProgram(int, int)"), pluginId, index)
  361. @make_method('/carla-control/set_program_count', 'ii')
  362. def set_program_count_callback(self, path, args):
  363. pluginId, count = args
  364. self.fParent.emit(SIGNAL("SetProgramCount(int, int)"), pluginId, count)
  365. @make_method('/carla-control/set_program_name', 'iis')
  366. def set_program_name_callback(self, path, args):
  367. pluginId, index, name = args
  368. self.fParent.emit(SIGNAL("SetProgramName(int, int, QString)"), pluginId, index, name)
  369. @make_method('/carla-control/set_midi_program', 'ii')
  370. def set_midi_program_callback(self, path, args):
  371. pluginId, index = args
  372. self.fParent.emit(SIGNAL("SetMidiProgram(int, int)"), pluginId, index)
  373. @make_method('/carla-control/set_midi_program_count', 'ii')
  374. def set_midi_program_count_callback(self, path, args):
  375. pluginId, count = args
  376. self.fParent.emit(SIGNAL("SetMidiProgramCount(int, int)"), pluginId, count)
  377. @make_method('/carla-control/set_midi_program_data', 'iiiis')
  378. def set_midi_program_data_callback(self, path, args):
  379. pluginId, index, bank, program, name = args
  380. self.fParent.emit(SIGNAL("SetMidiProgramData(int, int, int, int, QString)"), pluginId, index, bank, program, name)
  381. @make_method('/carla-control/note_on', 'iiii')
  382. def note_on_callback(self, path, args):
  383. pluginId, channel, note, velo = args
  384. self.fParent.emit(SIGNAL("NoteOn(int, int, int, int)"), pluginId, channel, note, velo)
  385. @make_method('/carla-control/note_off', 'iii')
  386. def note_off_callback(self, path, args):
  387. pluginId, channel, note = args
  388. self.fParent.emit(SIGNAL("NoteOff(int, int, int)"), pluginId, channel, note)
  389. @make_method('/carla-control/set_peaks', 'iffff')
  390. def set_output_peak_value_callback(self, path, args):
  391. pluginId, in1, in2, out1, out2 = args
  392. self.fParent.emit(SIGNAL("SetPeaks(int, double, double, double, double)"), pluginId, in1, in2, out1, out2)
  393. @make_method('/carla-control/exit', '')
  394. def exit_callback(self, path, args):
  395. self.fParent.emit(SIGNAL("Exit()"))
  396. @make_method(None, None)
  397. def fallback(self, path, args):
  398. print("ControlServer::fallback(\"%s\") - unknown message, args =" % path, args)
  399. # ------------------------------------------------------------------------------------------------------------
  400. # Main Window
  401. class CarlaControlW(QMainWindow):
  402. def __init__(self, oscAddr=None):
  403. QMainWindow.__init__(self, None)
  404. self.ui = ui_carla_control.Ui_CarlaControlW()
  405. self.ui.setupUi(self)
  406. if MACOS:
  407. self.setUnifiedTitleAndToolBarOnMac(True)
  408. # -------------------------------------------------------------
  409. # Internal stuff
  410. self.fProjectFilename = None
  411. self.fProjectLoading = False
  412. self.fPluginCount = 0
  413. self.fPluginList = []
  414. self.fIdleTimerFast = 0
  415. self.fIdleTimerSlow = 0
  416. self.fLastPluginName = ""
  417. self.lo_address = ""
  418. self.lo_server = None
  419. # -------------------------------------------------------------
  420. # Load Settings
  421. self.loadSettings()
  422. # -------------------------------------------------------------
  423. # Set-up GUI stuff
  424. self.ui.act_file_refresh.setEnabled(False)
  425. #self.ui.act_plugin_remove_all.setEnabled(False)
  426. self.resize(self.width(), 0)
  427. # -------------------------------------------------------------
  428. # Connect actions to functions
  429. #self.connect(self.ui.act_file_connect, SIGNAL("triggered()"), SLOT("slot_fileConnect()"))
  430. #self.connect(self.ui.act_file_refresh, SIGNAL("triggered()"), SLOT("slot_fileRefresh()"))
  431. #self.connect(self.ui.act_plugin_add, SIGNAL("triggered()"), SLOT("slot_pluginAdd()"))
  432. #self.connect(self.ui.act_plugin_add2, SIGNAL("triggered()"), SLOT("slot_pluginAdd()"))
  433. #self.connect(self.ui.act_plugin_refresh, SIGNAL("triggered()"), SLOT("slot_pluginRefresh()"))
  434. #self.connect(self.ui.act_plugin_remove_all, SIGNAL("triggered()"), SLOT("slot_pluginRemoveAll()"))
  435. #self.connect(self.ui.act_settings_show_toolbar, SIGNAL("triggered(bool)"), SLOT("slot_toolbarShown()"))
  436. #self.connect(self.ui.act_settings_configure, SIGNAL("triggered()"), SLOT("slot_configureCarla()"))
  437. #self.connect(self.ui.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarlaControl()"))
  438. #self.connect(self.ui.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()"))
  439. #self.connect(self, SIGNAL("SIGTERM()"), SLOT("slot_handleSIGTERM()"))
  440. #self.connect(self, SIGNAL("AddPluginStart(int, QString)"), SLOT("slot_handleAddPluginStart(int, QString)"))
  441. #self.connect(self, SIGNAL("AddPluginEnd(int)"), SLOT("slot_handleAddPluginEnd(int)"))
  442. #self.connect(self, SIGNAL("RemovePlugin(int)"), SLOT("slot_handleRemovePlugin(int)"))
  443. #self.connect(self, SIGNAL("SetPluginData(int, int, int, int, QString, QString, QString, QString, int)"), SLOT("slot_handleSetPluginData(int, int, int, int, QString, QString, QString, QString, int)"))
  444. #self.connect(self, SIGNAL("SetPluginPorts(int, int, int, int, int, int, int, int)"), SLOT("slot_handleSetPluginPorts(int, int, int, int, int, int, int, int)"))
  445. #self.connect(self, SIGNAL("SetParameterData(int, int, int, int, QString, QString, double)"), SLOT("slot_handleSetParameterData(int, int, int, int, QString, QString, double)"))
  446. #self.connect(self, SIGNAL("SetParameterRanges(int, int, double, double, double, double, double, double)"), SLOT("slot_handleSetParameterRanges(int, int, double, double, double, double, double, double)"))
  447. #self.connect(self, SIGNAL("SetParameterMidiCC(int, int, int)"), SLOT("slot_handleSetParameterMidiCC(int, int, int)"))
  448. #self.connect(self, SIGNAL("SetParameterMidiChannel(int, int, int)"), SLOT("slot_handleSetParameterMidiChannel(int, int, int)"))
  449. #self.connect(self, SIGNAL("SetParameterValue(int, int, double)"), SLOT("slot_handleSetParameterValue(int, int, double)"))
  450. #self.connect(self, SIGNAL("SetDefaultValue(int, int, double)"), SLOT("slot_handleSetDefaultValue(int, int, double)"))
  451. #self.connect(self, SIGNAL("SetProgram(int, int)"), SLOT("slot_handleSetProgram(int, int)"))
  452. #self.connect(self, SIGNAL("SetProgramCount(int, int)"), SLOT("slot_handleSetProgramCount(int, int)"))
  453. #self.connect(self, SIGNAL("SetProgramName(int, int, QString)"), SLOT("slot_handleSetProgramName(int, int, QString)"))
  454. #self.connect(self, SIGNAL("SetMidiProgram(int, int)"), SLOT("slot_handleSetMidiProgram(int, int)"))
  455. #self.connect(self, SIGNAL("SetMidiProgramCount(int, int)"), SLOT("slot_handleSetMidiProgramCount(int, int)"))
  456. #self.connect(self, SIGNAL("SetMidiProgramData(int, int, int, int, QString)"), SLOT("slot_handleSetMidiProgramData(int, int, int, int, QString)"))
  457. #self.connect(self, SIGNAL("NoteOn(int, int, int, int)"), SLOT("slot_handleNoteOn(int, int, int, int)"))
  458. #self.connect(self, SIGNAL("NoteOff(int, int, int)"), SLOT("slot_handleNoteOff(int, int, int)"))
  459. #self.connect(self, SIGNAL("SetPeaks(int, double, double, double, double)"), SLOT("slot_handleSetPeaks(int, double, double, double, double)"))
  460. #self.connect(self, SIGNAL("Exit()"), SLOT("slot_handleExit()"))
  461. if oscAddr:
  462. self.connectToAddr(oscAddr)
  463. def connectToAddr(self, addr):
  464. global lo_target, lo_targetName
  465. self.lo_address = addr
  466. lo_target = Address(self.lo_address)
  467. lo_targetName = self.lo_address.rsplit("/", 1)[-1]
  468. print("Connecting to \"%s\" as '%s'..." % (self.lo_address, lo_targetName))
  469. try:
  470. self.lo_server = ControlServer(self, LO_UDP if self.lo_address.startswith("osc.udp") else LO_TCP)
  471. except: # ServerError, err:
  472. print("Connecting error!")
  473. #print str(err)
  474. QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to connect, operation failed."))
  475. return
  476. if self.lo_server:
  477. self.lo_server.start()
  478. self.ui.act_file_refresh.setEnabled(True)
  479. lo_send(lo_target, "/register", self.lo_server.getFullURL())
  480. self.fIdleTimerFast = self.startTimer(60)
  481. self.fIdleTimerSlow = self.startTimer(60*2)
  482. def removeAll(self):
  483. self.killTimer(self.fIdleTimerFast)
  484. self.killTimer(self.fIdleTimerSlow)
  485. self.fIdleTimerFast = 0
  486. self.fIdleTimerSlow = 0
  487. for i in range(self.fPluginCount):
  488. pwidget = self.fPluginList[i]
  489. if pwidget is None:
  490. break
  491. pwidget.ui.edit_dialog.close()
  492. pwidget.close()
  493. pwidget.deleteLater()
  494. del pwidget
  495. self.fPluginCount = 0
  496. self.fPluginList = []
  497. gCarla.host.fPluginsInfo = []
  498. self.fIdleTimerFast = self.startTimer(60)
  499. self.fIdleTimerSlow = self.startTimer(60*2)
  500. @pyqtSlot()
  501. def slot_handleSIGTERM(self):
  502. print("Got SIGTERM -> Closing now")
  503. self.close()
  504. @pyqtSlot()
  505. def slot_fileConnect(self):
  506. global lo_target, lo_targetName
  507. if lo_target and self.lo_server:
  508. urlText = self.lo_address
  509. else:
  510. urlText = "osc.tcp://127.0.0.1:19000/Carla"
  511. askValue = QInputDialog.getText(self, self.tr("Carla Control - Connect"), self.tr("Address"), text=urlText)
  512. if not askValue[1]:
  513. return
  514. self.slot_handleExit()
  515. self.connectToAddr(askValue[0])
  516. @pyqtSlot()
  517. def slot_fileRefresh(self):
  518. global lo_target
  519. if lo_target and self.lo_server:
  520. self.removeAll()
  521. lo_send(lo_target, "/unregister")
  522. lo_send(lo_target, "/register", self.lo_server.getFullURL())
  523. @pyqtSlot()
  524. def slot_aboutCarlaControl(self):
  525. CarlaAboutW(self).exec_()
  526. @pyqtSlot(int, str)
  527. def slot_handleAddPluginStart(self, pluginId, pluginName):
  528. self.fLastPluginName = pluginName
  529. gCarla.host._add(pluginId)
  530. @pyqtSlot(int)
  531. def slot_handleAddPluginEnd(self, pluginId):
  532. pwidget = PluginWidget(self, pluginId)
  533. self.ui.w_plugins.layout().addWidget(pwidget)
  534. self.fPluginCount += 1
  535. self.fPluginList.append(pwidget)
  536. @pyqtSlot(int)
  537. def slot_handleRemovePlugin(self, pluginId):
  538. if pluginId >= self.fPluginCount:
  539. print("handleRemovePlugin(%i) - invalid plugin id" % pluginId)
  540. return
  541. pwidget = self.fPluginList[pluginId]
  542. if pwidget is None:
  543. print("handleRemovePlugin(%i) - invalid plugin" % pluginId)
  544. return
  545. self.fPluginList.pop(pluginId)
  546. self.fPluginCount -= 1
  547. self.ui.w_plugins.layout().removeWidget(pwidget)
  548. pwidget.ui.edit_dialog.close()
  549. pwidget.close()
  550. pwidget.deleteLater()
  551. del pwidget
  552. # push all plugins 1 slot back
  553. for i in range(pluginId, self.fPluginCount):
  554. self.fPluginList[i].setId(i)
  555. gCarla.host.fPluginsInfo.pop(pluginId)
  556. @pyqtSlot(int, int, int, int, str, str, str, str, int)
  557. def slot_handleSetPluginData(self, pluginId, type_, category, hints, realName, label, maker, copyright, uniqueId):
  558. info = deepcopy(CarlaPluginInfo)
  559. info['type'] = type_
  560. info['category'] = category
  561. info['hints'] = hints
  562. info['name'] = self.fLastPluginName
  563. info['label'] = label
  564. info['maker'] = maker
  565. info['copyright'] = copyright
  566. info['uniqueId'] = uniqueId
  567. gCarla.host._set_pluginInfo(pluginId, info)
  568. gCarla.host._set_pluginRealName(pluginId, realName)
  569. @pyqtSlot(int, int, int, int, int, int, int, int)
  570. def slot_handleSetPluginPorts(self, pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals):
  571. audioInfo = deepcopy(CarlaPortCountInfo)
  572. midiInfo = deepcopy(CarlaPortCountInfo)
  573. paramInfo = deepcopy(CarlaPortCountInfo)
  574. audioInfo['ins'] = audioIns
  575. audioInfo['outs'] = audioOuts
  576. audioInfo['total'] = audioIns + audioOuts
  577. midiInfo['ins'] = midiIns
  578. midiInfo['outs'] = midiOuts
  579. midiInfo['total'] = midiIns + midiOuts
  580. paramInfo['ins'] = cIns
  581. paramInfo['outs'] = cOuts
  582. paramInfo['total'] = cTotals
  583. gCarla.host._set_audioCountInfo(pluginId, audioInfo)
  584. gCarla.host._set_midiCountInfo(pluginId, midiInfo)
  585. gCarla.host._set_parameterCountInfo(pluginId, paramInfo)
  586. @pyqtSlot(int, int, int, int, str, str, float)
  587. def slot_handleSetParameterData(self, pluginId, index, type_, hints, name, label, current):
  588. # remove hints not possible in bridge mode
  589. hints &= ~(PARAMETER_USES_SCALEPOINTS | PARAMETER_USES_CUSTOM_TEXT)
  590. data = deepcopy(ParameterData)
  591. data['type'] = type_
  592. data['index'] = index
  593. data['rindex'] = index
  594. data['hints'] = hints
  595. info = deepcopy(CarlaParameterInfo)
  596. info['name'] = name
  597. info['label'] = label
  598. gCarla.host._set_parameterDataS(pluginId, index, data)
  599. gCarla.host._set_parameterInfoS(pluginId, index, info)
  600. gCarla.host._set_parameterValueS(pluginId, index, current)
  601. @pyqtSlot(int, int, float, float, float, float, float, float)
  602. def slot_handleSetParameterRanges(self, pluginId, index, min_, max_, def_, step, stepSmall, stepLarge):
  603. ranges = deepcopy(ParameterRanges)
  604. ranges['min'] = min_
  605. ranges['max'] = max_
  606. ranges['def'] = def_
  607. ranges['step'] = step
  608. ranges['stepSmall'] = stepSmall
  609. ranges['stepLarge'] = stepLarge
  610. gCarla.host._set_parameterRangeS(pluginId, index, ranges)
  611. @pyqtSlot(int, int, float)
  612. def slot_handleSetParameterValue(self, pluginId, parameterId, value):
  613. if parameterId >= 0:
  614. gCarla.host._set_parameterValueS(pluginId, parameterId, value)
  615. if pluginId >= self.fPluginCount:
  616. return
  617. pwidget = self.fPluginList[pluginId]
  618. if pwidget is None:
  619. return
  620. pwidget.setParameterValue(parameterId, value)
  621. @pyqtSlot(int, int, float)
  622. def slot_handleSetDefaultValue(self, pluginId, parameterId, value):
  623. gCarla.host._set_parameterDefaultValue(pluginId, parameterId, value)
  624. if pluginId >= self.fPluginCount:
  625. return
  626. pwidget = self.fPluginList[pluginId]
  627. if pwidget is None:
  628. return
  629. pwidget.setParameterDefault(parameterId, value)
  630. @pyqtSlot(int, int, int)
  631. def slot_handleSetParameterMidiCC(self, pluginId, index, cc):
  632. gCarla.host._set_parameterMidiCC(pluginId, index, cc)
  633. if pluginId >= self.fPluginCount:
  634. return
  635. pwidget = self.fPluginList[pluginId]
  636. if pwidget is None:
  637. return
  638. pwidget.setParameterMidiControl(index, cc)
  639. @pyqtSlot(int, int, int)
  640. def slot_handleSetParameterMidiChannel(self, pluginId, index, channel):
  641. gCarla.host._set_parameterMidiChannel(pluginId, index, channel)
  642. if pluginId >= self.fPluginCount:
  643. return
  644. pwidget = self.fPluginList[pluginId]
  645. if pwidget is None:
  646. return
  647. pwidget.setParameterMidiChannel(index, channel)
  648. @pyqtSlot(int, int)
  649. def slot_handleSetProgram(self, pluginId, index):
  650. gCarla.host._set_currentProgram(pluginId, index)
  651. if pluginId >= self.fPluginCount:
  652. return
  653. pwidget = self.fPluginList[pluginId]
  654. if pwidget is None:
  655. return
  656. pwidget.setProgram(index)
  657. @pyqtSlot(int, int)
  658. def slot_handleSetProgramCount(self, pluginId, count):
  659. gCarla.host._set_programCount(pluginId, count)
  660. @pyqtSlot(int, int, str)
  661. def slot_handleSetProgramName(self, pluginId, index, name):
  662. gCarla.host._set_programNameS(pluginId, index, name)
  663. @pyqtSlot(int, int)
  664. def slot_handleSetMidiProgram(self, pluginId, index):
  665. gCarla.host._set_currentMidiProgram(pluginId, index)
  666. if pluginId >= self.fPluginCount:
  667. return
  668. pwidget = self.fPluginList[pluginId]
  669. if pwidget is None:
  670. return
  671. pwidget.setMidiProgram(index)
  672. @pyqtSlot(int, int)
  673. def slot_handleSetMidiProgramCount(self, pluginId, count):
  674. gCarla.host._set_midiProgramCount(pluginId, count)
  675. @pyqtSlot(int, int, int, int, str)
  676. def slot_handleSetMidiProgramData(self, pluginId, index, bank, program, name):
  677. data = deepcopy(MidiProgramData)
  678. data['bank'] = bank
  679. data['program'] = program
  680. data['label'] = name
  681. gCarla.host._set_midiProgramDataS(pluginId, index, data)
  682. @pyqtSlot(int, int, float)
  683. def slot_handleSetInputPeakValue(self, pluginId, portId, value):
  684. gCarla.host._set_inPeak(pluginId, portId-1, value)
  685. @pyqtSlot(int, int, float)
  686. def slot_handleSetOutputPeakValue(self, pluginId, portId, value):
  687. gCarla.host._set_outPeak(pluginId, portId-1, value)
  688. @pyqtSlot(int, int, int, int)
  689. def slot_handleNoteOn(self, pluginId, channel, note, velo):
  690. if pluginId >= self.fPluginCount:
  691. return
  692. pwidget = self.fPluginList[pluginId]
  693. if pwidget is None:
  694. return
  695. pwidget.sendNoteOn(channel, note)
  696. @pyqtSlot(int, int, int)
  697. def slot_handleNoteOff(self, pluginId, channel, note):
  698. if pluginId >= self.fPluginCount:
  699. return
  700. pwidget = self.fPluginList[pluginId]
  701. if pwidget is None:
  702. return
  703. pwidget.sendNoteOff(channel, note)
  704. @pyqtSlot(int, float, float, float, float)
  705. def slot_handleSetPeaks(self, pluginId, in1, in2, out1, out2):
  706. gCarla.host._set_peaks(pluginId, in1, in2, out1, out2)
  707. @pyqtSlot()
  708. def slot_handleExit(self):
  709. self.removeAll()
  710. if self.lo_server:
  711. self.lo_server.stop()
  712. self.lo_server = None
  713. self.ui.act_file_refresh.setEnabled(False)
  714. global lo_target, lo_targetName
  715. lo_target = None
  716. lo_targetName = ""
  717. self.lo_address = ""
  718. def saveSettings(self):
  719. settings = QSettings()
  720. settings.setValue("Geometry", self.saveGeometry())
  721. #settings.setValue("ShowToolbar", self.ui.toolBar.isVisible())
  722. def loadSettings(self):
  723. settings = QSettings()
  724. self.restoreGeometry(settings.value("Geometry", ""))
  725. #showToolbar = settings.value("ShowToolbar", True, type=bool)
  726. #self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  727. #self.ui.toolBar.setVisible(showToolbar)
  728. def timerEvent(self, event):
  729. if event.timerId() == self.fIdleTimerFast:
  730. for pwidget in self.fPluginList:
  731. if pwidget is None:
  732. break
  733. pwidget.idleFast()
  734. elif event.timerId() == self.fIdleTimerSlow:
  735. for pwidget in self.fPluginList:
  736. if pwidget is None:
  737. break
  738. pwidget.idleSlow()
  739. QMainWindow.timerEvent(self, event)
  740. def closeEvent(self, event):
  741. self.saveSettings()
  742. global lo_target
  743. if lo_target and self.lo_server:
  744. lo_send(lo_target, "/unregister")
  745. QMainWindow.closeEvent(self, event)
  746. #--------------- main ------------------
  747. if __name__ == '__main__':
  748. # App initialization
  749. app = QApplication(sys.argv)
  750. app.setApplicationName("Carla-Control")
  751. app.setApplicationVersion(VERSION)
  752. app.setOrganizationName("Cadence")
  753. app.setWindowIcon(QIcon(":/scalable/carla-control.svg"))
  754. libPrefix = None
  755. oscAddr = None
  756. argv = app.arguments()
  757. argc = len(argv)
  758. for i in range(argc):
  759. if i == 0: continue
  760. argument = argv[i]
  761. if argument.startswith("--with-libprefix="):
  762. libPrefix = argument.replace("--with-libprefix=", "")
  763. elif argument.startswith("osc."):
  764. oscAddr = argument
  765. if libPrefix is not None:
  766. libName = os.path.join(libPrefix, "lib", "carla", carla_libname)
  767. else:
  768. libName = carla_library_filename
  769. # Init backend (OSC bridge version)
  770. gCarla.host = Host()
  771. # Create GUI
  772. gCarla.gui = CarlaControlW(oscAddr)
  773. # Set-up custom signal handling
  774. setUpSignals()
  775. # Show GUI
  776. gCarla.gui.show()
  777. # App-Loop
  778. sys.exit(app.exec_())