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.

209 lines
6.2KB

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