/*
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 "sampler.h"
#include "sfzparser.h"
#include "sampler_impl.h"
#define DUMP_LAYER_ATTRIBS 0
enum sfz_load_section_type
{
slst_normal,
slst_control,
slst_effect,
slst_curve,
};
struct sfz_load_state
{
struct sampler_module *m;
const char *filename, *default_path;
struct sampler_program *program;
struct sampler_layer *global, *master, *group, *region, *target;
struct sampler_midi_curve *curve;
enum sfz_load_section_type section_type;
uint32_t curve_index;
GError **error;
};
static void load_sfz_end_region(struct sfz_parser_client *client)
{
struct sfz_load_state *ls = client->user_data;
// printf("-- copy current region to the list of layers\n");
struct sampler_layer *l = ls->region;
sampler_layer_data_finalize(&l->data, l->parent ? &l->parent->data : NULL, ls->program);
sampler_layer_reset_switches(l, ls->m);
sampler_layer_update(l);
sampler_program_add_layer(ls->program, ls->region);
ls->region = NULL;
}
static void end_token(struct sfz_parser_client *client)
{
struct sfz_load_state *ls = client->user_data;
#if DUMP_LAYER_ATTRIBS
if (ls->group)
{
fprintf(stdout, "");
sampler_layer_dump(&ls->group, stdout);
}
if (ls->region)
{
fprintf(stdout, "");
sampler_layer_dump(&ls->region, stdout);
}
#endif
if (ls->section_type == slst_curve)
{
uint32_t i = ls->curve_index;
if (i < MAX_MIDI_CURVES)
{
if (ls->program->curves[i])
g_free(ls->program->curves[i]);
else
ls->program->interpolated_curves[i] = g_new(float, 128);
sampler_midi_curve_interpolate(ls->curve, ls->program->interpolated_curves[i], 0, 1, FALSE);
ls->program->curves[i] = ls->curve;
}
else
{
if (i == (uint32_t)-1)
g_warning("Curve index not specified");
else
g_warning("Curve number %u is greater than the maximum of %u", (unsigned)i, (unsigned)MAX_MIDI_CURVES);
g_free(ls->curve);
}
ls->curve = NULL;
}
if (ls->region)
load_sfz_end_region(client);
ls->region = NULL;
ls->section_type = slst_normal;
}
static gboolean load_sfz_global(struct sfz_parser_client *client)
{
struct sfz_load_state *ls = client->user_data;
// printf("-- start global\n");
ls->target = ls->global = ls->program->global;
ls->master = ls->global->default_child;
ls->group = ls->master->default_child;
return TRUE;
}
static gboolean load_sfz_master(struct sfz_parser_client *client)
{
struct sfz_load_state *ls = client->user_data;
// printf("-- start master\n");
ls->target = ls->master = sampler_layer_new(ls->m, ls->program, ls->program->global);
ls->group = ls->master->default_child = sampler_layer_new(ls->m, ls->program, ls->master);
return TRUE;
}
static gboolean load_sfz_group(struct sfz_parser_client *client)
{
struct sfz_load_state *ls = client->user_data;
// printf("-- start group\n");
ls->target = ls->group = sampler_layer_new(ls->m, ls->program, ls->master);
return TRUE;
}
static gboolean load_sfz_region(struct sfz_parser_client *client)
{
struct sfz_load_state *ls = client->user_data;
ls->target = ls->region = sampler_layer_new(ls->m, ls->program, ls->group);
// g_warning("-- start region");
return TRUE;
}
static gboolean load_sfz_control(struct sfz_parser_client *client)
{
struct sfz_load_state *ls = client->user_data;
ls->section_type = slst_control;
return TRUE;
}
static gboolean load_sfz_curve(struct sfz_parser_client *client)
{
struct sfz_load_state *ls = client->user_data;
ls->section_type = slst_curve;
ls->curve = g_new0(struct sampler_midi_curve, 1);
ls->curve_index = -1;
sampler_midi_curve_init(ls->curve);
return TRUE;
}
static gboolean load_sfz_key_value(struct sfz_parser_client *client, const char *key, const char *value)
{
struct sfz_load_state *ls = client->user_data;
if (ls->section_type == slst_curve)
{
if (key[0] == 'v' && isdigit(key[1]))
{
int pos = atoi(key + 1);
if (pos >= 0 && pos < 128)
{
double fvalue = -1;
if (!atof_C_verify(key, value, &fvalue, ls->error))
return FALSE;
ls->curve->values[pos] = fvalue;
return TRUE;
}
else
g_warning("Out of range curve point: %s", key);
}
else if (!strcmp(key, "curve_index"))
{
ls->curve_index = atoi(value);
return TRUE;
}
else
g_warning("Unknown parameter in curve section: %s=%s", key, value);
return TRUE;
}
if (ls->section_type == slst_effect)
{
g_warning("Parameter found in unsupported effect section: %s=%s", key, value);
return TRUE;
}
if (ls->section_type == slst_control)
{
if (!strncmp(key, "label_cc", 8))
{
int ctrl = atoi(key + 8);
sampler_program_add_controller_label(ls->program, ctrl, g_strdup(value));
}
else if (!strncmp(key, "set_cc", 6))
{
int ctrl = atoi(key + 6);
int val = atoi(value);
if (ctrl >= 0 && ctrl < CC_COUNT && val >=0 && val <= 127)
sampler_program_add_controller_init(ls->program, ctrl, val);
else
g_warning("Invalid CC initialisation: %s=%s", key, value);
}
else if (!strcmp(key, "default_path"))
{
g_free(ls->program->sample_dir);
gchar *dir = g_path_get_dirname(ls->filename);
char value2[strlen(value) + 1];
int i;
for (i = 0; value[i]; ++i)
value2[i] = value[i] == '\\' ? '/' : value[i];
value2[i] = '\0';
gchar *combined = g_build_filename(dir, value2, NULL);
ls->program->sample_dir = combined;
g_free(dir);
}
else
g_warning("Unrecognized SFZ key in control section: %s", key);
return TRUE;
}
struct sampler_layer *l = ls->target;
if (!ls->target)
{
g_warning("Parameter '%s' entered outside of global, master, region or group", key);
return TRUE;
}
if (!sampler_layer_apply_param(l, key, value, ls->error))
return FALSE;
return TRUE;
}
static gboolean handle_token(struct sfz_parser_client *client, const char *token, GError **error)
{
struct sfz_load_state *ls = client->user_data;
end_token(client);
if (!strcmp(token, "region"))
return load_sfz_region(client);
if (!strcmp(token, "group"))
return load_sfz_group(client);
if (!strcmp(token, "master"))
return load_sfz_master(client);
if (!strcmp(token, "global"))
return load_sfz_global(client);
if (!strcmp(token, "control"))
return load_sfz_control(client);
if (!strcmp(token, "curve"))
return load_sfz_curve(client);
if (!strcmp(token, "effect"))
{
ls->section_type = slst_effect;
return TRUE;
}
g_set_error(error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_HEADER, "Unexpected header <%s>", token);
return FALSE;
}
gboolean sampler_module_load_program_sfz(struct sampler_module *m, struct sampler_program *prg, const char *sfz, int is_from_string, GError **error)
{
struct sfz_load_state ls = { .global = prg->global, .master = prg->global->default_child, .group = prg->global->default_child->default_child, .target = NULL, .m = m, .filename = sfz, .region = NULL, .error = error, .program = prg, .section_type = slst_normal, .default_path = NULL };
struct sfz_parser_client c = { .user_data = &ls, .token = handle_token, .key_value = load_sfz_key_value };
g_clear_error(error);
gboolean status;
if (is_from_string)
status = load_sfz_from_string(sfz, strlen(sfz), &c, error);
else
{
status = load_sfz(sfz, prg->tarfile, &c, error); //Loads the audio files but also sets fields, like prg->sample_dir. After this we cannot modify any values anymore.
}
if (!status)
{
if (ls.region)
CBOX_DELETE(ls.region);
return FALSE;
}
end_token(&c);
prg->all_layers = g_slist_reverse(prg->all_layers);
sampler_program_update_layers(prg);
return TRUE;
}