2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2013 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "config-api.h"
49 static gboolean
lookup_midi_merger(const char *output
, struct cbox_midi_merger
**pmerger
, GError
**error
)
53 struct cbox_uuid uuid
;
54 if (!cbox_uuid_fromstring(&uuid
, output
, error
))
57 *pmerger
= cbox_rt_get_midi_output(app
.rt
, &uuid
);
60 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Unknown MIDI output UUID: '%s'", output
);
66 if (!app
.engine
->scene_count
)
68 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Scene not set");
71 *pmerger
= &app
.engine
->scenes
[0]->scene_input_merger
;
76 static gboolean
app_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, GError
**error
)
80 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "NULL command");
83 if (cmd
->command
[0] != '/')
85 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Invalid global command path '%s'", cmd
->command
);
88 const char *obj
= &cmd
->command
[1];
89 const char *pos
= strchr(obj
, '/');
92 if (!strncmp(obj
, "master/", 7))
93 return cbox_execute_sub(&app
.engine
->master
->cmd_target
, fb
, cmd
, pos
, error
);
95 if (!strncmp(obj
, "config/", 7))
96 return cbox_execute_sub(&app
.config_cmd_target
, fb
, cmd
, pos
, error
);
98 if (!strncmp(obj
, "scene/", 6))
99 return cbox_execute_sub(&app
.engine
->scenes
[0]->cmd_target
, fb
, cmd
, pos
, error
);
101 if (!strncmp(obj
, "engine/", 7))
102 return cbox_execute_sub(&app
.engine
->cmd_target
, fb
, cmd
, pos
, error
);
104 if (!strncmp(obj
, "rt/", 3))
105 return cbox_execute_sub(&app
.rt
->cmd_target
, fb
, cmd
, pos
, error
);
107 if (!strncmp(obj
, "io/", 3))
108 return cbox_execute_sub(&app
.io
.cmd_target
, fb
, cmd
, pos
, error
);
110 if (!strncmp(obj
, "song/", 5) && app
.engine
->master
->song
)
111 return cbox_execute_sub(&app
.engine
->master
->song
->cmd_target
, fb
, cmd
, pos
, error
);
113 if (!strncmp(obj
, "waves/", 6))
114 return cbox_execute_sub(&cbox_waves_cmd_target
, fb
, cmd
, pos
, error
);
116 if (!strncmp(obj
, "doc/", 4))
117 return cbox_execute_sub(cbox_document_get_cmd_target(app
.document
), fb
, cmd
, pos
, error
);
120 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Unknown combination of target path and argument: '%s', '%s'", cmd
->command
, cmd
->arg_types
);
125 if (!strcmp(obj
, "on_idle") && !strcmp(cmd
->arg_types
, ""))
127 return cbox_app_on_idle(fb
, error
);
130 if (!strcmp(obj
, "send_event_to") && (!strcmp(cmd
->arg_types
, "siii") || !strcmp(cmd
->arg_types
, "sii") || !strcmp(cmd
->arg_types
, "si")))
132 const char *output
= CBOX_ARG_S(cmd
, 0);
133 struct cbox_midi_merger
*merger
= NULL
;
134 if (!lookup_midi_merger(output
, &merger
, error
))
136 int mcmd
= CBOX_ARG_I(cmd
, 1);
137 int arg1
= 0, arg2
= 0;
138 if (cmd
->arg_types
[2] == 'i')
140 arg1
= CBOX_ARG_I(cmd
, 2);
141 if (cmd
->arg_types
[3] == 'i')
142 arg2
= CBOX_ARG_I(cmd
, 3);
144 struct cbox_midi_buffer buf
;
145 cbox_midi_buffer_init(&buf
);
146 cbox_midi_buffer_write_inline(&buf
, 0, mcmd
, arg1
, arg2
);
147 cbox_engine_send_events_to(app
.engine
, merger
, &buf
);
151 if (!strcmp(obj
, "send_sysex_to") && !strcmp(cmd
->arg_types
, "sb"))
153 const char *output
= CBOX_ARG_S(cmd
, 0);
154 struct cbox_midi_merger
*merger
= NULL
;
155 if (!lookup_midi_merger(output
, &merger
, error
))
157 const struct cbox_blob
*blob
= CBOX_ARG_B(cmd
, 1);
158 struct cbox_midi_buffer buf
;
159 cbox_midi_buffer_init(&buf
);
160 cbox_midi_buffer_write_event(&buf
, 0, blob
->data
, blob
->size
);
161 cbox_engine_send_events_to(app
.engine
, merger
, &buf
);
165 if (!strcmp(obj
, "update_playback") && !strcmp(cmd
->arg_types
, ""))
167 cbox_engine_update_song_playback(app
.engine
);
171 if (!strcmp(obj
, "get_pattern") && !strcmp(cmd
->arg_types
, ""))
173 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
176 if (app
.engine
->master
->song
&& app
.engine
->master
->song
->tracks
)
178 struct cbox_track
*track
= app
.engine
->master
->song
->tracks
->data
;
181 struct cbox_track_item
*item
= track
->items
->data
;
182 struct cbox_midi_pattern
*pattern
= item
->pattern
;
184 struct cbox_blob
*blob
= cbox_midi_pattern_to_blob(pattern
, &length
);
185 gboolean res
= cbox_execute_on(fb
, NULL
, "/pattern", "bi", error
, blob
, length
);
186 cbox_blob_destroy(blob
);
194 if (!strcmp(obj
, "new_meter") && !strcmp(cmd
->arg_types
, ""))
196 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
199 struct cbox_meter
*meter
= cbox_meter_new(app
.document
, app
.rt
->io_env
.srate
);
201 return cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, meter
);
204 if (!strcmp(obj
, "new_engine") && !strcmp(cmd
->arg_types
, "ii"))
206 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
209 struct cbox_engine
*e
= cbox_engine_new(app
.document
, NULL
);
210 e
->io_env
.srate
= CBOX_ARG_I(cmd
, 0);
211 e
->io_env
.buffer_size
= CBOX_ARG_I(cmd
, 1);
213 return e
? cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, e
) : FALSE
;
216 if (!strcmp(obj
, "print_s") && !strcmp(cmd
->arg_types
, "s"))
218 g_message("Print: %s", CBOX_ARG_S(cmd
, 0));
222 if (!strcmp(obj
, "print_i") && !strcmp(cmd
->arg_types
, "i"))
224 g_message("Print: %d", CBOX_ARG_I(cmd
, 0));
228 if (!strcmp(obj
, "print_f") && !strcmp(cmd
->arg_types
, "f"))
230 g_message("Print: %f", CBOX_ARG_F(cmd
, 0));
235 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Unknown combination of target path and argument: '%s', '%s'", cmd
->command
, cmd
->arg_types
);
240 struct config_foreach_data
244 struct cbox_command_target
*fb
;
249 void api_config_cb(void *user_data
, const char *key
)
251 struct config_foreach_data
*cfd
= user_data
;
254 if (cfd
->prefix
&& strncmp(cfd
->prefix
, key
, strlen(cfd
->prefix
)))
257 if (!cbox_execute_on(cfd
->fb
, NULL
, cfd
->command
, "s", cfd
->error
, key
))
259 cfd
->success
= FALSE
;
264 static gboolean
config_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, GError
**error
)
266 if (!strcmp(cmd
->command
, "/sections") && (!strcmp(cmd
->arg_types
, "") || !strcmp(cmd
->arg_types
, "s")))
268 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
271 struct config_foreach_data cfd
= {cmd
->arg_types
[0] == 's' ? CBOX_ARG_S(cmd
, 0) : NULL
, "/section", fb
, error
, TRUE
};
272 cbox_config_foreach_section(api_config_cb
, &cfd
);
275 else if (!strcmp(cmd
->command
, "/keys") && (!strcmp(cmd
->arg_types
, "s") || !strcmp(cmd
->arg_types
, "ss")))
277 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
280 struct config_foreach_data cfd
= {cmd
->arg_types
[1] == 's' ? CBOX_ARG_S(cmd
, 1) : NULL
, "/key", fb
, error
, TRUE
};
281 cbox_config_foreach_key(api_config_cb
, CBOX_ARG_S(cmd
, 0), &cfd
);
284 else if (!strcmp(cmd
->command
, "/get") && !strcmp(cmd
->arg_types
, "ss"))
286 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
289 const char *value
= cbox_config_get_string(CBOX_ARG_S(cmd
, 0), CBOX_ARG_S(cmd
, 1));
292 return cbox_execute_on(fb
, NULL
, "/value", "s", error
, value
);
294 else if (!strcmp(cmd
->command
, "/set") && !strcmp(cmd
->arg_types
, "sss"))
296 cbox_config_set_string(CBOX_ARG_S(cmd
, 0), CBOX_ARG_S(cmd
, 1), CBOX_ARG_S(cmd
, 2));
299 else if (!strcmp(cmd
->command
, "/delete") && !strcmp(cmd
->arg_types
, "ss"))
301 cbox_config_remove_key(CBOX_ARG_S(cmd
, 0), CBOX_ARG_S(cmd
, 1));
304 else if (!strcmp(cmd
->command
, "/delete_section") && !strcmp(cmd
->arg_types
, "s"))
306 cbox_config_remove_section(CBOX_ARG_S(cmd
, 0));
309 else if (!strcmp(cmd
->command
, "/save") && !strcmp(cmd
->arg_types
, ""))
311 return cbox_config_save(NULL
, error
);
313 else if (!strcmp(cmd
->command
, "/save") && !strcmp(cmd
->arg_types
, "s"))
315 return cbox_config_save(CBOX_ARG_S(cmd
, 0), error
);
319 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Unknown combination of target path and argument: '%s', '%s'", cmd
->command
, cmd
->arg_types
);
324 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
326 gboolean
cbox_app_on_idle(struct cbox_command_target
*fb
, GError
**error
)
330 GError
*error2
= NULL
;
331 if (cbox_io_get_disconnect_status(&app
.io
, &error2
))
332 cbox_io_poll_ports(&app
.io
, fb
);
336 g_error_free(error2
);
337 int auto_reconnect
= cbox_config_get_int("io", "auto_reconnect", 0);
338 if (auto_reconnect
> 0)
340 sleep(auto_reconnect
);
341 GError
*error2
= NULL
;
342 if (!cbox_io_cycle(&app
.io
, fb
, &error2
))
344 gboolean suppress
= FALSE
;
346 suppress
= cbox_execute_on(fb
, NULL
, "/io/cycle_failed", "s", NULL
, error2
->message
);
348 g_warning("Cannot cycle the I/O: %s", (error2
&& error2
->message
) ? error2
->message
: "Unknown error");
349 g_error_free(error2
);
354 cbox_execute_on(fb
, NULL
, "/io/cycled", "", NULL
);
361 // Process results of asynchronous commands
362 cbox_rt_handle_cmd_queue(app
.rt
);
364 if (!cbox_midi_appsink_send_to(&app
.engine
->appsink
, fb
, error
))
370 struct cbox_app app
=
373 .current_scene_name
= NULL
,
376 .process_cmd
= app_process_cmd
,
381 .process_cmd
= config_process_cmd
,