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.
626 lines
22 KiB
626 lines
22 KiB
3 years ago
|
/*
|
||
|
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 "app.h"
|
||
|
#include "config.h"
|
||
|
#include "config-api.h"
|
||
|
#include "engine.h"
|
||
|
#include "errors.h"
|
||
|
#include "hwcfg.h"
|
||
|
#include "io.h"
|
||
|
#include "meter.h"
|
||
|
#include "midi.h"
|
||
|
#include "mididest.h"
|
||
|
#include "recsrc.h"
|
||
|
#include "seq.h"
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <glib.h>
|
||
|
|
||
|
const char *cbox_io_section = "io";
|
||
|
|
||
|
gboolean cbox_io_init(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error)
|
||
|
{
|
||
|
#if USE_JACK
|
||
|
#if USE_LIBUSB
|
||
|
if (cbox_config_get_int(cbox_io_section, "use_usb", 0))
|
||
|
return cbox_io_init_usb(io, params, fb, error);
|
||
|
#endif
|
||
|
return cbox_io_init_jack(io, params, fb, error);
|
||
|
#else
|
||
|
#if USE_LIBUSB
|
||
|
return cbox_io_init_usb(io, params, fb, error);
|
||
|
#endif
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int cbox_io_get_sample_rate(struct cbox_io *io)
|
||
|
{
|
||
|
return io->impl->getsampleratefunc(io->impl);
|
||
|
}
|
||
|
|
||
|
void cbox_io_poll_ports(struct cbox_io *io, struct cbox_command_target *fb)
|
||
|
{
|
||
|
io->impl->pollfunc(io->impl, fb);
|
||
|
}
|
||
|
|
||
|
int cbox_io_get_midi_data(struct cbox_io *io, struct cbox_midi_buffer *destination)
|
||
|
{
|
||
|
return io->impl->getmidifunc(io->impl, destination);
|
||
|
}
|
||
|
|
||
|
int cbox_io_start(struct cbox_io *io, struct cbox_io_callbacks *cb, struct cbox_command_target *fb)
|
||
|
{
|
||
|
io->cb = cb;
|
||
|
return io->impl->startfunc(io->impl, fb, NULL);
|
||
|
}
|
||
|
|
||
|
gboolean cbox_io_get_disconnect_status(struct cbox_io *io, GError **error)
|
||
|
{
|
||
|
return io->impl->getstatusfunc(io->impl, error);
|
||
|
}
|
||
|
|
||
|
gboolean cbox_io_cycle(struct cbox_io *io, struct cbox_command_target *fb, GError **error)
|
||
|
{
|
||
|
return io->impl->cyclefunc(io->impl, fb, error);
|
||
|
}
|
||
|
|
||
|
int cbox_io_stop(struct cbox_io *io)
|
||
|
{
|
||
|
int result = io->impl->stopfunc(io->impl, NULL);
|
||
|
if (io->cb && io->cb->on_stopped)
|
||
|
io->cb->on_stopped(io->cb->user_data);
|
||
|
io->cb = NULL;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
struct cbox_midi_output *cbox_io_get_midi_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid)
|
||
|
{
|
||
|
if (uuid)
|
||
|
{
|
||
|
for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_midi_output *midiout = p->data;
|
||
|
if (!midiout->removing && cbox_uuid_equal(&midiout->uuid, uuid))
|
||
|
return midiout;
|
||
|
}
|
||
|
}
|
||
|
if (name)
|
||
|
{
|
||
|
for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_midi_output *midiout = p->data;
|
||
|
if (!midiout->removing && !strcmp(midiout->name, name))
|
||
|
return midiout;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
struct cbox_midi_input *cbox_io_get_midi_input(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid)
|
||
|
{
|
||
|
if (uuid)
|
||
|
{
|
||
|
for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_midi_input *midiin = p->data;
|
||
|
if (!midiin->removing && cbox_uuid_equal(&midiin->uuid, uuid))
|
||
|
return midiin;
|
||
|
}
|
||
|
}
|
||
|
if (name)
|
||
|
{
|
||
|
for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_midi_input *midiin = p->data;
|
||
|
if (!midiin->removing && !strcmp(midiin->name, name))
|
||
|
return midiin;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
struct cbox_audio_output *cbox_io_get_audio_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid)
|
||
|
{
|
||
|
if (uuid)
|
||
|
{
|
||
|
for (GSList *p = io->audio_outputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_audio_output *audioout = p->data;
|
||
|
if (!audioout->removing && cbox_uuid_equal(&audioout->uuid, uuid))
|
||
|
return audioout;
|
||
|
}
|
||
|
}
|
||
|
if (name)
|
||
|
{
|
||
|
for (GSList *p = io->audio_outputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_audio_output *audioout = p->data;
|
||
|
if (!audioout->removing && !strcmp(audioout->name, name))
|
||
|
return audioout;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
struct cbox_midi_output *cbox_io_create_midi_output(struct cbox_io *io, const char *name, GError **error)
|
||
|
{
|
||
|
struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, name, NULL);
|
||
|
if (midiout)
|
||
|
return midiout;
|
||
|
|
||
|
midiout = io->impl->createmidioutfunc(io->impl, name, error);
|
||
|
if (!midiout)
|
||
|
return NULL;
|
||
|
|
||
|
io->midi_outputs = g_slist_prepend(io->midi_outputs, midiout);
|
||
|
|
||
|
// Notify client code to connect to new outputs if needed
|
||
|
if (io->cb->on_midi_outputs_changed)
|
||
|
io->cb->on_midi_outputs_changed(io->cb->user_data);
|
||
|
return midiout;
|
||
|
}
|
||
|
|
||
|
void cbox_io_destroy_midi_output(struct cbox_io *io, struct cbox_midi_output *midiout)
|
||
|
{
|
||
|
midiout->removing = TRUE;
|
||
|
|
||
|
// This is not a very efficient way to do it. However, in this case,
|
||
|
// the list will rarely contain more than 10 elements, so simplicity
|
||
|
// and correctness may be more important.
|
||
|
GSList *copy = g_slist_copy(io->midi_outputs);
|
||
|
copy = g_slist_remove(copy, midiout);
|
||
|
|
||
|
GSList *old = io->midi_outputs;
|
||
|
io->midi_outputs = copy;
|
||
|
|
||
|
cbox_midi_merger_close(&midiout->merger, app.rt);
|
||
|
assert(!midiout->merger.inputs);
|
||
|
|
||
|
// Notify client code to disconnect the output and to make sure the RT code
|
||
|
// is not using the old list anymore
|
||
|
if (io->cb->on_midi_outputs_changed)
|
||
|
io->cb->on_midi_outputs_changed(io->cb->user_data);
|
||
|
|
||
|
assert(!midiout->merger.inputs);
|
||
|
|
||
|
g_slist_free(old);
|
||
|
io->impl->destroymidioutfunc(io->impl, midiout);
|
||
|
}
|
||
|
|
||
|
struct cbox_midi_input *cbox_io_create_midi_input(struct cbox_io *io, const char *name, GError **error)
|
||
|
{
|
||
|
struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, name, NULL);
|
||
|
if (midiin)
|
||
|
return midiin;
|
||
|
|
||
|
midiin = io->impl->createmidiinfunc(io->impl, name, error);
|
||
|
if (!midiin)
|
||
|
return NULL;
|
||
|
|
||
|
io->midi_inputs = g_slist_prepend(io->midi_inputs, midiin);
|
||
|
|
||
|
// Notify client code to connect to new inputs if needed
|
||
|
if (io->cb->on_midi_inputs_changed)
|
||
|
io->cb->on_midi_inputs_changed(io->cb->user_data);
|
||
|
return midiin;
|
||
|
}
|
||
|
|
||
|
void cbox_io_destroy_midi_input(struct cbox_io *io, struct cbox_midi_input *midiin)
|
||
|
{
|
||
|
midiin->removing = TRUE;
|
||
|
|
||
|
// This is not a very efficient way to do it. However, in this case,
|
||
|
// the list will rarely contain more than 10 elements, so simplicity
|
||
|
// and correctness may be more important.
|
||
|
GSList *copy = g_slist_copy(io->midi_inputs);
|
||
|
copy = g_slist_remove(copy, midiin);
|
||
|
|
||
|
GSList *old = io->midi_inputs;
|
||
|
io->midi_inputs = copy;
|
||
|
|
||
|
// Notify client code to disconnect the input and to make sure the RT code
|
||
|
// is not using the old list anymore
|
||
|
if (io->cb->on_midi_inputs_changed)
|
||
|
io->cb->on_midi_inputs_changed(io->cb->user_data);
|
||
|
|
||
|
g_slist_free(old);
|
||
|
io->impl->destroymidiinfunc(io->impl, midiin);
|
||
|
}
|
||
|
|
||
|
void cbox_io_destroy_all_midi_ports(struct cbox_io *io)
|
||
|
{
|
||
|
for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_midi_output *midiout = p->data;
|
||
|
midiout->removing = TRUE;
|
||
|
}
|
||
|
for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_midi_output *midiin = p->data;
|
||
|
midiin->removing = TRUE;
|
||
|
}
|
||
|
|
||
|
GSList *old_i = io->midi_inputs, *old_o = io->midi_outputs;
|
||
|
io->midi_outputs = NULL;
|
||
|
io->midi_inputs = NULL;
|
||
|
// Notify client code to disconnect the output and to make sure the RT code
|
||
|
// is not using the old list anymore
|
||
|
if (io->cb && io->cb->on_midi_outputs_changed)
|
||
|
io->cb->on_midi_outputs_changed(io->cb->user_data);
|
||
|
if (io->cb && io->cb->on_midi_inputs_changed)
|
||
|
io->cb->on_midi_inputs_changed(io->cb->user_data);
|
||
|
|
||
|
while(old_o)
|
||
|
{
|
||
|
struct cbox_midi_output *midiout = old_o->data;
|
||
|
cbox_midi_merger_close(&midiout->merger, app.rt);
|
||
|
assert(!midiout->merger.inputs);
|
||
|
io->impl->destroymidioutfunc(io->impl, midiout);
|
||
|
old_o = g_slist_remove(old_o, midiout);
|
||
|
}
|
||
|
g_slist_free(old_o);
|
||
|
|
||
|
while(old_i)
|
||
|
{
|
||
|
struct cbox_midi_input *midiin = old_i->data;
|
||
|
io->impl->destroymidiinfunc(io->impl, midiin);
|
||
|
old_i = g_slist_remove(old_i, midiin);
|
||
|
}
|
||
|
g_slist_free(old_i);
|
||
|
}
|
||
|
|
||
|
static void cbox_audio_output_router_record_block(struct cbox_recorder *handler, const float **buffers, uint32_t offset, uint32_t numsamples)
|
||
|
{
|
||
|
struct cbox_audio_output_router *router = handler->user_data;
|
||
|
if (router->left->removing || router->right->removing)
|
||
|
return;
|
||
|
cbox_gain_add_stereo(&router->gain, &router->left->buffer[offset], buffers[0], &router->right->buffer[offset], buffers[1], numsamples);
|
||
|
}
|
||
|
|
||
|
static gboolean cbox_audio_output_router_attach(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error)
|
||
|
{
|
||
|
struct cbox_audio_output_router *router = handler->user_data;
|
||
|
if (router->attached)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Router already attached");
|
||
|
return FALSE;
|
||
|
}
|
||
|
router->source = src;
|
||
|
router->attached++;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static gboolean cbox_audio_output_router_detach(struct cbox_recorder *handler, GError **error)
|
||
|
{
|
||
|
struct cbox_audio_output_router *router = handler->user_data;
|
||
|
if (router->attached != 1)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Router not yet attached");
|
||
|
return FALSE;
|
||
|
}
|
||
|
assert (router->source);
|
||
|
--router->attached;
|
||
|
assert (router->attached == 0);
|
||
|
router->source = NULL;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void cbox_audio_output_router_destroy(struct cbox_recorder *handler)
|
||
|
{
|
||
|
struct cbox_audio_output_router *router = handler->user_data;
|
||
|
if (router->attached)
|
||
|
cbox_recording_source_detach(router->source, &router->recorder, NULL);
|
||
|
assert(!router->attached);
|
||
|
router->left->users--;
|
||
|
router->right->users--;
|
||
|
}
|
||
|
|
||
|
static gboolean cbox_audio_output_router_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
|
||
|
{
|
||
|
struct cbox_audio_output_router *router = ct->user_data;
|
||
|
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, "/gain", "f", error, router->gain.db_gain))
|
||
|
return FALSE;
|
||
|
return CBOX_OBJECT_DEFAULT_STATUS(&router->recorder, fb, error);
|
||
|
}
|
||
|
if (!strcmp(cmd->command, "/gain") && !strcmp(cmd->arg_types, "f"))
|
||
|
{
|
||
|
cbox_gain_set_db(&router->gain, CBOX_ARG_F(cmd, 0));
|
||
|
return TRUE;
|
||
|
}
|
||
|
return cbox_object_default_process_cmd(ct, fb, cmd, error);
|
||
|
}
|
||
|
|
||
|
struct cbox_audio_output_router *cbox_io_create_audio_output_router(struct cbox_io *io, struct cbox_engine *engine, struct cbox_audio_output *left, struct cbox_audio_output *right)
|
||
|
{
|
||
|
struct cbox_audio_output_router *router = calloc(1, sizeof(struct cbox_audio_output_router));
|
||
|
CBOX_OBJECT_HEADER_INIT(&router->recorder, cbox_recorder, CBOX_GET_DOCUMENT(engine));
|
||
|
cbox_command_target_init(&router->recorder.cmd_target, cbox_audio_output_router_process_cmd, &router->recorder);
|
||
|
router->recorder.user_data = router;
|
||
|
router->recorder.attach = cbox_audio_output_router_attach;
|
||
|
router->recorder.record_block = cbox_audio_output_router_record_block;
|
||
|
router->recorder.detach = cbox_audio_output_router_detach;
|
||
|
router->recorder.destroy = cbox_audio_output_router_destroy;
|
||
|
router->source = NULL;
|
||
|
router->left = left;
|
||
|
router->right = right;
|
||
|
router->attached = 0;
|
||
|
cbox_gain_init(&router->gain);
|
||
|
cbox_gain_set_db(&router->gain, 12.0);
|
||
|
left->users++;
|
||
|
right->users++;
|
||
|
CBOX_OBJECT_REGISTER(&router->recorder);
|
||
|
return router;
|
||
|
}
|
||
|
|
||
|
struct cbox_audio_output *cbox_io_create_audio_output(struct cbox_io *io, const char *name, GError **error)
|
||
|
{
|
||
|
struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, name, NULL);
|
||
|
if (audioout)
|
||
|
return audioout;
|
||
|
|
||
|
audioout = io->impl->createaudiooutfunc(io->impl, name, error);
|
||
|
if (!audioout)
|
||
|
return NULL;
|
||
|
|
||
|
io->audio_outputs = g_slist_prepend(io->audio_outputs, audioout);
|
||
|
|
||
|
// Notify client code to connect to new outputs if needed
|
||
|
if (io->cb->on_audio_outputs_changed)
|
||
|
io->cb->on_audio_outputs_changed(io->cb->user_data);
|
||
|
return audioout;
|
||
|
}
|
||
|
|
||
|
struct cbox_audio_output *cbox_io_get_audio_output_by_uuid_string(struct cbox_io *io, const char *uuidstr, GError **error)
|
||
|
{
|
||
|
struct cbox_uuid uuid;
|
||
|
if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
|
||
|
return NULL;
|
||
|
struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid);
|
||
|
if (!audioout)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
|
||
|
return NULL;
|
||
|
}
|
||
|
return audioout;
|
||
|
}
|
||
|
|
||
|
gboolean cbox_io_destroy_audio_output(struct cbox_io *io, struct cbox_audio_output *audioout, GError **error)
|
||
|
{
|
||
|
if (audioout->users)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' is in use", audioout->name);
|
||
|
return FALSE;
|
||
|
}
|
||
|
audioout->removing = TRUE;
|
||
|
|
||
|
// This is not a very efficient way to do it. However, in this case,
|
||
|
// the list will rarely contain more than 10 elements, so simplicity
|
||
|
// and correctness may be more important.
|
||
|
GSList *copy = g_slist_copy(io->audio_outputs);
|
||
|
copy = g_slist_remove(copy, audioout);
|
||
|
|
||
|
GSList *old = io->audio_outputs;
|
||
|
io->audio_outputs = copy;
|
||
|
|
||
|
// Notify client code to disconnect the output and to make sure the RT code
|
||
|
// is not using the old list anymore
|
||
|
if (io->cb->on_audio_outputs_changed)
|
||
|
io->cb->on_audio_outputs_changed(io->cb->user_data);
|
||
|
|
||
|
g_slist_free(old);
|
||
|
io->impl->destroyaudiooutfunc(io->impl, audioout);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
gboolean cbox_io_process_cmd(struct cbox_io *io, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error, gboolean *cmd_handled)
|
||
|
{
|
||
|
*cmd_handled = FALSE;
|
||
|
if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
if (!cbox_check_fb_channel(fb, cmd->command, error))
|
||
|
return FALSE;
|
||
|
for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_midi_input *midiin = p->data;
|
||
|
if (!midiin->removing)
|
||
|
{
|
||
|
if (!cbox_execute_on(fb, NULL, "/midi_input", "su", error, midiin->name, &midiin->uuid))
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
|
||
|
{
|
||
|
struct cbox_midi_output *midiout = p->data;
|
||
|
if (!midiout->removing)
|
||
|
{
|
||
|
if (!cbox_execute_on(fb, NULL, "/midi_output", "su", error, midiout->name, &midiout->uuid))
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
return cbox_execute_on(fb, NULL, "/audio_inputs", "i", error, io->io_env.input_count) &&
|
||
|
cbox_execute_on(fb, NULL, "/audio_outputs", "i", error, io->io_env.output_count) &&
|
||
|
cbox_execute_on(fb, NULL, "/sample_rate", "i", error, io->io_env.srate) &&
|
||
|
cbox_execute_on(fb, NULL, "/buffer_size", "i", error, io->io_env.buffer_size);
|
||
|
}
|
||
|
else if (io->impl->createmidiinfunc && !strcmp(cmd->command, "/create_midi_input") && !strcmp(cmd->arg_types, "s"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
struct cbox_midi_input *midiin;
|
||
|
midiin = cbox_io_create_midi_input(io, CBOX_ARG_S(cmd, 0), error);
|
||
|
if (!midiin)
|
||
|
return FALSE;
|
||
|
cbox_midi_appsink_init(&midiin->appsink, app.rt, &app.engine->stmap->tmap);
|
||
|
return cbox_uuid_report(&midiin->uuid, fb, error);
|
||
|
}
|
||
|
else if (!strcmp(cmd->command, "/route_midi_input") && !strcmp(cmd->arg_types, "ss"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
const char *uuidstr = CBOX_ARG_S(cmd, 0);
|
||
|
struct cbox_uuid uuid;
|
||
|
if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
|
||
|
return FALSE;
|
||
|
struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
|
||
|
if (!midiin)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (*CBOX_ARG_S(cmd, 1))
|
||
|
{
|
||
|
if (cbox_uuid_fromstring(&midiin->output, CBOX_ARG_S(cmd, 1), error))
|
||
|
midiin->output_set = TRUE;
|
||
|
}
|
||
|
else
|
||
|
midiin->output_set = FALSE;
|
||
|
if (io->impl->updatemidiinroutingfunc)
|
||
|
io->impl->updatemidiinroutingfunc(io->impl);
|
||
|
if (io->cb->on_midi_inputs_changed)
|
||
|
io->cb->on_midi_inputs_changed(io->cb->user_data);
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (!strcmp(cmd->command, "/set_appsink_for_midi_input") && !strcmp(cmd->arg_types, "si"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
const char *uuidstr = CBOX_ARG_S(cmd, 0);
|
||
|
struct cbox_uuid uuid;
|
||
|
if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
|
||
|
return FALSE;
|
||
|
struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
|
||
|
if (!midiin)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
midiin->enable_appsink = CBOX_ARG_I(cmd, 1);
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (!strcmp(cmd->command, "/get_new_events") && !strcmp(cmd->arg_types, "s"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
if (!cbox_check_fb_channel(fb, cmd->command, error))
|
||
|
return FALSE;
|
||
|
const char *uuidstr = CBOX_ARG_S(cmd, 0);
|
||
|
struct cbox_uuid uuid;
|
||
|
if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
|
||
|
return FALSE;
|
||
|
struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
|
||
|
if (!midiin)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (!midiin->enable_appsink)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "App sink not enabled for port '%s'", uuidstr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
return cbox_midi_appsink_send_to(&midiin->appsink, fb, error);
|
||
|
}
|
||
|
else if (io->impl->createmidioutfunc && !strcmp(cmd->command, "/create_midi_output") && !strcmp(cmd->arg_types, "s"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
struct cbox_midi_output *midiout;
|
||
|
midiout = cbox_io_create_midi_output(io, CBOX_ARG_S(cmd, 0), error);
|
||
|
if (!midiout)
|
||
|
return FALSE;
|
||
|
return cbox_uuid_report(&midiout->uuid, fb, error);
|
||
|
}
|
||
|
else if (io->impl->destroymidiinfunc && !strcmp(cmd->command, "/delete_midi_input") && !strcmp(cmd->arg_types, "s"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
const char *uuidstr = CBOX_ARG_S(cmd, 0);
|
||
|
struct cbox_uuid uuid;
|
||
|
if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
|
||
|
return FALSE;
|
||
|
struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
|
||
|
if (!midiin)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
cbox_io_destroy_midi_input(io, midiin);
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (io->impl->destroymidioutfunc && !strcmp(cmd->command, "/delete_midi_output") && !strcmp(cmd->arg_types, "s"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
const char *uuidstr = CBOX_ARG_S(cmd, 0);
|
||
|
struct cbox_uuid uuid;
|
||
|
if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
|
||
|
return FALSE;
|
||
|
struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid);
|
||
|
if (!midiout)
|
||
|
{
|
||
|
g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
cbox_io_destroy_midi_output(io, midiout);
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (io->impl->createaudiooutfunc && !strcmp(cmd->command, "/create_audio_output") && !strcmp(cmd->arg_types, "s"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
struct cbox_audio_output *audioout;
|
||
|
audioout = cbox_io_create_audio_output(io, CBOX_ARG_S(cmd, 0), error);
|
||
|
if (!audioout)
|
||
|
return FALSE;
|
||
|
return cbox_uuid_report(&audioout->uuid, fb, error);
|
||
|
}
|
||
|
else if (io->impl->destroyaudiooutfunc && !strcmp(cmd->command, "/delete_audio_output") && !strcmp(cmd->arg_types, "s"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
const char *uuidstr = CBOX_ARG_S(cmd, 0);
|
||
|
struct cbox_audio_output *audioout = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error);
|
||
|
if (!audioout)
|
||
|
return FALSE;
|
||
|
return cbox_io_destroy_audio_output(io, audioout, error);
|
||
|
}
|
||
|
else if (io->impl->createmidioutfunc && !strcmp(cmd->command, "/create_audio_output_router") && !strcmp(cmd->arg_types, "ss"))
|
||
|
{
|
||
|
*cmd_handled = TRUE;
|
||
|
const char *uuidstr = CBOX_ARG_S(cmd, 0);
|
||
|
struct cbox_audio_output *left = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error);
|
||
|
if (!left)
|
||
|
return FALSE;
|
||
|
uuidstr = CBOX_ARG_S(cmd, 1);
|
||
|
struct cbox_audio_output *right = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error);
|
||
|
if (!right)
|
||
|
return FALSE;
|
||
|
// XXXKF hack alert
|
||
|
struct cbox_audio_output_router *router = cbox_io_create_audio_output_router(io, app.engine, left, right);
|
||
|
return cbox_uuid_report(&router->recorder._obj_hdr.instance_uuid, fb, error);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void cbox_io_close(struct cbox_io *io)
|
||
|
{
|
||
|
io->impl->destroyfunc(io->impl);
|
||
|
io->impl = NULL;
|
||
|
}
|
||
|
|