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.

externalui.py 6.6KB

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