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.

231 lines
6.7KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # External UI
  4. # Copyright (C) 2013-2020 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 (Custom Stuff)
  19. from carla_backend import charPtrToString
  20. from carla_shared import *
  21. # ------------------------------------------------------------------------------------------------------------
  22. # External UI
  23. class ExternalUI(object):
  24. def __init__(self):
  25. object.__init__(self)
  26. self.fQuitReceived = False
  27. if len(sys.argv) > 1:
  28. self.fSampleRate = float(sys.argv[1])
  29. self.fUiName = sys.argv[2]
  30. self.fPipeClient = gCarla.utils.pipe_client_new(lambda s,msg: self.msgCallback(msg))
  31. else:
  32. self.fSampleRate = 44100.0
  33. self.fUiName = "TestUI"
  34. self.fPipeClient = None
  35. # -------------------------------------------------------------------
  36. # Public methods
  37. def ready(self):
  38. if self.fPipeClient is None:
  39. # testing, show UI only
  40. self.uiShow()
  41. def isRunning(self):
  42. if self.fPipeClient is not None:
  43. return gCarla.utils.pipe_client_is_running(self.fPipeClient)
  44. return False
  45. def idleExternalUI(self):
  46. if self.fPipeClient is not None:
  47. gCarla.utils.pipe_client_idle(self.fPipeClient)
  48. def closeExternalUI(self):
  49. if self.fPipeClient is None:
  50. return
  51. # FIXME
  52. if not self.fQuitReceived:
  53. self.send(["exiting"])
  54. gCarla.utils.pipe_client_destroy(self.fPipeClient)
  55. self.fPipeClient = None
  56. # -------------------------------------------------------------------
  57. # Host DSP State
  58. def getSampleRate(self):
  59. return self.fSampleRate
  60. def sendControl(self, index, value):
  61. self.send(["control", index, value])
  62. def sendProgram(self, channel, bank, program):
  63. self.send(["program", channel, bank, program])
  64. def sendConfigure(self, key, value):
  65. self.send(["configure", key, value])
  66. def sendNote(self, onOff, channel, note, velocity):
  67. self.send(["note", onOff, channel, note, velocity])
  68. # -------------------------------------------------------------------
  69. # DSP Callbacks
  70. def dspParameterChanged(self, index, value):
  71. return
  72. def dspProgramChanged(self, channel, bank, program):
  73. return
  74. def dspStateChanged(self, key, value):
  75. return
  76. def dspNoteReceived(self, onOff, channel, note, velocity):
  77. return
  78. # -------------------------------------------------------------------
  79. # ExternalUI Callbacks
  80. def uiShow(self):
  81. return
  82. def uiFocus(self):
  83. return
  84. def uiHide(self):
  85. return
  86. def uiQuit(self):
  87. self.closeExternalUI()
  88. def uiTitleChanged(self, uiTitle):
  89. return
  90. # -------------------------------------------------------------------
  91. # Callback
  92. def msgCallback(self, msg):
  93. msg = charPtrToString(msg)
  94. #if not msg:
  95. #return
  96. if msg == "control":
  97. index = self.readlineblock_int()
  98. value = self.readlineblock_float()
  99. self.dspParameterChanged(index, value)
  100. elif msg == "program":
  101. channel = self.readlineblock_int()
  102. bank = self.readlineblock_int()
  103. program = self.readlineblock_int()
  104. self.dspProgramChanged(channel, bank, program)
  105. elif msg == "configure":
  106. key = self.readlineblock()
  107. value = self.readlineblock()
  108. self.dspStateChanged(key, value)
  109. elif msg == "note":
  110. onOff = self.readlineblock_bool()
  111. channel = self.readlineblock_int()
  112. note = self.readlineblock_int()
  113. velocity = self.readlineblock_int()
  114. self.dspNoteReceived(onOff, channel, note, velocity)
  115. elif msg == "show":
  116. self.uiShow()
  117. elif msg == "focus":
  118. self.uiFocus()
  119. elif msg == "hide":
  120. self.uiHide()
  121. elif msg == "quit":
  122. self.fQuitReceived = True
  123. self.uiQuit()
  124. elif msg == "uiTitle":
  125. uiTitle = self.readlineblock()
  126. self.uiTitleChanged(uiTitle)
  127. else:
  128. print("unknown message: \"" + msg + "\"")
  129. # -------------------------------------------------------------------
  130. # Internal stuff
  131. def readlineblock(self):
  132. if self.fPipeClient is None:
  133. return ""
  134. return gCarla.utils.pipe_client_readlineblock(self.fPipeClient, 5000)
  135. def readlineblock_bool(self):
  136. if self.fPipeClient is None:
  137. return False
  138. return gCarla.utils.pipe_client_readlineblock_bool(self.fPipeClient, 5000)
  139. def readlineblock_int(self):
  140. if self.fPipeClient is None:
  141. return 0
  142. return gCarla.utils.pipe_client_readlineblock_int(self.fPipeClient, 5000)
  143. def readlineblock_float(self):
  144. if self.fPipeClient is None:
  145. return 0.0
  146. return gCarla.utils.pipe_client_readlineblock_float(self.fPipeClient, 5000)
  147. def send(self, lines):
  148. if self.fPipeClient is None or len(lines) == 0:
  149. return False
  150. hasError = False
  151. gCarla.utils.pipe_client_lock(self.fPipeClient)
  152. try:
  153. for line in lines:
  154. if line is None:
  155. line2 = "(null)"
  156. elif isinstance(line, str):
  157. line2 = line.replace("\n", "\r")
  158. elif isinstance(line, bool):
  159. line2 = "true" if line else "false"
  160. elif isinstance(line, int):
  161. line2 = "%i" % line
  162. elif isinstance(line, float):
  163. line2 = "%.10f" % line
  164. else:
  165. print("unknown data type to send:", type(line))
  166. hasError = True
  167. break
  168. if not gCarla.utils.pipe_client_write_msg(self.fPipeClient, line2 + "\n"):
  169. hasError = True
  170. break
  171. finally:
  172. return gCarla.utils.pipe_client_flush_and_unlock(self.fPipeClient) and not hasError