/************************************************************************
 *	TITLE:			PARSETC.C (MIDI)			*
 *	PROJECT:		JAGUAR					*
 *	FUNCTION:		Support functions for real-time parser	*
 *	PROGRAMMER:		Hans-Martin Krober			*
 *	DATE:			4/95					*
 *									*
 *              COPYRIGHT 1993,1994 Atari U.S. Corporation              *
 *          UNATHORIZED REPRODUCTION, ADAPTATION, DISTRIBUTION,         *
 *          PERFORMANCE OR DISPLAY OF THIS COMPUTER PROGRAM OR          *
 *        THE ASSOCIATED AUDIOVISUAL WORK IS STRICTLY PROHIBITED.       *
 *                            ALL RIGHTS RESERVED.                      *
 *									*
 ************************************************************************/

#include "ptchptch.h"

/*
 *	Externals
 */

extern long	midi_score[];
extern long	midi_score2[];
extern void	play_midi(void);
extern short	alg_omni_mode;
extern short	pmt_omni_mode;
extern short	curr_pnr;
extern long	event;

/*
 * 	Defines
 */
#define TRUE  1
#define FALSE 0


#define USED_VOICES	12
#define MAX_VOICES	32


/*	pitchpatch
 *	this data structure stores information
 *	for changing a pitch range to a patch
 */

struct _pitchpatch pitchpatch[MIDI_CHANNELS];

short		n_voices;	/* number of voices to use (default is 8) */
short		voice_flag[MAX_VOICES];
long		voice_time[MAX_VOICES];
long		cum_time;
short		last_voice;

short		noteoff;
short		noteon;

short		sound_off;
short		sound_off_cnt;

/*
 *	Prototypes
 */
short	patch_trans(short *, short, short *);
short	alloc_voice(short, short);
short	free_voice(short, short);
short	find_voices(short);
short	find_patches(short);
/* void	program_change(short, short); - you find this in jaz.c */
void	init_voices(void);
void	voices_off(void);


/*
 *	check if we want to translate a pitch range into a patch
 */



	short
patch_trans(channel, pitch, priority)
	register short *channel;
	register short pitch;
	short *priority;
{
	register struct _pitchpatch *p;
	register int i, old;
	

	if (alg_omni_mode || pmt_omni_mode) {
		*channel = curr_pnr;
		*priority = 7;
		return pitch;
	}

	p = &pitchpatch[*channel];
	if (p->translate == TRUE) {
		for (i = 0; i < p->n; i++) {
			if ((p->start[i] <= pitch) &&
			(pitch <= p->end[i])) {
/*				old = *channel;*/
				*channel = (short) p->patch[i];
				pitch += (short) p->offset[i];
				*priority = p->priority[i];
				break;
			}
		}
	}

	return pitch;
}


	short
alloc_voice(patch, note)
	short patch, note;
{
	short flag;
	short found;
	short max_index;
	long max_time;
	register short i, j;

	flag = (patch << 8) + note;

	found = FALSE;

#if 0	/* old way ..*/
	for (i = 0 ; i < n_voices ; i++) {
#if 1
		j = (i + last_voice + 1) % n_voices; 
#else
		j = i;
#endif

		if (voice_flag[j] == -1) {
			voice_flag[j] = flag;
			last_voice = j;
			found = 1;
			break;
		}
	}
#else	/* use the one which was used the longest time ago */
	for (i=0 ; i < n_voices ; i++) {
		if (voice_flag[i] == -1) {
			found = TRUE;
			max_time = voice_time[i];
			max_index = i;
			for (j = i + 1; j < n_voices ; j++) {
				if (voice_flag[j] == -1) {
					if (max_time > voice_time[j]) {
						max_time = voice_time[j];
						max_index = j;
					}
				}
			}
			break;
		}
	}
#endif
	cum_time++;
	if (!found) {
		noteon++;
		return -1;
	} else {
		voice_flag[max_index] = flag;
		return max_index;
	}
}

	short
free_voice(patch, note)
	short patch, note;
{
	short flag, found;
	register short i;

	found = 0;
	flag = (patch << 8) + note;

	for (i = 0; i < n_voices; i++) {
		if (voice_flag[i] == flag) {
			voice_flag[i] = -1;
			voice_time[i] = cum_time++;
			found = 1;
			break;
		}
	}
	if (!found) {
		noteoff++;
		return -1;
	} else
		return i;
}

short pbix;
short pbvi;
short pbch;
short pbpatch;

	short
find_voices(channel)
	register short channel;
{
	register struct _pitchpatch *p;
	register short patch, voice;

	voice = -1;
	if (channel != -1) {
		pbch = channel;
		pbix = 0;
		pbvi = 0;
 	} else if (pbix == -1)
		return -1;

	if (alg_omni_mode || pmt_omni_mode) {
		pbpatch = curr_pnr;
		pbix = -1;
	} else {
		p = &pitchpatch[pbch];
		if (p->translate == FALSE) {			
			pbix = -1;
			pbpatch = channel;
		}
	}

	do {
		if ((voice == -1) && (pbix != -1)) {
			if (pbix >= p->n) {
				return -1;				
			}		
			pbpatch = (short) p->patch[pbix++];
			pbvi = 0;
		}
		for (; pbvi < n_voices; pbvi++) {
			if ((voice_flag[pbvi] >> 8) == pbpatch) {
				voice = pbvi;
				break;
			}
		}

	} while ((pbix != -1) && (voice == -1));
	
	return voice;
}


	short
find_patches(channel)
	register short channel;
{
	register struct _pitchpatch *p;
	register short patch;

	patch = -1;
	if (channel != -1) {
		pbch = channel;
		pbix = 0;
		pbvi = 0;
 	} else if (pbix == -1)
		return -1;

	if (alg_omni_mode || pmt_omni_mode) {
		patch = curr_pnr;
		pbix = -1;
	} else {
		p = &pitchpatch[pbch];
		if (p->translate == FALSE) {			
			pbix = -1;
			patch = channel;
		}
	}

	do {
		if ((patch == -1) && (pbix != -1)) {
			if (pbix >= (p->n - 1)) { /* don't do default/cover patch */
				return -1;				
			}		
			patch = (short) p->patch[pbix++];
		}

	} while ((pbix != -1) && (patch == -1));
	
	return patch;
}



	void
init_voices(void)
{
	register int i;

	n_voices = USED_VOICES;
	for (i = 0; i < n_voices; i++) {
		voice_flag[i] = -1;		
		voice_time[i] = 0L;
	}
	for (i = 0; i < MIDI_CHANNELS; i++)
		pitchpatch[i].translate = FALSE;

	cum_time = 0L;
	last_voice = -1;
	noteoff = 0;
	noteon = 0;
}


	void
voices_off(void)
{
	register int i;
	register long voice;
	extern long *_pr_table;
	long *table;
	
	table=_pr_table;
	
	for (i = 0; i < 14; i++)
	{
		*table++=4;
		table++;
	}
}

	void
disable_sound(void) 
{
	if (sound_off)
		sound_off_cnt++;
	else {
		voices_off();
		
		sound_off = TRUE;
		sound_off_cnt = 1;
	}
}

	void
enable_sound(void)
{
	if (sound_off_cnt > 0)
		sound_off_cnt--;
	if (sound_off_cnt == 0)
		sound_off = FALSE;
}
