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.

760 lines
26KB

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