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.

783 lines
29KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla plugin host (plugin UI)
  4. # Copyright (C) 2013-2014 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 (Custom Stuff)
  19. from carla_host import *
  20. from externalui import ExternalUI
  21. # ------------------------------------------------------------------------------------------------------------
  22. # Helper object
  23. class PluginStoreInfo(object):
  24. __slots__ = [
  25. 'pluginInfo',
  26. 'pluginRealName',
  27. 'audioCountInfo',
  28. 'midiCountInfo',
  29. 'parameterCount',
  30. 'parameterCountInfo',
  31. 'parameterInfoS',
  32. 'parameterDataS',
  33. 'parameterRangeS',
  34. 'parameterValueS',
  35. 'parameterValueInt',
  36. 'programCount',
  37. 'programCurrent',
  38. 'programNameS',
  39. 'midiProgramCount',
  40. 'midiProgramCurrent',
  41. 'midiProgramDataS',
  42. 'peaks'
  43. ]
  44. # ------------------------------------------------------------------------------------------------------------
  45. # Host Plugin object
  46. class PluginHost(object):
  47. def __init__(self, sampleRate):
  48. object.__init__(self)
  49. self.fSupportedFileExts = ""
  50. self.fBufferSize = 0
  51. self.fSampleRate = sampleRate
  52. self.fLastError = ""
  53. self.fIsRunning = True
  54. self.fPluginsInfo = []
  55. def _add(self, pluginId):
  56. if len(self.fPluginsInfo) != pluginId:
  57. return
  58. info = PluginStoreInfo()
  59. info.pluginInfo = PyCarlaPluginInfo
  60. info.pluginRealName = ""
  61. info.audioCountInfo = PyCarlaPortCountInfo
  62. info.midiCountInfo = PyCarlaPortCountInfo
  63. info.parameterCount = 0
  64. info.parameterCountInfo = PyCarlaPortCountInfo
  65. info.parameterInfoS = []
  66. info.parameterDataS = []
  67. info.parameterRangeS = []
  68. info.parameterValueS = []
  69. info.parameterValueInt = [0.0, 1.0, 1.0, -1.0, 1.0, 0.0, -1.0]
  70. info.programCount = 0
  71. info.programCurrent = -1
  72. info.programNameS = []
  73. info.midiProgramCount = 0
  74. info.midiProgramCurrent = -1
  75. info.midiProgramDataS = []
  76. info.peaks = [0.0, 0.0, 0.0, 0.0]
  77. self.fPluginsInfo.append(info)
  78. def _set_pluginInfo(self, pluginId, info):
  79. self.fPluginsInfo[pluginId].pluginInfo = info
  80. def _set_pluginName(self, pluginId, name):
  81. self.fPluginsInfo[pluginId].pluginInfo['name'] = name
  82. def _set_pluginRealName(self, pluginId, realName):
  83. self.fPluginsInfo[pluginId].pluginRealName = realName
  84. def _set_audioCountInfo(self, pluginId, info):
  85. self.fPluginsInfo[pluginId].audioCountInfo = info
  86. def _set_midiCountInfo(self, pluginId, info):
  87. self.fPluginsInfo[pluginId].midiCountInfo = info
  88. def _set_parameterCountInfo(self, pluginId, count, info):
  89. self.fPluginsInfo[pluginId].parameterCount = count
  90. self.fPluginsInfo[pluginId].parameterCountInfo = info
  91. # clear
  92. self.fPluginsInfo[pluginId].parameterInfoS = []
  93. self.fPluginsInfo[pluginId].parameterDataS = []
  94. self.fPluginsInfo[pluginId].parameterRangeS = []
  95. self.fPluginsInfo[pluginId].parameterValueS = []
  96. # add placeholders
  97. for x in range(count):
  98. self.fPluginsInfo[pluginId].parameterInfoS.append(PyCarlaParameterInfo)
  99. self.fPluginsInfo[pluginId].parameterDataS.append(PyParameterData)
  100. self.fPluginsInfo[pluginId].parameterRangeS.append(PyParameterRanges)
  101. self.fPluginsInfo[pluginId].parameterValueS.append(0.0)
  102. def _set_programCount(self, pluginId, count):
  103. self.fPluginsInfo[pluginId].programCount = count
  104. # clear
  105. self.fPluginsInfo[pluginId].programNameS = []
  106. # add placeholders
  107. for x in range(count):
  108. self.fPluginsInfo[pluginId].programNameS.append("")
  109. def _set_midiProgramCount(self, pluginId, count):
  110. self.fPluginsInfo[pluginId].midiProgramCount = count
  111. # clear
  112. self.fPluginsInfo[pluginId].midiProgramDataS = []
  113. # add placeholders
  114. for x in range(count):
  115. self.fPluginsInfo[pluginId].midiProgramDataS.append(PyMidiProgramData)
  116. def _set_parameterInfoS(self, pluginId, paramIndex, info):
  117. if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
  118. self.fPluginsInfo[pluginId].parameterInfoS[paramIndex] = info
  119. def _set_parameterDataS(self, pluginId, paramIndex, data):
  120. if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
  121. self.fPluginsInfo[pluginId].parameterDataS[paramIndex] = data
  122. def _set_parameterRangeS(self, pluginId, paramIndex, ranges):
  123. if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
  124. self.fPluginsInfo[pluginId].parameterRangeS[paramIndex] = ranges
  125. def _set_parameterValueS(self, pluginId, paramIndex, value):
  126. if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
  127. self.fPluginsInfo[pluginId].parameterValueS[paramIndex] = value
  128. def _set_parameterValueInt(self, pluginId, paramIndex, value):
  129. if pluginId < len(self.fPluginsInfo) and PARAMETER_NULL > paramIndex > PARAMETER_MAX:
  130. self.fPluginsInfo[pluginId].parameterValueInt[abs(paramIndex)-2] = float(value)
  131. def _set_parameterDefault(self, pluginId, paramIndex, value):
  132. if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
  133. self.fPluginsInfo[pluginId].parameterRangeS[paramIndex]['def'] = value
  134. def _set_parameterMidiChannel(self, pluginId, paramIndex, channel):
  135. if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
  136. self.fPluginsInfo[pluginId].parameterDataS[paramIndex]['midiChannel'] = channel
  137. def _set_parameterMidiCC(self, pluginId, paramIndex, cc):
  138. if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
  139. self.fPluginsInfo[pluginId].parameterDataS[paramIndex]['midiCC'] = cc
  140. def _set_currentProgram(self, pluginId, pIndex):
  141. self.fPluginsInfo[pluginId].programCurrent = pIndex
  142. def _set_currentMidiProgram(self, pluginId, mpIndex):
  143. self.fPluginsInfo[pluginId].midiProgramCurrent = mpIndex
  144. def _set_programNameS(self, pluginId, pIndex, name):
  145. if pIndex < self.fPluginsInfo[pluginId].programCount:
  146. self.fPluginsInfo[pluginId].programNameS[pIndex] = name
  147. def _set_midiProgramDataS(self, pluginId, mpIndex, data):
  148. if mpIndex < self.fPluginsInfo[pluginId].midiProgramCount:
  149. self.fPluginsInfo[pluginId].midiProgramDataS[mpIndex] = data
  150. def _set_peaks(self, pluginId, in1, in2, out1, out2):
  151. self.fPluginsInfo[pluginId].peaks = [in1, in2, out1, out2]
  152. # -------------------------------------------------------------------
  153. def get_complete_license_text(self):
  154. return ""
  155. def get_supported_file_extensions(self):
  156. return self.fSupportedFileExts
  157. # -------------------------------------------------------------------
  158. def get_engine_driver_count(self):
  159. return 1
  160. def get_engine_driver_name(self, index):
  161. return "Plugin"
  162. def get_engine_driver_device_names(self, index):
  163. return []
  164. def get_engine_driver_device_info(self, index, name):
  165. return PyEngineDriverDeviceInfo
  166. # -------------------------------------------------------------------
  167. def get_internal_plugin_count(self):
  168. return 0
  169. def get_internal_plugin_info(self, index):
  170. return None
  171. # -------------------------------------------------------------------
  172. def engine_init(self, driverName, clientName):
  173. return True
  174. def engine_close(self):
  175. return True
  176. def engine_idle(self):
  177. if gCarla.gui.idleExternalUI():
  178. return
  179. self.fIsRunning = False
  180. gCarla.gui.d_uiQuit()
  181. def is_engine_running(self):
  182. return self.fIsRunning
  183. def set_engine_about_to_close(self):
  184. pass
  185. def set_engine_callback(self, func):
  186. pass
  187. def set_engine_option(self, option, value, valueStr):
  188. gCarla.gui.send(["set_engine_option", option, value, valueStr])
  189. def set_file_callback(self, func):
  190. pass
  191. # -------------------------------------------------------------------
  192. def set_file_callback(self, func):
  193. pass
  194. def load_file(self, filename):
  195. gCarla.gui.send(["load_file", filename])
  196. return True
  197. def load_project(self, filename):
  198. gCarla.gui.send(["load_project", filename])
  199. return True
  200. def save_project(self, filename):
  201. gCarla.gui.send(["save_project", filename])
  202. return True
  203. # -------------------------------------------------------------------
  204. def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB):
  205. gCarla.gui.send(["patchbay_connect", groupIdA, portIdA, groupIdB, portIdB])
  206. return True
  207. def patchbay_disconnect(self, connectionId):
  208. gCarla.gui.send(["patchbay_disconnect", connectionId])
  209. return True
  210. def patchbay_refresh(self):
  211. gCarla.gui.send(["patchbay_refresh"])
  212. return True
  213. # -------------------------------------------------------------------
  214. def transport_play(self):
  215. gCarla.gui.send(["transport_play"])
  216. def transport_pause(self):
  217. gCarla.gui.send(["transport_pause"])
  218. def transport_relocate(self, frame):
  219. gCarla.gui.send(["transport_relocate"])
  220. def get_current_transport_frame(self):
  221. return 0
  222. def get_transport_info(self):
  223. return PyCarlaTransportInfo
  224. # -------------------------------------------------------------------
  225. def add_plugin(self, btype, ptype, filename, name, label, uniqueId, extraPtr):
  226. gCarla.gui.send(["add_plugin", btype, ptype, filename, name, label, uniqueId])
  227. return True
  228. def remove_plugin(self, pluginId):
  229. gCarla.gui.send(["remove_plugin", pluginId])
  230. return True
  231. def remove_all_plugins(self):
  232. gCarla.gui.send(["remove_all_plugins"])
  233. return True
  234. def rename_plugin(self, pluginId, newName):
  235. gCarla.gui.send(["rename_plugin", pluginId, newName])
  236. return newName
  237. def clone_plugin(self, pluginId):
  238. gCarla.gui.send(["clone_plugin", pluginId])
  239. return True
  240. def replace_plugin(self, pluginId):
  241. gCarla.gui.send(["replace_plugin", pluginId])
  242. return True
  243. def switch_plugins(self, pluginIdA, pluginIdB):
  244. gCarla.gui.send(["switch_plugins", pluginIdA, pluginIdB])
  245. return True
  246. # -------------------------------------------------------------------
  247. def load_plugin_state(self, pluginId, filename):
  248. gCarla.gui.send(["load_plugin_state", pluginId, filename])
  249. return True
  250. def save_plugin_state(self, pluginId, filename):
  251. gCarla.gui.send(["save_plugin_state", pluginId, filename])
  252. return True
  253. # -------------------------------------------------------------------
  254. def get_plugin_info(self, pluginId):
  255. return self.fPluginsInfo[pluginId].pluginInfo
  256. def get_audio_port_count_info(self, pluginId):
  257. return self.fPluginsInfo[pluginId].audioCountInfo
  258. def get_midi_port_count_info(self, pluginId):
  259. return self.fPluginsInfo[pluginId].midiCountInfo
  260. def get_parameter_count_info(self, pluginId):
  261. return self.fPluginsInfo[pluginId].parameterCountInfo
  262. def get_parameter_info(self, pluginId, parameterId):
  263. return self.fPluginsInfo[pluginId].parameterInfoS[parameterId]
  264. def get_parameter_scalepoint_info(self, pluginId, parameterId, scalePointId):
  265. return PyCarlaScalePointInfo
  266. # -------------------------------------------------------------------
  267. def get_parameter_data(self, pluginId, parameterId):
  268. return self.fPluginsInfo[pluginId].parameterDataS[parameterId]
  269. def get_parameter_ranges(self, pluginId, parameterId):
  270. return self.fPluginsInfo[pluginId].parameterRangeS[parameterId]
  271. def get_midi_program_data(self, pluginId, midiProgramId):
  272. return self.fPluginsInfo[pluginId].midiProgramDataS[midiProgramId]
  273. def get_custom_data(self, pluginId, customDataId):
  274. return PyCustomData
  275. def get_chunk_data(self, pluginId):
  276. return ""
  277. # -------------------------------------------------------------------
  278. def get_parameter_count(self, pluginId):
  279. return self.fPluginsInfo[pluginId].parameterCount
  280. def get_program_count(self, pluginId):
  281. return self.fPluginsInfo[pluginId].programCount
  282. def get_midi_program_count(self, pluginId):
  283. return self.fPluginsInfo[pluginId].midiProgramCount
  284. def get_custom_data_count(self, pluginId):
  285. return 0
  286. # -------------------------------------------------------------------
  287. def get_parameter_text(self, pluginId, parameterId):
  288. return ""
  289. def get_program_name(self, pluginId, programId):
  290. return self.fPluginsInfo[pluginId].programNameS[programId]
  291. def get_midi_program_name(self, pluginId, midiProgramId):
  292. return self.fPluginsInfo[pluginId].midiProgramDataS[midiProgramId]['label']
  293. def get_real_plugin_name(self, pluginId):
  294. return self.fPluginsInfo[pluginId].pluginRealName
  295. # -------------------------------------------------------------------
  296. def get_current_program_index(self, pluginId):
  297. return self.fPluginsInfo[pluginId].programCurrent
  298. def get_current_midi_program_index(self, pluginId):
  299. return self.fPluginsInfo[pluginId].midiProgramCurrent
  300. def get_default_parameter_value(self, pluginId, parameterId):
  301. return self.fPluginsInfo[pluginId].parameterRangeS[parameterId]['def']
  302. def get_current_parameter_value(self, pluginId, parameterId):
  303. return self.fPluginsInfo[pluginId].parameterValueS[parameterId]
  304. def get_internal_parameter_value(self, pluginId, parameterId):
  305. if parameterId == PARAMETER_NULL or parameterId <= PARAMETER_MAX:
  306. return 0.0
  307. if parameterId < 0:
  308. return self.fPluginsInfo[pluginId].parameterValueInt[abs(parameterId)-2]
  309. return self.fPluginsInfo[pluginId].parameterValueS[parameterId]
  310. def get_input_peak_value(self, pluginId, isLeft):
  311. return self.fPluginsInfo[pluginId].peaks[0 if isLeft else 1]
  312. def get_output_peak_value(self, pluginId, isLeft):
  313. return self.fPluginsInfo[pluginId].peaks[2 if isLeft else 3]
  314. # -------------------------------------------------------------------
  315. def set_option(self, pluginId, option, yesNo):
  316. gCarla.gui.send(["set_option", pluginId, option, yesNo])
  317. def set_active(self, pluginId, onOff):
  318. gCarla.gui.send(["set_active", pluginId, onOff])
  319. def set_drywet(self, pluginId, value):
  320. gCarla.gui.send(["set_drywet", pluginId, value])
  321. def set_volume(self, pluginId, value):
  322. gCarla.gui.send(["set_volume", pluginId, value])
  323. def set_balance_left(self, pluginId, value):
  324. gCarla.gui.send(["set_balance_left", pluginId, value])
  325. def set_balance_right(self, pluginId, value):
  326. gCarla.gui.send(["set_balance_right", pluginId, value])
  327. def set_panning(self, pluginId, value):
  328. gCarla.gui.send(["set_panning", pluginId, value])
  329. def set_ctrl_channel(self, pluginId, channel):
  330. gCarla.gui.send(["set_ctrl_channel", pluginId, channel])
  331. # -------------------------------------------------------------------
  332. def set_parameter_value(self, pluginId, parameterId, value):
  333. gCarla.gui.send(["set_parameter_value", pluginId, parameterId, value])
  334. def set_parameter_midi_channel(self, pluginId, parameterId, channel):
  335. gCarla.gui.send(["set_parameter_midi_channel", pluginId, parameterId, channel])
  336. def set_parameter_midi_cc(self, pluginId, parameterId, cc):
  337. gCarla.gui.send(["set_parameter_midi_cc", pluginId, parameterId, cc])
  338. def set_program(self, pluginId, programId):
  339. gCarla.gui.send(["set_program", pluginId, programId])
  340. def set_midi_program(self, pluginId, midiProgramId):
  341. gCarla.gui.send(["set_midi_program", pluginId, midiProgramId])
  342. def set_custom_data(self, pluginId, type_, key, value):
  343. gCarla.gui.send(["set_custom_data", pluginId, type_, key, value])
  344. def set_chunk_data(self, pluginId, chunkData):
  345. gCarla.gui.send(["set_chunk_data", pluginId, chunkData])
  346. # -------------------------------------------------------------------
  347. def prepare_for_save(self, pluginId):
  348. gCarla.gui.send(["prepare_for_save", pluginId])
  349. def reset_parameters(self, pluginId):
  350. gCarla.gui.send(["reset_parameters", pluginId])
  351. def randomize_parameters(self, pluginId):
  352. gCarla.gui.send(["randomize_parameters", pluginId])
  353. def send_midi_note(self, pluginId, channel, note, velocity):
  354. gCarla.gui.send(["send_midi_note", pluginId, channel, note, velocity])
  355. def show_custom_ui(self, pluginId, yesNo):
  356. gCarla.gui.send(["show_custom_ui", pluginId, yesNo])
  357. # -------------------------------------------------------------------
  358. def get_buffer_size(self):
  359. return self.fBufferSize
  360. def get_sample_rate(self):
  361. return self.fSampleRate
  362. def get_last_error(self):
  363. return self.fLastError
  364. def get_host_osc_url_tcp(self):
  365. return ""
  366. def get_host_osc_url_udp(self):
  367. return ""
  368. # ------------------------------------------------------------------------------------------------------------
  369. # Main Window
  370. class CarlaMiniW(ExternalUI, HostWindow):
  371. def __init__(self):
  372. ExternalUI.__init__(self)
  373. HostWindow.__init__(self, None)
  374. if False:
  375. from carla_patchbay import CarlaPatchbayW
  376. self.fContainer = CarlaPatchbayW(self)
  377. else:
  378. from carla_rack import CarlaRackW
  379. self.fContainer = CarlaRackW(self)
  380. self.setupContainer(False)
  381. self.setWindowTitle(self.fUiName)
  382. self.showUiIfTesting()
  383. # -------------------------------------------------------------------
  384. # ExternalUI Callbacks
  385. def d_uiShow(self):
  386. self.show()
  387. def d_uiHide(self):
  388. self.hide()
  389. def d_uiQuit(self):
  390. self.close()
  391. app.quit()
  392. def d_uiTitleChanged(self, uiTitle):
  393. self.setWindowTitle(uiTitle)
  394. # -------------------------------------------------------------------
  395. # Qt events
  396. def closeEvent(self, event):
  397. self.closeExternalUI()
  398. HostWindow.closeEvent(self, event)
  399. # -------------------------------------------------------------------
  400. # Custom idler
  401. def idleExternalUI(self):
  402. while True:
  403. if self.fPipeRecv is None:
  404. return True
  405. try:
  406. msg = self.fPipeRecv.readline().strip()
  407. except IOError:
  408. return False
  409. if not msg:
  410. return True
  411. elif msg.startswith("PEAKS_"):
  412. pluginId = int(msg.replace("PEAKS_", ""))
  413. in1, in2, out1, out2 = [float(i) for i in self.fPipeRecv.readline().strip().split(":")]
  414. gCarla.host._set_peaks(pluginId, in1, in2, out1, out2)
  415. elif msg.startswith("PARAMVAL_"):
  416. pluginId, paramId = [int(i) for i in msg.replace("PARAMVAL_", "").split(":")]
  417. paramValue = float(self.fPipeRecv.readline().strip())
  418. gCarla.host._set_parameterValueS(pluginId, paramId, paramValue)
  419. elif msg.startswith("ENGINE_CALLBACK_"):
  420. action = int(msg.replace("ENGINE_CALLBACK_", ""))
  421. pluginId = int(self.fPipeRecv.readline().strip())
  422. value1 = int(self.fPipeRecv.readline().strip())
  423. value2 = int(self.fPipeRecv.readline().strip())
  424. value3 = float(self.fPipeRecv.readline().strip())
  425. valueStr = self.fPipeRecv.readline().strip().replace("\r", "\n")
  426. if action == ENGINE_CALLBACK_PLUGIN_RENAMED:
  427. gCarla.host._set_pluginName(pluginId, valueStr)
  428. elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
  429. if value1 < 0:
  430. gCarla.host._set_parameterValueInt(pluginId, value1, value3)
  431. else:
  432. gCarla.host._set_parameterValueS(pluginId, value1, value3)
  433. elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED:
  434. gCarla.host._set_parameterDefault(pluginId, value1, value3)
  435. elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  436. gCarla.host._set_parameterMidiCC(pluginId, value1, value2)
  437. elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  438. gCarla.host._set_parameterMidiChannel(pluginId, value1, value2)
  439. elif action == ENGINE_CALLBACK_PROGRAM_CHANGED:
  440. gCarla.host._set_currentProgram(pluginId, value1)
  441. elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED:
  442. gCarla.host._set_currentMidiProgram(pluginId, value1)
  443. engineCallback(None, action, pluginId, value1, value2, value3, valueStr)
  444. elif msg.startswith("PLUGIN_INFO_"):
  445. pluginId = int(msg.replace("PLUGIN_INFO_", ""))
  446. gCarla.host._add(pluginId)
  447. type_, category, hints, uniqueId, optsAvail, optsEnabled = [int(i) for i in self.fPipeRecv.readline().strip().split(":")]
  448. filename = self.fPipeRecv.readline().strip().replace("\r", "\n")
  449. name = self.fPipeRecv.readline().strip().replace("\r", "\n")
  450. iconName = self.fPipeRecv.readline().strip().replace("\r", "\n")
  451. realName = self.fPipeRecv.readline().strip().replace("\r", "\n")
  452. label = self.fPipeRecv.readline().strip().replace("\r", "\n")
  453. maker = self.fPipeRecv.readline().strip().replace("\r", "\n")
  454. copyright = self.fPipeRecv.readline().strip().replace("\r", "\n")
  455. pinfo = {
  456. 'type': type_,
  457. 'category': category,
  458. 'hints': hints,
  459. 'optionsAvailable': optsAvail,
  460. 'optionsEnabled': optsEnabled,
  461. 'filename': filename,
  462. 'name': name,
  463. 'label': label,
  464. 'maker': maker,
  465. 'copyright': copyright,
  466. 'iconName': iconName,
  467. 'patchbayClientId': 0,
  468. 'uniqueId': uniqueId
  469. }
  470. gCarla.host._set_pluginInfo(pluginId, pinfo)
  471. gCarla.host._set_pluginRealName(pluginId, realName)
  472. elif msg.startswith("AUDIO_COUNT_"):
  473. pluginId, ins, outs = [int(i) for i in msg.replace("AUDIO_COUNT_", "").split(":")]
  474. gCarla.host._set_audioCountInfo(pluginId, {'ins': ins, 'outs': outs})
  475. elif msg.startswith("MIDI_COUNT_"):
  476. pluginId, ins, outs = [int(i) for i in msg.replace("MIDI_COUNT_", "").split(":")]
  477. gCarla.host._set_midiCountInfo(pluginId, {'ins': ins, 'outs': outs})
  478. elif msg.startswith("PARAMETER_COUNT_"):
  479. pluginId, ins, outs, count = [int(i) for i in msg.replace("PARAMETER_COUNT_", "").split(":")]
  480. gCarla.host._set_parameterCountInfo(pluginId, count, {'ins': ins, 'outs': outs})
  481. elif msg.startswith("PARAMETER_DATA_"):
  482. pluginId, paramId = [int(i) for i in msg.replace("PARAMETER_DATA_", "").split(":")]
  483. paramType, paramHints, midiChannel, midiCC = [int(i) for i in self.fPipeRecv.readline().strip().split(":")]
  484. paramName = self.fPipeRecv.readline().strip().replace("\r", "\n")
  485. paramUnit = self.fPipeRecv.readline().strip().replace("\r", "\n")
  486. paramInfo = {
  487. 'name': paramName,
  488. 'symbol': "",
  489. 'unit': paramUnit,
  490. 'scalePointCount': 0,
  491. }
  492. gCarla.host._set_parameterInfoS(pluginId, paramId, paramInfo)
  493. paramData = {
  494. 'type': paramType,
  495. 'hints': paramHints,
  496. 'index': paramId,
  497. 'rindex': -1,
  498. 'midiCC': midiCC,
  499. 'midiChannel': midiChannel
  500. }
  501. gCarla.host._set_parameterDataS(pluginId, paramId, paramData)
  502. elif msg.startswith("PARAMETER_RANGES_"):
  503. pluginId, paramId = [int(i) for i in msg.replace("PARAMETER_RANGES_", "").split(":")]
  504. def_, min_, max_, step, stepSmall, stepLarge = [float(i) for i in self.fPipeRecv.readline().strip().split(":")]
  505. paramRanges = {
  506. 'def': def_,
  507. 'min': min_,
  508. 'max': max_,
  509. 'step': step,
  510. 'stepSmall': stepSmall,
  511. 'stepLarge': stepLarge
  512. }
  513. gCarla.host._set_parameterRangeS(pluginId, paramId, paramRanges)
  514. elif msg.startswith("PROGRAM_COUNT_"):
  515. pluginId, count, current = [int(i) for i in msg.replace("PROGRAM_COUNT_", "").split(":")]
  516. gCarla.host._set_programCount(pluginId, count)
  517. gCarla.host._set_currentProgram(pluginId, current)
  518. elif msg.startswith("PROGRAM_NAME_"):
  519. pluginId, progId = [int(i) for i in msg.replace("PROGRAM_NAME_", "").split(":")]
  520. progName = self.fPipeRecv.readline().strip().replace("\r", "\n")
  521. gCarla.host._set_programNameS(pluginId, progId, progName)
  522. elif msg.startswith("MIDI_PROGRAM_COUNT_"):
  523. pluginId, count, current = [int(i) for i in msg.replace("MIDI_PROGRAM_COUNT_", "").split(":")]
  524. gCarla.host._set_midiProgramCount(pluginId, count)
  525. gCarla.host._set_currentMidiProgram(pluginId, current)
  526. elif msg.startswith("MIDI_PROGRAM_DATA_"):
  527. pluginId, midiProgId = [int(i) for i in msg.replace("MIDI_PROGRAM_DATA_", "").split(":")]
  528. bank, program = [int(i) for i in self.fPipeRecv.readline().strip().split(":")]
  529. name = self.fPipeRecv.readline().strip().replace("\r", "\n")
  530. gCarla.host._set_midiProgramDataS(pluginId, midiProgId, {'bank': bank, 'program': program, 'name': name})
  531. elif msg == "error":
  532. error = self.fPipeRecv.readline().strip().replace("\r", "\n")
  533. engineCallback(None, ENGINE_CALLBACK_ERROR, 0, 0, 0, 0.0, error)
  534. elif msg == "show":
  535. self.d_uiShow()
  536. elif msg == "hide":
  537. self.d_uiHide()
  538. elif msg == "quit":
  539. self.fQuitReceived = True
  540. self.d_uiQuit()
  541. elif msg == "uiTitle":
  542. uiTitle = self.fPipeRecv.readline().strip().replace("\r", "\n")
  543. self.d_uiTitleChanged(uiTitle)
  544. else:
  545. print("unknown message: \"" + msg + "\"")
  546. return True
  547. # ------------------------------------------------------------------------------------------------------------
  548. # Main
  549. if __name__ == '__main__':
  550. # -------------------------------------------------------------
  551. # App initialization
  552. app = CarlaApplication("Carla2-Plugin")
  553. # -------------------------------------------------------------
  554. # Set-up custom signal handling
  555. setUpSignals()
  556. # -------------------------------------------------------------
  557. # Init plugin host data
  558. gCarla.isControl = False
  559. gCarla.isLocal = True
  560. gCarla.isPlugin = True
  561. gCarla.processMode = ENGINE_PROCESS_MODE_CONTINUOUS_RACK
  562. # -------------------------------------------------------------
  563. # Create GUI first
  564. gCarla.gui = CarlaMiniW()
  565. # -------------------------------------------------------------
  566. # Init plugin host now
  567. gCarla.host = PluginHost(gCarla.gui.d_getSampleRate())
  568. initHost("Carla-Plugin")
  569. # simulate an engire started callback
  570. engineCallback(None, ENGINE_CALLBACK_ENGINE_STARTED, 0, ENGINE_PROCESS_MODE_CONTINUOUS_RACK, ENGINE_TRANSPORT_MODE_PLUGIN, 0.0, "Plugin")
  571. # -------------------------------------------------------------
  572. # App-Loop
  573. ret = app.exec_()
  574. # disable parenting
  575. gCarla.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0")
  576. sys.exit(ret)