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 35KB

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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  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. # ------------------------------------------------------------------------------------------------------------
  25. # Imports (Custom)
  26. import ui_carla_control
  27. from carla_shared import *
  28. global lo_target, lo_targetName
  29. lo_target = None
  30. lo_targetName = ""
  31. Carla.isControl = True
  32. # ------------------------------------------------------------------------------------------------------------
  33. # Python Object dicts compatible to carla-backend struct ctypes
  34. MidiProgramData = {
  35. 'bank': 0,
  36. 'program': 0,
  37. 'label': None
  38. }
  39. ParameterData = {
  40. 'type': PARAMETER_NULL,
  41. 'index': 0,
  42. 'rindex': -1,
  43. 'hints': 0x0,
  44. 'midiChannel': 0,
  45. 'midiCC': -1
  46. }
  47. ParameterRanges = {
  48. 'def': 0.0,
  49. 'min': 0.0,
  50. 'max': 1.0,
  51. 'step': 0.0,
  52. 'stepSmall': 0.0,
  53. 'stepLarge': 0.0
  54. }
  55. CustomData = {
  56. 'type': CUSTOM_DATA_INVALID,
  57. 'key': None,
  58. 'value': None
  59. }
  60. PluginInfo = {
  61. 'type': PLUGIN_NONE,
  62. 'category': PLUGIN_CATEGORY_NONE,
  63. 'hints': 0x0,
  64. 'binary': None,
  65. 'name': None,
  66. 'label': None,
  67. 'maker': None,
  68. 'copyright': None,
  69. 'uniqueId': 0
  70. }
  71. PortCountInfo = {
  72. 'ins': 0,
  73. 'outs': 0,
  74. 'total': 0
  75. }
  76. ParameterInfo = {
  77. 'name': None,
  78. 'symbol': None,
  79. 'unit': None,
  80. 'scalePointCount': 0,
  81. }
  82. ScalePointInfo = {
  83. 'value': 0.0,
  84. 'label': None
  85. }
  86. class ControlPluginInfo(object):
  87. __slots__ = [
  88. 'pluginInfo',
  89. 'pluginRealName',
  90. 'audioCountInfo',
  91. 'midiCountInfo',
  92. 'parameterCountInfo',
  93. 'parameterInfoS',
  94. 'parameterDataS',
  95. 'parameterRangeS',
  96. 'parameterValueS',
  97. 'programCount',
  98. 'programCurrent',
  99. 'programNameS',
  100. 'midiProgramCount',
  101. 'midiProgramCurrent',
  102. 'midiProgramDataS',
  103. 'inPeak',
  104. 'outPeak'
  105. ]
  106. # ------------------------------------------------------------------------------------------------------------
  107. # Python Object class compatible to 'Host' on the Carla Backend code
  108. class Host(object):
  109. def __init__(self):
  110. object.__init__(self)
  111. self.pluginInfo = []
  112. def _clear(self, index):
  113. self.pluginInfo[index].pluginInfo = PluginInfo
  114. self.pluginInfo[index].pluginRealName = None
  115. self.pluginInfo[index].audioCountInfo = PortCountInfo
  116. self.pluginInfo[index].midiCountInfo = PortCountInfo
  117. self.pluginInfo[index].parameterCountInfo = PortCountInfo
  118. self.pluginInfo[index].parameterInfoS = []
  119. self.pluginInfo[index].parameterDataS = []
  120. self.pluginInfo[index].parameterRangeS = []
  121. self.pluginInfo[index].parameterValueS = []
  122. self.pluginInfo[index].programCount = 0
  123. self.pluginInfo[index].programCurrent = -1
  124. self.pluginInfo[index].programNameS = []
  125. self.pluginInfo[index].midiProgramCount = 0
  126. self.pluginInfo[index].midiProgramCurrent = -1
  127. self.pluginInfo[index].midiProgramDataS = []
  128. self.pluginInfo[index].inPeak = [0.0, 0.0]
  129. self.pluginInfo[index].outPeak = [0.0, 0.0]
  130. def _set_pluginInfo(self, index, info):
  131. self.pluginInfo[index].pluginInfo = info
  132. def _set_pluginRealName(self, index, realName):
  133. self.pluginInfo[index].pluginRealName = realName
  134. def _set_audioCountInfo(self, index, info):
  135. self.pluginInfo[index].audioCountInfo = info
  136. def _set_midiCountInfo(self, index, info):
  137. self.pluginInfo[index].midiCountInfo = info
  138. def _set_parameterCountInfo(self, index, info):
  139. self.pluginInfo[index].parameterCountInfo = info
  140. # clear
  141. self.pluginInfo[index].parameterInfoS = []
  142. self.pluginInfo[index].parameterDataS = []
  143. self.pluginInfo[index].parameterRangeS = []
  144. self.pluginInfo[index].parameterValueS = []
  145. # add placeholders
  146. for x in range(info['total']):
  147. self.pluginInfo[index].parameterInfoS.append(ParameterInfo)
  148. self.pluginInfo[index].parameterDataS.append(ParameterData)
  149. self.pluginInfo[index].parameterRangeS.append(ParameterRanges)
  150. self.pluginInfo[index].parameterValueS.append(0.0)
  151. def _set_programCount(self, index, count):
  152. self.pluginInfo[index].programCount = count
  153. # clear
  154. self.pluginInfo[index].programNameS = []
  155. # add placeholders
  156. for x in range(count):
  157. self.pluginInfo[index].programNameS.append(None)
  158. def _set_midiProgramCount(self, index, count):
  159. self.pluginInfo[index].midiProgramCount = count
  160. # clear
  161. self.pluginInfo[index].midiProgramDataS = []
  162. # add placeholders
  163. for x in range(count):
  164. self.pluginInfo[index].midiProgramDataS.append(MidiProgramData)
  165. def _set_parameterInfoS(self, index, paramIndex, data):
  166. if paramIndex < self.pluginInfo[index].parameterCountInfo['total']:
  167. self.pluginInfo[index].parameterInfoS[paramIndex] = data
  168. def _set_parameterDataS(self, index, paramIndex, data):
  169. if paramIndex < self.pluginInfo[index].parameterCountInfo['total']:
  170. self.pluginInfo[index].parameterDataS[paramIndex] = data
  171. def _set_parameterRangeS(self, index, paramIndex, data):
  172. if paramIndex < self.pluginInfo[index].parameterCountInfo['total']:
  173. self.pluginInfo[index].parameterRangeS[paramIndex] = data
  174. def _set_parameterValueS(self, index, paramIndex, value):
  175. if paramIndex < self.pluginInfo[index].parameterCountInfo['total']:
  176. self.pluginInfo[index].parameterValueS[paramIndex] = value
  177. def _set_parameterDefaultValue(self, index, paramIndex, value):
  178. if paramIndex < self.pluginInfo[index].parameterCountInfo['total']:
  179. self.pluginInfo[index].parameterRangeS[paramIndex]['def'] = value
  180. def _set_parameterMidiCC(self, index, paramIndex, cc):
  181. if paramIndex < self.pluginInfo[index].parameterCountInfo['total']:
  182. self.pluginInfo[index].parameterDataS[paramIndex]['midiCC'] = cc
  183. def _set_parameterMidiChannel(self, index, paramIndex, channel):
  184. if paramIndex < self.pluginInfo[index].parameterCountInfo['total']:
  185. self.pluginInfo[index].parameterDataS[paramIndex]['midiChannel'] = channel
  186. def _set_currentProgram(self, index, pIndex):
  187. self.pluginInfo[index].programCurrent = pIndex
  188. def _set_currentMidiProgram(self, index, mpIndex):
  189. self.pluginInfo[index].midiProgramCurrent = mpIndex
  190. def _set_programNameS(self, index, pIndex, data):
  191. if pIndex < self.pluginInfo[index].programCount:
  192. self.pluginInfo[index].programNameS[pIndex] = data
  193. def _set_midiProgramDataS(self, index, mpIndex, data):
  194. if mpIndex < self.pluginInfo[index].midiProgramCount:
  195. self.pluginInfo[index].midiProgramDataS[mpIndex] = data
  196. def _set_inPeak(self, index, port, value):
  197. self.pluginInfo[index].inPeak[port] = value
  198. def _set_outPeak(self, index, port, value):
  199. self.pluginInfo[index].outPeak[port] = value
  200. def get_plugin_info(self, plugin_id):
  201. return self.pluginInfo[plugin_id].pluginInfo
  202. def get_audio_port_count_info(self, plugin_id):
  203. return self.pluginInfo[plugin_id].audioCountInfo
  204. def get_midi_port_count_info(self, plugin_id):
  205. return self.pluginInfo[plugin_id].midiCountInfo
  206. def get_parameter_count_info(self, plugin_id):
  207. return self.pluginInfo[plugin_id].parameterCountInfo
  208. def get_parameter_info(self, plugin_id, parameter_id):
  209. return self.pluginInfo[plugin_id].parameterInfoS[parameter_id]
  210. def get_parameter_scalepoint_info(self, plugin_id, parameter_id, scalepoint_id):
  211. return ScalePointInfo
  212. def get_parameter_data(self, plugin_id, parameter_id):
  213. return self.pluginInfo[plugin_id].parameterDataS[parameter_id]
  214. def get_parameter_ranges(self, plugin_id, parameter_id):
  215. return self.pluginInfo[plugin_id].parameterRangeS[parameter_id]
  216. def get_midi_program_data(self, plugin_id, midi_program_id):
  217. return self.pluginInfo[plugin_id].midiProgramDataS[midi_program_id]
  218. def get_custom_data(self, plugin_id, custom_data_id):
  219. return CustomData
  220. def get_chunk_data(self, plugin_id):
  221. return None
  222. def get_parameter_count(self, plugin_id):
  223. return self.pluginInfo[plugin_id].parameterCountInfo['total']
  224. def get_program_count(self, plugin_id):
  225. return self.pluginInfo[plugin_id].programCount
  226. def get_midi_program_count(self, plugin_id):
  227. return self.pluginInfo[plugin_id].midiProgramCount
  228. def get_custom_data_count(self, plugin_id):
  229. return 0
  230. def get_parameter_text(self, plugin_id, program_id):
  231. return None
  232. def get_program_name(self, plugin_id, program_id):
  233. return self.pluginInfo[plugin_id].programNameS[program_id]
  234. def get_midi_program_name(self, plugin_id, midi_program_id):
  235. return self.pluginInfo[plugin_id].midiProgramDataS[midi_program_id]['label']
  236. def get_real_plugin_name(self, plugin_id):
  237. return self.pluginInfo[plugin_id].pluginRealName
  238. def get_current_program_index(self, plugin_id):
  239. return self.pluginInfo[plugin_id].programCurrent
  240. def get_current_midi_program_index(self, plugin_id):
  241. return self.pluginInfo[plugin_id].midiProgramCurrent
  242. def get_default_parameter_value(self, plugin_id, parameter_id):
  243. return self.pluginInfo[plugin_id].parameterRangeS[parameter_id]['def']
  244. def get_current_parameter_value(self, plugin_id, parameter_id):
  245. return self.pluginInfo[plugin_id].parameterValueS[parameter_id]
  246. def get_input_peak_value(self, plugin_id, port_id):
  247. return self.pluginInfo[plugin_id].inPeak[port_id-1]
  248. def get_output_peak_value(self, plugin_id, port_id):
  249. return self.pluginInfo[plugin_id].outPeak[port_id-1]
  250. def set_active(self, plugin_id, onoff):
  251. global to_target, lo_targetName
  252. lo_path = "/%s/%i/set_active" % (lo_targetName, plugin_id)
  253. lo_send(lo_target, lo_path, 1 if onoff else 0)
  254. def set_drywet(self, plugin_id, value):
  255. global to_target, lo_targetName
  256. lo_path = "/%s/%i/set_drywet" % (lo_targetName, plugin_id)
  257. lo_send(lo_target, lo_path, value)
  258. def set_volume(self, plugin_id, value):
  259. global to_target, lo_targetName
  260. lo_path = "/%s/%i/set_volume" % (lo_targetName, plugin_id)
  261. lo_send(lo_target, lo_path, value)
  262. def set_balance_left(self, plugin_id, value):
  263. global to_target, lo_targetName
  264. lo_path = "/%s/%i/set_balance_left" % (lo_targetName, plugin_id)
  265. lo_send(lo_target, lo_path, value)
  266. def set_balance_right(self, plugin_id, value):
  267. global to_target, lo_targetName
  268. lo_path = "/%s/%i/set_balance_right" % (lo_targetName, plugin_id)
  269. lo_send(lo_target, lo_path, value)
  270. def set_parameter_value(self, plugin_id, parameter_id, value):
  271. global to_target, lo_targetName
  272. lo_path = "/%s/%i/set_parameter_value" % (lo_targetName, plugin_id)
  273. lo_send(lo_target, lo_path, parameter_id, value)
  274. def set_parameter_midi_channel(self, plugin_id, parameter_id, channel):
  275. global to_target, lo_targetName
  276. lo_path = "/%s/%i/set_parameter_midi_channel" % (lo_targetName, plugin_id)
  277. lo_send(lo_target, lo_path, parameter_id, channel)
  278. def set_parameter_midi_cc(self, plugin_id, parameter_id, midi_cc):
  279. global to_target, lo_targetName
  280. lo_path = "/%s/%i/set_parameter_midi_cc" % (lo_targetName, plugin_id)
  281. lo_send(lo_target, lo_path, parameter_id, midi_cc)
  282. def set_program(self, plugin_id, program_id):
  283. global to_target, lo_targetName
  284. lo_path = "/%s/%i/set_program" % (lo_targetName, plugin_id)
  285. lo_send(lo_target, lo_path, program_id)
  286. def set_midi_program(self, plugin_id, midi_program_id):
  287. global to_target, lo_targetName
  288. lo_path = "/%s/%i/set_midi_program" % (lo_targetName, plugin_id)
  289. lo_send(lo_target, lo_path, midi_program_id)
  290. def send_midi_note(self, plugin_id, channel, note, velocity):
  291. global to_target, lo_targetName
  292. if velocity:
  293. lo_path = "/%s/%i/note_on" % (lo_targetName, plugin_id)
  294. lo_send(lo_target, lo_path, channel, note, velocity)
  295. else:
  296. lo_path = "/%s/%i/note_off" % (lo_targetName, plugin_id)
  297. lo_send(lo_target, lo_path, channel, note)
  298. # ------------------------------------------------------------------------------------------------------------
  299. # OSC Control server
  300. class ControlServer(ServerThread):
  301. def __init__(self, parent):
  302. ServerThread.__init__(self, 8087, LO_TCP)
  303. self.parent = parent
  304. def getFullURL(self):
  305. return "%scarla-control" % self.get_url()
  306. @make_method('/carla-control/add_plugin_start', 'is')
  307. def add_plugin_start_callback(self, path, args):
  308. pluginId, pluginName = args
  309. self.parent.emit(SIGNAL("AddPluginStart(int, QString)"), pluginId, pluginName)
  310. @make_method('/carla-control/add_plugin_end', 'i')
  311. def add_plugin_end_callback(self, path, args):
  312. pluginId, = args
  313. self.parent.emit(SIGNAL("AddPluginEnd(int)"), pluginId)
  314. @make_method('/carla-control/remove_plugin', 'i')
  315. def remove_plugin_callback(self, path, args):
  316. pluginId, = args
  317. self.parent.emit(SIGNAL("RemovePlugin(int)"), pluginId)
  318. @make_method('/carla-control/set_plugin_data', 'iiiissssh')
  319. def set_plugin_data_callback(self, path, args):
  320. pluginId, ptype, category, hints, realName, label, maker, copyright_, uniqueId = args
  321. self.parent.emit(SIGNAL("SetPluginData(int, int, int, int, QString, QString, QString, QString, int)"), pluginId, ptype, category, hints, realName, label, maker, copyright_, uniqueId)
  322. @make_method('/carla-control/set_plugin_ports', 'iiiiiiii')
  323. def set_plugin_ports_callback(self, path, args):
  324. pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals = args
  325. self.parent.emit(SIGNAL("SetPluginPorts(int, int, int, int, int, int, int, int)"), pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals)
  326. @make_method('/carla-control/set_parameter_data', 'iiiissd')
  327. def set_parameter_data_callback(self, path, args):
  328. pluginId, index, type_, hints, name, label, current = args
  329. self.parent.emit(SIGNAL("SetParameterData(int, int, int, int, QString, QString, double)"), pluginId, index, type_, hints, name, label, current)
  330. @make_method('/carla-control/set_parameter_ranges', 'iidddddd')
  331. def set_parameter_ranges_callback(self, path, args):
  332. pluginId, index, min_, max_, def_, step, stepSmall, stepLarge = args
  333. self.parent.emit(SIGNAL("SetParameterRanges(int, int, double, double, double, double, double, double)"), pluginId, index, min_, max_, def_, step, stepSmall, stepLarge)
  334. @make_method('/carla-control/set_parameter_midi_cc', 'iii')
  335. def set_parameter_midi_cc_callback(self, path, args):
  336. pluginId, index, cc = args
  337. self.parent.emit(SIGNAL("SetParameterMidiCC(int, int, int)"), pluginId, index, cc)
  338. @make_method('/carla-control/set_parameter_midi_channel', 'iii')
  339. def set_parameter_midi_channel_callback(self, path, args):
  340. pluginId, index, channel = args
  341. self.parent.emit(SIGNAL("SetParameterMidiChannel(int, int, int)"), pluginId, index, channel)
  342. @make_method('/carla-control/set_parameter_value', 'iid')
  343. def set_parameter_value_callback(self, path, args):
  344. pluginId, index, value = args
  345. self.parent.emit(SIGNAL("SetParameterValue(int, int, double)"), pluginId, index, value)
  346. @make_method('/carla-control/set_default_value', 'iid')
  347. def set_default_value_callback(self, path, args):
  348. pluginId, index, value = args
  349. self.parent.emit(SIGNAL("SetDefaultValue(int, int, double)"), pluginId, index, value)
  350. @make_method('/carla-control/set_program', 'ii')
  351. def set_program_callback(self, path, args):
  352. pluginId, index = args
  353. self.parent.emit(SIGNAL("SetProgram(int, int)"), pluginId, index)
  354. @make_method('/carla-control/set_program_count', 'ii')
  355. def set_program_count_callback(self, path, args):
  356. pluginId, count = args
  357. self.parent.emit(SIGNAL("SetProgramCount(int, int)"), pluginId, count)
  358. @make_method('/carla-control/set_program_name', 'iis')
  359. def set_program_name_callback(self, path, args):
  360. pluginId, index, name = args
  361. self.parent.emit(SIGNAL("SetProgramName(int, int, QString)"), pluginId, index, name)
  362. @make_method('/carla-control/set_midi_program', 'ii')
  363. def set_midi_program_callback(self, path, args):
  364. pluginId, index = args
  365. self.parent.emit(SIGNAL("SetMidiProgram(int, int)"), pluginId, index)
  366. @make_method('/carla-control/set_midi_program_count', 'ii')
  367. def set_midi_program_count_callback(self, path, args):
  368. pluginId, count = args
  369. self.parent.emit(SIGNAL("SetMidiProgramCount(int, int)"), pluginId, count)
  370. @make_method('/carla-control/set_midi_program_data', 'iiiis')
  371. def set_midi_program_data_callback(self, path, args):
  372. pluginId, index, bank, program, name = args
  373. self.parent.emit(SIGNAL("SetMidiProgramData(int, int, int, int, QString)"), pluginId, index, bank, program, name)
  374. @make_method('/carla-control/set_input_peak_value', 'iid')
  375. def set_input_peak_value_callback(self, path, args):
  376. pluginId, portId, value = args
  377. self.parent.emit(SIGNAL("SetInputPeakValue(int, int, double)"), pluginId, portId, value)
  378. @make_method('/carla-control/set_output_peak_value', 'iid')
  379. def set_output_peak_value_callback(self, path, args):
  380. pluginId, portId, value = args
  381. self.parent.emit(SIGNAL("SetOutputPeakValue(int, int, double)"), pluginId, portId, value)
  382. @make_method('/carla-control/note_on', 'iiii')
  383. def note_on_callback(self, path, args):
  384. pluginId, channel, note, velo = args
  385. self.parent.emit(SIGNAL("NoteOn(int, int, int, int)"), pluginId, channel, note, velo)
  386. @make_method('/carla-control/note_off', 'iii')
  387. def note_off_callback(self, path, args):
  388. pluginId, channel, note = args
  389. self.parent.emit(SIGNAL("NoteOff(int, int, int)"), pluginId, channel, note)
  390. @make_method('/carla-control/exit', '')
  391. def exit_callback(self, path, args):
  392. self.parent.emit(SIGNAL("Exit()"))
  393. @make_method(None, None)
  394. def fallback(self, path, args):
  395. print("ControlServer::fallback(\"%s\") - unknown message, args =" % path, args)
  396. # ------------------------------------------------------------------------------------------------------------
  397. # Main Window
  398. class CarlaControlW(QMainWindow):
  399. def __init__(self, parent=None):
  400. QMainWindow.__init__(self, parent)
  401. self.ui = ui_carla_control.Ui_CarlaControlW()
  402. self.ui.setupUi(self)
  403. # -------------------------------------------------------------
  404. # Load Settings
  405. self.loadSettings()
  406. self.setStyleSheet("""
  407. QWidget#centralwidget {
  408. background-color: qlineargradient(spread:pad,
  409. x1:0.0, y1:0.0,
  410. x2:0.2, y2:1.0,
  411. stop:0 rgb( 7, 7, 7),
  412. stop:1 rgb(28, 28, 28)
  413. );
  414. }
  415. """)
  416. # -------------------------------------------------------------
  417. # Internal stuff
  418. self.lo_address = ""
  419. self.lo_server = None
  420. self.fLastPluginName = ""
  421. self.fPluginCount = 0
  422. self.fPluginList = []
  423. self.fIdleTimerFast = 0
  424. self.fIdleTimerSlow = 0
  425. # -------------------------------------------------------------
  426. # Set-up GUI stuff
  427. self.ui.act_file_refresh.setEnabled(False)
  428. self.resize(self.width(), 0)
  429. # -------------------------------------------------------------
  430. # Connect actions to functions
  431. self.connect(self.ui.act_file_connect, SIGNAL("triggered()"), SLOT("slot_fileConnect()"))
  432. self.connect(self.ui.act_file_refresh, SIGNAL("triggered()"), SLOT("slot_fileRefresh()"))
  433. self.connect(self.ui.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarlaControl()"))
  434. self.connect(self.ui.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()"))
  435. self.connect(self, SIGNAL("AddPluginStart(int, QString)"), SLOT("slot_handleAddPluginStart(int, QString)"))
  436. self.connect(self, SIGNAL("AddPluginEnd(int)"), SLOT("slot_handleAddPluginEnd(int)"))
  437. self.connect(self, SIGNAL("RemovePlugin(int)"), SLOT("slot_handleRemovePlugin(int)"))
  438. 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)"))
  439. self.connect(self, SIGNAL("SetPluginPorts(int, int, int, int, int, int, int, int)"), SLOT("slot_handleSetPluginPorts(int, int, int, int, int, int, int, int)"))
  440. self.connect(self, SIGNAL("SetParameterData(int, int, int, int, QString, QString, double)"), SLOT("slot_handleSetParameterData(int, int, int, int, QString, QString, double)"))
  441. self.connect(self, SIGNAL("SetParameterRanges(int, int, double, double, double, double, double, double)"), SLOT("slot_handleSetParameterRanges(int, int, double, double, double, double, double, double)"))
  442. self.connect(self, SIGNAL("SetParameterMidiCC(int, int, int)"), SLOT("slot_handleSetParameterMidiCC(int, int, int)"))
  443. self.connect(self, SIGNAL("SetParameterMidiChannel(int, int, int)"), SLOT("slot_handleSetParameterMidiChannel(int, int, int)"))
  444. self.connect(self, SIGNAL("SetParameterValue(int, int, double)"), SLOT("slot_handleSetParameterValue(int, int, double)"))
  445. self.connect(self, SIGNAL("SetDefaultValue(int, int, double)"), SLOT("slot_handleSetDefaultValue(int, int, double)"))
  446. self.connect(self, SIGNAL("SetProgram(int, int)"), SLOT("slot_handleSetProgram(int, int)"))
  447. self.connect(self, SIGNAL("SetProgramCount(int, int)"), SLOT("slot_handleSetProgramCount(int, int)"))
  448. self.connect(self, SIGNAL("SetProgramName(int, int, QString)"), SLOT("slot_handleSetProgramName(int, int, QString)"))
  449. self.connect(self, SIGNAL("SetMidiProgram(int, int)"), SLOT("slot_handleSetMidiProgram(int, int)"))
  450. self.connect(self, SIGNAL("SetMidiProgramCount(int, int)"), SLOT("slot_handleSetMidiProgramCount(int, int)"))
  451. self.connect(self, SIGNAL("SetMidiProgramData(int, int, int, int, QString)"), SLOT("slot_handleSetMidiProgramData(int, int, int, int, QString)"))
  452. self.connect(self, SIGNAL("SetInputPeakValue(int, int, double)"), SLOT("slot_handleSetInputPeakValue(int, int, double)"))
  453. self.connect(self, SIGNAL("SetOutputPeakValue(int, int, double)"), SLOT("slot_handleSetOutputPeakValue(int, int, double)"))
  454. self.connect(self, SIGNAL("NoteOn(int, int, int, int)"), SLOT("slot_handleNoteOn(int, int, int, int)"))
  455. self.connect(self, SIGNAL("NoteOff(int, int, int)"), SLOT("slot_handleNoteOff(int, int, int)"))
  456. self.connect(self, SIGNAL("Exit()"), SLOT("slot_handleExit()"))
  457. # Peaks
  458. self.fIdleTimerFast = self.startTimer(50)
  459. # LEDs and edit dialog parameters
  460. self.fIdleTimerSlow = self.startTimer(50*2)
  461. def removeAll(self):
  462. for i in range(self.fPluginCount):
  463. self.slot_handleRemovePlugin(i)
  464. @pyqtSlot()
  465. def slot_fileConnect(self):
  466. global lo_target, lo_targetName
  467. if lo_target and self.lo_server:
  468. urlText = self.lo_address
  469. else:
  470. urlText = "osc.tcp://127.0.0.1:19000/Carla"
  471. askValue = QInputDialog.getText(self, self.tr("Carla Control - Connect"), self.tr("Address"), text=urlText)
  472. if not askValue[1]:
  473. return
  474. self.slot_handleExit()
  475. self.lo_address = askValue[0]
  476. lo_target = Address(self.lo_address)
  477. lo_targetName = self.lo_address.rsplit("/", 1)[-1]
  478. print("Connecting to \"%s\" as '%s'..." % (self.lo_address, lo_targetName))
  479. try:
  480. self.lo_server = ControlServer(self)
  481. except: # ServerError, err:
  482. print("Connecting error!")
  483. #print str(err)
  484. QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to connect, operation failed."))
  485. if self.lo_server:
  486. self.lo_server.start()
  487. self.ui.act_file_refresh.setEnabled(True)
  488. lo_send(lo_target, "/register", self.lo_server.getFullURL())
  489. @pyqtSlot()
  490. def slot_fileRefresh(self):
  491. global lo_target
  492. if lo_target and self.lo_server:
  493. self.removeAll()
  494. lo_send(lo_target, "/unregister")
  495. lo_send(lo_target, "/register", self.lo_server.getFullURL())
  496. @pyqtSlot()
  497. def slot_aboutCarlaControl(self):
  498. CarlaAboutW(self).exec_()
  499. @pyqtSlot(int, str)
  500. def slot_handleAddPluginStart(self, pluginId, pluginName):
  501. self.m_lastPluginName = pluginName
  502. @pyqtSlot(int)
  503. def slot_handleAddPluginEnd(self, pluginId):
  504. pwidget = PluginWidget(self, pluginId)
  505. pwidget.setRefreshRate(50)
  506. self.ui.w_plugins.layout().addWidget(pwidget)
  507. self.fPluginCount += 1
  508. self.fPluginList.append(pwidget)
  509. @pyqtSlot(int)
  510. def slot_handleRemovePlugin(self, pluginId):
  511. pwidget = self.fPluginList[pluginId]
  512. if pwidget is None:
  513. return
  514. self.fPluginList[pluginId] = None
  515. self.fPluginCount -= 1
  516. self.ui.w_plugins.layout().removeWidget(pwidget)
  517. pwidget.ui.edit_dialog.close()
  518. pwidget.close()
  519. pwidget.deleteLater()
  520. del pwidget
  521. # push all plugins 1 slot back
  522. for i in range(pluginId, self.fPluginCount):
  523. self.fPluginList[i] = self.fPluginList[i+1]
  524. self.fPluginList[i].setId(i)
  525. # TODO - move Carla.host.* stuff too
  526. @pyqtSlot(int, int, int, int, str, str, str, str, int)
  527. def slot_handleSetPluginData(self, pluginId, type_, category, hints, realName, label, maker, copyright, uniqueId):
  528. info = deepcopy(PluginInfo)
  529. info['type'] = type_
  530. info['category'] = category
  531. info['hints'] = hints
  532. info['name'] = self.m_lastPluginName
  533. info['label'] = label
  534. info['maker'] = maker
  535. info['copyright'] = copyright
  536. info['uniqueId'] = uniqueId
  537. Carla.host._set_pluginInfo(pluginId, info)
  538. Carla.host._set_pluginRealName(pluginId, realName)
  539. @pyqtSlot(int, int, int, int, int, int, int, int)
  540. def slot_handleSetPluginPorts(self, pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals):
  541. audioInfo = deepcopy(PortCountInfo)
  542. midiInfo = deepcopy(PortCountInfo)
  543. paramInfo = deepcopy(PortCountInfo)
  544. audioInfo['ins'] = audioIns
  545. audioInfo['outs'] = audioOuts
  546. audioInfo['total'] = audioIns + audioOuts
  547. midiInfo['ins'] = midiIns
  548. midiInfo['outs'] = midiOuts
  549. midiInfo['total'] = midiIns + midiOuts
  550. paramInfo['ins'] = cIns
  551. paramInfo['outs'] = cOuts
  552. paramInfo['total'] = cTotals
  553. Carla.host._set_audioCountInfo(pluginId, audioInfo)
  554. Carla.host._set_midiCountInfo(pluginId, midiInfo)
  555. Carla.host._set_parameterCountInfo(pluginId, paramInfo)
  556. @pyqtSlot(int, int, int, int, str, str, float)
  557. def slot_handleSetParameterData(self, pluginId, index, type_, hints, name, label, current):
  558. # remove hints not possible in bridge mode
  559. hints &= ~(PARAMETER_USES_SCALEPOINTS | PARAMETER_USES_CUSTOM_TEXT)
  560. data = deepcopy(ParameterData)
  561. data['type'] = type_
  562. data['index'] = index
  563. data['rindex'] = index
  564. data['hints'] = hints
  565. info = deepcopy(ParameterInfo)
  566. info['name'] = name
  567. info['label'] = label
  568. Carla.host._set_parameterDataS(pluginId, index, data)
  569. Carla.host._set_parameterInfoS(pluginId, index, info)
  570. Carla.host._set_parameterValueS(pluginId, index, current)
  571. @pyqtSlot(int, int, float, float, float, float, float, float)
  572. def slot_handleSetParameterRanges(self, pluginId, index, min_, max_, def_, step, stepSmall, stepLarge):
  573. ranges = deepcopy(ParameterRanges)
  574. ranges['min'] = min_
  575. ranges['max'] = max_
  576. ranges['def'] = def_
  577. ranges['step'] = step
  578. ranges['stepSmall'] = stepSmall
  579. ranges['stepLarge'] = stepLarge
  580. Carla.host._set_parameterRangeS(pluginId, index, ranges)
  581. @pyqtSlot(int, int, float)
  582. def slot_handleSetParameterValue(self, pluginId, parameterId, value):
  583. if parameterId >= 0:
  584. Carla.host._set_parameterValueS(pluginId, parameterId, value)
  585. pwidget = self.fPluginList[pluginId]
  586. if pwidget is None:
  587. return
  588. pwidget.setParameterValue(parameterId, value)
  589. @pyqtSlot(int, int, float)
  590. def slot_handleSetDefaultValue(self, pluginId, parameterId, value):
  591. Carla.host._set_parameterDefaultValue(pluginId, parameterId, value)
  592. pwidget = self.fPluginList[pluginId]
  593. if pwidget is None:
  594. return
  595. pwidget.setParameterDefault(parameterId, value)
  596. @pyqtSlot(int, int, int)
  597. def slot_handleSetParameterMidiCC(self, pluginId, index, cc):
  598. Carla.host._set_parameterMidiCC(pluginId, index, cc)
  599. pwidget = self.fPluginList[pluginId]
  600. if pwidget is None:
  601. return
  602. pwidget.setParameterMidiControl(index, cc)
  603. @pyqtSlot(int, int, int)
  604. def slot_handleSetParameterMidiChannel(self, pluginId, index, channel):
  605. Carla.host._set_parameterMidiChannel(pluginId, index, channel)
  606. pwidget = self.fPluginList[pluginId]
  607. if pwidget is None:
  608. return
  609. pwidget.setParameterMidiChannel(index, channel)
  610. @pyqtSlot(int, int)
  611. def slot_handleSetProgram(self, pluginId, index):
  612. Carla.host._set_currentProgram(pluginId, index)
  613. pwidget = self.fPluginList[pluginId]
  614. if pwidget is None:
  615. return
  616. pwidget.setProgram(index)
  617. @pyqtSlot(int, int)
  618. def slot_handleSetProgramCount(self, pluginId, count):
  619. Carla.host._set_programCount(pluginId, count)
  620. @pyqtSlot(int, int, str)
  621. def slot_handleSetProgramName(self, pluginId, index, name):
  622. Carla.host._set_programNameS(pluginId, index, name)
  623. @pyqtSlot(int, int)
  624. def slot_handleSetMidiProgram(self, pluginId, index):
  625. Carla.host._set_currentMidiProgram(pluginId, index)
  626. pwidget = self.fPluginList[pluginId]
  627. if pwidget is None:
  628. return
  629. pwidget.setMidiProgram(index)
  630. @pyqtSlot(int, int)
  631. def slot_handleSetMidiProgramCount(self, pluginId, count):
  632. Carla.host._set_midiProgramCount(pluginId, count)
  633. @pyqtSlot(int, int, int, int, str)
  634. def slot_handleSetMidiProgramData(self, pluginId, index, bank, program, name):
  635. data = deepcopy(MidiProgramData)
  636. data['bank'] = bank
  637. data['program'] = program
  638. data['label'] = name
  639. Carla.host._set_midiProgramDataS(pluginId, index, data)
  640. @pyqtSlot(int, int, float)
  641. def slot_handleSetInputPeakValue(self, pluginId, portId, value):
  642. Carla.host._set_inPeak(pluginId, portId-1, value)
  643. @pyqtSlot(int, int, float)
  644. def slot_handleSetOutputPeakValue(self, pluginId, portId, value):
  645. Carla.host._set_outPeak(pluginId, portId-1, value)
  646. @pyqtSlot(int, int, int, int)
  647. def slot_handleNoteOn(self, pluginId, channel, note, velo):
  648. pwidget = self.fPluginList[pluginId]
  649. if pwidget is None:
  650. return
  651. pwidget.sendNoteOn(note)
  652. @pyqtSlot(int, int, int)
  653. def slot_handleNoteOff(self, pluginId, channel, note):
  654. pwidget = self.fPluginList[pluginId]
  655. if pwidget is None:
  656. return
  657. pwidget.sendNoteOff(note)
  658. @pyqtSlot()
  659. def slot_handleExit(self):
  660. self.removeAll()
  661. if self.lo_server:
  662. self.lo_server.stop()
  663. self.lo_server = None
  664. self.ui.act_file_refresh.setEnabled(False)
  665. global lo_target, lo_targetName
  666. lo_target = None
  667. lo_targetName = ""
  668. self.lo_address = ""
  669. def saveSettings(self):
  670. settings = QSettings()
  671. settings.setValue("Geometry", self.saveGeometry())
  672. #settings.setValue("ShowToolbar", self.ui.toolBar.isVisible())
  673. def loadSettings(self):
  674. settings = QSettings()
  675. self.restoreGeometry(settings.value("Geometry", ""))
  676. #showToolbar = settings.value("ShowToolbar", True, type=bool)
  677. #self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  678. #self.ui.toolBar.setVisible(showToolbar)
  679. def timerEvent(self, event):
  680. if event.timerId() == self.fIdleTimerFast:
  681. for pwidget in self.fPluginList:
  682. if pwidget is None:
  683. break
  684. pwidget.idleFast()
  685. elif event.timerId() == self.fIdleTimerSlow:
  686. for pwidget in self.fPluginList:
  687. if pwidget is None:
  688. break
  689. pwidget.idleSlow()
  690. QMainWindow.timerEvent(self, event)
  691. def closeEvent(self, event):
  692. self.saveSettings()
  693. global lo_target
  694. if lo_target and self.lo_server:
  695. lo_send(lo_target, "/unregister")
  696. QMainWindow.closeEvent(self, event)
  697. #--------------- main ------------------
  698. if __name__ == '__main__':
  699. # App initialization
  700. app = QApplication(sys.argv)
  701. app.setApplicationName("Carla-Control")
  702. app.setApplicationVersion(VERSION)
  703. app.setOrganizationName("Cadence")
  704. app.setWindowIcon(QIcon(":/scalable/carla-control.svg"))
  705. libPrefix = None
  706. for i in range(len(app.arguments())):
  707. if i == 0: continue
  708. argument = app.arguments()[i]
  709. if argument.startswith("--with-libprefix="):
  710. libPrefix = argument.replace("--with-libprefix=", "")
  711. if libPrefix is not None:
  712. libName = os.path.join(libPrefix, "lib", "carla", carla_libname)
  713. else:
  714. libName = carla_library_path
  715. # Load backend DLL, so it registers the theme
  716. libDLL = QLibrary()
  717. libDLL.setFileName(libName)
  718. try:
  719. libDLL.load()
  720. except:
  721. pass
  722. # Init backend (OSC bridge version)
  723. Carla.host = Host()
  724. # Create GUI
  725. Carla.gui = CarlaControlW()
  726. # Set-up custom signal handling
  727. setUpSignals()
  728. # Show GUI
  729. Carla.gui.show()
  730. # App-Loop
  731. ret = app.exec_()
  732. # Close backend DLL
  733. if libDLL.isLoaded():
  734. # Need to destroy app before theme
  735. del app
  736. libDLL.unload()
  737. sys.exit(ret)