struct cbox_engine *cbox_engine_new(struct cbox_document *doc, struct cbox_rt *rt) { struct cbox_engine *engine = malloc(sizeof(struct cbox_engine)); CBOX_OBJECT_HEADER_INIT(engine, cbox_engine, doc); engine->rt = rt; engine->scenes = NULL; engine->scene_count = 0; engine->effect = NULL; engine->master = cbox_master_new(engine); engine->master->song = cbox_song_new(doc); engine->spb = NULL; if (rt) cbox_io_env_copy(&engine->io_env, &rt->io_env); else { engine->io_env.srate = 0; // must be overridden engine->io_env.buffer_size = 256; engine->io_env.input_count = 0; engine->io_env.output_count = 2; } cbox_midi_buffer_init(&engine->midibuf_aux); cbox_midi_buffer_init(&engine->midibuf_jack); cbox_midi_buffer_init(&engine->midibuf_song); cbox_midi_appsink_init(&engine->appsink, rt); cbox_command_target_init(&engine->cmd_target, cbox_engine_process_cmd, engine); CBOX_OBJECT_REGISTER(engine); return engine; }
struct cbox_module *cbox_module_manifest_create_module(struct cbox_module_manifest *manifest, const char *cfg_section, struct cbox_document *doc, struct cbox_rt *rt, struct cbox_engine *engine, const char *instance_name, GError **error) { g_clear_error(error); struct cbox_module *module = manifest->create(manifest->user_data, cfg_section, doc, rt, engine, error); if (!module) return NULL; module->instance_name = g_strdup(instance_name); module->input_samples = malloc(sizeof(float) * CBOX_BLOCK_SIZE * module->inputs); module->output_samples = malloc(sizeof(float) * CBOX_BLOCK_SIZE * module->outputs); module->engine_name = manifest->name; cbox_midi_buffer_init(&module->midi_input); return module; }
struct cbox_adhoc_pattern *cbox_adhoc_pattern_new(struct cbox_engine *engine, int id, struct cbox_midi_pattern *pattern) { struct cbox_adhoc_pattern *ap = calloc(1, sizeof(struct cbox_adhoc_pattern)); ap->next = NULL; ap->pattern = pattern; ap->pattern_playback = cbox_midi_pattern_playback_new(pattern); ap->master = cbox_master_new(engine); cbox_midi_playback_active_notes_init(&ap->active_notes); cbox_midi_clip_playback_init(&ap->playback, &ap->active_notes, ap->master); cbox_midi_buffer_init(&ap->output_buffer); ap->id = id; ap->completed = FALSE; return ap; }
struct cbox_midi_input *cbox_jackio_create_midi_in(struct cbox_io_impl *impl, const char *name, GError **error) { struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; jack_port_t *port = jack_port_register(jii->client, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); if (!port) { g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create input MIDI port '%s'", name); return FALSE; } struct cbox_jack_midi_input *input = calloc(1, sizeof(struct cbox_jack_midi_input)); input->hdr.name = g_strdup(name); input->hdr.removing = FALSE; input->port = port; input->jii = jii; cbox_uuid_generate(&input->hdr.uuid); cbox_midi_buffer_init(&input->hdr.buffer); return (struct cbox_midi_input *)input; }
static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) { struct cbox_engine *engine = ct->user_data; if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, "")) { for (int i = 0; i < engine->scene_count; i++) { if (!cbox_execute_on(fb, NULL, "/scene", "o", error, engine->scenes[i])) return FALSE; } return CBOX_OBJECT_DEFAULT_STATUS(engine, fb, error); } else if (!strcmp(cmd->command, "/render_stereo") && !strcmp(cmd->arg_types, "i")) { if (!cbox_check_fb_channel(fb, cmd->command, error)) return FALSE; if (engine->rt && engine->rt->io) { g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot use render function in real-time mode."); return FALSE; } struct cbox_midi_buffer midibuf_song; cbox_midi_buffer_init(&midibuf_song); int nframes = CBOX_ARG_I(cmd, 0); float *data = malloc(2 * nframes * sizeof(float)); float *data_i = malloc(2 * nframes * sizeof(float)); float *buffers[2] = { data, data + nframes }; for (int i = 0; i < nframes; i++) { buffers[0][i] = 0.f; buffers[1][i] = 0.f; } cbox_engine_process(engine, NULL, nframes, buffers); for (int i = 0; i < nframes; i++) { data_i[i * 2] = buffers[0][i]; data_i[i * 2 + 1] = buffers[1][i]; } free(data); if (!cbox_execute_on(fb, NULL, "/data", "b", error, cbox_blob_new_acquire_data(data_i, nframes * 2 * sizeof(float)))) return FALSE; return TRUE; } else if (!strncmp(cmd->command, "/master_effect/",15)) { return cbox_module_slot_process_cmd(&engine->effect, fb, cmd, cmd->command + 14, CBOX_GET_DOCUMENT(engine), engine->rt, engine, error); } else if (!strcmp(cmd->command, "/new_scene") && !strcmp(cmd->arg_types, "")) { if (!cbox_check_fb_channel(fb, cmd->command, error)) return FALSE; struct cbox_scene *s = cbox_scene_new(CBOX_GET_DOCUMENT(engine), engine); return s ? cbox_execute_on(fb, NULL, "/uuid", "o", error, s) : FALSE; } else if (!strcmp(cmd->command, "/new_recorder") && !strcmp(cmd->arg_types, "s")) { if (!cbox_check_fb_channel(fb, cmd->command, error)) return FALSE; struct cbox_recorder *rec = cbox_recorder_new_stream(engine, engine->rt, CBOX_ARG_S(cmd, 0)); return rec ? cbox_execute_on(fb, NULL, "/uuid", "o", error, rec) : FALSE; } else return cbox_object_default_process_cmd(ct, fb, cmd, error); }
static void run_audio_loop(struct cbox_usb_io_impl *uii) { while(!uii->stop_engine && !uii->device_removed) { struct cbox_io *io = uii->ioi.pio; struct timeval tv = { .tv_sec = 0, .tv_usec = 1000 }; libusb_handle_events_timeout(uii->usbctx, &tv); for (GSList *p = io->midi_outputs; p; p = p->next) { struct cbox_usb_midi_output *umo = p->data; usbio_send_midi_to_output(umo); } } } void usbio_run_idle_loop(struct cbox_usb_io_impl *uii) { while(!uii->stop_engine) { struct cbox_io *io = uii->ioi.pio; for (int b = 0; b < uii->output_channels; b++) memset(io->output_buffers[b], 0, io->io_env.buffer_size * sizeof(float)); io->cb->process(io->cb->user_data, io, io->io_env.buffer_size); for (GList *p = uii->rt_midi_ports; p; p = p->next) { struct cbox_usb_midi_interface *umi = p->data; cbox_midi_buffer_clear(&umi->input_port->hdr.buffer); } for (GSList *p = io->midi_outputs; p; p = p->next) { struct cbox_usb_midi_output *umo = p->data; usbio_send_midi_to_output(umo); } struct timeval tv = { .tv_sec = 0, .tv_usec = 1 }; libusb_handle_events_timeout(uii->usbctx, &tv); usleep((int)(io->io_env.buffer_size * 1000000.0 / uii->sample_rate)); } } static void *engine_thread(void *user_data) { struct cbox_usb_io_impl *uii = user_data; usbio_start_midi_capture(uii); if (uii->handle_audiodev) { uii->no_resubmit = FALSE; struct sched_param p; memset(&p, 0, sizeof(p)); p.sched_priority = cbox_config_get_int("io", "rtpriority", 10); pid_t tid = syscall(SYS_gettid); if (0 != sched_setscheduler(tid, SCHED_FIFO, &p)) g_warning("Cannot set realtime priority for the processing thread: %s.", strerror(errno)); usbio_start_audio_playback(uii); if (!uii->setup_error) { run_audio_loop(uii); } uii->no_resubmit = TRUE; memset(&p, 0, sizeof(p)); p.sched_priority = 0; if (0 != sched_setscheduler(tid, SCHED_OTHER, &p)) g_warning("Cannot unset realtime priority for the processing thread: %s.", strerror(errno)); usbio_stop_audio_playback(uii); } else { uii->no_resubmit = TRUE; g_message("No audio device found - running idle loop."); // notify the UI thread that the (fake) audio loop is running uii->playback_counter = uii->playback_buffers; usbio_run_idle_loop(uii); } usbio_stop_midi_capture(uii); return NULL; } static void cbox_usbio_destroy_midi_out(struct cbox_io_impl *ioi, struct cbox_midi_output *midiout) { g_free(midiout->name); free(midiout); } static struct cbox_usb_midi_interface *cur_midi_interface = NULL; struct cbox_midi_input *cbox_usbio_create_midi_in(struct cbox_io_impl *impl, const char *name, GError **error) { // struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; struct cbox_usb_midi_input *input = calloc(1, sizeof(struct cbox_usb_midi_input)); input->hdr.name = g_strdup(name); input->hdr.removing = FALSE; cbox_uuid_generate(&input->hdr.uuid); cbox_midi_buffer_init(&input->hdr.buffer); input->ifptr = cur_midi_interface; cbox_midi_appsink_init(&input->hdr.appsink, NULL); input->hdr.enable_appsink = FALSE; return (struct cbox_midi_input *)input; } struct cbox_midi_output *cbox_usbio_create_midi_out(struct cbox_io_impl *impl, const char *name, GError **error) { // struct cbox_usb_io_impl *uii = (struct cbox_usb_io_impl *)impl; struct cbox_usb_midi_output *output = calloc(1, sizeof(struct cbox_usb_midi_output)); output->hdr.name = g_strdup(name); output->hdr.removing = FALSE; cbox_uuid_generate(&output->hdr.uuid); cbox_midi_buffer_init(&output->hdr.buffer); cbox_midi_merger_init(&output->hdr.merger, &output->hdr.buffer); output->ifptr = cur_midi_interface; return (struct cbox_midi_output *)output; } static void create_midi_ports(struct cbox_usb_io_impl *uii) { uii->ioi.createmidiinfunc = cbox_usbio_create_midi_in; uii->ioi.createmidioutfunc = cbox_usbio_create_midi_out; for (GList *p = uii->midi_ports; p; p = p->next) { struct cbox_usb_midi_interface *umi = p->data; char buf[80]; sprintf(buf, "usb:%03d:%03d", umi->devinfo->bus, umi->devinfo->devadr); cur_midi_interface = umi; if (umi->epdesc_in.found) umi->input_port = (struct cbox_usb_midi_input *)cbox_io_create_midi_input(uii->ioi.pio, buf, NULL); else umi->input_port = NULL; if (umi->epdesc_out.found) umi->output_port = (struct cbox_usb_midi_output *)cbox_io_create_midi_output(uii->ioi.pio, buf, NULL); else umi->output_port = NULL; } uii->ioi.createmidiinfunc = NULL; uii->ioi.createmidioutfunc = NULL; cur_midi_interface = NULL; }