/* Calf Box, an open source musical instrument. Copyright (C) 2010-2013 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 "app.h" #include "blob.h" #include "config-api.h" #include "engine.h" #include "instr.h" #include "io.h" #include "layer.h" #include "menu.h" #include "menuitem.h" #include "meter.h" #include "midi.h" #include "module.h" #include "scene.h" #include "seq.h" #include "song.h" #include "track.h" #include "ui.h" #include "wavebank.h" #if USE_NCURSES #include #include #include #include #include #include #include #include #include #include int cmd_quit(struct cbox_menu_item_command *item, void *context) { return 1; } static void set_current_scene_name(gchar *name) { if (app.current_scene_name) g_free(app.current_scene_name); app.current_scene_name = name; } int cmd_load_scene(struct cbox_menu_item_command *item, void *context) { GError *error = NULL; struct cbox_scene *scene = app.engine->scenes[0]; cbox_scene_clear(scene); if (!cbox_scene_load(scene, item->item.item_context, &error)) cbox_print_error(error); set_current_scene_name(g_strdup_printf("scene:%s", (const char *)item->item.item_context)); return 0; } int cmd_load_instrument(struct cbox_menu_item_command *item, void *context) { GError *error = NULL; struct cbox_scene *scene = app.engine->scenes[0]; cbox_scene_clear(scene); struct cbox_layer *layer = cbox_layer_new_with_instrument(scene, (char *)item->item.item_context, &error); if (layer) { if (!cbox_scene_add_layer(scene, layer, &error)) cbox_print_error(error); set_current_scene_name(g_strdup_printf("instrument:%s", (const char *)item->item.item_context)); } else { cbox_print_error(error); } return 0; } int cmd_load_layer(struct cbox_menu_item_command *item, void *context) { GError *error = NULL; struct cbox_scene *scene = app.engine->scenes[0]; cbox_scene_clear(scene); struct cbox_layer *layer = cbox_layer_new_from_config(scene, (char *)item->item.item_context, &error); if (layer) { if (!cbox_scene_add_layer(scene, layer, &error)) cbox_print_error(error); set_current_scene_name(g_strdup_printf("layer:%s", (const char *)item->item.item_context)); } else { cbox_print_error(error); CBOX_DELETE(scene); } return 0; } gchar *scene_format_value(const struct cbox_menu_item_static *item, void *context) { if (app.current_scene_name) return g_strdup(app.current_scene_name); else return g_strdup("- No scene -"); } gchar *transport_format_value(const struct cbox_menu_item_static *item, void *context) { // XXXKF // struct cbox_bbt bbt; // cbox_master_to_bbt(app.engine->master, &bbt); if (app.engine->master->spb == NULL) return g_strdup("N/A"); if (!strcmp((const char *)item->item.item_context, "pos")) return g_strdup_printf("%d", (int)app.engine->master->spb->song_pos_samples); else return g_strdup_printf("%d", (int)app.engine->master->spb->song_pos_ppqn); } struct cbox_config_section_cb_data { struct cbox_menu *menu; cbox_menu_item_execute_func func; const char *prefix; }; static void config_key_process(void *user_data, const char *key) { struct cbox_config_section_cb_data *data = user_data; if (!strncmp(key, data->prefix, strlen(data->prefix))) { char *title = cbox_config_get_string(key, "title"); if (title) cbox_menu_add_item(data->menu, cbox_menu_item_new_command(title, data->func, strdup(key + strlen(data->prefix)), mif_dup_label | mif_free_context)); else cbox_menu_add_item(data->menu, cbox_menu_item_new_command(key, data->func, strdup(key + strlen(data->prefix)), mif_dup_label | mif_free_context)); } } struct cbox_menu *create_scene_menu(struct cbox_menu_item_menu *item, void *menu_context) { struct cbox_menu *scene_menu = cbox_menu_new(); struct cbox_config_section_cb_data cb = { .menu = scene_menu }; cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Scenes", NULL, NULL, 0)); cb.prefix = "scene:"; cb.func = cmd_load_scene; cbox_config_foreach_section(config_key_process, &cb); cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Layers", NULL, NULL, 0)); cb.prefix = "layer:"; cb.func = cmd_load_layer; cbox_config_foreach_section(config_key_process, &cb); cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Instruments", NULL, NULL, 0)); cb.prefix = "instrument:"; cb.func = cmd_load_instrument; cbox_config_foreach_section(config_key_process, &cb); cbox_menu_add_item(scene_menu, cbox_menu_item_new_ok()); return scene_menu; } /////////////////////////////////////////////////////////////////////////////// static struct cbox_command_target *find_module_target(const struct cbox_menu_item_command *item) { struct cbox_instrument *instr = item->item.item_context; return &instr->module->cmd_target; } int cmd_stream_rewind(struct cbox_menu_item_command *item, void *context) { GError *error = NULL; struct cbox_command_target *target = find_module_target(item); if (target) cbox_execute_on(target, NULL, "/seek", "i", &error, 0); cbox_print_error_if(error); return 0; } int cmd_stream_play(struct cbox_menu_item_command *item, void *context) { GError *error = NULL; struct cbox_command_target *target = find_module_target(item); if (target) cbox_execute_on(target, NULL, "/play", "", &error); cbox_print_error_if(error); return 0; } int cmd_stream_stop(struct cbox_menu_item_command *item, void *context) { GError *error = NULL; struct cbox_command_target *target = find_module_target(item); if (target) cbox_execute_on(target, NULL, "/stop", "", &error); cbox_print_error_if(error); return 0; } int cmd_stream_unload(struct cbox_menu_item_command *item, void *context) { GError *error = NULL; struct cbox_command_target *target = find_module_target(item); if (target) cbox_execute_on(target, NULL, "/unload", "", &error); cbox_print_error_if(error); return 0; } struct stream_response_data { gchar *filename; uint32_t pos, length, sample_rate, channels; }; gboolean result_parser_status(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error) { struct stream_response_data *res = ct->user_data; if (!strcmp(cmd->command, "/filename")) res->filename = g_strdup(cmd->arg_values[0]); if (!strcmp(cmd->command, "/pos")) res->pos = *((uint32_t **)cmd->arg_values)[0]; if (!strcmp(cmd->command, "/length")) res->length = *((uint32_t **)cmd->arg_values)[0]; if (!strcmp(cmd->command, "/sample_rate")) res->sample_rate = *((uint32_t **)cmd->arg_values)[0]; if (!strcmp(cmd->command, "/channels")) res->channels = *((uint32_t **)cmd->arg_values)[0]; //cbox_osc_command_dump(cmd); return TRUE; } char *cmd_stream_status(const struct cbox_menu_item_static *item, void *context) { struct stream_response_data data = { NULL, 0, 0, 0, 0 }; struct cbox_command_target response = { &data, result_parser_status }; GError *error = NULL; struct cbox_command_target *target = find_module_target((struct cbox_menu_item_command *)item); if (target) cbox_execute_on(target, &response, "/status", "", &error); cbox_print_error_if(error); gchar *res = NULL; if (data.filename && data.length && data.sample_rate) { double duration = data.length * 1.0 / data.sample_rate; res = g_strdup_printf("%s (%um%0.2fs, %uch, %uHz) (%0.2f%%)", data.filename, (unsigned)floor(duration / 60), duration - 60 * floor(duration / 60), (unsigned)data.channels, (unsigned)data.sample_rate, data.pos * 100.0 / data.length); } else res = g_strdup("-"); g_free(data.filename); return res; } struct load_waveform_context { struct cbox_menu_item_context header; struct cbox_instrument *instrument; char *filename; }; static void destroy_load_waveform_context(void *p) { struct load_waveform_context *context = p; g_free(context->filename); free(context); } int cmd_stream_load(struct cbox_menu_item_command *item, void *context) { struct load_waveform_context *ctx = item->item.item_context; GError *error = NULL; struct cbox_command_target *target = &ctx->instrument->module->cmd_target; if (target) cbox_execute_on(target, NULL, "/load", "si", &error, ctx->filename, 0); cbox_print_error_if(error); return 0; } struct cbox_menu *create_streamplay_menu(struct cbox_menu_item_menu *item, void *menu_context) { struct cbox_menu *menu = cbox_menu_new(); struct cbox_instrument *instr = item->item.item_context; assert(instr); cbox_menu_add_item(menu, cbox_menu_item_new_static("Current stream", NULL, NULL, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_static("File", cmd_stream_status, instr, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_static("Module commands", NULL, NULL, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_command("Play stream", cmd_stream_play, instr, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_command("Stop stream", cmd_stream_stop, instr, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_command("Rewind stream", cmd_stream_rewind, instr, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_command("Unload stream", cmd_stream_unload, instr, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_static("Files available", NULL, NULL, 0)); glob_t g; gboolean found = (glob("*.wav", GLOB_TILDE_CHECK, NULL, &g) == 0); found = glob("*.ogg", GLOB_TILDE_CHECK | (found ? GLOB_APPEND : 0), NULL, &g) || found; if (found) { for (size_t i = 0; i < g.gl_pathc; i++) { struct load_waveform_context *context = calloc(1, sizeof(struct load_waveform_context)); context->header.destroy_func = destroy_load_waveform_context; context->instrument = instr; context->filename = g_strdup(g.gl_pathv[i]); cbox_menu_add_item(menu, cbox_menu_item_new_command(g_strdup_printf("Load: %s", g.gl_pathv[i]), cmd_stream_load, context, mif_free_label | mif_context_is_struct)); } } globfree(&g); cbox_menu_add_item(menu, cbox_menu_item_new_ok()); return menu; } /////////////////////////////////////////////////////////////////////////////// struct cbox_menu *create_module_menu(struct cbox_menu_item_menu *item, void *menu_context) { struct cbox_menu *menu = cbox_menu_new(); cbox_menu_add_item(menu, cbox_menu_item_new_static("Scene instruments", NULL, NULL, 0)); struct cbox_scene *scene = app.engine->scenes[0]; for (uint32_t i = 0; i < scene->instrument_count; ++i) { struct cbox_instrument *instr = scene->instruments[i]; create_menu_func menufunc = NULL; if (!strcmp(instr->module->engine_name, "stream_player")) menufunc = create_streamplay_menu; if (menufunc) cbox_menu_add_item(menu, cbox_menu_item_new_dynamic_menu(g_strdup_printf("%s (%s)", instr->module->instance_name, instr->module->engine_name), menufunc, instr, mif_free_label)); else cbox_menu_add_item(menu, cbox_menu_item_new_static(g_strdup_printf("%s (%s)", instr->module->instance_name, instr->module->engine_name), NULL, NULL, mif_free_label)); } cbox_menu_add_item(menu, cbox_menu_item_new_ok()); return menu; } /////////////////////////////////////////////////////////////////////////////// static void restart_song() { cbox_master_stop(app.engine->master); cbox_master_seek_ppqn(app.engine->master, 0); cbox_master_play(app.engine->master); } int cmd_pattern_none(struct cbox_menu_item_command *item, void *context) { cbox_song_clear(app.engine->master->song); cbox_engine_update_song_playback(app.engine); return 0; } int cmd_pattern_simple(struct cbox_menu_item_command *item, void *context) { cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_new_metronome(app.engine->master->song, 1, app.engine->master->ppqn_factor)); restart_song(); return 0; } int cmd_pattern_normal(struct cbox_menu_item_command *item, void *context) { cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_new_metronome(app.engine->master->song, app.engine->master->timesig_num, app.engine->master->ppqn_factor)); restart_song(); return 0; } int cmd_load_drumpattern(struct cbox_menu_item_command *item, void *context) { cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load(app.engine->master->song, item->item.item_context, 1, app.engine->master->ppqn_factor)); restart_song(); return 0; } int cmd_load_drumtrack(struct cbox_menu_item_command *item, void *context) { cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load_track(app.engine->master->song, item->item.item_context, 1, app.engine->master->ppqn_factor)); restart_song(); return 0; } int cmd_load_pattern(struct cbox_menu_item_command *item, void *context) { cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load(app.engine->master->song, item->item.item_context, 0, app.engine->master->ppqn_factor)); restart_song(); return 0; } int cmd_load_track(struct cbox_menu_item_command *item, void *context) { cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load_track(app.engine->master->song, item->item.item_context, 0, app.engine->master->ppqn_factor)); restart_song(); return 0; } struct cbox_menu *create_pattern_menu(struct cbox_menu_item_menu *item, void *menu_context) { struct cbox_menu *menu = cbox_menu_new(); struct cbox_config_section_cb_data cb = { .menu = menu }; cbox_menu_add_item(menu, cbox_menu_item_new_static("Pattern commands", NULL, NULL, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_command("No pattern", cmd_pattern_none, NULL, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_command("Simple metronome", cmd_pattern_simple, NULL, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_command("Normal metronome", cmd_pattern_normal, NULL, 0)); cbox_menu_add_item(menu, cbox_menu_item_new_static("Drum tracks", NULL, NULL, 0)); cb.prefix = "drumtrack:"; cb.func = cmd_load_drumtrack; cbox_config_foreach_section(config_key_process, &cb); cbox_menu_add_item(menu, cbox_menu_item_new_static("Melodic tracks", NULL, NULL, 0)); cb.prefix = "track:"; cb.func = cmd_load_track; cbox_config_foreach_section(config_key_process, &cb); cbox_menu_add_item(menu, cbox_menu_item_new_static("Drum patterns", NULL, NULL, 0)); cb.prefix = "drumpattern:"; cb.func = cmd_load_drumpattern; cbox_config_foreach_section(config_key_process, &cb); cbox_menu_add_item(menu, cbox_menu_item_new_static("Melodic patterns", NULL, NULL, 0)); cb.prefix = "pattern:"; cb.func = cmd_load_pattern; cbox_config_foreach_section(config_key_process, &cb); cbox_menu_add_item(menu, cbox_menu_item_new_ok()); return menu; } struct cbox_menu *create_main_menu() { struct cbox_menu *main_menu = cbox_menu_new(); cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Current scene:", scene_format_value, NULL, 0)); cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Set scene", create_scene_menu, NULL, 0)); cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Module control", create_module_menu, NULL, 0)); cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Pattern control", create_pattern_menu, NULL, 0)); cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Variables", NULL, NULL, 0)); // cbox_menu_add_item(main_menu, cbox_menu_item_new_int("foo:", &var1, 0, 127, NULL)); // cbox_menu_add_item(main_menu, "bar:", menu_item_value_double, &mx_double_var2, &var2); //cbox_menu_add_item(main_menu, cbox_menu_item_new_static("pos:", transport_format_value, "pos", 0)); //cbox_menu_add_item(main_menu, cbox_menu_item_new_static("bbt:", transport_format_value, "bbt", 0)); cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Commands", NULL, NULL, 0)); cbox_menu_add_item(main_menu, cbox_menu_item_new_command("Quit", cmd_quit, NULL, 0)); return main_menu; } #endif