/************************************************************************
 *	TITLE:			JAZSYN.C				*
 *	PROJECT:		JAGUAR/JAZ				*
 *	FUNCTION:		JAZ SYNTH CONFIG MODULE			*
 *	PROGRAMMER:		Hans-Martin Krober			*
 *	DATE:			5/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.                      *
 *									*
 ************************************************************************/

/*
 *	DON'T FORGET LIST:
 
	- instead of giving labels with no name the "noname" label, append
	  also a number ("noname1")

 */


#include <stdarg.h>
#include <ctype.h>
#include "portab.h"
#include "slibc.h"
#include "alloc.h"
#include "glib.h"
#include "gemlib.h"
#include "jazobj.h"
#include "jazsyn.h"
#include "ptchptch.h"
#include "dos.h"

/*
 *	EXTERNALS
 */
EXTERN WORD	setup_alg_values(WORD);		/* init algorithm page values */
EXTERN BYTE	SLASH;				/* slash '/' or '\' */
EXTERN LONG	*_def_patches;			/* patches which came with synth */
EXTERN OBJECT	pstabtree[];			/* patch loading page tree */
EXTERN OBJECT	jaztree[];			/* main page tree */
EXTERN WORD	scrn_w, scrn_h;			/* screen sizes */
EXTERN BYTE	filebuf[];			/* file i.o */
EXTERN ULONG	*_xvalue;			/* for getting symbol refs */
EXTERN ULONG	*_attn;				/* ptr to SYNTH ATTENUATOR */
EXTERN ULONG	*_mdtab_start;			/* start of dsp mdtabs */
EXTERN ULONG	*_env_start;			/* start of dsp envelopes */
EXTERN ULONG	*_env_end;			/* end  of dsp envelopes */
EXTERN ULONG	*_env_temp;			/* envelope temp buffer */
EXTERN ULONG	*_vt_return_loc;
EXTERN struct _pitchpatch pitchpatch[];

EXTERN LONG	patchtab[][PATCH_SIZE];		/* buffer for patches */
EXTERN PTT_TAB 	ptt_table[];			/* PTT Data (Patch Map) */
EXTERN WORD	ptt_count;			/* # of entries in PTT */

EXTERN OBJECT 	algtree[];			/* algorithm edit patge */

EXTERN LONG	*curr_patch;			/* current edited patch */
EXTERN WORD 	curr_pnr;			/* current edited patch number */

EXTERN ENVTAB	*pi_find_envtab(WORD, WORD);
EXTERN WORD	pi_find_envtab_ix(WORD, WORD);

EXTERN ULONG	_osc_sampler;
EXTERN ULONG	_osc_nisampler;
EXTERN ULONG	_osc_fixedwave;
EXTERN ULONG	_osc_fm;
EXTERN ULONG	_osc_polywave;
EXTERN ULONG	_mdtab_reverb;
EXTERN ULONG	_reverb;
EXTERN ULONG	_delay;

/*
 *	GLOBAL VARIABLES
 */

OBJECT	proctree[4];				/* work in progress box */
BYTE	patch_names[NR_PATCHES][16];		/* names of patches */

BYTE	nrstr[NR_PATCHES][4];			/* just strings with number */
						/* 1, 2, 3, 4, ...	    */
BYTE	active_str[] = "   X";
BYTE	non_active_str[] = "    ";


BYTE osc_types[][18] = {
	"SAMPLER",
	"NON INTP. SAMP",
	"X SAMPLER",
	"X NON INTP. SAMP",
	"FIXED WAVE",
	"FM",
	"POLY WAVE"
};



BYTE	*patchnameslist[NR_PATCHES * 4];	/* for list obj */
BYTE	*samplenameslist[NR_SAMPLES * 3];	/* for list obj */


/*
 *	Symbol Reference Handling
 */
#define N_REFS	256

SYMTAB	sym_ref[N_REFS];
WORD	ref_cnt;

/*
 * 	Sample Reference Handling
 */

SMPTAB	sample_table[N_SAMPLES];	/* sample pointers */
WORD	smpslot_cnt;			/* current sample slot */

/*
 * 	Envelope Reference Handling
 */
WORD	n_envtab;			/* for debugging */
ENVTAB	env_table[N_ENVS];		/* envelope data */
WORD	envslot_cnt;			/* current env slot */
WORD	temp_env_ix;			/* Env_tab currently in temp */
WORD	temp_env_flag;			/* Flag for modifying all or just one */
					/* patch's envelope */
WORD	temp_env_new;			/* Flag showing if we created a new */
ENVTAB	*temp_env_envtab_bak;

ENVTAB	dflt_envtab = {
	{ "dflt_env" },
	{ 0 }, { 0 }, { 0 },
	{ 0L }, { 0L }, { 0L },
	{ 6 },
	{ 0x00010000L }, {0xfe000000L },
	{ 0x00100000L, 0x32000000L, 0x00095555L, 0x16000000L,
	  0x00000000L, 0x40000001L }

};
					/* envelope entry */
/*
 * 	MDTAB Reference Handling
 */

MDTAB	mdtab_table[N_MDTABS];		/* mdtab data */
WORD	mdtabslot_cnt;			/* current mdtab slot */


/*
 * 	Patch Info Table
 */

PATCHINFO patchinfo_table[NR_PATCHES];
PATCHINFO patchinfo_temp;

WORD	  patch_cnt;		/* number of loaded patches */
/*
 *	DSP Memory management
 */
#define N_DSP_MDTABS 10
#define N_DSP_ENVS   64
BYTE	dsp_mdtabslots[N_DSP_MDTABS];
ENVSLOT	dsp_envslots[N_DSP_ENVS];		
WORD	dsp_envslot_cnt;

/*
 *	Build in stuff (ROM WAVES)
 */

SYMTAB rom_symbols[] = {
	{ "ROM_TRI", 	SYMTYP_WAVEFRM, &sample_table[0], 0, 0xf1d000L },
	{ "ROM_SINE",	SYMTYP_WAVEFRM, &sample_table[1], 1, 0xf1d200L },
	{ "ROM_AMSINE",	SYMTYP_WAVEFRM, &sample_table[2], 2, 0xf1d400L },
	{ "ROM_12W",	SYMTYP_WAVEFRM, &sample_table[3], 3, 0xf1d600L },
	{ "ROM_CHIRP16",SYMTYP_WAVEFRM, &sample_table[4], 4, 0xf1d800L },
	{ "ROM_NTRI",	SYMTYP_WAVEFRM, &sample_table[5], 5, 0xf1da00L },
	{ "ROM_DELTA",	SYMTYP_WAVEFRM, &sample_table[6], 6, 0xf1dc00L },
	{ "ROM_NOISE",	SYMTYP_WAVEFRM, &sample_table[7], 7, 0xf1de00L },
	{ "", 		0,		0L,   0, 0x0L 	   }
};
BYTE *romwave[] = {
	"TRIANGLE", "SINUS", "AM SINUS", "HARM SINUS", "CHIRP", 
	"TRIAN+NOISE", "DELTA", "NOISE"
};
BYTE *sampletypes[] = {
	"SAMP", "COMP", "WAVE", "ROMW" 
};

/*	----------------------------------------------------------------------
 *	Prototypes
 */

VOID	pstab_handler(OBJECT *, WORD, WORD, WORD, ULONG);
WORD	read_patch(WORD, BYTE *, WORD);
WORD	read_sample(BYTE *, WORD);
ULONG	symbol_lookup(BYTE *, WORD, WORD);
BYTE	*section_mdtab(MFILE *);
BYTE	*section_patchmap(MFILE *);
BYTE	*section_patch(MFILE *, WORD);
BYTE	*section_sample(MFILE *);
BYTE	*section_envelope(MFILE *);
BYTE	*section_waveform(MFILE *);
VOID	store_patch(ULONG *, WORD, BYTE *);
WORD	find_sampleSlot(VOID);
VOID	free_sampleSlot(WORD);
VOID	free_allSamples(VOID);
WORD	find_mdtabSlot(VOID);
VOID	free_mdtabSlot(WORD);
WORD	find_envSlot(VOID);
BYTE	*find_sampleByName(BYTE *);
BYTE	*string2fname(BYTE *);
VOID	update_samplenameslist(VOID);
VOID	update_patchnameslist(VOID);
VOID	label2name(BYTE *);
VOID	mk_patches_active();
WORD	activate_mdtab(MDTAB *, WORD);
WORD	activate_env(ENVTAB **, WORD);
VOID	init_dspmem(VOID);
LONG	*allocENV(WORD, WORD);
VOID	freeENV(LONG *);
VOID	env2dsp(ENVTAB *);
WORD	dspenv2temp(LONG*, WORD);
WORD	temp2dspenv(WORD, LONG, LONG);
WORD	cancel_tempenv(VOID);
LONG	*allocMDTAB(VOID);
VOID	freeMDTAB(LONG*);
VOID	mdtab2dsp(MDTAB *);
BYTE	*mk_noname(VOID);
WORD	get_channel(WORD);

/*	----------------------------------------------------------------------
 *	FUNCTIONS
 */

BYTE _no_name[] = "noname";
WORD _no_name_cnt;

	BYTE*
mk_noname()
{
	REG BYTE *s, *t = _no_name;


	if ((s = alloc(sizeof(_no_name + 4))) == NULL)
		return t;
	sprintf(s, "%s%03d", t, _no_name_cnt++);

	return s;	
}

/*
 *	Init patch handling data structures
 */


	VOID
init_patches()
{
	REG WORD	i, j;
	REG LONG	*p, *q;
	REG LONG	rom;
	WORD ix;


	/*
	 * ALL are undefined!
	 */
	for (i = 0; i < NR_PATCHES; i++)
		strcpy(patch_names[i], "UNDEFINED");

	/*
	 * Fill number string table with numbers
	 */
	for (i = 1; i <= NR_PATCHES; i++)
		itoa(i, 3, nrstr[i-1]);
	
	/*
	 * Build list for patch & sample list objects 
	 */
	for (i = 0, j = 0; i < NR_PATCHES; i++) {
		patchnameslist[j++] = nrstr[i];
		patchnameslist[j++] = patch_names[i];
		patchnameslist[j++] = " ";
	}
	for (i = 0, j = 0; i < NR_SAMPLES; i++) {
		samplenameslist[j++] = nrstr[i];
		samplenameslist[j++] = "UNDEFINDED";
		samplenameslist[j++] = " ";
		sample_table[i].addr = NULL;
		sample_table[i].free = TRUE;
	}
	for (i = 0; i < N_ENVS; i++) {
		env_table[i].free = TRUE;
	}
	for (i = 0; i < N_MDTABS; i++) {
		mdtab_table[i].free = TRUE;
	}
	smpslot_cnt = 0;	/* no samples yet */
	envslot_cnt = 0;	/* no envelopes yet */
	mdtabslot_cnt = 0;	/* no mdtabs yet */
	ref_cnt = 0;		/* no symbol refs */
	_no_name_cnt = 0;
	patch_cnt = 0;
	/* 
	 * The ROM Waves are ALWAYS the first entries in the sample table
	 */
	rom = 0xf1d000L;
	for (i = 0; i < 8; i++) {
		sample_table[smpslot_cnt].addr = (BYTE*) rom;
		sample_table[smpslot_cnt].size = 512L;
		sample_table[smpslot_cnt].free = FALSE;
		strcpy(sample_table[smpslot_cnt].name, romwave[i]);
		sample_table[smpslot_cnt].type = SMPTYPE_ROMWAVE;
		smpslot_cnt++;
		rom += 0x200L;
	}

	/*
	 *	Init DSP Memory & data structures
	 */
	init_dspmem();	
	n_envtab = 0;
	for (i = 0; i < N_ENVS; i++) {
		env_table[i].dsp_addr = 0L;
		env_table[i].used[0] = 0L;
		env_table[i].used[1] = 0L;
		env_table[i].used[2] = 0L;
		env_table[i].used[3] = 0L;
	}
	for (i = 0; i < N_MDTABS; i++) {
		mdtab_table[i].dsp_addr = 0L;
		mdtab_table[i].used[0] = 0L;
		mdtab_table[i].used[1] = 0L;
		mdtab_table[i].used[2] = 0L;
		mdtab_table[i].used[3] = 0L;
	}

	/*
	 * Put in a default envelope
	 */
	if ((ix = find_envSlot()) == -1) {	
		form_alert(FRM_ALERT, FMSTR_NOENVSLOT);

	} else {
		env_table[ix] = dflt_envtab;
		env_table[ix].type = 0;
		env_table[ix].dsp_addr = NULL;
		env_table[ix].used[0] = 0L;
		env_table[ix].used[1] = 0L;
		env_table[ix].used[2] = 0L;
		env_table[ix].used[3] = 0x80000000L; 
			/* so that it never gets freed !!! */
		strcpy(env_table[ix].name, "DFLT_ENV");
		env_table[ix].dsp_addr = (LONG*)
		allocENV(env_table[ix].n_entries, ix);
		if (env_table[ix].dsp_addr == NULL) {
			form_alert(FRM_ALERT, FMSTR_NODSPENVSPACE);
		} else {
			env2dsp(&env_table[ix]);
			env_table[ix].free = FALSE;
		}
	}

	objc_xadd(proctree, NIL, 0, 
		G_BOX, -1, -1, 
		120, 70, 150, 20,
		IS3DOBJ|NONE, NORMAL,
		0);	
		objc_xadd(proctree, 0, 1, 
			G_XTEXT, -1, -1, 
			4, 3, 128, 14, 
			IS3DOBJ|NONE, NORMAL,
			T_OB_LATTACH, 0, 4,
			T_OB_RATTACH, 0, 4,
			T_BX_TEXT, "Loading patch...",
			T_BX_FONTID, FNT_MEDIUM,
			T_BX_ALIGN, TE_CNTR,
			0);
}


/*
 *	"Load patch" handling callback
 */
	VOID
pstab_pload(tree, curr, found, n, k)
	OBJECT *tree;
	WORD	curr;
	WORD	found;
	WORD	n;
	ULONG	k;
{
	BYTE path[256];
	BYTE fname[16];
	WORD x;

	objc_outline(tree, NIL, curr);
	x = fsel_input("Load Patch", ".", ".pat", path, &fname[1]);
	objc_draw(tree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);

	if (x) {
		BYTE bbuf[28];
		BYTE dbuf[324];
		LISTINFO *list = pstabtree[PSTAB_PLIST].ob_spec.list;
	
		fname[0] = SLASH;
		strcat(path, fname);
		strrncpy(bbuf, path, 24);
		sprintf(dbuf, FMSTR_LOADPTT, path);
		objc_draw(proctree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);
		read_patch(PATCH_FILE, path, list->index /* Patch No */);
		objc_draw(tree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);
	}


	objc_outline(tree, curr, NIL);
}

/*
 *	Read a patch/performance file into memory
 */

	WORD
read_patch(file_flag, fname, patch_no)
	WORD file_flag;	/* tells us if we want a patch or performance file */
	BYTE *fname;	/* file name */
	WORD patch_no;	/* first patch slot to load into */
{
	BYTE		buf[256];
	REG BYTE	*s, *t;
	MFILE 		*fp;
	WORD		found = FALSE;
	BYTE		str[64];
	WORD		n;
	WORD		ret;

	if (dos_fsize(fname) < 0L) {	/* check for existance */
		form_alert(FRM_ALERT, FMSTR_FILENOTEXIST);
		return FAILURE;
	}

	dos_read(fname);	/* read the guy into memory */		

	ref_cnt = 0;		/* set symbol reference count to zero */

	/*
	 * do file operations on it
	 */
	if ((fp = mfopen(filebuf, "ra")) == NULL) {
		form_alert(FRM_ALERT, FMSTR_FILEOPENERROR);
		return -1;
	}
	/*
	 * check first line for magic word "ATPATCH"
	 */
	if (mfgets(buf, 256, fp) == NULL) {
		form_alert(FRM_ALERT, FMSTR_INCORRECTSIZE);
		mfclose(fp);
		return -1;
	}
	switch (file_flag) {
	case PATCH_FILE:
		if (strstr(buf, "ATPATCH") == NULL) {
			form_alert(FRM_ALERT, "[Not a valid JAZ patch.][ OK ]");
			mfclose(fp);
			return -1;
		}
		break;
	case PERF_FILE:
		if (strstr(buf, "ATPERF") == NULL) {
			form_alert(FRM_ALERT, "[Not a valid JAZ|performance file.][ OK ]");
			mfclose(fp);
			return -1;
		}
		break;
	}
	/*
	 * Read one line at a time and look for beginning of a section
	 */
	while (mfgets(buf, 256, fp) != NULL) {
		s = buf;
		for (; *s && (*s == ' ') && (*s == '\t'); )
			s++;
		if (!*s)
			continue;

		if (*s == ';')
			continue;
		for (t = s; *t ; t++) {
			if (*t == ';') {
				*t = '\0';
				break;
			}
		}
		if ((n = sscanf(s, "section set %s", &str)) == 1) {
			s = str;

			/*
			 * Section found
			 */

			while (s != NULL) {
				if ((file_flag == PERF_FILE) &&
				(strcmp(s, "PATCHMAP") == 0))
					s = section_patchmap(fp);
				if (strcmp(s, "MDTAB") == 0)
					s = section_mdtab(fp);
				if (strcmp(s, "PATCH") == 0) 
					s = section_patch(fp, patch_no);
				if (strcmp(s, "SAMPLE") == 0)
					s = section_sample(fp);
				if (strcmp(s, "ENVELOPE") == 0)
					s = section_envelope(fp);
				if (strcmp(s, "WAVEFORM") == 0)
					s = section_waveform(fp);
				found = TRUE;
			}
		}

	}
	if (!found) {
		form_alert(FRM_ALERT, FMSTR_NOSECTION);
		ret = FAILURE;
	} else { 
		update_samplenameslist();

/* ???? */
		mk_patches_active();	/* only when perfomance file or always???*/
		PttTable2PitchPatch();
		update_patchnameslist();
/* ???? */

		ret = SUCCESS;	
	}


	mfclose(fp);
	return ret;
}
/*
 *	Patchmap section
 *	CAUTION: this is a little bit different. We want to keep the same 
 *	format as in the PMAP file but also want it to allow to go through
 *	the assembler. That's why every Patchmap line begins here with a  ';:'
 *	so that the assembler will think it's a comment. But we parse it when
 *	there is a colon after the semicolon.
 */

	BYTE
*section_patchmap(fp)
	MFILE *fp;
{
	BYTE		buf[256];
	REG WORD	i = 0;
	WORD		n;
	REG BYTE	*s;
	BYTE		*sect;
	BYTE		str[64];
	WORD		ch, patch, note_b, note_e, offset, pri, vol;
	LONG		tune;
	LISTINFO *list = jaztree[JAZ_PTTBOX].ob_spec.list;

	sect = NULL;
	while (mfgets(buf, 256, fp) != NULL) {
		s = buf;
		for (; *s && (*s == ' ') && (*s == '\t'); )
			s++;
		if (!*s)
			continue;

		if ((*s == ';') && ((*(s+1) == ';') || (*(s+1) == '*')))
			continue;

		/*
		 *	is there a new section? -> then we are out of here
		 *
		 */
		if ((n = sscanf(s, "section set %s", &str)) == 1) {
			sect = str;
			break;
		}

		n = sscanf(s, ";: %d %d %d - %d %d %d %d $%lx", &ch, &patch,
			&note_b, &note_e, &offset, &pri, &vol, &tune);
		if (n == 8) {
			ptt_table[i].act      = 0;
			ptt_table[i].channel  = ch + 1;
			ptt_table[i].patch    = patch + 1;
			ptt_table[i].start    = note_b;
			ptt_table[i].end      = note_e;
			ptt_table[i].offset   = offset;
			ptt_table[i].priority = pri;
			ptt_table[i].volume   = vol;
			ptt_table[i].tuning   = tune;

			i++;
		}

	}
	list->maxitems = ptt_count = i;
	PttTable2PitchPatch();
	PttTable2Strings(-1);
	return sect;
}


/*
 *	Sample Section, load samples
 */

	BYTE
*section_sample(fp)
	MFILE *fp;
{
	BYTE		buf[256];
	WORD		more = FALSE;
	REG WORD	i = 0;
	REG BYTE	*s, *t;
	BYTE		fname[256];
	WORD		n;
	WORD		ix;
	LONG		size;
	BYTE 		*sect = NULL;
	ULONG		addr;
	MLOCAL BYTE	str[64];
	BYTE		labelname[64], *label;


	/*
	 * parse one line at a time
	 */
	label = mk_noname();
	while (mfgets(buf, 256, fp) != NULL) {
		s = buf;
		/*
		 * get rid of leading spaces & comments
		 */
		for (; *s && (*s == ' ') && (*s == '\t'); )
			s++;
		if (!*s)
			continue;

		if (*s == ';')
			continue;
		for (t = s; *t ; t++) {
			if (*t == ';') {
				*t = '\0';
				break;
			}
		}
		/*
		 *	is there a new section? -> then we are out of here
		 *
		 */
		if ((n = sscanf(s, "section set %s", &str)) == 1) {
			sect = str;
			break;
		}

		/*
		 * check for sample label name
		 */
		if ((n = sscanf(s, "%s:", &str)) == 1) {
			WORD l;
			/* really a label??? */
			if (str[l = strlen(str) - 1] == ':') {
				strcpy(labelname, str);
				labelname[l] = '\0'; /* kill ':' */
				s = strchr(s, ':');	/* go behind ':' */
				s++;
				label = labelname;
			}
		}	
		/*
		 * check for include statement
		 */
		if (((n = sscanf(s, ".incbin %s", &fname)) == 1) ||
		(n = sscanf(s, "incbin %s", &fname)) == 1) {
			string2fname(fname);	/* get rid of " */

			if ((addr = (ULONG) find_sampleByName(fname)) != 0L) {
				strncpy(sym_ref[ref_cnt].name, label, 32);
				sym_ref[ref_cnt].addr = (ULONG) addr;
				ref_cnt++;
				/*
				 * if label name not equal to sample table
				 * name -> should we include this new label
			 	 * name in the list as well????
				 */
				continue;
			}
			if ((ix = find_sampleSlot()) == -1) {
				form_alert(FRM_ALERT, FMSTR_NOSAMPLESLOT);
				break;	
			}
			if ((size = dos_fsize(fname)) <= 0L) {
				if (size < 0)
				 form_alert(FRM_ALERT, FMSTR_FILENOTEXIST);
				else
				 form_alert(FRM_ALERT, FMSTR_INCORRECTSIZE);
				break;					
			}
			if ((sample_table[ix].addr = alloc(size)) == NULL) {
				form_alert(FRM_ALERT, FMSTR_OUTOFMEMORY);
				break;					
			}
			dos_bufread(fname, sample_table[ix].addr);
			sample_table[ix].size = size;

			/* LATER: check for aiff & avr () !!!! */

			if ((strstr(fname, ".cmp") != NULL) || 
			(strstr(fname, ".cpx") != NULL)) {
				sample_table[ix].type = SMPTYPE_COMPRESSED;
			} else {
				sample_table[ix].type = SMPTYPE_RAW;
			}
			/*
			 *	put it into sample table
			 */
			sample_table[ix].type |= NAME_FLAG;
			strncpy(sample_table[ix].filename, fname, 256);
			strncpy(sample_table[ix].name, label, 32);
			strncpy(sym_ref[ref_cnt].name, label, 32);
			sym_ref[ref_cnt].addr = (ULONG) sample_table[ix].addr;
			sym_ref[ref_cnt].type = SYMTYP_SAMPLE;
			sym_ref[ref_cnt].sym_spec.smp_p = &sample_table[ix];
			sym_ref[ref_cnt].sym_ix = ix;

			ref_cnt++;
		}	
	}
	return sect;
}

/*
 *	section for Synth Module Tables (MDTABS)
 */

	BYTE
*section_mdtab(fp)
	MFILE *fp;
{
	BYTE		buf[256];
	WORD		more = FALSE;
	REG WORD	i = 0, k = -1;
	REG BYTE	*s, *t;
	WORD		ix;
	WORD		n;
	LONG		lvalue;
	BYTE 		*sect = NULL;
	MLOCAL BYTE	str[64];
	BYTE		labelname[64], *label;
	WORD		no_slot, delay_flag, reverb_flag;
	WORD		last_value;
	WORD		u;
	ULONG		*rev_p;

	/*
	 * parse one line at a time
	 */
	label   = mk_noname();
	no_slot = TRUE;
	reverb_flag = FALSE;
	rev_p = (ULONG*) _mdtab_reverb;

	last_value = FALSE;
	while (mfgets(buf, 256, fp) != NULL) {
		s = buf;
		u = FALSE;
		/*
		 * get rid of leading spaces & comments
		 */
		for (; *s && (*s == ' ') && (*s == '\t'); )
			s++;
		if (!*s)
			continue;

		if (*s == ';')
			continue;
		for (t = s; *t ; t++) {
			if (*t == ';') {
				*t = '\0';
				break;
			}
		}
		/*
		 *	is there a new section? -> then we are out of here
		 *
		 */
		if ((n = sscanf(s, "section set %s", &str)) == 1) {
			sect = str;
			break;
		}

		/*
		 * check for mdtab label name
		 */
		if ((n = sscanf(s, "%s:", &str)) == 1) {
			WORD l;
			/* really a label??? */
			if (str[l = strlen(str) - 1] == ':') {
				strcpy(labelname, str);
				labelname[l] = '\0'; /* kill ':' */
				s = strchr(s, ':');	/* go behind ':' */
				s++;
				label = labelname;
				no_slot = TRUE;
			}
			if (strcmp(label, "MDTAB_REVERB") == 0)
 				reverb_flag = TRUE;
			else
 				reverb_flag = FALSE;
			if (strcmp(label, "MDTAB_END") == 0)
				continue;
			if (strcmp(label, "VT_RET_VALUE") == 0)
				continue;
			if (strcmp(label, "vt_ret_value") == 0)
				continue;

		}	
		
		/*
		 *	Get data (dc.l statements)
		 *	these should all be symbols of Synth Modules (like OSC)
		 */
		if (((n = sscanf(s, "dc.l %s", &str)) == 1) ||
		  ((n = sscanf(s, ".dc.l %s", &str)) == 1)) {
			if ((lvalue = symbol_lookup(str, ix, -1)) == (ULONG) NULL) {
				BYTE		sbuf[128];

				_xvalue = (ULONG *) NULL;
				sprintf(sbuf, "sl .__xvalue %s;g\n", str);
				xbios(11, 5, 0xf100, sbuf);
				if (_xvalue == NULL) {
					form_alert(FRM_ALERT, FMSTR_UNDEFSYMBOL);
					break;				
				} else
					lvalue = (ULONG) _xvalue;
			}
			if (lvalue != (ULONG) _vt_return_loc) { /* ignore empty ones */
				u = TRUE;
				i++;
			}
			/* error checking ?? */
		}
		if (u) {
			if (reverb_flag) {
				if (lvalue == _delay)
					*rev_p++ = _delay;
				else if (lvalue == _reverb)
					*rev_p++ = _reverb;	
				*rev_p = (ULONG) _vt_return_loc;
			} else {
				if (no_slot) {
					if ((ix = find_mdtabSlot()) == -1) {
						form_alert(FRM_ALERT, FMSTR_NOMDTABSLOT);
						break;	
					}
					mdtab_table[ix].type = 0;
					mdtab_table[ix].dsp_addr = NULL;
					mdtab_table[ix].used[0] = 0L;
					mdtab_table[ix].used[1] = 0L;
					mdtab_table[ix].used[2] = 0L;
					mdtab_table[ix].used[3] = 0L;
					strncpy(mdtab_table[ix].name, label, 32);
					strncpy(sym_ref[ref_cnt].name, label, 32);
					sym_ref[ref_cnt].addr = (ULONG) &mdtab_table[ix].modules;
					sym_ref[ref_cnt].type = SYMTYP_MDTAB;
					sym_ref[ref_cnt].sym_spec.mdtab_p = &mdtab_table[ix];
					ref_cnt++;
					no_slot = FALSE;			
					k = 0;
				} 

				mdtab_table[ix].modules[k++] = (LONG*) lvalue;
				if (lvalue == (LONG) _attn) {
					no_slot = TRUE;
					last_value = TRUE;
					mdtab_table[ix].n_entries = k;
				}
			}		
		}
	}
	return sect;
}

	BYTE
*section_waveform(fp)
	MFILE *fp;
{

}

/*
 *	Envelope Section
 */


	BYTE
*section_envelope(fp)
	MFILE *fp;
{
	BYTE		buf[256];
	WORD		more = FALSE;
	REG WORD	i = 0, k = -1;
	REG BYTE	*s, *t;
	WORD		ix;
	WORD		n;
	LONG		lvalue;
	BYTE 		*sect = NULL;
	MLOCAL BYTE	str[64];
	BYTE		labelname[64], *label;
	WORD		no_slot;
	WORD		last_value;
	WORD		u;
	/*
	 * parse one line at a time
	 */
	label   = mk_noname();
	no_slot = TRUE;
	last_value = FALSE;
	while (mfgets(buf, 256, fp) != NULL) {
		s = buf;
		u = FALSE;
		/*
		 * get rid of leading spaces & comments
		 */
		for (; *s && (*s == ' ') && (*s == '\t'); )
			s++;
		if (!*s)
			continue;

		if ((*s == ';') && (*(s+1) != ':'))
			continue;
		for (t = s; *t ; t++) {
			if ((*t == ';') && (*(t+1) != ':')) {
				*t = '\0';
				break;
			}
		}
		/*
		 * Start and release value are in special comments (';:')
		 */
		if ((*s == ';') && (*(s+1) == ':'))
			s+=2;
		/*
		 *	is there a new section? -> then we are out of here
		 *
		 */
		if ((n = sscanf(s, "section set %s", &str)) == 1) {
			sect = str;
			break;
		}

		/*
		 * check for envelope label name
		 */
		if ((n = sscanf(s, "%s:", &str)) == 1) {
			WORD l;
			/* really a label??? */
			if (str[l = strlen(str) - 1] == ':') {
				strcpy(labelname, str);
				labelname[l] = '\0'; /* kill ':' */
				s = strchr(s, ':');	/* go behind ':' */
				s++;
				label = labelname;
				no_slot = TRUE;
			}
		}	
		/*
		 * check for long values 
		 */
		if ((n = sscanf(s, "dc.l $%lx", &lvalue)) == 1) {
			u = TRUE;
			i++;
		} else if ((n = sscanf(s, ".dc.l $%lx", &lvalue)) == 1) {
			u = TRUE;
			i++;
		} else if ((n = sscanf(s, "dc.l %ld", &lvalue)) == 1) {
			u = TRUE;
			i++;
		} else if ((n = sscanf(s, ".dc.l %ld", &lvalue)) == 1) {
			u = TRUE;
			i++;
		} 
		/*
		 * LATER: check also for include/incbin statements!!!!
		 * (well, maybe...)
 		 */

		if (u) {
			if (no_slot) {
				if ((ix = find_envSlot()) == -1) {
					form_alert(FRM_ALERT, FMSTR_NOENVSLOT);
					break;	
				}
				env_table[ix].type = 0;
				env_table[ix].dsp_addr = NULL;
				env_table[ix].used[0] = 0L;
				env_table[ix].used[1] = 0L;
				env_table[ix].used[2] = 0L;
				env_table[ix].used[3] = 0L;
				strncpy(env_table[ix].name, label, 32);
				strncpy(sym_ref[ref_cnt].name, label, 32);
				sym_ref[ref_cnt].addr = (ULONG) &env_table[ix].data;
				sym_ref[ref_cnt].type = SYMTYP_ENVELOPE;
				sym_ref[ref_cnt].sym_spec.env_p = &env_table[ix];
				ref_cnt++;
				no_slot = FALSE;

				k = -1;
			}
			if (last_value) {
				no_slot = TRUE;
				env_table[ix].release_slope = lvalue;
				last_value = FALSE;
			} else if (k == -1) {
				env_table[ix].start_value = lvalue;
				k++;
			} else {
				if (lvalue >= 0x40000000L) {
					last_value = TRUE;
					env_table[ix].n_entries = k + 1;
				}		
				env_table[ix].data[k++] = lvalue;
			}		
		}
	}
	return sect;

}

/*
 *	Patch section (HAS TO BE LAST!!!!!!)
 */

	BYTE
*section_patch(fp, patch_no)
	MFILE *fp;
	WORD patch_no;
{
	BYTE		buf[256];
	WORD		u;
	WORD		more = FALSE;
	REG WORD	i = 0;
	REG BYTE	*s, *t;
	REG ULONG	*p;
	ULONG		patbuf[PATCH_SIZE];
	ULONG		lvalue;
	WORD		n;
	WORD		ix;
	BYTE 		*sect = NULL;
	MLOCAL BYTE	str[64];
	BYTE		labelname[64], *lnp;


	strcpy(labelname, mk_noname());
	p = patbuf;
	ix = patch_no;

	patchinfo_temp.active = FALSE;
	patchinfo_temp.env_cnt = 0;
	patchinfo_temp.smp_cnt = 0;
	patchinfo_temp.mdtab = NULL;
	/*
	 * parse one line at a time
	 */
	while (mfgets(buf, 256, fp) != NULL) {
		u = FALSE;
		s = buf;
		
		/*
		 * get rid of leading spaces & comments
		 */
		for (; *s && (*s == ' ') && (*s == '\t'); )
			s++;
		if (!*s)
			continue;

		if (*s == ';')
			continue;
		for (t = s; *t ; t++) {
			if (*t == ';') {
				*t = '\0';
				break;
			}
		}
		/*
		 *	is there a new section? -> then we are out of here
		 *
		 *	??? OR ERROR BECAUSE THIS SHOULD BE THE LAST SECTION ??
		 */
		if ((n = sscanf(s, "section set %s", &str)) == 1) {
			sect = str;
			form_alert(FRM_ALERT, FMSTR_NOTLASTSECT);
			break;
		}

		/*
		 * check for patch label name
		 */
		if ((n = sscanf(s, "%s:", &str)) == 1) {
			WORD l;

			if (str[l = strlen(str) - 1] == ':') {
				if (i != 0) /* wrong size */
					form_alert(FRM_ALERT, FMSTR_SHORTPATCH);
				strcpy(labelname, str);
				labelname[l] = '\0'; /* kill ':' */
				if (strcmp(labelname, "patches") == 0)
					continue;
				s = strchr(s, ':');	/* go behind ':' */
				s++;
			}
		}	
		/*
		 * check for long values 
		 */
		if ((n = sscanf(s, "dc.l $%lx", &lvalue)) == 1) {
			u = TRUE;
			i++;
		} else if ((n = sscanf(s, ".dc.l $%lx", &lvalue)) == 1) {
			u = TRUE;
			i++;
		} else if ((n = sscanf(s, "dc.l %ld", &lvalue)) == 1) {
			u = TRUE;
			i++;
		} else if ((n = sscanf(s, ".dc.l %ld", &lvalue)) == 1) {
			u = TRUE;
			i++;
		} 
		/*
		 *	check for labels
		 */
		else if (((n = sscanf(s, "dc.l %s", &str)) == 1) ||
		  ((n = sscanf(s, ".dc.l %s", &str)) == 1)) {
			if ((lvalue = symbol_lookup(str, ix, i)) == (ULONG) NULL) {
				BYTE		sbuf[128];

				_xvalue = (ULONG *) NULL;
				sprintf(sbuf, "sl .__xvalue %s;g\n", str);
				xbios(11, 5, 0xf100, sbuf);
				if (_xvalue == NULL) {
					form_alert(FRM_ALERT, FMSTR_UNDEFSYMBOL);
					break;				
				} else
					lvalue = (ULONG) _xvalue;
			}
			u = TRUE;
			i++;
			/* error checking ??? */
		}
		/*
		 * we got a value to put into the buffer
		 */
		if (u) {
			/*
			 * if buffer full, do we want more than one patch
			 */
			if (!more && (i > PATCH_SIZE)) {
				if (form_alert(FRM_ALERT, FMSTR_MULTPATCHES)==2) {
					break;
				} else 
					more = TRUE;
			}
			/*
			 * store value
			 */
			*p++ = lvalue;
	
			/*
			 * if buffer is full, then store it
			 */
			if ((i % PATCH_SIZE) == 0) {
				memcpy((BYTE*)&patchinfo_table[ix], 
					(BYTE*)&patchinfo_temp,
					(LONG) sizeof(PATCHINFO));
				patchinfo_table[ix].active = FALSE;	
				store_patch(patbuf, ix++, labelname);
				strcpy(labelname, mk_noname());
				i = 0;
				p = patbuf;
				patchinfo_temp.env_cnt = 0; /* next guy */
				patchinfo_temp.smp_cnt = 0;
				patchinfo_temp.mdtab = NULL;
				patchinfo_temp.active = FALSE;
				patch_cnt++;
			}
		}	
	}
	if ((i % PATCH_SIZE) != 0)
		form_alert(FRM_ALERT, FMSTR_SHORTPATCH);

	return sect;
}

/*
 *	Look for a symbol in the symbol refs list
 */


	ULONG
symbol_lookup(s, patch_no, offset)
	BYTE *s;
	WORD patch_no;
	WORD offset;
{
	/*
	 * first, check ROM symbols
	 */
	{
		REG SYMTAB *t = rom_symbols;
	
		for (; t->addr != 0L; t++) {
			if (strcmp(s, t->name) == 0) {
				REG PATCHINFO *p;
				WORD d, r;

				if (offset == -1)
					return (ULONG) t->addr;

				d = patch_no / 32;
				r = patch_no % 32;
				t->sym_spec.smp_p->used[d] |=
						(ULONG) (1L << r);
				p = &patchinfo_temp;
				p->smp_offset[p->smp_cnt] = 
					offset;
				p->smplist_ix[p->smp_cnt] = 
					t->sym_ix;
				p->sample[p->smp_cnt++] = 
				(SMPTAB*) t->sym_spec.smp_p;
				return t->addr;
			}
		}
	}
	/*
	 * next, check for RAM symbols
	 */
	{
		REG SYMTAB *t = sym_ref;
		REG WORD i;
		WORD d, r;

		for (i = 0; i < ref_cnt; i++, t++) {
			if (t->addr != 0L)
				if (strcmp(s, t->name) == 0) {
					/*
					 * we want to know by which 
					 * patch the symbol data was used
					 * so we store the patch number in
					 * the bitfield of th appropriate
					 * table (envs/samples/waveforms)
					 */
					REG PATCHINFO *p;

					if (offset == -1)
						return (ULONG) t->addr;

					p = &patchinfo_temp;
					d = patch_no / 32;
					r = patch_no % 32;
					switch (t->type) {
					case SYMTYP_ENVELOPE:
					    t->sym_spec.env_p->used[d] |=
						(ULONG) (1L << r);
						p->env_offset[p->env_cnt] = 
							offset;
						p->env[p->env_cnt++] = 
							(ENVTAB *) 
							t->sym_spec.env_p;
						break;
					case SYMTYP_SAMPLE:
						t->sym_spec.smp_p->used[d] |=
							(ULONG) (1L << r);
						p->smp_offset[p->smp_cnt] = 
							offset;
						p->smplist_ix[p->smp_cnt] = 
							t->sym_ix;
						p->sample[p->smp_cnt++] = 
							(SMPTAB*)
							t->sym_spec.smp_p;
						break;
					case SYMTYP_MDTAB:
						 t->sym_spec.mdtab_p->used[d] |=
							(ULONG) (1L << r);
						p->mdtab =
							(MDTAB*) 
							t->sym_spec.mdtab_p;
						break;
					}
					return (ULONG) t->addr;
				}
		}
	}


	return (ULONG) NULL;
}

/*
 *	Store a loaded patch (copy from buffer into patch table)
 */

	VOID
store_patch(bufp, which, labelname)
	REG ULONG *bufp;
	WORD	 which;
	BYTE	 *labelname;
{
	REG LONG *p;
	REG WORD i;

	p = &patchtab[which][0]; 

	for (i = 0; i < PATCH_SIZE; i++)
		*p++ = *bufp++;
	label2name(labelname);
	strcpy(patch_names[which], labelname);
}


/*
 *	View/Edit patch handling (Call back)
 */
	VOID
pstab_pview(tree, curr, found, n, k)
	OBJECT *tree;
	WORD	curr;
	WORD	found;
	WORD	n;
	ULONG	k;
{
	LISTINFO *list = pstabtree[PSTAB_PLIST].ob_spec.list;
	WORD 	    pchannel;
	EXTERN WORD channel;
	BYTE 	    temp[255];

	if (list->index >= patch_cnt)
		return;
	curr_pnr = list->index;
	curr_patch = &patchtab[list->index][0];
	setup_alg_values(list->index);

	pchannel = get_channel(curr_pnr);
	if (!patchinfo_table[curr_pnr].active || (pchannel < 0))
		form_alert(FRM_ALERT,"[This patch isn't mapped|to a MIDI channel.|Therfore is's not active.][ Cancel ]");
	else {	 
#if 0 
		if (pchannel != channel) {
			sprintf(temp, "[This patch is mapped to|channel #%d. Please|adjust your keyboard.][ OK ]", pchannel+1);
			form_alert(FRM_ALERT, temp);
		}
#endif		
		objc_draw(algtree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);
		form_xdo(algtree, 2);
		objc_draw(tree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);
		objc_outline(tree, curr, NIL);	
	}
}

	WORD 
get_channel(WORD p)
{
	REG WORD i, j;
	
	for (i = 0; i < 16; i++)
		for (j = 0; j < pitchpatch[i].n; j++)
			if ((pitchpatch[i].patch[j] == p) &&
			 (pitchpatch[i].cover[j] == FALSE))
				return i;

	return -1;
}


/*
 *	find a place in the sample table
 */

	WORD
find_sampleSlot()
{
	REG WORD i;
	for (i = 0; i < smpslot_cnt; i++) {
		if (sample_table[i].free) {
			sample_table[i].free = FALSE;
			return i;
		}
	}
	if (smpslot_cnt >= NR_SAMPLES)
		return -1;
	else {
		sample_table[smpslot_cnt].free = FALSE;
		return smpslot_cnt++;
	}
}

	VOID
free_sampleSlot(slot)
	WORD slot;
{
	sample_table[slot].free = TRUE;
	sample_table[slot].used[0] = 0L;
	sample_table[slot].used[1] = 0L;
	sample_table[slot].used[2] = 0L;
	sample_table[slot].used[3] = 0L;

	free(sample_table[slot].addr);	/* free sample memory */
	sample_table[slot].size = 0L;

	if (slot == (smpslot_cnt - 1)) {
		if (smpslot_cnt > 0)
			smpslot_cnt--;
	}
}
	VOID
free_allSamples(VOID)
{
	REG WORD i;
	for (i = 0; i < smpslot_cnt; i++)
		if (!sample_table[i].free)
			free_sampleSlot(i);
}

/*
 *	find a place in the env table
 */

	WORD
find_envSlot()
{
	REG WORD i;

	n_envtab++;
	for (i = 0; i < envslot_cnt; i++) {
		if (env_table[i].free) {
			env_table[i].free = FALSE;
			return i;
		}
	}
	if (envslot_cnt >= N_ENVS)
		return -1;
	else {
		env_table[envslot_cnt].free = FALSE;
		return envslot_cnt++;
	}
}

	VOID
free_envSlot(slot)
	WORD slot;
{
	n_envtab--;
	env_table[slot].free = TRUE;
	env_table[slot].used[0] = 0L;
	env_table[slot].used[1] = 0L;
	env_table[slot].used[2] = 0L;
	env_table[slot].used[3] = 0L;

	if (slot == (envslot_cnt - 1)) {
		if (envslot_cnt > 0)
			envslot_cnt--;
	}
}

/*
 *	find a place in the module table (MDTAB)
 */

	WORD
find_mdtabSlot()
{
	REG WORD i;
	for (i = 0; i < mdtabslot_cnt; i++) {
		if (mdtab_table[i].free) {
			mdtab_table[i].free = FALSE;
			return i;
		}
	}
	if (mdtabslot_cnt >= N_MDTABS)
		return -1;
	else {
		mdtab_table[mdtabslot_cnt].free = FALSE;
		return mdtabslot_cnt++;
	}
}

	VOID
free_mdtabSlot(slot)
	WORD slot;
{

	mdtab_table[slot].free = TRUE;

	if (slot == (mdtabslot_cnt - 1)) {
		if (mdtabslot_cnt > 0)
			mdtabslot_cnt--;
	}
}


/*
 *	find a sample by its name in the sample table
 */

	BYTE
*find_sampleByName(fname)
	BYTE	*fname;
{
	REG WORD i;

	for (i = 0; i < smpslot_cnt; i++) {
		if (strcmp(fname, sample_table[i].name) == 0)
			return sample_table[i].addr;
	}

	return NULL;
}

/*
 *	Convert a string into a file name
 *	(get rid of " ' \\
 */

	BYTE
*string2fname(fname)
	BYTE *fname;
{
	REG BYTE *s = fname, *t;

	if (*s == '\"' || *s == '\'') {		
		for (s++, t = fname; *s ; ) {
			if (*s == '\"' || *s == '\'') 
				*s = '\0';
			if ((*s == '\\') && (*(s + 1) == '\\'))
				s++;
			*t++ = *s++;
		}
		*t = '\0';
	}
	return fname;
}

/*
 *	Convert a label name to a list name (upper case, '_' --> ' ')
 */
	VOID
label2name(s)
	REG BYTE *s;
{
	for (; *s; s++) {
		if (*s == '_')
			*s = ' ';
		*s = ((*s >= 97) && (*s <= 122)) ? _toupper(*s) : *s;
	}
}

/*
 *	Update sample names list
 */
	VOID
update_samplenameslist()
{
	REG WORD i, j;
	REG BYTE *s, *t;


	for (i = 0, j = 0; i < smpslot_cnt; i++) {
		samplenameslist[j++] = nrstr[i];
		if (sample_table[i].type & NAME_FLAG) {
			label2name(sample_table[i].name);
			sample_table[i].type &= ~NAME_FLAG;
		}
		samplenameslist[j++] = sample_table[i].name;
		samplenameslist[j++] = sampletypes[sample_table[i].type&0x7fff];
	}	
	for (; i < NR_SAMPLES; i++) {
		samplenameslist[j++] = nrstr[i];
		samplenameslist[j++] = "UNDEFINED";
		samplenameslist[j++] = "    ";
	}	
}


	VOID
update_patchnameslist()
{
	REG WORD i, j;
	REG BYTE *s, *t;
	ULONG osc;

	for (i = 0, j = 0; i < patch_cnt; i++) {
		patchnameslist[j++]  = nrstr[i];
		patchnameslist[j++] = patch_names[i];
		osc = (ULONG) patchinfo_table[i].mdtab->modules[0];
		if (osc == _osc_sampler)
			patchnameslist[j++] = osc_types[0];
		else if (osc == _osc_nisampler)
			patchnameslist[j++] = osc_types[1];
		else if (osc == _osc_fixedwave)
			patchnameslist[j++] = osc_types[4];
		else if (osc == _osc_fm)
			patchnameslist[j++] = osc_types[5];
		else if (osc == _osc_polywave)
			patchnameslist[j++] = osc_types[6];
		else 
			patchnameslist[j++] = "NO TYPE";

		if (patchinfo_table[i].active)
			patchnameslist[j++] = active_str;
		else
			patchnameslist[j++] = non_active_str;
	}	
	for (; i < NR_PATCHES; i++) {
		patchnameslist[j++] = nrstr[i];
		patchnameslist[j++] = "UNDEFINED";
		patchnameslist[j++] = "    ";
		patchnameslist[j++] = "    ";
	}	
}


/*
 *	mk_patches_active()
 *	Go through the patch translation table and see if all patches in there
 *	are active (appropriate data in DSP RAM).
 */
	VOID
mk_patches_active()
{
	REG WORD i;

	for (i = 0; i < ptt_count; i++) {
		if (patchinfo_table[ptt_table[i].patch-1].active == FALSE) {
			WORD patch_no = ptt_table[i].patch - 1;
			REG PATCHINFO *p = &patchinfo_table[patch_no];

			if (activate_mdtab(p->mdtab, patch_no) == FAILURE)
				continue;
			if (activate_env(p->env, patch_no) == FAILURE)
				continue;
			patchinfo_table[patch_no].active = TRUE;
		}
	}
}

/*
 *	activate_mdtab()
 */




	WORD
activate_mdtab(mdtab_p, patch_no)
	MDTAB *mdtab_p;
	WORD  patch_no;
{
	if (mdtab_p->dsp_addr == NULL) {		
		if ((mdtab_p->dsp_addr = (ULONG*) allocMDTAB()) == NULL) {
			form_alert(FRM_ALERT, FMSTR_NODSPMDTABSPACE);
			return FAILURE;
		}
		mdtab2dsp(mdtab_p);
	}
	if (patch_no >= 0) {
		patchtab[patch_no][0] = (LONG) mdtab_p->dsp_addr;
	}
	return SUCCESS;
}


	LONG *
allocMDTAB()
{
	REG LONG *p = _mdtab_start;
	REG WORD i;

	for (i = 0; i < N_DSP_MDTABS; i++) {
		if (dsp_mdtabslots[i] == 0) {
			dsp_mdtabslots[i] = 1;
			return p;
		}
		p += N_MDMODULES;
	}
	return NULL;
}

	VOID
freeMDTAB(addr)
	LONG *addr;
{
	REG LONG *p = _mdtab_start;
	REG WORD i;

	for (i = 0; i < N_DSP_MDTABS; i++) {
		if (p == addr) {
			dsp_mdtabslots[i] = 0;
			break;
		}
		p += N_MDMODULES;
	}
}


	VOID
mdtab2dsp(mdtab_p)
	MDTAB *mdtab_p;
{
	REG LONG *p, *q;
	REG WORD i, l;

	p = (LONG*)mdtab_p->modules;
	q = (LONG*)mdtab_p->dsp_addr;
	l = mdtab_p->n_entries;

	for (i = 0; i < l; i++)
		*q++ = *p++;

}


/*
 *	activate_env()
 *	Makes sure that the envelope is in DSP RAM
 */

	WORD
activate_env(envtab_p, patch_no)
	REG ENVTAB *envtab_p[];
	WORD  patch_no;
{
	PATCHINFO *p = &patchinfo_table[patch_no];
	REG WORD i;	
	for (i = 0; i < p->env_cnt; i++) {
		if (envtab_p[i]->dsp_addr == NULL) {
			if ((envtab_p[i]->dsp_addr = 
			allocENV(envtab_p[i]->n_entries, (WORD) /* xx */
			((envtab_p[i] - env_table)))) == NULL) {
				form_alert(FRM_ALERT, FMSTR_NODSPENVSPACE);
				return FAILURE;
			}
			/*
			 * xx = this casting already includes a divide by
			 *      sizeof(ENVTAB), so we automatically have the
			 *	array index of this pointer/field.
			 */

			env2dsp(envtab_p[i]);
		}
		if (patch_no >= 0) {
			/*
			 * only those places in the patch which point to this
			 * envelope data
			 */
			if (patchtab[patch_no][p->env_offset[i]] == 
			(LONG) envtab_p[i]->data)
				patchtab[patch_no][p->env_offset[i]] = 
					(LONG) envtab_p[i]->dsp_addr;
		}
		
	}
	return SUCCESS;
}

/*
 *	Alloc space for an envelope in DSP RAM
 */


	LONG *
allocENV(n, ix)
	WORD n;		/* size in longs */
	WORD ix;	/* index of envtab */
{
	REG LONG *p;
	REG WORD i;
	LONG size;

	if (dsp_envslot_cnt >= N_DSP_ENVS) {
		return NULL;
	}
	size = (LONG) n;
	if (dsp_envslot_cnt == 0) {
		p = _env_start;
		dsp_envslots[0].used = 1;
		dsp_envslots[0].start = p;
		dsp_envslots[0].size = size;
		dsp_envslots[0].env_ix = ix;
		dsp_envslot_cnt++;
		return p;
	} else {
		dsp_envslots[dsp_envslot_cnt].used = 1;

		p = dsp_envslots[dsp_envslot_cnt - 1].start + 
			dsp_envslots[dsp_envslot_cnt - 1].size;
		if ((LONG*)(p + size) > (LONG*)_env_end)
			return NULL;
		dsp_envslots[dsp_envslot_cnt].size  = size;
		dsp_envslots[dsp_envslot_cnt].start = p;
		dsp_envslots[dsp_envslot_cnt].env_ix = ix;
		dsp_envslot_cnt++;

		return p;
	}
}

/*
 *	Free an envelope in DSP RAM
 */
	VOID
freeENV(dsp_p)
	LONG *dsp_p;
{
	REG LONG *free, *next;
	REG LONG *old, *new;
	REG WORD m, l, k;
	REG WORD i, j, n;
	WORD nn, no, o;
	LONG size;

	/*
	 * Lot to do: 
	 * First, we find the envelope in dsp memory and use that
	 * slot structure to get its size. Then we copy all the other
	 * envelopes down and free the last slot. Next, we have to
	 * update all dsp ptrs in the envelope structures. Also, we
	 * have to update all the ACTIVE patches in patchtab.
 	 */

	for (i = 0; i < dsp_envslot_cnt; i++) {
	  if (dsp_envslots[i].used && (dsp_envslots[i].start == dsp_p)) {
	    free = dsp_p;
	    for (j = i; j < (dsp_envslot_cnt - 1); j++) {
	    	old = next = dsp_envslots[j+1].start;
			/* +++  HMK 6/14/95: 'old' was pointing to
				dsp_envslots[j].start, but that doesn't
				do us good, since we want to free that one
				AND so we have to update the 'next' guy!!!

			*/
		size = dsp_envslots[j+1].size;
		dsp_envslots[j].start;
		new = dsp_envslots[j].start = free;
		dsp_envslots[j].size = size;
		for (k = 0; k < size; k++)
			*free++ = *next++;
		for (k = 0; k < envslot_cnt; k++) {
		  if (env_table[k].dsp_addr == old) {
			env_table[k].dsp_addr = new;
			for (l = 0; l < 4; l++) {
			  for (m = 0; m < 32; m++) {
				if (((env_table[k].used[l] >> m) & 1)) {
				   no = l * 32 + m;
				   if (!patchinfo_table[no].active)
					continue;
				   nn = patchinfo_table[no].env_cnt;
				   for (n = 0; n < nn; n++) {
				      o = patchinfo_table[no].env_offset[n];
				      /*
				       * only those places where the patch
				       * points to this (old) envelope loc.
				       */
				      if (patchtab[no][o] == (LONG) old)
					      patchtab[no][o] = (LONG) new;	
				   }
				}
			  }
			}
		  }
		}
	     }
	     dsp_envslot_cnt--;
	     dsp_envslots[dsp_envslot_cnt].used = FALSE; 			
	     break; /* ther should be only ONE!!! */	
	  }
	}


}

/*
 *	Copy envelope data from Env_table into DSP RAM
 */

	VOID
env2dsp(envtab_p)
	ENVTAB *envtab_p;
{
	REG LONG *p, *q;
	REG WORD i, l;

	p = (LONG*)envtab_p->data;
	q = (LONG*)envtab_p->dsp_addr;
	l = envtab_p->n_entries;

	for (i = 0; i < l; i++)
		*q++ = *p++;

}
/*
 *	dspenv2temp()
 *
 *	Copy an envelope which is in DSP RAM into the temporary envelope
 *	buffer in DSP RAM for editing. This guy also updates all the patches
 *	pointing to this envelope. So they will point to the temporary env.
 *
 */

	WORD
dspenv2temp(dsp_p, flag)
	LONG *dsp_p;
	WORD flag;	/* flag for modifying all (-1) or if >= 0 */
			/* flag is the envelope # in the patch */
{
	REG LONG *p, *q;
	LONG *new, *old;
	REG WORD m, n, i, l;
	WORD nn, no, o, ix, used;
	ENVTAB *envtab_p;

	if (flag != -1)
		flag++;		/* we want 1,2,3 but get 0,1,2 */

	for (i = 0; i < dsp_envslot_cnt; i++) {
		if (dsp_envslots[i].start == dsp_p)
			break;
	}
	if (i == dsp_envslot_cnt)
		return FAILURE;

	used = 0;
	temp_env_new = FALSE;
	envtab_p = &env_table[ix = dsp_envslots[i].env_ix];
	for (l = 0; l < 4; l++) {
		for (n = 0; n < 32; n++)
			if ((envtab_p->used[l] >> n) & 1)
				used++;
	}
	
	if (flag == -1 || used == 1)
		envtab_p = &env_table[ix = dsp_envslots[i].env_ix];
	else {		
		if ((ix = find_envSlot()) < 0)
			return FAILURE;
		envtab_p = &env_table[ix];
		env_table[ix] = env_table[dsp_envslots[i].env_ix];
		envtab_p->used[0] = 0L;
		envtab_p->used[1] = 0L;
		envtab_p->used[2] = 0L;
		envtab_p->used[3] = 0L;

		nn = curr_pnr / 32;
		no = curr_pnr % 32;
		envtab_p->used[nn] |= (LONG) (1L << (LONG) no);
		temp_env_new = TRUE;
		
	}
	if ((p = (LONG*)envtab_p->dsp_addr) == NULL)
		return FAILURE;
	q = (LONG*)_env_temp;	
	l = envtab_p->n_entries;

	for (i = 0; i < l; i++)
		*q++ = *p++;

	old = envtab_p->dsp_addr_bak = envtab_p->dsp_addr;	
	new = envtab_p->dsp_addr = _env_temp;
	envtab_p->in_temp = TRUE;
	
	if (flag == -1) {
	  for (l = 0; l < 4; l++) {
		for (m = 0; m < 32; m++) {
			if (((envtab_p->used[l] >> m) & 1)) {
				no = l * 32 + m;
				if (!patchinfo_table[no].active)
					continue;
				nn = patchinfo_table[no].env_cnt;
				for (n = 0; n < nn; n++) {
					o = patchinfo_table[no].env_offset[n];
					if (patchtab[no][o] == (LONG) old) 
						patchtab[no][o] = (LONG) new;
				}
			}
		}
	  }
	} else {
		no = curr_pnr;
		/*
		 * change only one of the envelopes in the patch
		 */
		if (patchinfo_table[no].active) {
			o = 5 + flag * 3; /* here we are in the patch */
			i = pi_find_envtab_ix(no, flag);
			temp_env_envtab_bak = patchinfo_table[no].env[i];
			patchinfo_table[no].env[i] = envtab_p;
			patchtab[no][o] = (LONG) new;
		}
	}

	temp_env_flag = flag;
	temp_env_ix = ix;
	return SUCCESS;	
}

/*
 *	Store an envelope back from temporary DSP RAM buffer into DSP RAM.
 */
	WORD
temp2dspenv(entries, start, rel_slope)
	WORD entries;
	LONG start, rel_slope;
{
	REG LONG *p, *q;
	LONG *new, *old;
	REG WORD m, n, i, l;
	WORD nn, no, o, used;
	ENVTAB *envtab_p;

	if (temp_env_ix < 0)
		return FAILURE;

	envtab_p = &env_table[temp_env_ix];

	/*
	 * first, let's check if anything has changed at all
	 */
	p = _env_temp;
	q = envtab_p->dsp_addr_bak;
	l = entries;
	o = FALSE;
	for (i = 0; i < l; i++) {
		if (*q >= 0x40000000L) {
			if (*p < 0x40000000L) {
				o = TRUE;
			}			
			break;
		}
		if (*q++ != *p++) {
			o = TRUE;
			break;
		}				
	}
	if (!o) { /* LATER: WHAT about star & release !!! */
		return cancel_tempenv();
	}

	/*
	 *	used in more than one patch?
	 */
	used = 0;
	for (l = 0; l < 4; l++) {
		for (n = 0; n < 32; n++)
			if ((envtab_p->used[l] >> n) & 1)
				used++;
	}

	if (((temp_env_flag == -1) || (used == 1)) && !temp_env_new) {
		freeENV(envtab_p->dsp_addr_bak);
	}
	envtab_p->dsp_addr_bak = NULL;

	if ((new = allocENV(entries, temp_env_ix)) == NULL) {
		form_alert(FRM_ALERT, FMSTR_NODSPENVSPACE);
		return FAILURE;
	}
	p = _env_temp;
	q = new;
	l = entries;
	for (i = 0; i < l; i++) {
		*q++ = *p++;
	}
	envtab_p->dsp_addr      = new;
	envtab_p->n_entries     = entries;
	envtab_p->start_value   = start;
	envtab_p->release_slope = rel_slope;
	
	old = _env_temp;
	if (temp_env_flag == -1) {
	  for (l = 0; l < 4; l++) {
		for (m = 0; m < 32; m++) {
			if (((envtab_p->used[l] >> m) & 1)) {
				no = l * 32 + m;
				if (!patchinfo_table[no].active)
					continue;
				nn = patchinfo_table[no].env_cnt;
				for (n = 0; n < nn; n++) {
					o = patchinfo_table[no].env_offset[n];
					if (patchtab[no][o] == (LONG) old)
						patchtab[no][o] = (LONG) new;
				}
			}
		}
	  }
	} else {
		no = curr_pnr;
		/*
		 * change only one of the envelopes in the patch
		 */
		if (patchinfo_table[no].active) {
			o = 5 + temp_env_flag * 3; /* here we are in the patch */
			patchtab[no][o] = (LONG) new;
		}
	}
	envtab_p->in_temp = FALSE;
	temp_env_ix = -1;
	temp_env_new = FALSE;

	return SUCCESS;
}

/*
 *	Get orginal envelope back
 */
	WORD
cancel_tempenv()

{
	REG LONG *p, *q;
	LONG *new, *old;
	REG WORD m, n, i, l;
	WORD nn, no, o, used;
	ENVTAB *envtab_p;

	if (temp_env_ix < 0)
		return FAILURE;

	used = 0;
	envtab_p = &env_table[temp_env_ix];
	for (l = 0; l < 4; l++) {
		for (n = 0; n < 32; n++)
			if ((envtab_p->used[l] >> n) & 1)
				used++;
	}
	old = envtab_p->dsp_addr;	/* should be equal to _env_temp */
	new = envtab_p->dsp_addr_bak;
	envtab_p->dsp_addr_bak = NULL;
	envtab_p->dsp_addr = new;

	if (temp_env_flag == -1) {
	  for (l = 0; l < 4; l++) {
		for (m = 0; m < 32; m++) {
			if (((envtab_p->used[l] >> m) & 1)) {
				no = l * 32 + m;
				if (!patchinfo_table[no].active)
					continue;
				nn = patchinfo_table[no].env_cnt;
				for (n = 0; n < nn; n++) {
					o = patchinfo_table[no].env_offset[n];
					if (patchtab[no][o] == (LONG) old)
						patchtab[no][o] = (LONG) new;
				}
			}
		}
	  }
	} else {
		no = curr_pnr;
		/*
		 * change only one of the envelopes in the patch
		 */
		if (patchinfo_table[no].active) {
			o = 5 + temp_env_flag * 3; /* here we are in the patch */
			patchtab[no][o] = (LONG) new;
			i = pi_find_envtab_ix(no, temp_env_flag);
			patchinfo_table[no].env[i] = temp_env_envtab_bak;
		}
	}
	if ((temp_env_flag != -1) && (used == 1)) {
		free_envSlot(temp_env_ix);
	}

	envtab_p->in_temp = FALSE;
	temp_env_ix = -1;

	return SUCCESS;

}

/*
 *	Initialize DSP memory management
 */

	VOID
init_dspmem()
{
	REG WORD i;

	for (i = 0; i < N_DSP_MDTABS; i++)
		dsp_mdtabslots[i] = 0;
		
	for (i = 0; i < N_DSP_ENVS; i++)
		dsp_envslots[i].used = 0;
	dsp_envslot_cnt = 0;
	
}

WORD old_1;
WORD old_2;

dbg_envmem()
{
	BYTE	dbuf[80];

	if ((old_1 != dsp_envslot_cnt) || (old_2 != n_envtab)) {
		sprintf(dbuf, "dsp=%03d, %03d", dsp_envslot_cnt, n_envtab);

		dos_echo(dbuf);
		old_1 = dsp_envslot_cnt;
		old_2 = n_envtab;
	}
}
