Cleanup
[carla.git] / source / frontend / externalui.py
blob922aa4861a2d6ebfe86ce59392177053dafb070b
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 # External UI
5 # Copyright (C) 2013-2020 Filipe Coelho <falktx@falktx.com>
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation; either version 2 of
10 # the License, or any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # For a full copy of the GNU General Public License see the doc/GPL.txt file.
19 # ------------------------------------------------------------------------------------------------------------
20 # Imports (Custom Stuff)
22 from carla_backend import charPtrToString
23 from carla_shared import *
25 # ------------------------------------------------------------------------------------------------------------
26 # External UI
28 class ExternalUI(object):
29 def __init__(self):
30 object.__init__(self)
32 self.fQuitReceived = False
34 if len(sys.argv) > 1:
35 self.fSampleRate = float(sys.argv[1])
36 self.fUiName = sys.argv[2]
37 self.fPipeClient = gCarla.utils.pipe_client_new(lambda s,msg: self.msgCallback(msg))
38 else:
39 self.fSampleRate = 44100.0
40 self.fUiName = "TestUI"
41 self.fPipeClient = None
43 # -------------------------------------------------------------------
44 # Public methods
46 def ready(self):
47 if self.fPipeClient is None:
48 # testing, show UI only
49 self.uiShow()
51 def isRunning(self):
52 if self.fPipeClient is not None:
53 return gCarla.utils.pipe_client_is_running(self.fPipeClient)
54 return False
56 def idleExternalUI(self):
57 if self.fPipeClient is not None:
58 gCarla.utils.pipe_client_idle(self.fPipeClient)
60 def closeExternalUI(self):
61 if self.fPipeClient is None:
62 return
64 # FIXME
65 if not self.fQuitReceived:
66 self.send(["exiting"])
68 gCarla.utils.pipe_client_destroy(self.fPipeClient)
69 self.fPipeClient = None
71 # -------------------------------------------------------------------
72 # Host DSP State
74 def getSampleRate(self):
75 return self.fSampleRate
77 def sendControl(self, index, value):
78 self.send(["control", index, value])
80 def sendProgram(self, channel, bank, program):
81 self.send(["program", channel, bank, program])
83 def sendConfigure(self, key, value):
84 self.send(["configure", key, value])
86 # -------------------------------------------------------------------
87 # DSP Callbacks
89 def dspParameterChanged(self, index, value):
90 return
92 def dspProgramChanged(self, channel, bank, program):
93 return
95 def dspStateChanged(self, key, value):
96 return
98 def dspNoteReceived(self, onOff, channel, note, velocity):
99 return
101 # -------------------------------------------------------------------
102 # ExternalUI Callbacks
104 def uiShow(self):
105 return
107 def uiFocus(self):
108 return
110 def uiHide(self):
111 return
113 def uiQuit(self):
114 self.closeExternalUI()
116 def uiTitleChanged(self, uiTitle):
117 return
119 # -------------------------------------------------------------------
120 # Callback
122 def msgCallback(self, msg):
123 msg = charPtrToString(msg)
125 #if not msg:
126 #return
128 if msg == "control":
129 index = self.readlineblock_int()
130 value = self.readlineblock_float()
131 self.dspParameterChanged(index, value)
133 elif msg == "program":
134 channel = self.readlineblock_int()
135 bank = self.readlineblock_int()
136 program = self.readlineblock_int()
137 self.dspProgramChanged(channel, bank, program)
139 elif msg == "configure":
140 key = self.readlineblock()
141 value = self.readlineblock()
142 self.dspStateChanged(key, value)
144 elif msg == "note":
145 onOff = self.readlineblock_bool()
146 channel = self.readlineblock_int()
147 note = self.readlineblock_int()
148 velocity = self.readlineblock_int()
149 self.dspNoteReceived(onOff, channel, note, velocity)
151 elif msg == "show":
152 self.uiShow()
154 elif msg == "focus":
155 self.uiFocus()
157 elif msg == "hide":
158 self.uiHide()
160 elif msg == "quit":
161 self.fQuitReceived = True
162 self.uiQuit()
164 elif msg == "uiTitle":
165 uiTitle = self.readlineblock()
166 self.uiTitleChanged(uiTitle)
168 else:
169 print("unknown message: \"" + msg + "\"")
171 # -------------------------------------------------------------------
172 # Internal stuff
174 def readlineblock(self):
175 if self.fPipeClient is None:
176 return ""
178 return gCarla.utils.pipe_client_readlineblock(self.fPipeClient, 5000)
180 def readlineblock_bool(self):
181 if self.fPipeClient is None:
182 return False
184 return gCarla.utils.pipe_client_readlineblock_bool(self.fPipeClient, 5000)
186 def readlineblock_int(self):
187 if self.fPipeClient is None:
188 return 0
190 return gCarla.utils.pipe_client_readlineblock_int(self.fPipeClient, 5000)
192 def readlineblock_float(self):
193 if self.fPipeClient is None:
194 return 0.0
196 return gCarla.utils.pipe_client_readlineblock_float(self.fPipeClient, 5000)
198 def send(self, lines):
199 if self.fPipeClient is None or len(lines) == 0:
200 return False
202 hasError = False
203 gCarla.utils.pipe_client_lock(self.fPipeClient)
205 try:
206 for line in lines:
207 if line is None:
208 line2 = "(null)"
209 elif isinstance(line, str):
210 line2 = line.replace("\n", "\r")
211 elif isinstance(line, bool):
212 line2 = "true" if line else "false"
213 elif isinstance(line, int):
214 line2 = "%i" % line
215 elif isinstance(line, float):
216 line2 = "%.10f" % line
217 else:
218 print("unknown data type to send:", type(line))
219 hasError = True
220 break
222 if not gCarla.utils.pipe_client_write_msg(self.fPipeClient, line2 + "\n"):
223 hasError = True
224 break
226 finally:
227 return gCarla.utils.pipe_client_flush_and_unlock(self.fPipeClient) and not hasError