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.

235 lines
6.7KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # External 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 doc/GPL.txt file.
  17. # -----------------------------------------------------------------------
  18. # Imports
  19. from os import fdopen, O_NONBLOCK
  20. from fcntl import fcntl, F_GETFL, F_SETFL
  21. from sys import argv
  22. from time import sleep
  23. # -----------------------------------------------------------------------
  24. # External UI
  25. class ExternalUI(object):
  26. def __init__(self):
  27. object.__init__(self)
  28. self.fPipeRecv = None
  29. self.fPipeSend = None
  30. self.fQuitReceived = False
  31. if len(argv) > 1:
  32. self.fSampleRate = float(argv[1])
  33. self.fUiName = argv[2]
  34. pipeRecvServer = int(argv[3])
  35. pipeRecvClient = int(argv[4])
  36. pipeSendServer = int(argv[5])
  37. pipeSendClient = int(argv[6])
  38. oldFlags = fcntl(pipeRecvServer, F_GETFL)
  39. fcntl(pipeRecvServer, F_SETFL, oldFlags | O_NONBLOCK)
  40. self.fPipeRecv = fdopen(pipeRecvServer, 'r')
  41. self.fPipeSend = fdopen(pipeSendServer, 'w')
  42. else:
  43. self.fSampleRate = 44100.0
  44. self.fUiName = "TestUI"
  45. self.fPipeRecv = None
  46. self.fPipeSend = None
  47. def ready(self):
  48. if self.fPipeRecv is not None:
  49. # send empty line (just newline char)
  50. self.send([""])
  51. else:
  52. # testing, show UI only
  53. self.d_uiShow()
  54. # -------------------------------------------------------------------
  55. # Host DSP State
  56. def d_getSampleRate(self):
  57. return self.fSampleRate
  58. def d_editParameter(self, index, started):
  59. self.send(["editParam", index, started])
  60. def d_setParameterValue(self, index, value):
  61. self.send(["control", index, value])
  62. def d_setProgram(self, channel, bank, program):
  63. self.send(["program", channel, bank, program])
  64. def d_setState(self, key, value):
  65. self.send(["configure", key, value])
  66. def d_sendNote(self, onOff, channel, note, velocity):
  67. self.send(["note", onOff, channel, note, velocity])
  68. # -------------------------------------------------------------------
  69. # DSP Callbacks
  70. def d_parameterChanged(self, index, value):
  71. return
  72. def d_programChanged(self, channel, bank, program):
  73. return
  74. def d_stateChanged(self, key, value):
  75. return
  76. def d_noteReceived(self, onOff, channel, note, velocity):
  77. return
  78. # -------------------------------------------------------------------
  79. # ExternalUI Callbacks
  80. def d_uiShow(self):
  81. return
  82. def d_uiHide(self):
  83. return
  84. def d_uiQuit(self):
  85. return
  86. def d_uiTitleChanged(self, uiTitle):
  87. return
  88. # -------------------------------------------------------------------
  89. # Public methods
  90. def closeExternalUI(self):
  91. if not self.fQuitReceived:
  92. self.send(["exiting"])
  93. if self.fPipeRecv is not None:
  94. self.fPipeRecv.close()
  95. self.fPipeRecv = None
  96. if self.fPipeSend is not None:
  97. self.fPipeSend.close()
  98. self.fPipeSend = None
  99. def idleExternalUI(self):
  100. while True:
  101. if self.fPipeRecv is None:
  102. return True
  103. try:
  104. msg = self.fPipeRecv.readline().strip()
  105. except IOError:
  106. return False
  107. if not msg:
  108. return True
  109. elif msg == "control":
  110. index = int(self.readlineblock())
  111. value = float(self.readlineblock())
  112. self.d_parameterChanged(index, value)
  113. elif msg == "program":
  114. channel = int(self.readlineblock())
  115. bank = int(self.readlineblock())
  116. program = int(self.readlineblock())
  117. self.d_programChanged(channel, bank, program)
  118. elif msg == "configure":
  119. key = self.readlineblock().replace("\r", "\n")
  120. value = self.readlineblock().replace("\r", "\n")
  121. self.d_stateChanged(key, value)
  122. elif msg == "note":
  123. onOff = bool(self.readlineblock() == "true")
  124. channel = int(self.readlineblock())
  125. note = int(self.readlineblock())
  126. velocity = int(self.readlineblock())
  127. self.d_noteReceived(onOff, channel, note, velocity)
  128. elif msg == "show":
  129. self.d_uiShow()
  130. elif msg == "hide":
  131. self.d_uiHide()
  132. elif msg == "quit":
  133. self.fQuitReceived = True
  134. self.d_uiQuit()
  135. elif msg == "uiTitle":
  136. uiTitle = self.readlineblock().replace("\r", "\n")
  137. self.d_uiTitleChanged(uiTitle)
  138. else:
  139. print("unknown message: \"" + msg + "\"")
  140. return True
  141. # -------------------------------------------------------------------
  142. # Internal stuff
  143. def readlineblock(self):
  144. if self.fPipeRecv is None:
  145. return ""
  146. # try a maximum of 20 times
  147. # 20 * 50ms = 1000ms
  148. for x in range(20):
  149. try:
  150. msg = self.fPipeRecv.readline()
  151. except IOError:
  152. msg = ""
  153. if msg:
  154. return msg.strip()
  155. # try again in 50 ms
  156. sleep(0.050)
  157. print("readlineblock timed out")
  158. return ""
  159. def send(self, lines):
  160. if self.fPipeSend is None:
  161. return
  162. for line in lines:
  163. if line is None:
  164. line2 = "(null)"
  165. elif isinstance(line, str):
  166. line2 = line.replace("\n", "\r")
  167. else:
  168. if isinstance(line, bool):
  169. line2 = "true" if line else "false"
  170. elif isinstance(line, int):
  171. line2 = "%i" % line
  172. elif isinstance(line, float):
  173. line2 = "%.10f" % line
  174. else:
  175. print("unknown data type to send:", type(line))
  176. return
  177. self.fPipeSend.write(line2 + "\n")
  178. self.fPipeSend.flush()