/* seq.c -- implement adagio scores as abstract data type */ /***************************************************************************** * Change Log * Date | Change *-----------+----------------------------------------------------------------- * 2-Apr-91 | JDW : further changes * 16-Feb-92 | GWL : use reg_timebase in seq_play() * 28-Apr-03 | DM : false->FALSE, true->TRUE, portability changes * 19-May-03 | RBD : no longer assume seq->current remains untouched between * | note inserts *****************************************************************************/ #include "stdio.h" #include "cext.h" #include "userio.h" #include "midicode.h" #include "midifns.h" #include "timebase.h" #include "moxc.h" #include "seq.h" #include "string.h" extern int moxcdebug; extern timebase_type default_base; boolean seq_print = FALSE; /* debugging print switch */ seq_type sequence; /* this is a global to be accessed by routines called * from the sequence */ /* clock state: */ time_type clock_ticksize; /* millisec per tick shifted 16 bits */ boolean clock_running = FALSE; /* TRUE if clock is running */ boolean external_midi_clock = FALSE; boolean suppress_midi_clock = FALSE; private void insert_event(); private void process_event(); private char *chunk_alloc(); private void clock_tick(); private void ramp_event(seq_type seq, event_type event, unsigned int value, unsigned int to_value, int increment, time_type step, int n); /*private*/ void send_macro(); /* chunk_alloc -- allocate data for a sequence */ /* * NOTE: This assumes one chunk is already allocated. * The first chunk holds shared sequence information in * the info struct, and by convention this is always in * the first chunk. */ private char *chunk_alloc(seq_type seq, int size) { chunk_type chunk = seq->chunklist->u.info.last_chunk; /* gprintf(TRANS, "chunk_alloc: seq %lx size %d\n", seq, size); */ if (size & 1) size++; /* make it even */ if (chunk->free + size >= CHUNK_SIZE) { chunk_type new_chunk = chunk_create(FALSE); if (!chunk) { gprintf(FATAL, "Out of memory while reading seq\n"); return NULL; } /* add new_chunk to chunk chain */ seq->chunklist->u.info.last_chunk = new_chunk; chunk->next = new_chunk; chunk = new_chunk; } chunk->free += size; return &(chunk->u.data[chunk->free - size]); } /* chunk_create -- create a new chunk for seq data */ /* * If this is the first chunk, set first_flag to reserve * space for the info structure. */ chunk_type chunk_create(boolean first_flag) { chunk_type result = (chunk_type) memget(sizeof(chunk_node)); if (result) { result->next = NULL; result->u.info.refcount = 1; /* pre-initialize for caller */ result->free = 0; if (first_flag) { result->free = sizeof(struct info_struct); result->u.info.last_chunk = result; result->u.info.dictionary = NULL; result->u.info.eventlist = NULL; result->u.info.ctrlcount = 0; result->u.info.notecount = 0; result->u.info.duration = 0; result->u.info.used_mask = 0; } } /* gprintf(TRANS, "chunk_create: got %lx (size %d)\n", */ /* result, sizeof(chunk_node)); */ return result; } /* clock_tick -- advance the clock and send a tick */ /**/ private void clock_tick(seq, fraction) seq_type seq; time_type fraction; { int delay; fraction += clock_ticksize; delay = fraction >> 16; fraction &= 0xFFFF; if (seq->runflag && clock_ticksize && seq->note_enable) { midi_clock(); cause((delay_type)delay, clock_tick, seq, fraction); } else { clock_running = FALSE; midi_stop(); midi_clock(); /* stop takes effect on next clock, so provide one */ } } private void cycle(seq) seq_type seq; { seq_reset(seq); seq_play(seq); } /**************************************************************************** * event_create * Inputs: * seq_type seq: the seq to hold the event * int size: the size of the event in bytes * time_type etime: the time of the event * int eline: the line number of the event * Returns: * event_type: a new event structure or * NULL if there is not enough memory left * Effect: * allocates memory from the chunk, then heap as needed * Implementation: * to reduce the per block storage overhead, we allocate memory in * large chunks and do our own allocation. Allocate from first * chunk first. If full, allocate a new chunk. * WARNING: this implementation assumes that individual events are never freed!! ****************************************************************************/ private event_type event_create(seq, size, etime, eline) seq_type seq; int size; time_type etime; int eline; { event_type result = (event_type) chunk_alloc(seq, size); if (result) { result->ntime = etime; result->nline = eline; /* since we know the time, we can insert now: */ insert_event(seq, result); seq_duration(seq) = MAX(seq_duration(seq), etime); } return result; } /* insert_call -- add a call event to the seq */ /**/ event_type insert_call(seq, ctime, cline, voice, addr, value, n) seq_type seq; time_type ctime; int cline; int voice; int (*addr)(); long value[SEQ_MAX_PARMS]; int n; { int i; register event_type event = event_create(seq, callsize, ctime, cline); if (seq_print) { gprintf(TRANS, "call(%lx): time %ld, line %d, voice %d, fn %lx,\n\tvalues:", event, ctime, cline, voice, addr); for (i = 0; i < n; i++) gprintf(TRANS, " %ld", value[i]); gprintf(TRANS, "\n"); } if (event) { seq_used_mask(seq) |= 1 << (voice - 1); event->nvoice = ctrl_voice(ESC_CTRL, voice); event->value = CALL_VALUE; event->u.call.routine = addr; /* save the arguments */ for (i = 0; i < n; i++) event->u.call.args.a[i] = value[i]; seq_ctrlcount(seq)++; } return event; } /* insert_clock -- add a clock cmd to the seq */ /**/ event_type insert_clock(seq, ctime, cline, ticksize) seq_type seq; time_type ctime; int cline; time_type ticksize; { register event_type event = event_create(seq, clocksize, ctime, cline); if (seq_print) { gprintf(TRANS, "clock(%lx): time %ld, line %d\n", event, ctime, cline); } if (event) { event->nvoice = ctrl_voice(ESC_CTRL, 1); event->value = CLOCK_VALUE; event->u.clock.ticksize = ticksize; seq_ctrlcount(seq)++; } return event; } /* insert_ctrl -- add a control to the seq */ /**/ event_type insert_ctrl(seq, ctime, cline, ctrl, voice, value) seq_type seq; time_type ctime; int cline; int ctrl; int voice; int value; { register event_type event = event_create(seq, ctrlsize, ctime, cline); if (seq_print) { gprintf(TRANS, "ctrl(%lx): time %ld, line %d, ctrl %d, voice %d, value %d\n", event, ctime, cline, ctrl, voice, value); } if (event) { seq_used_mask(seq) |= 1 << (voice - 1); event->nvoice = ctrl_voice(ctrl, voice); event->value = value; seq_ctrlcount(seq)++; } return event; } /* insert_ctrlramp -- add a control ramp event to the seq */ /**/ event_type insert_ctrlramp(seq, rtime, rline, voice, step, dur, ctrl, v1, v2) seq_type seq; time_type rtime; int rline; int voice; time_type step; time_type dur; int ctrl; int v1, v2; { register event_type event = event_create(seq, ctrlrampsize, rtime, rline); if (seq_print) { gprintf(TRANS, "ctrlramp(%lx): time %ld, line %d, step %ld, dur %ld, ctrl %d, voice %d\n", event, rtime, rline, step, dur, ctrl, voice); gprintf(TRANS, "\tfrom %d to %d\n", v1, v2); } if (event) { seq_used_mask(seq) |= 1 << (voice - 1); event->nvoice = ctrl_voice(ESC_CTRL, voice); event->value = CTRLRAMP_VALUE; if (dur <= 0) dur = 1L; /* don't allow zero duration */ event->u.ramp.dur = dur; event->u.ramp.ctrl = ctrl; if (step <= 0) step = 1; /* don't allow zero step size */ event->u.ramp.step = (short) step; event->u.ramp.u.ctrl.from_value = v1; event->u.ramp.u.ctrl.to_value = v2; seq_ctrlcount(seq)++; seq_duration(seq) = MAX(seq_duration(seq), rtime + dur); } return event; } /* insert_def -- add a definition to the dictionary */ /**/ def_type insert_def(seq, symbol, definition, deflen) seq_type seq; char *symbol; unsigned char *definition; int deflen; { int i; def_type defn = (def_type) chunk_alloc(seq, sizeof(def_node)); defn->symbol = chunk_alloc(seq, strlen(symbol) + 1); defn->definition = (unsigned char *) chunk_alloc(seq, deflen); strcpy(defn->symbol, symbol); for (i = 0; i < deflen; i++) { defn->definition[i] = definition[i]; } defn->next = seq_dictionary(seq); seq_dictionary(seq) = defn; if (seq_print) { gprintf(TRANS, "def(%ld): symbol %s defn \n", defn, symbol); for (i = 0; i < deflen; i++) gprintf(TRANS, "%x", definition[i]); gprintf(TRANS, "\n"); } return defn; } /* insert_deframp -- add a def ramp event to the seq */ /**/ event_type insert_deframp(seq, rtime, rline, voice, step, dur, def, nparms, parms, parm_num, to_value) seq_type seq; time_type rtime; int rline; int voice; time_type step; time_type dur; def_type def; int nparms; /* number of parameters for macro */ short parms[]; /* actual parameter vector */ int parm_num; /* which of the actual parameters to ramp */ int to_value; /* final destination of ramp */ { register event_type event = event_create(seq, deframpsize, rtime, rline); if (seq_print) { int i; gprintf(TRANS, "deframp(%ld): time %ld, line %d, voice %d, step %ld, dur %ld\n", event, rtime, rline, voice, step, dur); gprintf(TRANS, "def %ld, parms"); for (i = 0; i < nparms; i++) gprintf(TRANS, " %d", parms[i]); gprintf(TRANS, "parm_num %d to %d\n", parm_num, to_value); } if (event) { int i; seq_used_mask(seq) |= 1 << (voice - 1); event->nvoice = ctrl_voice(ESC_CTRL, voice); event->value = DEFRAMP_VALUE; if (dur <= 0) dur = 1L; /* don't allow zero duration */ event->u.ramp.dur = dur; event->u.ramp.ctrl = 0; if (step <= 0) step = 1; /* don't allow zero step size */ event->u.ramp.step = (short) step; event->u.ramp.u.def.definition = def->definition; for (i = 0; i < nmacroparms; i++) { event->u.ramp.u.def.parameter[i] = (i < nparms ? parms[i] : 0); } event->u.ramp.u.def.parm_num = parm_num; event->u.ramp.u.def.to_value = to_value; seq_ctrlcount(seq)++; seq_duration(seq) = MAX(seq_duration(seq), rtime + dur); } return event; } /**************************************************************************** * insert_event * Inputs: * seq_type seq: where to put the event * event_type event: the event to insert * Effect: * inserts event into the event list * NOTE: it is inserted *after* previously inserted events with the same time * Implementation: * adagio files often contain many independent voices. Although each voice * consists of events in sequence, the voices need not be inter-twined in * the input file. Rather, all the events of voice 1 appear followed by all * the events of voice 2, and so forth. As phase one merges these event * sequences, it must make many passes over an increasingly long list of * events: expensive if we always start from the beginning of the list! * we can exploit the fact that each voice is sequential by starting the * search for the proper point of insertion at the last event inserted. * the variable "last_event" is used to remember this hint. We could * also snapshot "last_event" in "ref_event" when a !tempo or !rate * command occurs as another hint, but we don't. ****************************************************************************/ private void insert_event(seq, event) seq_type seq; register event_type event; { event_type *evlptr = &(seq_eventlist(seq)); if ((*evlptr == NULL) || (event->ntime < (*evlptr)->ntime)) { /* insert at the head of the list */ event->next = *evlptr; *evlptr = event; seq->current = event; } else { /* insert somewhere after the head of the list * do not assume: current is not NULL. Although we always leave * it set, the client may access the sequence before the next * insert. */ register event_type previous; register event_type insert_before; if (!seq->current) { seq->current = seq_eventlist(seq); } if (event->ntime >= seq->current->ntime) { /* insertion point is after current */ previous = seq->current; insert_before = previous->next; } else { /* insertion point is before current; start at beginning */ /* assume: not inserting at very head of list; that would * have been taken care of above */ previous = seq_events(seq); insert_before = previous->next; } while ((insert_before != NULL) && (event->ntime >= insert_before->ntime)) { previous = insert_before; insert_before = insert_before->next; } previous->next = event; event->next = insert_before; seq->current = event; } } /* insert_macctrl -- add a control to the seq */ /**/ event_type insert_macctrl(seq, ctime, cline, ctrl, voice, value) seq_type seq; time_type ctime; int cline; int ctrl; int voice; int value; { register event_type event = event_create(seq, macctrlsize, ctime, cline); if (seq_print) { gprintf(TRANS, "macctrl(%lx): time %ld, line %d, ctrl %d, voice %d, value %d\n", event, ctime, cline, ctrl, voice, value); } if (event) { seq_used_mask(seq) |= 1 << (voice - 1); event->nvoice = ctrl_voice(ESC_CTRL, voice); event->value = MACCTRL_VALUE; event->u.macctrl.ctrl_number = ctrl; event->u.macctrl.value = value; seq_ctrlcount(seq)++; } return event; } /* insert_macro -- insert a macro call seq */ /**/ event_type insert_macro(seq, ctime, cline, def, voice, nparms, parms) seq_type seq; time_type ctime; int cline; def_type def; int voice; int nparms; short *parms; { register event_type event = event_create(seq, macrosize, ctime, cline); if (seq_print) { int i; gprintf(TRANS, "macro(%lx): time %ld, line %d, def %ld, voice %d, parms", event, ctime, cline, def, voice); for (i = 0; i < nparms; i++) gprintf(TRANS, " %d", parms[i]); gprintf(TRANS, "\n"); } if (event) { seq_used_mask(seq) |= 1 << (voice - 1); event->nvoice = ctrl_voice(ESC_CTRL, voice); event->value = MACRO_VALUE; event->u.macro.definition = def->definition; while (nparms-- > 0) { event->u.macro.parameter[nparms] = parms[nparms]; } seq_ctrlcount(seq)++; } return event; } /* insert_note -- add a note to the seq */ /**/ event_type insert_note(seq, ntime, nline, voice, pitch, dur, loud) seq_type seq; time_type ntime; int nline; int voice; int pitch; time_type dur; int loud; { register event_type event = event_create(seq, notesize, ntime, nline); if (seq_print) { gprintf(TRANS, "note(%lx): time %ld, line %d, dur %ld, pitch %d, voice %d, loudness %d\n", event, ntime, nline, dur, pitch, voice, loud); } if (event) { seq_used_mask(seq) |= 1 << (voice - 1); event->nvoice = voice - 1; event->value = pitch; event->u.note.ndur = (dur << 8) + loud; seq_notecount(seq)++; seq_duration(seq) = MAX(seq_duration(seq), ntime + dur); } return event; } /* insert_seti -- add a seti event to the seq */ /**/ event_type insert_seti(seq, stime, sline, voice, addr, value) seq_type seq; time_type stime; int sline; int voice; int *addr; int value; { register event_type event = event_create(seq, setisize, stime, sline); if (seq_print) { gprintf(TRANS, "seti(%ld): time %ld, line %d, voice %d, addr %ld, value %d\n", event, stime, sline, voice, addr, value); } if (event) { event->nvoice = ctrl_voice(ESC_CTRL, voice); event->value = SETI_VALUE; event->u.seti.int_to_set = addr; event->u.seti.value = value; seq_ctrlcount(seq)++; } return event; } /* noop -- just returns, the default stopfunc for sequences */ /**/ void noop(seq_type seq) {} private void process_event(seq) seq_type seq; { register event_type event; if (!seq->runflag) return; while ((event = seq->current) && (event->ntime <= virttime)) { int voice; /* process all current (and earlier) events */ if (is_note(event)) { /*** play a note or rest ***/ /* if this note is not a rest, play it and schedule an off event */ if (event->value != NO_PITCH && (seq_channel_mask(seq) & (1 << ((voice = vc_voice(event->nvoice)) - 1)))) { seq_noteon(seq, voice, event->value, (int) event->u.note.ndur & 0xFF); if (debug) { gprintf(TRANS, "play pitch %d at %ld\n", event->value, event->ntime); } seq_cause_noteoff(seq, (event->u.note.ndur) >> 8, voice, event->value); } } else { /*** send a control command ***/ int n; time_type step; int delta; long increment; int voice = vc_voice(event->nvoice); ulong enabled = seq_channel_mask(seq) & (1 << (voice - 1)); switch (vc_ctrl(event->nvoice)) { case PSWITCH_CTRL: if (!enabled) break; seq_midi_ctrl(seq, voice, PORTASWITCH, event->value); break; case MODWHEEL_CTRL: if (!enabled) break; seq_midi_ctrl(seq, voice, MODWHEEL, event->value); break; case TOUCH_CTRL: if (!enabled) break; seq_midi_touch(seq, voice, event->value); break; case VOLUME_CTRL: if (!enabled) break; seq_midi_ctrl(seq, voice, VOLUME, event->value); break; case BEND_CTRL: if (!enabled) break; seq_midi_bend(seq, voice, (event->value << 6)); break; case PROGRAM_CTRL: if (!enabled) break; seq_midi_program(seq, voice, event->value + 1); break; case ESC_CTRL: switch (event->value) { case CALL_VALUE: sequence = seq; (*(event->u.call.routine))(event->u.call.args); break; case CLOCK_VALUE: clock_ticksize = event->u.clock.ticksize; if (!clock_running && !suppress_midi_clock && !external_midi_clock) { clock_running = TRUE; midi_start(); clock_tick(seq, 0L); } break; case MACCTRL_VALUE: if (!enabled) break; seq_midi_ctrl(seq, voice, event->u.macctrl.ctrl_number, event->u.macctrl.value); break; case MACRO_VALUE: { if (!enabled) break; send_macro(event->u.macro.definition, voice, event->u.macro.parameter, -1, 0, event->nline); break; } case CTRLRAMP_VALUE: case DEFRAMP_VALUE: { int from, to; if (!enabled) break; step = event->u.ramp.step; if (event->value == CTRLRAMP_VALUE) { from = event->u.ramp.u.ctrl.from_value; to = event->u.ramp.u.ctrl.to_value; } else { from = event->u.ramp.u.def.parameter[ event->u.ramp.u.def.parm_num]; to = event->u.ramp.u.def.to_value; } delta = to - from; increment = delta; if (delta < 0) delta = -delta; /* Note: Step is always non-zero */ n = event->u.ramp.dur / step; increment = (increment << 8) / n; ramp_event(seq, event, from << 8, to << 8, (int) increment, step, n); seq->noteoff_count++; break; } case SETI_VALUE: *(event->u.seti.int_to_set) = event->u.seti.value; break; default: gprintf(TRANS, "unexpected ESC_CTRL value\n"); break; } break; default: gprintf(TRANS, "unexpected seq data\n"); break; } } seq->current = event->next; } if (seq->current) { cause((delay_type)(event->ntime - virttime), process_event, seq); } else if (seq->noteoff_count == 0 && seq->note_enable) { /* if we're just advancing to a start point, note_enable will be * FALSE and this won't get called: */ if (seq->stopfunc) { (*(seq->stopfunc))(seq); } } } /* ramp_event -- generate a ramp */ /**/ private void ramp_event(seq, event, value, to_value, increment, step, n) seq_type seq; register event_type event; unsigned int value; unsigned int to_value; int increment; time_type step; int n; { if (seq->runflag) { int voice = vc_voice(event->nvoice); /* printf("ramp_event: value %d to_value %d increment %d step %d n %d time %d\n", value, to_value, increment, step, n, virttime); */ if (n == 0) value = to_value; else { causepri((delay_type)step, 5, ramp_event, seq, event, value + increment, to_value, increment, step, n - 1); } if (event->value == CTRLRAMP_VALUE) { int ctrl = event->u.ramp.ctrl; if (ctrl == -TOUCH_CTRL) midi_touch(voice, value >> 8); else if (ctrl == -BEND_CTRL) midi_bend(voice, value >> 2); else midi_ctrl(voice, ctrl, value >> 8); } else { /* must be DEFRAMP_VALUE */ send_macro(event->u.ramp.u.def.definition, vc_voice(event->nvoice), event->u.ramp.u.def.parameter, event->u.ramp.u.def.parm_num, value >> 8, event->nline); } if (n == 0) seq_end_event(seq); } } /* report_enabled_channels -- print out concise listing of channels */ /* * to fit on one line, write out ranges, e.g. 1-5 9-11 */ void report_enabled_channels(seq) seq_type seq; { ulong mask = seq_channel_mask(seq); int i, range_open_at = 0; for (i = 1; i <= MAX_CHANNELS; i++) { if (!range_open_at && (mask & 1)) { gprintf(TRANS, " %d", i); range_open_at = i; } else if (range_open_at && !(mask & 1)) { if (i > (range_open_at + 1)) { gprintf(TRANS, "-%d", i - 1); } range_open_at = 0; /* FALSE */ } mask = mask >> 1; } if (range_open_at) gprintf(TRANS, "-%d", MAX_CHANNELS); } /* send_macro -- instantiate macro and send it */ /* * note: to support ramping, "value" is used in place of * parameter["parm_num"] */ /*private*/ void send_macro(ptr, voice, parameter, parm_num, value, nline) register unsigned char *ptr; int voice; short parameter[]; int parm_num; int value; int nline; { register unsigned char code, *loc; while ((code = *ptr++)) { loc = ptr + *ptr; ptr++; if (code <= nmacroparms) { code--; *loc = (code == parm_num ? value : parameter[code]) & 0x7f; } else if (code == nmacroparms + 1) { /* take old high order bits and OR in 4 voice bits */ *loc = (*loc & 0xF0) | ((voice - 1) & 0xF); } else { code -= (nmacroparms + 2); *loc = ((code == parm_num ? value : parameter[code]) >> 7) & 0x7F; } } if (ptr[1] == MIDI_SYSEX) { midi_exclusive(ptr + 1); } else { /* make sure user didn't try to send more than 3 bytes. This test * could be done at sequence read time, but it's tricky because the * first byte could be a parameter, so in general you need to * plug the actual parameters into the message and then do the test. * Currently, this is the only place parameters are plugged in. */ if (*ptr > 3) { gprintf(ERROR, "Non-sysex macro longer than 3 bytes ignored, line %d.\n", nline); } else { midi_write((int) *ptr, MIDI_PORT(voice), ptr[1], ptr[2], ptr[3]); } } } /* seq_alloc -- a utility function to allocate a seq struct */ /**/ seq_type seq_alloc() { seq_type seq; seq = (seq_type) memget(sizeof(seq_node)); return seq; } /* seq_at_end -- set the function to be called at sequence end */ /**/ void seq_at_end(seq, fn) seq_type seq; void (*fn)(seq_type); { if (!fn) fn = noop; seq->stopfunc = fn; } /* seq_cause_noteoff_meth -- turn off a note in the future */ /**/ void seq_cause_noteoff_meth(seq, delay, voice, pitch) seq_type seq; time_type delay; int voice; int pitch; { if (seq->note_enable) { pitch += seq->transpose; while (pitch < 0) pitch += 12; while (pitch > 127) pitch -= 12; seq->noteoff_count++; causepri((delay_type) delay, 10, seq->noteoff_fn, seq, voice, pitch); } } /* seq_copy -- copy a sequence, share the eventlist */ /**/ seq_type seq_copy(from_seq) seq_type from_seq; { register seq_type seq = seq_init(seq_alloc(), FALSE); if (!seq) return NULL; seq->chunklist = from_seq->chunklist; seq->current = seq_events(seq); seq->chunklist->u.info.refcount++; seq->transpose = from_seq->transpose; seq->loudness = from_seq->loudness; seq->rate = from_seq->rate; seq->paused = from_seq->paused; seq->noteoff_count = 0; return seq; } /* seq_create -- create a seq structure and an initial event chunk */ /**/ seq_type seq_create() { return seq_init(seq_alloc(), TRUE); } /* seq_cycle -- set parameters for cycling a sequence */ /**/ void seq_cycle(seq_type seq, boolean flag, time_type dur) { seq->cycleflag = flag; seq->cycledur = dur; } /* seq_end_event -- call this when an score-generated event ends */ /* * Assumes that noteoff_count was incremented when event started. */ void seq_end_event(seq) seq_type seq; { /*gprintf(TRANS, "nd");*/ seq->noteoff_count--; if (seq->current == NULL /* finished seq */ && seq->noteoff_count == 0 /* finished noteoff's */ && seq->runflag /* we've not been stopped */) { if (seq->cycleflag) { cause((delay_type) (seq->cycledur - virttime), cycle, seq); } else if (seq->stopfunc) { (*(seq->stopfunc))(seq); } } } /**************************************************************************** * seq_free_meth * Input: a seq_type * Effect: * frees storage occupied by a seq ****************************************************************************/ private void seq_free_meth(seq) seq_type seq; { seq_free_chunks(seq); if (seq->timebase) timebase_free(seq->timebase); memfree((void *) seq, sizeof(seq_node)); } /* seq_free_chunks -- free storage for note list */ /* * NOTE: in its original form, this routine was perhaps more readable, * but would not compile under Microsoft C V7.00 due to a compiler bug. * I rewrote the code until the bug disappeared, hopefully without * changing the semantics! If you change this code, make sure it still * compiles under Microsoft C. * * This module frees chunks from a seq_type in preparation for freeing * the seq_type itself. Reference counts are checked and chunks are * only freed when the last reference is removed. */ public void seq_free_chunks(seq) seq_type seq; { chunk_type tail; chunk_type head; head = seq->chunklist; if (((head->u.info.refcount)--) != 0) return; while (head != NULL) { tail = head->next; memfree((void *) head, sizeof(chunk_node)); head = tail; seq->chunklist = head; } } seq_type seq_init(seq, create_chunk) seq_type seq; int create_chunk; { if (!seq || !(seq->timebase = timebase_create(50))) { return NULL; } seq->chunklist = NULL; if (create_chunk) { seq->chunklist = chunk_create(TRUE); if (!seq->chunklist) { seq_free(seq); return NULL; } } seq->cause_noteoff_fn = seq_cause_noteoff_meth; seq->midi_bend_fn = seq_midi_bend_meth; seq->midi_ctrl_fn = seq_midi_ctrl_meth; seq->midi_program_fn = seq_midi_program_meth; seq->midi_touch_fn = seq_midi_touch_meth; seq->noteoff_fn = seq_noteoff_meth; seq->noteon_fn = seq_noteon_meth; seq->free_fn = seq_free_meth; seq->reset_fn = seq_reset_meth; seq->current = NULL; seq->transpose = 0; seq->loudness = 0; seq->cycleflag = FALSE; seq->cycledur = 0L; seq->rate = 256L; seq->paused = FALSE; seq->stopfunc = noop; seq->channel_mask = 0xFFFFFFFFL; seq->runflag = seq->note_enable = FALSE; return seq; } /* seq_midi_bend_meth -- send a midi bend */ /**/ void seq_midi_bend_meth(seq_type seq, int voice, int value) { midi_bend(voice, value); } /* seq_midi_ctrl_meth -- send a midi ctrl change */ /**/ void seq_midi_ctrl_meth(seq_type seq, int voice, int ctrl, int value) { midi_ctrl(voice, ctrl, value); } /* seq_midi_program_meth -- send a midi program change */ /**/ void seq_midi_program_meth(seq_type seq, int voice, int prog) { midi_bend(voice, prog); } /* seq_midi_touch_meth -- send a midi touch */ /**/ void seq_midi_touch_meth(seq_type seq, int voice, int value) { midi_touch(voice, value); } /* seq_noteoff_meth -- turn a seq note off */ /**/ void seq_noteoff_meth(seq, voice, pitch) seq_type seq; int voice; int pitch; { midi_note(voice, pitch, 0); /*gprintf(TRANS, "_e");*/ seq_end_event(seq); } /* seq_noteon_meth -- play a note with transformations */ /**/ void seq_noteon_meth(seq, chan, pitch, vel) seq_type seq; int chan, pitch, vel; { if (seq->note_enable) { pitch += seq->transpose; while (pitch < 0) pitch += 12; while (pitch > 127) pitch -= 12; vel += seq->loudness; if (vel <= 0) vel = 1; else if (vel > 127) vel = 127; midi_note(chan, pitch, vel); } } /* seq_pause -- stop playing momentarily or resume playing */ /**/ time_type seq_pause(seq_type seq, boolean flag) { if (!seq->paused && flag) { seq->paused = TRUE; seq->rate = seq->timebase->rate; set_rate(seq->timebase, STOPRATE); } else if (seq->paused && !flag) { seq_play(seq); } return (time_type) seq->timebase->virt_base; } /* seq_play -- play a sequence from the current event forward */ /**/ void seq_play(seq) seq_type seq; { timebase_type prev_timebase = timebase; register timebase_type reg_timebase = seq->timebase; if (!seq->runflag) { seq_reset(seq); } if (!seq->paused) return; eventtime = gettime(); /* assume that virt_base is correct virtual time as the result of seq_start_time or seq_reset */ timebase = reg_timebase; virttime = reg_timebase->virt_base; /* note that set_rate will set reg_timebase->real_base to eventtime */ set_rate(reg_timebase, seq->rate); seq->paused = FALSE; /* in case the score had been paused; note that seq_pause() has no effect if paused is TRUE */ seq->runflag = TRUE; seq->note_enable = TRUE; /* restore previous timebase */ timebase_use(prev_timebase); } /* seq_reset_meth -- reset a sequence to start back at the first event */ /**/ void seq_reset_meth(seq) seq_type seq; { timebase_type old_timebase = timebase; if (seq->runflag) { /* maybe this seq is already reset, and process_event is * already scheduled. If so, don't schedule another one. */ if ((seq->timebase->virt_base == 0) && (seq->timebase->rate == STOPRATE)) { /* in case the reader just iterated through the list without * cause'ing events, reset the event list */ seq->current = seq_events(seq); return; } /* Otherwise, the seq is running, so stop it. */ seq_stop(seq); } timebase_use(seq->timebase); set_rate(seq->timebase, STOPRATE); set_virttime(seq->timebase, 0L); seq->current = seq_events(seq); seq->noteoff_count = 0L; seq->runflag = TRUE; seq->paused = TRUE; if (seq->current) cause((delay_type)(seq->current->ntime - virttime), process_event, seq); timebase_use(old_timebase); } /* seq_set_loudness -- set the loudness offset of a sequence */ /**/ void seq_set_loudness(seq, loud) seq_type seq; int loud; { seq->loudness = loud; } /* seq_set_rate -- set the rate of a sequence */ /**/ void seq_set_rate(seq, rate) seq_type seq; time_type rate; { seq->rate = rate; if (!seq->paused) set_rate(seq->timebase, rate); } /* seq_set_transpose -- set the sequence transposition */ /**/ void seq_set_transpose(seq, trans) seq_type seq; int trans; { seq->transpose = trans; } /* seq_start_time -- set the current pointer so the sequence starts here */ /**/ void seq_start_time(seq, start_time) seq_type seq; time_type start_time; { timebase_type prev_timebase = timebase; if (!seq->runflag) { seq_reset(seq); } if (real_to_virt(seq->timebase, eventtime) > start_time) { seq_reset(seq); } timebase_use(seq->timebase); seq->note_enable = FALSE; /* prime the pump */ set_rate(timebase, STOPRATE); set_virttime(timebase, start_time); catchup(); seq->note_enable = TRUE; seq->paused = TRUE; /* restore previous timebase */ timebase_use(prev_timebase); } /* seq_stop -- stop a sequence, clear out all pending events */ /**/ void seq_stop(seq) seq_type seq; { timebase_type prev_timebase = timebase; if (seq->runflag) { if (moxcdebug) gprintf(TRANS, "seq_reset swap from timebase 0x%x to 0x%x\n", timebase, seq->timebase); timebase = seq->timebase; seq->runflag = FALSE; set_rate(timebase, STOPRATE); set_virttime(timebase, MAXTIME); catchup(); } timebase_use(prev_timebase); }