/*
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 <http://www.gnu.org/licenses/>.
*/

#include "config-api.h"
#include "dspmath.h"
#include "engine.h"
#include "errors.h"
#include "midi.h"
#include "module.h"
#include "rt.h"
#include "sampler.h"
#include "sampler_impl.h"
#include "sfzloader.h"
#include "stm.h"
#include <assert.h>
#include <errno.h>
#include <glib.h>
#include <math.h>
#include <memory.h>
#include <sndfile.h>
#include <stdio.h>
#include <stdlib.h>

float sampler_sine_wave[2049];

GQuark cbox_sampler_error_quark()
{
    return g_quark_from_string("cbox-sampler-error-quark");
}

static void sampler_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs);
static void sampler_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len);
static void sampler_destroyfunc(struct cbox_module *module);

void sampler_steal_voice(struct sampler_module *m)
{
    int max_age = 0;
    struct sampler_voice *voice_found = NULL;
    for (int i = 0; i < 16; i++)
    {
        FOREACH_VOICE(m->channels[i].voices_running, v)
        {
            if (v->amp_env.cur_stage == 15)
                continue;
            int age = m->serial_no - v->serial_no;
            if (v->gen.loop_start == (uint32_t)-1)
                age += (int)((v->gen.bigpos >> 32) * 100.0 / v->gen.cur_sample_end);
            else
            if (v->released)
                age += 10;
            if (age > max_age)
            {
                max_age = age;
                voice_found = v;
            }
        }
    }
    if (voice_found)
    {
        voice_found->released = 1;
        cbox_envelope_go_to(&voice_found->amp_env, 15);
    }
}

static inline float clip01(float v)
{
    if (v < 0.f)
        return 0;
    if (v > 1.f)
        return 1;
    return v;
}

void sampler_create_voice_from_prevoice(struct sampler_module *m, struct sampler_prevoice *pv)
{
    if (!m->voices_free)
        return;
    struct sampler_released_groups exgroups;
    sampler_released_groups_init(&exgroups);
    sampler_voice_start(m->voices_free, pv->channel, pv->layer_data, pv->note, pv->vel, &exgroups);
    if (exgroups.low_groups || exgroups.group_count)
        sampler_channel_release_groups(pv->channel, pv->note, &exgroups);
}

void sampler_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
{
    struct sampler_module *m = (struct sampler_module *)module;
    
    int active_prevoices[16];
    uint32_t active_prevoices_mask = 0;
    FOREACH_PREVOICE(m->prevoices_running, pv)
    {
        uint32_t c = pv->channel - m->channels;
        active_prevoices[c] = (active_prevoices_mask >> c) & 1 ? 1 + active_prevoices[c] : 1;
        active_prevoices_mask |= 1 << c;
        if (sampler_prevoice_process(pv, m))
        {
            sampler_prevoice_unlink(&m->prevoices_running, pv);
            sampler_create_voice_from_prevoice(m, pv);
            sampler_prevoice_link(&m->prevoices_free, pv);
        }
    }

    for (int c = 0; c < m->output_pairs + m->aux_pairs; c++)
    {
        int oo = 2 * c;
        for (int i = 0; i < CBOX_BLOCK_SIZE; i++)
            outputs[oo][i] = outputs[oo + 1][i] = 0.f;
    }
    
    int vcount = 0, vrel = 0, pvcount = 0;
    for (int i = 0; i < 16; i++)
    {
        int cvcount = 0;
        FOREACH_VOICE(m->channels[i].voices_running, v)
        {
            sampler_voice_process(v, m, outputs);

            if (v->amp_env.cur_stage == 15)
                vrel++;
            cvcount++;
        }
        int cpvcount = (active_prevoices_mask >> i) & 1 ? active_prevoices[i] : 0;
        m->channels[i].active_voices = cvcount;
        m->channels[i].active_prevoices = cpvcount;
        vcount += cvcount;
        pvcount += cpvcount;
    }
    m->active_voices = vcount;
    m->active_prevoices = pvcount;
    if(vcount - vrel > m->max_voices + 1)
        sampler_steal_voice(m);
    if(vcount - vrel > m->max_voices)
        sampler_steal_voice(m);
    m->serial_no++;
    m->current_time += CBOX_BLOCK_SIZE;
}

void sampler_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
{
    struct sampler_module *m = (struct sampler_module *)module;
    if (len > 0)
    {
        int cmd = data[0] >> 4;
        int chn = data[0] & 15;
        struct sampler_channel *c = &m->channels[chn];
        switch(cmd)
        {
            case 8:
                sampler_channel_stop_note(c, data[1], data[2], FALSE);
                break;

            case 9:
                if (data[2] > 0)
                    sampler_channel_start_note(c, data[1], data[2], FALSE);
                else
                    sampler_channel_stop_note(c, data[1], data[2], FALSE);
                break;
            
            case 10:
                c->last_polyaft = data[2];
                // Lazy clearing
                if (!(c->poly_pressure_mask & (1 << (data[1] >> 2))))
                {
                    // Clear the group of 4
                    memset(c->poly_pressure + (data[1] & ~3), 0, 4);
                    c->poly_pressure_mask |= 1 << (data[1] >> 2);
                }
                c->poly_pressure[data[1]] = data[2];
                // XXXKF add a new opcode for cymbal chokes via poly pressure
                // if (data[2] == 127)
                //    sampler_channel_stop_note(c, data[1], data[2], TRUE);
                break;
            
            case 11:
                sampler_channel_process_cc(c, data[1], data[2]);
                break;

            case 12:
                sampler_channel_program_change(c, data[1]);
                break;

            case 13:
                c->last_chanaft = data[1];
                break;

            case 14:
                c->pitchwheel = data[1] + 128 * data[2] - 8192;
                break;

            }
    }
}

static int get_first_free_program_no(struct sampler_module *m)
{
    int prog_no = -1;
    gboolean found;
    
    // XXXKF this has a N-squared complexity - but I'm not seeing
    // this being used with more than 10 programs at the same time
    // in the near future
    do {
        prog_no++;
        found = FALSE;
        for (uint32_t i = 0; i < m->program_count; i++)
        {
            if (m->programs[i]->prog_no == prog_no)
            {
                found = TRUE;
                break;
            }
        }        
    } while(found);
    
    return prog_no;
}

static int find_program(struct sampler_module *m, int prog_no)
{
    for (uint32_t i = 0; i < m->program_count; i++)
    {
        if (m->programs[i]->prog_no == prog_no)
            return i;
    }
    return -1;
}

struct release_program_voices_data
{
    struct sampler_module *module;
    
    struct sampler_program *old_pgm, *new_pgm;
    uint16_t channels_to_wait_for;
};

static int release_program_voices_execute(void *data)
{
    struct release_program_voices_data *rpv = data;
    struct sampler_module *m = rpv->module;
    int finished = 1;
    
    FOREACH_PREVOICE(m->prevoices_running, pv)
    {
        if (pv->channel->program == rpv->old_pgm)
        {
            sampler_prevoice_unlink(&m->prevoices_running, pv);
            sampler_prevoice_link(&m->prevoices_free, pv);            
        }
    }
    for (int i = 0; i < 16; i++)
    {
        uint16_t mask = 1 << i;
        struct sampler_channel *c = &m->channels[i];
        if (c->program == rpv->old_pgm || c->program == NULL)
        {
            sampler_channel_set_program_RT(c, rpv->new_pgm);
            rpv->channels_to_wait_for |= mask;
        }
        if (rpv->channels_to_wait_for & mask)
        {
            FOREACH_VOICE(c->voices_running, v)
            {
                if (m->deleting || !m->module.rt)
                {
                    sampler_voice_inactivate(v, TRUE);
                    continue;
                }
                // This is a new voice, started after program change, so it
                // should not be terminated and waited for.
                if (v->program == rpv->new_pgm)
                    continue;
                // The voice is still going, so repeat until it fades out
                finished = 0;
                // If not in final fadeout stage, force final fadeout.
                if (v->amp_env.cur_stage != 15)
                {
                    v->released = 1;
                    cbox_envelope_go_to(&v->amp_env, 15);
                }
            }
        }
    }
    
    return finished;
}

static void swap_program(struct sampler_module *m, int index, struct sampler_program *pgm, gboolean delete_old)
{
    static struct cbox_rt_cmd_definition release_program_voices = { NULL, release_program_voices_execute, NULL };
    
    struct sampler_program *old_program = NULL;
    if (pgm)
        old_program = cbox_rt_swap_pointers(m->module.rt, (void **)&m->programs[index], pgm);
    else
        old_program = cbox_rt_array_remove(m->module.rt, (void ***)&m->programs, &m->program_count, index);

    struct release_program_voices_data data = {m, old_program, pgm, 0};

    cbox_rt_execute_cmd_sync(m->module.rt, &release_program_voices, &data);
    
    if (delete_old && old_program)
        CBOX_DELETE(old_program);
}

static void select_initial_program(struct sampler_module *m)
{
    static struct cbox_rt_cmd_definition release_program_voices = { NULL, release_program_voices_execute, NULL };
    struct release_program_voices_data data = {m, NULL, m->programs[0], 0};
    cbox_rt_execute_cmd_sync(m->module.rt, &release_program_voices, &data);
}

void sampler_register_program(struct sampler_module *m, struct sampler_program *pgm)
{
    struct sampler_program **programs = malloc(sizeof(struct sampler_program *) * (m->program_count + 1));
    memcpy(programs, m->programs, sizeof(struct sampler_program *) * m->program_count);
    programs[m->program_count] = pgm;
    free(cbox_rt_swap_pointers_and_update_count(m->module.rt, (void **)&m->programs, programs, &m->program_count, m->program_count + 1));
    if (m->program_count == 1U)
        select_initial_program(m);
}

static gboolean load_program_at(struct sampler_module *m, const char *cfg_section, const char *name, int prog_no, struct sampler_program **ppgm, GError **error)
{
    struct sampler_program *pgm = NULL;
    int index = find_program(m, prog_no);
    pgm = sampler_program_new_from_cfg(m, cfg_section, name, prog_no, error);
    if (!pgm)
        return FALSE;
    
    if (index != -1)
        swap_program(m, index, pgm, TRUE);
    else
        sampler_register_program(m, pgm);

    if (ppgm)
        *ppgm = pgm;
    return TRUE;
}

void sampler_unselect_program(struct sampler_module *m, struct sampler_program *prg)
{
    // Ensure no new notes are played on that program
    prg->deleting = TRUE;
    // Remove from the list of available programs, so that it cannot be selected again
    for (uint32_t i = 0; i < m->program_count; i++)
    {
        if (m->programs[i] == prg)
            swap_program(m, i, NULL, FALSE);
    }
}

static gboolean load_from_string(struct sampler_module *m, const char *sample_dir, const char *sfz_data, const char *name, int prog_no, struct sampler_program **ppgm, GError **error)
{
    int index = find_program(m, prog_no);
    struct sampler_program *pgm = sampler_program_new(m, prog_no, name, NULL, sample_dir, error);
    if (!pgm)
        return FALSE;
    pgm->source_file = g_strdup("string");
    if (!sampler_module_load_program_sfz(m, pgm, sfz_data, TRUE, error))
    {
        free(pgm);
        return FALSE;
    }

    if (index != -1)
    {
        swap_program(m, index, pgm, TRUE);
        if (ppgm)
            *ppgm = pgm;
        return TRUE;
    }
    
    struct sampler_program **programs = calloc((m->program_count + 1), sizeof(struct sampler_program *));
    memcpy(programs, m->programs, sizeof(struct sampler_program *) * m->program_count);
    programs[m->program_count] = pgm;
    if (ppgm)
        *ppgm = pgm;
    free(cbox_rt_swap_pointers_and_update_count(m->module.rt, (void **)&m->programs, programs, &m->program_count, m->program_count + 1));    
    if (m->program_count == 1)
        select_initial_program(m);
    return TRUE;
}

gboolean sampler_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
{
    struct sampler_module *m = (struct sampler_module *)ct->user_data;
    
    if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
    {
        if (!cbox_check_fb_channel(fb, cmd->command, error))
            return FALSE;
        for (int i = 0; i < 16; i++)
        {
            struct sampler_channel *channel = &m->channels[i];
            gboolean result;
            if (channel->program)
                result = cbox_execute_on(fb, NULL, "/patch", "iis", error, i + 1, channel->program->prog_no, channel->program->name);
            else
                result = cbox_execute_on(fb, NULL, "/patch", "iis", error, i + 1, -1, "");
            if (!result)
                return FALSE;
            if (!(cbox_execute_on(fb, NULL, "/channel_voices", "ii", error, i + 1, channel->active_voices) &&
                cbox_execute_on(fb, NULL, "/channel_prevoices", "ii", error, i + 1, channel->active_prevoices) &&
                cbox_execute_on(fb, NULL, "/output", "ii", error, i + 1, channel->output_shift) &&
                cbox_execute_on(fb, NULL, "/volume", "ii", error, i + 1, sampler_channel_addcc(channel, 7)) &&
                cbox_execute_on(fb, NULL, "/pan", "ii", error, i + 1, sampler_channel_addcc(channel, 10))))
                return FALSE;
        }
        
        return cbox_execute_on(fb, NULL, "/active_voices", "i", error, m->active_voices) &&
            cbox_execute_on(fb, NULL, "/active_prevoices", "i", error, m->active_prevoices) &&
            cbox_execute_on(fb, NULL, "/active_pipes", "i", error, cbox_prefetch_stack_get_active_pipe_count(m->pipe_stack)) &&
            cbox_execute_on(fb, NULL, "/polyphony", "i", error, m->max_voices) && 
            CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
    }
    else
    if (!strcmp(cmd->command, "/keyswitch_state") && !strcmp(cmd->arg_types, "ii"))
    {
        int channel = CBOX_ARG_I(cmd, 0);
        if (channel < 1 || channel > 16)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid channel %d", channel);
            return FALSE;
        }
        int group = CBOX_ARG_I(cmd, 1);
        if (group < 0 || group >= MAX_KEYSWITCH_GROUPS)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid keyswitch group %d", group);
            return FALSE;
        }
        if (!cbox_execute_on(fb, NULL, "/last_key", "i", error, m->channels[channel - 1].keyswitch_lastkey[group]))
            return FALSE;
        return TRUE;
    }
    else
    if (!strcmp(cmd->command, "/patches") && !strcmp(cmd->arg_types, ""))
    {
        if (!cbox_check_fb_channel(fb, cmd->command, error))
            return FALSE;
        for (uint32_t i = 0; i < m->program_count; i++)
        {
            struct sampler_program *prog = m->programs[i];
            if (!cbox_execute_on(fb, NULL, "/patch", "isoi", error, prog->prog_no, prog->name, prog, prog->in_use))
                return FALSE;
        }
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/polyphony") && !strcmp(cmd->arg_types, "i"))
    {
        int polyphony = CBOX_ARG_I(cmd, 0);
        if (polyphony < 1 || polyphony > MAX_SAMPLER_VOICES)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid polyphony %d (must be between 1 and %d)", polyphony, (int)MAX_SAMPLER_VOICES);
            return FALSE;
        }
        m->max_voices = polyphony;
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/set_patch") && !strcmp(cmd->arg_types, "ii"))
    {
        int channel = CBOX_ARG_I(cmd, 0);
        if (channel < 1 || channel > 16)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid channel %d", channel);
            return FALSE;
        }
        int value = CBOX_ARG_I(cmd, 1);
        struct sampler_program *pgm = NULL;
        for (uint32_t i = 0; i < m->program_count; i++)
        {
            if (m->programs[i]->prog_no == value)
            {
                pgm = m->programs[i];
                break;
            }
        }
        sampler_channel_set_program(&m->channels[channel - 1], pgm);
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/set_output") && !strcmp(cmd->arg_types, "ii"))
    {
        int channel = CBOX_ARG_I(cmd, 0);
        int output = CBOX_ARG_I(cmd, 1);
        if (channel < 1 || channel > 16)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid channel %d", channel);
            return FALSE;
        }
        if (output < 0 || output >= m->output_pairs)
        {
            g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid output %d", output);
            return FALSE;
        }
        m->channels[channel - 1].output_shift = output;
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/load_patch") && !strcmp(cmd->arg_types, "iss"))
    {
        struct sampler_program *pgm = NULL;
        if (!load_program_at(m, CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2), CBOX_ARG_I(cmd, 0), &pgm, error))
            return FALSE;
        if (fb)
            return cbox_execute_on(fb, NULL, "/uuid", "o", error, pgm);
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/load_patch_from_file") && !strcmp(cmd->arg_types, "iss"))
    {
        struct sampler_program *pgm = NULL;
        char *cfg_section = g_strdup_printf("spgm:!%s", CBOX_ARG_S(cmd, 1));
        gboolean res = load_program_at(m, cfg_section, CBOX_ARG_S(cmd, 2), CBOX_ARG_I(cmd, 0), &pgm, error);
        g_free(cfg_section);
        if (res && pgm && fb)
            return cbox_execute_on(fb, NULL, "/uuid", "o", error, pgm);
        return res;
    }
    else if (!strcmp(cmd->command, "/load_patch_from_string") && !strcmp(cmd->arg_types, "isss"))
    {
        struct sampler_program *pgm = NULL; 
        if (!load_from_string(m, CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2), CBOX_ARG_S(cmd, 3), CBOX_ARG_I(cmd, 0), &pgm, error))
            return FALSE;
        if (fb && pgm)
            return cbox_execute_on(fb, NULL, "/uuid", "o", error, pgm);
        return TRUE;
    }
    else if (!strcmp(cmd->command, "/get_unused_program") && !strcmp(cmd->arg_types, ""))
    {
        if (!cbox_check_fb_channel(fb, cmd->command, error))
            return FALSE;
        return cbox_execute_on(fb, NULL, "/program_no", "i", error, get_first_free_program_no(m));
    }
    else
        return cbox_object_default_process_cmd(ct, fb, cmd, error);
    return TRUE;
}

gboolean sampler_select_program(struct sampler_module *m, int channel, const gchar *preset, GError **error)
{
    for (uint32_t i = 0; i < m->program_count; i++)
    {
        if (!strcmp(m->programs[i]->name, preset))
        {
            sampler_channel_set_program(&m->channels[channel], m->programs[i]);
            return TRUE;
        }
    }
    g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Preset not found: %s", preset);
    return FALSE;
}

double sampler_get_current_beat(struct sampler_module *m)
{
    uint32_t song_pos_samples = cbox_engine_current_pos_samples(m->module.engine);
    double tempo_bpm = m->module.engine->master->tempo;
    double song_pos_sec = song_pos_samples * 1.0 / m->module.srate;
    return song_pos_sec * tempo_bpm / 60;
}

MODULE_CREATE_FUNCTION(sampler)
{
    int i;
    static int inited = 0;
    if (!inited)
    {
        for (int i = 0; i < 2049; i++)
            sampler_sine_wave[i] = sin(i * M_PI / 1024.0);
        inited = 1;
    }
    
    int max_voices = cbox_config_get_int(cfg_section, "polyphony", MAX_SAMPLER_VOICES);
    if (max_voices < 1 || max_voices > MAX_SAMPLER_VOICES)
    {
        g_set_error(error, CBOX_SAMPLER_ERROR, CBOX_SAMPLER_ERROR_INVALID_LAYER, "%s: invalid polyphony value", cfg_section);
        return NULL;
    }
    int output_pairs = cbox_config_get_int(cfg_section, "output_pairs", 1);
    if (output_pairs < 1 || output_pairs > 16)
    {
        g_set_error(error, CBOX_SAMPLER_ERROR, CBOX_SAMPLER_ERROR_INVALID_LAYER, "%s: invalid output pairs value", cfg_section);
        return NULL;
    }
    int aux_pairs = cbox_config_get_int(cfg_section, "aux_pairs", 0);
    if (aux_pairs < 0 || aux_pairs > 4)
    {
        g_set_error(error, CBOX_SAMPLER_ERROR, CBOX_SAMPLER_ERROR_INVALID_LAYER, "%s: invalid aux pairs value", cfg_section);
        return NULL;
    }
    
    struct sampler_module *m = calloc(1, sizeof(struct sampler_module));
    CALL_MODULE_INIT(m, 0, (output_pairs + aux_pairs) * 2, sampler);
    m->output_pairs = output_pairs;
    m->aux_pairs = aux_pairs;
    m->module.aux_offset = m->output_pairs * 2;
    m->module.process_event = sampler_process_event;
    m->module.process_block = sampler_process_block;
    m->programs = NULL;
    m->max_voices = max_voices;
    m->serial_no = 0;
    m->deleting = FALSE;
    // XXXKF read defaults from some better place, like config
    // XXXKF allow dynamic change of the number of the pipes
    m->pipe_stack = cbox_prefetch_stack_new(MAX_SAMPLER_VOICES, cbox_config_get_int("streaming", "streambuf_size", 65536), cbox_config_get_int("streaming", "min_buf_frames", PIPE_MIN_PREFETCH_SIZE_FRAMES));
    m->disable_mixer_controls = cbox_config_get_int("sampler", "disable_mixer_controls", 0);

    float srate = m->module.srate;
    for (i = 0; i < 12800; i++)
    {
        float freq = 440 * pow(2, (i - 5700) / 1200.0);
        if (freq < 20.0)
            freq = 20.0;
        if (freq > srate * 0.45)
            freq = srate * 0.45;
        float omega=(float)(2*M_PI*freq/srate);
        m->sincos[i].sine = sinf(omega);
        m->sincos[i].cosine = cosf(omega);
        m->sincos[i].prewarp = 2.0 * tan(hz2w(freq, srate) * 0.5f);
        m->sincos[i].prewarp2 = 1.0 / (1.0 + m->sincos[i].prewarp);

    }
    for (i = 0; ; i++)
    {
        gchar *s = g_strdup_printf("program%d", i);
        char *p = cbox_config_get_string(cfg_section, s);
        g_free(s);
        
        if (!p)
        {
            m->program_count = i;
            break;
        }
    }
    m->programs = calloc(m->program_count, sizeof(struct sampler_program *));
    int success = 1;
    for (i = 0; i < (int)m->program_count; i++)
    {
        gchar *s = g_strdup_printf("program%d", i);
        char *pgm_section = NULL;
        int pgm_id = -1;
        const char *pgm_name = cbox_config_get_string(cfg_section, s);
        g_free(s);
        char *at = strchr(pgm_name, '@');
        if (at)
        {
            pgm_id = atoi(at + 1);
            s = g_strndup(pgm_name, at - pgm_name);
            pgm_section = g_strdup_printf("spgm:%s", s);
            g_free(s);
        }
        else
        {
            pgm_id = i;
            pgm_section = g_strdup_printf("spgm:%s", pgm_name);
        }
        
        m->programs[i] = sampler_program_new_from_cfg(m, pgm_section, pgm_section + 5, pgm_id, error);
        g_free(pgm_section);
        if (!m->programs[i])
        {
            success = 0;
            break;
        }
    }
    if (!success)
    {
        // XXXKF free programs/layers, first ensuring that they're fully initialised
        free(m);
        return NULL;
    }
    m->voices_free = NULL;
    memset(m->voices_all, 0, sizeof(m->voices_all));
    for (i = 0; i < MAX_SAMPLER_VOICES; i++)
    {
        struct sampler_voice *v = &m->voices_all[i];
        v->gen.mode = spt_inactive;
        sampler_voice_link(&m->voices_free, v);
    }
    m->active_voices = 0;
    m->active_prevoices = 0;

    m->prevoices_free = NULL;
    memset(m->prevoices_all, 0, sizeof(m->prevoices_all));
    for (i = 0; i < MAX_SAMPLER_PREVOICES; i++)
    {
        struct sampler_prevoice *v = &m->prevoices_all[i];
        sampler_prevoice_link(&m->prevoices_free, v);
    }
    
    for (i = 0; i < 16; i++)
        sampler_channel_init(&m->channels[i], m);

    for (i = 0; i < 16; i++)
    {
        gchar *key = g_strdup_printf("channel%d", i + 1);
        gchar *preset = cbox_config_get_string(cfg_section, key);
        if (preset)
        {
            if (!sampler_select_program(m, i, preset, error))
            {
                CBOX_DELETE(&m->module);
                return NULL;
            }
        }
        g_free(key);
        key = g_strdup_printf("channel%d_output", i + 1);
        m->channels[i].output_shift = cbox_config_get_int(cfg_section, key, 1) - 1;
        g_free(key);
    }
    

    return &m->module;
}

void sampler_destroyfunc(struct cbox_module *module)
{
    struct sampler_module *m = (struct sampler_module *)module;
    uint32_t i;
    m->deleting = TRUE;
    
    for (i = 0; i < m->program_count;)
    {
        if (m->programs[i])
            CBOX_DELETE(m->programs[i]);
        else
            i++;
    }
    for (i = 0; i < 16U; i++)
    {
        assert (m->channels[i].voices_running == NULL);
    }
    cbox_prefetch_stack_destroy(m->pipe_stack);
    free(m->programs);
}

#define MAKE_TO_STRING_CONTENT(name, v) \
    case v: return name;

#define MAKE_FROM_STRING_CONTENT(n, v) \
    if (!strcmp(name, n)) { *value = v; return TRUE; }

#define MAKE_FROM_TO_STRING(enumtype) \
    const char *enumtype##_to_string(enum enumtype value) \
    { \
        switch(value) { \
            ENUM_VALUES_##enumtype(MAKE_TO_STRING_CONTENT) \
            default: return NULL; \
        } \
    } \
    \
    gboolean enumtype##_from_string(const char *name, enum enumtype *value) \
    { \
        ENUM_VALUES_##enumtype(MAKE_FROM_STRING_CONTENT) \
        return FALSE; \
    }

ENUM_LIST(MAKE_FROM_TO_STRING)

//////////////////////////////////////////////////////////////////////////

struct cbox_module_livecontroller_metadata sampler_controllers[] = {
};

struct cbox_module_keyrange_metadata sampler_keyranges[] = {
};

DEFINE_MODULE(sampler, 0, 2)

