/* Calf Box, an open source musical instrument. Copyright (C) 2010-2011 Krzysztof Foltman This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "auxbus.h" #include "config-api.h" #include "instr.h" #include "module.h" #include "io.h" #include "rt.h" #include "scene.h" #include #include CBOX_CLASS_DEFINITION_ROOT(cbox_instrument) static gboolean cbox_instrument_output_process_cmd(struct cbox_instrument *instr, struct cbox_instrument_output *output, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, GError **error) { if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, "")) { if (!(cbox_execute_on(fb, NULL, "/gain_linear", "f", error, output->gain_obj.lin_gain) && cbox_execute_on(fb, NULL, "/gain", "f", error, output->gain_obj.db_gain) && cbox_execute_on(fb, NULL, "/output", "i", error, output->output_bus + 1))) return FALSE; return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error); } if (!strcmp(subcmd, "/gain") && !strcmp(cmd->arg_types, "f")) { // XXXKF this needs proper handling of concurrency/race conditions, might // be built into the gain class in future. cbox_gain_set_db(&output->gain_obj, CBOX_ARG_F(cmd, 0)); return TRUE; } if (!strcmp(subcmd, "/output") && !strcmp(cmd->arg_types, "i")) { int obus = CBOX_ARG_I(cmd, 0); int max_outputs = instr->scene->rt->io ? instr->scene->rt->io->io_env.output_count : 2; int max_obus = 1 + (max_outputs - 1) / 2; if (obus < 0 || obus > max_obus) { g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid output %d (must be between 1 and %d, or 0 for none)", obus, max_obus); return FALSE; } output->output_bus = obus - 1; return TRUE; } if (!strncmp(subcmd, "/rec_dry/", 9)) return cbox_execute_sub(&output->rec_dry.cmd_target, fb, cmd, subcmd + 8, error); if (!strncmp(subcmd, "/rec_wet/", 9)) return cbox_execute_sub(&output->rec_wet.cmd_target, fb, cmd, subcmd + 8, error); return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error); } static gboolean cbox_instrument_aux_process_cmd(struct cbox_instrument *instr, struct cbox_instrument_output *output, int id, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, GError **error) { if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, "")) { if (!cbox_check_fb_channel(fb, cmd->command, error)) return FALSE; if (!(cbox_execute_on(fb, NULL, "/gain_linear", "f", error, output->gain_obj.lin_gain) && cbox_execute_on(fb, NULL, "/gain", "f", error, output->gain_obj.db_gain) && cbox_execute_on(fb, NULL, "/bus", "s", error, instr->aux_output_names[id] ? instr->aux_output_names[id] : ""))) return FALSE; return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error); } else if (!strcmp(subcmd, "/bus") && !strcmp(cmd->arg_types, "s")) { struct cbox_scene *scene = instr->scene; if (!CBOX_ARG_S(cmd, 0)) { struct cbox_aux_bus *old_bus = cbox_rt_swap_pointers(instr->module->rt, (void **)&instr->aux_outputs[id], NULL); if (old_bus) cbox_aux_bus_unref(old_bus); return TRUE; } for (uint32_t i = 0; i < scene->aux_bus_count; i++) { if (!scene->aux_buses[i]) continue; if (!strcmp(scene->aux_buses[i]->name, CBOX_ARG_S(cmd, 0))) { g_free(instr->aux_output_names[id]); instr->aux_output_names[id] = g_strdup(scene->aux_buses[i]->name); cbox_aux_bus_ref(scene->aux_buses[i]); struct cbox_aux_bus *old_bus = cbox_rt_swap_pointers(instr->module->rt, (void **)&instr->aux_outputs[id], scene->aux_buses[i]); if (old_bus) cbox_aux_bus_unref(old_bus); return TRUE; } } g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown aux bus: %s", CBOX_ARG_S(cmd, 0)); return FALSE; } else if (!strcmp(subcmd, "/output") && !strcmp(cmd->arg_types, "i")) // not supported { cbox_set_command_error(error, cmd); return FALSE; } else // otherwise, treat just like an command on normal (non-aux) output return cbox_instrument_output_process_cmd(instr, output, fb, cmd, subcmd, error); } gboolean cbox_instrument_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) { struct cbox_instrument *instr = ct->user_data; const char *subcommand = NULL; int index = 0; int aux_offset = instr->module->aux_offset / 2; if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) { if (!cbox_check_fb_channel(fb, cmd->command, error)) return FALSE; if (!cbox_execute_on(fb, NULL, "/engine", "s", error, instr->module->engine_name)) return FALSE; if (!cbox_execute_on(fb, NULL, "/aux_offset", "i", error, instr->module->aux_offset / 2 + 1)) return FALSE; if (!cbox_execute_on(fb, NULL, "/outputs", "i", error, instr->module->outputs / 2)) return FALSE; return CBOX_OBJECT_DEFAULT_STATUS(instr, fb, error); } else if (cbox_parse_path_part_int(cmd, "/output/", &subcommand, &index, 1, aux_offset, error)) { if (!subcommand) return FALSE; if (index < 1 || index > (int)(1 + instr->module->aux_offset)) { g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", index, instr->module->aux_offset); return FALSE; } return cbox_instrument_output_process_cmd(instr, &instr->outputs[index - 1], fb, cmd, subcommand, error); } else if (cbox_parse_path_part_int(cmd, "/aux/", &subcommand, &index, 1, instr->aux_output_count, error)) { if (!subcommand) return FALSE; int acount = 1 + instr->module->outputs - instr->module->aux_offset; if (index < 1 || index > acount) { g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", index, acount); return FALSE; } return cbox_instrument_aux_process_cmd(instr, &instr->outputs[aux_offset + index - 1], index - 1, fb, cmd, subcommand, error); } else if (!strncmp(cmd->command, "/engine/",8)) { if (!instr->module->cmd_target.process_cmd) { g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "The engine %s has no command target defined", instr->module->engine_name); return FALSE; } return cbox_execute_sub(&instr->module->cmd_target, fb, cmd, cmd->command + 7, error); } else if (!strcmp(cmd->command, "/move_to") && !strcmp(cmd->arg_types, "si")) { struct cbox_scene *new_scene = (struct cbox_scene *)CBOX_ARG_O(cmd, 0, instr->scene, cbox_scene, error); if (!new_scene) return FALSE; int dstpos = CBOX_ARG_I(cmd, 1) - 1; if (dstpos < 0 || (uint32_t)dstpos > new_scene->layer_count) { g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d or 0 for append)", dstpos + 1, 1 + new_scene->layer_count); return FALSE; } return cbox_scene_move_instrument_to(instr->scene, instr, new_scene, dstpos, error); } else return cbox_object_default_process_cmd(ct, fb, cmd, error); } void cbox_instrument_destroy_if_unused(struct cbox_instrument *instrument) { if (instrument->refcount == 0) CBOX_DELETE(instrument); } void cbox_instrument_destroyfunc(struct cbox_objhdr *objhdr) { struct cbox_instrument *instrument = CBOX_H2O(objhdr); assert(instrument->refcount == 0); for (uint32_t i = 0; i < (uint32_t)instrument->module->outputs >> 1; i ++) { cbox_instrument_output_uninit(&instrument->outputs[i]); } free(instrument->outputs); for (uint32_t i = 0; i < instrument->aux_output_count; i++) { g_free(instrument->aux_output_names[i]); } free(instrument->aux_output_names); free(instrument->aux_outputs); CBOX_DELETE(instrument->module); free(instrument); } void cbox_instrument_unref_aux_buses(struct cbox_instrument *instrument) { for (uint32_t j = 0; j < instrument->aux_output_count; j++) { if (instrument->aux_outputs[j]) cbox_aux_bus_unref(instrument->aux_outputs[j]); } } void cbox_instrument_disconnect_aux_bus(struct cbox_instrument *instrument, struct cbox_aux_bus *bus) { for (uint32_t j = 0; j < instrument->aux_output_count; j++) { if (instrument->aux_outputs[j] == bus) { cbox_aux_bus_unref(instrument->aux_outputs[j]); instrument->aux_outputs[j] = NULL; } } } void cbox_instrument_output_init(struct cbox_instrument_output *output, struct cbox_scene *scene, uint32_t max_numsamples) { cbox_recording_source_init(&output->rec_dry, scene, max_numsamples, 2); cbox_recording_source_init(&output->rec_wet, scene, max_numsamples, 2); output->insert = NULL; output->output_bus = 0; cbox_gain_init(&output->gain_obj); } void cbox_instrument_output_uninit(struct cbox_instrument_output *output) { cbox_recording_source_uninit(&output->rec_dry); cbox_recording_source_uninit(&output->rec_wet); if (output->insert) { CBOX_DELETE(output->insert); output->insert = NULL; } }