/************************************************************************
 *	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 "algobj.h"
#include "ptchptch.h"
#include "dos.h"

/*
 *	EXTERNALS
 */
EXTERN WORD	_debug;		
EXTERN WORD	setup_alg_values(WORD);		/* init algorithm page values */
EXTERN WORD	smp_formats(WORD);
EXTERN LONG	midi_filter;
EXTERN WORD	gl_effect;
EXTERN WORD	gl_pitchbend;
EXTERN LONG	*_effect_mode;
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 OBJECT	rentree[];			/* rename box tree */
EXTERN OBJECT	globaltree[];			/* global settings */
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;

EXTERN BYTE	roar_cmp[];
EXTERN WORD	roar_raw[];

/*
 *	GLOBAL VARIABLES
 */

WORD	perf_file_flag;

OBJECT	procLtree[4];				/* work in progress box */
OBJECT	procStree[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 * 5];	/* for list obj */
BYTE	*samplenameslist[NR_SAMPLES * 3];	/* for list obj */

WORD	dspwave1, dspwave2;

/*
 *	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;
WORD	temp_dft_env;			/* Flag showing that we are editing */
					/* the default envelope */
LONG	*org_work_addr;

WORD	dft_env_ix;

ENVTAB	dflt_envtab = {
	"dflt_env",			/*name*/
	0,				/*free*/
	0,				/*type*/
	0,				/*in_temp*/
	{ 0L, 0L, 0L, 0L },		/*used*/
	6,				/*entries*/		
	(LONG *)0L,			/*dsp_addr_bak*/
	(LONG *)0L,			/*dsp_addr*/
	0x00010000L,			/*start*/
	0xfe000000L,			/*release*/
	{ 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
WORD	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", "DSPW1", "DSPW2" 
};

/*	----------------------------------------------------------------------
 *	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_globalvar(MFILE *);
BYTE	*section_synthcnf(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 *);
VOID	free_patchinfo(WORD);
WORD	find_sampleSlot(VOID);
VOID	free_sampleSlot(WORD);
VOID	free_envSlot(WORD);
VOID	free_allSamples(VOID);
WORD	find_mdtabSlot(VOID);
VOID	free_mdtabSlot(WORD);
WORD	find_envSlot(VOID);
BYTE	*find_sampleByName(BYTE *, WORD *);
BYTE	*string2fname(BYTE *);
VOID	update_samplenameslist(VOID);
VOID	update_patchnameslist(VOID);
VOID	label2name(BYTE *);
VOID	mk_patches_active(VOID);
VOID	mk_patches_inactive(VOID);
WORD	mk_one_patch_active(WORD);
VOID	mk_one_patch_inactive(WORD);
WORD	activate_mdtab(MDTAB *, WORD);
VOID	deactivate_mdtab(MDTAB *, WORD);
WORD	activate_env(ENVTAB **, WORD);
VOID	deactivate_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);
VOID	garbage_collection(VOID);
VOID	replace_mdtab(WORD, 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;

	disable_sound();

	dspwave1 = dspwave2 = -1;
	/*
	 * ALL are undefined!
	 */
	for (i = 0; i < NR_PATCHES; i++) {
		strcpy(patch_names[i], "UNDEFINED");
		patchinfo_table[i].active = FALSE;
		for (j = 0; j < 5; j++) {
			patchinfo_table[i].sample[j] = NULL;
			patchinfo_table[i].env[j] = NULL;
		}
	}
	/*
	 * 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++] = " ";
		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].used[0] = 0L;
		sample_table[i].used[1] = 0L;
		sample_table[i].used[2] = 0L;
		sample_table[i].used[3] = 0L;
		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;
	}
	sample_table[smpslot_cnt].addr = (BYTE*) roar_raw;
	sample_table[smpslot_cnt].headaddr = (BYTE*) roar_raw;
	sample_table[smpslot_cnt].size = 52296L;
	sample_table[smpslot_cnt].looplen = (52296L/2)-1L;
	sample_table[smpslot_cnt].loopend = (52296L/2)-1L;
	sample_table[smpslot_cnt].samplestart = 0L;
	sample_table[smpslot_cnt].sampleend = (52296L/2);
	sample_table[smpslot_cnt].looping = FALSE;
	sample_table[smpslot_cnt].free = FALSE;
	strcpy(sample_table[smpslot_cnt].name, "ROAR");
	strcpy(sample_table[smpslot_cnt].filename, "samples\\roar.raw");
	sample_table[smpslot_cnt].type = SMPTYPE_RAW;
	smpslot_cnt++;

	sample_table[smpslot_cnt].addr = (BYTE*) roar_cmp;
	sample_table[smpslot_cnt].headaddr = (BYTE*) roar_cmp;
	sample_table[smpslot_cnt].size = (52296L/2);
	sample_table[smpslot_cnt].looplen = (52296L/2)-1L;
	sample_table[smpslot_cnt].loopend = (52296L/2)-1L;
	sample_table[smpslot_cnt].samplestart = 0L;
	sample_table[smpslot_cnt].sampleend = (52296L/2)-1L;
	sample_table[smpslot_cnt].looping = FALSE;
	sample_table[smpslot_cnt].free = FALSE;
	strcpy(sample_table[smpslot_cnt].name, "ROAR");
	strcpy(sample_table[smpslot_cnt].filename, "samples\\roar.cmp");
	sample_table[smpslot_cnt].type = SMPTYPE_COMPRESSED;
	smpslot_cnt++;


	/*
	 *	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].in_temp = 0;
		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 {
		dft_env_ix = ix;
		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(procLtree, NIL, 0, 
		G_BOX, -1, -1, 
		120, 70, 150, 20,
		IS3DOBJ|NONE, NORMAL,
		0);	
		objc_xadd(procLtree, 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...",
			T_BX_FONTID, FNT_MEDIUM,
			T_BX_ALIGN, TE_CNTR,
			0);
	objc_xadd(procStree, NIL, 0, 
		G_BOX, -1, -1, 
		120, 70, 150, 20,
		IS3DOBJ|NONE, NORMAL,
		0);	
		objc_xadd(procStree, 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, "Saving...",
			T_BX_FONTID, FNT_MEDIUM,
			T_BX_ALIGN, TE_CNTR,
			0);
	enable_sound();
}


/*
 *	"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[20];
	WORD x;

	disable_sound();

	objc_outline(tree, NIL, curr);

	x = fsel_input("Patch", "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);

		objc_draw(procLtree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);
		if (list->index > patch_cnt) {
			list->index = patch_cnt;
			patchinfo_table[patch_cnt].active = FALSE;
		}
		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);

	enable_sound();
}


pstab_psave(tree, curr, found, n, k)
	OBJECT *tree;
	WORD	curr;
	WORD	found;
	WORD	n;
	ULONG	k;
{
	BYTE path[256];
	BYTE fname[16];
	LISTINFO *list = pstabtree[PSTAB_PLIST].ob_spec.list;
	WORD x;
	BYTE name[10];
	
	if (list->index >= patch_cnt) {
		form_alert(FRM_ALERT, "[Cannot save undefined|patch.][ OK ]");
		return;
	}
	disable_sound();

	objc_outline(tree, NIL, curr);

	strncpy(name, patch_names[list->index], 8);
	x = fsel_input("Patch", "Save Patch", ".", name, " *.pat| *.*", path, &fname[1]);
	objc_draw(tree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);

	if (x) {
		BYTE bbuf[28];
		BYTE dbuf[324];

	
		fname[0] = SLASH;
		strcat(path, fname);
		strrncpy(bbuf, path, 24);

		curr_pnr = list->index;
		curr_patch = &patchtab[list->index][0];
		sprintf(dbuf, FMSTR_SAVEPTT, &fname[1]);
	  	form_alert(FRM_MSGSHOW, dbuf);

		/*objc_draw(procStree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);*/
		write_patch(PATCH_FILE, path, list->index /* Patch No */);
		form_alert(FRM_MSGCLOSE, NULL);
		/*objc_draw(tree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);*/
	}

	
	objc_outline(tree, curr, NIL);
	enable_sound();
}


	VOID
pstab_pdelete(tree, curr, found, n1, k)
	OBJECT *tree;
	WORD	curr;
	WORD	found;
	WORD	n1;
	ULONG	k;
{
	LISTINFO *list = pstabtree[PSTAB_PLIST].ob_spec.list;
	WORD x, d, r;
	BYTE sbuf[256];
	REG WORD i, j;
	LONG old, mask, l;

	
	if (list->index >= patch_cnt) {
		form_alert(FRM_ALERT, "[You cannot delete|an undefined patch.][ OK ]");
		return;
	}
	objc_outline(tree, NIL, curr);
	sprintf(sbuf, "[Do you really want to|delete %s?][Delete |Cancel ]",
		patch_names[list->index]);
	x = form_alert(FRM_ALERT, sbuf);
	if (x == 1) {
		LISTINFO *pttlist = jaztree[JAZ_PTTBOX].ob_spec.list;

		if (patch_cnt == 1) {
			form_alert(FRM_ALERT, 
			"[You cannot delete the|last patch.][ OK ]");
			return;
		}
		disable_sound();
		x = list->index;
		d = x / 32;
		r = x % 32;

		deactivate_mdtab(patchinfo_table[x].mdtab, x);
		patchinfo_table[x].mdtab->used[d] &= (LONG) ~(1L << r);
		if ((patchinfo_table[x].mdtab->used[0] + 
		patchinfo_table[x].mdtab->used[1] +
		patchinfo_table[x].mdtab->used[2] +
		patchinfo_table[x].mdtab->used[3]) == 0) {
			free_mdtabSlot((WORD)(patchinfo_table[x].mdtab - 
					mdtab_table));
		}
		deactivate_env(patchinfo_table[x].env, x);
		for (i = 0; i < 5; i++) {
			if (patchinfo_table[x].env[i] == NULL)
				continue;
			patchinfo_table[x].env[i]->used[d] &= (ULONG) ~(1L << r);

			if ((patchinfo_table[x].env[i]->used[0] + 
			patchinfo_table[x].env[i]->used[1] +
			patchinfo_table[x].env[i]->used[2] +
			patchinfo_table[x].env[i]->used[3]) == 0) {
				free_envSlot((WORD)(patchinfo_table[x].env[i] - 
					env_table));


			}			
			patchinfo_table[x].env[i] = NULL;
		}
		for (i = 0; i < 5; i++) {
			if (patchinfo_table[x].sample[i] == NULL)
				continue;
			patchinfo_table[x].sample[i]->used[d] &= (ULONG) ~(1L << r);
		}

		for (i = x; i <	(patch_cnt - 1); i++) {			
			patchinfo_table[i] = patchinfo_table[i+1];
			for (j = 0; j < 16; j++)
				patch_names[i][j] = patch_names[i+1][j];
			for (j = 0; j < PATCH_SIZE; j++)
				patchtab[i][j] = patchtab[i+1][j];
		}
		patch_cnt--;
		
		/*
		 * update MDtab used fields
		 */
		for (i = 0; i < mdtabslot_cnt; i++) {
			if (mdtab_table[i].free)
				continue;
			l = mdtab_table[i].used[d];
			mask = ((1 << r) - 1L);
			old = l & mask;
			mask = ~mask;
			l >>= 1L;
			l &= mask;
			mdtab_table[i].used[d] = l | old;

			for (j = d+1; j < 4; j++) {
				mdtab_table[i].used[j] >>= 1L;
			}
		}
		/*
		 * update Env used fields
		 */
		env_table[0].used[3] &= 0x7fffffffL;
		for (i = 0; i < envslot_cnt; i++) {
			if (env_table[i].free)
				continue;
			l = env_table[i].used[d];
			mask = ((1 << r) - 1L);
			old = l & mask;
			mask = ~mask;
			l >>= 1L;
			l &= mask;
			env_table[i].used[d] = l | old;

			for (j = d+1; j < 4; j++) {
				env_table[i].used[j] >>= 1L;
			}
		}
		env_table[0].used[3] |= 0x80000000L;
		/*
		 * update Sample used fields
		 */
		for (i = 0; i < smpslot_cnt; i++) {
			if (sample_table[i].free)
				continue;
			l = sample_table[i].used[d];
			mask = ((1 << r) - 1L);
			old = l & mask;
			mask = ~mask;
			l >>= 1L;
			l &= mask;
			sample_table[i].used[d] = l | old;

			for (j = d+1; j < 4; j++) {
				sample_table[i].used[j] >>= 1L;
			}
		}
			


		for (i = 0; i < ptt_count; i++) {
			if (ptt_table[i].patch > (x+1))
				ptt_table[i].patch--;
			else if (ptt_table[i].patch == (x+1)) {
				for (j = i; j < (ptt_count-1); j++) {
					ptt_table[j] = ptt_table[j+1];
				}
				ptt_count--;
				i--;		/* do this one again */
						/* since we copied down */
			}
		}

		pttlist->maxitems = ptt_count;
		if (pttlist->index >= pttlist->maxitems)
			pttlist->index = pttlist->maxitems - 1;
		if (pttlist->index < 0)
			pttlist->index = 0;

		garbage_collection();

		PttTable2Strings(-1);
		PttTable2PitchPatch();
		update_patchnameslist();
		objc_draw(pstabtree, 0, MAXDEPTH, 0, 0, scrn_w, scrn_h);
		enable_sound();
	}
	objc_outline(tree, curr, NIL);
}

	VOID
pstab_pnew(tree, curr, found, n, k)
	OBJECT *tree;
	WORD	curr;
	WORD	found;
	WORD	n;
	ULONG	k;
{
	LISTINFO *list = pstabtree[PSTAB_PLIST].ob_spec.list;
	REG WORD i, x;
	WORD d, r;

	if (list->index >= patch_cnt)
		d = 0;
	else
		d = list->index;

	disable_sound();
	for (x = 0; x < PATCH_SIZE; x++)
		patchtab[patch_cnt][x] = patchtab[d][x];
	patchinfo_table[patch_cnt] = patchinfo_table[d];
	patchinfo_table[patch_cnt].active = FALSE;
	d = patch_cnt / 32;
	r = patch_cnt % 32;
	patchinfo_table[patch_cnt].mdtab->used[d] |= (LONG) (1L << r);
	for (i = 0; i < 5; i++) {
		if (patchinfo_table[patch_cnt].env[i] == NULL)
			continue;
		patchinfo_table[patch_cnt].env[i]->used[d] |= (LONG) (1L << r);
	}
	for (i = 0; i < 5; i++) {
		if (patchinfo_table[patch_cnt].sample[i] == NULL)
			continue;
		patchinfo_table[patch_cnt].sample[i]->used[d] |= (LONG) (1L << r);
	}
	strcpy(patch_names[patch_cnt], patch_names[list->index]);

	list->index = patch_cnt++;
	rentree[RENAME_TITLE].ob_spec.boxtext->bx_text = "NAME NEW PATCH";
	pstab_prename(tree, curr, found, n, k);		
	rentree[RENAME_TITLE].ob_spec.boxtext->bx_text = "RENAME PATCH";

	enable_sound();
}

/*
 *	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;
	WORD		active_flag;

	if (dos_fsize(fname) < 0L) {	/* check for existance */
		sprintf(buf, FMSTR_FILENOTEXIST, fname);
		form_alert(FRM_ALERT, buf);
		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;
		}
		perf_file_flag = FALSE;
		if (patchinfo_table[patch_no].active) {
				mk_one_patch_inactive(patch_no);
				active_flag = TRUE;
		} else {
				active_flag = FALSE;
		}
		free_patchinfo(patch_no);	
		break;
	case PERF_FILE:
		if (strstr(buf, "ATPERF") == NULL) {
			form_alert(FRM_ALERT, "[Not a valid JAZ|performance file.][ OK ]");
			mfclose(fp);
			return -1;
		}
		perf_file_flag = TRUE;
		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 ((file_flag == PERF_FILE) &&
				(strcmp(s, "GLOBALVAR") == 0))
					s = section_globalvar(fp);
				if ((file_flag == PERF_FILE) &&
				(strcmp(s, "SYNTHCNF") == 0))
					s = section_synthcnf(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 { 
		if (perf_file_flag) {
			update_samplenameslist();
			mk_patches_active();	
			PttTable2PitchPatch();
			update_patchnameslist();
		} else {
			update_samplenameslist();
			if (active_flag)
				mk_one_patch_active(patch_no);	
			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;
}


/*
 *	Global Variables Settings
 */

	BYTE
*section_globalvar(fp)
	MFILE *fp;
{
	BYTE		buf[256];
	REG WORD	i = 0;
	LONG		l;
	WORD		n, x;
	REG BYTE	*s;
	BYTE		*sect;
	BYTE		str[64];

	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, ";: MIDIFILTER = $%lx", &l);
		if (n == 1) {
			midi_filter = l;
			midi_set_all();
		}


		n = sscanf(s, ";: DELAY = $%x", &x);
		if (n == 1) {
			POPUPINFO *info = globaltree[GLOBAL_EFFECT].ob_spec.popup;
			gl_effect = x;
			info->index = x;
			set_delay(globaltree, GLOBAL_EFFECT, info);
		}
		n = sscanf(s, ";: PITCHBEND = $%x", &x);
		if (n == 1) {
			POPUPINFO *info = globaltree[GLOBAL_PITCHBEND].ob_spec.popup;
			info->index = x;
			set_pitchbend(globaltree, GLOBAL_PITCHBEND, info);
		}

	}

	return sect;
}


/*
 *	Synth Cnf (ignored by JAZ)
 */

	BYTE
*section_synthcnf(fp)
	MFILE *fp;
{
	BYTE		buf[256];
	REG WORD	i = 0;
	REG BYTE	*s;
	WORD		n;
	BYTE		*sect;
	BYTE		str[64];

	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;
		}
	}

	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, &ix))!= 0L) {
				strncpy(sym_ref[ref_cnt].name, label, 32);
				sym_ref[ref_cnt].addr = (ULONG) 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++;
				/*
				 * 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) {
		 		 sprintf(buf, FMSTR_FILENOTEXIST, fname);
				 form_alert(FRM_ALERT, buf);
				}
				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;
			sample_table[ix].headaddr = sample_table[ix].addr;

			smp_formats(ix);

			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].used[0] = 0L;
			sample_table[ix].used[1] = 0L;
			sample_table[ix].used[2] = 0L;
			sample_table[ix].used[3] = 0L;
			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, j, 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, l;
	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 0
			if (reverb_flag) {
				if (lvalue == _delay)
					*rev_p++ = _delay;
				else if (lvalue == _reverb)
					*rev_p++ = _reverb;	
				*rev_p = (ULONG) _vt_return_loc;
			} else {
#endif
				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;
						

					/*
					 * check if we already have one
					 * like this
					 */				
					for (l = 0; l < (mdtabslot_cnt-1); l++) {
					  if (mdtab_table[l].free || l == ix)
					    continue;
					
					  for (j = 0; j < k; j++) {
					    if (mdtab_table[l].modules[j] != 
					        mdtab_table[ix].modules[j])
							break;
					  }
					  if (j == k) { /* same */
					    sym_ref[ref_cnt-1].addr = 
					    (ULONG) &mdtab_table[l].modules;
					    sym_ref[ref_cnt-1].sym_spec.mdtab_p = 
				             &mdtab_table[l];
					    free_mdtabSlot(ix);
					    break;

					  }

					}
				}
#if 0
			}		
#endif
		}
	}
	return sect;
}


	BYTE
*section_waveform(fp)
	MFILE *fp;
{
#if 1
	BYTE		buf[256];
	WORD		more = FALSE;
	REG WORD	i = 0, k = 0;
	REG BYTE	*s, *t;
	REG ULONG	*p;
	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;
	LONG		size;
	WORD		dspwave;

	/*
	 * 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 waveform 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 ((k % 128) != 0)
					form_alert(FRM_ALERT, FMSTR_SHORTWAVE);
				if (strstr(label, "_D1") != NULL) {
					dspwave = 1;
				} else if (strstr(label, "_D2") != NULL) {
					dspwave = 2;
				} else
					dspwave = 0;
			}
		}	
		/*
		 * 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_sampleSlot()) == -1) {
					form_alert(FRM_ALERT, FMSTR_NOSAMPLESLOT);
					break;	
				}
				size = 512L;
				if ((sample_table[ix].addr = PHalloc(size, 512L)) == NULL) {
					form_alert(FRM_ALERT, FMSTR_OUTOFMEMORY);
					break;					
				}
				sample_table[ix].size = size;
				sample_table[ix].headaddr = sample_table[ix].addr;
				sample_table[ix].type = SMPTYPE_WAVEFORM;
				/*
				 *	put it into sample table
				 */
				sample_table[ix].used[0] = 0L;
				sample_table[ix].used[1] = 0L;
				sample_table[ix].used[2] = 0L;
				sample_table[ix].used[3] = 0L;
				sample_table[ix].type |= NAME_FLAG;
				strncpy(sample_table[ix].filename, "", 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++;

				no_slot = FALSE;

				k = 0;
				p = (LONG*) sample_table[ix].addr;
				*p++ = lvalue; k++;
			} else {
				*p++ = lvalue; k++;
			}
			if (k >= 128) {
				no_slot = TRUE;		
				if (dspwave > 0) {
					ULONG mask;
					WORD x, shift, dspwave;
					REG WORD i1;
					ULONG *rom,
					      *p = (LONG*) (0xf1d000L - 512L); 
						/* here're DSP waveforms */
					ULONG *q;

					rom = p;
					if (dspwave == 1) {
						shift = 0;
						mask = 0xffff0000L;
						dspwave1 = ix;
					} else {
						shift = 16;
						mask = 0x0000ffffL;
						dspwave2 = ix;
					} 
					q = (ULONG *) sample_table[ix].addr;
	
					for (i1 = 0; i1 < 128; i1++, p++, q++) {
						*p = (*p & mask) | 
						     ((*q & 0xffffL) << shift);
					}
					sample_table[ix].bakaddr = 
						sample_table[ix].addr;
					sample_table[ix].addr = (BYTE*)rom; 

					if (dspwave == 2) {
						LONG l;
						l = (LONG) sample_table[ix].addr;
						l |= 0x40000000L; /* odd */
						sample_table[ix].addr = 
							(BYTE *) l;

						sample_table[ix].type = 
							SMPTYPE_DSPWAVE2;
						sample_table[ix].type |= NAME_FLAG;

					} else {
						sample_table[ix].type = 
							SMPTYPE_DSPWAVE1;
						sample_table[ix].type |= NAME_FLAG;
					}
				}				
			}
		}
	}
	if ((k % 128) != 0)
		form_alert(FRM_ALERT, FMSTR_SHORTWAVE);

	return sect;
#endif
}


/*
 *	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];
				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, j;
	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;
	for (j = 0; j < 5; j++) {
		patchinfo_temp.env[j] = NULL;
		patchinfo_temp.sample[j] = NULL;
	}	

	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;

				for (j = 0; j < 5; j++) {
					patchinfo_temp.env[j] = NULL;
					patchinfo_temp.sample[j] = NULL;
				}	
				patchinfo_temp.mdtab = NULL;
				patchinfo_temp.active = FALSE;
				if (perf_file_flag || (patch_no >= patch_cnt))
					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;
				offset = (offset - 2) / 3;
				t->sym_spec.smp_p->used[d] |=
						(ULONG) (1L << r);
				p = &patchinfo_temp;

				p->sample[offset] = (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;
					offset = (offset - 2) / 3;
					switch (t->type) {
					case SYMTYP_ENVELOPE:
					    t->sym_spec.env_p->used[d] |=
						(ULONG) (1L << r);
						p->env[offset] = 
							(ENVTAB *) 
							t->sym_spec.env_p;
						break;
					case SYMTYP_SAMPLE:
						t->sym_spec.smp_p->used[d] |=
							(ULONG) (1L << r);
						p->sample[offset] = 
							(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 0
	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);	
	}
#else
sprintf(temp,"Algorithm for Patch '%s', #%d, GRP %d",
	patch_names[curr_pnr], curr_pnr+1, 
	1+(WORD)(patchinfo_table[curr_pnr].mdtab - mdtab_table));

	objc_xupdate(algtree, ALG_TITLE, T_BX_TEXT, temp, 0);	
	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);	
#endif
}

	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_patchinfo(pnr)
	WORD pnr;
{
	WORD d, r;
	MDTAB *m = patchinfo_table[pnr].mdtab;
	ENVTAB *e;
	SMPTAB *s;
	WORD i;

	if (pnr >= patch_cnt)
		return;

	d = pnr / 32;
	r = pnr % 32;

	m->used[d] &= (ULONG) ~(1L << r);

	for (i = 0; i < 5; i++) {
		if (patchinfo_table[pnr].env[i] == NULL)
			continue;
		e = patchinfo_table[pnr].env[i];
		e->used[d] &= (ULONG) ~(1L << r);
	}

	for (i = 0; i < 5; i++) {
		if (patchinfo_table[pnr].sample[i] == NULL)
			continue;
		s = patchinfo_table[pnr].sample[i];
		s->used[d] &= (ULONG) ~(1L << r);
	}
}

	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].headaddr);	/* free sample memory */
	sample_table[slot].size = 0L;
	sample_table[slot].addr = NULL;
	sample_table[slot].headaddr = NULL;

	if (slot == (smpslot_cnt - 1)) {
		if (smpslot_cnt > 0)
			smpslot_cnt--;
	}
}
	VOID
free_allSamples(VOID)
{
	REG WORD i;
	for (i = 10; i < smpslot_cnt; i++) /* start after ROM WAVES !!! */
		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, ix)
	BYTE	*fname;
	WORD 	*ix;
{
	REG WORD i;

	for (i = 0; i < smpslot_cnt; i++) {
		if (strcmp(fname, sample_table[i].filename) == 0) {
			*ix = i;
			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;
{
	BYTE *t = s;
	for (; *s; s++) {
		if (*s == '_')
			*s = ' ';
		*s = ((*s >= 97) && (*s <= 122)) ? _toupper(*s) : *s;
	}
	if ((s = strstr(t, " WFM")) != NULL) {
		*s = '\0';
	}
}

/*
 *	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";

		patchnameslist[j++] = 
		    &nrstr[(WORD)(patchinfo_table[i].mdtab - mdtab_table)][0];
		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++] = "    ";
		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;
		}
	}
}

	VOID
mk_patches_inactive()
{
	REG WORD i;

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

			deactivate_mdtab(p->mdtab, patch_no);
			deactivate_env(p->env, patch_no);

			patchinfo_table[patch_no].active = FALSE;
		}
	}
}

/*
 *	mk_one_patch_active()
 *	
 */
	WORD
mk_one_patch_active(pnr)
	WORD pnr;
{
	REG WORD i;
	REG PATCHINFO *p = &patchinfo_table[pnr];
	WORD r = SUCCESS;

	if (activate_mdtab(p->mdtab, pnr) == FAILURE)
		r = FAILURE;
	if (activate_env(p->env, pnr) == FAILURE)
		r = FAILURE;
	if (r == SUCCESS)
		p->active = TRUE;
	else
		p->active = FALSE;

	return r;
}

/*
 *	mk_one_patch_active()
 *	
 */
	VOID
mk_one_patch_inactive(pnr)
	WORD pnr;
{
	REG WORD i;
	REG PATCHINFO *p = &patchinfo_table[pnr];


	p->active = FALSE;
	deactivate_mdtab(p->mdtab, pnr);
	deactivate_env(p->env, pnr);

}

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


	VOID
deactivate_mdtab(mdtab_p, patch_no)
	MDTAB *mdtab_p;
	WORD  patch_no;
{
	REG WORD i, j;
	WORD flag = FALSE;

	disable_sound();
	if (mdtab_p->dsp_addr != NULL) {		
		for (i = 0; i < 4; i++) {
			for (j = 0; j < 32; j++) {
				if ((i*32+j) == patch_no)
					continue;
				if ((mdtab_p->used[i] >> j) & 1)
					if (patchinfo_table[i*32+j].active)
						flag = TRUE;		
			}
		}		
		if (!flag) {
			freeMDTAB(mdtab_p->dsp_addr);
			mdtab_p->dsp_addr = NULL;
		}
	}
	if (patch_no >= 0) {
		patchtab[patch_no][0] = (LONG) mdtab_p;		
	}

	enable_sound();
}



	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;	

	disable_sound();
	for (i = 0; i < 5; i++) {
		if (envtab_p[i] == NULL)
			continue;
		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);
				enable_sound();
				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][2+(i*3)] == 
			(LONG) envtab_p[i])
				patchtab[patch_no][2+(i*3)] = 
					(LONG) envtab_p[i]->dsp_addr;
		}
		
	}
	enable_sound();
	return SUCCESS;
}

	VOID
deactivate_env(envtab_p, patch_no)
	REG ENVTAB *envtab_p[];
	WORD  patch_no;
{
	PATCHINFO *p = &patchinfo_table[patch_no];
	REG WORD i, j, k;
	WORD flag = FALSE;

	for (k = 0; k < 5; k++) { 
		if (envtab_p[k] == NULL)
			continue;
		if (envtab_p[k]->dsp_addr != NULL) {		
			flag = FALSE;
			for (i = 0; i < 4; i++) {
				for (j = 0; j < 32; j++) {
					if ((i*32+j) == patch_no)
						continue;
					if ((envtab_p[k]->used[i] >> j) & 1)
					     if (patchinfo_table[i*32+j].active)
							flag = TRUE;		

				}
			}
			if (!flag) {
				freeENV(envtab_p[k]->dsp_addr);
				envtab_p[k]->dsp_addr = NULL;
			}
		}
		if (patch_no >= 0) {
			patchtab[patch_no][2+k*3] = (LONG) envtab_p[k];
		}
	}

}


/*
 *	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].env_ix = dsp_envslots[j+1].env_ix;
		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;
				   for (n = 0; n < 5; n++) {
				      o = 2 + n *3;
				      /*
				       * 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;

	temp_dft_env = FALSE;
	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;
		}
		if (envtab_p->used[3] & 0x80000000L)
			temp_dft_env = TRUE;
		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;
	org_work_addr = p;

	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;
				for (n = 0; n < 5; n++) {
					o = 2+n*3;
					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 */
			temp_env_envtab_bak = patchinfo_table[no].env[1+flag];
			patchinfo_table[no].env[1+flag] = 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, *r;
	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;
	r = envtab_p->data;	/* also, copy it back to the data strucutre */
	l = entries;
	for (i = 0; i < l; i++) {
		*r++ = *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;
				for (n = 0; n < 5; n++) {
					o = 2+3*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;

	if (temp_dft_env) {	/* if it was temp, then copy last edited */
		freeENV(dflt_envtab.dsp_addr);
		if ((new = allocENV(entries, dft_env_ix)) == NULL) {
			form_alert(FRM_ALERT, FMSTR_NODSPENVSPACE);
		}
		p = _env_temp;
		q = new;
		r = env_table[dft_env_ix].data;
		l = entries;
		for (i = 0; i < l; i++) {
			*r++ = *q++ = *p++;
		}
		env_table[dft_env_ix].dsp_addr      = new;
		env_table[dft_env_ix].n_entries     = entries;
		env_table[dft_env_ix].start_value   = start;
		env_table[dft_env_ix].release_slope = rel_slope;	
	}

	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;
				for (n = 0; n < 5; n++) {
					o = 2+n*3;
					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;
			patchinfo_table[no].env[temp_env_flag+1] = 
				temp_env_envtab_bak;
		}
	}
	if (((temp_env_flag != -1) && (used != 1)) || temp_env_new) { /* +++ HMK 7/11/ 95    */
				/* changed from "used == 1" to "used != 1" */
		free_envSlot(temp_env_ix);
	}

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

	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 (_debug) {
		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;
		}
	}
}

	VOID
garbage_collection()
{
	REG LONG *p, *q;
	REG WORD i, j, k;
	WORD x, found, flag;
	LONG lvalue;
	/*
	 * first, lets find multiple MDTABs
	 */

	for (i = 0; i < (N_DSP_MDTABS-1); i++) {
		if (dsp_mdtabslots[i] == 0)
			continue;	

		for (j = i + 1; j < N_DSP_MDTABS; j++) {
			if (dsp_mdtabslots[j] == 0) {
				continue;
			}
			p = &_mdtab_start[i * N_MDMODULES];
			q = &_mdtab_start[j * N_MDMODULES];
			flag = 0;
			for (k = 0; k < N_MDMODULES; k++) {
				if (*p++ != *q++) {
					flag = 1;
					break;
				}
				if (*p == (LONG) _attn)
					break;
			}
			if (!flag && (*p == *q)) {/* they are the same */
				p = &_mdtab_start[i * N_MDMODULES];
				q = &_mdtab_start[j * N_MDMODULES];	
				dsp_mdtabslots[j] = 0; /* free it!! */
				for (k = 0; k < N_MDTABS; k++) {
					if (mdtab_table[k].free)
						continue;
					if (mdtab_table[k].dsp_addr == p) 
						break;
				}
				if (k == N_MDTABS) {
					form_alert(FRM_ALERT, "[Garbage collection:|Lost MDTAB.][ OK ]");
					continue;
				}
				x = k;
				for (k = 0; k < N_MDTABS; k++) {
					if (mdtab_table[k].free)
						continue;
					if (mdtab_table[k].dsp_addr == q) {
						replace_mdtab(k, x);
						freeMDTAB(q);
					}
				}
			}
		}

	}
	/*
	 * find unused MDTABs
	 */
	for (i = 0; i < N_MDTABS; i++) {
		if (mdtab_table[i].free)
			continue;
		if ((mdtab_table[i].used[0] + mdtab_table[i].used[1] +
		     mdtab_table[i].used[2] + mdtab_table[i].used[3]) <= 0) {
			if ((lvalue = (LONG)mdtab_table[i].dsp_addr) != 0L) {
	
				/*
				 * anyone else using it?
				 */	
				 found = 0;
				 for (j = 0; j < N_MDTABS; j++) {
					if (j == i)
						continue;
					if (lvalue == (LONG)mdtab_table[i].dsp_addr)
						found = 1;
				}
				if (found == 0)
					freeMDTAB((LONG*)lvalue);
				mdtab_table[i].dsp_addr = NULL;
			} 
			free_mdtabSlot(i);
		}
	}
	/*
	 * next envelopes...
	 */

	/* MISSING: Checking for double envelopes...
	 */
	
	/*
	 * find unused Envs
	 */
	for (i = 0; i < N_ENVS; i++) {
		if (env_table[i].free)
			continue;
		if ((env_table[i].used[0] + env_table[i].used[1] +
		     env_table[i].used[2] + env_table[i].used[3]) <= 0) {
			if ((lvalue = (LONG)env_table[i].dsp_addr) != 0L) {
	
				/*
				 * anyone else using it?
				 */	
				 found = 0;
				 for (j = 0; j < N_ENVS; j++) {
					if (j == i)
						continue;
					if (lvalue == (LONG)env_table[i].dsp_addr)
						found = 1;
				}
				if (found == 0)
					freeENV((LONG*)lvalue);
				env_table[i].dsp_addr = NULL;
			}
			free_envSlot(i);
		}
	}
	/*
	 * ... to be continued 
	 */


}

	VOID
replace_mdtab(old, new)
	WORD old, new;
{
	REG WORD i, j;

	for (i = 0; i < 4; i++) {
		for (j = 0; j < 32; j++) {
			if ((mdtab_table[old].used[i] >> j) & 1) {
			      patchinfo_table[i*32+j].mdtab = &mdtab_table[new];
			      mdtab_table[new].used[i] |= (ULONG)(1L << j);
			}
		}	
	}
	free_mdtabSlot(old);
}



