| @@ -1881,6 +1881,7 @@ class CarlaHostNull(CarlaHostMeta): | |||
| CarlaHostMeta.__init__(self) | |||
| self.fEngineCallback = None | |||
| self.fFileCallback = None | |||
| self.fEngineRunning = False | |||
| def get_engine_driver_count(self): | |||
| @@ -1923,7 +1924,7 @@ class CarlaHostNull(CarlaHostMeta): | |||
| return | |||
| def set_file_callback(self, func): | |||
| return | |||
| self.fFileCallback = func | |||
| def load_file(self, filename): | |||
| return False | |||
| @@ -26,6 +26,52 @@ import requests | |||
| from carla_backend_qt import * | |||
| import os | |||
| from time import sleep | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| # Iterates over the content of a file-like object line-by-line. | |||
| # Based on code by Lars Kellogg-Stedman, see https://github.com/requests/requests/issues/2433 | |||
| def iterate_stream_nonblock(stream, chunk_size=128): | |||
| pending = None | |||
| while True: | |||
| try: | |||
| chunk = os.read(stream.raw.fileno(), chunk_size) | |||
| except BlockingIOError: | |||
| break | |||
| if not chunk: | |||
| break | |||
| if pending is not None: | |||
| chunk = pending + chunk | |||
| pending = None | |||
| lines = chunk.splitlines() | |||
| if lines and lines[-1]: | |||
| pending = lines.pop() | |||
| for line in lines: | |||
| yield line | |||
| if not pending: | |||
| break | |||
| if pending: | |||
| yield pending | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| def create_stream(baseurl): | |||
| stream = requests.get("{}/stream".format(baseurl), stream=True, timeout=0.1) | |||
| if stream.encoding is None: | |||
| stream.encoding = 'utf-8' | |||
| return stream | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| # Carla Host object for connecting to the REST API | |||
| @@ -34,6 +80,7 @@ class CarlaHostQtWeb(CarlaHostQtNull): | |||
| CarlaHostQtNull.__init__(self) | |||
| self.baseurl = "http://localhost:2228" | |||
| self.stream = create_stream(self.baseurl) | |||
| def get_engine_driver_count(self): | |||
| # FIXME | |||
| @@ -49,33 +96,277 @@ class CarlaHostQtWeb(CarlaHostQtNull): | |||
| return requests.get("{}/get_engine_driver_device_info/{}/{}".format(self.baseurl, index, name)).json() | |||
| def engine_init(self, driverName, clientName): | |||
| resp = requests.get("{}/engine_init/{}/{}".format(self.baseurl, driverName, clientName)).status_code | |||
| # TESTING | |||
| if self.fEngineCallback is not None: | |||
| self.fEngineCallback(None, ENGINE_CALLBACK_ENGINE_STARTED, 0, self.processMode, self.transportMode, 0.0, driverName) | |||
| return resp == 200 | |||
| return requests.get("{}/engine_init/{}/{}".format(self.baseurl, driverName, clientName)).status_code == 200 | |||
| def engine_close(self): | |||
| # TESTING | |||
| if self.fEngineCallback is not None: | |||
| self.fEngineCallback(None, ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0.0, "") | |||
| return requests.get("{}/engine_close".format(self.baseurl)).status_code == 200 | |||
| def engine_idle(self): | |||
| requests.get("{}/engine_idle".format(self.baseurl)) | |||
| closed = False | |||
| stream = self.stream | |||
| for line in iterate_stream_nonblock(stream): | |||
| line = line.decode('utf-8', errors='ignore') | |||
| if line.startswith("Carla: "): | |||
| if self.fEngineCallback is None: | |||
| continue | |||
| # split values from line | |||
| action, pluginId, value1, value2, value3, valueStr = line[7:].split(" ",5) | |||
| # convert to proper types | |||
| action = int(action) | |||
| pluginId = int(pluginId) | |||
| value1 = int(value1) | |||
| value2 = int(value2) | |||
| value3 = float(value3) | |||
| # pass to callback | |||
| self.fEngineCallback(None, action, pluginId, value1, value2, value3, valueStr) | |||
| elif line == "Connection: close": | |||
| closed = True | |||
| if closed: | |||
| self.stream = create_stream(self.baseurl) | |||
| stream.close() | |||
| def is_engine_running(self): | |||
| return requests.get("{}/is_engine_running".format(self.baseurl)).status_code == 200 | |||
| try: | |||
| return requests.get("{}/is_engine_running".format(self.baseurl)).status_code == 200 | |||
| except requests.exceptions.ConnectionError: | |||
| if self.fEngineCallback is None: | |||
| self.fEngineCallback(None, ENGINE_CALLBACK_QUIT, 0, 0, 0, 0.0, "") | |||
| def set_engine_about_to_close(self): | |||
| return requests.get("{}/set_engine_about_to_close".format(self.baseurl)).status_code == 200 | |||
| def set_engine_option(self, option, value, valueStr): | |||
| return | |||
| def load_file(self, filename): | |||
| return False | |||
| def load_project(self, filename): | |||
| return False | |||
| def save_project(self, filename): | |||
| return False | |||
| def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB): | |||
| return False | |||
| def patchbay_disconnect(self, connectionId): | |||
| return False | |||
| def patchbay_refresh(self, external): | |||
| return False | |||
| def transport_play(self): | |||
| return | |||
| def transport_pause(self): | |||
| return | |||
| def transport_bpm(self, bpm): | |||
| return | |||
| def transport_relocate(self, frame): | |||
| return | |||
| def get_current_transport_frame(self): | |||
| return 0 | |||
| def get_transport_info(self): | |||
| return PyCarlaTransportInfo | |||
| def get_current_plugin_count(self): | |||
| return 0 | |||
| def get_max_plugin_number(self): | |||
| return 0 | |||
| def add_plugin(self, btype, ptype, filename, name, label, uniqueId, extraPtr, options): | |||
| return False | |||
| def remove_plugin(self, pluginId): | |||
| return False | |||
| def remove_all_plugins(self): | |||
| return False | |||
| def rename_plugin(self, pluginId, newName): | |||
| return "" | |||
| def clone_plugin(self, pluginId): | |||
| return False | |||
| def replace_plugin(self, pluginId): | |||
| return False | |||
| def switch_plugins(self, pluginIdA, pluginIdB): | |||
| return False | |||
| def load_plugin_state(self, pluginId, filename): | |||
| return False | |||
| def save_plugin_state(self, pluginId, filename): | |||
| return False | |||
| def export_plugin_lv2(self, pluginId, lv2path): | |||
| return False | |||
| def get_plugin_info(self, pluginId): | |||
| return PyCarlaPluginInfo | |||
| def get_audio_port_count_info(self, pluginId): | |||
| return PyCarlaPortCountInfo | |||
| def get_midi_port_count_info(self, pluginId): | |||
| return PyCarlaPortCountInfo | |||
| def get_parameter_count_info(self, pluginId): | |||
| return PyCarlaPortCountInfo | |||
| def get_parameter_info(self, pluginId, parameterId): | |||
| return PyCarlaParameterInfo | |||
| def get_parameter_scalepoint_info(self, pluginId, parameterId, scalePointId): | |||
| return PyCarlaScalePointInfo | |||
| def get_parameter_data(self, pluginId, parameterId): | |||
| return PyParameterData | |||
| def get_parameter_ranges(self, pluginId, parameterId): | |||
| return PyParameterRanges | |||
| def get_midi_program_data(self, pluginId, midiProgramId): | |||
| return PyMidiProgramData | |||
| def get_custom_data(self, pluginId, customDataId): | |||
| return PyCustomData | |||
| def get_chunk_data(self, pluginId): | |||
| return "" | |||
| def get_parameter_count(self, pluginId): | |||
| return 0 | |||
| def get_program_count(self, pluginId): | |||
| return 0 | |||
| def get_midi_program_count(self, pluginId): | |||
| return 0 | |||
| def get_custom_data_count(self, pluginId): | |||
| return 0 | |||
| def get_parameter_text(self, pluginId, parameterId): | |||
| return "" | |||
| def get_program_name(self, pluginId, programId): | |||
| return "" | |||
| def get_midi_program_name(self, pluginId, midiProgramId): | |||
| return "" | |||
| def get_real_plugin_name(self, pluginId): | |||
| return "" | |||
| def get_current_program_index(self, pluginId): | |||
| return 0 | |||
| def get_current_midi_program_index(self, pluginId): | |||
| return 0 | |||
| def get_default_parameter_value(self, pluginId, parameterId): | |||
| return 0.0 | |||
| def get_current_parameter_value(self, pluginId, parameterId): | |||
| return 0.0 | |||
| def get_internal_parameter_value(self, pluginId, parameterId): | |||
| return 0.0 | |||
| def get_input_peak_value(self, pluginId, isLeft): | |||
| return 0.0 | |||
| def get_output_peak_value(self, pluginId, isLeft): | |||
| return 0.0 | |||
| def set_option(self, pluginId, option, yesNo): | |||
| return | |||
| def set_active(self, pluginId, onOff): | |||
| return | |||
| def set_drywet(self, pluginId, value): | |||
| return | |||
| def set_volume(self, pluginId, value): | |||
| return | |||
| def set_balance_left(self, pluginId, value): | |||
| return | |||
| def set_balance_right(self, pluginId, value): | |||
| return | |||
| def set_panning(self, pluginId, value): | |||
| return | |||
| def set_ctrl_channel(self, pluginId, channel): | |||
| return | |||
| def set_parameter_value(self, pluginId, parameterId, value): | |||
| return | |||
| def set_parameter_midi_channel(self, pluginId, parameterId, channel): | |||
| return | |||
| def set_parameter_midi_cc(self, pluginId, parameterId, cc): | |||
| return | |||
| def set_program(self, pluginId, programId): | |||
| return | |||
| def set_midi_program(self, pluginId, midiProgramId): | |||
| return | |||
| def set_custom_data(self, pluginId, type_, key, value): | |||
| return | |||
| def set_chunk_data(self, pluginId, chunkData): | |||
| return | |||
| def prepare_for_save(self, pluginId): | |||
| return | |||
| def reset_parameters(self, pluginId): | |||
| return | |||
| def randomize_parameters(self, pluginId): | |||
| return | |||
| def send_midi_note(self, pluginId, channel, note, velocity): | |||
| return | |||
| def get_buffer_size(self): | |||
| return 0 | |||
| def get_sample_rate(self): | |||
| return 0.0 | |||
| def get_last_error(self): | |||
| return requests.get("{}/get_last_error".format(self.baseurl)).text | |||
| def get_host_osc_url_tcp(self): | |||
| return "" | |||
| def get_host_osc_url_udp(self): | |||
| return "" | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| # TESTING | |||
| @@ -83,18 +374,36 @@ if __name__ == '__main__': | |||
| baseurl = "http://localhost:2228" | |||
| driver_count = int(requests.get("{}/get_engine_driver_count".format(baseurl)).text) | |||
| #driver_count = int(requests.get("{}/get_engine_driver_count".format(baseurl)).text) | |||
| ## FIXME | |||
| #driver_count -= 1 | |||
| #print("Driver names:") | |||
| #for index in range(driver_count): | |||
| #print("\t -", requests.get("{}/get_engine_driver_name/{}".format(baseurl, index)).text) | |||
| #print("Driver device names:") | |||
| #for index in range(driver_count): | |||
| #for name in requests.get("{}/get_engine_driver_device_names/{}".format(baseurl, index)).text.split("\n"): | |||
| #print("\t {}:".format(name), requests.get("{}/get_engine_driver_device_info/{}/{}".format(baseurl, index, name)).json()) | |||
| requests.get("{}/engine_close".format(baseurl)).status_code | |||
| if requests.get("{}/engine_init/{}/{}".format(baseurl, "JACK", "test")).status_code == 200: | |||
| stream = requests.get("{}/stream".format(baseurl), stream=True, timeout=0.25) | |||
| #stream.add_done_callback(cb) | |||
| #print(stream, dir(stream)) | |||
| # FIXME | |||
| driver_count -= 1 | |||
| #for line in stream.iter_lines(): | |||
| #print("line", stream, line) | |||
| print("Driver names:") | |||
| for index in range(driver_count): | |||
| print("\t -", requests.get("{}/get_engine_driver_name/{}".format(baseurl, index)).text) | |||
| while True: | |||
| print("idle") | |||
| for line in iterate_stream_nonblock(stream): | |||
| print("line", line) | |||
| sleep(0.5) | |||
| print("Driver device names:") | |||
| for index in range(driver_count): | |||
| for name in requests.get("{}/get_engine_driver_device_names/{}".format(baseurl, index)).text.split("\n"): | |||
| print("\t {}:".format(name), requests.get("{}/get_engine_driver_device_info/{}/{}".format(baseurl, index, name)).json()) | |||
| requests.get("{}/engine_close".format(baseurl)).status_code | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| @@ -23,7 +23,7 @@ endif | |||
| BUILD_CXX_FLAGS += -I$(CWD) -I$(CWD)/backend -I$(CWD)/includes -I$(CWD)/modules -I$(CWD)/utils | |||
| LINK_FLAGS += -L$(BINDIR) -lcarla_standalone2 -lcarla_utils -lrestbed -Wl,-rpath=$(shell realpath $(CWD)/../bin) | |||
| LINK_FLAGS += -L$(BINDIR) -lcarla_standalone2 -lcarla_utils -lrestbed -lpthread -Wl,-rpath=$(shell realpath $(CWD)/../bin) | |||
| # ---------------------------------------------------------------------------------------------------------------------- | |||
| @@ -18,6 +18,59 @@ | |||
| #include "common.hpp" | |||
| #include "CarlaHost.h" | |||
| #include "CarlaBackendUtils.hpp" | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| static bool gEngineRunning = false; | |||
| void engine_idle_handler() | |||
| { | |||
| if (gEngineRunning) | |||
| carla_engine_idle(); | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| static void EngineCallback(void* ptr, EngineCallbackOpcode action, uint pluginId, int value1, int value2, float value3, const char* valueStr) | |||
| { | |||
| carla_stdout("EngineCallback(%p, %u:%s, %u, %i, %i, %f, %s)", | |||
| ptr, (uint)action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr); | |||
| char msgBuf[1024]; | |||
| std::snprintf(msgBuf, 1023, "Carla: %u %u %i %i %f %s\n", action, pluginId, value1, value2, value3, valueStr); | |||
| msgBuf[1023] = '\0'; | |||
| switch (action) | |||
| { | |||
| case ENGINE_CALLBACK_ENGINE_STARTED: | |||
| gEngineRunning = true; | |||
| break; | |||
| case ENGINE_CALLBACK_ENGINE_STOPPED: | |||
| case ENGINE_CALLBACK_QUIT: | |||
| gEngineRunning = false; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| send_server_side_message(msgBuf); | |||
| } | |||
| static const char* FileCallback(void* ptr, FileCallbackOpcode action, bool isDir, const char* title, const char* filter) | |||
| { | |||
| carla_stdout("FileCallback(%p, %u:%s, %s, %s, %s)", | |||
| ptr, (uint)action, FileCallbackOpcode(action), bool2str(isDir), title, filter); | |||
| char msgBuf[1024]; | |||
| std::snprintf(msgBuf, 1023, "fc %u %i \"%s\" \"%s\"", action, isDir, title, filter); | |||
| msgBuf[1023] = '\0'; | |||
| send_server_side_message(msgBuf); | |||
| // FIXME, need to wait for response! | |||
| return nullptr; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -74,6 +127,11 @@ void handle_carla_get_engine_driver_device_info(const std::shared_ptr<Session> s | |||
| void handle_carla_engine_init(const std::shared_ptr<Session> session) | |||
| { | |||
| // setup callbacks | |||
| carla_set_engine_callback(EngineCallback, nullptr); | |||
| carla_set_file_callback(FileCallback, nullptr); | |||
| // handle request now | |||
| const std::shared_ptr<const Request> request = session->get_request(); | |||
| const std::string driverName = request->get_path_parameter("driverName"); | |||
| @@ -89,12 +147,6 @@ void handle_carla_engine_close(const std::shared_ptr<Session> session) | |||
| session->close(resp ? OK : BAD_REQUEST); | |||
| } | |||
| void handle_carla_engine_idle(const std::shared_ptr<Session> session) | |||
| { | |||
| carla_engine_idle(); | |||
| session->close(OK); | |||
| } | |||
| void handle_carla_is_engine_running(const std::shared_ptr<Session> session) | |||
| { | |||
| const bool resp = carla_is_engine_running(); | |||
| @@ -109,10 +161,124 @@ void handle_carla_set_engine_about_to_close(const std::shared_ptr<Session> sessi | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT void carla_set_engine_option(EngineOption option, int value, const char* valueStr); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT bool carla_load_file(const char* filename); | |||
| CARLA_EXPORT bool carla_load_project(const char* filename); | |||
| CARLA_EXPORT bool carla_save_project(const char* filename); | |||
| CARLA_EXPORT bool carla_patchbay_connect(uint groupIdA, uint portIdA, uint groupIdB, uint portIdB); | |||
| CARLA_EXPORT bool carla_patchbay_disconnect(uint connectionId); | |||
| CARLA_EXPORT bool carla_patchbay_refresh(bool external); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT void carla_transport_play(); | |||
| CARLA_EXPORT void carla_transport_pause(); | |||
| CARLA_EXPORT void carla_transport_bpm(double bpm); | |||
| CARLA_EXPORT void carla_transport_relocate(uint64_t frame); | |||
| CARLA_EXPORT uint64_t carla_get_current_transport_frame(); | |||
| CARLA_EXPORT const CarlaTransportInfo* carla_get_transport_info(); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT uint32_t carla_get_current_plugin_count(); | |||
| CARLA_EXPORT uint32_t carla_get_max_plugin_number(); | |||
| CARLA_EXPORT bool carla_add_plugin(BinaryType btype, PluginType ptype, | |||
| const char* filename, const char* name, const char* label, int64_t uniqueId, | |||
| const void* extraPtr, uint options); | |||
| CARLA_EXPORT bool carla_remove_plugin(uint pluginId); | |||
| CARLA_EXPORT bool carla_remove_all_plugins(); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT const char* carla_rename_plugin(uint pluginId, const char* newName); | |||
| CARLA_EXPORT bool carla_clone_plugin(uint pluginId); | |||
| CARLA_EXPORT bool carla_replace_plugin(uint pluginId); | |||
| CARLA_EXPORT bool carla_switch_plugins(uint pluginIdA, uint pluginIdB); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT bool carla_load_plugin_state(uint pluginId, const char* filename); | |||
| CARLA_EXPORT bool carla_save_plugin_state(uint pluginId, const char* filename); | |||
| CARLA_EXPORT bool carla_export_plugin_lv2(uint pluginId, const char* lv2path); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT const CarlaPluginInfo* carla_get_plugin_info(uint pluginId); | |||
| CARLA_EXPORT const CarlaPortCountInfo* carla_get_audio_port_count_info(uint pluginId); | |||
| CARLA_EXPORT const CarlaPortCountInfo* carla_get_midi_port_count_info(uint pluginId); | |||
| CARLA_EXPORT const CarlaPortCountInfo* carla_get_parameter_count_info(uint pluginId); | |||
| CARLA_EXPORT const CarlaParameterInfo* carla_get_parameter_info(uint pluginId, uint32_t parameterId); | |||
| CARLA_EXPORT const CarlaScalePointInfo* carla_get_parameter_scalepoint_info(uint pluginId, uint32_t parameterId, uint32_t scalePointId); | |||
| CARLA_EXPORT const ParameterData* carla_get_parameter_data(uint pluginId, uint32_t parameterId); | |||
| CARLA_EXPORT const ParameterRanges* carla_get_parameter_ranges(uint pluginId, uint32_t parameterId); | |||
| CARLA_EXPORT const MidiProgramData* carla_get_midi_program_data(uint pluginId, uint32_t midiProgramId); | |||
| CARLA_EXPORT const CustomData* carla_get_custom_data(uint pluginId, uint32_t customDataId); | |||
| CARLA_EXPORT const char* carla_get_chunk_data(uint pluginId); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT uint32_t carla_get_parameter_count(uint pluginId); | |||
| CARLA_EXPORT uint32_t carla_get_program_count(uint pluginId); | |||
| CARLA_EXPORT uint32_t carla_get_midi_program_count(uint pluginId); | |||
| CARLA_EXPORT uint32_t carla_get_custom_data_count(uint pluginId); | |||
| CARLA_EXPORT const char* carla_get_parameter_text(uint pluginId, uint32_t parameterId); | |||
| CARLA_EXPORT const char* carla_get_program_name(uint pluginId, uint32_t programId); | |||
| CARLA_EXPORT const char* carla_get_midi_program_name(uint pluginId, uint32_t midiProgramId); | |||
| CARLA_EXPORT const char* carla_get_real_plugin_name(uint pluginId); | |||
| CARLA_EXPORT int32_t carla_get_current_program_index(uint pluginId); | |||
| CARLA_EXPORT int32_t carla_get_current_midi_program_index(uint pluginId); | |||
| CARLA_EXPORT float carla_get_default_parameter_value(uint pluginId, uint32_t parameterId); | |||
| CARLA_EXPORT float carla_get_current_parameter_value(uint pluginId, uint32_t parameterId); | |||
| CARLA_EXPORT float carla_get_internal_parameter_value(uint pluginId, int32_t parameterId); | |||
| CARLA_EXPORT float carla_get_input_peak_value(uint pluginId, bool isLeft); | |||
| CARLA_EXPORT float carla_get_output_peak_value(uint pluginId, bool isLeft); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT void carla_set_active(uint pluginId, bool onOff); | |||
| CARLA_EXPORT void carla_set_drywet(uint pluginId, float value); | |||
| CARLA_EXPORT void carla_set_volume(uint pluginId, float value); | |||
| CARLA_EXPORT void carla_set_balance_left(uint pluginId, float value); | |||
| CARLA_EXPORT void carla_set_balance_right(uint pluginId, float value); | |||
| CARLA_EXPORT void carla_set_panning(uint pluginId, float value); | |||
| CARLA_EXPORT void carla_set_ctrl_channel(uint pluginId, int8_t channel); | |||
| CARLA_EXPORT void carla_set_option(uint pluginId, uint option, bool yesNo); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT void carla_set_parameter_value(uint pluginId, uint32_t parameterId, float value); | |||
| CARLA_EXPORT void carla_set_parameter_midi_channel(uint pluginId, uint32_t parameterId, uint8_t channel); | |||
| CARLA_EXPORT void carla_set_parameter_midi_cc(uint pluginId, uint32_t parameterId, int16_t cc); | |||
| CARLA_EXPORT void carla_set_program(uint pluginId, uint32_t programId); | |||
| CARLA_EXPORT void carla_set_midi_program(uint pluginId, uint32_t midiProgramId); | |||
| CARLA_EXPORT void carla_set_custom_data(uint pluginId, const char* type, const char* key, const char* value); | |||
| CARLA_EXPORT void carla_set_chunk_data(uint pluginId, const char* chunkData); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT void carla_prepare_for_save(uint pluginId); | |||
| CARLA_EXPORT void carla_reset_parameters(uint pluginId); | |||
| CARLA_EXPORT void carla_randomize_parameters(uint pluginId); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT void carla_send_midi_note(uint pluginId, uint8_t channel, uint8_t note, uint8_t velocity); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| CARLA_EXPORT uint32_t carla_get_buffer_size(); | |||
| CARLA_EXPORT double carla_get_sample_rate(); | |||
| void handle_carla_get_last_error(const std::shared_ptr<Session> session) | |||
| { | |||
| const char* const buf = carla_get_last_error(); | |||
| session->close(OK, buf, { { "Content-Length", size_buf(buf) } } ); | |||
| } | |||
| CARLA_EXPORT const char* carla_get_host_osc_url_tcp(); | |||
| CARLA_EXPORT const char* carla_get_host_osc_url_udp(); | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| @@ -35,4 +35,6 @@ using restbed::OK; | |||
| CARLA_BACKEND_USE_NAMESPACE; | |||
| void send_server_side_message(const char* const message); | |||
| #endif // REST_COMMON_HPP_INCLUDED | |||
| @@ -26,9 +26,71 @@ | |||
| #include "carla-host.cpp" | |||
| #include "carla-utils.cpp" | |||
| #include "CarlaMutex.hpp" | |||
| #include "CarlaStringList.hpp" | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| std::vector<std::shared_ptr<Session>> gSessions; | |||
| CarlaStringList gSessionMessages; | |||
| CarlaMutex gSessionMessagesMutex; | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| void send_server_side_message(const char* const message) | |||
| { | |||
| const CarlaMutexLocker cml(gSessionMessagesMutex); | |||
| gSessionMessages.append(message); | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| void make_resource(Service& service, | |||
| static void register_server_side_handler(const std::shared_ptr<Session> session) | |||
| { | |||
| const auto headers = std::multimap<std::string, std::string> { | |||
| { "Connection", "keep-alive" }, | |||
| { "Cache-Control", "no-cache" }, | |||
| { "Content-Type", "text/event-stream" }, | |||
| { "Access-Control-Allow-Origin", "*" } //Only required for demo purposes. | |||
| }; | |||
| session->yield(OK, headers, [](const std::shared_ptr<Session> rsession) { | |||
| gSessions.push_back(rsession); | |||
| }); | |||
| } | |||
| static void event_stream_handler(void) | |||
| { | |||
| gSessions.erase( | |||
| std::remove_if(gSessions.begin(), gSessions.end(), | |||
| [](const std::shared_ptr<Session> &a) { | |||
| return a->is_closed(); | |||
| }), | |||
| gSessions.end()); | |||
| CarlaStringList messages; | |||
| { | |||
| const CarlaMutexLocker cml(gSessionMessagesMutex); | |||
| if (gSessionMessages.count() > 0) | |||
| gSessionMessages.moveTo(messages); | |||
| } | |||
| for (auto message : messages) | |||
| { | |||
| std::puts(message); | |||
| for (auto session : gSessions) | |||
| session->yield(OK, message); | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| static void make_resource(Service& service, | |||
| const char* const path, | |||
| const std::function<void (const std::shared_ptr<Session>)>& callback) | |||
| { | |||
| @@ -44,6 +106,14 @@ int main(int, const char**) | |||
| { | |||
| Service service; | |||
| // server-side messages | |||
| { | |||
| std::shared_ptr<Resource> resource = std::make_shared<Resource>(); | |||
| resource->set_path("/stream"); | |||
| resource->set_method_handler("GET", register_server_side_handler); | |||
| service.publish(resource); | |||
| } | |||
| // carla-host | |||
| make_resource(service, "/get_engine_driver_count", handle_carla_get_engine_driver_count); | |||
| make_resource(service, "/get_engine_driver_name/{index: .*}", handle_carla_get_engine_driver_name); | |||
| @@ -52,7 +122,6 @@ int main(int, const char**) | |||
| make_resource(service, "/engine_init/{driverName: .*}/{clientName: .*}", handle_carla_engine_init); | |||
| make_resource(service, "/engine_close", handle_carla_engine_close); | |||
| make_resource(service, "/engine_idle", handle_carla_engine_idle); | |||
| make_resource(service, "/is_engine_running", handle_carla_is_engine_running); | |||
| make_resource(service, "/set_engine_about_to_close", handle_carla_set_engine_about_to_close); | |||
| @@ -66,6 +135,10 @@ int main(int, const char**) | |||
| make_resource(service, "/get_cached_plugin_count/{ptype: .*}/{pluginPath: .*}", handle_carla_get_cached_plugin_count); | |||
| make_resource(service, "/get_cached_plugin_info/{ptype: .*}/{index: .*}", handle_carla_get_cached_plugin_info); | |||
| // schedule events | |||
| service.schedule(engine_idle_handler, std::chrono::milliseconds(33)); // ~30Hz | |||
| service.schedule(event_stream_handler, std::chrono::milliseconds(500)); | |||
| std::shared_ptr<Settings> settings = std::make_shared<Settings>(); | |||
| settings->set_port(2228); | |||
| settings->set_default_header("Connection", "close"); | |||
| @@ -159,6 +159,7 @@ public: | |||
| return *this; | |||
| } | |||
| #if 0 | |||
| T& operator*() noexcept | |||
| { | |||
| static T& fallback(_getFallback()); | |||
| @@ -168,6 +169,7 @@ public: | |||
| return data->value; | |||
| } | |||
| #endif | |||
| const T& operator*() const noexcept | |||
| { | |||
| @@ -180,8 +182,8 @@ public: | |||
| } | |||
| private: | |||
| ListHead* fEntry; | |||
| ListHead* fEntry2; | |||
| const ListHead* fEntry; | |||
| const ListHead* fEntry2; | |||
| static T& _getFallback() | |||
| { | |||