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"
48 static gboolean
app_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, GError
**error
)
52 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "NULL command");
55 if (cmd
->command
[0] != '/')
57 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Invalid global command path '%s'", cmd
->command
);
60 const char *obj
= &cmd
->command
[1];
61 const char *pos
= strchr(obj
, '/');
64 if (!strncmp(obj
, "master/", 7))
65 return cbox_execute_sub(&app
.rt
->master
->cmd_target
, fb
, cmd
, pos
, error
);
67 if (!strncmp(obj
, "config/", 7))
68 return cbox_execute_sub(&app
.config_cmd_target
, fb
, cmd
, pos
, error
);
70 if (!strncmp(obj
, "scene/", 6))
71 return cbox_execute_sub(&app
.rt
->scene
->cmd_target
, fb
, cmd
, pos
, error
);
73 if (!strncmp(obj
, "rt/", 3))
74 return cbox_execute_sub(&app
.rt
->cmd_target
, fb
, cmd
, pos
, error
);
76 if (!strncmp(obj
, "io/", 3))
77 return cbox_execute_sub(&app
.io
.cmd_target
, fb
, cmd
, pos
, error
);
79 if (!strncmp(obj
, "song/", 5))
80 return cbox_execute_sub(&app
.rt
->master
->song
->cmd_target
, fb
, cmd
, pos
, error
);
82 if (!strncmp(obj
, "waves/", 6))
83 return cbox_execute_sub(&cbox_waves_cmd_target
, fb
, cmd
, pos
, error
);
85 if (!strncmp(obj
, "doc/", 4))
86 return cbox_execute_sub(cbox_document_get_cmd_target(app
.document
), fb
, cmd
, pos
, error
);
89 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
);
94 if (!strcmp(obj
, "on_idle") && !strcmp(cmd
->arg_types
, ""))
96 return cbox_app_on_idle(fb
, error
);
99 if (!strcmp(obj
, "send_event") && (!strcmp(cmd
->arg_types
, "iii") || !strcmp(cmd
->arg_types
, "ii") || !strcmp(cmd
->arg_types
, "i")))
101 int mcmd
= CBOX_ARG_I(cmd
, 0);
102 int arg1
= 0, arg2
= 0;
103 if (cmd
->arg_types
[1] == 'i')
105 arg1
= CBOX_ARG_I(cmd
, 1);
106 if (cmd
->arg_types
[2] == 'i')
107 arg2
= CBOX_ARG_I(cmd
, 2);
109 struct cbox_midi_buffer buf
;
110 cbox_midi_buffer_init(&buf
);
111 cbox_midi_buffer_write_inline(&buf
, 0, mcmd
, arg1
, arg2
);
112 cbox_rt_send_events(app
.rt
, &buf
);
116 if (!strcmp(obj
, "play_note") && !strcmp(cmd
->arg_types
, "iii"))
118 int channel
= CBOX_ARG_I(cmd
, 0);
119 int note
= CBOX_ARG_I(cmd
, 1);
120 int velocity
= CBOX_ARG_I(cmd
, 2);
121 struct cbox_midi_buffer buf
;
122 cbox_midi_buffer_init(&buf
);
123 cbox_midi_buffer_write_inline(&buf
, 0, 0x90 + ((channel
- 1) & 15), note
& 127, velocity
& 127);
124 cbox_midi_buffer_write_inline(&buf
, 1, 0x80 + ((channel
- 1) & 15), note
& 127, velocity
& 127);
125 cbox_rt_send_events(app
.rt
, &buf
);
129 if (!strcmp(obj
, "update_playback") && !strcmp(cmd
->arg_types
, ""))
131 cbox_rt_update_song_playback(app
.rt
);
135 if (!strcmp(obj
, "get_pattern") && !strcmp(cmd
->arg_types
, ""))
137 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
140 if (app
.rt
->master
->song
&& app
.rt
->master
->song
->tracks
)
142 struct cbox_track
*track
= app
.rt
->master
->song
->tracks
->data
;
145 struct cbox_track_item
*item
= track
->items
->data
;
146 struct cbox_midi_pattern
*pattern
= item
->pattern
;
148 struct cbox_blob
*blob
= cbox_midi_pattern_to_blob(pattern
, &length
);
149 gboolean res
= cbox_execute_on(fb
, NULL
, "/pattern", "bi", error
, blob
, length
);
150 cbox_blob_destroy(blob
);
158 if (!strcmp(obj
, "new_meter") && !strcmp(cmd
->arg_types
, ""))
160 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
163 struct cbox_meter
*meter
= cbox_meter_new(app
.document
, cbox_rt_get_sample_rate(app
.rt
));
165 return cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, meter
);
168 if (!strcmp(obj
, "new_recorder") && !strcmp(cmd
->arg_types
, "s"))
170 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
173 struct cbox_recorder
*rec
= cbox_recorder_new_stream(app
.rt
, CBOX_ARG_S(cmd
, 0));
175 return cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, rec
);
178 if (!strcmp(obj
, "new_scene") && !strcmp(cmd
->arg_types
, "ii"))
180 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
183 struct cbox_rt
*rt
= cbox_rt_new(app
.document
);
184 cbox_rt_set_offline(rt
, CBOX_ARG_I(cmd
, 0), CBOX_ARG_I(cmd
, 1));
185 struct cbox_scene
*rec
= cbox_scene_new(app
.document
, rt
, TRUE
);
187 return cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, rec
);
190 if (!strcmp(obj
, "print_s") && !strcmp(cmd
->arg_types
, "s"))
192 g_message("Print: %s", CBOX_ARG_S(cmd
, 0));
196 if (!strcmp(obj
, "print_i") && !strcmp(cmd
->arg_types
, "i"))
198 g_message("Print: %d", CBOX_ARG_I(cmd
, 0));
202 if (!strcmp(obj
, "print_f") && !strcmp(cmd
->arg_types
, "f"))
204 g_message("Print: %f", CBOX_ARG_F(cmd
, 0));
209 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
);
214 struct config_foreach_data
218 struct cbox_command_target
*fb
;
223 void api_config_cb(void *user_data
, const char *key
)
225 struct config_foreach_data
*cfd
= user_data
;
228 if (cfd
->prefix
&& strncmp(cfd
->prefix
, key
, strlen(cfd
->prefix
)))
231 if (!cbox_execute_on(cfd
->fb
, NULL
, cfd
->command
, "s", cfd
->error
, key
))
233 cfd
->success
= FALSE
;
238 static gboolean
config_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, GError
**error
)
240 if (!strcmp(cmd
->command
, "/sections") && (!strcmp(cmd
->arg_types
, "") || !strcmp(cmd
->arg_types
, "s")))
242 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
245 struct config_foreach_data cfd
= {cmd
->arg_types
[0] == 's' ? CBOX_ARG_S(cmd
, 0) : NULL
, "/section", fb
, error
, TRUE
};
246 cbox_config_foreach_section(api_config_cb
, &cfd
);
249 else if (!strcmp(cmd
->command
, "/keys") && (!strcmp(cmd
->arg_types
, "s") || !strcmp(cmd
->arg_types
, "ss")))
251 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
254 struct config_foreach_data cfd
= {cmd
->arg_types
[1] == 's' ? CBOX_ARG_S(cmd
, 1) : NULL
, "/key", fb
, error
, TRUE
};
255 cbox_config_foreach_key(api_config_cb
, CBOX_ARG_S(cmd
, 0), &cfd
);
258 else if (!strcmp(cmd
->command
, "/get") && !strcmp(cmd
->arg_types
, "ss"))
260 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
263 const char *value
= cbox_config_get_string(CBOX_ARG_S(cmd
, 0), CBOX_ARG_S(cmd
, 1));
266 return cbox_execute_on(fb
, NULL
, "/value", "s", error
, value
);
268 else if (!strcmp(cmd
->command
, "/set") && !strcmp(cmd
->arg_types
, "sss"))
270 cbox_config_set_string(CBOX_ARG_S(cmd
, 0), CBOX_ARG_S(cmd
, 1), CBOX_ARG_S(cmd
, 2));
273 else if (!strcmp(cmd
->command
, "/delete") && !strcmp(cmd
->arg_types
, "ss"))
275 cbox_config_remove_key(CBOX_ARG_S(cmd
, 0), CBOX_ARG_S(cmd
, 1));
278 else if (!strcmp(cmd
->command
, "/delete_section") && !strcmp(cmd
->arg_types
, "s"))
280 cbox_config_remove_section(CBOX_ARG_S(cmd
, 0));
283 else if (!strcmp(cmd
->command
, "/save") && !strcmp(cmd
->arg_types
, ""))
285 return cbox_config_save(NULL
, error
);
287 else if (!strcmp(cmd
->command
, "/save") && !strcmp(cmd
->arg_types
, "s"))
289 return cbox_config_save(CBOX_ARG_S(cmd
, 0), error
);
293 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
);
298 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
300 gboolean
cbox_app_on_idle(struct cbox_command_target
*fb
, GError
**error
)
304 GError
*error2
= NULL
;
305 if (cbox_io_get_disconnect_status(&app
.io
, &error2
))
306 cbox_io_poll_ports(&app
.io
, fb
);
310 g_error_free(error2
);
311 int auto_reconnect
= cbox_config_get_int("io", "auto_reconnect", 0);
312 if (auto_reconnect
> 0)
314 sleep(auto_reconnect
);
315 GError
*error2
= NULL
;
316 if (!cbox_io_cycle(&app
.io
, fb
, &error2
))
318 gboolean suppress
= FALSE
;
320 suppress
= cbox_execute_on(fb
, NULL
, "/io/cycle_failed", "s", NULL
, error2
->message
);
322 g_warning("Cannot cycle the I/O: %s", (error2
&& error2
->message
) ? error2
->message
: "Unknown error");
323 g_error_free(error2
);
328 cbox_execute_on(fb
, NULL
, "/io/cycled", "", NULL
);
335 // Process results of asynchronous commands
336 cbox_rt_handle_cmd_queue(app
.rt
);
338 const struct cbox_midi_buffer
*midi_in
= cbox_rt_get_input_midi_data(app
.rt
);
339 // If no feedback, the input events are lost - probably better than if
340 // they filled up the input buffer needlessly.
343 for (int i
= 0; i
< midi_in
->count
; i
++)
345 const struct cbox_midi_event
*event
= cbox_midi_buffer_get_event(midi_in
, i
);
346 const uint8_t *data
= cbox_midi_event_get_data(event
);
347 // XXXKF doesn't handle SysEx properly yet, only 3-byte values
348 if (event
->size
<= 3)
350 if (!cbox_execute_on(fb
, NULL
, "/io/midi/simple_event", "iii" + (3 - event
->size
), error
, data
[0], data
[1], data
[2]))
354 g_warning("Propagating 'long' events not implemented yet.");
361 struct cbox_app app
=
364 .current_scene_name
= NULL
,
367 .process_cmd
= app_process_cmd
,
372 .process_cmd
= config_process_cmd
,