/*-
 * Copyright (c) 2000
 * Tatsuya Kudoh(CDR/TK),ROYALPANDA.    All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include<stdlib.h>
#include<sys/bios.h>

#define NUM_CHANNELS	2
#define NUM_VOICES	3

#define SE_CHANNEL	2
#define SE_VOL		3
#define SE_VOICE	1


typedef struct{
	int ch;
	int tone;
	unsigned char *data_start;
	unsigned char *ptr;
	int volume_r;
	int volume_l;
	int cur_vol_r;
	int cur_vol_l;
	int count;
	int sustain;
	int quote;
}sound_t;

static sound_t Sound[NUM_CHANNELS];

static int Tempo;


static int Note_data[96] = {
147, 254, 354, 449, 539, 624, 704, 779, 850, 918, 981, 1041,
1098, 1151, 1201, 1249, 1294, 1336, 1376, 1414, 1449, 1483, 1515, 1544,
1573, 1599, 1625, 1648, 1671, 1692, 1712, 1731, 1749, 1765, 1781, 1796,
1810, 1824, 1836, 1848, 1859, 1870, 1880, 1889, 1898, 1907, 1915, 1922,
1929, 1936, 1942, 1948, 1954, 1959, 1964, 1969, 1973, 1977, 1981, 1985,
1989, 1992, 1995, 1998, 2001, 2003, 2006, 2008, 2011, 2013, 2015, 2017,
2018, 2020, 2022, 2023, 2024, 2026, 2027, 2028, 2029, 2030, 2031, 2032,
2033, 2034, 2035, 2036, 2036, 2037, 2037, 2038, 2039, 2039, 2040, 
0, /* dummy */
};


#define O1	0
#define O2	12
#define O3	24
#define O4	36
#define O5	48
#define O6	60
#define O7	72
#define O8	84

#define C	0
#define CP	1
#define D	2
#define DP	3
#define E	4
#define F	5
#define FP	6
#define G	7
#define GP	8
#define A	9
#define AP	10
#define	B	11

#define R	95


#define CONTROL	96

#define VOL_R	96
#define VOL_L	112
#define QUOTE	128
#define VOICE	136

#define JUMP	0xff


static unsigned char Music_ch1[] = 
{
	VOICE+1, VOL_R+9, VOL_L+9, QUOTE+6,
	R,150, R,150, R,150, R,150, R,150, R,150, R,150,O4+D,75, O4+F,75,
	O4+A,75, O4+A,75, O4+A,75, O4+G,75, O4+A,75, O4+A,75, O4+A,75, O4+G,75,
	O4+A,75, O4+G,75, O4+F,255,45, O4+A,75, O4+G,75,
	O4+A,150, O4+G,75, O4+F,75, O4+G,150, O4+F,75, O4+E,75,
	O4+F,75, O4+E,75, O4+D,255,45, O4+D,75, O4+F,75,
	O4+A,75, O4+A,75, O4+A,75, O4+G,75, O4+A,75, O4+A,75, O4+A,75, O4+G,75,
	O4+A,75, O4+G,75, O4+F,255,45, O4+A,75, O4+G,75,
	O4+A,75, O4+A,75, O4+G,75, O4+F,75, O4+G,150, O4+A,150,
	VOL_R+4,VOL_L+4,
	O5+D,255,195,VOL_R+9,VOL_L+9,
	JUMP,0,18,
};

static unsigned char Music_ch2[] = 
{
	VOICE+2, VOL_R+5, VOL_L+5, QUOTE+2,
	O1+A,150, O1+A,150, O1+A,150, O1+A,150, O1+A,150, O1+A,150, O1+A,150,
							O1+A,75, O1+A,75,
	O1+A,150, R,150, O1+A,150, R, 150,
	O1+A,150, O1+A,150, R,150, O1+A,75, O1+A,75,
	O1+A,150, R,150, O1+A,150, R, 150,
	O1+A,150, O1+A,150, R,150, O1+A,75, O1+A,75,
	O1+A,150, R,150, O1+A,150, R, 150,
	O1+A,150, O1+A,150, R,150, O1+A,75, O1+A,75,
	O1+A,150, R,150, O1+A,150, O1+A,150,
	O1+A,75, O1+A,75, O1+A,75, O1+A,75, O1+A,75, O1+A,75,
	JUMP,0,18,
};


static unsigned char *Music[NUM_CHANNELS] =
{
	Music_ch1,Music_ch2
};


static unsigned char Voice_data[NUM_VOICES][16] = {
    {
	/* sin wave */
	0x00,0x10,0x32,0x64,0x98,0xcb,0xed,0xff,
	0xff,0xef,0xcd,0x9b,0x68,0x34,0x12,0x00,
    },{
	/* LPF'ed square wave */
	0xc8,0xfe,0xff,0xff,0xff,0xff,0xef,0x8c,
	0x24,0x01,0x00,0x00,0x00,0x00,0x10,0x42,
    },{
	/* white noise */
	0x34,0xd2,0x93,0x27,0x25,0x7e,0x4e,0x13,
	0xa9,0x51,0x89,0xd0,0x1a,0x0b,0x17,0x4a,
    },
};

void SOUND_init( void )
{
	int i;

	sound_init();

	sound_set_channel(1<<SE_CHANNEL|0x0f>>(4-NUM_CHANNELS));
	sound_set_output(0x0f);

	for( i =0 ; i < NUM_CHANNELS ; i++ ){
		Sound[i].ch = i;
		Sound[i].data_start = Music[i];
		sound_set_volume(i,0x0);
	}

	sound_set_wave(SE_CHANNEL,Voice_data[SE_VOICE]);
	sound_set_volume(SE_VOICE,0);
}


static void parse( sound_t *sp )
{
	unsigned char *p;
	int len;
	int addr;

	p = sp->ptr;

	while( *p >= CONTROL ){
		if( *p < VOL_R+16 ){
			sp->volume_r = *p++ - VOL_R;
		}else if( *p < VOL_L+16 ){
			sp->volume_l = *p++ - VOL_L;
		}else if( *p < QUOTE+8 ){
			sp->quote = *p++ - QUOTE;
		}else if( *p < VOICE+NUM_VOICES ){
			sound_set_wave(sp->ch,Voice_data[*p++ - VOICE]);
		}else if( *p == JUMP ){
			addr = (unsigned)p[1] << 8 | p[2];
			sp->ptr = sp->data_start + addr;
			p = sp->ptr;
		}else{
			sp->ptr = sp->data_start;
			p = sp->ptr;
		}
	}

	if( *p == R ){
		sp->cur_vol_r = 0;
		sp->cur_vol_l = 0;
	}else{
		sp->tone = Note_data[*p];
		sp->cur_vol_r = sp->volume_r;
		sp->cur_vol_l = sp->volume_l;
	}
	p++;

	len = 0;
	do{
		len += *p;
	}while( *p++ == 0xff );

	sp->count += len;
	sp->sustain = len - (len * sp->quote / 8);

	sp->ptr = p;
}

void SOUND_set_tempo( int t )
{
	Tempo = t;
}

void SOUND_play( void )
{
	int i;
	sound_t *sp;

	sp = Sound;
	for( i = 0 ; i < NUM_CHANNELS ; i++, sp++ ){
		while( sp->count <= 0 )
			parse(sp);

		sp->count -= Tempo;
		if( sp->count < sp->sustain ){
			if( sp->cur_vol_r > 0 )
				sp->cur_vol_r--;
			if( sp->cur_vol_l > 0 )
				sp->cur_vol_l--;
		}
	}

	sp = Sound;
	for( i = 0 ; i < NUM_CHANNELS ; i++, sp++ ){
		sound_set_volume(i,sp->cur_vol_l<<4|sp->cur_vol_r);
		sound_set_pitch(i,sp->tone);
	}
}


void SOUND_stop( void )
{
	int i;

	for( i = 0 ; i < NUM_CHANNELS ; i++ )
		sound_set_volume(i,0x0);
}

void SOUND_start( void )
{
	int i;

	for( i =0 ; i < NUM_CHANNELS ; i++ ){
		Sound[i].ptr = Sound[i].data_start;
		Sound[i].count = 0;
	}
}


void SOUND_set_SE( int tone )
{
	if( tone < 0 ){
		sound_set_volume(SE_CHANNEL,0);
	}else{
		sound_set_volume(SE_CHANNEL,SE_VOL<<4|SE_VOL);
		sound_set_pitch(SE_CHANNEL,tone);
	}
}

