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 # ---------------------------------------------------------------------------------------------------------------------
23 from websocket
import WebSocket
, WebSocketConnectionClosedException
25 # ---------------------------------------------------------------------------------------------------------------------
28 from carla_backend_qt
import *
31 from time
import sleep
33 # ---------------------------------------------------------------------------------------------------------------------
34 # Carla Host object for connecting to the REST API backend
36 class CarlaHostQtWeb(CarlaHostQtNull
):
38 CarlaHostQtNull
.__init
__(self
)
40 self
.host
= "localhost"
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)
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
={
63 def get_engine_driver_device_names(self
, index
):
64 return requests
.get("{}/get_engine_driver_device_names".format(self
.baseurl
), params
={
68 def get_engine_driver_device_info(self
, index
, name
):
69 return requests
.get("{}/get_engine_driver_device_info".format(self
.baseurl
), params
={
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
,
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
:
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, "")
96 if line
== "Keep-Alive":
99 elif line
.startswith("Carla: "):
100 if self
.fEngineCallback
is None:
103 # split values from line
104 action
, pluginId
, value1
, value2
, value3
, valueStr
= line
[7:].split(" ",5)
106 # convert to proper types
108 pluginId
= int(pluginId
)
111 value3
= float(value3
)
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
)
128 self
.peaks
[pluginId
] = (value1
, value2
, value3
, value4
)
130 def is_engine_running(self
):
131 if not self
.isRunning
:
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
={
147 'valueStr': valueStr
,
150 def load_file(self
, filename
):
151 return bool(int(requests
.get("{}/load_file".format(self
.baseurl
), params
={
152 'filename': filename
,
155 def load_project(self
, filename
):
156 return bool(int(requests
.get("{}/load_project".format(self
.baseurl
), params
={
157 'filename': filename
,
160 def save_project(self
, filename
):
161 return bool(int(requests
.get("{}/save_project".format(self
.baseurl
), params
={
162 'filename': filename
,
165 def patchbay_connect(self
, groupIdA
, portIdA
, groupIdB
, portIdB
):
166 return bool(int(requests
.get("{}/patchbay_connect".format(self
.baseurl
), params
={
167 'groupIdA': groupIdA
,
169 'groupIdB': groupIdB
,
173 def patchbay_disconnect(self
, connectionId
):
174 return bool(int(requests
.get("{}/patchbay_disconnect".format(self
.baseurl
), params
={
175 'connectionId': connectionId
,
178 def patchbay_refresh(self
, external
):
179 return bool(int(requests
.get("{}/patchbay_refresh".format(self
.baseurl
), params
={
180 'external': int(external
),
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
={
194 def transport_relocate(self
, frame
):
195 requests
.get("{}/transport_relocate".format(self
.baseurl
), params
={
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
):
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
={
221 'filename': filename
,
224 'uniqueId': uniqueId
,
228 def remove_plugin(self
, pluginId
):
229 return bool(int(requests
.get("{}/remove_plugin".format(self
.baseurl
), params
={
230 'filename': pluginId
,
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
,
242 def clone_plugin(self
, pluginId
):
243 return bool(int(requests
.get("{}/clone_plugin".format(self
.baseurl
), params
={
244 'pluginId': pluginId
,
247 def replace_plugin(self
, pluginId
):
248 return bool(int(requests
.get("{}/replace_plugin".format(self
.baseurl
), params
={
249 'pluginId': pluginId
,
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
,
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
,
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
,
270 def export_plugin_lv2(self
, pluginId
, lv2path
):
271 return bool(int(requests
.get("{}/export_plugin_lv2".format(self
.baseurl
), params
={
272 'pluginId': pluginId
,
276 def get_plugin_info(self
, pluginId
):
277 return requests
.get("{}/get_plugin_info".format(self
.baseurl
), params
={
278 'pluginId': pluginId
,
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
,
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
,
291 def get_parameter_count_info(self
, pluginId
):
292 return requests
.get("{}/get_parameter_count_info".format(self
.baseurl
), params
={
293 'pluginId': pluginId
,
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
,
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
,
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
,
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
,
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
,
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
,
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
,
340 def get_chunk_data(self
, pluginId
):
341 return requests
.get("{}/get_chunk_data".format(self
.baseurl
), params
={
342 'pluginId': pluginId
,
345 def get_parameter_count(self
, pluginId
):
346 return int(requests
.get("{}/get_parameter_count".format(self
.baseurl
), params
={
347 'pluginId': pluginId
,
350 def get_program_count(self
, pluginId
):
351 return int(requests
.get("{}/get_program_count".format(self
.baseurl
), params
={
352 'pluginId': pluginId
,
355 def get_midi_program_count(self
, pluginId
):
356 return int(requests
.get("{}/get_midi_program_count".format(self
.baseurl
), params
={
357 'pluginId': pluginId
,
360 def get_custom_data_count(self
, pluginId
):
361 return int(requests
.get("{}/get_custom_data_count".format(self
.baseurl
), params
={
362 'pluginId': pluginId
,
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
,
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
,
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
,
383 def get_real_plugin_name(self
, pluginId
):
384 return requests
.get("{}/get_real_plugin_name".format(self
.baseurl
), params
={
385 'pluginId': pluginId
,
388 def get_current_program_index(self
, pluginId
):
389 return int(requests
.get("{}/get_custom_data_count".format(self
.baseurl
), params
={
390 'pluginId': pluginId
,
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
,
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
,
404 def get_current_parameter_value(self
, pluginId
, parameterId
):
407 return float(requests
.get("{}/get_current_parameter_value".format(self
.baseurl
), params
={
408 'pluginId': pluginId
,
409 'parameterId': parameterId
,
411 except requests
.exceptions
.ConnectionError
:
412 if self
.fEngineCallback
is None:
413 self
.fEngineCallback(None, ENGINE_CALLBACK_QUIT
, 0, 0, 0, 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
,
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
,
435 def set_active(self
, pluginId
, onOff
):
436 requests
.get("{}/set_active".format(self
.baseurl
), params
={
437 'pluginId': pluginId
,
441 def set_drywet(self
, pluginId
, value
):
442 requests
.get("{}/set_drywet".format(self
.baseurl
), params
={
443 'pluginId': pluginId
,
447 def set_volume(self
, pluginId
, value
):
448 requests
.get("{}/set_volume".format(self
.baseurl
), params
={
449 'pluginId': pluginId
,
453 def set_balance_left(self
, pluginId
, value
):
454 requests
.get("{}/set_balance_left".format(self
.baseurl
), params
={
455 'pluginId': pluginId
,
459 def set_balance_right(self
, pluginId
, value
):
460 requests
.get("{}/set_balance_right".format(self
.baseurl
), params
={
461 'pluginId': pluginId
,
465 def set_panning(self
, pluginId
, value
):
466 requests
.get("{}/set_panning".format(self
.baseurl
), params
={
467 'pluginId': pluginId
,
471 def set_ctrl_channel(self
, pluginId
, channel
):
472 requests
.get("{}/set_ctrl_channel".format(self
.baseurl
), params
={
473 'pluginId': pluginId
,
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
,
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
,
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
,
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
,
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
,
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