#include	<curses.h>

/*
 *	The following source code is being placed in the public domain
 *	with the sole restriction that if any of it is used in any manner,
 *	proper acknowledgement will be given to the original author -
 *	Eric Pettersen
 */

#define	Effect		(!((Random() >> 3) % weirdness))
#define sfunc(i,b)	((types[i][b] == 1) ? "high": ((types[i][b] == 2) ? \
			" mid": " low"))
#define Get_roll(a)	roll = get_roll(a)
#define	AND_CHAR	241
#define	OPTIONS		9
#define	EDIT_OPTIONS	8
#define	MAIN		0
#define	EDIT		1
#define	OK		0
#undef	NULL
#define	NULL		-1
#define	ABORT		-2
#define	BAD		-3

#define	VOICES		1000

/* either #define DX100 or CZ101 here */
#ifdef	DX100
#define	RANDOM_FILE	"DX100.DTA"
#define	VOICE_FILE	"DXVOICES.DTA"
#define	VOICE_SIZE	77
#define	TOTAL_SIZE	93
#else
#define	RANDOM_FILE	"CZ101.DTA"
#define	VOICE_FILE	"CZVOICES.DTA"
#define	VOICE_SIZE	81
#define	TOTAL_SIZE	128
#define	VDT		0
#define	VRm		1
#define	VRl		2
#define VD		3
#define	SLOTS		4

char	table[SLOTS][100]; /* the CZMIDI.DTA data file fits in this table */
int	dca_kf[10] = { 0, 8, 17, 26, 36, 47, 58, 69, 82, 95 };
int	dcw_kf[10] = { 0, 31, 44, 57, 70, 83, 96, 110, 146, 255 };
#endif

int	weirdness;
int	ranges[VOICE_SIZE];		/* data from DX/CZDATA.DTA	*/
int	types[VOICE_SIZE][2];		/* is read into these		*/
int	tries[VOICE_SIZE][2];		/* arrays			*/
char	out[80], key_tbl[384];		/* data from KEYTAB.DTA goes in */
					/* key_tbl array		*/
int	cur_voice, load_ptr;		/* cur_voice: points to where	*/
					/* next saved voice will go in	*/
					/* the voices array (and also,	*/
					/* therefore, how many voices	*/
					/* we have in the library)	*/
					/* load_ptr: which voice from	*/
					/* voices array was last loaded	*/
					/* into the synth		*/
long	cur_space;			/* how much free space on disk	*/
unsigned char	voices[VOICES][TOTAL_SIZE]; /* actual voices */
char	v_names[VOICES+1][80];		/* associated voice names	*/

/* main: Handle all main menu I/O and call any subroutines requested */
main()
{
	char	key, ans;
	char	voice[TOTAL_SIZE], v_name[80], search_str[80];
	int	i, j, name_valid, load_voice, leng, found, init_rez;

	if ((init_rez = Getrez()) == 0) Setscreen(-1L, -1L, 1);
	init_curses();
	scr_func(CLEAR_SCR);
	printf("Getting old voices from file\n\r");
	get_voices(v_names, voices);
	load_ptr = -1;
	printf("Getting random voice weights from file\n\r");
	initialize();
	show_screen(MAIN);
	do {
		pos_cursor(8, 3+OPTIONS);
		scr_func(ERASE_TO_EOP);
		switch((int)(key = Crawcin())) {
		case 'n':
		case 'N':
			printf("n");
			printf("\n\rmaking voice");
			make_voice(voice);
			load_ptr = cur_voice;
			/* FALL THROUGH */
		case 'r':
		case 'R':
			if (key == 'r' || key == 'R'){
				printf("r");
			}
			if (load_ptr < 0)
				printf("\n\rno current voice\n\r");
			else {
				printf("\n\rsending voice\n\r");
				if (load_ptr == cur_voice)
					send_voice(voice);
				else 
					send_voice(voices[load_ptr]);
				show_voice(load_ptr);
			}
			break;
		case 's':
		case 'S':
			printf("s");
			printf("\n\rreceiving voice\n\r");
#ifndef	DEBUG
			receive_voice(voice);
#endif
			do {
				do {
					pos_cursor(0,5+OPTIONS);
					scr_func(CLEAR_TO_EOL);
					printf("enter voice name: \007");
					printf("\n\r[ESC to abort]");
					pos_cursor(18, 5+OPTIONS);
					leng = get_str(v_name);
				} while (leng == 0);
				if (leng < 0) break;
				name_valid = TRUE;
				for(i=0; i<leng; i++)
					if (v_name[i]<32 || v_name[i]>127) {
						printf("\n\rName has invalid ");
						printf("character in it - ");
						printf("re-try");
						name_valid = FALSE;
						break;
					}
				v_name[i] = '\0';
				if (name_valid)
					for(i=0; i<cur_voice; i++) {
						if(!strcmp(v_names[i],v_name))
						{
							printf("\n\rThere is al");
							printf("ready a voice");
							printf(" named '%s'",
								v_name);
							printf(" - re-try");
							name_valid = FALSE;
							break;
						}
					}
			} while(name_valid == FALSE);
			if (leng < 0) break;
#ifdef	DX100
			strncpy(voice+77, v_name, 10); 
			for(i=strlen(v_name); i<10; i++) voice[77+i] = ' ';
#endif
			pos_cursor(0,6+OPTIONS);
			scr_func(CLEAR_TO_EOL);
			for(i=0; i<TOTAL_SIZE; i++) voices[cur_voice][i] = voice[i];
			strcpy(v_names[cur_voice], v_name);
			printf("writing voice '%s' to file\n\r", v_name);
			write_voice(v_name, voice);
			cur_voice++;
			show_voice(load_ptr);
			break;
		case 'a':
		case 'A':
			printf("a");
			goto test;
		case 'l':
		case 'L':
			printf("l");
test:
			if (cur_voice == 0) {
				warn("No voices to load");
				break;
			}
			if (key == 'a' || key == 'A') {
				load_ptr = (load_ptr + 1) % cur_voice;
				goto load_it;
			}
			load_voice = get_v_num(5+OPTIONS);
			if (load_voice == ABORT) break;
			load_ptr = load_voice;
load_it:
			pos_cursor(0,6+OPTIONS);
			scr_func(CLEAR_TO_EOL);
			printf("Loading voice '%s'\n\r", v_names[load_ptr]);
			send_voice(voices[load_ptr]);
			show_voice(load_ptr);
			break;
		case 'w':
		case 'W':
			printf("w");
			do {
				pos_cursor(0, 5+OPTIONS);
				scr_func(CLEAR_TO_EOL);
				printf("New weirdness factor: ");
				leng = get_num();
				if (leng == BAD || leng == 0) {
					printf("\n\rInput 1 or higher ");
					printf("[ESC to abort]");
				}
			} while(leng == 0 || leng == BAD);
			if (leng > 0) {
				weirdness = leng;
				show_weirdness();
			}
			break;
		case 'e':
		case 'E':
			show_screen(EDIT);
			edit();
			show_screen(MAIN);
			break;
		case 'f':
		case 'F':
			printf("f");
			Keytbl(key_tbl, key_tbl+128, key_tbl+256); 
			pos_cursor(0, 5+OPTIONS);
			printf("Enter string(s) to search for: \n\r");
			printf("[ESC to abort]");
			pos_cursor(32, 5+OPTIONS);
			leng = get_str(search_str);
			Bioskeys(); 
			if (leng <= 0) break;
			found = -1;
			pos_cursor(0, 8+OPTIONS);
			printf(" #  name\n\r");
			printf("--- ----");
			do {
				found = search(search_str, found+1);
				if (found < cur_voice) {
					pos_cursor(0, 10+OPTIONS);
					scr_func(CLEAR_TO_EOL);
					printf("%03d '%s'", found, 
						v_names[found]);
					pos_cursor(0, 6+OPTIONS);
					scr_func(CLEAR_TO_EOL);
					printf("Continue search (y/n)? \007");
					printf("%c", (ans = Crawcin()));
				}
				else {
					pos_cursor(0, 10+OPTIONS);
					scr_func(CLEAR_TO_EOL);
					printf("*** NONE");
					pos_cursor(0, 6+OPTIONS);
					scr_func(CLEAR_TO_EOL);
					printf("-- Hit any key to continue --");
					wait();
				}
			} while (found<cur_voice && (ans == 'y' || ans == 'Y'));
			break;
		case 'q':
		case 'Q':
			printf("q");
			break;
		default:
			printf("\007");
			printf("\n\rBad option '%c' - re-try", key);
			break;
		}
	} while (key != 'q' && key != 'Q');
	if (init_rez == 0) Setscreen(-1L, -1L, 0);
	curses_cleanup();
}

/* get_voices: read the standard voice library file and initialize the
	voices in memory */
get_voices(names, voice_data)
char names[][80], voice_data[][TOTAL_SIZE];
{
	long	result;
	int	handle;

	check_disk();
	if (cur_space < 80 + TOTAL_SIZE) {
		warn(
	"WARNING: There is not enough space on the disk to store more voices");
	}
	result = Fsfirst(VOICE_FILE, 0);
	if (result == EFILNF) {
		printf("file '%s' not found -- attempting to create\n\r",
			VOICE_FILE);
		handle = Fcreate(VOICE_FILE, 0);
		if (handle < 0) {
			sprintf(out, "cannot create '%s'", VOICE_FILE);
			error(out);
		}
		else {
			printf("'%s' created\n\r", VOICE_FILE);
			Fclose(handle);
			result = E_OK;
		}
	}
	if (result == E_OK) {
		handle = Fopen(VOICE_FILE, READ);
		if (handle < 0) {
			sprintf(out, "cannot open '%s'", VOICE_FILE);
			error(out);
		}
		else {
			printf("file opened\n\r");
		}
	}
	else {
		sprintf(out, "cannot find '%s'", VOICE_FILE);
		error(out);
	}
	cur_voice = 0;
	printf("reading in voice data\n\r");
	while(TRUE) {
		if (scan_name(handle, names[cur_voice]) != OK) break;
		if (strlen(names[cur_voice]) > 0) {
			if (scan_voice(handle, voice_data[cur_voice]) != OK) 
				break;
			cur_voice++;
		}
		else break;
	}
	Fclose(handle);
}

/* make_voice: create a random voice and put it in 'voice' */
make_voice(voice)
char voice[];
{
	int	i, roll, wave1, wave2, nmod, rmod, line, step, sustain;
	int	rate, level;	

#ifdef	DX100
	for (i=0; i<VOICE_SIZE; i++) {
		if (Effect) voice[i] = func(i,0);
		else voice[i] = func(i,1);
	}
	if (!((Random() >> 3) % 4)) voice[10] = 0; /* each operator	*/
	if (!((Random() >> 3) % 4)) voice[23] = 0; /* (except # 1)	*/
	if (!((Random() >> 3) % 4)) voice[36] = 0; /* given arbitrary	*/
						   /* 25% chance of not */
						   /* being used	*/
	if (!Effect) voice[11] = round(voice[11]); /* normal frequency	*/
	if (!Effect) voice[24] = round(voice[24]); /* ratios are 	*/
	if (!Effect) voice[37] = round(voice[37]); /* rational multiples*/
	if (!Effect) voice[50] = round(voice[50]); /* of 1.0		*/
	voice[49] = 99;		/* max operator 1 output */
	voice[62] /= 12;
	voice[62] *= 12;	/* make key change shift whole octaves */
	sprintf(voice+77, "RANDOM    ");
	for(i=87; i<93; i++) voice[i] = 0;
#else
	/* Do I want to explain how the code works for the CZ101? NOOO!!! *
	 * I suggest you get a copy of CZ101 MIDI system exclusive specs  *
	 * and then examine the code closely.  What makes this stuff so   *
	 * hard to understand is that the random values have to be put    *
	 * into specific *bit positions* in specific bytes, instead of    *
	 * being given bytes of their very own, i.e. several different    *
	 * values are stuffed into a specific byte.  If there is something*
	 * crucial that you cannot understand you can E-mail me.  If you  *
	 * scrap the code and write your own, I suggest you retain CZMIDI.*
	 * DTA because that is a pain to generate by hand.		  */
	Get_roll(0);
	roll = (roll - 1) % 3;
	voice[0] = 0 | (roll << 2) | (line = 3); 
		/* Line select and Octave */
	voice[1] = (Random() >> 3) % 2; 
		/* Detune + or - */
	Get_roll(1);
	voice[2] = (((10*roll)/151) << 4) + ((roll + roll/15 - 1) % 16);
		/* Fine Detune */
	Get_roll(2);
	voice[3] = roll;
		/* Octave and Note Detune */
	Get_roll(3);
	voice[4] = 8/(roll+1) + (roll == 3) * 28;
		/* Vibrato Waveform */
	Get_roll(4);
	voice[5] = roll;
	voice[6] = table[VDT][roll];
	voice[7] = (roll > 79) + (roll > 95);
		/* Vibrato Delay Time (3 bytes) */
	Get_roll(5);
	voice[8] = roll;
	voice[9] = table[VRl][roll];
	voice[10] = table[VRm][roll];
		/* Vibrato Rate (3 bytes) */
	Get_roll(6);
	voice[11] = roll;
	voice[12] = table[VD][roll];
	voice[13] = (roll > 78) + (roll > 94) + (roll > 98);
		/* Vibrato Depth (3 bytes) */
	for(i=0; i<2; i++) {
		Get_roll(7+i*37);
		wave1 = roll;
		Get_roll(8+i*37);
		if (roll) {
			Get_roll(9+i*37);
			wave2 = roll+1;
			if (wave1 > 4 && wave2 > 5) wave2 = 0;
		}
		else wave2 = 0;
		Get_roll(10+i*37);
		if (!(rmod = roll)) {
			Get_roll(11+i*37);
			nmod = roll;
		} else nmod = 0;
		if (i || line < 2) nmod = rmod = 0;
		voice[14+i*57] = 
			((wave1 < 3 ? wave1 : (wave1 > 4 ? 6 : wave1+1)) << 5)
			+(wave2>0)*((wave2<4 ? wave2-1:(wave2>5 ? 6:wave2))<<2)
			+((wave2 > 0) << 1);
			/* DCO waves */
		wave2--;
		voice[15+i*57] = 
			((max(wave1,wave2) > 4 ? max(wave1,wave2)-4 : 0) << 6)
			+ (rmod << 5) + ((3*nmod) << 3);
			/* Modulation and DCO wave resonance */
		Get_roll(12+i*37);
		voice[16+i*57] = roll;
		voice[17+i*57] = dca_kf[roll];
			/* DCA Key Follow */
		Get_roll(13+i*37);
		voice[18+i*57] = roll;
		voice[19+i*57] = dcw_kf[roll];
			/* DCW Key Follow */
		Get_roll(14+i*37);
		if (!i && roll < 1) roll = 1;
		voice[20+i*57] = roll;
			/* DCA End Step */
		if (Effect) sustain = 8;
		else sustain = mid(voice[20+i*57], 3);
		if (sustain == voice[20+i*57]) sustain = 8;
		Get_roll(15+i*37);
		rate = roll;
		voice[21+i*57] = (119 * rate) / 99;
			/* DCA Attack Rate */
		Get_roll(16+i*37);
		level = roll;
		voice[22+i*57] = (sustain == 0 ? 128 : 0) +
			(level == 0 ? 0 : level+28);
			/* DCA Attack Level */
		for (step=1; step<voice[20+i*57]; step++) {
			if (sustain < 8) {
				if (step < sustain) {
					Get_roll(17+i*37);
					rate = roll;
					Get_roll(18+i*37);
					level = roll;
				}
				else if (step == sustain) {
					Get_roll(19+i*37);
					rate = roll;
					Get_roll(20+i*37);
					level = roll;
				}
				else {
					Get_roll(21+i*37);
					rate = roll;
					Get_roll(22+i*37);
					level = roll;
				}
			}
			else if (step < (voice[20+i*37]+1)/2) {
				Get_roll(17+i*37);
				rate = roll;
				Get_roll(18+i*37);
				level = roll;
			}
			else {
				Get_roll(21+i*37);
				rate = roll;
				Get_roll(22+i*37);
				level = roll;
			}
			voice[21+2*step+i*57] = (level < (voice[20+2*step+i*57]
				& 0x007F) ? 128 : 0) + ((119 * rate)/99);
				/* DCA intermediate rate */
			voice[22+2*step+i*57] = (sustain == step ? 128 : 0) +
				(level == 0 ? 0 : level+28);
				/* DCA intermediate level */
		}
		Get_roll(23+i*37);
		rate = roll;
		voice[21+2*step+i*57] = 128 + ((119 * rate)/99);
			/* DCA End Rate */
		voice[22+2*step+i*57] = 0;
			/* DCA End Level */
		for(step=step+1;step<8;step++) {
			voice[21+2*step+i*57] = (119*50)/99;
			voice[22+2*step+i*57] = 0;
			/* Envelope cleanup */
		}
		Get_roll(24+i*37);
		voice[37+i*57] = roll;
			/* DCW End Step */
		if (Effect) sustain = 8;
		else sustain = mid(voice[37+i*57], 3);
		if (sustain == voice[37+i*57]) sustain = 8;
		Get_roll(25+i*37);
		rate = roll;
		Get_roll(26+i*37);
		level = roll;
		voice[38+i*57] = (119 * rate) / 99;
			/* DCW Attack Rate */
		voice[39+i*57] = (127 * level) / 99;
			/* DCW Attack Level */
		for (step=1; step<voice[37+i*57]; step++) {
			if (sustain < 8) {
				if (step < sustain) {
					Get_roll(27+i*37);
					rate = roll;
					Get_roll(28+i*37);
					level = roll;
				}
				else if (step == sustain) {
					Get_roll(29+i*37);
					rate = roll;
					Get_roll(30+i*37);
					level = roll;
				}
				else {
					Get_roll(31+i*37);
					rate = roll;
					Get_roll(32+i*37);
					level = roll;
				}
			}
			else if (step < (voice[20+i*37]+1)/2) {
				Get_roll(27+i*37);
				rate = roll;
				Get_roll(28+i*37);
				level = roll;
			}
			else {
				Get_roll(31+i*37);
				rate = roll;
				Get_roll(32+i*37);
				level = roll;
			}
			voice[38+2*step+i*57] = (level < (voice[37+2*step+i*57]
				& 0x007F) ? 128 : 0) + ((119 * rate)/99);
				/* DCW intermediate rate */
			voice[39+2*step+i*57] = (sustain == step ? 128 : 0) +
				((127 * level) / 99);
				/* DCW intermediate level */
		}
		Get_roll(33+i*37);
		rate = roll;
		voice[38+2*step+i*57] = 128 + ((119 * rate)/99);
			/* DCW End Rate */
		voice[39+2*step+i*57] = 0;
			/* DCW End Level */
		for(step=step+1;step<8;step++) {
			voice[38+2*step+i*57] = (119*50)/99;
			voice[39+2*step+i*57] = 0;
			/* Envelope cleanup */
		}
		Get_roll(34+i*37);
		voice[54+i*57] = roll;
			/* DCO End Step */
		if (Effect) sustain = 8;
		else sustain = mid(voice[54+i*57], 3);
		if (sustain == voice[54+i*57]) sustain = 8;
		Get_roll(35+i*37);
		rate = roll;
		Get_roll(36+i*37);
		level = roll;
		voice[55+i*57] = (127 * rate) / 99;
			/* DCO Attack Rate */
		voice[56+i*57] = (level < 64 ? level : level+4);
			/* DCO Attack Level */
		for (step=1; step<voice[54+i*57]; step++) {
			if (sustain < 8) {
				if (step < sustain) {
					Get_roll(37+i*37);
					rate = roll;
					Get_roll(38+i*37);
					level = roll;
				}
				else if (step == sustain) {
					Get_roll(39+i*37);
					rate = roll;
					Get_roll(40+i*37);
					level = roll;
				}
				else {
					Get_roll(41+i*37);
					rate = roll;
					Get_roll(42+i*37);
					level = roll;
				}
			}
			else if (step < (voice[20+i*37]+1)/2) {
				Get_roll(37+i*37);
				rate = roll;
				Get_roll(38+i*37);
				level = roll;
			}
			else {
				Get_roll(41+i*37);
				rate = roll;
				Get_roll(42+i*37);
				level = roll;
			}
			voice[55+2*step+i*57] = (level < (voice[54+2*step+i*57]
				& 0x007F) ? 128 : 0) + ((127 * rate)/99);
				/* DCO intermediate rate */
			voice[56+2*step+i*57] = (sustain == step ? 128 : 0) +
				(level < 64 ? level : level+4);
				/* DCO intermediate level */
		}
		Get_roll(43+i*37);
		rate = roll;
		voice[55+2*step+i*57] = 128 + ((127 * rate)/99);
			/* DCO End Rate */
		voice[56+2*step+i*57] = 0;
			/* DCO End Level */
		for(step=step+1;step<8;step++) {
			voice[55+2*step+i*57] = (127*50)/99;
			voice[56+2*step+i*57] = 0;
			/* Envelope cleanup */
		}
	}
	for (i=0; i<TOTAL_SIZE; i++)
		voice[i] = (voice[i] << 4) + (0x000F & (voice[i] >> 4));
		/* Casio sends nibbles in *reverse* order */
#endif
}

/* send_voice: send a voice ('voice') to the synth */
send_voice(voice)
char voice[];
{
	int	i,j;
	char	checksum;

#ifdef	CZ101
retry:
	midi_flush();
#endif
	send('\360');
#ifdef	DX100
	send('\103');
	send('\000');
	send('\003');
	send('\000');
	send('\135');
	checksum = 0;
	for(i=0;i<TOTAL_SIZE;i++) {
		send(voice[i]);
		checksum += ~voice[i] + 1;
	}
	checksum &= '\177';
	send(checksum);
#else
	send('\104');
	send('\000');
	send('\000');
	send('\160');
	send('\040');
	send('\140');
	if(!handshake('\360')) goto retry;
	if(!handshake('\104')) goto retry;
	if(!handshake('\000')) goto retry;
	if(!handshake('\000')) goto retry;
	if(!handshake('\160')) goto retry;
	if(!handshake('\060')) goto retry;
	for(i=0; i<TOTAL_SIZE; i++) {
		send((char)(0x000F & (voice[i] >> 4)));
		send((char)(0x000F & voice[i]));
	}
	for (i=0; i<256 - 2*TOTAL_SIZE; i++) send('\000');
#endif
	send('\367');
#ifdef	CZ101
	if(!handshake('\367')) goto retry;
#endif
}

/* receive_voice: receive a voice from the synth and place it in 'voice' */
receive_voice(voice)
char voice[];
{
	int	i,j;
#ifdef	DX100
	char	checksum, test;
#else
	char	high, low;
#endif

retry:
	midi_flush();
	send('\360');
#ifdef	DX100
	send('\103');
	send('\040');
	send('\003');
	send('\367');
	for(i=0;i<5;i++) {
		while((test=Bconin(3)) & '\200');
	}
	test = 0;
	for(i=0;i<TOTAL_SIZE;i++) {
		while((voice[i] = Bconin(3)) & '\200');
		test += ~voice[i] + 1;
	}
	test &= '\177';
	while((checksum = Bconin(3)) & '\200');
	if (test == checksum) { 
		printf("good checksum\n\r");
	}
	else {
		printf("bad checksum -- re-trying\n\r");
		goto retry;
	}
#else
	send('\104');
	send('\000');
	send('\000');
	send('\160');
	send('\020');
	send('\140');
	if (!handshake('\360')) goto retry;
	if (!handshake('\104')) goto retry;
	if (!handshake('\000')) goto retry;
	if (!handshake('\000')) goto retry;
	if (!handshake('\160')) goto retry;
	if (!handshake('\060')) goto retry;
	send('\160');
	send('\061');
	for(i=0;i<TOTAL_SIZE;i++) { 
		while((high = Bconin(3)) & '\200');
		while((low = Bconin(3)) & '\200');
		voice[i] = (high << 4) + low;
	}
	send('\367');
#endif	
}

/* write_voice: write voice 'voice' with name 'name' to the standard
	library file */
write_voice(name, voice)
char name[], voice[];
{
	int	handle;
	
	if (cur_voice >= VOICES) {
		printf("At voice number limit [%d], to save current voice:\n\r",
			VOICES);
		printf("   (1) use 'quit' option to exit program.\n\r");
		printf("   (2) rename file %s to any other name.\n\r", 
			VOICE_FILE);
		printf("   (3) restart program and save voice.\n\r");
		printf("Note: To access the voices stored in the old ");
		printf("%s it must be renamed back\n\rto %s ", VOICE_FILE,
			VOICE_FILE);
		printf("and the current %s named something else ", VOICE_FILE);
		printf("temporarily.\n\r");
		printf("-- hit any key to continue --");
		wait();
		return;
	}
	if (cur_space < 94 + strlen(name))
		error("Not enough free space on disk to store voice data");
	handle = Fopen(VOICE_FILE, READ_WRITE);
	if (handle < 0) {
		sprintf(out, "Cannot open '%s'", VOICE_FILE);
		error(out);
	}
	Fseek(0L, handle, 2);
	if (Fwrite(handle, (long)(strlen(name) + 1), name) < 0)
		error("Error while writing voice name to file");
	cur_space -= strlen(name)+1;
	if (Fwrite(handle, (long)TOTAL_SIZE, voice) < 0)
		error("Error while writing voice data to file");
	cur_space -= TOTAL_SIZE;
	Fclose(handle);
	if (cur_space < 80 + TOTAL_SIZE) {
		warn(
"WARNING: there may not be enough space on the current disk for another voice");
	}
	if (cur_voice == VOICES-1) {
		printf("WARNING: voice number limit of %d reached\n\r", VOICES);
		printf("To save more voices:\n\r");
		printf("   (1) use 'quit' option to exit program.\n\r");
		printf("   (2) rename file %s to any other name.\n\r",
			VOICE_FILE);
		printf("   (3) restart program.\n\r");
		printf("Note: To access the voices stored in the old ");
		printf("%s it must be renamed back\n\rto %s ", VOICE_FILE,
			VOICE_FILE);
		printf("and the current %s named something else ", VOICE_FILE);
		printf("temporarily.\n\r");
		printf("-- hit any key to continue --");
		wait();
	}
}

/* scan_name: read a voice name from a file with handle 'handle' into 'name',
	returning the success of the operation */
int
scan_name(handle, name)
int	handle;
char	name[];
{
	if (Fread(handle, 1L, name) == 0) {
		name[0] = '\0';
	}
	else do {
		name++;
		if (Fread(handle, 1L, name) == 0) {
		warn("unexpected end-of-file in voice file -- check voices");
			return(BAD);
		}
	} while(*name != '\0');
	return(OK);
}

/* scan_voice: read a voice from a file with handle 'handle' into 'voice',
	returning the success of the operation */
int
scan_voice(handle, voice)
int	handle;
char	voice[];
{
	if (Fread(handle, (long)TOTAL_SIZE, voice) != TOTAL_SIZE) {
		warn("Unexpected end-of-file in voice file -- check voices");
		return(BAD);
	}
	return(OK);
}

/* error: print message 'message', wait, and terminate */
error(message)
char *message;
{
	scr_func(CLEAR_SCR);
	printf("%s\n\r", message);
	printf("\n\r-- hit any key to exit --\n\r");
	wait();
	curses_cleanup();
	exit();
}

/* warn: print message 'message', wait, and continue */
warn(message)
char	*message;
{
	printf("\n\r%s\n\r", message);
	printf("-- hit any key to continue --\n\r");
	wait();
}

/* midi_flush: flush the MIDI input buffer */
midi_flush()
{
	while(Bconstat(3)) Bconin(3);
}

/* send: send the byte 'data' to MIDI out */
send(data)
char	data;
{
	while(!Bcostat(3));
	Bconout(3, data);
}

/* mid: find the median of 'rolls' numbers between zero and 'range' */
mid(range, rolls)
int	range, rolls;
{
	int	i, lowest, place, j;
	int	results[100];

	for(i=0; i<rolls; i++)
		results[i] = Random() % (range+1);
	for(i=0; i<rolls/2; i++) {
		lowest = range;
		for(j=0;j<rolls;j++)
			if(results[j] < lowest) {
				lowest = results[j];
				place = j;
			}
		results[place] = range;
	}
	lowest = range;
	for(j=0;j<rolls;j++)
		if(results[j] < lowest) {
			lowest = results[j];
			place = j;
		}
	return(lowest);
}

/* low: find the lowest of 'rolls' numbers in the range zero to 'range' */
low(range, rolls)
int	range, rolls;
{
	int	i, lowest, roll;

	lowest = range;
	for(i=0; i<rolls; i++) 
		if ((roll = Random() % (range+1)) < lowest) lowest = roll;
	return(lowest);
}

/* high: find the highest of 'rolls' numbers in the range zero to 'range' */
high(range, rolls)
int	range, rolls;
{
	int	i, highest, roll;

	highest = 0;
	for(i=0; i<rolls; i++) 
		if ((roll = Random() % (range+1)) > highest) highest = roll;
	return(highest);
}

/* initialize: initialize various tables and variables */
initialize()
{
	int	i, handle, slot;

	if ((handle = Fopen("KEYTAB.DTA", READ)) < 0)
		error("Cannot open 'KEYTAB.DTA'");
	if (Fread(handle, 384L, key_tbl) < 384)
		error("Read error on 'KEYTAB.DTA'");
	Fclose(handle);
	handle = Fopen(RANDOM_FILE, READ);
	if (handle < 0)
		error("Cannot open random weight file");
	weirdness = 50;
	for (i=0; i<VOICE_SIZE; i++) ranges[i] = get_int(handle);
	for (i=0; i<VOICE_SIZE; i++) {
		types[i][0] = get_int(handle);
		types[i][1] = get_int(handle);
	}
	for (i=0; i<VOICE_SIZE; i++) {
		tries[i][0] = get_int(handle);
		tries[i][1] = get_int(handle);
	}
	Fclose(handle);
#ifdef	CZ101
	handle = Fopen("CZMIDI.DTA", READ);
	if (handle < 0) error("Cannot open MIDI table file");
	for (slot=0; slot<SLOTS; slot++) for(i=0; i<100; i++) {
		if (Fread(handle, 1L, &table[slot][i]) < 1)
			error("Unexpected EOF in 'CZMIDI.DTA'");
	}
	Fclose(handle);
#endif
}

/* get_int: read an integer from the file pointed to by 'handle',
	ignoring non-numeric characters */
int
get_int(handle)
int	handle;
{
	char	data;
	int	num, in_comment;

	in_comment = FALSE;
	do {
		if (Fread(handle, 1L, &data) < 1) {
			sprintf(out, "Unexpected EOF in '%s'", RANDOM_FILE);
			error(out);
		}
		if (data == '*') in_comment = !in_comment;
	} while(data < '0' || data > '9' || in_comment);
	num = data - '0';
	if (Fread(handle, 1L, &data) < 1) {
		sprintf(out, "Unexpected EOF in '%s'", RANDOM_FILE);
		error(out);
	}
	else if (data >= '0' && data <= '9')
		num = 10*num + data - '0';
	return(num);
}

/* get_num: read an integer from the keyboard and return it or an error code */
int	
get_num()
{
	int	i, num, leng;

	if ((leng = get_str(out)) < 0) return(ABORT);
	else if (leng == 0) return(NULL);
	num = 0;
	for(i=0; i<leng; i++) {
		if (out[i] < '0' || out[i] > '9')
			return(BAD);
		else
			num = 10*num + out[i] - '0';
	}
	return(num);
}

/* handshake: wait for character 'verify' from the CZ101 */
handshake(verify)
char	verify;
{
	int	received;

	while(!Bconstat(3));
	if ((received = (0x00FF & Bconin(3))) != (0x00FF & verify)) {
		warn("Bad handshaking from synth - Make sure MIDI channel = 1");
		sprintf(out, "Expecting %x but received %x", (int) verify,
			received);
		warn(out);
		sprintf(out, "Next byte = %x", (0x00FF & Bconin(3)));
		error(out);
		return(FALSE);
	}
	return(TRUE);
}

/* show_weirdness: display the weirdness factor on the screen */
show_weirdness()
{
	pos_cursor(40, 2);
	scr_func(CLEAR_TO_EOL);
	sprintf(out, "weirdness factor = 1 in %d", weirdness);
	pos_cursor(80 - strlen(out), 2);
	printf("%s", out);
}

/* show_voice: show the name and voice number of voice number 'voice'
	on the screen */
show_voice(voice)
{
	pos_cursor(77, 3);
	if (voice < 0) printf("---");
	else printf("%03d", voice);
	pos_cursor(40, 4);
	scr_func(CLEAR_TO_EOL);
	if (voice < 0) {
		pos_cursor(76, 4);
		printf("NONE");
	}
	else if (voice == cur_voice) {
		pos_cursor(74, 4);
		printf("RANDOM");
	}
	else {
		pos_cursor(max(78-strlen(v_names[voice]),40), 4);
		printf("'%s'", v_names[voice]);
	}
}

/* show_screen: if 'screen' equals MAIN, display the main menu,
	otherwise display the edit menu */
show_screen(screen)
int	screen;
{
	scr_func(CLEAR_SCR);
	if (screen == MAIN) {
		printf("OPTIONS\n\r");
		printf("-------\n\r");
		printf("n = generate and load new voice\n\r");
		printf("s = save currently loaded [and edited] voice\n\r");
		printf("r = reload current voice\n\r");
		printf("l = load previously saved voice\n\r");
		printf("a = audition next voice in library\n\r");
		printf("w = change weirdness factor\n\r");
		printf("e = edit voice library\n\r");
		printf("f = find voice by name\n\r");
		printf("q = quit\n\r");
		printf("\n\rOption? ");
		show_weirdness();
		sprintf(out, "current voice #   ");
		pos_cursor(80 - strlen(out), 3);
		printf("%s", out);
		show_voice(load_ptr);
	}
	else {
		printf("EDIT OPTIONS\n\r");
		printf("---- -------\n\r");
		printf("c = change voice name\n\r");
		printf("d = delete voice\n\r");
		printf("D = delete range of voices\n\r");
		printf("m = move voice\n\r");
		printf("M = move range of voices\n\r");
		printf("r = read in and append voices from alternate file\n\r");
		printf("x = execute changes and return to main menu\n\r");
		printf("q = quit edit menu, abandoning changes\n\r");
		printf("\n\rOption? ");
	}
}

/* list_voices: list the voice names and numbers available in the library */
list_voices()
{
	int	i,j;

	pos_cursor(0,14);
	scr_func(ERASE_TO_EOP);
	for(i=0; i<cur_voice; i+=10){
		for(j=0; j<10; j++) {
			pos_cursor(0, 14+j);
			scr_func(CLEAR_TO_EOL);
			if (i+j < cur_voice) 
				printf("[%3d] %s", i+j, v_names[i+j]);
		}
		printf("\n\r -- hit any key to ");
		printf("continue or ESC to ");
		printf("quit listing --");
		if ((char)Crawcin() == 27) 
			break;
	}
	pos_cursor(0,14);
	scr_func(ERASE_TO_EOP);
}

/* edit: handle edit screen I/O and call appropiate subroutines */
edit()
{
	int	i, j, e_voice, e_max, leng, name_valid, changed, target;
	int	last_slash, main_drive, start, f_handle;
	long	drv_map;
	char	key, voice[TOTAL_SIZE], v_name[80];
	char	pfname[80], path[80], file[20], main_path[80];

	changed = FALSE;
	do {
		pos_cursor(8, 3+EDIT_OPTIONS);
		scr_func(ERASE_TO_EOP);
		switch((int)(key = Crawcin())) {
		case 'c':
		case 'C':
			printf("c\n\r");
			e_voice = get_v_num(5+EDIT_OPTIONS);
			if (e_voice == ABORT) break;
			do {
				do {
					pos_cursor(0,6+EDIT_OPTIONS);
					scr_func(CLEAR_TO_EOL);
					printf("enter voice name: \007");
					printf("\n\r[ESC to abort]");
					pos_cursor(18, 6+EDIT_OPTIONS);
					leng = get_str(v_name);
				} while (leng == 0);
				if (leng < 0) break;
				name_valid = TRUE;
				for(i=0; i<leng; i++)
					if (v_name[i]<32 || v_name[i]>127) {
						printf("\n\rName has invalid ");
						printf("character in it - ");
						printf("re-try");
						name_valid = FALSE;
						break;
					}
				if (name_valid)
					for(i=0; i<cur_voice; i++) {
						if(!strcmp(v_names[i],v_name))
						{
							printf("\n\rThere is al");
							printf("ready a voice");
							printf(" named '%s'",
								v_name);
							printf(" - re-try");
							name_valid = FALSE;
							break;
						}
					}
			} while(name_valid == FALSE);
			if (leng < 0) break;
#ifdef	DX100
			strncpy(voices[e_voice]+77, v_name, 10); 
			for(i=strlen(v_name); i<10; i++) 
				voices[e_voice][77+i] = ' ';
#endif
			strcpy(v_names[e_voice], v_name);
			changed = TRUE;
			break;
		case 'd':
		case 'D':
			printf("%c\n\r", key);
			if (key == 'd')
				printf("Delete voice...\n\r");
			else
				printf("Delete from voice...\n\r");
			e_voice = get_v_num(6+EDIT_OPTIONS);
			if (e_voice < 0) break;
			if (key == 'D') {
				printf("\n\r");
				scr_func(CLEAR_TO_EOL);
				printf("up through voice...\n\r");
				do {
					e_max = get_v_num(8+EDIT_OPTIONS);
					if (e_max >= 0 && e_max < e_voice) {
						sprintf( out,
	"\n\rLast voice number must be greater than first voice number [%d]",
							e_voice);
						warn(out);
					}
				} while (e_max >= 0 && e_max < e_voice);
				if (e_max < 0) break;
			} else e_max = e_voice;
			for (i = e_max+1; i < cur_voice; i++) {
				strcpy(v_names[e_voice + i - (e_max+1)],
					v_names[i]);
				voice_copy(voices[e_voice + i - (e_max+1)],
					voices[i]);
			}
			cur_voice -= e_max+1 - e_voice;
			changed = TRUE;
			break;
		case 'm':
		case 'M':
			printf("%c\n\r", key);
			if (key == 'm')
				printf("Move voice...\n\r");
			else
				printf("First voice to move...\n\r");
			e_voice = get_v_num(6+EDIT_OPTIONS);
			if (e_voice < 0) break;
			if (key == 'M') {
				printf("\n\r");
				scr_func(CLEAR_TO_EOL);
				printf("up through voice...\n\r");
				do {
					e_max = get_v_num(8+EDIT_OPTIONS);
					if (e_max >= 0 && e_max < e_voice) {
						sprintf( out,
	"\n\rLast voice number must be greater than first voice number [%d]",
							e_voice);
						warn(out);
					}
				} while (e_max >= 0 && e_max < e_voice);
				if (e_max < 0) break;
			} else e_max = e_voice;
			do {
				pos_cursor(0, 5+EDIT_OPTIONS);
				scr_func(ERASE_TO_EOP);
				if (e_max == e_voice)
				printf("move voice %d in front of voice...",
						e_voice);
				else
			printf("move voices %d to %d in front of voice...",
					e_voice, e_max);
				printf("\n\r(End of file is position %d)",
					cur_voice);
				strcpy(v_names[cur_voice++], "END OF FILE");
				target = get_v_num(7+EDIT_OPTIONS);
				cur_voice--;
				if (target > cur_voice) {
					sprintf(out, 
					"\n\r\n\rhighest legal position is %d",
						cur_voice);
					warn(out);
				} else if (target > e_voice && target <= e_max){
					printf(
				"\n\r\n\rblock cannot be moved inside itself!");
					sprintf(out,
			"\n\rLegal ranges are from 0 to %d and from %d to %d",
						e_voice, e_max+1, cur_voice);
					warn(out);
				}
			} while (target > cur_voice || 
				target > e_voice && target <= e_max);
			if (target < 0) break;
			printf("\n\rMoving voice(s)...");
			for (i = e_voice; i < e_max+1; i++) {
				if (target > e_max) {
					strcpy(v_name, v_names[e_voice]);
					voice_copy(voice, voices[e_voice]);
					for (j = e_voice+1; j < target; j++) {
						strcpy(v_names[j-1],v_names[j]);
						voice_copy(voices[j-1],
							voices[j]);
					}
					strcpy(v_names[target-1], v_name);
					voice_copy(voices[target-1], voice);
				} else {
					strcpy(v_name, v_names[i]);
					voice_copy(voice, voices[i]);
					for (j = i-1; j >=  target+i - e_voice;
						j--) {
						strcpy(v_names[j+1],v_names[j]);
						voice_copy(voices[j+1],
							voices[j]);
					}
					strcpy(v_names[target + i - e_voice],
						v_name);
					voice_copy(voices[target + i - e_voice],
						voice);
				}
			}
			changed = TRUE;
			break;
		case 'r':
			printf("r\n\r");
			Dgetpath(main_path, 0);
			drv_map = Dsetdrv(main_drive = Dgetdrv());
			do {
				name_valid = !E_OK;
				warn("Enter disk if necessary");
				pos_cursor(0, 5+EDIT_OPTIONS);
				scr_func(ERASE_TO_EOP);
				printf("[Drive:] Path/File name: ");
				printf("\n\r[ESC to abort]");
				pos_cursor(25, 5+EDIT_OPTIONS);
				if ((leng = get_str(pfname)) < 0) break;
				printf("\n\r");
				if (pfname[1] == ':') {
					if (pfname[0] >= 'a' 
						&& pfname[0] <= 'z')
						pfname[0] += 'A' - 'a';
					if (pfname[0] < 'A' || pfname[0] > 'Z'){
	warn("Bad drive; Drive letter must be in the range A-Z... re-try");
						continue;
					}
					if (!((1L<<(pfname[0]-'A')) & drv_map)){
						sprintf(out, 
				"Bad drive; Drive '%c' not installed... re-try",
							pfname[0]);
						warn(out);
						continue;
					}
					Dsetdrv(pfname[0] - 'A');
					strcpy(pfname, &pfname[2]);
				}
				if (pfname[0] == '\\') {
					last_slash = 0;
					for (i=1; i < leng; i++) 
						if (pfname[i] == '\\') 
							last_slash = i;
					for (i=0; i<last_slash; i++) 
						path[i] = pfname[i];
					path[last_slash] = '\0';
					Dsetpath(path);
					strcpy(file, &pfname[last_slash+1]);
				}
				else strcpy(file, pfname);
				if ((name_valid = Fsfirst(file, 0)) != E_OK)
					printf("'%s' not found, re-try...\n\r",
						pfname);
				else printf("'%s' found...", pfname);
			} while (name_valid != E_OK);
			if (leng < 0) break;
			if ((f_handle = Fopen(file, READ)) < 0) {
				sprintf(out, 
				"Cannot open '%s' for reading -- aborting...",
					pfname);
				warn(out);
				break;
			}
			printf("reading in voice data\n\r");
			while(TRUE) {
				if (cur_voice+1 >= VOICES) {
					sprintf(out,
			"voices number limit reached (%d) -- file read aborted",
						VOICES);
					warn(out);
					break;
				}
				if (scan_name(f_handle, v_names[cur_voice]) 
					!= OK) break;
				if (strlen(v_names[cur_voice]) > 0) {
					if (scan_voice(f_handle, 
						voices[cur_voice]) != OK) break;
					cur_voice++;
				}
				else break;
			}
			Fclose(f_handle);
			changed = TRUE;
			warn("Re-insert original disk if necessary");
			Dsetdrv(main_drive);
			Dsetpath(main_path);
			break;
		case 'x':
		case 'X':
			printf("x\n\r");
			if (changed && save_edit() < 0) break;
			else key = 'q';
			break;
		case 'q':
		case 'Q':
			printf("q\n\r");
			if (!changed) break;
			printf("Restoring library to pre-edit state from disk\n\r");
			get_voices(v_names, voices);
			break;
		default:
			printf("\007");
			printf("Bad option '%c' - re-try", key);
			break;
		}
	} while (key != 'q' && key != 'Q');
}

/* save_edit: save the edited voice library, returning a success/failure code */
int
save_edit()
{
	long	needed_space, lib_size(), *fsize;
	unsigned char	response;

	needed_space = lib_size();
	while (Fsfirst(VOICE_FILE, 0) != E_OK) {
		printf("'%s' not found.\n\r", VOICE_FILE);
		warn("Please insert voice disk.");
	}
	fsize = Fgetdta() + 26;
	if (*fsize + cur_space >= needed_space) {
		return(dump_voices());
	}
	else {
		do {
			printf("\n\rNot enough room on disk to save ");
			printf("file\n\rDo you have a formatted disk ");
			printf("with at least %ld free bytes \n\r",
				needed_space);
			printf("\tavailable to insert (y/n)? ");
			if ((response = Crawcin()) == 'y') {
				printf("y\n\r");
				warn("Insert disk");
				check_disk();
				if (Fsfirst(VOICE_FILE, 0) == E_OK) {
					fsize = Fgetdta() + 26;
				} else {
					fsize = Fgetdta() + 26;
					*fsize = 0;
				}
				if (cur_space + *fsize > needed_space) {
					return(dump_voices());
					break;
				}
			} else {
				printf("n\n\r");
				printf("Aborting save...\n\r");
				printf("It is recommended that you ");
				printf("quit out of this program and ");
				printf("\n\rcreate enough space on a ");
				printf("disk to save the file as ");
				printf("desired\n\r");
				printf("Hit any key to continue...");
				wait();
				break;
			}
		} while ((response = Crawcin()) == 'y');
	}
}

/* lib_size: return the size of the voice library (in bytes) */
long
lib_size()
{
	int	i;
	long	size;
		
	for (i=0, size=0; i < cur_voice; i++) {
		size += strlen(v_names[i]) + 1;
		size += TOTAL_SIZE;
	}
	return(size);
}

/* search: search the voice library names for a match with 's_str', handling
	special cases in 's_str', starting from voice 'from' */
int
search(s_str, from)
char	*s_str;
int	from;
{
	int	found, target_len;
	char	ps_str[80], rs_str[80], ss_str[80], srs_str[80];

	if (extract(s_str, ps_str, rs_str) == NULL) return(from);
	for (found=from; found < cur_voice; found++) {
		if (substr(v_names[found], ps_str)) {
			strcpy(srs_str, rs_str);
			do {
				if (extract(srs_str, ss_str, srs_str) == NULL) 
					return(found);
			} while (substr(v_names[found], ss_str));
		}
	}
	return(found);
}

/* extract: break string 'raw_str' into an initial search string ('fs_str')
	and a remnant string ('rs_str') returning NULL if 'raw_str' is empty,
	!NULL otherwise */
int
extract(raw_str, fs_str, rs_str)
char	*raw_str, *fs_str, *rs_str;
{
	if (*raw_str == '\0') return(NULL);
	if (*raw_str == AND_CHAR) *raw_str++;
	while (*raw_str != '\0' && *raw_str != AND_CHAR)
		*fs_str++ = *raw_str++;
	*fs_str = '\0';
	while ((*rs_str++ = *raw_str++) != '\0');
	return(!NULL);
}

/* substr: determine if pattern 'pattern' exists in string 'field',
	returning TRUE if so, FALSE otherwise */
int
substr(field, pattern)
char	*field, *pattern;
{
	int	i, reps, leng;
	
	if ((leng = strlen(pattern)) == 0) return(TRUE);
	reps = strlen(field) - leng;
	for (i=0; i <= reps; i++) 
		if (!strncmp(field+i, pattern, leng)) return(TRUE);
	return(FALSE);
}

/* check_disk: determine free space on disk */
check_disk()
{
	struct { long free_c, tot_c, sec_siz, sec_in_c; } disk_info;

	printf("checking disk usage\n\r");	
	Dfree(&disk_info, 0);
	cur_space = disk_info.free_c * disk_info.sec_in_c * disk_info.sec_siz;
}

/* dump_voices: actually write the voice library to disk, an error *should*
	not be possible at this point.  Nonetheless, a success/failure code
	is returned */
int
dump_voices()
{
	int	f_handle, i;
	long	f_start_leng, offset, incr;
	char	response;

	if (Fsfirst(VOICE_FILE, 0) != E_OK) f_start_leng = 0;
	else {
		f_start_leng = *(long *)(Fgetdta() + 26);
		Fdelete(VOICE_FILE);
		printf("'%s' deleted\n\r", VOICE_FILE);
	}
	do {
		if ((f_handle = Fcreate(VOICE_FILE, 0)) < 0) {
			printf("Cannot create voice file.\n\r");
			printf("Ensure disk is inserted and ");
			printf("not write protected.\n\r");
			printf("Re-try (y/n)? ");
			if ((response = Crawcin()) == 'y') {
				printf("y\n\r");
				continue;
			} else {
				printf("n\n\r");
				sprintf(out, 
		"'%s' deleted/not found and current voices NOT saved!",
					VOICE_FILE);	
				warn(out);
				return(BAD);
			}
		} else {
			printf("'%s' created.\n\r", VOICE_FILE);
			Fclose(f_handle);
		}
	} while (f_handle < 0);
	f_handle = Fopen(VOICE_FILE, READ_WRITE);
	if (f_handle < 0) {
		sprintf(out, "Cannot open '%s' -- Aborting save", VOICE_FILE);
		warn(out);
		return(BAD);
	}
	printf("'%s' opened for reading/writing...\n\r", VOICE_FILE);
	pos_cursor(0, 24);
	printf("Saving voice number ");
	for (i = 0, offset = 0; i < cur_voice; i++) {
		pos_cursor(20, 24);
		scr_func(CLEAR_TO_EOL);
		printf("%d", i);
		incr = strlen(v_names[i]) + 1;
		if (Fwrite(f_handle, incr, v_names[i]) < incr)
			error("Unknown write error on voice file - bye!");
		offset += incr;
		if (Fwrite(f_handle, (long) TOTAL_SIZE, voices[i]) < 0)
			error("Unknown write error on voice file - bye!");
		offset += TOTAL_SIZE;
	}
	cur_space += f_start_leng;
	cur_space -= offset;
	Fclose(f_handle);
	return(OK);
}

/* get_v_num: interact with the user to get a voice number and return it
	(putting prompts at screen line 'curs_y') */
int
get_v_num(curs_y)
int	curs_y;
{
	int	v_num;

	do {
		pos_cursor(0,curs_y);
		scr_func(CLEAR_TO_EOL);
		printf("enter voice number: \n\r");
		printf("[ESC to abort, RETURN to list, voices 0-%d available]", 
			cur_voice-1);
		pos_cursor(20, curs_y);
		if ((v_num = get_num()) == NULL)
			list_voices();
		if (v_num == BAD)
			printf("\n\r\n\rBad input - re-try");
	} while (v_num != ABORT &&	
		(v_num < 0 || v_num >= cur_voice));
	return(v_num);
}

/* func: call the appropiate voice parameter determining function and 
	return its value */
int
func(i,b)
int	i,b;
{
	switch(types[i][b]) {
	case 1:
		return(high(ranges[i], tries[i][b]));
		break;
	case 2:
		return(mid(ranges[i], tries[i][b]));
		break;
	case 3:
	default:
		return(low(ranges[i], tries[i][b]));
		break;
	}
}

/* get_roll: determine whether func should be called with a normal or
	weird value and return what func returns */
int
get_roll(a)
int	a;
{
	if (Effect) return(func(a,0));
	else return(func(a,1));
}

/* voice_copy: copy voice 'v2' into voice 'v1' */
voice_copy(v1, v2)
char	*v1, *v2;
{
	int i;

	for (i=0; i<TOTAL_SIZE; i++) *v1++ = *v2++;
}

#ifdef	DX100
/* round: return the nearest integral frequency ratio to ratio 'freq' */
int
round(freq)
int	freq;
{
	if (freq < 2) return(0);
	if (freq < 6) return(4);
	if (freq < 9) return(8);
	if (freq < 12) return(10);
	if (freq < 15) return(13);
	if (freq < 18) return(16);
	if (freq < 21) return(19);
	if (freq < 24) return(22);
	if (freq < 27) return(25);
	if (freq < 30) return(28);
	if (freq < 33) return(31);
	if (freq < 36) return(34);
	if (freq < 38) return(36);
	if (freq < 41) return(40);
	if (freq < 45) return(42);
	if (freq < 64) return(45);
	return(4);
}
#endif
