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