You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
10 KiB
251 lines
10 KiB
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "auxbus.h"
|
|
#include "config-api.h"
|
|
#include "instr.h"
|
|
#include "module.h"
|
|
#include "io.h"
|
|
#include "rt.h"
|
|
#include "scene.h"
|
|
#include <assert.h>
|
|
#include <glib.h>
|
|
|
|
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;
|
|
}
|
|
}
|
|
|