#include <stdio.h>
#include <stdlib.h>

#include "config.h"
#include "common.h"
#include "instrum.h"
#include "output.h"
#include "controls.h"
#include "tables.h"
#include "wrd.h"

#define SFPATTUNE 0.2

/*ARGSUSED*/
static int null_cmsg(int type, int verbosity_level, char *fmt, ...){return 0;}
static void null_proc(void){}
static ControlMode null_control_mode =
{
    "", 0,
    1,0,0,
    NULL,NULL, null_proc, NULL, null_cmsg,
    NULL, NULL, NULL, NULL, NULL,
    NULL,
    NULL, NULL, NULL,
    NULL, NULL, NULL, NULL
};

static PlayMode null_play_mode =
{
    1, PE_MONO,
    -1,
    {0,0,0,0,0},
    "", 0,
    "",
    NULL, null_proc, NULL, NULL, null_proc, NULL, NULL
};

static int  null_wrdt_open(void) { }
/*ARGSUSED*/
static void null_wrdt_apply(int cmd, int arg) { }
static void null_wrdt_update_events(void) { }
static void null_wrdt_end(void) { }
static void null_wrdt_close(void) { }
static WRDTracer null_wrdt_mode =
{
    "No WRD trace", '-',
    0, 0,
    null_wrdt_open,
    null_wrdt_apply,
    null_wrdt_update_events,
    null_wrdt_end,
    null_wrdt_close
};

PlayMode *play_mode = &null_play_mode;
ControlMode *ctl = &null_control_mode;
WRDTracer *wrdt = &null_wrdt_mode;

static ToneBank standard_tonebank, standard_drumset;
ToneBank
  *tonebank[128]={&standard_tonebank},
  *drumset[128]={&standard_drumset};

int32 control_ratio = 1;
int fast_decay = 0;
int antialiasing_allowed = 0;
int progbase = 0;

/* ARGSUSED */
void antialiasing(int16 *data, int32 data_length,
		  int32 sample_rate, int32 output_rate) { }
/* ARGSUSED */
void pre_resample(Sample * sp) { }

static void Put8(FILE* fp, uint8 x)
{
    putc((int)x, fp);
}

static void Put16(FILE* fp, uint16 x)
{
    x = LE_SHORT(x);
    fwrite(&x, 2, 1, fp);
}

static void Put32(FILE* fp, uint32 x)
{
    x = LE_LONG(x);
    fwrite(&x, 4, 1, fp);
}

static void PutS(FILE* fp, char *s, int maxsiz)
{
    char buff[BUFSIZ];
    int i;

    for(i = 0; i < maxsiz - 1 && s[i]; i++)
	buff[i] = s[i];
    while(i < maxsiz)
	buff[i++] = '\0';
    fwrite(buff, 1, i, fp);
}

static void Skip(FILE *fp, int n)
{
    char buff[BUFSIZ];
    int i;

    i = sizeof(buff);
    if(i > n)
	i = n;
    memset(buff, 0, i);
    while(n > 0)
    {
	i = sizeof(buff);
	if(i > n)
	    i = n;
	fwrite(buff, 1, i, fp);
	n -= i;
    }
}

static int rate_table[256] =
{
    0, 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632,
    6144, 6656, 7168, 7680, 8192, 8704, 9216, 9728, 10240, 10752, 11264,
    11776, 12288, 12800, 13312, 13824, 14336, 14848, 15360, 15872, 16384,
    16896, 17408, 17920, 18432, 18944, 19456, 19968, 20480, 20992, 21504,
    22016, 22528, 23040, 23552, 24064, 24576, 25088, 25600, 26112, 26624,
    27136, 27648, 28160, 28672, 29184, 29696, 30208, 30720, 31232, 31744,
    32256, 0, 64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768,
    832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536,
    1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240,
    2304, 2368, 2432, 2496, 2560, 2624, 2688, 2752, 2816, 2880, 2944,
    3008, 3072, 3136, 3200, 3264, 3328, 3392, 3456, 3520, 3584, 3648,
    3712, 3776, 3840, 3904, 3968, 4032, 0, 8, 16, 24, 32, 40, 48, 56, 64,
    72, 80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 160, 168, 176, 184,
    192, 200, 208, 216, 224, 232, 240, 248, 256, 264, 272, 280, 288, 296,
    304, 312, 320, 328, 336, 344, 352, 360, 368, 376, 384, 392, 400, 408,
    416, 424, 432, 440, 448, 456, 464, 472, 480, 488, 496, 504, 0, 1, 2,
    3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
    22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
    39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
    56, 57, 58, 59, 60, 61, 62, 63
};

static uint8 cnv_rate(int32 rate)
{
    int r;
    int d, rn, dn;

    if(rate == 0)
	return 0;
    if(rate >= 32256)
	return 0x3f;

    rate >>= 9;
    rate /= 44100;

    dn = 0x7f;
    rn = 0;
    for(r = 0; r < 256; r++)
    {
	if(rate < rate_table[r])
	    d = rate_table[r] - rate;
	else
	    d = rate - rate_table[r];
	if(dn > d)
	{
	    if(d == 0)
		return r;
	    dn = d;
	    rn = r;
	}
    }

    return rn;
}

static uint8 cnv_offs(int32 offs)
{
    offs >>= 7+15;
    if(offs > 0xff)
	offs = 0xff;
    return (uint8)offs;
}

static uint8 cnv_trsinc(int32 sweep)
{
    if(sweep == 0)
	return 0;
    sweep = (SWEEP_TUNING << SWEEP_SHIFT) / sweep;
    if(sweep > 0xff)
	sweep = 0xff;
    return (uint8)sweep;
}

static uint8 cnv_trrate(int32 rate)
{
    rate = ((rate * TREMOLO_RATE_TUNING) >> RATE_SHIFT) / SINE_CYCLE_LENGTH;
    if(rate > 0xff)
	rate = 0xff;
    return (uint8)rate;
}

static uint8 cnv_visinc(int32 sweep, int32 vib_control_ratio)
{
    if(sweep == 0)
	return 0;
    sweep = (int32)(TIM_FSCALE((double)vib_control_ratio * SWEEP_TUNING,
			       SWEEP_SHIFT) / (double)sweep);
    if(sweep > 0xff)
	sweep = 0xff;
    return sweep;
}

static uint8 cnv_virate(int32 rate)
{
    rate = ((rate * TREMOLO_RATE_TUNING) >> RATE_SHIFT) / SINE_CYCLE_LENGTH;
    if(rate > 0xff)
	rate = 0xff;
    return rate;
}

static void WriteDat(FILE *fp, int16 *data, int32 len)
{
    int16 buff[1024];
    int32 i, n;

    while(len > 0)
    {
	n = 1024;
	if(n > len)
	    n = len;
	for(i = 0; i < n; i++)
	{
	    buff[i] = LE_SHORT(*data);
	    data++;
	}
	fwrite(buff, 2, n, fp);
	len -= n;
    }
}

extern struct URL_module URL_module_file;
extern struct URL_module URL_module_dir;
#ifdef SUPPORT_SOCKET
extern struct URL_module URL_module_http;
extern struct URL_module URL_module_ftp;
extern struct URL_module URL_module_news;
extern struct URL_module URL_module_newsgroup;
#endif /* SUPPORT_SOCKET */

void main(int argc, char **argv)
{
    int bank, preset, keynote;
    char *sndfont;
    Instrument *ip;
    Sample *sp;
    int i, j, nsp;
    int32 datasize, sz;
    FILE *fp;
    char *name;

    if(argc < 4)
    {
	fprintf(stderr, "Usage: %s soundfont bank preset [keynote]\n",
		argv[0]);
	exit(0);
    }

    url_add_modules(
	&URL_module_file,
	&URL_module_dir,
#ifdef SUPPORT_SOCKET
	&URL_module_http,
	&URL_module_ftp,
	&URL_module_news,
	&URL_module_newsgroup,
#endif /* SUPPORT_SOCKET */
	NULL);

    sndfont = argv[1];
    bank = atoi(argv[2]);
    preset = atoi(argv[3]);
    if(argc > 4)
	keynote = atoi(argv[4]);
    else
	keynote = -1;

    add_soundfont(sndfont, 0, -1, -1, -1);
    init_load_soundfont();

    ip = load_soundfont_inst(0, bank, preset, keynote);

    if(ip == NULL)
    {
	fprintf(stderr,
		"Can't load soundfont: bank=%d, preset=%d, keynote=%d\n",
		bank, preset, keynote);
	exit(0);
    }

    name = soundfont_preset_name(bank, preset, keynote, NULL);

    fprintf(stderr, "Name: %s\n", name);

    nsp = ip->samples;
    sp = ip->sample;

    datasize = 0;
    for(i = 0; i < nsp; i++)
    {
	sz = sp[i].data_length >> (FRACTION_BITS - 1);
	datasize += sz;
    }

    fp = stdout;

    /* header */
    PutS(fp, "GF1PATCH110", 12);

    /* Gravis ID */
    PutS(fp, "ID#000002", 10);

    /* description */
    PutS(fp, "Copyleft 1995 EWE&U Conductions and one Retreated Gravi\032",
	 60);

    Put8(fp, 1); /* instruments */
    Put8(fp, 14); /* voices */
    Put8(fp, 0); /* channels */
    Put16(fp, 1); /* waveforms */
    Put16(fp, 127); /* master volume */
    Put32(fp, datasize); /* data size */
    Skip(fp, 36); /* Reserved */

    Put16(fp, 1); /* instrument # */
    PutS(fp, name, 16);
    Put32(fp, datasize); /* instrument size */
    Put8(fp, 1); /* layers */
    Skip(fp, 40); /* reserved */

    Put8(fp, 0); /* layer duplicate */
    Put8(fp, 0); /* layer */
    Put32(fp, datasize); /* layer size */
    Put8(fp, nsp); /* samples */
    Skip(fp, 40); /* reserved */

    for(i = 0; i < nsp; i++, sp++)
    {
	sz = sp->data_length >> (FRACTION_BITS - 1);
	fprintf(stderr, "DataSize[%d]: %d\n", i, sz);

	PutS(fp, "NoName", 7);	/* name */
	Put8(fp, 0); /* fractions */
	Put32(fp, sz); /* wave size */
	Put32(fp, sp->loop_start >> (FRACTION_BITS - 1)); /* loop start */
	Put32(fp, sp->loop_end >> (FRACTION_BITS) - 1); /* loop end */
	Put16(fp, (uint16)(sp->sample_rate * SFPATTUNE)); /* sample rate */
	Put32(fp, sp->low_freq); /* low freq */
	Put32(fp, sp->high_freq); /* high freq */
	Put32(fp, (uint32)(sp->root_freq * SFPATTUNE)); /* root freq */
	Put16(fp, 512); /* tune */

	/* balance */
	if(sp->panning >= 127)
	    Put8(fp, 15);
	else if(sp->panning == 64)
	    Put8(fp, 7);
	else
	    Put8(fp, (uint8)(sp->panning / 8));

	for(j = 0; j < 6; j++)
	    Put8(fp, cnv_rate(sp->envelope_rate[0])); /* envelope rate */

	for(j = 0; j < 6; j++)
	    Put8(fp, cnv_offs(sp->envelope_offset[0])); /* envelope offset */

	/* tremolo */
	if(sp->tremolo_sweep_increment == 0 ||
	   sp->tremolo_phase_increment == 0)
	{
	    Put8(fp, 0);
	    Put8(fp, 0);
	    Put8(fp, 0);
	}
	else
	{
	    Put8(fp, cnv_trsinc(sp->tremolo_sweep_increment));
	    Put8(fp, cnv_trrate(sp->tremolo_phase_increment));
	    Put8(fp, sp->tremolo_depth);
	}

	/* vibrato */
	if(sp->vibrato_control_ratio == 0 ||
	   sp->vibrato_depth == 0)
	{
	    Put8(fp, 0);
	    Put8(fp, 0);
	    Put8(fp, 0);
	}
	else
	{
	    Put8(fp, cnv_visinc(sp->vibrato_sweep_increment,
				sp->vibrato_control_ratio));
	    Put8(fp, cnv_virate(sp->vibrato_control_ratio));
	    Put8(fp, sp->vibrato_depth);
	}

	sp->modes |= MODES_16BIT;
	sp->modes &= ~(MODES_UNSIGNED|MODES_REVERSE);
	Put8(fp, sp->modes);  /* modes */

	Put16(fp, 60); /* scale freq */
	Put16(fp, 1024); /* scale factor */
	Skip(fp, 36);

	WriteDat(fp, (int16 *)sp->data, sz / 2);
    }
    exit(0);
}

