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_host_control.py 26KB

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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla Backend code (OSC stuff)
  4. # Copyright (C) 2011-2019 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 doc/GPL.txt file.
  17. # ----------------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. from PyQt5.QtCore import QEventLoop
  20. # ------------------------------------------------------------------------------------------------------------
  21. # Imports (Custom)
  22. import ui_carla_osc_connect
  23. from carla_backend_qt import CarlaHostQtPlugin
  24. from carla_host import *
  25. # ------------------------------------------------------------------------------------------------------------
  26. # Imports (liblo)
  27. from liblo import (
  28. Address,
  29. AddressError,
  30. ServerError,
  31. Server,
  32. make_method,
  33. send as lo_send,
  34. TCP as LO_TCP,
  35. UDP as LO_UDP,
  36. )
  37. from random import random
  38. # ------------------------------------------------------------------------------------------------------------
  39. DEBUG = False
  40. # ----------------------------------------------------------------------------------------------------------------------
  41. # OSC connect Dialog
  42. class ConnectDialog(QDialog):
  43. def __init__(self, parent):
  44. QDialog.__init__(self, parent)
  45. self.ui = ui_carla_osc_connect.Ui_Dialog()
  46. self.ui.setupUi(self)
  47. self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
  48. # -------------------------------------------------------------------------------------------------------------
  49. # Load settings
  50. self.loadSettings()
  51. # -------------------------------------------------------------------------------------------------------------
  52. # Set-up connections
  53. self.finished.connect(self.slot_saveSettings)
  54. self.ui.le_host.textChanged.connect(self.slot_hostChanged)
  55. # -----------------------------------------------------------------------------------------------------------------
  56. def getResult(self):
  57. return (self.ui.le_host.text(),
  58. self.ui.sb_tcp_port.value(),
  59. self.ui.sb_udp_port.value())
  60. def checkIfButtonBoxShouldBeEnabled(self, host):
  61. enabled = len(host) > 0
  62. self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enabled)
  63. def loadSettings(self):
  64. settings = QSafeSettings("falkTX", "CarlaOSCConnect")
  65. self.ui.le_host.setText(settings.value("Host", "127.0.0.1", str))
  66. self.ui.sb_tcp_port.setValue(settings.value("TCPPort", CARLA_DEFAULT_OSC_TCP_PORT_NUMBER, int))
  67. self.ui.sb_udp_port.setValue(settings.value("UDPPort", CARLA_DEFAULT_OSC_UDP_PORT_NUMBER, int))
  68. self.checkIfButtonBoxShouldBeEnabled(self.ui.le_host.text())
  69. # ------------------------------------------------------------------------------------------------------------------
  70. @pyqtSlot(str)
  71. def slot_hostChanged(self, text):
  72. self.checkIfButtonBoxShouldBeEnabled(text)
  73. @pyqtSlot()
  74. def slot_saveSettings(self):
  75. settings = QSafeSettings("falkTX", "CarlaOSCConnect")
  76. settings.setValue("Host", self.ui.le_host.text())
  77. settings.setValue("TCPPort", self.ui.sb_tcp_port.value())
  78. settings.setValue("UDPPort", self.ui.sb_udp_port.value())
  79. # ------------------------------------------------------------------------------------------------------------------
  80. def done(self, r):
  81. QDialog.done(self, r)
  82. self.close()
  83. # ------------------------------------------------------------------------------------------------------------
  84. # Host OSC object
  85. class CarlaHostOSC(CarlaHostQtPlugin):
  86. def __init__(self):
  87. CarlaHostQtPlugin.__init__(self)
  88. self.lo_server_tcp = None
  89. self.lo_server_udp = None
  90. self.lo_target_tcp = None
  91. self.lo_target_udp = None
  92. self.lo_target_tcp_name = ""
  93. self.lo_target_udp_name = ""
  94. self.resetPendingMessages()
  95. # -------------------------------------------------------------------
  96. def resetPendingMessages(self):
  97. self.lastMessageId = 1
  98. self.pendingMessages = []
  99. self.responses = {}
  100. def printAndReturnError(self, error):
  101. print(error)
  102. self.fLastError = error
  103. return False
  104. def sendMsg(self, lines):
  105. if len(lines) < 1:
  106. return self.printAndReturnError("not enough arguments")
  107. method = lines.pop(0)
  108. if method == "set_engine_option":
  109. return True
  110. if self.lo_target_tcp is None:
  111. return self.printAndReturnError("lo_target_tcp is None")
  112. if self.lo_target_tcp_name is None:
  113. return self.printAndReturnError("lo_target_tcp_name is None")
  114. if method in ("clear_engine_xruns",
  115. "cancel_engine_action",
  116. #"load_file",
  117. #"load_project",
  118. #"save_project",
  119. #"clear_project_filename",
  120. "patchbay_connect",
  121. "patchbay_disconnect",
  122. "patchbay_set_group_pos",
  123. "patchbay_refresh",
  124. "transport_play",
  125. "transport_pause",
  126. "transport_bpm",
  127. "transport_relocate",
  128. "add_plugin",
  129. "remove_plugin",
  130. "remove_all_plugins",
  131. "rename_plugin",
  132. "clone_plugin",
  133. "replace_plugin",
  134. "switch_plugins",
  135. #"load_plugin_state",
  136. #"save_plugin_state",
  137. ):
  138. path = "/ctrl/" + method
  139. needResp = True
  140. elif method in (#"set_option",
  141. "set_active",
  142. "set_drywet",
  143. "set_volume",
  144. "set_balance_left",
  145. "set_balance_right",
  146. "set_panning",
  147. #"set_ctrl_channel",
  148. "set_parameter_value",
  149. "set_parameter_midi_channel",
  150. "set_parameter_midi_cc",
  151. "set_program",
  152. "set_midi_program",
  153. #"set_custom_data",
  154. #"set_chunk_data",
  155. #"prepare_for_save",
  156. #"reset_parameters",
  157. #"randomize_parameters",
  158. ):
  159. pluginId = lines.pop(0)
  160. needResp = False
  161. path = "/%s/%i/%s" % (self.lo_target_tcp_name, pluginId, method)
  162. elif method == "send_midi_note":
  163. pluginId = lines.pop(0)
  164. needResp = False
  165. channel, note, velocity = lines
  166. if velocity:
  167. path = "/%s/%i/note_on" % (self.lo_target_tcp_name, pluginId)
  168. else:
  169. path = "/%s/%i/note_off" % (self.lo_target_tcp_name, pluginId)
  170. lines.pop(2)
  171. else:
  172. return self.printAndReturnError("invalid method '%s'" % method)
  173. if len(self.pendingMessages) != 0:
  174. return self.printAndReturnError("A previous operation is still pending, please wait")
  175. args = [int(line) if isinstance(line, bool) else line for line in lines]
  176. #print(path, args)
  177. if not needResp:
  178. lo_send(self.lo_target_tcp, path, *args)
  179. return True
  180. messageId = self.lastMessageId
  181. self.lastMessageId += 1
  182. self.pendingMessages.append(messageId)
  183. lo_send(self.lo_target_tcp, path, messageId, *args)
  184. while messageId in self.pendingMessages:
  185. QApplication.processEvents(QEventLoop.AllEvents, 100)
  186. error = self.responses.pop(messageId)
  187. if not error:
  188. return True
  189. self.fLastError = error
  190. return False
  191. def sendMsgAndSetError(self, lines):
  192. return self.sendMsg(lines)
  193. # -------------------------------------------------------------------
  194. def engine_init(self, driverName, clientName):
  195. return self.lo_target_tcp is not None
  196. def engine_close(self):
  197. return True
  198. def engine_idle(self):
  199. return
  200. def is_engine_running(self):
  201. return self.lo_target_tcp is not None
  202. def set_engine_about_to_close(self):
  203. return
  204. # ---------------------------------------------------------------------------------------------------------------------
  205. # OSC Control server
  206. class CarlaControlServerTCP(Server):
  207. def __init__(self, host):
  208. Server.__init__(self, proto=LO_TCP)
  209. if False:
  210. host = CarlaHostOSC()
  211. self.host = host
  212. def idle(self):
  213. self.fReceivedMsgs = False
  214. while self.recv(0) and self.fReceivedMsgs:
  215. pass
  216. def getFullURL(self):
  217. return "%sctrl" % self.get_url()
  218. @make_method('/ctrl/cb', 'iiiiifs')
  219. def carla_cb(self, path, args):
  220. if DEBUG: print(path, args)
  221. self.fReceivedMsgs = True
  222. action, pluginId, value1, value2, value3, valuef, valueStr = args
  223. self.host._setViaCallback(action, pluginId, value1, value2, value3, valuef, valueStr)
  224. engineCallback(self.host, action, pluginId, value1, value2, value3, valuef, valueStr)
  225. @make_method('/ctrl/info', 'iiiihiisssssss')
  226. def carla_info(self, path, args):
  227. if DEBUG: print(path, args)
  228. self.fReceivedMsgs = True
  229. (
  230. pluginId, type_, category, hints, uniqueId, optsAvail, optsEnabled,
  231. name, filename, iconName, realName, label, maker, copyright,
  232. ) = args
  233. hints &= ~PLUGIN_HAS_CUSTOM_UI
  234. pinfo = {
  235. 'type': type_,
  236. 'category': category,
  237. 'hints': hints,
  238. 'optionsAvailable': optsAvail,
  239. 'optionsEnabled': optsEnabled,
  240. 'uniqueId': uniqueId,
  241. 'filename': filename,
  242. 'name': name,
  243. 'label': label,
  244. 'maker': maker,
  245. 'copyright': copyright,
  246. 'iconName': iconName
  247. }
  248. self.host._set_pluginInfoUpdate(pluginId, pinfo)
  249. self.host._set_pluginRealName(pluginId, realName)
  250. @make_method('/ctrl/ports', 'iiiiiiii')
  251. def carla_ports(self, path, args):
  252. if DEBUG: print(path, args)
  253. self.fReceivedMsgs = True
  254. pluginId, audioIns, audioOuts, midiIns, midiOuts, paramIns, paramOuts, paramTotal = args
  255. self.host._set_audioCountInfo(pluginId, {'ins': audioIns, 'outs': audioOuts})
  256. self.host._set_midiCountInfo(pluginId, {'ins': midiOuts, 'outs': midiOuts})
  257. self.host._set_parameterCountInfo(pluginId, paramTotal, {'ins': paramIns, 'outs': paramOuts})
  258. @make_method('/ctrl/paramInfo', 'iissss')
  259. def carla_paramInfo(self, path, args):
  260. if DEBUG: print(path, args)
  261. self.fReceivedMsgs = True
  262. pluginId, paramId, name, unit, comment, groupName = args
  263. paramInfo = {
  264. 'name': name,
  265. 'symbol': "",
  266. 'unit': unit,
  267. 'comment': comment,
  268. 'groupName': groupName,
  269. 'scalePointCount': 0,
  270. 'scalePoints': [],
  271. }
  272. self.host._set_parameterInfo(pluginId, paramId, paramInfo)
  273. @make_method('/ctrl/paramData', 'iiiiiifff')
  274. def carla_paramData(self, path, args):
  275. if DEBUG: print(path, args)
  276. self.fReceivedMsgs = True
  277. pluginId, paramId, type_, hints, midiChan, mappedCtrl, mappedMin, mappedMax, value = args
  278. hints &= ~(PARAMETER_USES_SCALEPOINTS | PARAMETER_USES_CUSTOM_TEXT)
  279. paramData = {
  280. 'type': type_,
  281. 'hints': hints,
  282. 'index': paramId,
  283. 'rindex': -1,
  284. 'midiChannel': midiChan,
  285. 'mappedControlIndex': mappedCtrl,
  286. 'mappedMinimum': mappedMin,
  287. 'mappedMaximum': mappedMax,
  288. }
  289. self.host._set_parameterData(pluginId, paramId, paramData)
  290. self.host._set_parameterValue(pluginId, paramId, value)
  291. @make_method('/ctrl/paramRanges', 'iiffffff')
  292. def carla_paramRanges(self, path, args):
  293. if DEBUG: print(path, args)
  294. self.fReceivedMsgs = True
  295. pluginId, paramId, def_, min_, max_, step, stepSmall, stepLarge = args
  296. paramRanges = {
  297. 'def': def_,
  298. 'min': min_,
  299. 'max': max_,
  300. 'step': step,
  301. 'stepSmall': stepSmall,
  302. 'stepLarge': stepLarge,
  303. }
  304. self.host._set_parameterRanges(pluginId, paramId, paramRanges)
  305. @make_method('/ctrl/count', 'iiiiii')
  306. def carla_count(self, path, args):
  307. if DEBUG: print(path, args)
  308. self.fReceivedMsgs = True
  309. pluginId, pcount, mpcount, cdcount, cp, cmp = args
  310. self.host._set_programCount(pluginId, pcount)
  311. self.host._set_midiProgramCount(pluginId, mpcount)
  312. self.host._set_customDataCount(pluginId, cdcount)
  313. self.host._set_pluginInfoUpdate(pluginId, { 'programCurrent': cp, 'midiProgramCurrent': cmp })
  314. @make_method('/ctrl/pcount', 'iii')
  315. def carla_pcount(self, path, args):
  316. if DEBUG: print(path, args)
  317. self.fReceivedMsgs = True
  318. pluginId, pcount, mpcount = args
  319. self.host._set_programCount(pluginId, pcount)
  320. self.host._set_midiProgramCount(pluginId, mpcount)
  321. @make_method('/ctrl/prog', 'iis')
  322. def carla_prog(self, path, args):
  323. if DEBUG: print(path, args)
  324. self.fReceivedMsgs = True
  325. pluginId, progId, progName = args
  326. self.host._set_programName(pluginId, progId, progName)
  327. @make_method('/ctrl/mprog', 'iiiis')
  328. def carla_mprog(self, path, args):
  329. if DEBUG: print(path, args)
  330. self.fReceivedMsgs = True
  331. pluginId, midiProgId, bank, program, name = args
  332. self.host._set_midiProgramData(pluginId, midiProgId, {'bank': bank, 'program': program, 'name': name})
  333. @make_method('/ctrl/cdata', 'iisss')
  334. def carla_cdata(self, path, args):
  335. if DEBUG: print(path, args)
  336. self.fReceivedMsgs = True
  337. pluginId, index, type_, key, value = args
  338. self.host._set_customData(pluginId, index, { 'type': type_, 'key': key, 'value': value })
  339. @make_method('/ctrl/iparams', 'ifffffff')
  340. def carla_iparams(self, path, args):
  341. if DEBUG: print(path, args)
  342. self.fReceivedMsgs = True
  343. pluginId, active, drywet, volume, balLeft, balRight, pan, ctrlChan = args
  344. self.host._set_internalValue(pluginId, PARAMETER_ACTIVE, active)
  345. self.host._set_internalValue(pluginId, PARAMETER_DRYWET, drywet)
  346. self.host._set_internalValue(pluginId, PARAMETER_VOLUME, volume)
  347. self.host._set_internalValue(pluginId, PARAMETER_BALANCE_LEFT, balLeft)
  348. self.host._set_internalValue(pluginId, PARAMETER_BALANCE_RIGHT, balRight)
  349. self.host._set_internalValue(pluginId, PARAMETER_PANNING, pan)
  350. self.host._set_internalValue(pluginId, PARAMETER_CTRL_CHANNEL, ctrlChan)
  351. @make_method('/ctrl/resp', 'is')
  352. def carla_resp(self, path, args):
  353. if DEBUG: print(path, args)
  354. self.fReceivedMsgs = True
  355. messageId, error = args
  356. self.host.responses[messageId] = error
  357. self.host.pendingMessages.remove(messageId)
  358. @make_method('/ctrl/exit', '')
  359. def carla_exit(self, path, args):
  360. if DEBUG: print(path, args)
  361. self.fReceivedMsgs = True
  362. #self.host.lo_target_tcp = None
  363. self.host.QuitCallback.emit()
  364. @make_method('/ctrl/exit-error', 's')
  365. def carla_exit_error(self, path, args):
  366. if DEBUG: print(path, args)
  367. self.fReceivedMsgs = True
  368. error, = args
  369. self.host.lo_target_tcp = None
  370. self.host.QuitCallback.emit()
  371. self.host.ErrorCallback.emit(error)
  372. @make_method(None, None)
  373. def fallback(self, path, args):
  374. print("ControlServerTCP::fallback(\"%s\") - unknown message, args =" % path, args)
  375. self.fReceivedMsgs = True
  376. # ---------------------------------------------------------------------------------------------------------------------
  377. class CarlaControlServerUDP(Server):
  378. def __init__(self, host):
  379. Server.__init__(self, proto=LO_UDP)
  380. if False:
  381. host = CarlaHostOSC()
  382. self.host = host
  383. def idle(self):
  384. self.fReceivedMsgs = False
  385. while self.recv(0) and self.fReceivedMsgs:
  386. pass
  387. def getFullURL(self):
  388. return "%sctrl" % self.get_url()
  389. @make_method('/ctrl/runtime', 'fiihiiif')
  390. def carla_runtime(self, path, args):
  391. self.fReceivedMsgs = True
  392. load, xruns, playing, frame, bar, beat, tick, bpm = args
  393. self.host._set_runtime_info(load, xruns)
  394. self.host._set_transport(bool(playing), frame, bar, beat, tick, bpm)
  395. @make_method('/ctrl/param', 'iif')
  396. def carla_param_fixme(self, path, args):
  397. self.fReceivedMsgs = True
  398. pluginId, paramId, paramValue = args
  399. self.host._set_parameterValue(pluginId, paramId, paramValue)
  400. @make_method('/ctrl/peaks', 'iffff')
  401. def carla_peaks(self, path, args):
  402. self.fReceivedMsgs = True
  403. pluginId, in1, in2, out1, out2 = args
  404. self.host._set_peaks(pluginId, in1, in2, out1, out2)
  405. @make_method(None, None)
  406. def fallback(self, path, args):
  407. print("ControlServerUDP::fallback(\"%s\") - unknown message, args =" % path, args)
  408. self.fReceivedMsgs = True
  409. # ---------------------------------------------------------------------------------------------------------------------
  410. # Main Window
  411. class HostWindowOSC(HostWindow):
  412. def __init__(self, host, oscAddr = None):
  413. self.fCustomOscAddress = oscAddr
  414. HostWindow.__init__(self, host, True)
  415. self.host = host
  416. if False:
  417. # kdevelop likes this :)
  418. host = CarlaHostOSC()
  419. self.host = host
  420. # ----------------------------------------------------------------------------------------------------
  421. # Connect actions to functions
  422. self.ui.act_file_connect.triggered.connect(self.slot_fileConnect)
  423. self.ui.act_file_refresh.triggered.connect(self.slot_fileRefresh)
  424. # ----------------------------------------------------------------------------------------------------
  425. # Final setup
  426. if oscAddr:
  427. QTimer.singleShot(0, self.connectOsc)
  428. def connectOsc(self, addrTCP = None, addrUDP = None):
  429. if self.fCustomOscAddress is not None:
  430. addrTCP = self.fCustomOscAddress.replace("osc.udp://","osc.tcp://")
  431. addrUDP = self.fCustomOscAddress.replace("osc.tcp://","osc.udp://")
  432. else:
  433. if addrTCP is not None:
  434. self.fOscAddressTCP = addrTCP
  435. if addrUDP is not None:
  436. self.fOscAddressUDP = addrUDP
  437. lo_target_tcp_name = addrTCP.rsplit("/", 1)[-1]
  438. lo_target_udp_name = addrUDP.rsplit("/", 1)[-1]
  439. err = None
  440. try:
  441. lo_target_tcp = Address(addrTCP)
  442. lo_server_tcp = CarlaControlServerTCP(self.host)
  443. lo_send(lo_target_tcp, "/register", lo_server_tcp.getFullURL())
  444. lo_target_udp = Address(addrUDP)
  445. lo_server_udp = CarlaControlServerUDP(self.host)
  446. lo_send(lo_target_udp, "/register", lo_server_udp.getFullURL())
  447. except AddressError as e:
  448. err = e
  449. except OSError as e:
  450. err = e
  451. except:
  452. err = Exception()
  453. if err is not None:
  454. fullError = self.tr("Failed to connect to the Carla instance.")
  455. if len(err.args) > 0:
  456. fullError += " %s\n%s\n" % (self.tr("Error was:"), err.args[0])
  457. fullError += "\n"
  458. fullError += self.tr("Make sure the remote Carla is running and the URL and Port are correct.") + "\n"
  459. fullError += self.tr("If it still does not work, check your current device and the remote's firewall.")
  460. CustomMessageBox(self,
  461. QMessageBox.Warning,
  462. self.tr("Error"),
  463. self.tr("Connection failed"),
  464. fullError,
  465. QMessageBox.Ok,
  466. QMessageBox.Ok)
  467. return
  468. self.host.lo_server_tcp = lo_server_tcp
  469. self.host.lo_target_tcp = lo_target_tcp
  470. self.host.lo_target_tcp_name = lo_target_tcp_name
  471. self.host.lo_server_udp = lo_server_udp
  472. self.host.lo_target_udp = lo_target_udp
  473. self.host.lo_target_udp_name = lo_target_udp_name
  474. self.ui.act_file_refresh.setEnabled(True)
  475. self.startTimers()
  476. def disconnectOsc(self):
  477. self.killTimers()
  478. self.unregister()
  479. self.removeAllPlugins()
  480. patchcanvas.clear()
  481. self.ui.act_file_refresh.setEnabled(False)
  482. # --------------------------------------------------------------------------------------------------------
  483. def unregister(self):
  484. if self.host.lo_server_tcp is not None:
  485. if self.host.lo_target_tcp is not None:
  486. try:
  487. lo_send(self.host.lo_target_tcp, "/unregister", self.host.lo_server_tcp.getFullURL())
  488. except:
  489. pass
  490. self.host.lo_target_tcp = None
  491. while self.host.lo_server_tcp.recv(0):
  492. pass
  493. #self.host.lo_server_tcp.free()
  494. self.host.lo_server_tcp = None
  495. if self.host.lo_server_udp is not None:
  496. if self.host.lo_target_udp is not None:
  497. try:
  498. lo_send(self.host.lo_target_udp, "/unregister", self.host.lo_server_udp.getFullURL())
  499. except:
  500. pass
  501. self.host.lo_target_udp = None
  502. while self.host.lo_server_udp.recv(0):
  503. pass
  504. #self.host.lo_server_udp.free()
  505. self.host.lo_server_udp = None
  506. self.host.lo_target_tcp_name = ""
  507. self.host.lo_target_udp_name = ""
  508. # --------------------------------------------------------------------------------------------------------
  509. # Timers
  510. def idleFast(self):
  511. HostWindow.idleFast(self)
  512. if self.host.lo_server_tcp is not None:
  513. self.host.lo_server_tcp.idle()
  514. else:
  515. self.disconnectOsc()
  516. if self.host.lo_server_udp is not None:
  517. self.host.lo_server_udp.idle()
  518. else:
  519. self.disconnectOsc()
  520. # --------------------------------------------------------------------------------------------------------
  521. def removeAllPlugins(self):
  522. self.host.fPluginsInfo = {}
  523. HostWindow.removeAllPlugins(self)
  524. # --------------------------------------------------------------------------------------------------------
  525. def loadSettings(self, firstTime):
  526. settings = HostWindow.loadSettings(self, firstTime)
  527. if self.fCustomOscAddress is not None:
  528. self.fOscAddressTCP = settings.value("RemoteAddressTCP", "osc.tcp://127.0.0.1:22752/Carla", str)
  529. self.fOscAddressUDP = settings.value("RemoteAddressUDP", "osc.udp://127.0.0.1:22752/Carla", str)
  530. def saveSettings(self):
  531. settings = HostWindow.saveSettings(self)
  532. if self.fOscAddressTCP:
  533. settings.setValue("RemoteAddressTCP", self.fOscAddressTCP)
  534. if self.fOscAddressUDP:
  535. settings.setValue("RemoteAddressUDP", self.fOscAddressUDP)
  536. # --------------------------------------------------------------------------------------------------------
  537. @pyqtSlot()
  538. def slot_fileConnect(self):
  539. dialog = ConnectDialog(self)
  540. if not dialog.exec_():
  541. return
  542. host, tcpPort, udpPort = dialog.getResult()
  543. self.disconnectOsc()
  544. self.connectOsc("osc.tcp://%s:%i/Carla" % (host, tcpPort),
  545. "osc.udp://%s:%i/Carla" % (host, udpPort))
  546. @pyqtSlot()
  547. def slot_fileRefresh(self):
  548. if None in (self.host.lo_server_tcp, self.host.lo_server_udp, self.host.lo_target_tcp, self.host.lo_target_udp):
  549. return
  550. lo_send(self.host.lo_target_udp, "/unregister", self.host.lo_server_udp.getFullURL())
  551. while self.host.lo_server_udp.recv(0):
  552. pass
  553. #self.host.lo_server_udp.free()
  554. lo_send(self.host.lo_target_tcp, "/unregister", self.host.lo_server_tcp.getFullURL())
  555. while self.host.lo_server_tcp.recv(0):
  556. pass
  557. #self.host.lo_server_tcp.free()
  558. self.removeAllPlugins()
  559. patchcanvas.clear()
  560. self.host.lo_server_tcp = CarlaControlServerTCP(self.host)
  561. self.host.lo_server_udp = CarlaControlServerUDP(self.host)
  562. try:
  563. lo_send(self.host.lo_target_tcp, "/register", self.host.lo_server_tcp.getFullURL())
  564. except:
  565. self.disconnectOsc()
  566. return
  567. try:
  568. lo_send(self.host.lo_target_udp, "/register", self.host.lo_server_udp.getFullURL())
  569. except:
  570. self.disconnectOsc()
  571. return
  572. # --------------------------------------------------------------------------------------------------------
  573. @pyqtSlot()
  574. def slot_handleSIGTERM(self):
  575. print("Got SIGTERM -> Closing now")
  576. self.host.pendingMessages = []
  577. self.close()
  578. @pyqtSlot()
  579. def slot_handleQuitCallback(self):
  580. self.disconnectOsc()
  581. HostWindow.slot_handleQuitCallback(self)
  582. # --------------------------------------------------------------------------------------------------------
  583. def closeEvent(self, event):
  584. self.killTimers()
  585. self.unregister()
  586. HostWindow.closeEvent(self, event)
  587. # ------------------------------------------------------------------------------------------------------------