#ifndef lint
static char rcsid[] = "$Id: glib.c,v 1.9 93/03/29 00:00:00 lee Exp $";
#endif
/*
 * GLIB - a Generic LIBrarian and editor for synths
 *
 * Tim Thompson's original with modifications by Michael Kesti,
 *	Greg Lee, Alan Bland, Scott Snyder, Mark Rinfret
 * $Log:	glib.c,v $
 * Revision 1.9  93/03/29  00:00:00  lee
 * XView version for Linux
 *
 * Revision 1.8  92/01/16  00:00:00  lee
 * SoundBlaster support for Linux
 *
 * Revision 1.7  89/05/14  13:44:36  lee
 * fixed bug that caused 2 redisplays in editor when vis function set Redraw
 * changed 'W' command to allow initial spaces in file name
 * added 'V' command to write screen pic of voice parameters
 * 
 * Revision 1.6  89/05/06  17:13:24  lee
 * release to comp.sources.misc
 * 
 */
#define MAXSYNTHS 8
#ifndef PDIR
#define PDIR "/usr/midi/Plib"
#endif

#include "glib.h"
#include <ctype.h>
#include <unistd.h>

#ifdef XGL
#ifndef NOGLSS
#define GLSS
#endif
#include <xview/font.h>
#include <xview/icon.h>
#include <xview/scrollbar.h>
#include <xview/notice.h>
#ifdef MIDIIN
#include <xview/notify.h>
#endif

static unsigned short xgl_bits[] = {
#include "xgl.icon"
};
#include "note.bits"
#include "left.bits"
#include "right.bits"

#ifdef GLSS
#include <xview/cms.h>
#include <sspkg/drawobj.h>
#include <sspkg/canshell.h>
#include <sspkg/grip.h>
#endif

#else
#define TRUE 1
#define FALSE 0
#endif

char *Reason = "";

int Currrow = 0;	/* at top of screen, for messages */
int Libbank = 0;	/* from 0 to LIBBANKS-1, is the current library bank*/
int Nsynths = 0;

int Lastvalue = 0;

char *Currdata;
/* can the data in the current editing buffer be uploaded to the synth? */
static int Global_can_play_data_flag = 0;
/* has the current data already been uploaded to the synth? (the synth's
 * "Sendedit" routine may set this to false after a failed attempt to
 * upload a patch)
 */
int Global_chorus_depth = 0;
int Global_chorus_spread = 16;
int Global_reverberation = 0;
int Global_main_volume = 90;
int Global_expression = 100;
int Global_tuning = -600;

int Global_can_play_note_flag = 0;
/* point to the patch data for the "Datain" and "Dataout" routines
 * (do try to keep this pointed at the right place, Greg)
 */
static char *Global_current_edit_data;

#ifdef XGL
/* is the edit window up? */
static int currently_editing = 0;
#endif
char *Yankdata;		/* current 'yank' buffer (middle of screen) */

struct peredinfo *PE;	/* array of per-editor miscellany */

char Buff[BUFSIZ];
static char curr_fname[100] = "";
int Redraw = 0;		/* if non-0, edit screen is completely redrawn. */
			/* parameter functions can make use of this. */
int Negparm = 0;	/* if non-0, sense of inc/dec is reversed. */
			/* parameter functions can make use of this. */
int Changed = 0;
int Metavalue = 0;	/* if set to non-0 by parameter function, change in
			 * value does not require re-uploading voice to synth */
int DataID = 0;		/* Data Id for file write and read operations */

/* All the global values below are set as appropriate for the */
/* synthesizer currently being dealt with. */

int Nvoices = 0;
int Voicesize =0;
int Namesize = 0;
int Libindex = 0;		/* from 0 to Nvoices-1 */
int Synindex;		/* from 0 to Nvoices-1 */
int Channel;
int Editrow;		/* from 0 to NUMONSCREEN-1 */
int Editcol;		/* 0==synth, 1==library */
char *Libdata;		/* current library data (includes all LIBBANKS) */
			/* ie. the stuff on the right side of the screen */
char *Syndata;		/* current synth data (1 bank), ie. the left side */
struct paraminfo *P;	/* list of parameter info */
struct labelinfo *L;	/* arbitrary screen labels for edit screen */
int Synthnumber = -1;
char *Synthname;
char *Synthsuffix;
#ifdef SSS
int *scr_p;
int NParams;
#endif
#ifdef KAWAIK1
char sngnames[64][11];
#endif
#ifdef FMCARD
#include "sblast.h"
char sbnames[128][SBNAMELEN+1];
#endif

/* is sysex input redirected to come from a file? */
int synthinfileflag = 0;
/* is sysex output redirected to go to a file? */
int synthoutfileflag = 0;
int fditflag = 0;
char Syinfname[100], Syofname[100];

int (*Sendedit)();	/* function to send parameters to synth's edit buffer*/
int (*Datain)();	/* convert data from file-storage format to the */
			/* format stored in the P[] parameter array (p_val) */
int (*Dataout)();	/* reverse of Datain */
int (*Sendone)();	/* function to send one (permanent) voice to synth */
int (*Sendbulk)();	/* function to send bulk dump to synth */
int (*Getbulk)();	/* reverse of Sendbulk */
char *(*Nameof)();	/* pulls voice name out of file-storage data */
int (*Setnameof)();	/* reverse of Nameof */
char *(*Numof)();	/* convert voice number to on-screen text */
int (*Cvtnum)();	/* convert visible voice number to std. format */
int (*Cvtanum)();	/* convert alphanumeric voice number to std. format */
			/* should never define both Cvtnum and Cvtanum */

char *vnumtext();

#ifdef SSS
# define PARAMAT(r, c) (*(scr_p + Cols*(r) + (c)))
#endif

#ifdef XGL

Frame frame, edit_frame[MAXSYNTHS];
Rect edit_rect;
Panel panel, edit_panel[MAXSYNTHS];
Panel_item edit_voice_label[MAXSYNTHS];
Panel_item edit_play_note[MAXSYNTHS];

#ifdef GLSS
Cms cms;
Canvas_shell	shell[MAXSYNTHS];
Grip grip[MAXSYNTHS];
#endif

Panel_item lib_bank, syn_bank, yank_name;
Panel_item synth_choice, save_text_item;
Menu save_menu;
Panel_item play_note;
Xv_notice misc_notice;
Xv_Font font_fixed;
Server_image note_image;
static int font_fixed_width;
void quit_xgl();
Menu filelist();
void readall();
void choose_bank();
void choose_synth();
void yank_from();
int libinteract();

void
message(char *prompt)
{
	xv_set(misc_notice,
		NOTICE_MESSAGE_STRING, prompt,
		XV_SHOW, TRUE, NULL);
}
static int
check_synth()
{
	if (Synthnumber >=0) return(TRUE);
	message("please select a synth first");
	return(FALSE);
}
static void
unshow_edit(Panel_item item, Event *ev)
{
	Frame frame = (Frame)xv_get(item, PANEL_CLIENT_DATA);
	xv_set(frame, XV_SHOW, FALSE, NULL);
	currently_editing = 0;
}

static int
selsynth(Panel_item it, Event *ev)
{	int n, u;

	if ((u=Synthnumber) >= 0) unsetedit(Synthnumber);
	for (n = 0; n < Nsynths; n++) {
		setedit(n);
		if (!strcmp(Synthname, (char *)xv_get(it, PANEL_LABEL_STRING))) {
			if (Synthnumber == u) return(XV_OK);
			if (u >= 0) {
				xv_set(edit_frame[u], XV_SHOW, FALSE, NULL);
				currently_editing = 0;
			}
			xv_set(synth_choice, PANEL_VALUE, Synthnumber, NULL);
			syntodisplay(0);
			libtodisplay(0);
			updatedisplay();
			pryankname();
			return(XV_OK);
		}
		unsetedit(n);
	}
	return(XV_ERROR);
}

#ifdef LINUXIO
#ifdef MIDIIN

Notify_value
read_it(Notify_client client, int fd)
{
	extern void midimessage();
	extern int midipending();

	if (Editcol || !i_can_play()) flushmidi();
	else while (midipending()) midimessage();
	return(NOTIFY_DONE);
}

void
register_my_input_func()
{
	Notify_client client = (Notify_client)10101;
	extern int opensb();
	extern int seq_fd;

	if (!opensb()) return;
	(void)notify_set_input_func(client, read_it, seq_fd);
}
void
unregister_input()
{
	Notify_client client = (Notify_client)10101;
	extern int seq_fd;

	(void)notify_set_input_func(client, NOTIFY_FUNC_NULL, seq_fd);
	closesb();
	Global_can_play_note_flag = 0;
}

#endif
#endif

#endif

/* measure to work around Xview 3.2 bug */
#define COPS_ON_BUTTON

main(int argc, char **argv)
{
	int n;
#ifdef XGL
	Menu fmenu, libmenu;
	Rect *rect;
	Panel_item save_button;
	void save_file();
	Panel_item trsyn_button, trlib_button, upload_button, upload_all_button;
	int trans_syn(), trans_lib(), upload_syn(), upload_all_syn();
	void voice_action(), space_note();
	void main_volume_proc(), chorus_depth_proc(), tuning_proc();
	void expression_proc(), reverberation_proc();
	Panel_setting new_save_file();
	Panel_setting edit_play_action();
	Icon icon;
	Rect icon_rect;
	Server_image closed_image;
	Server_image left_image, right_image;

	xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);

	frame = (Frame)xv_create(XV_NULL, FRAME,
		XV_X,		50,
		XV_Y,		50,
		XV_WIDTH,	500,
		FRAME_LABEL,	argv[0],
		NULL);

	xv_set(frame,
		WIN_EVENT_PROC,	space_note,
		WIN_CONSUME_EVENTS,	WIN_ASCII_EVENTS, NULL,
		NULL);

	closed_image = xv_create(XV_NULL, SERVER_IMAGE,
		XV_WIDTH, 32,
		XV_HEIGHT, 36,
		SERVER_IMAGE_BITS, xgl_bits,
		NULL);
	icon = (Icon)xv_create(frame, ICON,
		ICON_IMAGE,	closed_image,
		ICON_TRANSPARENT, TRUE,
		NULL);
	icon_rect.r_top = 10;
	icon_rect.r_left = 80;
	icon_rect.r_width = 32;
	icon_rect.r_height = 36;
	xv_set(frame,
		FRAME_ICON, icon,
		FRAME_CLOSED_RECT, &icon_rect,
		NULL);

	note_image = (Server_image)xv_create(XV_NULL, SERVER_IMAGE,
		XV_WIDTH,	note_width,
		XV_HEIGHT,	note_height,
		SERVER_IMAGE_X_BITS, note_bits,
		NULL);
	left_image = (Server_image)xv_create(XV_NULL, SERVER_IMAGE,
		XV_WIDTH,	left_width,
		XV_HEIGHT,	left_height,
		SERVER_IMAGE_X_BITS, left_bits,
		NULL);
	right_image = (Server_image)xv_create(XV_NULL, SERVER_IMAGE,
		XV_WIDTH,	right_width,
		XV_HEIGHT,	right_height,
		SERVER_IMAGE_X_BITS, right_bits,
		NULL);

	panel = (Panel)xv_create(frame, PANEL,
		XV_X,		0,
		XV_Y,		0,
		NULL);

	(void)xv_create(panel, PANEL_BUTTON,
		PANEL_LABEL_STRING,	"quit",
		PANEL_NOTIFY_PROC,	quit_xgl,
		NULL);

	(void)xv_create(panel, PANEL_MESSAGE,
		PANEL_LABEL_STRING,	"    patch files: ",
		NULL);

#else
	hello();
	windinit();
#endif
	if (argc == 1) n = chdir(PDIR);
	else n = chdir(argv[1]);
	if (n == -1) {
		fprintf(stderr, "no such directory\n");
		bye();
		exit(1);
	}
	initstuff();

#ifdef XGL
	font_fixed = (Xv_Font)xv_find(frame, FONT,
		FONT_FAMILY,	FONT_FAMILY_DEFAULT_FIXEDWIDTH,
		FONT_STYLE,	FONT_STYLE_NORMAL,
		FONT_SCALE,	WIN_SCALE_SMALL,
		NULL);
	if (font_fixed == (Xv_Font)NULL) {
		fprintf(stderr, "no fixed font\n");
		quit_xgl();
	}
	font_fixed_width = (int)xv_get(font_fixed, FONT_DEFAULT_CHAR_WIDTH);

#ifdef GLSS
	cms = (Cms)xv_create(XV_NULL, CMS,
		CMS_SIZE,		CMS_CONTROL_COLORS + 1,
		CMS_CONTROL_CMS,	TRUE,
		CMS_NAMED_COLORS,	"black", NULL,
		NULL);
	if (cms == (Cms)NULL) {
		fprintf(stderr, "couldn't get color map\n");
		exit(1);
	}
#endif
	for (n = 0; n < Nsynths; n++) {
		setedit(n);
		new_edit_frame();
		fmenu = filelist();
		(void)xv_create(panel, PANEL_BUTTON,
			PANEL_LABEL_STRING,	Synthname,
			PANEL_NOTIFY_PROC,	selsynth,
			PANEL_ITEM_MENU,	fmenu,
			NULL);
		unsetedit(n);
	}
	if (Nsynths > 1) Synthnumber = -1;
	else Synthnumber = 0;
#else
	if ( Nsynths == 0 )
		windstr("Hey, the E array is empty?");
	else if ( Nsynths == 1 ) {
		/* If there's only 1 synth, don't bother asking */
		setedit(0);
		libinteract();
	}
	else {
		while ( (n=choosesynth()) >= 0 ) {
			setedit(n);
			libinteract();
			unsetedit(n);
		}
	}
#endif

#ifdef XGL
	(void)xv_create(panel, PANEL_CHOICE,
		PANEL_NEXT_ROW,		-1,
		PANEL_LABEL_STRING,	"Library",
		PANEL_CHOICE_STRINGS,	"bank 1", "bank 2", "bank 3", NULL,
		PANEL_NOTIFY_PROC,	choose_bank,
		PANEL_VALUE,		0,
		NULL);

	if (Nsynths > 1)
	synth_choice = (Panel_item)xv_create(panel, PANEL_CHOICE,
		PANEL_LABEL_STRING,	"Synth",
		PANEL_CHOICE_STRINGS,	"1", "2", "3", "4", "5", "6", "7", "8", NULL,
		PANEL_CHOOSE_NONE,	TRUE,
		PANEL_NOTIFY_PROC,	choose_synth,
		PANEL_VALUE,		Synthnumber,
		NULL);

	libmenu = (Menu)xv_create(XV_NULL, MENU_CHOICE_MENU,
		MENU_STRINGS, "edit", "put", "swap", NULL,
		MENU_NOTIFY_PROC,	voice_action,
		NULL);

	syn_bank = (Panel_item)xv_create(panel, PANEL_LIST,
		XV_SHOW,		(Nsynths > 1)? FALSE : TRUE,
		PANEL_NEXT_ROW,		-1,
		PANEL_LIST_TITLE,	"synth 1",
		PANEL_CHOOSE_NONE,	TRUE,
		PANEL_CHOOSE_ONE,	TRUE,
		PANEL_NOTIFY_PROC,	libinteract,
		PANEL_LIST_DISPLAY_ROWS,	NUMONSCREEN,
		PANEL_LIST_WIDTH,	160,
		NULL);

#ifndef COPS_ON_BUTTON
	xv_set(syn_bank, PANEL_ITEM_MENU, libmenu, NULL);
#endif

	save_menu = (Menu)xv_create(XV_NULL, MENU,
		MENU_ACTION_ITEM,	"(what name??)", save_file,
		NULL);
	save_button = (Panel_item)xv_create(panel, PANEL_BUTTON,
		PANEL_LABEL_STRING,	"      save   -->",
		PANEL_ITEM_MENU,	save_menu,
		NULL);
	rect = (Rect *)xv_get(save_button, XV_RECT);

	lib_bank = (Panel_item)xv_create(panel, PANEL_LIST,
		XV_SHOW,		(Nsynths > 1)? FALSE : TRUE,
		PANEL_LIST_TITLE,	"bank 1",
		PANEL_CHOOSE_NONE,	TRUE,
		PANEL_CHOOSE_ONE,	TRUE,
		PANEL_NOTIFY_PROC,	libinteract,
		PANEL_LIST_DISPLAY_ROWS,	NUMONSCREEN,
		PANEL_LIST_WIDTH,	160,
		NULL);
#ifndef COPS_ON_BUTTON
	xv_set(lib_bank, PANEL_ITEM_MENU, libmenu, NULL);
#endif

	save_text_item = (Panel_item)xv_create(panel, PANEL_TEXT,
		PANEL_LABEL_STRING,	"file:",
		PANEL_VALUE_DISPLAY_LENGTH, 10,
		PANEL_VALUE,		"",
		PANEL_NOTIFY_PROC,	new_save_file,
		XV_X,			rect->r_left,
		XV_Y,			rect_bottom(rect) + 10,
		NULL);
	rect = (Rect *)xv_get(save_text_item, XV_RECT);

#ifdef COPS_ON_BUTTON
	trsyn_button = (Panel_item)xv_create(panel, PANEL_BUTTON,
		PANEL_LABEL_STRING,	"lib operations",
		PANEL_ITEM_MENU,	libmenu,
		XV_X,			rect->r_left,
		XV_Y,			rect_bottom(rect) + 10,
		NULL);
	rect = (Rect *)xv_get(trsyn_button, XV_RECT);
#endif

	play_note = (Panel_item)xv_create(panel,
		PANEL_TEXT,
		PANEL_LABEL_IMAGE,	note_image,
		PANEL_VALUE_DISPLAY_LENGTH, 1,
		PANEL_MASK_CHAR,	'*',
		PANEL_NOTIFY_LEVEL,	PANEL_ALL,
		PANEL_NOTIFY_PROC,	edit_play_action,
		XV_X,			rect->r_left + 20,
		XV_Y,			rect_bottom(rect) + 20,
		NULL);
	xv_set(panel,
		PANEL_CARET_ITEM, play_note,
		NULL);
	rect = (Rect *)xv_get(play_note, XV_RECT);
	rect->r_left -= 20;

	yank_name = xv_create(panel, PANEL_MESSAGE,
		PANEL_LABEL_STRING,	"  [  (yank buffer)  ]",
		PANEL_NOTIFY_PROC,	yank_from,
		XV_X,			rect->r_left-8,
		XV_Y,			rect_bottom(rect) + 10,
		NULL);
	rect = (Rect *)xv_get(yank_name, XV_RECT);
	rect->r_left += 8;

	trsyn_button = (Panel_item)xv_create(panel, PANEL_BUTTON,
		PANEL_LABEL_IMAGE,	left_image,
		PANEL_NOTIFY_PROC,	trans_syn,
		XV_X,			rect->r_left + 10,
		XV_Y,			rect_bottom(rect) + 10,
		NULL);

	trlib_button = (Panel_item)xv_create(panel, PANEL_BUTTON,
		PANEL_LABEL_IMAGE,	right_image,
		PANEL_NOTIFY_PROC,	trans_lib,
		XV_X,			rect->r_left + 10
			+ xv_get(trsyn_button, PANEL_LABEL_WIDTH) + 22,
		XV_Y,			rect_bottom(rect) + 10,
		NULL);
	rect = (Rect *)xv_get(trsyn_button, XV_RECT);
	rect->r_left -= 10;

	upload_button = (Panel_item)xv_create(panel, PANEL_BUTTON,
		PANEL_LABEL_STRING,	"  upload one  ",
		PANEL_NOTIFY_PROC,	upload_syn,
		XV_X,			rect->r_left,
		XV_Y,			rect_bottom(rect) + 10,
		NULL);
	rect = (Rect *)xv_get(upload_button, XV_RECT);

	upload_all_button = (Panel_item)xv_create(panel, PANEL_BUTTON,
		PANEL_LABEL_STRING,	"<-- upload all ",
		PANEL_NOTIFY_PROC,	upload_all_syn,
		XV_X,			rect->r_left,
		XV_Y,			rect_bottom(rect) + 10,
		NULL);
	rect = (Rect *)xv_get(upload_all_button, XV_RECT);

/** controller sliders **/
#define SLDRVS 20
#define SLDRHS 20
#define SLDRLN 75
	(void)xv_create(panel, PANEL_MESSAGE,
		PANEL_LABEL_STRING,	"V   E   C   R   T",
		XV_X,			rect->r_left + 2,
		XV_Y,			rect_bottom(rect) + 6,
		NULL);
	(void)xv_create(panel, PANEL_SLIDER,
		PANEL_DIRECTION,	PANEL_VERTICAL,
		PANEL_VALUE,		Global_main_volume,
		PANEL_MIN_VALUE,	0,
		PANEL_MAX_VALUE,	127,
		PANEL_SHOW_VALUE,	FALSE,
		PANEL_SHOW_RANGE,	FALSE,
		PANEL_SLIDER_WIDTH,	SLDRLN,
		PANEL_NOTIFY_PROC,	main_volume_proc,
		XV_X,			rect->r_left,
		XV_Y,			rect_bottom(rect) + SLDRVS,
		NULL);
	(void)xv_create(panel, PANEL_SLIDER,
		PANEL_DIRECTION,	PANEL_VERTICAL,
		PANEL_VALUE,		Global_expression,
		PANEL_MIN_VALUE,	0,
		PANEL_MAX_VALUE,	127,
		PANEL_SHOW_VALUE,	FALSE,
		PANEL_SHOW_RANGE,	FALSE,
		PANEL_SLIDER_WIDTH,	SLDRLN,
		PANEL_NOTIFY_PROC,	expression_proc,
		XV_X,			rect->r_left + SLDRHS,
		XV_Y,			rect_bottom(rect) + SLDRVS,
		NULL);
	(void)xv_create(panel, PANEL_SLIDER,
		PANEL_DIRECTION,	PANEL_VERTICAL,
		PANEL_VALUE,		Global_chorus_depth,
		PANEL_MIN_VALUE,	0,
		PANEL_MAX_VALUE,	127,
		PANEL_SHOW_VALUE,	FALSE,
		PANEL_SHOW_RANGE,	FALSE,
		PANEL_SLIDER_WIDTH,	SLDRLN,
		PANEL_NOTIFY_PROC,	chorus_depth_proc,
		XV_X,			rect->r_left + 2*SLDRHS,
		XV_Y,			rect_bottom(rect) + SLDRVS,
		NULL);
	(void)xv_create(panel, PANEL_SLIDER,
		PANEL_DIRECTION,	PANEL_VERTICAL,
		PANEL_VALUE,		Global_reverberation,
		PANEL_MIN_VALUE,	0,
		PANEL_MAX_VALUE,	127,
		PANEL_SHOW_VALUE,	FALSE,
		PANEL_SHOW_RANGE,	FALSE,
		PANEL_SLIDER_WIDTH,	SLDRLN,
		PANEL_NOTIFY_PROC,	reverberation_proc,
		XV_X,			rect->r_left + 3*SLDRHS,
		XV_Y,			rect_bottom(rect) + SLDRVS,
		NULL);
	(void)xv_create(panel, PANEL_SLIDER,
		PANEL_DIRECTION,	PANEL_VERTICAL,
		PANEL_VALUE,		Global_tuning,
		PANEL_MIN_VALUE,	-4000,
		PANEL_MAX_VALUE,	4000,
		PANEL_SHOW_VALUE,	FALSE,
		PANEL_SHOW_RANGE,	FALSE,
		PANEL_SLIDER_WIDTH,	SLDRLN,
		PANEL_NOTIFY_PROC,	tuning_proc,
		XV_X,			rect->r_left + 4*SLDRHS,
		XV_Y,			rect_bottom(rect) + SLDRVS,
		NULL);


	misc_notice = (Xv_notice)xv_create(panel, NOTICE,
		NOTICE_LOCK_SCREEN,	TRUE,
		NOTICE_MESSAGE_STRING,	Buff,
		NOTICE_NO_BEEPING,	TRUE,
		XV_SHOW,		FALSE,
		NULL);

	window_fit(panel);
	window_fit(frame);

	if (Nsynths == 1) {
		syntodisplay(0);
		libtodisplay(0);
		updatedisplay();
	}
	xv_main_loop(frame);
#endif
	bye();
	exit(0);
}

#ifdef XGL
void
quit_xgl()
{
	/*xv_destroy_safe(frame);*/
	exit(0);
}

void
choose_bank(Panel_item it, int value, Event *ev)
{
	Libbank = value;
	libtodisplay(0);
	updatedisplay();
	if (Editcol && currently_editing) readyedit();
}

int
check_selected(int notify)
{
	int first_selected = (int)xv_get(lib_bank, PANEL_LIST_FIRST_SELECTED);
	if (first_selected < 0)
		first_selected = (int)xv_get(syn_bank, PANEL_LIST_FIRST_SELECTED);
	if (first_selected < 0 && notify) message("please select a voice first");
	return(first_selected);
}
void
yank_from(Panel_item it, int value, Event *ev)
{	int n;

	if (check_selected(TRUE) < 0) return;
	if (Namesize == 0) return;
	for(n=0;n<Voicesize;n++)
			Yankdata[n] = Currdata[n];
	pryankname();
}

void
choose_synth(Panel_item item, int value, Event *ev)
{
	if (value >= Nsynths) {
		xv_set(item, PANEL_VALUE, Synthnumber, NULL);
		return;
	}
	if (value == Synthnumber) return;
	if (Synthnumber >= 0) {
		unsetedit(Synthnumber);
		xv_set(edit_frame[Synthnumber], XV_SHOW, FALSE, NULL);
		currently_editing = 0;
	}
	setedit(value);
	syntodisplay(0);
	libtodisplay(0);
	updatedisplay();
	pryankname();
}
#endif /* XGL */
int
i_can_play()
{
	if (Synthnumber < 0) return(FALSE);
	if (Global_can_play_data_flag) {
		if (!Global_can_play_note_flag) {
			Global_can_play_note_flag = TRUE;
			(*Sendedit)(Global_current_edit_data);
			Changed = 0;
		}
	}
	else Global_can_play_note_flag = 0;
	if (Global_can_play_note_flag) return(TRUE);
	else return(FALSE);
}
#ifdef XGL

static int asc_notes[128] = {
	49,33,41,37,36,55,38,40,72,50,43,45,47,72,42,65,
	67,52,57,35,59,62,39,53,36,60,34,69,51,71,49,0,
	0,51,50,54,56,58,61,50,65,66,63,70,46,68,48,49,
	66,51,53,54,56,58,60,61,63,65,48,48,46,70,48,49,
	53,33,41,37,36,55,38,40,41,64,43,45,47,44,42,65,
	67,52,57,35,59,62,39,53,36,60,34,69,51,71,60,68,
	49,33,41,37,36,55,38,40,41,64,43,45,47,44,42,65,
	67,52,57,35,59,62,39,53,36,60,34,69,51,71,49,0
};
static int asc_remember[128] = {
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};

void
space_note(Xv_window win, Event *ev, Notify_arg arg)
{
	int id, vol, pitch, dest, octave = 0;

#ifdef LINUXIO
	if (event_id(ev) == WIN_MAP_NOTIFY) {
		if (win == frame) {
#ifdef MIDIIN
#ifdef XKEYS
			register_my_input_func();
#else
			(void)opensb();
#endif
#else
			(void)opensb();
#endif
		}
		return;
	}
	if (event_id(ev) == WIN_UNMAP_NOTIFY) {
		if (win == frame) {
#ifdef MIDIIN
#ifdef XKEYS
			unregister_input();
#else
			closesb();
			Global_can_play_note_flag = 0;
#endif
#else
			closesb();
			Global_can_play_note_flag = 0;
#endif
		}
		return;
	}
#endif
	if (!event_is_ascii(ev)) {
		int i;
/*printf("nonascii event %d =? %d\n", event_id(ev), WIN_UNMAP_NOTIFY);*/
		if (!event_is_down(ev)) return;
		if (check_selected(FALSE) < 0) return;
		if (event_shift_is_down(ev)) i = 8;
		else if (event_ctrl_is_down(ev)) i = 16;
		else i = 1;
		if (event_id(ev) == KEY_RIGHT(8)) { /* up arrow */
			if (Editcol) {
				if (!Libindex) return;
				if (Libindex - i < 0) Libindex = 0;
				else Libindex -= i;
			}
			else {
				if (!Synindex) return;
				if (Synindex - i < 0) Synindex = 0;
				else Synindex -= i;
			}
		}
		else if (event_id(ev) == KEY_RIGHT(10)) { /* left arrow */
			if (!Editcol) return;
			Editcol = 0;
			Synindex = Libindex
				- xv_get(xv_get(lib_bank, PANEL_LIST_SCROLLBAR),
					SCROLLBAR_VIEW_START)
				+ xv_get(xv_get(syn_bank, PANEL_LIST_SCROLLBAR),
					SCROLLBAR_VIEW_START);
		}
		else if (event_id(ev) == KEY_RIGHT(12)) { /* right arrow */
			if (Editcol) return;
			Editcol = 1;
			Libindex = Synindex
				- xv_get(xv_get(syn_bank, PANEL_LIST_SCROLLBAR),
					SCROLLBAR_VIEW_START)
				+ xv_get(xv_get(lib_bank, PANEL_LIST_SCROLLBAR),
					SCROLLBAR_VIEW_START);
		}
		else if (event_id(ev) == KEY_RIGHT(14)) { /* down arrow */
			if (Editcol) {
				if (Libindex == Nvoices - 1) return;
				if (Libindex + i > Nvoices - 1) Libindex = Nvoices - 1;
				else Libindex += i;
			}
			else {
				if (Synindex == Nvoices - 1) return;
				if (Synindex + i > Nvoices - 1) Synindex = Nvoices - 1;
				else Synindex += i;
			}
		}
		else return;

		updatedisplay();
		if (currently_editing) readyedit();
		return;
	}

/*printf("ascii event %d\n", event_id(ev));*/
	if (!i_can_play()) return;

	id = event_id(ev);
	if (event_is_down(ev) && id == ' ') {
		playnote(0);
		return;
	}

#ifdef LINUXIO
	if (id < 0 || id > 127) return;
	vol = getval("autovol");
       	if (!strcmp(Synthname, "SoundBlaster")) dest = 1;
       	else if (!strcmp(Synthname, "Opl3")) dest = 1;
       	else if (!strcmp(Synthname, "Ultrasound")) dest = 3;
	else dest = 2;
	pitch = asc_notes[id];
	if (!pitch) return;
	if (event_shift_is_down(ev)) octave++;
	pitch += octave*12;
	if (event_is_down(ev)) asc_remember[id] = pitch;
	else if (asc_remember[id] != pitch) {
		if (event_shift_is_down(ev) && id < 128-32 && asc_remember[id+32] >= 0) {
			pitch = asc_remember[id+32];
			asc_remember[id+32] = -1;
		}
		else if (!event_shift_is_down(ev) && id >= 32 && asc_remember[id-32] >= 0) {
			pitch = asc_remember[id-32];
			asc_remember[id-32] = -1;
		}
		else {
			int j;
			for (j = 0; j < 128 && asc_remember[j] < 0; j++) ;
			if (j < 128) {
				pitch = asc_remember[j];
				asc_remember[j] = -1;
			}
			else return;
		}
	}
	else asc_remember[id] = -1;
	if (event_is_down(ev)) playsb(dest,pitch,vol,0,1);
	else playsb(dest,pitch,vol,0,-1);
#endif
}

Panel_setting
edit_play_action(Panel_item item, Event *ev)
{
	int pitch;

	if (Synthnumber < 0) return(PANEL_NONE);
	space_note((Xv_window)NULL, ev, (Notify_arg)NULL);
	xv_set(edit_play_note[Synthnumber], PANEL_VALUE, " ", NULL);
	xv_set(play_note, PANEL_VALUE, " ", NULL);
	return(PANEL_INSERT);
}

void
voice_action(Menu menu, Menu_item item)
{	int r;
	char *choice = (char *)xv_get(item, MENU_STRING);
	int swap = !strcmp(choice, "swap");

	if (Synthnumber < 0) return;
	/* verify that a voice is selected */
	r = (int)xv_get(syn_bank, PANEL_LIST_FIRST_SELECTED);
	if (r>=0) {
		Editcol = 0;
		Synindex = r;
	}
	else {
		r = (int)xv_get(lib_bank, PANEL_LIST_FIRST_SELECTED);
		if (r<0) {
			message("please select a voice");
			return;
		}
		Editcol = 1;
		Libindex = r;
	}
	if (!strcmp(choice, "edit")) {
		if (currently_editing) return;
		readyedit();
		xv_set(edit_frame[Synthnumber], XV_SHOW, TRUE, NULL);
		xv_set(edit_panel[Synthnumber],
			PANEL_CARET_ITEM, edit_play_note[Synthnumber],
			NULL);
		currently_editing = 1;
		return;
	}
	if ( Editcol==0 ) tosyn(Synindex,Yankdata,swap);
	else tolib(Libindex,Yankdata,swap);

	if (swap) pryankname();
	updatedisplay();
	if (currently_editing) readyedit();
}

Panel_setting
new_save_file(Panel_item item, Event *ev)
{
	char *p = (char *)xv_get(item, PANEL_VALUE);
	int n = strlen(p);
	int m = strlen(Synthsuffix);
	synthoutfileflag = FALSE;
	if (!n) {
		message("null file name??");
		return;
	}
	strcpy(curr_fname, p);
	if (strcmp(Synthsuffix, curr_fname + n - m))
		strcat(curr_fname, Synthsuffix);
	xv_set((Menu_item)xv_get(save_menu, MENU_NTH_ITEM, 1),
		MENU_STRING, curr_fname, NULL);
	xv_set(save_text_item, PANEL_VALUE, curr_fname, NULL);
	return(PANEL_NEXT);
}
void
save_file(Menu menu, Menu_item item)
{
	int n = strlen(curr_fname);
	int m = strlen(Synthsuffix);
	if (!check_synth()) return;
	if (!n) {
		message("please enter a file name first");
		return;
	}
	if (m && strcmp(Synthsuffix, curr_fname + n - m)) return;
	writeall();
}
int
trans_syn(Panel_item item, Event *ev)
{
	if (!check_synth()) return;
	copyall(bankvoice(0),Syndata);
	if (!Editcol) {
		Changed = 1;
		editto(0, Editcol);
	}
	syntodisplay(0);
	if (currently_editing && !Editcol) readyedit();
	return(XV_OK);
}
int
trans_lib(Panel_item item, Event *ev)
{
	if (!check_synth()) return;
	copyall(Syndata,bankvoice(0));
	if (Editcol) {
		Changed = 1;
		editto(0, Editcol);
	}
	libtodisplay(0);
	if (currently_editing && Editcol) readyedit();
	return(XV_OK);
}
int
upload_syn(Panel_item item, Event *ev)
{
	if (!check_synth()) return(XV_OK);
	if (check_selected(TRUE) < 0) return(XV_OK);
	if ( (Panel_item)xv_get(panel, PANEL_CARET_ITEM) == save_text_item) {
		strcpy(Syofname, curr_fname);
		synthoutfileflag = TRUE;
	}
	if (!synthoutfileflag && Sendone == NULL) {
		message("can't upload single voices to this synth");
		return(XV_OK);
	}
	if ( (*Sendone)(Editcol? Libindex : Synindex, Global_current_edit_data) != 0)
		message("upload failed");
	flushmidi();
	synthoutfileflag = FALSE;
	Syofname[0] = '\0';
	return(XV_OK);
}
int
upload_all_syn(Panel_item item, Event *ev)
{
	int n;
	if (!check_synth()) return;
	if ( (Panel_item)xv_get(panel, PANEL_CARET_ITEM) == save_text_item) {
		strcpy(Syofname, curr_fname);
		synthoutfileflag = TRUE;
	}
	if (Sendbulk != NULL) {
		if ((*Sendbulk)(Syndata) != 0) message("upload failed");
		synthoutfileflag = FALSE;
		Syofname[0] = '\0';
		return(XV_OK);
	}
	else if (!synthoutfileflag && Sendone == NULL) {
		message("uploading to this synth is not implemented");
		return(XV_OK);
	}
	for ( n=0; n<Nvoices; n++ ) {
		if ( (*Sendone)(n, &(VOICEBYTE(Syndata,n,0)) ) != 0 ) {
			(void)sprintf(Buff,"can't send to synth: %s",Reason);
			xv_set(misc_notice,
				NOTICE_MESSAGE_STRING, Buff,
				XV_SHOW, TRUE, NULL);
			synthoutfileflag = FALSE;
			Syofname[0] = '\0';
			return(XV_OK);
		}
	}
	synthoutfileflag = FALSE;
	Syofname[0] = '\0';
	return(XV_OK);
}
void
tuning_proc(Panel_item item, int value, Event *ev)
{
	Global_tuning = value;
}
void
chorus_depth_proc(Panel_item item, int value, Event *ev)
{
	Global_chorus_depth = value;
}
void
main_volume_proc(Panel_item item, int value, Event *ev)
{
	Global_main_volume = value;
	Global_can_play_note_flag = 0;
}
void
expression_proc(Panel_item item, int value, Event *ev)
{
	Global_expression = value;
}
void
reverberation_proc(Panel_item item, int value, Event *ev)
{
	Global_reverberation = value;
	Global_can_play_note_flag = 0;
}

#else
/* choose a synth, returning its position in the E array */
choosesynth()
{
	int n, pick;

    retry:
	flushconsole();
	windclear();
	windgoto(2,23);
	windstr("GLIB - A Generic Librarian/Editor");

	for ( n=1; n<=Nsynths; n++ )
		libchoice(n);
	windgoto(8+Nsynths,27);
	windstr("q  -  Quit");
	windgoto(11+Nsynths,27);
	windstr("Make selection --> ");
	windrefresh();
	pick = mouseorkey();
	if ( pick == 'q' || pick == EOF )
		return(-1);
	if ( pick != MOUSE )
		pick = pick - '0';
	else {
		int row, col;
		getmouse(&row,&col);
		/* wait until mouse goes down */
		while ( statmouse() > 0 )
			;
		pick = row - 6; 
	}
	if ( pick < 1 || pick > Nsynths )
		goto retry;
	return(pick-1);
}
#endif
#ifndef XGL
libchoice(n)
{
	windgoto(6+n,27);
	(void)sprintf(Buff,"%d  -  %s",n,E[n-1].ed_name);
	windstr(Buff);
}
#endif

#ifndef SINGLEDATA

initstuff()
{
	int n, banksize, maxvsize;
	
	maxvsize = 0;
	for ( n=0; E[n].ed_name != NULL; n++ ) {
		if ( maxvsize < E[n].ed_vsize )
			maxvsize = E[n].ed_vsize;
	}
	Nsynths = n;
	Currdata = alloc( maxvsize );

	/* allocate an array of peredinfo structs */
	PE =(struct peredinfo *)alloc((int)(Nsynths*sizeof(struct peredinfo)));
	for ( n=0; n<Nsynths; n++ ) {
		banksize =  E[n].ed_nvoices * E[n].ed_vsize;

		PE[n].ed_libdata = alloc(LIBBANKS * banksize);
		clrdata(PE[n].ed_libdata, LIBBANKS * banksize);

		PE[n].ed_syndata = alloc(banksize);
		clrdata(PE[n].ed_syndata, banksize);

		PE[n].ed_yankdata = alloc(E[n].ed_vsize);
		clrdata(PE[n].ed_yankdata, E[n].ed_vsize);

		PE[n].ed_libindex = 0;
		PE[n].ed_synindex = 0;
		PE[n].ed_channel = 1;
		PE[n].ed_erow = 0;
		PE[n].ed_ecol = 0;
	}
}

#else

initstuff()
{
	int n, banksize, maxvsize;
	char *lib, *syn, *yank;
	
	maxvsize = 0;
	for ( n=0; E[n].ed_name != NULL; n++ ) {
		if ( maxvsize < E[n].ed_vsize )
			maxvsize = E[n].ed_vsize;
	}
	Nsynths = n;
	Currdata = alloc( maxvsize );

	PE =(struct peredinfo *)alloc((int)(Nsynths*sizeof(struct peredinfo)));

	/* allocate common data areas */
	banksize =  E[0].ed_nvoices * E[0].ed_vsize;

	lib = alloc(LIBBANKS * banksize);
	clrdata(lib, LIBBANKS * banksize);

	syn = alloc(banksize);
	clrdata(syn, banksize);

	yank = alloc(E[0].ed_vsize);
	clrdata(yank, E[0].ed_vsize);

	for ( n=0; n<Nsynths; n++ ) {
		PE[n].ed_libdata = lib;
		PE[n].ed_syndata = syn;
		PE[n].ed_yankdata = yank;
		PE[n].ed_libindex = 0;
		PE[n].ed_synindex = 0;
		PE[n].ed_channel = 1;
		PE[n].ed_erow = 0;
		PE[n].ed_ecol = 0;
	}
}

#endif

clrdata(data,size)
char *data;
{
	register char *p, *endp;

	p = data;
	endp = data+size;

	while ( p<endp )
		*p++ = 0;
}

#ifdef SSS
int paramcmp(p1, p2)
struct paraminfo *p1, *p2;
{
  return strcmp(p1->p_name, p2->p_name);
}

init_params()
{
  int i;

  for (NParams=0; P[NParams].p_name != NULL; NParams++)
    ;
  qsort((void *)P, NParams, sizeof(struct paraminfo), paramcmp);

  scr_p = (int *)alloc(Rows * Cols * sizeof(int));
  clrdata((char *)scr_p, Rows * Cols * sizeof(int));
  for (i=0; i<NParams; i++) {
    int r = P[i].p_vrow;
    int c = P[i].p_vcol;

    if (r >= 0 && r < Rows && c >= 0 && c < Cols)
      PARAMAT(r, c) = i+1;
  }
}
#endif

#ifndef SINGLEDATA

setedit(n)
{
	Synthnumber = n;
	Synthname = E[n].ed_name;
	Synthsuffix = E[n].ed_suffix;
	Datain = E[n].ed_din;
	Dataout = E[n].ed_dout;
	Nvoices = E[n].ed_nvoices;
	Sendedit = E[n].ed_sedit;
	Sendone = E[n].ed_sone;
	Sendbulk = E[n].ed_sbulk;
	Getbulk = E[n].ed_gbulk;
	Nameof = E[n].ed_nof;
	Numof = E[n].ed_numof;
	Cvtnum = E[n].ed_cvtnum;
	Cvtanum = E[n].ed_cvtanum;
	Setnameof = E[n].ed_snof;
	Voicesize = E[n].ed_vsize;
	Namesize = E[n].ed_nsize;
	DataID = E[n].ed_dataid;
	Libdata = PE[n].ed_libdata;
	Syndata = PE[n].ed_syndata;
	Yankdata = PE[n].ed_yankdata;
	Libindex = PE[n].ed_libindex;
	Synindex = PE[n].ed_synindex;
	Channel = PE[n].ed_channel;
	Editrow = PE[n].ed_erow;
	Editcol = PE[n].ed_ecol;
	clrdata(Currdata,Voicesize);
	Global_can_play_data_flag = 0;
	Global_can_play_note_flag = 0;
	P = E[n].ed_params;
	L = E[n].ed_labels;

#ifdef SSS
        init_params();
#endif
}

unsetedit(n)
{
	int k;

	DataID = 0;
	PE[n].ed_libindex = Libindex;
	PE[n].ed_synindex = Synindex;
	PE[n].ed_channel = Channel;
	PE[n].ed_erow = Editrow;
	PE[n].ed_ecol = Editcol;
	for ( k=0; k<Voicesize; k++ )
		PE[n].ed_yankdata[k] = Yankdata[k];
}

#else

setedit(n)
{
	Synthnumber = n;
	Synthname = E[n].ed_name;
	Synthsuffix = E[n].ed_suffix;
	Datain = E[n].ed_din;
	Dataout = E[n].ed_dout;
	Nvoices = E[n].ed_nvoices;
	Sendedit = E[n].ed_sedit;
	Sendone = E[n].ed_sone;
	Sendbulk = E[n].ed_sbulk;
	Getbulk = E[n].ed_gbulk;
	Nameof = E[n].ed_nof;
	Numof = E[n].ed_numof;
	Cvtnum = E[n].ed_cvtnum;
	Cvtanum = E[n].ed_cvtanum;
	Setnameof = E[n].ed_snof;
	Voicesize = E[n].ed_vsize;
	Namesize = E[n].ed_nsize;
	DataID = E[n].ed_dataid;
	Libdata = PE[n].ed_libdata;
	Syndata = PE[n].ed_syndata;
	Yankdata = PE[n].ed_yankdata;
	Libindex = PE[n].ed_libindex;
	Synindex = PE[n].ed_synindex;
	Channel = PE[n].ed_channel;
	Editrow = PE[n].ed_erow;
	Editcol = PE[n].ed_ecol;
	clrdata(Currdata,Voicesize);
	Global_can_play_data_flag = 0;
	Global_can_play_note_flag = 0;
	P = E[n].ed_params;
	L = E[n].ed_labels;
#ifdef SSS
        init_params();
#endif
}

unsetedit(n)
{
	int j, k;

	DataID = 0;
	for(j=0; j < Nsynths; j++) {
		PE[j].ed_libindex = Libindex;
		PE[j].ed_synindex = Synindex;
		PE[j].ed_channel = Channel;
		PE[j].ed_erow = Editrow;
		PE[j].ed_ecol = Editcol;
		for ( k=0; k<Voicesize; k++ ) {
			PE[j].ed_yankdata[k] = Yankdata[k];
		}
	}
}

#endif

#ifndef XGL
/* template - show the boxes and such on the main library screen */
template()
{
	int n, k, r;

	r = FIRSTROW-1;
	(void)sprintf(Buff,"%s",Synthname);
	n = 13 - strlen(Buff)/2;	/* center it */
	windgoto(r,n<0?0:n);
	windstr(Buff);
	r++;
	windgoto(r,0);
	for ( n=0; n<25; n++ )
		windputc('=');
	windgoto(r,53);
	for ( n=0; n<25; n++ )
		windputc('=');
	for ( n=r+1; n<(r+13); n++ ) {
		windgoto(n,0);
		windputc('|');
		windgoto(n,5);
		windputc('|');
		windgoto(n,24);
		windputc('|');

		k=53;
		windgoto(n,k);
		windputc('|');
		windgoto(n,k+5);
		windputc('|');
		windgoto(n,k+24);
		windputc('|');
	}
	windgoto(r+13,0);
	for ( n=0; n<25; n++ )
		windputc('=');
	windgoto(r+13,53);
	for ( n=0; n<25; n++ )
		windputc('=');

	windgoto(YANKROW-2,YANKCOL);
	windstr("  Yank Buffer");
	windgoto(YANKROW-1,YANKCOL);
	windstr(" ------------- ");
	windgoto(YANKROW,YANKCOL);
	windstr("               ");
	windgoto(YANKROW+1,YANKCOL);
	windstr(" ------------- ");
	windrefresh();
}

/* clear the message area */
clearmess()
{
	int n;
	for(n=1;n<(FIRSTROW-1);n++)
		winderaserow(n);
	Currrow = 0;
}
#endif

/* set the current voice (ie. the synth's edit buffer) to the indicated */
/* voice.  c==0 is the synth (left) side, c==1 is the library (right) side. */
editto(r,c)
{
	int voicenum;

#ifdef XGL
	Editrow = 0;
#else
	/* Clear the existing '*' */
	editchar(' ',Editrow,Editcol);
	editchar('*',Editrow=r,Editcol=c);
	Changed = 1;
#endif
	if ( Editcol==0 ) {
		/* we're on the synth side */
		voicenum = Editrow+Synindex;
		tocurrent(Syndata,voicenum);
	}
	else {
		/* we're on the lib side */
		voicenum = Editrow+Libindex;
		tocurrent(bankvoice(0),voicenum);
	}
}

#ifndef XGL
editchar(ec,r,c)
{
	r = r + FIRSTROW + 1;
	if ( c == 0 )
		c = LEFTSIDE-1;
	else
		c = RIGHTSIDE-1;
	windgoto(r,c);
	windputc(ec);
	windrefresh();
}
#endif

#ifdef XGL
int
libinteract(Panel_item item,
	char *rowlabel,
	Xv_opaque clidata,
	Panel_list_op op,
	Event *ev,
	int row)
{
	int other_select;

	if (op != PANEL_LIST_OP_SELECT) return(XV_OK);
	if (item == syn_bank) {
		Editcol = 0;
		Synindex = row;
		Global_current_edit_data = &(VOICEBYTE(Syndata,Synindex,0));
	}
	else {
		Editcol = 1;
		Libindex = row;
		Global_current_edit_data = bankvoice(Libindex);
	}
	other_select = (int)xv_get(Editcol? syn_bank : lib_bank, PANEL_LIST_FIRST_SELECTED);
	if (other_select >= 0) {
		xv_set(Editcol? syn_bank : lib_bank, XV_SHOW, FALSE, NULL);
		xv_set(Editcol? syn_bank : lib_bank,
			PANEL_LIST_SELECT, other_select, FALSE, NULL);
		xv_set(Editcol? syn_bank : lib_bank, XV_SHOW, TRUE, NULL);
	}
	Changed = 1;
	editto(0, Editcol);
	if (currently_editing) readyedit();
	return(XV_OK);
}

readyedit()
{
	char *p, *data;

	if (Namesize == 0) p = NULL;
	else p = (*Nameof)(Currdata);

	if ( Editcol==0 ) {
		Global_current_edit_data = data = &(VOICEBYTE(Syndata,Synindex,0));
		editdata(p,data);
	}
	else {
		Global_current_edit_data = data = bankvoice(Libindex);
		editdata(p,data);
	}
#ifdef GLSS
	readywave();
#endif
}

#else
/* control interaction on the main library bank screen */
libinteract()
{
	int c, n, swap, voicenum, maxindex, q;
	char *p, *data;

	flushmidi();
	drawall();
	for ( ;; ) {
		Currrow = 0;
		winderaserow(Currrow);
		windgoto(Currrow,0);
		windstr("Command --> ");
		windrefresh();
		
		c = mouseorkey();
		if ( c == MOUSE ) {
			libmouse();
			continue;
		}

		if ( isprint(c) )
			windputc(c);
		clearmess();
		switch ( c ) {
		case ' ':
			if (i_can_play()) playnote(1);
			break;
		case '\n':
#ifndef macintosh
		case '\r':
#endif
			/* ignore */
			break;
		case EOF:
		case 'q':
			return;
		case CH_REDRAW:
			drawall();
			break;
		case 's':
		case 'p':
			swap = (c=='s')?1:0;
			if ( Editcol==0 )
				tosyn(Synindex+Editrow,Yankdata,swap);
			else
				tolib(Libindex+Editrow,Yankdata,swap);
			updatedisplay();
			pryankname();
			break;
		case 'y':
			for(n=0;n<Voicesize;n++)
				Yankdata[n] = Currdata[n];
			pryankname();
			break;
		case '?':
			helpmessage();
			break;
		case SCR_DOWN:
			if(Nvoices < NUMONSCREEN)
				q = Nvoices;
			else
				q = NUMONSCREEN;

			maxindex = Nvoices - q;
			if ( Editcol==0 ) {
				/* we're on the synth side */
				if ( (Synindex+=q/2) > maxindex )
					Synindex = maxindex;
			}
			else {
				/* we're on the lib side */
				if ( (Libindex+=q/2) > maxindex )
					Libindex = maxindex;
			}
			updatedisplay();
			break;
		case SCR_UP:
			if ( Editcol==0 ) {
				/* we're on the synth side */
				if ( (Synindex-=NUMONSCREEN/2) < 0 )
					Synindex = 0;
			}
			else {
				/* we're on the lib side */
				if ( (Libindex-=NUMONSCREEN/2) < 0 )
					Libindex = 0;
			}
			updatedisplay();
			break;
		case '\033':
		case '`':
			allnotesoff();
			break;
		case 't':
			transcmd();
			editto(Editrow,Editcol);
			break;
		case 'D':	/* download FROM file */
			synthinfileflag = 1;
		case 'd':	/* download FROM synth */
			clrdata(Syndata,Nvoices*Voicesize);
			if ( readsynth(Syndata) == 0 ) {
				syntodisplay(Synindex=0);
				if(Editcol == 0)
					editto(Editrow,Editcol);
			}
			synthinfileflag = 0;
			break;
		case 'U':	/* upload to file */
			synthoutfileflag = 1;
		case 'u':	/* upload TO synth */
			if ( Editcol==0 ) {
				voicenum = Editrow+Synindex;
				Global_current_edit_data =
				data = &(VOICEBYTE(Syndata,voicenum,0));
			}
			else
				Global_current_edit_data =
				data = bankvoice(Editrow+Libindex);
			upload(data);
			synthoutfileflag = 0;
			break;
		case 'r':
			readall();
			if(Editcol == 1)
				editto(Editrow,Editcol);
			break;
		case 'R':
			readprintable();
			if(Editcol == 1)
				editto(Editrow,Editcol);
			break;
		case 'w':
			writeall();
			break;
		case 'W':
			writeprintable();
			break;
		case 'c':
			setchan();
			break;
		case 'b':
			/* cycle through banks, from 0 to LIBBANKS-1 */
			if ( ++Libbank >= LIBBANKS )
				Libbank = 0;
			libtodisplay(Libindex);
			updatedisplay();
			break;
		case 'V':
			windgoto(1,0);
			windstr(" 'V' -- appending voice to VPIC file ...");
			fditflag = 1;
		case 'e':
			if ( Namesize == 0)
				p = NULL;
			else
				p = (*Nameof)(Currdata);
			if ( Editcol==0 ) {
				voicenum = Editrow+Synindex;
				Global_current_edit_data =
				data = &(VOICEBYTE(Syndata,voicenum,0));
				editdata(p,data);
				if (!fditflag) windclear();
				/* Update Currdata */
				for ( n=0; n<Voicesize; n++ )
				    Currdata[n] = VOICEBYTE(Syndata,voicenum,n);
			}
			else {
				Global_current_edit_data =
				data = bankvoice(Editrow+Libindex);
				editdata(p,data);
			}
			if (!fditflag) drawall();
			else {
				fditflag = 0;
				windstr(" done");
			}
			break;
		case 'g': /* goto a specific voice */
			do_goto();
			break;
		case CH_LEFT:
		case ALTCH_LEFT:
			if ( Editcol==1 )
				editto(Editrow,0);
			break;
		case CH_DOWN:
		case ALTCH_DOWN:
			if(Nvoices < NUMONSCREEN)
				q = Nvoices;
			else
				q = NUMONSCREEN;
			if ( Editrow < (q-1) )
				editto(Editrow+1,Editcol);
			else {
				/* we're at the bottom, so try to scroll */
				if ( Editcol==0 ) {
					/* we're on the synth side */
					if (Synindex<(Nvoices-q))
						Synindex++;
				}
				else {
					/* we're on the lib side */
					if (Libindex<(Nvoices-q))
						Libindex++;
				}
				updatedisplay();
			}
			break;
		case CH_UP:
		case ALTCH_UP:
			if ( Editrow>0 )
				editto(Editrow-1,Editcol);
			else {
				/* we're at the top, so try to scroll */
				if ( Editcol==0 ) {
					/* we're on the synth side */
					if (Synindex>0)
						Synindex--;
				}
				else {
					/* we're on the lib side */
					if (Libindex>0)
						Libindex--;
				}
				updatedisplay();
			}	
			break;
		case CH_RIGHT:
		case ALTCH_RIGHT:
			if ( Editcol==0 )
				editto(Editrow,1);
			break;
		case 'f':
			filelist();
			break;
		default:
			message("Unrecognized command!  Press '?' for help.");
			break;
		}
	}
}
#endif

#ifdef XGL
Menu
#endif
filelist()
{
	char *p, *q, *qq, buff[BUFSIZ];
	int m, n, ninline = 0, nprinted = 0;
#ifdef XGL
	Menu fmenu;
	Menu_item mi;

	fmenu = (Menu)xv_create(XV_NULL, MENU,
		MENU_TITLE_ITEM,	"files",
		MENU_NCOLS,		3,
		NULL);
#else
	clearmess();
	Currrow = -1;	/* To start message on top line */
#endif
	openls();
#ifndef XGL
	message("Files in current directory:");
	strcpy(buff,"  ");
#endif
	while ( (p=nextls()) != NULL ) {
		n = strlen(p);
		m = 0;
		if (p[0] == '\033') {
			n -= 3;
			while (m < n && p[m++] != 'm') ;
			n -= m;
		}
		qq = (char *)strncpy(alloc(n+1), p+m, n);
		qq[n] = '\0';
		m = strlen(Synthsuffix);
		if (!strcmp(Synthsuffix, qq + n - m))
			qq[n-m] = '\0';
		else continue;
#ifdef XGL
		mi = (Menu_item)xv_create(XV_NULL, MENUITEM,
			MENU_STRING,	qq,
			MENU_NOTIFY_PROC, readall,
			NULL);
		xv_set(fmenu, MENU_APPEND_ITEM, mi, NULL);
		if (!strcmp(qq, "std"))
			xv_set(fmenu, MENU_DEFAULT_ITEM, mi, NULL);
#else
		p = qq;
		/* add the next file to the line being constructed */
		q = &buff[strlen(buff)];
		strcpy(q,p);
		q += (n=strlen(p));
		free(p);
		while ( n++ < 15 )
			*q++ = ' ';
		*q = '\0';
		if ( ninline++ > 3 ) {
			message(buff);
			if ( nprinted++ > 4 ) {
				message("Press any key to continue ...");
				(void)getconsole();
				clearmess();
				nprinted = 0;
			}
			strcpy(buff,"  ");
			ninline = 0;
		}
#endif
	}
#ifndef XGL
	if ( ninline > 0 )
		message(buff);
#endif
	closels();
#ifdef XGL
	return(fmenu);
#endif
}

#ifndef XGL
libmouse()
{
	int row, col, q;

	getmouse(&row,&col);
	if(Nvoices < NUMONSCREEN)
		q = Nvoices;
	else
		q = NUMONSCREEN;
	if ( row <= FIRSTROW || row > FIRSTROW+q+1 )
		goto getout;
	if ( col < Cols/2 )
		col = 0;
	else
		col = 1;
	row = row - FIRSTROW - 1;
	editto(row,col);
getout:
	/* wait until mouse button is released */
	while ( statmouse() > 0 )
		;
}

do_goto()
{
	int n, r, maxindex, new_ecol,q;
	char sbuf[100], *sp;
	
	message("");
	message("Where to? ");
	windgets(sbuf);
	
	sp = sbuf;
	switch(*sp++) {
	  case 's': /* synth side */
		new_ecol = 0;
		break;
	  case 'l': /* library side */
		new_ecol = 1;
		break;
	  default: /* no change in side */
	  	new_ecol = Editcol;
		sp--;	/* but don't trash the first character */
		break;
	}
	
	clearmess();
	r = sscanf(sp, "%d", &n); /* this may fail - we handle it later */
	if(Cvtnum != NULL) { /* convert to internal format if needed */
		n = (*Cvtnum)(n) + 1; /* we are 1-based for user input */
	}
	if (Cvtanum != NULL) { /* convert using alphanumeric voice number */
		n = (*Cvtanum)(sp) + 1;
		if (n) r = 1;
	}
	if(r != 1) {
		message("type one of: sn, ln, or n");
		message("  s = synth side (literal character 's')");
		message("  l = library side (literal character 'l')");
#ifdef KAWAIK1
		message("  n = voice number to select (letter + number)");
#else
#ifdef KAWAIK5
		message("  n = voice number to select (letter + number)");
#else
		message("  n = voice number to select (an integer)");
#endif
#endif
		return;
	}
	if(n <= 0 || n > Nvoices) { /* 1-based */
		message("Bad voice number!");
		return;
	}
	
	/* it can be done.  nuke the old '*' and change columes (if needed) */
	editchar(' ', Editrow, Editcol);
	Editcol = new_ecol;
	
	/* try to center it */
	if(Nvoices < NUMONSCREEN)
		q = Nvoices;
	else
		q = NUMONSCREEN;
	maxindex = Nvoices - q;
	if(Editcol == 0) {
		Synindex = (n - 1) - q/2; /* 0-based */
		if(Synindex < 0) { /* impossible to center */
			Synindex = 0; /* so do your best */
		} else if(Synindex > maxindex) {
			Synindex = maxindex;
		}
		Editrow = (n - 1) - Synindex; /* and put a '*' on it */
	} else {
		Libindex = (n - 1) - q/2; /* 0-based */
		if(Libindex < 0) {
			Libindex = 0;
		} else if(Libindex > maxindex) {
			Libindex = maxindex;
		}
		Editrow = (n - 1) - Libindex;
	}
	
	updatedisplay(); /* do the real work */
	return;
}
#endif

#ifndef XGL
upload(data)
char *data;
{
	int c, n;
	char num[16];

	message("");
	if (synthoutfileflag) {
		message("Upload to synth file:");
		message("Output synth file --> ");
		windgets(Syofname);
		flushmidi();
	}
	else message("Upload TO synth:");
	message("                 c - current voice");
	message("                 a - ALL voices");
	message("Choose --> ");
	c = getconsole();
	if ( c == 'c' ) {
		clearmess();
		if ( Sendone == NULL ) {
			if (synthoutfileflag) (*Sendedit)(data);
			else
			message("Single voices can't be sent to that synth!");
			return;
		}
		message("What voice number to you want to send it TO? --> ");
		windgets(num);
		clearmess();
		n = atoi(num);
		if(Cvtnum != NULL) { /* convert to internal format if needed */
			n = (*Cvtnum)(n) + 1; /* we are 1-based for user input */
		}
		if (Cvtanum != NULL) { /* howzabout alphanumeric format? */
			n = (*Cvtanum)(num) + 1;
		}
		if ( n > 0 && n <= Nvoices ) {
			if ( (*Sendone)(n-1,data) != 0 ) { /* 0-based on calls -SAF */
				message("Unable to write data to synth!");
				(void)sprintf(Buff,"Reason: %s",Reason);
				message(Buff);
				return;
			}
		} else {
			message("Bad voice number!");
		}
	}
	else if ( c == 'a' ) {
		clearmess();
		if ( Sendbulk != NULL ) {
			message("Uploading to synth using bulk routine.");
			if ( (*Sendbulk)(Syndata) != 0 ) {
				message("Upload failure!");
				(void)sprintf(Buff,"Reason: %s\n",Reason);
				message(Buff);
				return;
			}
		} else {
			message("Uploading to synth using single routine.");
			for ( n=0; n<Nvoices; n++ ) {
				if ( (*Sendone)(n, &(VOICEBYTE(Syndata,n,0)) ) != 0 ) {
					message("Unable to write data to synth!");
					(void)sprintf(Buff,"Reason: %s",Reason);
					message(Buff);
					return;
				}
			}
		}
		/* clearmess(); */
	}
	else {
		clearmess();
		message("Bad choice!");
	}
}

helpmessage()
{
clearmess();
(void)sprintf(Buff,
"%s,%s,%s,%s - move around              e - edit current voice  U/D - midi file",
	STR_LEFT,STR_DOWN,STR_UP,STR_RIGHT);
message(Buff);
message(
"r - read voices from a file        y - yank into buffer    R/W - print file");
message(
"w - write voices to a file         p - put from buffer     V   - VPIC file");
message(
"b - cycle through library banks    s - swap current voice with yank buffer");
message(
"t - transfer all voices            f - list files in current directory");
message(
"d - download voices from synth     c - set MIDI channel");
message(
"u - upload voices to synth         g - 'goto' form of moving around");
message(
"q - quit                           <space> - play a note");
}
#endif

updatedisplay()
{	int r;

	if ( Editcol==0 ) {
		/* we're on the synth side */
#ifdef XGL
/*printf("Synth index %d; ", Synindex);*/
		if (Synindex != (int)xv_get(syn_bank, PANEL_LIST_FIRST_SELECTED))
			xv_set(syn_bank, PANEL_LIST_SELECT, Synindex, TRUE, NULL);
/*printf("he say %d\n", (int)xv_get(syn_bank, PANEL_LIST_FIRST_SELECTED));*/
		Global_current_edit_data = &(VOICEBYTE(Syndata,Synindex,0));
		(void)sprintf(Buff,"%s | ",vnumtext(Synindex+1));
		strcat(Buff, (*Nameof)( & (VOICEBYTE(Syndata,Synindex,0) )) );
		xv_set(syn_bank, PANEL_LIST_STRING, Synindex, Buff, NULL);
		r = (int)xv_get(lib_bank, PANEL_LIST_FIRST_SELECTED);
		if (r >= 0) xv_set(lib_bank, PANEL_LIST_SELECT, r, FALSE, NULL);
/*printf("(r = %d deselected\n", r);*/
#else
		syntodisplay(Synindex);
#endif
	}
	else {
		/* we're on the lib side */
#ifdef XGL
/*printf("Lib index %d; ", Libindex);*/
		if (Libindex != (int)xv_get(lib_bank, PANEL_LIST_FIRST_SELECTED))
			xv_set(lib_bank, PANEL_LIST_SELECT, Libindex, TRUE, NULL);
/*printf("he say %d\n", (int)xv_get(lib_bank, PANEL_LIST_FIRST_SELECTED));*/
		Global_current_edit_data = bankvoice(Libindex);
		(void)sprintf(Buff,"%s | ",vnumtext(Libindex+1));
		strcat(Buff, (*Nameof)(bankvoice(Libindex)));
		xv_set(lib_bank, PANEL_LIST_STRING, Libindex, Buff, NULL);
		r = (int)xv_get(syn_bank, PANEL_LIST_FIRST_SELECTED);
		if (r >= 0) xv_set(syn_bank, PANEL_LIST_SELECT, r, FALSE, NULL);
/*printf("(r = %d deselected\n", r);*/
#else
		libtodisplay(Libindex);
#endif
	}
#ifdef XGL
	Changed = 1;
#endif
	editto(Editrow,Editcol);

}

pryankname()
{
	static char ybuff[33];
	char *p;

	if ( Namesize == 0 )
		return;

#ifdef XGL
	strcpy(ybuff, "[");
	strcat(ybuff,(*Nameof)(Yankdata));
	if (!ybuff[2]) strcat(ybuff,"(empty)");
#else
	windgoto(YANKROW,YANKCOL-4);
	windstr("                    ");
	strcpy(ybuff,(*Nameof)(Yankdata));
#endif
	/* take off trailing blanks */
	p = ybuff + strlen(ybuff) - 1;
	while ( p>ybuff && *p == ' ' )
		*p-- = '\0';
#ifdef XGL
	strcat(ybuff, "]");
	xv_set(yank_name, PANEL_LABEL_STRING, ybuff, NULL);
#else
	windgoto(YANKROW,YANKCOL+7-strlen(ybuff)/2);
	windstr(ybuff);
	windrefresh();
#endif
}

#ifndef XGL
transcmd()
{
	int fromc;

	message("");
	message("Transfer ALL voices:");
	message("                       1:   <<-----   from library bank to synth bank");
	message("                       2:   ----->>   from synth bank to library bank");
	message("1 or 2 --> ");
	fromc = getconsole();
	windputc(fromc);
	if ( fromc!='1' && fromc!='2' ) {
		clearmess();
		return;
	}
	switch ( fromc ) {
	case '1':
		copyall(bankvoice(0),Syndata);
		syntodisplay(Synindex);
		clearmess();
		message("Use the 'u'pload command to actually send the synth bank voices to the synth.");
		break;
	case '2':
		copyall(Syndata,bankvoice(0));
		libtodisplay(Libindex);
		clearmess();
		break;
	}
}
#endif

copyall(fromdata,todata)
char *fromdata;
char *todata;
{
	int n, v;

	for ( v=0; v<Nvoices; v++ )
		for ( n=0; n<Voicesize; n++ )
			VOICEBYTE(todata,v,n) = VOICEBYTE(fromdata,v,n);
}

#ifndef XGL
message(s)
char *s;
{
	windgoto(++Currrow,0);
	windstr(s);
	windrefresh();
}

setchan()
{
	int c;

	message("New MIDI channel --> ");
	windgets(Buff);
	if ( (c=atoi(Buff)) <= 0 || c > 16 ) {
		clearmess();
		message("Invalid channel!");
	}
	else {
		clearmess();
		Channel = c;
		showchan();
	}
}

showchan()
{
	windgoto(20,31);
	windstr("MIDI Channel: ");
	(void)sprintf(Buff,"%d ",Channel);
	windstr(Buff);
	windrefresh();
}
#endif

#ifdef DX7
#include <sys/types.h>
#include <sys/stat.h>
long flength(handle)
int handle;
{
  struct stat buf;
  fstat(handle, &buf);
  return buf.st_size;
}
#endif

/* read data from a file, filling the current library bank. */
#ifdef XGL
void
readall(Menu m, Menu_item mi)
#else
readall()
#endif
{
	FILE *f;
	int v, n, r;
	char *p;

#ifdef XGL
	strcpy(curr_fname, (char *)xv_get(mi, MENU_STRING));
	strcat(curr_fname, Synthsuffix);
	xv_set((Menu_item)xv_get(save_menu, MENU_NTH_ITEM, 1),
		MENU_STRING, curr_fname, NULL);
	xv_set(save_text_item, PANEL_VALUE, curr_fname, NULL);
#else
	message("File name --> ");
	windgets(curr_fname);
	strcat(curr_fname, Synthsuffix);
#endif

#ifdef BSD
	OPENBINFILE(f,curr_fname,"r");
#else
	OPENBINFILE(f,curr_fname,"rb");
#endif
	if (f == NULL ) {
		(void)sprintf(Buff,"can't open file '%s'",curr_fname);
		message(Buff);
		return;
	}
	/* If the first byte matches DataID, then the format is OK. */
	if (DataID) n = (getc(f) & 0xff);
	else n = 0;

#ifndef ROLANDD10
# ifndef KAWAIIK1
/* If we are running the Aztec C compiler, it's not yet ANSII.  The
   abominable kludge that follows is therefore necessary.           */
#  ifdef AZTEC_C
#    ifdef DX7
#     define SKIP_IT
#    endif
#    ifdef DX7S
#     define SKIP_IT
#    endif
#    ifndef SKIP_IT
	if (n != DataID && n <= 31) {
		(void)ungetc(n,f);
		n = DataID;
	}
#    endif
#    undef SKIP_IT
#  else
#   if !defined(DX7) && !defined(DX7S)
        if (n != DataID && n <= 31) {
                (void)ungetc(n,f);
                n = DataID;
        }
#   endif
#  endif
# endif
#endif

#ifdef DX7S

/* check for reading a DX7 file in DX7s mode */

        if ( strcmp(Synthname, "DX7s") == 0 )
          if (flength(fileno(f)) == 4096) {
            (void)ungetc(n, f);
            dx7Sread_dx7(f, bankvoice(0));
            r = 0;
            goto done;
          }

/* check for reading a DX7S file in DX7 mode */

          if ( strcmp(Synthname, "DX7") == 0 ) {
            n = getc(f) & 0xff;
            if (n == 0xd7) {	/* DX7s dataID */
              dx7read_dx7S(f, bankvoice(0));
              r = 0;
              goto done;
            }
            (void)ungetc(n, f);
            n = 0;
          }
#endif

#ifdef DX7

/* validate a DX7 file based on length */

        if ( strcmp(Synthname, "DX7") == 0 ) {
          if (flength(fileno(f)) != Nvoices * Voicesize)
            n = DataID + 1;
        }

#endif

	if ( n == DataID ) {
		p = bankvoice(0);
		for ( v=0; v<Nvoices; v++ ) {
			int c = 0;
			for ( n=0; n<Voicesize; n++ ) {
				c = getc(f);
				if (c == EOF) break;
#ifdef CZ1
				*p++ = c;
#else
#ifdef CZ101
				*p++ = c;
#else
#ifdef LINUXIO
				*p++ = c;
#else
				*p++ = c & 0x7f;
#endif
#endif
#endif
			}
			if (c == EOF) break;
		}
		r = 0;
	} else {
		(void)sprintf(Buff, "'%s' is not the right type of file\n", curr_fname);
		message(Buff);
		r = 1;
	}
#ifdef DX7S
done:
#endif
	(void)fclose(f);
	if ( r==0 ) {
		libtodisplay(Libindex=0);
#ifdef XGL
		/*if (Editcol) Global_can_play_data_flag = 0;*/
		if (Editcol) Global_can_play_note_flag = 0;
		updatedisplay();
		if (Editcol && currently_editing) readyedit();
#else
		clearmess();
#endif
	}
}

#ifndef XGL
/* read printable data from a textfile, filling the current library bank. */
readprintable()
{
	char fname[100];
	FILE *pfin;
	int v, n, r, val;
	char *p = NULL;
	char vname[20];

	message("File name --> ");
	windgets(fname);
#ifdef BSD
	OPENBINFILE(pfin,fname,"r");
#else
	OPENBINFILE(pfin,fname,"rb");
#endif
	if (pfin == NULL ) {
		(void)sprintf(Buff,"Can't open '%s'!",fname);
		message(Buff);
		return;
	}
	/* If the first byte matches DataID, then the format is OK. */
	r = fscanf(pfin, "BANK TYPE %d", &n);

	v = -1;
	if ( r == 1 && n == DataID ) while (r) {
	    r = fscanf(pfin, " %s ", vname);

	    if (r == 1) {
		if (strcmp(vname, "voice") == 0) {
			if (v > 0) (*Dataout)(p);
			/*r = fscanf(pfin,"%d = %[ !-~]", &v, vname);*/
			r = fscanf(pfin,"%d = @)%[ !-~](@", &v, vname);
			if (r == 2 && v > 0) {
			    p = bankvoice(v-1);
			    (*Setnameof)(p, vname);
			}
		} else {
			r = fscanf(pfin, "= %d", &val);
			if (r == 1) setval(vname, val);
		}
	    }
	    if (r == EOF) r = 0;
	} else {
		(void)sprintf(Buff, "'%s' is invalid for this function!\n", fname);
		message(Buff);
		r = 1;
	}
	if (v > 0) (*Dataout)(p);
	(void)fclose(pfin);
	if ( r==0 ) {
		libtodisplay(Libindex=0);
		clearmess();
	}
}
#endif

/* write current library bank to a file */
writeall()
{
	char fname[100];
	FILE *f;
	int v, n;
	char *p;

#ifdef XGL
	p = (char *)xv_get(save_text_item, PANEL_VALUE);
	if (p[0]) strcpy(curr_fname, p);
	strcpy(fname, curr_fname);
#else
	message("File name --> ");
	windgets(fname);
	strcat(fname, Synthsuffix);
#endif
#ifdef BSD
	OPENBINFILE(f,fname,"w");
#else
	OPENBINFILE(f,fname,"wb");
#endif
	if ( f == NULL ) {
		(void)sprintf(Buff,"Can't open '%s'!",fname);
		message(Buff);
		return;
	}
	if (DataID) putc(DataID,f);		/* write data identifier */

	p = bankvoice(0);
	for ( v=0; v<Nvoices; v++ ) {
		for ( n=0; n<Voicesize; n++ )
			putc(*p++,f);
	}
	(void)fclose(f);
#ifndef XGL
	clearmess();
#endif
}


FILE *pfout;
int pfoutflag = 0;

#ifndef XGL
/* write current library bank to a file in printable form */
writeprintable()
{
	char fname[100];
	int v;
	char *p, *q;

	message("File name --> ");
	windgets(fname);
#ifdef BSD
	OPENBINFILE(pfout,fname,"w");
#else
	OPENBINFILE(pfout,fname,"wb");
#endif
	if ( pfout == NULL ) {
		(void)sprintf(Buff,"Can't open '%s'!",fname);
		message(Buff);
		return;
	}
	fprintf(pfout, "BANK TYPE %d\n", DataID);

	for (v=0; v<Nvoices; v++) {
		p = bankvoice(v);
		q = (*Nameof)(p);
		if (q[0] >= ' ') {
			(*Datain)(p);
			fprintf(pfout, "voice %d = @)%s(@\n", v+1, q);
			pfoutflag = 1;
			(*Dataout)(p);
			pfoutflag = 0;
		}
	}
	(void)fclose(pfout);
	clearmess();
}
#endif

#ifndef XGL
/* draw main library/synth voice bank screen */
drawall()
{
	windclear();
	template();
	libtodisplay(Libindex);
	syntodisplay(Synindex);
	editto(Editrow,Editcol);
	pryankname();
	showchan();
}
#endif

/*
 * tosyn
 *
 * Store the given 'data' in in voice 'voicenum' (both in Syndata
 * AND on the synth itself).  If swap is non-zero, the voice is swapped
 * with the current voice in Syndata.
 */

tosyn(voicenum,data,swap)
char *data;
{
	int n, t;

	for ( n=0; n<Voicesize; n++ ) {
		if ( swap ) {
			t = VOICEBYTE(Syndata,voicenum,n);
			VOICEBYTE(Syndata,voicenum,n) = data[n];
			data[n] = t;
		}
		else
			VOICEBYTE(Syndata,voicenum,n) = data[n];
	}
}

tolib(voicenum,data,swap)
char *data;
{
	int n, t;
	char *p = bankvoice(voicenum);

	for ( n=0; n<Voicesize; n++ ) {
		if ( swap ) {
			t = *p;
			*p = data[n];
			data[n] = t;
		}
		else
			*p = data[n];
		p++;
	}
}

tocurrent(data,voicenum)
char *data;
int voicenum;
{
	int n, f = 0;

	for ( n=0; n<Voicesize; n++ )
		if ( (Currdata[n] = VOICEBYTE(data,voicenum,n)) != 0) f = 1;
	Global_can_play_data_flag = f;
	if (Changed) Global_can_play_note_flag = 0;
#ifndef XGL
	if (Editcol)
	Global_current_edit_data = bankvoice(voicenum);
	else
	Global_current_edit_data = &(VOICEBYTE(Syndata,voicenum,0));
#endif
/**
	if( f == 1 && !synthoutfileflag) (*Sendedit)(Currdata);
**/
#ifdef XGL
    if (!currently_editing)
#endif
	(*Datain)(Currdata); /* so that gus ramp parameters are set */
}

#ifndef XGL
/*
 * readsynth
 *
 * Read a bulk dump from the synth, with some tolerance for errors.
 */
readsynth(data)
char *data;
{
	if ( Getbulk == NULL ) {
		message("That synth is unable to dump anything!!");
		return(1);
	}
	if (synthinfileflag) {
		message("Input synth file --> ");
		windgets(Syinfname);
		synthoutfileflag = 1;
		flushmidi();
		synthoutfileflag = 0;
	}
	else message("Downloading from synth");

	if ( (*Getbulk)(data) == 0 )
		return(0);

	message("Unable to read data from synth!");
	(void)sprintf(Buff,"Reason: %s",Reason);
	message(Buff);
	message("Perhaps connections are amiss?");
	return(1);
}
#endif

char *
vnumtext(n)
{
	static char vnbuff[6];

	if ( Numof == NULL ) {
		(void)sprintf(vnbuff,"%2d",n);
		return(vnbuff);
	}
	else
		return((*Numof)(n - 1)); /* keep this 0-based */
}

#ifdef XGL
static char Syntitle[80];
#endif
/*
 * syntodisplay(n)
 *
 * Tranfer Syndata names to dialog boxes (Dxvoices) starting at n.
 */
syntodisplay(n)
{
	int k, r, q;

#ifdef XGL
	if (Synthnumber == -1) return;
	(void)sprintf(Syntitle,"%s synth", Synthname);
	r = (int)xv_get(syn_bank, PANEL_LIST_NROWS);
	xv_set(syn_bank, XV_SHOW, FALSE, NULL);
	xv_set(syn_bank, PANEL_LIST_TITLE, Syntitle, NULL);
	while (r < Nvoices) {
		xv_set(syn_bank, PANEL_LIST_INSERT, r, NULL);
		r++;
	}
	while (r > Nvoices) {
		r--;
		xv_set(syn_bank, PANEL_LIST_DELETE, r, NULL);
	}
	for (k = 0; k < Nvoices; k++) {
		(void)sprintf(Buff,"%s | ",vnumtext(n+k+1));
		strcat(Buff, (*Nameof)( & (VOICEBYTE(Syndata,n+k,0) )) );
		xv_set(syn_bank, PANEL_LIST_STRING, k, Buff, NULL);
	}
	/*updatedisplay();*/
	xv_set(syn_bank, XV_SHOW, TRUE, NULL);
#else
	if(Nvoices < NUMONSCREEN)
		q = Nvoices;
	else
		q = NUMONSCREEN;
	for ( k=0; k<q; k++ ) {
		r = FIRSTROW+1+k;
		windgoto(r,LEFTSIDE);
		(void)sprintf(Buff,"%s |                 ",vnumtext(n+k+1));
		windstr(Buff);
		/* pull the name out of the Syndata */
		if ( Namesize != 0 ) {
			windgoto(r,LEFTSIDE+5);
			windstr((*Nameof)( & (VOICEBYTE(Syndata,n+k,0) )) );
		}
	}
	windrefresh();
#endif
#ifdef KAWAIK1
        if ( strcmp(Synthname, "K1 Single") == 0 ) synnames();
#endif
#ifdef FMCARD
        if ( strcmp(Synthname, "SoundBlaster") == 0 ) sbsynnames();
       	else if ( strcmp(Synthname, "Opl3") == 0 ) sbsynnames();
#endif
}

#ifdef KAWAIK1
/*
 * synnames()
 *
 * Tranfer Syndata names to array.
 */
synnames()
{	int i, k;
	char *p, *q;

	if (Nvoices != 64) return;

	for ( k=0; k < 64; k++ ) {
		p = (*Nameof)( & (VOICEBYTE(Syndata,k,0) ));
		q = sngnames[k];
		for (i=0; i < 10; i++) *q++ = *p++;
		*q = 0;
	}
}
#endif

#ifdef FMCARD
/*
 * sbnames()
 *
 * Tranfer Syndata names to array.
 */
sbsynnames()
{	int i, k;
	char *p, *q;

	for ( k=0; k < 128; k++ ) {
		p = (*Nameof)( & (VOICEBYTE(Syndata,k,0) ));
		q = sbnames[k];
		for (i=0; i < SBNAMELEN; i++) *q++ = *p++;
		*q = 0;
	}
}
#endif

#ifdef XGL
static char Libtitle[80];
#endif
/*
 * libtodisplay
 *
 * Tranfer Libdata names to the screen, starting at voice 'firstv'.
 */
libtodisplay(firstv)
{
	int k, r, q;

	if (Synthsuffix[0]) (void)sprintf(Buff,"Library (%s %d)",Synthsuffix+1,Libbank+1);
	else (void)sprintf(Buff,"Library (Bank %d)",Libbank+1);
#ifdef XGL
	if (Synthnumber == -1) return;
	strcpy(Libtitle, Buff);
	r = (int)xv_get(lib_bank, PANEL_LIST_NROWS);
	xv_set(lib_bank, XV_SHOW, FALSE, NULL);
	xv_set(lib_bank, PANEL_LIST_TITLE, Libtitle, NULL);
	while (r < Nvoices) {
		xv_set(lib_bank, PANEL_LIST_INSERT, r, NULL);
		r++;
	}
	while (r > Nvoices) {
		r--;
		xv_set(lib_bank, PANEL_LIST_DELETE, r, NULL);
	}
	for (k = 0; k < Nvoices; k++) {
		(void)sprintf(Buff,"%s | ",vnumtext(firstv+k+1));
		strcat(Buff, (*Nameof)(bankvoice(firstv+k)));
		xv_set(lib_bank, PANEL_LIST_STRING, k, Buff, NULL);
	}
	xv_set(lib_bank, XV_SHOW, TRUE, NULL);
#else
	windgoto(FIRSTROW-1,RIGHTSIDE+2);
	windstr(Buff);

	if(Nvoices < NUMONSCREEN)
		q = Nvoices;
	else
		q = NUMONSCREEN;
	for ( k=0; k<q; k++ ) {
		r = FIRSTROW+1+k;
		windgoto(r,RIGHTSIDE);
		(void)sprintf(Buff,"%s |                 ",vnumtext(firstv+k+1));
		windstr(Buff);
		/* pull the name out of the Libdata */
		if ( Namesize != 0 ) {
			windgoto(r,RIGHTSIDE+5);
			windstr((*Nameof)(bankvoice(firstv+k)));
		}
	}
	windrefresh();
#endif
}

allnotesoff()
{
	int n;

	/* Go through all channels. */
	for ( n=0; n<15; n++ ) {
		sendmidi(n | 0xb0);
		sendmidi(0x7b);
		sendmidi(0x00);
	}
}

/*
 * Below are generic edit routines, which manage a screen display
 * showing parameter values, and let you roam around and make changes.
 * The display is managed by the contents of the P array, which
 * contains the name, screen location, min/max values, etc. of
 * each parameter.
 */

int Prow = 0;
int Pcol = 0;
int Parm = 0;

#ifdef XGL
#define ED_X(h) (8*h)
#define ED_Y(v) (14*v)
#define ED_VM	5
#define ED_HM	5

#ifdef GLSS
#define CSHEIGHT 156
Drawline wave1[MAXSYNTHS][4];
Drawline wave2[MAXSYNTHS][4];
Drawline wave3[MAXSYNTHS][4];
Drawline wave4[MAXSYNTHS][4];
int csheight[MAXSYNTHS];
int nwaves[MAXSYNTHS];

#endif

void
fix_panel_to_right(int n)
{	Panel_item item;
	int xb = xv_get(P[n].p_button, XV_X);
	int yb = xv_get(P[n].p_button, XV_Y);

	PANEL_EACH_ITEM(edit_panel[Synthnumber], item)
		if (yb == xv_get(item, XV_Y) &&
		    xb < xv_get(item, XV_X))
		panel_paint(item, PANEL_NO_CLEAR);
	PANEL_END_EACH
}

void
up_down(Panel_item item, int val, Event *ev)
{
	int n;
	Parm = (int)xv_get(item, PANEL_CLIENT_DATA);
	switch (val) {
		case 0: adjuparm(1); break;
		case 1: adjuparm(-1); break;
		case 2: adjuparm(4); break;
		case 3: adjuparm(-4); break;
		case 4: adjuparm(P[Parm].p_max - P[Parm].p_min); break;
		case 5: adjuparm(P[Parm].p_min - P[Parm].p_max); break;
		default:
	}
	fix_panel_to_right(Parm);
	if (n = P[Parm].p_wave / 100) showwave(n);
	if (check_selected(FALSE) < 0) return;
	(*Dataout)(Global_current_edit_data);
	/*if (!Changed) return;*/
	/* Update Currdata */
/**
	if (Editcol) tocurrent(Global_current_edit_data,Libindex);
	else tocurrent(Global_current_edit_data,Synindex);
	updatedisplay();
**/
	editto(Editrow,Editcol);
}

Panel_setting
new_voice_label(Panel_item item, Event *ev)
{
	char *p = (char *)xv_get(item, PANEL_VALUE);
	if ( p[0]!='\0' && p[0]!='\n' )
		(*Setnameof)(Global_current_edit_data, p);
	Changed = 1;
	if (check_selected(FALSE) < 0) return;
	(*Dataout)(Global_current_edit_data);
	/* Update Currdata */
	if (Editcol) tocurrent(Global_current_edit_data,Libindex);
	else tocurrent(Global_current_edit_data,Synindex);
	updatedisplay();
	return(PANEL_NEXT);
}

#ifdef GLSS

int shell_event_proc(Xv_window win, Event* ev, Canvas_shell canvas_shell)
{
	if (event_is_ascii(ev))
		space_note(win, ev, (Notify_arg)NULL);
	return(FALSE);
}

#endif

new_edit_frame()
{
#ifdef GLSS
	int grip_move_proc();
	void grip_done_proc();
#endif
	void space_note();
	int n;
	char *s;
	char *title = strcpy(alloc(strlen(Synthname)+6), "Edit ");
	strcat(title, Synthname);

	nwaves[Synthnumber] = csheight[Synthnumber] = 0;
	for ( n=0; P[n].p_name != NULL; n++ ) {
		int u = P[n].p_wave / 100;
		if (u > nwaves[Synthnumber]) nwaves[Synthnumber] = u;
	}
	if (nwaves[Synthnumber] > 0) csheight[Synthnumber] = CSHEIGHT;

	edit_rect.r_width = ED_X(80) + 2*ED_HM;
	edit_rect.r_left = 800 - edit_rect.r_width - 15;
#ifdef GLSS
	edit_rect.r_height = ED_Y(24) + 2*ED_VM + csheight[Synthnumber];
#else
	edit_rect.r_height = ED_Y(24) + 2*ED_VM;
#endif
	edit_rect.r_top = 600 - edit_rect.r_height - 50;

	edit_frame[Synthnumber] = (Frame) xv_create(frame, FRAME,
			FRAME_LABEL,		title,
			XV_WIDTH,		edit_rect.r_width,
			XV_HEIGHT,		edit_rect.r_height,
			XV_X,			edit_rect.r_left,
			XV_Y,			edit_rect.r_top,
			NULL);
	/*frame_set_rect(edit_frame[Synthnumber], &edit_rect);*/

	xv_set(edit_frame[Synthnumber],
		WIN_EVENT_PROC,	space_note,
		WIN_CONSUME_EVENTS,	WIN_ASCII_EVENTS, NULL,
		NULL);

	edit_panel[Synthnumber] = (Panel)xv_create(edit_frame[Synthnumber], PANEL,
#ifdef GLSS
		XV_HEIGHT,	edit_rect.r_height - csheight[Synthnumber],
#endif
		NULL);

	(void)xv_create(edit_panel[Synthnumber], PANEL_BUTTON,
		PANEL_LABEL_STRING,	"quit edit",
		PANEL_NOTIFY_PROC,	unshow_edit,
		PANEL_CLIENT_DATA,	edit_frame[Synthnumber],
		XV_X,			ED_X(70)+ED_HM,
		XV_Y,			ED_VM+5,
		NULL);
	edit_play_note[Synthnumber] = (Panel_item)xv_create(edit_panel[Synthnumber],
		PANEL_TEXT,
		PANEL_LABEL_IMAGE,	note_image,
		PANEL_VALUE_DISPLAY_LENGTH, 1,
		PANEL_MASK_CHAR,	'*',
		PANEL_NOTIFY_LEVEL,	PANEL_ALL,
		PANEL_NOTIFY_PROC,	edit_play_action,
		XV_X,			ED_X(62)+ED_HM,
		XV_Y,			ED_VM+5,
		NULL);

#ifdef GLSS
	if (csheight[Synthnumber]) {
		shell[Synthnumber] = xv_create(edit_frame[Synthnumber], CANVAS_SHELL,
			WIN_CMS, cms,
			WIN_FOREGROUND_COLOR, 0,
			CANVAS_SHELL_EVENT_PROC, shell_event_proc,
			NULL);
		window_fit_height(shell[Synthnumber]);

		if (nwaves[Synthnumber] > 0) makewave1();
		if (nwaves[Synthnumber] > 1) makewave2();
		if (nwaves[Synthnumber] > 2) makewave3();
		if (nwaves[Synthnumber] > 3) makewave4();

		grip[Synthnumber] = xv_create(shell[Synthnumber], GRIP,
			RECTOBJ_FG,	CMS_CONTROL_HIGHLIGHT,
			GRIP_RUBBER_STYLE, GRIP_RUBBER_RECT,
			GRIP_MOVE_PROC, grip_move_proc,
			GRIP_DONE_PROC, grip_done_proc,
			XV_WIDTH,	20,
			XV_HEIGHT,	20,
			NULL);
	}
#endif
	edit_voice_label[Synthnumber] = (Panel_item)xv_create(edit_panel[Synthnumber],
		PANEL_TEXT,
		PANEL_LABEL_STRING,	"N:",
		PANEL_VALUE_DISPLAY_LENGTH, 20,
		PANEL_NOTIFY_PROC,	new_voice_label,
		XV_X,	ED_HM,
		XV_Y,	ED_VM+5,
		NULL);

	/* The L array contains arbitrary screen labels */
	for ( n=0; L[n].l_text != NULL; n++ ) {
		(void)xv_create(edit_panel[Synthnumber], PANEL_MESSAGE,
			PANEL_LABEL_STRING,	L[n].l_text,
			XV_X,	ED_X(L[n].l_col)+ED_HM,
			XV_Y,	ED_Y(L[n].l_row)+ED_VM+5,
			NULL);
	}
	for ( n=0; P[n].p_name != NULL; n++ ) {

		if (P[n].p_vcol < 0 || P[n].p_vrow < 0) continue;

		if ( (s=P[n].p_label) != NULL )
		    (void)xv_create(edit_panel[Synthnumber], PANEL_MESSAGE,
			PANEL_LABEL_STRING,	s,
			XV_X,	ED_X(P[n].p_lcol)+ED_HM,
			XV_Y,	ED_Y(P[n].p_lrow)+ED_VM+5,
			NULL);

		P[n].p_button = (Panel_item)xv_create(edit_panel[Synthnumber],
			PANEL_CHOICE,
			PANEL_DISPLAY_LEVEL,	PANEL_NONE,
			PANEL_LABEL_FONT,	font_fixed,
			PANEL_CHOICE_NCOLS,	3,
			PANEL_CHOICE_STRINGS,	"+", "-", "+4", "-4", "max", "min", NULL,
			PANEL_NOTIFY_PROC,	up_down,
			PANEL_CLIENT_DATA,	n,
			XV_X,	ED_X(P[n].p_vcol)+ED_HM-3*font_fixed_width,
			XV_Y,	ED_Y(P[n].p_vrow)+ED_VM+5,
			NULL);
	}
}
#ifdef GLSS

#define MXVAL 63

#define ATTACK 2
#define DECAY 3
#define SUSTAIN 4
#define RELEASE 5

static int wparm[5][6];
static int windex[5][6];
static int gdone[5][6] = {
	-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1 };

void valton()
{
	int w, parm, val, n;
	float factor;
	char *p;
	int wave_changed[5] = {0,0,0,0,0};

	for (w = 1; w <= 4; w++)
	for (parm = ATTACK; parm <= RELEASE; parm++) if ((val=gdone[w][parm]) >= 0) {
		int done, best, try, delta, bestdelta;
		n = windex[w][parm];
		if (val == wparm[w][parm]) continue;
		factor = 64.0 / (float)(P[n].p_max - P[n].p_min + 1);
		val = (int)( (float)val/factor );

		best = try = P[n].p_min;
		bestdelta = P[n].p_max - P[n].p_min + 1;
		while (try <= P[n].p_max && bestdelta) {
			p = (*(P[n].p_tovis))(try);
			delta = val - atoi(p);
			if (delta < 0) delta = -delta;
			if (delta < bestdelta) {
				best = try;
				bestdelta = delta;
			}
			try++;
		}
		P[n].p_val = best;
		if (bestdelta) wave_changed[w] = 1;
		showparam(n,0);
		gdone[w][parm] = -1;
	}
	for (w = 1; w <= 4; w++) if (wave_changed[w]) showwave(w);
}

int ntoval(int w, int parm, int n)
{
	char *p = (*(P[n].p_tovis))(P[n].p_val);
	int val = atoi(p);
	float factor = 64.0 / (float)(P[n].p_max - P[n].p_min + 1);
	val = (int)(factor * (float)val);
	if (val < 0) val = 0;
	if (val > MXVAL) val = MXVAL;
	wparm[w][parm] = val;
	windex[w][parm] = n;
	return(val);
}
readywave()
{
	if (!csheight[Synthnumber]) return;
	showwave(1);
	showwave(2);
	showwave(3);
	showwave(4);
}

showwave(int w)
{	int n, att=0, dec=0, sus=0, rel=0;

	if (w < 1 || w > nwaves[Synthnumber]) return;
	for ( n=0; P[n].p_name != NULL; n++ ) {
		int u = P[n].p_wave - w * 100;
		switch (u) {
			case ATTACK: att = ntoval(w,ATTACK,n); break;
			case DECAY:  dec = ntoval(w,DECAY,n); break;
			case SUSTAIN:sus = ntoval(w,SUSTAIN,n); break;
			case RELEASE:rel = ntoval(w,RELEASE,n); break;
		}
	}
	sus = sus/2 + 16;
	switch(w) {
		case 1: waveset1(att, dec, sus, rel); break;
		case 2: waveset2(att, dec, sus, rel); break;
		case 3: waveset3(att, dec, sus, rel); break;
		case 4: waveset4(att, dec, sus, rel); break;
	}
}

#define BASE1 74
#define BASE2 144
#define LLY(y) (base-y)
#define MXY 63
#define MXX 290
#define SSX0 (MXX-MXVAL)
#define LLX(x) (x+xo)
#define XLL(x) (x-xo)
#define LAG 10

void 
grip_done_proc(paint_window, event, canvas_shell, grip)
	Xv_window	paint_window;
	Event		*event;
	Canvas_shell	canvas_shell;
	Grip		grip;
{
	valton();
	if (check_selected(FALSE) < 0) return;
	(*Dataout)(Global_current_edit_data);
	Changed = 1;
	/* Update Currdata */
/*
	if (Editcol) tocurrent(Global_current_edit_data,Libindex);
	else tocurrent(Global_current_edit_data,Synindex);
*/
	editto(Editrow,Editcol);
}

int
grip_move_proc(paint_window, event, canvas_shell, grip, new_x, new_y)
	Xv_window	paint_window;
	Event		*event;
	Canvas_shell	canvas_shell;
	Grip		grip;
	short		*new_x;
	short		*new_y;
{
	int x, y, base, xo, wave = 1;
	int x0, y0, x1, y1, x2, y2, x3, y3;

	if (event_middle_is_down(event)) return(TRUE);

	if (*new_y <= BASE1 - LAG) base = BASE1 - LAG;
	else {
		base = BASE2 - LAG;
		wave = 2;
	}
	xo = 20 - LAG;
	if (nwaves[Synthnumber] < 3) xo = 160 - LAG;
	else if (*new_x > 320) {
		xo = 340 - LAG;
		wave += 2;
	}
	if (wave > nwaves[Synthnumber]) return(TRUE);

	x = XLL(*new_x);
	y = LLY(*new_y);
	if (x < 0 || x > MXX || y < 0 || y > MXY) return(TRUE);

	switch(wave) {
		case 1: x0 = xv_get(wave1[Synthnumber][0],
				DRAWLINE_X, 0);
			y0 = xv_get(wave1[Synthnumber][0],
				DRAWLINE_Y, 0);
			x1 = xv_get(wave1[Synthnumber][1],
				DRAWLINE_X, 1);
			y1 = xv_get(wave1[Synthnumber][1],
				DRAWLINE_Y, 1);
			break;
		case 2: x0 = xv_get(wave2[Synthnumber][0],
				DRAWLINE_X, 0);
			y0 = xv_get(wave2[Synthnumber][0],
				DRAWLINE_Y, 0);
			x1 = xv_get(wave2[Synthnumber][1],
				DRAWLINE_X, 1);
			y1 = xv_get(wave2[Synthnumber][1],
				DRAWLINE_Y, 1);
			break;
		case 3: x0 = xv_get(wave3[Synthnumber][0],
				DRAWLINE_X, 0);
			y0 = xv_get(wave3[Synthnumber][0],
				DRAWLINE_Y, 0);
			x1 = xv_get(wave3[Synthnumber][1],
				DRAWLINE_X, 1);
			y1 = xv_get(wave3[Synthnumber][1],
				DRAWLINE_Y, 1);
			break;
		case 4: x0 = xv_get(wave4[Synthnumber][0],
				DRAWLINE_X, 0);
			y0 = xv_get(wave4[Synthnumber][0],
				DRAWLINE_Y, 0);
			x1 = xv_get(wave4[Synthnumber][1],
				DRAWLINE_X, 1);
			y1 = xv_get(wave4[Synthnumber][1],
				DRAWLINE_Y, 1);
			break;
	}

	if (*new_x < x0 && x0 < *new_x + 2*LAG
	 && *new_y < y0 && y0 < *new_y + 2*LAG) {

	    if (x > MXVAL || *new_x+LAG > x1 || x1 - *new_x+LAG > MXVAL) return(TRUE);
	    gdone[wave][ATTACK] = x;
	    gdone[wave][DECAY] = x1 - *new_x+LAG;

	    switch(wave) {
		case 1: xv_set(wave1[Synthnumber][0],
				DRAWLINE_X, 0, *new_x+LAG, NULL);
			xv_set(wave1[Synthnumber][1],
				DRAWLINE_X, 0, *new_x+LAG, NULL);
			break;
		case 2: xv_set(wave2[Synthnumber][0],
				DRAWLINE_X, 0, *new_x+LAG, NULL);
			xv_set(wave2[Synthnumber][1],
				DRAWLINE_X, 0, *new_x+LAG, NULL);
			break;
		case 3: xv_set(wave3[Synthnumber][0],
				DRAWLINE_X, 0, *new_x+LAG, NULL);
			xv_set(wave3[Synthnumber][1],
				DRAWLINE_X, 0, *new_x+LAG, NULL);
			break;
		case 4: xv_set(wave4[Synthnumber][0],
				DRAWLINE_X, 0, *new_x+LAG, NULL);
			xv_set(wave4[Synthnumber][1],
				DRAWLINE_X, 0, *new_x+LAG, NULL);
			break;
	    }
	    return(TRUE);
	}


	if (*new_x < x1 && x1 < *new_x + 2*LAG
	 && *new_y < y1 && y1 < *new_y + 2*LAG) {

	    if (*new_x+LAG < x0 || *new_x+LAG > x0+MXVAL) return(TRUE);
	    gdone[wave][DECAY] = *new_x+LAG - x0;

	    switch(wave) {
		case 1: xv_set(wave1[Synthnumber][1],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			xv_set(wave1[Synthnumber][2],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			break;
		case 2: xv_set(wave2[Synthnumber][1],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			xv_set(wave2[Synthnumber][2],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			break;
		case 3: xv_set(wave3[Synthnumber][1],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			xv_set(wave3[Synthnumber][2],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			break;
		case 4: xv_set(wave4[Synthnumber][1],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			xv_set(wave4[Synthnumber][2],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			break;
	    }
	    return(TRUE);
	}

	switch(wave) {
		case 1: x2 = xv_get(wave1[Synthnumber][3],
				DRAWLINE_X, 0);
			x3 = xv_get(wave1[Synthnumber][3],
				DRAWLINE_X, 1);
			break;
		case 2: x2 = xv_get(wave2[Synthnumber][3],
				DRAWLINE_X, 0);
			x3 = xv_get(wave2[Synthnumber][3],
				DRAWLINE_X, 1);
			break;
		case 3: x2 = xv_get(wave3[Synthnumber][3],
				DRAWLINE_X, 0);
			x3 = xv_get(wave3[Synthnumber][3],
				DRAWLINE_X, 1);
			break;
		case 4: x2 = xv_get(wave4[Synthnumber][3],
				DRAWLINE_X, 0);
			x3 = xv_get(wave4[Synthnumber][3],
				DRAWLINE_X, 1);
			break;
	}
	y2 = y1;

	if (*new_x < x2 && x1 < *new_x + 2*LAG
	 && *new_y < y2 && y2 < *new_y + 2*LAG) {

	    if (y < 16 || y >= 16+32) return(TRUE);
	    gdone[wave][SUSTAIN] = 2*(y - 16);

	    switch(wave) {
		case 1: xv_set(wave1[Synthnumber][1],
				DRAWLINE_Y, 1, *new_y+LAG, NULL);
			xv_set(wave1[Synthnumber][2],
				DRAWLINE_Y, 0, *new_y+LAG,
				DRAWLINE_Y, 1, *new_y+LAG, NULL);
			xv_set(wave1[Synthnumber][3],
				DRAWLINE_Y, 0, *new_y+LAG, NULL);
			break;
		case 2: xv_set(wave2[Synthnumber][1],
				DRAWLINE_Y, 1, *new_y+LAG, NULL);
			xv_set(wave2[Synthnumber][2],
				DRAWLINE_Y, 0, *new_y+LAG,
				DRAWLINE_Y, 1, *new_y+LAG, NULL);
			xv_set(wave2[Synthnumber][3],
				DRAWLINE_Y, 0, *new_y+LAG, NULL);
			break;
		case 3: xv_set(wave3[Synthnumber][1],
				DRAWLINE_Y, 1, *new_y+LAG, NULL);
			xv_set(wave3[Synthnumber][2],
				DRAWLINE_Y, 0, *new_y+LAG,
				DRAWLINE_Y, 1, *new_y+LAG, NULL);
			xv_set(wave3[Synthnumber][3],
				DRAWLINE_Y, 0, *new_y+LAG, NULL);
			break;
		case 4: xv_set(wave4[Synthnumber][1],
				DRAWLINE_Y, 1, *new_y+LAG, NULL);
			xv_set(wave4[Synthnumber][2],
				DRAWLINE_Y, 0, *new_y+LAG,
				DRAWLINE_Y, 1, *new_y+LAG, NULL);
			xv_set(wave4[Synthnumber][3],
				DRAWLINE_Y, 0, *new_y+LAG, NULL);
			break;
	    }
	    return(TRUE);
	}
	y3 = LLY(0);

	if (*new_x < x3 && x3 < *new_x + 2*LAG
	 && *new_y < y3 && y3 < *new_y + 2*LAG) {

	    if (x < SSX0 || x > SSX0+MXVAL) return(TRUE);
	    gdone[wave][RELEASE] = x - SSX0;

	    switch(wave) {
		case 1: xv_set(wave1[Synthnumber][3],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			break;
		case 2: xv_set(wave2[Synthnumber][3],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			break;
		case 3: xv_set(wave3[Synthnumber][3],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			break;
		case 4: xv_set(wave4[Synthnumber][3],
				DRAWLINE_X, 1, *new_x+LAG, NULL);
			break;
	    }
	    return(TRUE);
	}


	return(TRUE);
}

drawbase(int base, int xo)
{
	(void)xv_create(shell[Synthnumber], DRAWLINE, 
		RECTOBJ_FG,	CMS_CONTROL_BG2,
		DRAWLINE_X, 0, LLX(MXX),
		DRAWLINE_Y, 0, LLY(0),
		DRAWLINE_ARROW_STYLE, 0, ARROW_FILLED, 
		DRAWLINE_X, 1, LLX(0)-2,
		DRAWLINE_Y, 1, LLY(0),
		NULL);
	(void)xv_create(shell[Synthnumber], DRAWLINE, 
		RECTOBJ_FG,	CMS_CONTROL_BG2,
		DRAWLINE_X, 0, LLX(0)-2,
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_ARROW_STYLE, 0, ARROW_FILLED, 
		DRAWLINE_X, 1, LLX(0)-2,
		DRAWLINE_Y, 1, LLY(0),
		NULL);
}
makewave1()
{
	int n;
	int base = BASE1;
	int xo = 20;
	if (nwaves[Synthnumber] < 3) xo = 160;

	for (n = 0; n < 4; n++) {
		wave1[Synthnumber][n] = xv_create(shell[Synthnumber], DRAWLINE, 
			RECTOBJ_FG,	0 + CMS_CONTROL_COLORS,
			NULL);
	}
	drawbase(base, xo);
}
waveset1(int att, int dec, int sus, int rel)
{	int base = BASE1;
	int xo = 20;
	if (nwaves[Synthnumber] < 3) xo = 160;

	xv_set(wave1[Synthnumber][0],
		DRAWLINE_X, 0, LLX(att),
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_X, 1, LLX(0),
		DRAWLINE_Y, 1, LLY(0),
		NULL);
	xv_set(wave1[Synthnumber][1],
		DRAWLINE_X, 0, LLX(att),
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_X, 1, LLX(att + dec),
		DRAWLINE_Y, 1, LLY(sus),
		NULL);
	xv_set(wave1[Synthnumber][2],
		DRAWLINE_X, 0, LLX(SSX0),
		DRAWLINE_Y, 0, LLY(sus),
		DRAWLINE_X, 1, LLX(att + dec),
		DRAWLINE_Y, 1, LLY(sus),
		NULL);
	xv_set(wave1[Synthnumber][3],
		DRAWLINE_X, 0, LLX(SSX0),
		DRAWLINE_Y, 0, LLY(sus),
		DRAWLINE_X, 1, LLX(SSX0 + rel),
		DRAWLINE_Y, 1, LLY(0),
		NULL);
}
makewave2()
{
	int n;
	int base = BASE2;
	int xo = 20;
	if (nwaves[Synthnumber] < 3) xo = 160;

	for (n = 0; n < 4; n++) {
		wave2[Synthnumber][n] = xv_create(shell[Synthnumber], DRAWLINE, 
			RECTOBJ_FG,	0 + CMS_CONTROL_COLORS,
			NULL);
	}
	drawbase(base, xo);
}
waveset2(int att, int dec, int sus, int rel)
{
	int base = BASE2;
	int xo = 20;
	if (nwaves[Synthnumber] < 3) xo = 160;

	xv_set(wave2[Synthnumber][0],
		DRAWLINE_X, 0, LLX(att),
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_X, 1, LLX(0),
		DRAWLINE_Y, 1, LLY(0),
		NULL);
	xv_set(wave2[Synthnumber][1],
		DRAWLINE_X, 0, LLX(att),
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_X, 1, LLX(att + dec),
		DRAWLINE_Y, 1, LLY(sus),
		NULL);
	xv_set(wave2[Synthnumber][2],
		DRAWLINE_X, 0, LLX(SSX0),
		DRAWLINE_Y, 0, LLY(sus),
		DRAWLINE_X, 1, LLX(att + dec),
		DRAWLINE_Y, 1, LLY(sus),
		NULL);
	xv_set(wave2[Synthnumber][3],
		DRAWLINE_X, 0, LLX(SSX0),
		DRAWLINE_Y, 0, LLY(sus),
		DRAWLINE_X, 1, LLX(SSX0 + rel),
		DRAWLINE_Y, 1, LLY(0),
		NULL);
}
makewave3()
{
	int n;
	int base = BASE1;
	int xo = 340;

	for (n = 0; n < 4; n++) {
		wave3[Synthnumber][n] = xv_create(shell[Synthnumber], DRAWLINE, 
			RECTOBJ_FG,	0 + CMS_CONTROL_COLORS,
			NULL);
	}
	drawbase(base, xo);
}
waveset3(int att, int dec, int sus, int rel)
{
	int base = BASE1;
	int xo = 340;

	xv_set(wave3[Synthnumber][0],
		DRAWLINE_X, 0, LLX(att),
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_X, 1, LLX(0),
		DRAWLINE_Y, 1, LLY(0),
		NULL);
	xv_set(wave3[Synthnumber][1],
		DRAWLINE_X, 0, LLX(att),
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_X, 1, LLX(att + dec),
		DRAWLINE_Y, 1, LLY(sus),
		NULL);
	xv_set(wave3[Synthnumber][2],
		DRAWLINE_X, 0, LLX(SSX0),
		DRAWLINE_Y, 0, LLY(sus),
		DRAWLINE_X, 1, LLX(att + dec),
		DRAWLINE_Y, 1, LLY(sus),
		NULL);
	xv_set(wave3[Synthnumber][3],
		DRAWLINE_X, 0, LLX(SSX0),
		DRAWLINE_Y, 0, LLY(sus),
		DRAWLINE_X, 1, LLX(SSX0 + rel),
		DRAWLINE_Y, 1, LLY(0),
		NULL);
}
makewave4()
{
	int n;
	int base = BASE2;
	int xo = 340;
	for (n = 0; n < 4; n++) {
		wave4[Synthnumber][n] = xv_create(shell[Synthnumber], DRAWLINE, 
			RECTOBJ_FG,	0 + CMS_CONTROL_COLORS,
			NULL);
	}
	drawbase(base, xo);
}
waveset4(int att, int dec, int sus, int rel)
{
	int base = BASE2;
	int xo = 340;

	xv_set(wave4[Synthnumber][0],
		DRAWLINE_X, 0, LLX(att),
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_X, 1, LLX(0),
		DRAWLINE_Y, 1, LLY(0),
		NULL);
	xv_set(wave4[Synthnumber][1],
		DRAWLINE_X, 0, LLX(att),
		DRAWLINE_Y, 0, LLY(MXY),
		DRAWLINE_X, 1, LLX(att + dec),
		DRAWLINE_Y, 1, LLY(sus),
		NULL);
	xv_set(wave4[Synthnumber][2],
		DRAWLINE_X, 0, LLX(SSX0),
		DRAWLINE_Y, 0, LLY(sus),
		DRAWLINE_X, 1, LLX(att + dec),
		DRAWLINE_Y, 1, LLY(sus),
		NULL);
	xv_set(wave4[Synthnumber][3],
		DRAWLINE_X, 0, LLX(SSX0),
		DRAWLINE_Y, 0, LLY(sus),
		DRAWLINE_X, 1, LLX(SSX0 + rel),
		DRAWLINE_Y, 1, LLY(0),
		NULL);
}
#endif /* GLSS */

showallparms(name)
char *name;
{
	int n;

	if (name == NULL) Buff[0] = '\0';
	else strcpy(Buff, name);
	n = strlen(Buff) - 1;
	while (n >= 0 && Buff[n] == ' ') Buff[n--] = '\0';
	xv_set(edit_voice_label[Synthnumber],
		PANEL_VALUE,	Buff,
		NULL);
	for ( n=0; P[n].p_name != NULL; n++ ) {
		if ( P[n].p_flags != 0 )
			continue;
		showparam(n,0);
	}
}
#else /* not XGL */
/* redraw the parameter screen */
showallparms(name)
char *name;
{
	int n;
	char *s;

	windclear();

	if( Namesize != 0 ) {
		showname(name);
		windgoto(1,0);
		for(n=strlen(name)+6;n>0;n--)
			windputc('=');
	}

	/* The L array contains arbitrary screen labels */
	for ( n=0; L[n].l_text != NULL; n++ ) {
		windgoto(L[n].l_row,L[n].l_col);
		windstr(L[n].l_text);
	}
	/* Display each parameter value, and a label if there is one. */
	for ( n=0; P[n].p_name != NULL; n++ ) {
		if ( P[n].p_flags != 0 )
			continue;
		if ( (s=P[n].p_label) != NULL )
			showstr(s,P[n].p_lrow,P[n].p_lcol,0);
		showparam(n,0);
	}
	windrefresh();
}
#endif

#ifndef XGL
showname(name)
char *name;
{
	windgoto(0,0);
	windstr("Name:                 ");
	windgoto(0,6);
	windstr(name);
}
#endif

showparam(n,eras)
{
	char *p;
#ifdef XGL
	char *q;
	int l, m;
#endif

	/* The p_tovis element of the P array is a function which, given */
	/* the parameter value as an argument, returns a string which is */
	/* what should be displayed on the screen. */
        if (P[n].p_vrow < 0 || P[n].p_vcol < 0) return;
#ifdef DX7
	p = (*(P[n].p_tovis))(P[n].p_val, eras);
#else
	p = (*(P[n].p_tovis))(P[n].p_val);
#endif
#ifdef XGL
	if (eras) return;
	l = strlen(p);
	if (l == 1) q = "  ";
	else if (l == 2) q = " ";
	else q = "";
	strcpy(Buff, q);
	strcat(Buff, p);
	xv_set(P[n].p_button,
		PANEL_LABEL_WIDTH, font_fixed_width*(strlen(Buff)-1),
		PANEL_LABEL_STRING, Buff, NULL);
#else
	showstr(p,P[n].p_vrow,P[n].p_vcol,eras);
#endif
}

#ifndef XGL
#ifdef DX7

/*
 * define defaults for the graphics characters, if they haven't been specified
 * in machdep.
 */

# ifndef DRAW_VERT
#  define DRAW_VERT      '|'
#  define DRAW_HORIZ     '-'
#  define DRAW_CROSS     '+'
#  define DRAW_UPTEE     '+'
#  define DRAW_DOWNTEE   '+'
#  define DRAW_LEFTTEE   '+'
#  define DRAW_RIGHTTEE  '+'
#  define DRAW_UPLEFT    '+'
#  define DRAW_UPRIGHT   '+'
#  define DRAW_DOWNLEFT  '+'
#  define DRAW_DOWNRIGHT '+'
# endif

showstr(p,row,col,eras)
register char *p;
register int col;
{
  register int c;
  int rept, mincol;

  mincol = col;

  windgoto(row,col);
  while ( (c=(*p++)) != '\0' ) {
    switch(c){
      case '~':
        c = *p++;
        if (isdigit(c)) {
          rept = 0;
          while (isdigit(c)) {
            rept = 10*rept + (c-'0');
            c = *p++;
          }
        }
        else
          rept = 1;

        while (rept--) {
          switch( c ) {
            case 'n': row++; col=mincol; goto wgoto;
            case 'u': row--; goto wgoto;
            case 'd': row++; goto wgoto;
            case 'l': col--; if (col < mincol) mincol = col; goto wgoto;
            case 'r': col++;
              wgoto:
                windgoto(row,col);
                break;
            case '|': c=DRAW_VERT;      goto wput;
            case '-': c=DRAW_HORIZ;     goto wput;
            case '+': c=DRAW_CROSS;     goto wput;
            case 'T': c=DRAW_UPTEE;     goto wput;
            case '^': c=DRAW_DOWNTEE;   goto wput;
            case '<': c=DRAW_RIGHTTEE;  goto wput;
            case '>': c=DRAW_LEFTTEE;   goto wput;
            case '{': c=DRAW_UPLEFT;    goto wput;
            case '}': c=DRAW_UPRIGHT;   goto wput;
            case '[': c=DRAW_DOWNLEFT;  goto wput;
            case ']': c=DRAW_DOWNRIGHT;
              wput:
                windputc(eras?' ':c);
                col++;
                break;
            default:
              windputc(eras?' ':c);
              col++;
              break;
          }
        }
        break;
      default:
        windputc(eras?' ':c);
        col++;
        break;
    }
  }
}

#else  /* DX7 */

showstr(p,row,col,eras)
register char *p;
register int col;
{
	register int c;

	windgoto(row,col);
	while ( (c=(*p++)) != '\0' ) {
		switch(c){
		case '~':
			switch( (c=(*p++)) ) {
			case 'u': row--; goto wgoto;
			case 'd': row++; goto wgoto;
			case 'l': col--; goto wgoto;
			case 'r': col++;
			    wgoto:
				windgoto(row,col);
				break;
			default:
				windputc(eras?' ':c);
				col++;
				break;
			}
			break;
		default:
			windputc(eras?' ':c);
			col++;
			break;
		}
	}
}
#endif
#endif /* not XGL */

/* Allow roaming around and changing of parameter values. */
editdata(name,data)
char *name;
char *data;	/* vmem format */
{
	int c, n;

#ifndef XGL
	windclear();
	windrefresh();
#endif
	/* enable all the parameters */
	for ( n=0; P[n].p_name != NULL; n++ )
		enableparm(n);

	/* Take the voice data and put it into P */
	(*Datain)(data);

#ifdef XGL
	showallparms(name);
#endif
	Prow = Pcol = 0;
	Changed = 0;
	Redraw = 1;
	Lastvalue = 0;
#ifndef XGL
	gotoparm(CH_RIGHT);	/* Get to the first parameter */

	if (fditflag) showallparms(name);
	else for ( ;; ) {
		if ( Redraw ) {
			showallparms(name);
			Redraw = 0;
		}
		windgoto(Prow,Pcol);
		windrefresh();
		c = mouseorkey();
		if ( c == MOUSE ) {
			editmouse();
			continue;
		}
		switch(c){
		case CH_RIGHT:
		case ALTCH_RIGHT:
		case CH_UP:
		case ALTCH_UP:
		case CH_DOWN:
		case ALTCH_DOWN:
		case CH_LEFT:
		case ALTCH_LEFT:
			gotoparm(c);
			break;
		case CH_SAME:
			sameparm();
			break;
		case CH_REDRAW:
			Redraw = 1;
			break;
		case 'N':
			if ( Namesize != 0 ) {
			/* Allow changing of voice name */
				windgoto(0,5);
				windstr("                ");
				windgoto(0,6);
				windrefresh();
				windgets(Buff);
				if ( Buff[0]!='\0' && Buff[0]!='\n' )
					(*Setnameof)(data,Buff);
				showname(name=(*Nameof)(data));
				Changed = 1;
			}
			break;

		case CH_INC:
			adjuparm(1);
			break;
		case CH_INC2:
			adjuparm(4);
			break;
		case CH_INC3:
			adjuparm(P[Parm].p_max - P[Parm].p_min);
			break;
		case CH_DEC:
			adjuparm(-1);
			break;
		case CH_DEC2:
			adjuparm(-4);
			break;
		case CH_DEC3:
			adjuparm(P[Parm].p_min - P[Parm].p_max);
			break;
#ifdef OLDSTUFF
		case 'a':
			sendaced(data);
			playnote(0);
			break;
#endif
		case ' ':
		case '\n':
#ifndef macintosh
		case '\r':
#endif
			if ( Changed ) {
				(*Dataout)(data);
				Global_can_play_data_flag = 1;
				Global_can_play_note_flag = 0;
				/*(*Sendedit)(data);*/
				/*Changed = 0;*/
			}
			if (i_can_play()) playnote(0);
			break;
		case '\033':
		case '`':
			allnotesoff();
			break;
		case 'q':
		case EOF:
			if ( Changed ) {
				(*Dataout)(data);
				Global_can_play_data_flag = 1;
				Global_can_play_note_flag = 0;
				/*(*Sendedit)(data);*/
			}
			return;
		default:
			break;
		}
	}
#endif /* not XGL */
}

adjuparm(int incdec)
{
	int v, nv, n;

	v = P[Parm].p_val + incdec;
	nv = P[Parm].p_val - incdec;
	if ( v < (n=P[Parm].p_min) )
		v = n;
	if ( v > (n=P[Parm].p_max) )
		v = n;
	Lastvalue = v;
	Negparm = 0;
	showparam(Parm,1);	/* erase the old val */
	if (Negparm) {		/* the vis- function may have set global */
		v = nv;		/*  Negparm for parm with negative sense (gl)*/
		if ( v < (n=P[Parm].p_min) )
			v = n;
		if ( v > (n=P[Parm].p_max) )
			v = n;
		Lastvalue = v;
	}
	P[Parm].p_val = v;
	Metavalue = 0;
	showparam(Parm,0);	/* show the new val */
	if (!Changed && !Metavalue &&
		Parm != parmindex("autopitch") &&
		Parm != parmindex("autovol") &&
		Parm != parmindex("autodur") &&
		Parm != parmindex("autochan")) {
			Changed = 1;
			Global_can_play_note_flag = 0;
	}
}

sameparm()
{
	int v, n;

	v = Lastvalue;
	if ( v < (n=P[Parm].p_min) )
		v = n;
	if ( v > (n=P[Parm].p_max) )
		v = n;
	Changed = 1;
	showparam(Parm,1);	/* erase the old val */
	P[Parm].p_val = v;
	showparam(Parm,0);	/* show the new val */
}

#ifndef XGL
editmouse()
{
	int row, col, thisparm;

	getmouse(&row,&col);
	thisparm = closeparm(row,col);
	if ( thisparm == Parm ) {
		if ( statmouse() > 1 )
			adjuparm(-1);	/* right button */
		else if ( statmouse() == 1 ) /* added by mab - bug fix */
			adjuparm(1);	/* left button */
	}
	else {
		Parm = thisparm;
		Prow = P[Parm].p_vrow;
		Pcol = P[Parm].p_vcol;
	}
}

/* closeparm - Find the closest parameter */
closeparm(row,col)
{
	register struct paraminfo *pp;
	register int n;
	int dist, mindist, minparm, dr, dc;

	minparm = 0;
	mindist = Rows + Cols;
	for ( n=0,pp=P; pp->p_name != NULL; n++,pp++ ) {
		if ( pp->p_flags != 0 )
			continue;
		if ( (dr=row-(pp->p_vrow)) < 0 )
			dr = -dr;
		if ( (dc=col-(pp->p_vcol)) < 0 )
			dc = -dc;
		if ( (dist=dr*dr+dc*dc) < mindist ) {
			minparm = n;
			mindist = dist;
		}
	}
	return(minparm);
}
#endif

/* playnote - play the 'auto' note */
playnote(i)
{
	int pitch, vol, dur, chan, dest;
	long endtime;

	pitch = getval("autopitch");
	if(i == 0) { /* called from inside edit-mode */
		chan = getval("autochan");
	} else { /* called from the top level */
		chan = Channel;
	}
	vol = getval("autovol");
	dur = getval("autodur");
#ifdef LINUXIO
        if (!strcmp(Synthname, "SoundBlaster")) dest = 1;
       	else if (!strcmp(Synthname, "Opl3")) dest = 1;
       	else if (!strcmp(Synthname, "Ultrasound")) dest = 3;
	else dest = 2;
	playsb(dest,pitch,vol,dur,0);
#else
	endtime = milliclock() + dur * 100;
	midinote(1,chan,pitch,vol);
	while ( milliclock() < endtime )
		;
	midinote(0,chan,pitch,vol);
#endif
}

#ifndef XGL
/* gotoparm - search for the next parameter in the specified direction */
gotoparm(dir)
{
	int n, k, inc, pm, orig, r = Prow, c = Pcol;

	switch(dir) {
	case ALTCH_UP:		dir = CH_UP;	break;
	case ALTCH_DOWN:	dir = CH_DOWN;	break;
	case ALTCH_LEFT:	dir = CH_LEFT;	break;
	case ALTCH_RIGHT:	dir = CH_RIGHT;	break;
	}

	if ( dir==CH_LEFT || dir==CH_RIGHT ) {
		if ( dir==CH_LEFT )
			c--;
		else
			c++;
		orig = c;
		inc = 0;
		pm = -1;
		/* look up and down, alternately */
		for ( n=2*Rows; n>0; n-- ) {
			r += (pm * inc++);
			pm = -pm;
			if ( r < 0 || r >= Rows )
				continue;
			if ( dir == CH_LEFT ) {
				for ( c=orig; c>=0; c-- ) {
					if ( parmat(r,c) )
						return;
				}
			}
			else {
				for ( c=orig; c<Cols; c++ ) {
					if ( parmat(r,c) )
						return;
				}
			}
		}
		return;
	}
	if ( dir==CH_DOWN || dir==CH_UP ) {
		if ( dir==CH_DOWN )
			r++;
		else
			r--;
		orig = c;
		while ( r >= 0 && r < Rows ) {
			/* look toward both sides at the same time */
			inc = 0;
			pm = -1;
			for ( k=2*Cols; k>0; k-- ) {
				c += (pm * inc++);
				pm = -pm;
				if ( c < 0 || c >= Cols )
					continue;
				if ( parmat(r,c) )
					return;
			}
			if ( dir==CH_DOWN )
				r++;
			else
				r--;
			c = orig;
		}
		return;
	}
}
#endif

#ifndef XGL
/* paramat - return non-zero if a parameter value is at position r,c */
parmat(r,c)
register int r, c;
{
#ifdef SSS
  int p = PARAMAT(r, c);

  if (p == 0 || P[p-1].p_flags)
    return 0;
  else {
    Parm = p-1;
    Prow = r;
    Pcol = c;
    return 1;
  }
#else
	register int n;
	register struct paraminfo *pp;

	for ( n=0,pp=P; pp->p_name != NULL; n++,pp++ ) {
		if ( pp->p_flags != 0 )
			continue;
		if ( pp->p_vrow==r && pp->p_vcol==c ) {
			Prow = r;
			Pcol = c;
			Parm = n;
			return(1);
		}
	}
	return(0);
#endif
}
#endif

/* parmindex - return index (in P) of a given parameter name. */
parmindex(name)
char *name;
{
#ifdef SSS
  struct paraminfo key, *p;
  extern void *bsearch();

  key.p_name = name;
  p = (struct paraminfo *)bsearch((void *)&key, (void *)P, NParams,
		sizeof(struct paraminfo), paramcmp);
  if (p == 0) {
#ifndef XGL
    (void)sprintf(Buff,"HEY, PARMINDEX(%s) NOT FOUND!\n",name);
    windstr(Buff);
    windrefresh();
#endif
    return(-1);
  }
  else
    return p-P;
#else
	int n;
	char *s;

	for ( n=0; (s=P[n].p_name) != NULL; n++ ) {
		if ( strcmp(s,name) == 0 )
			return(n);
	}
#ifndef XGL
	(void)sprintf(Buff,"HEY, PARMINDEX(%s) NOT FOUND!\n",name);
	windstr(Buff);
	windrefresh();
#endif
	return(-1);
#endif
}

void
setval(name,v)
INT16 v;
char *name;
{
	int n;

	if ( (n=parmindex(name)) < 0 )
		return;
	P[n].p_val = v;
}

int
getval(name)
char *name;
{
	int n;
/***
if (!strcmp(name,"rrate")||!strcmp(name,"rspeed")) {
n=parmindex(name);
sprintf(Buff, "%s, n=%d, v=%d", name, n, P[n].p_val);
windgoto(2+n,0);
windstr(Buff);
}
***/
	if ( (n=parmindex(name)) < 0 )
		return(0);

	if (pfoutflag)
		fprintf(pfout, "%s = %d\n", name, P[n].p_val);

	return(P[n].p_val);
}


enableparm(n)
{
	if ( P[n].p_flags != 0 )
		P[n].p_flags = 0;
}

disableparm(n)
{
	if ( P[n].p_flags == 0 )
		P[n].p_flags = 1;
}

midinote(onoff,chan,pitch,vol)
{
	sendmidi( ((onoff==1)?(0x90):(0x80)) | ((chan-1)&0xf) );
	sendmidi( pitch & 0x7f );
	sendmidi( vol & 0x7f );
}

static char Nbuff[16];

char *visnum(v)
{
	(void)sprintf(Nbuff,"%d",v);
	return(Nbuff);
}
char *visonoff(v)
{
	if ( v==0 )
		return("OFF");
	else
		return("ON");
}

char *
bankvoice(voice)
{
	int offset = Libbank * Nvoices * Voicesize + voice * Voicesize;
	return(Libdata + offset);
}
