Update CI version
[carla.git] / source / frontend / carla_backend_qtweb.py
blobc10f0e2149cec89fc1cc914b6e87fb63d10fcbb9
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 # Carla Backend code (Web stuff)
5 # Copyright (C) 2018 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 (Global)
22 import requests
23 from websocket import WebSocket, WebSocketConnectionClosedException
25 # ---------------------------------------------------------------------------------------------------------------------
26 # Imports (Custom)
28 from carla_backend_qt import *
30 import os
31 from time import sleep
33 # ---------------------------------------------------------------------------------------------------------------------
34 # Carla Host object for connecting to the REST API backend
36 class CarlaHostQtWeb(CarlaHostQtNull):
37 def __init__(self):
38 CarlaHostQtNull.__init__(self)
40 self.host = "localhost"
41 self.port = 2228
43 self.baseurl = "http://{}:{}".format(self.host, self.port)
45 self.socket = WebSocket()
46 self.socket.connect("ws://{}:{}/ws".format(self.host, self.port), timeout=1)
48 self.isRemote = True
49 self.isRunning = True
50 self.peaks = []
52 for i in range(99):
53 self.peaks.append((0.0, 0.0, 0.0, 0.0))
55 def get_engine_driver_count(self):
56 return int(requests.get("{}/get_engine_driver_count".format(self.baseurl)).text)
58 def get_engine_driver_name(self, index):
59 return requests.get("{}/get_engine_driver_name".format(self.baseurl), params={
60 'index': index,
61 }).text
63 def get_engine_driver_device_names(self, index):
64 return requests.get("{}/get_engine_driver_device_names".format(self.baseurl), params={
65 'index': index,
66 }).text.split("\n")
68 def get_engine_driver_device_info(self, index, name):
69 return requests.get("{}/get_engine_driver_device_info".format(self.baseurl), params={
70 'index': index,
71 'name': name,
72 }).json()
74 def engine_init(self, driverName, clientName):
75 return bool(int(requests.get("{}/engine_init".format(self.baseurl), params={
76 'driverName': driverName,
77 'clientName': clientName,
78 }).text))
80 def engine_close(self):
81 return bool(int(requests.get("{}/engine_close".format(self.baseurl)).text))
83 def engine_idle(self):
84 if not self.isRunning:
85 return
87 while True:
88 try:
89 line = self.socket.recv().strip()
90 except WebSocketConnectionClosedException:
91 self.isRunning = False
92 if self.fEngineCallback is None:
93 self.fEngineCallback(None, ENGINE_CALLBACK_QUIT, 0, 0, 0, 0.0, "")
94 return
96 if line == "Keep-Alive":
97 return
99 elif line.startswith("Carla: "):
100 if self.fEngineCallback is None:
101 continue
103 # split values from line
104 action, pluginId, value1, value2, value3, valueStr = line[7:].split(" ",5)
106 # convert to proper types
107 action = int(action)
108 pluginId = int(pluginId)
109 value1 = int(value1)
110 value2 = int(value2)
111 value3 = float(value3)
113 # pass to callback
114 self.fEngineCallback(None, action, pluginId, value1, value2, value3, valueStr)
116 elif line.startswith("Peaks: "):
117 # split values from line
118 pluginId, value1, value2, value3, value4 = line[7:].split(" ",5)
120 # convert to proper types
121 pluginId = int(pluginId)
122 value1 = float(value1)
123 value2 = float(value2)
124 value3 = float(value3)
125 value4 = float(value4)
127 # store peaks
128 self.peaks[pluginId] = (value1, value2, value3, value4)
130 def is_engine_running(self):
131 if not self.isRunning:
132 return False
134 try:
135 return bool(int(requests.get("{}/is_engine_running".format(self.baseurl)).text))
136 except requests.exceptions.ConnectionError:
137 if self.fEngineCallback is None:
138 self.fEngineCallback(None, ENGINE_CALLBACK_QUIT, 0, 0, 0, 0.0, "")
140 def set_engine_about_to_close(self):
141 return bool(int(requests.get("{}/set_engine_about_to_close".format(self.baseurl)).text))
143 def set_engine_option(self, option, value, valueStr):
144 requests.get("{}/set_engine_option".format(self.baseurl), params={
145 'option': option,
146 'value': value,
147 'valueStr': valueStr,
150 def load_file(self, filename):
151 return bool(int(requests.get("{}/load_file".format(self.baseurl), params={
152 'filename': filename,
153 }).text))
155 def load_project(self, filename):
156 return bool(int(requests.get("{}/load_project".format(self.baseurl), params={
157 'filename': filename,
158 }).text))
160 def save_project(self, filename):
161 return bool(int(requests.get("{}/save_project".format(self.baseurl), params={
162 'filename': filename,
163 }).text))
165 def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB):
166 return bool(int(requests.get("{}/patchbay_connect".format(self.baseurl), params={
167 'groupIdA': groupIdA,
168 'portIdA': portIdA,
169 'groupIdB': groupIdB,
170 'portIdB': portIdB,
171 }).text))
173 def patchbay_disconnect(self, connectionId):
174 return bool(int(requests.get("{}/patchbay_disconnect".format(self.baseurl), params={
175 'connectionId': connectionId,
176 }).text))
178 def patchbay_refresh(self, external):
179 return bool(int(requests.get("{}/patchbay_refresh".format(self.baseurl), params={
180 'external': int(external),
181 }).text))
183 def transport_play(self):
184 requests.get("{}/transport_play".format(self.baseurl))
186 def transport_pause(self):
187 requests.get("{}/transport_pause".format(self.baseurl))
189 def transport_bpm(self, bpm):
190 requests.get("{}/transport_bpm".format(self.baseurl), params={
191 'bpm': bpm,
194 def transport_relocate(self, frame):
195 requests.get("{}/transport_relocate".format(self.baseurl), params={
196 'frame': frame,
199 def get_current_transport_frame(self):
200 return int(requests.get("{}/get_current_transport_frame".format(self.baseurl)).text)
202 def get_transport_info(self):
203 if self.isRunning:
204 try:
205 return requests.get("{}/get_transport_info".format(self.baseurl)).json()
206 except requests.exceptions.ConnectionError:
207 if self.fEngineCallback is None:
208 self.fEngineCallback(None, ENGINE_CALLBACK_QUIT, 0, 0, 0, 0.0, "")
209 return PyCarlaTransportInfo()
211 def get_current_plugin_count(self):
212 return int(requests.get("{}/get_current_plugin_count".format(self.baseurl)).text)
214 def get_max_plugin_number(self):
215 return int(requests.get("{}/get_max_plugin_number".format(self.baseurl)).text)
217 def add_plugin(self, btype, ptype, filename, name, label, uniqueId, extraPtr, options):
218 return bool(int(requests.get("{}/add_plugin".format(self.baseurl), params={
219 'btype': btype,
220 'ptype': ptype,
221 'filename': filename,
222 'name': name,
223 'label': label,
224 'uniqueId': uniqueId,
225 'options': options,
226 }).text))
228 def remove_plugin(self, pluginId):
229 return bool(int(requests.get("{}/remove_plugin".format(self.baseurl), params={
230 'filename': pluginId,
231 }).text))
233 def remove_all_plugins(self):
234 return bool(int(requests.get("{}/remove_all_plugins".format(self.baseurl)).text))
236 def rename_plugin(self, pluginId, newName):
237 return requests.get("{}/rename_plugin".format(self.baseurl), params={
238 'pluginId': pluginId,
239 'newName': newName,
240 }).text
242 def clone_plugin(self, pluginId):
243 return bool(int(requests.get("{}/clone_plugin".format(self.baseurl), params={
244 'pluginId': pluginId,
245 }).text))
247 def replace_plugin(self, pluginId):
248 return bool(int(requests.get("{}/replace_plugin".format(self.baseurl), params={
249 'pluginId': pluginId,
250 }).text))
252 def switch_plugins(self, pluginIdA, pluginIdB):
253 return bool(int(requests.get("{}/switch_plugins".format(self.baseurl), params={
254 'pluginIdA': pluginIdA,
255 'pluginIdB': pluginIdB,
256 }).text))
258 def load_plugin_state(self, pluginId, filename):
259 return bool(int(requests.get("{}/load_plugin_state".format(self.baseurl), params={
260 'pluginId': pluginId,
261 'filename': filename,
262 }).text))
264 def save_plugin_state(self, pluginId, filename):
265 return bool(int(requests.get("{}/save_plugin_state".format(self.baseurl), params={
266 'pluginId': pluginId,
267 'filename': filename,
268 }).text))
270 def export_plugin_lv2(self, pluginId, lv2path):
271 return bool(int(requests.get("{}/export_plugin_lv2".format(self.baseurl), params={
272 'pluginId': pluginId,
273 'lv2path': lv2path,
274 }).text))
276 def get_plugin_info(self, pluginId):
277 return requests.get("{}/get_plugin_info".format(self.baseurl), params={
278 'pluginId': pluginId,
279 }).json()
281 def get_audio_port_count_info(self, pluginId):
282 return requests.get("{}/get_audio_port_count_info".format(self.baseurl), params={
283 'pluginId': pluginId,
284 }).json()
286 def get_midi_port_count_info(self, pluginId):
287 return requests.get("{}/get_midi_port_count_info".format(self.baseurl), params={
288 'pluginId': pluginId,
289 }).json()
291 def get_parameter_count_info(self, pluginId):
292 return requests.get("{}/get_parameter_count_info".format(self.baseurl), params={
293 'pluginId': pluginId,
294 }).json()
296 def get_parameter_info(self, pluginId, parameterId):
297 return requests.get("{}/get_parameter_info".format(self.baseurl), params={
298 'pluginId': pluginId,
299 'parameterId': parameterId,
300 }).json()
302 def get_parameter_scalepoint_info(self, pluginId, parameterId, scalePointId):
303 return requests.get("{}/get_parameter_scalepoint_info".format(self.baseurl), params={
304 'pluginId': pluginId,
305 'parameterId': parameterId,
306 'scalePointId': scalePointId,
307 }).json()
309 def get_parameter_data(self, pluginId, parameterId):
310 return requests.get("{}/get_parameter_data".format(self.baseurl), params={
311 'pluginId': pluginId,
312 'parameterId': parameterId,
313 }).json()
315 def get_parameter_ranges(self, pluginId, parameterId):
316 return requests.get("{}/get_parameter_ranges".format(self.baseurl), params={
317 'pluginId': pluginId,
318 'parameterId': parameterId,
319 }).json()
321 def get_midi_program_data(self, pluginId, midiProgramId):
322 return requests.get("{}/get_midi_program_data".format(self.baseurl), params={
323 'pluginId': pluginId,
324 'midiProgramId': midiProgramId,
325 }).json()
327 def get_custom_data(self, pluginId, customDataId):
328 return requests.get("{}/get_custom_data".format(self.baseurl), params={
329 'pluginId': pluginId,
330 'customDataId': customDataId,
331 }).json()
333 def get_custom_data_value(self, pluginId, type_, key):
334 return requests.get("{}/get_custom_data_value".format(self.baseurl), params={
335 'pluginId': pluginId,
336 'type_': type_,
337 'key': key,
338 }).text
340 def get_chunk_data(self, pluginId):
341 return requests.get("{}/get_chunk_data".format(self.baseurl), params={
342 'pluginId': pluginId,
343 }).text
345 def get_parameter_count(self, pluginId):
346 return int(requests.get("{}/get_parameter_count".format(self.baseurl), params={
347 'pluginId': pluginId,
348 }).text)
350 def get_program_count(self, pluginId):
351 return int(requests.get("{}/get_program_count".format(self.baseurl), params={
352 'pluginId': pluginId,
353 }).text)
355 def get_midi_program_count(self, pluginId):
356 return int(requests.get("{}/get_midi_program_count".format(self.baseurl), params={
357 'pluginId': pluginId,
358 }).text)
360 def get_custom_data_count(self, pluginId):
361 return int(requests.get("{}/get_custom_data_count".format(self.baseurl), params={
362 'pluginId': pluginId,
363 }).text)
365 def get_parameter_text(self, pluginId, parameterId):
366 return requests.get("{}/get_parameter_text".format(self.baseurl), params={
367 'pluginId': pluginId,
368 'parameterId': parameterId,
369 }).text
371 def get_program_name(self, pluginId, programId):
372 return requests.get("{}/get_program_name".format(self.baseurl), params={
373 'pluginId': pluginId,
374 'programId': programId,
375 }).text
377 def get_midi_program_name(self, pluginId, midiProgramId):
378 return requests.get("{}/get_midi_program_name".format(self.baseurl), params={
379 'pluginId': pluginId,
380 'midiProgramId': midiProgramId,
381 }).text
383 def get_real_plugin_name(self, pluginId):
384 return requests.get("{}/get_real_plugin_name".format(self.baseurl), params={
385 'pluginId': pluginId,
386 }).text
388 def get_current_program_index(self, pluginId):
389 return int(requests.get("{}/get_custom_data_count".format(self.baseurl), params={
390 'pluginId': pluginId,
391 }).text)
393 def get_current_midi_program_index(self, pluginId):
394 return int(requests.get("{}/get_custom_data_count".format(self.baseurl), params={
395 'pluginId': pluginId,
396 }).text)
398 def get_default_parameter_value(self, pluginId, parameterId):
399 return float(requests.get("{}/get_default_parameter_value".format(self.baseurl), params={
400 'pluginId': pluginId,
401 'parameterId': parameterId,
402 }).text)
404 def get_current_parameter_value(self, pluginId, parameterId):
405 if self.isRunning:
406 try:
407 return float(requests.get("{}/get_current_parameter_value".format(self.baseurl), params={
408 'pluginId': pluginId,
409 'parameterId': parameterId,
410 }).text)
411 except requests.exceptions.ConnectionError:
412 if self.fEngineCallback is None:
413 self.fEngineCallback(None, ENGINE_CALLBACK_QUIT, 0, 0, 0, 0.0, "")
414 return 0.0
416 def get_internal_parameter_value(self, pluginId, parameterId):
417 return float(requests.get("{}/get_internal_parameter_value".format(self.baseurl), params={
418 'pluginId': pluginId,
419 'parameterId': parameterId,
420 }).text)
422 def get_input_peak_value(self, pluginId, isLeft):
423 return self.peaks[pluginId][0 if isLeft else 1]
425 def get_output_peak_value(self, pluginId, isLeft):
426 return self.peaks[pluginId][2 if isLeft else 3]
428 def set_option(self, pluginId, option, yesNo):
429 requests.get("{}/set_option".format(self.baseurl), params={
430 'pluginId': pluginId,
431 'option': option,
432 'yesNo': int(yesNo),
435 def set_active(self, pluginId, onOff):
436 requests.get("{}/set_active".format(self.baseurl), params={
437 'pluginId': pluginId,
438 'onOff': int(onOff),
441 def set_drywet(self, pluginId, value):
442 requests.get("{}/set_drywet".format(self.baseurl), params={
443 'pluginId': pluginId,
444 'value': value,
447 def set_volume(self, pluginId, value):
448 requests.get("{}/set_volume".format(self.baseurl), params={
449 'pluginId': pluginId,
450 'value': value,
453 def set_balance_left(self, pluginId, value):
454 requests.get("{}/set_balance_left".format(self.baseurl), params={
455 'pluginId': pluginId,
456 'value': value,
459 def set_balance_right(self, pluginId, value):
460 requests.get("{}/set_balance_right".format(self.baseurl), params={
461 'pluginId': pluginId,
462 'value': value,
465 def set_panning(self, pluginId, value):
466 requests.get("{}/set_panning".format(self.baseurl), params={
467 'pluginId': pluginId,
468 'value': value,
471 def set_ctrl_channel(self, pluginId, channel):
472 requests.get("{}/set_ctrl_channel".format(self.baseurl), params={
473 'pluginId': pluginId,
474 'channel': channel,
477 def set_parameter_value(self, pluginId, parameterId, value):
478 requests.get("{}/set_parameter_value".format(self.baseurl), params={
479 'pluginId': pluginId,
480 'parameterId': parameterId,
481 'value': value,
484 def set_parameter_midi_channel(self, pluginId, parameterId, channel):
485 requests.get("{}/set_parameter_midi_channel".format(self.baseurl), params={
486 'pluginId': pluginId,
487 'parameterId': parameterId,
488 'channel': channel,
491 def set_parameter_midi_cc(self, pluginId, parameterId, cc):
492 requests.get("{}/set_parameter_midi_cc".format(self.baseurl), params={
493 'pluginId': pluginId,
494 'parameterId': parameterId,
495 'cc': cc,
498 def set_program(self, pluginId, programId):
499 requests.get("{}/set_program".format(self.baseurl), params={
500 'pluginId': pluginId,
503 def set_midi_program(self, pluginId, midiProgramId):
504 requests.get("{}/set_midi_program".format(self.baseurl), params={
505 'pluginId': pluginId,
506 'midiProgramId': midiProgramId,
509 def set_custom_data(self, pluginId, type_, key, value):
510 requests.get("{}/set_custom_data".format(self.baseurl), params={
511 'pluginId': pluginId,
512 'type': type_,
513 'key': key,
514 'value': value,
517 def set_chunk_data(self, pluginId, chunkData):
518 requests.get("{}/set_chunk_data".format(self.baseurl), params={
519 'pluginId': pluginId,
520 'chunkData': chunkData,
523 def prepare_for_save(self, pluginId):
524 requests.get("{}/prepare_for_save".format(self.baseurl), params={
525 'pluginId': pluginId,
528 def reset_parameters(self, pluginId):
529 requests.get("{}/reset_parameters".format(self.baseurl), params={
530 'pluginId': pluginId,
533 def randomize_parameters(self, pluginId):
534 requests.get("{}/randomize_parameters".format(self.baseurl), params={
535 'pluginId': pluginId,
538 def send_midi_note(self, pluginId, channel, note, velocity):
539 requests.get("{}/send_midi_note".format(self.baseurl), params={
540 'pluginId': pluginId,
541 'channel': channel,
542 'note': note,
543 'velocity': velocity,
546 def get_buffer_size(self):
547 return int(requests.get("{}/get_buffer_size".format(self.baseurl)).text)
549 def get_sample_rate(self):
550 return float(requests.get("{}/get_sample_rate".format(self.baseurl)).text)
552 def get_last_error(self):
553 return requests.get("{}/get_last_error".format(self.baseurl)).text
555 def get_host_osc_url_tcp(self):
556 return requests.get("{}/get_host_osc_url_tcp".format(self.baseurl)).text
558 def get_host_osc_url_udp(self):
559 return requests.get("{}/get_host_osc_url_udp".format(self.baseurl)).text