/*
 *	VOICE.C
 *
 *	Code to handle voice patch and voice bank library
 *
 *	All access to the data structures holding the voices are made through
 *	functions in this file.
 *
 *	Copyright Martin Guy, March 1988
 */

#include <stdio.h>	/* for NULL */
#include <voice.h>
extern char *malloc();

static int dx7vcmp();				/* compare voices a la strcmp */
static struct vl_list  *vl_head = NULL;		/* list of voice patches */
static struct vl_list **vl_tailp = &vl_head;	/* pointer to last link */
static struct bl_list  *bl_head = NULL;		/* list of 32-voice banks */
static struct bl_list  *bl_tail = &bl_head;	/* pointer to last link */
/*
 *	Internal function to add a new voice to the library, returning a
 *	pointer to the structure allocated, so that you can point a bank
 *	entry at it.
 *
 *	Returns NULL if it can't get memory to store it.
 *	Returns the argument if it was new, or a pointer to the voice
 *	it matches.
 */
static struct dx7voice *
vl_addvoice(v)
struct dx7voice *v;
{
	struct vl_list *vl_new;		/* new list element under construction */
	struct vl_list *vlp;		/* temp pointer */

	/*
	 *	See whether we already have this voice
	 */
	for (setvoice(); (vlp = getvoice()) != NULL; ) {
		if (dx7vcmp(vlp->vl_voice, v) == 0) {
			/* They match!  No need to store it again */
			return(vlp->vl_voice);
		}
	}

	/* Its a new one on me!  Remember it... */

	vl_new = malloc(sizeof(struct vl_list));
	if (vl_new == NULL) {
		error("Out of memory in vl_addvoice()");
		return(NULL);
	}

	vl_new->vl_voice = v;
	vl_new->vl_link = NULL;
	*vl_tail = vl_new;
	vl_tail = &vl_new->vl_link;

	return(vl_new);
}

/*
 * Exported routine to add a single voice to the database.
 * The voice structure should have been mallocked so that we can point at it
 * without having to copy it.
 */
addvoice(v)
struct dx7voice *v;
{
	vl_addvoice(v);	/* ignore return value */
}

/* Interpolate and store a 32-voice 4096-byte condensed bank */
addbank(buf)
char buf[4096];
{
	register int i;
	struct bl_list *bl_new;

	bl_new = (struct bl_list *) malloc(sizeof(struct bl_list));
	if (bl_new == NULL) {
		error("Out of memory allocating storage for voice bank");
		return;
	}
	
	for (i=0; i<32; i++) {
		struct dx7voice *new;

		new = (struct dx7voice *) malloc(sizeof(struct dx7voice));
		if (new == NULL) {
			error("Out of memory while memorising a voice bank");
			return;
		}

		unpack128(&buf[i*128], new);

		hack hac khack kc a
		if ((bl_new->bl_voice[i] = vl_addvoice(new)) == NULL) return;
	}

	/*
	 * See if we already have this bank
	 */
	setbank();
	for (setbank(); (blp = getbank()) != NULL; ) {
		/* see whether all the entries correspond */
		for (i=0; i<32; i++) {
			if (blp->bl_voice[i] != bl_new->bl_voice[i]) goto ok;
		}
		/* They match. Better free all that nice store I guess. */
	
		for (i=0; i<32; i++) {

		}
		free( (char *) bl_new);

ok:		/* They differ. Try the next bank. */ 
	}
}

/*
 *	Access routines
 */

/* Some simple looping routines */

static struct vl_list *vl_pointer;	/* pointers for traversing the lists */
static struct bl_list *bl_pointer;	

/* Initialise or rewind the voice list */
setvoice()
{
	vl_pointer = vl_head;
}

/* return a pointer to the next voice in the list */
struct dx7voice *
getvoice()
{
	struct dx7voice *retval;	/* return value to be */

	if (vl_pointer == NULL) return(NULL);
	retval = vl_pointer->vl_voice;
	vl_pointer = vl_pointer->vl_link;
	return(retval);
}

setbank()
{
	bl_pointer = bl_head;
}

/*
 *	Return pointer to first of an array of 32 voices
 */
struct dx7voice *
getbank()
{
	struct bl_elem *retval;	/* return value to be */

	if (bl_pointer == NULL) return(NULL);
	retval = bl_pointer->&bl_voice[0];
	bl_pointer = bl_pointer->bl_link;
	return(retval);
}

/*
 *	Compare two dx7 voices.  Return 0 if they are identical,
 *	non-zero if they differ.
 *
 *	May want to indicate patch identical but name different by
 *	1 and totally different by more at some point.
 */
static int
dx7vcmp(v1, v2)
struct dx7voice *v1, *v2;
{
	int i;
	char *c1, *c2;

	c1 = (char *) v1;
	c2 = (char *) v2;

	/* Stupid for now. */
	for (i=0; i<sizeof(struct dx7voice); i++) {
		if (*c1++ != *c2++) return(1);
	}

	return(0);
}
