/* noteoff.c -- this module keeps track of pending note offs for adagio */

/*****************************************************************************
*	    Change Log
*  Date	    | Change
*-----------+-----------------------------------------------------------------
* 31-Dec-85 | Created changelog
* 31-Dec-85 | Add c:\ to include directives
*  1-Jan-86 | Declare malloc char * for lint consistency
* 21-Jan-86 | note_offs can now turn off more than one note per call
*****************************************************************************/

#include "cext.h"
#include "stdio.h"
#include "ctype.h"
#include "malloc.h"
#include "adagio.h"

/* off_type is a structure containing note-off information */

typedef struct off_struct {
    long when;
    int voice;
    int pitch;
    struct off_struct *next;
} *off_type;

private off_type free_off;		/* free list of off_type structures */
private off_type off_events = NULL;	/* active list */
extern void *malloc();

/****************************************************************************
*	Routines declared in this module
****************************************************************************/

	boolean		note_offs();
private off_type	off_alloc();
private void		off_free();
	void		off_init();
	void		off_schedule();

/****************************************************************************
*				note_offs
* Inputs:
*	long time: the current time
* Outputs:
*	return true if off list has more notes 
* Effect: turn off notes if it is time 
* Assumes:
* Implementation:
*	Find scheduled note off events in off_events, compare with time
****************************************************************************/

boolean note_offs(time)
long time;
{
    off_type temp;
    while (off_events != NULL && off_events->when <= time) {
	midi_note((off_events->voice) + 1, off_events->pitch, 0);
	temp = off_events;
	off_events = off_events->next;
	off_free(temp);
    }
    return (off_events != NULL);
}

/****************************************************************************
*				off_alloc
* Outputs:
*	returns off_type: an allocated note off structure
* Effect:
*	allocates a structure using malloc
****************************************************************************/

private off_type off_alloc()
{
    return (off_type) malloc(sizeof(struct off_struct));
}

/****************************************************************************
*				off_free
* Inputs:
*	off_type off: a structure to deallocate
* Effect: 
*	returns off to freelist
****************************************************************************/

private void off_free(off)
    off_type off;
{
    off->next = free_off;
    free_off = off;
}

/****************************************************************************
*				off_init
* Effect: initialize this module
* Assumes:
*	only called once, otherwise storage is leaked
****************************************************************************/

void off_init()
{
    int i;
    for (i = 0; i < 50; i++) off_free(off_alloc());
}

/****************************************************************************
*				off_schedule
* Inputs:
*	long offtime: time to turn note off
*	int voice: the midi channel
*	int pitch: the pitch
* Effect: 
*	schedules a note to be turned off
* Assumes:
*	note_offs will be called frequently to actually turn off notes
****************************************************************************/

void off_schedule(offtime, voice, pitch)
    long offtime;
    int voice, pitch;
{
    off_type off, ptr, prv;
    /* allocate off */
    if ((off = free_off) == NULL) {
	off = off_alloc();
    } else free_off = off->next;

    if (off == NULL) {
	fprintf(stderr, "out of space for note off events");
	musicterm();
	exit(1);
    }

    off->when = offtime;
    off->voice = voice;
    off->pitch = pitch;
    /* insert into list of off events */
    ptr = off_events;
    if (ptr == NULL || offtime <= ptr->when) {
	off->next = ptr;
	off_events = off;
    } else {
	while (ptr != NULL && offtime > ptr->when) {
	    prv = ptr;
	    ptr = ptr->next;
	}
	prv->next = off;
	off->next = ptr;
    }
/*
 *    printf("off_schedule(%ld, %d, %d): \n", offtime, voice, pitch);
 *    for (ptr = off_events; ptr != NULL; ptr = ptr->next) {
 *	printf("    %ld: %d, %d\n", ptr->when, ptr->voice, ptr->pitch);
 *    }
 */
}
