mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-04 17:49:45 +02:00
652 lines
23 KiB
C
652 lines
23 KiB
C
/* created by DMH (damonhorowitz): write seq_type as standard midifile */
|
|
/***************************************************************************
|
|
* Change Log
|
|
* Date | Change
|
|
*-----------+---------------------------------------------------------------
|
|
* 11-Mar-94 | Created Change Log
|
|
* 11-Mar-94 | PLu : Added private to function defs.
|
|
* 28-Apr-03 | DM : Change #include's for portability
|
|
****************************************************************************/
|
|
|
|
#include "switches.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "cext.h"
|
|
#include "userio.h"
|
|
#include "midicode.h"
|
|
#include "mfmidi.h"
|
|
#include "midifns.h"
|
|
#include "timebase.h"
|
|
#include "moxc.h"
|
|
#include "seq.h"
|
|
#include "seqread.h" /* to get scale() */
|
|
#include "seqmwrite.h"
|
|
|
|
long chunk_size_marker;
|
|
int seti_counter;
|
|
extern time_type a_start_time;
|
|
|
|
long last_event; /*time from last_clock_event to the last event*/
|
|
time_type last_clock_event;
|
|
time_type last_tick_size; /* millisec per tick shifted 16 bits */
|
|
|
|
struct smf_write_seq {
|
|
seq_type seq;
|
|
int track;
|
|
FILE *outfile;
|
|
} smfw_seq;
|
|
extern seq_type sequence; /* this is a global to be accessed by
|
|
* routines called from the sequence */
|
|
/* clock state: */
|
|
extern time_type clock_ticksize; /* millisec per tick shifted 16 bits */
|
|
extern boolean clock_running; /* TRUE if clock is running */
|
|
extern boolean use_midi_clock;
|
|
|
|
private void smfw_bend();
|
|
private void smfw_cause_noteoff();
|
|
private void smfw_ctrl();
|
|
private void smfw_deltatime();
|
|
private void smfw_dotrack();
|
|
private void smfw_exclusive();
|
|
private void smfw_noteoff();
|
|
private void smfw_noteon();
|
|
private void smfw_process_event();
|
|
private void smfw_ramp_event(seq_type seq, event_type event,
|
|
unsigned int value, unsigned int to_value, int increment,
|
|
time_type step, int n);
|
|
private void smfw_send_macro();
|
|
private void smfw_touch(seq_type seq, int voice, int value);
|
|
private void writevarlen();
|
|
|
|
|
|
/* smfw_bend -- write a pitch bend to a midi file */
|
|
/**/
|
|
private void smfw_bend(seq_type seq, int voice, int value)
|
|
{
|
|
if(debug) gprintf(TRANS, "smfw_bend %d\n", value);
|
|
smfw_deltatime();
|
|
putc(MIDI_BEND | (voice - 1), smfw_seq.outfile);
|
|
putc(0xFF & ((value & 0x1) << 6) , smfw_seq.outfile);
|
|
putc(0xFF & (value >> 1), smfw_seq.outfile);
|
|
}
|
|
|
|
|
|
/* smfw_cause_noteoff -- schedule a noteoff for midi file */
|
|
/*
|
|
* NOTE: this is called by smfw_process_event when it handles a note
|
|
* event node in a seq_type's event list. The ordinary moxc scheduler
|
|
* is used to "schedule" the noteoff in the future. In reality, the
|
|
* output is done as fast as possible (by attempting an infinite rate),
|
|
* so no real timing delays occur. The effect is to sort events by their
|
|
* specified time.
|
|
*/
|
|
private void smfw_cause_noteoff(seq, delay, voice, pitch)
|
|
seq_type seq;
|
|
time_type delay;
|
|
int voice;
|
|
int pitch;
|
|
{
|
|
if(debug) gprintf(TRANS, "cause noteoff at %ld...", virttime + delay);
|
|
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);
|
|
|
|
}
|
|
|
|
private void smfw_clock_event(old_ticksize, new_ticksize)
|
|
time_type old_ticksize, new_ticksize;
|
|
{
|
|
time_type temp_ticksize = new_ticksize;
|
|
new_ticksize = scale(new_ticksize, 375L, 1024L);
|
|
/* (new_ticksize >> 16) * 24000 ms/clock becomes us/midiquarter */
|
|
|
|
if(debug) gprintf(TRANS, "smfw_clock: write %ld (time:%ld) ->->->tempo %ld\n",
|
|
new_ticksize, virttime, 2500L / (new_ticksize / 24000));
|
|
|
|
/*use old ticksize to write the delta for the clock event*/
|
|
last_tick_size = old_ticksize;
|
|
smfw_deltatime();
|
|
last_tick_size = temp_ticksize;/* reset to = new_tick_size */
|
|
putc(0xFF, smfw_seq.outfile);
|
|
putc(0x51, smfw_seq.outfile);
|
|
putc(0x03, smfw_seq.outfile);
|
|
putc((int) ((new_ticksize >> 16) & 0xFF), smfw_seq.outfile);
|
|
putc((int) ((new_ticksize >> 8) & 0xFF), smfw_seq.outfile);
|
|
putc((int) (new_ticksize & 0xFF), smfw_seq.outfile);
|
|
|
|
last_clock_event = virttime;
|
|
last_event = 0L;
|
|
/*no time expired between last clockevent and last event(they are the same).*/
|
|
/*next clock event will be exactly the next this_event from last_clock_event*/
|
|
}
|
|
|
|
|
|
/* smfw_ctrl -- write a control change to a midi file */
|
|
/**/
|
|
private void smfw_ctrl(seq_type seq, int voice, int ctrl_name, int value)
|
|
{
|
|
if(debug) gprintf(TRANS, "smfw_ctrl %d: %d\n", ctrl_name, value);
|
|
smfw_deltatime();
|
|
putc(MIDI_CTRL | (voice - 1), smfw_seq.outfile);
|
|
putc(ctrl_name, smfw_seq.outfile);
|
|
putc(value, smfw_seq.outfile);
|
|
}
|
|
|
|
|
|
/* smfw_deltatime -- write the time difference between now an previous event */
|
|
/**/
|
|
private void smfw_deltatime()
|
|
{
|
|
/* if last_ and clock_ are different, use last_ for clock deltatime*/
|
|
time_type use_ticksize = (clock_ticksize != last_tick_size) ?
|
|
last_tick_size : clock_ticksize;
|
|
time_type this_event = virttime - last_clock_event;
|
|
if(debug) gprintf(TRANS, "delta! ticksize: %lu Lastev: %ld ThisevScaled: %lu Thisev: %lu ",
|
|
clock_ticksize, last_event, (this_event * ((2500L << 16) / use_ticksize)) / 100,
|
|
this_event);
|
|
|
|
this_event = ((virttime - last_clock_event) * ((2500L << 16) / use_ticksize)) / 100;
|
|
|
|
if(debug) gprintf(TRANS, "--- deltatime: %lu\n", this_event - last_event);
|
|
writevarlen((long) (this_event - last_event));
|
|
|
|
last_event = this_event;
|
|
}
|
|
|
|
|
|
/* smfw_dotrack -- write the remainder of a track */
|
|
private void smfw_dotrack(seq)
|
|
seq_type seq;
|
|
{
|
|
long end_marker;
|
|
timebase_type old_timebase = timebase;
|
|
unsigned long chunk_size;
|
|
|
|
if (seq->runflag) {
|
|
if ((seq->timebase->virt_base == 0) &&
|
|
(seq->timebase->rate == STOPRATE))
|
|
/*we just set these last time through... do nothing*/;
|
|
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;
|
|
last_clock_event = 0L;
|
|
last_event = 0L;
|
|
if(debug) gprintf(TRANS, "dotrack (reset) %d %ld (%lu) \n",
|
|
smfw_seq.track, last_event, virttime);
|
|
|
|
if (seq->current)
|
|
cause((delay_type)(seq->current->ntime - virttime), smfw_process_event,
|
|
seq);
|
|
set_virttime(timebase, MAXTIME);
|
|
catchup();
|
|
|
|
putc(0x00, smfw_seq.outfile);
|
|
putc(0xFF, smfw_seq.outfile);/*end of track chunk*/
|
|
putc(0x2F, smfw_seq.outfile);
|
|
putc(0x00, smfw_seq.outfile);
|
|
end_marker = ftell(smfw_seq.outfile);
|
|
fseek(smfw_seq.outfile, chunk_size_marker, 0);/*go back to enter chunksize*/
|
|
chunk_size = (end_marker - chunk_size_marker) - 4;/* - 4 for 4 size bytes*/
|
|
if(debug) gprintf(TRANS, "bytes written in previous track: %ld \n\n", chunk_size);
|
|
putc((int) ((0xFF & (chunk_size >> 24))), smfw_seq.outfile);
|
|
putc((int) ((0xFF & (chunk_size >> 16))), smfw_seq.outfile);
|
|
putc((int) ((0xFF & (chunk_size >> 8))), smfw_seq.outfile);
|
|
putc((int) ((0xFF & chunk_size)), smfw_seq.outfile);
|
|
fseek(smfw_seq.outfile, end_marker, 0);/*return file pointer to end of track*/
|
|
timebase_use(old_timebase);
|
|
}
|
|
|
|
|
|
/* smfw_exclusive -- write a system excl. msg to midi file */
|
|
private void smfw_exclusive(length, msg)
|
|
int length;
|
|
unsigned char *msg;
|
|
{
|
|
int length_count = 0;
|
|
|
|
if(debug) gprintf(TRANS, "SYSEX (time:%ld)\n", virttime);
|
|
|
|
smfw_deltatime();
|
|
|
|
while (length > length_count){ /* *(msg-1) != MIDI_EOX) { */
|
|
putc(*msg++, smfw_seq.outfile);
|
|
length_count++;
|
|
}
|
|
if(*(--msg) != MIDI_EOX) gprintf(TRANS, "ERROR: no end of sysex\n");
|
|
}
|
|
|
|
private void smfw_msg_write(n,c1,c2,c3)
|
|
int n;
|
|
unsigned char c1,c2,c3;
|
|
{
|
|
if(debug) gprintf(TRANS, "MSGWRITE %d bytes (time:%ld)\n", n, virttime);
|
|
smfw_deltatime();
|
|
switch(n) {
|
|
case 1: putc(c1, smfw_seq.outfile);
|
|
break;
|
|
case 2: putc(c1, smfw_seq.outfile);
|
|
putc(c2, smfw_seq.outfile);
|
|
break;
|
|
case 3: putc(c1, smfw_seq.outfile);
|
|
putc(c2, smfw_seq.outfile);
|
|
putc(c3, smfw_seq.outfile);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* smfw_noteoff -- write noteoff to midi file */
|
|
/**/
|
|
private void smfw_noteoff(seq_type seq, int voice, int pitch)
|
|
{
|
|
if(debug) gprintf(TRANS, "smfw_noteoff %d: %d (time:%ld)\n", voice, pitch, virttime);
|
|
smfw_deltatime();
|
|
putc(NOTEOFF | (voice - 1), smfw_seq.outfile);
|
|
putc(pitch, smfw_seq.outfile);
|
|
putc(0x40, smfw_seq.outfile);
|
|
}
|
|
|
|
|
|
/* smfw_noteon -- write noteon to midi file */
|
|
/*
|
|
* NOTE: the seq parameter is not used here, but is passed in by the
|
|
* seq_noteon macro, so we have to have a placeholder for it.
|
|
*/
|
|
private void smfw_noteon(seq, voice, pitch, vel)
|
|
seq_type seq;
|
|
int voice, pitch, vel;
|
|
{
|
|
if(debug) gprintf(TRANS, "smfw_noteon %d: %d %d(time:%ld)\n", voice, pitch, vel, virttime);
|
|
smfw_deltatime();
|
|
putc(NOTEON | (voice - 1), smfw_seq.outfile);
|
|
putc(pitch, smfw_seq.outfile);
|
|
putc(vel, smfw_seq.outfile);
|
|
}
|
|
|
|
|
|
/* smfw_process_event -- write a seq event to a midi file */
|
|
/**/
|
|
private void smfw_process_event(seq)
|
|
seq_type seq;
|
|
{
|
|
register event_type event;
|
|
if (!seq->runflag) return;
|
|
while ((event = seq->current) && (event->ntime <= virttime)) {
|
|
unsigned int voice;
|
|
if ((vc_voice(event->nvoice) == smfw_seq.track) || /*if on current track*/
|
|
(((vc_voice(event->nvoice) - 16) == smfw_seq.track)
|
|
&& (smfw_seq.track > 0)) ||
|
|
/* acknowledge clock change on all tracks*/
|
|
(event->value == CLOCK_VALUE && vc_ctrl(event->nvoice) == ESC_CTRL)) {
|
|
|
|
/* 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, (0xFF & event->value),
|
|
(int) (event->u.note.ndur & 0xFF));
|
|
|
|
seq_cause_noteoff(seq, (event->u.note.ndur) >> 8,
|
|
voice, (0xFF & event->value));
|
|
}
|
|
} else { /*** send a control command ***/
|
|
int n;
|
|
time_type step;
|
|
int delta;
|
|
int 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;
|
|
if(debug) gprintf(TRANS, "porta %d (time:%ld)... ", event->value, virttime);
|
|
seq_midi_ctrl(seq, voice, PORTASWITCH, 0xFF & event->value);
|
|
break;
|
|
case MODWHEEL_CTRL:
|
|
if (!enabled) break;
|
|
if(debug) gprintf(TRANS, "modw %d (time:%ld)...", event->value, virttime);
|
|
seq_midi_ctrl(seq, voice, MODWHEEL, 0xFF & event->value);
|
|
break;
|
|
case TOUCH_CTRL:
|
|
if (!enabled) break;
|
|
if(debug) gprintf(TRANS, "touch %d (time:%ld)... ", event->value, virttime);
|
|
seq_midi_touch(seq, voice, 0xFF & event->value);
|
|
break;
|
|
case VOLUME_CTRL:
|
|
if (!enabled) break;
|
|
if(debug) gprintf(TRANS, "ftvol %d (time:%ld)...", event->value, virttime);
|
|
seq_midi_ctrl(seq, voice, VOLUME, 0xFF & event->value);
|
|
break;
|
|
case BEND_CTRL:
|
|
if (!enabled) break;
|
|
if(debug) gprintf(TRANS, "bend %d (time:%ld)... ", event->value, virttime);
|
|
seq_midi_bend(seq, voice, event->value);
|
|
break;
|
|
case PROGRAM_CTRL:
|
|
if (!enabled) break;
|
|
if(debug) gprintf(TRANS, "prog %d (time:%ld)\n", event->value, virttime);
|
|
smfw_deltatime();
|
|
putc(MIDI_CH_PROGRAM | (voice - 1), smfw_seq.outfile);
|
|
putc(0xFF & event->value, smfw_seq.outfile);
|
|
break;
|
|
case ESC_CTRL:
|
|
switch (event->value) {
|
|
time_type this_event;
|
|
case CALL_VALUE: /*called routine will write to midifile in execution */
|
|
sequence = seq;
|
|
(*(event->u.call.routine))(event->u.call.args);
|
|
break;
|
|
case CLOCK_VALUE:
|
|
clock_ticksize = event->u.clock.ticksize;
|
|
if(debug) gprintf(TRANS, "clockevent! ticksize: %lu (time:%ld)\n",
|
|
clock_ticksize, virttime);
|
|
|
|
if (virttime > 0) { /* any clock before this is already recorded in the header */
|
|
if (smfw_seq.track == 0) { /* record clock event on tempo track = 0 */
|
|
/* cause clock write in half a newtick, because it was written .5 tick early*/
|
|
cause((delay_type) (clock_ticksize >> 17), smfw_clock_event,
|
|
last_tick_size, clock_ticksize);
|
|
last_tick_size = clock_ticksize; /*set new ticksize*/
|
|
} else { /*not on tempo track*/
|
|
this_event = ((virttime - last_clock_event) *
|
|
((2500L << 16) / last_tick_size)) / 100;
|
|
if(debug) gprintf(TRANS, "track != 0: Lastev: %ld Thisev: %ld NewLast: %ld\n",
|
|
last_event, this_event, this_event - last_event);
|
|
last_event = 0L - (this_event - last_event);
|
|
last_clock_event = virttime;
|
|
/*last_event is negative, so will be ADDED to next this_event*/
|
|
|
|
last_tick_size = clock_ticksize;
|
|
}
|
|
} else if (debug) gprintf(TRANS, "IGNORED\n");/* if virttime <= 0 */
|
|
break;
|
|
case MACCTRL_VALUE:
|
|
if (!enabled) break;
|
|
if (debug) gprintf(TRANS, "MACCTRL %d: %d (time:%ld)\n",
|
|
event->u.macctrl.ctrl_number, event->u.macctrl.value, virttime);
|
|
smfw_deltatime();
|
|
putc(MIDI_CTRL | (voice - 1), smfw_seq.outfile);
|
|
putc(0xFF & event->u.macctrl.ctrl_number, smfw_seq.outfile);
|
|
putc(0xFF & event->u.macctrl.value, smfw_seq.outfile);
|
|
break;
|
|
case MACRO_VALUE:
|
|
if (!enabled) break;
|
|
if (debug) gprintf(TRANS, "MACRO sent to...\n");
|
|
smfw_send_macro(event->u.macro.definition,
|
|
voice, event->u.macro.parameter, -1, 0);
|
|
break;
|
|
case CTRLRAMP_VALUE:
|
|
case DEFRAMP_VALUE: {
|
|
int from, to;
|
|
if (!enabled) break;
|
|
step = event->u.ramp.step;
|
|
if (event->value == CTRLRAMP_VALUE) {
|
|
if(debug) gprintf(TRANS, "CTRLRAMP (time:%ld)...", virttime);
|
|
from = event->u.ramp.u.ctrl.from_value;
|
|
to = event->u.ramp.u.ctrl.to_value;
|
|
} else {
|
|
if (debug) gprintf(TRANS, "DEFRAMP (time:%ld)...", virttime);
|
|
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;
|
|
/* RBD - Note: Step is always non-zero */
|
|
n = event->u.ramp.dur / step;
|
|
increment = (increment << 8) / n;
|
|
smfw_ramp_event(seq, event, from << 8, to << 8,
|
|
increment, step, n);
|
|
seq->noteoff_count++;
|
|
break;
|
|
}
|
|
case SETI_VALUE:
|
|
seti_counter++; /*will be printed after writing is completed*/
|
|
*(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) { /* if there is an event: delay, and then process again */
|
|
cause((delay_type)(event->ntime - virttime), smfw_process_event, seq);
|
|
}
|
|
}
|
|
|
|
/* smfw_ramp_event -- generate a ramp to write*/
|
|
private void smfw_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(debug) gprintf(TRANS, "ramp of %d: %d to %d\n", event->u.ramp.ctrl, value >> 8,
|
|
to_value >> 8);
|
|
if (seq->runflag) {
|
|
int voice = vc_voice(event->nvoice);
|
|
if (n == 0) value = to_value;
|
|
else cause((delay_type)step, smfw_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) smfw_touch(seq, voice, value >> 8);
|
|
else if (ctrl == -BEND_CTRL) smfw_bend(seq, voice, value >> 8);
|
|
else smfw_ctrl(seq, voice, ctrl, value >> 8);
|
|
} else { /* must be DEFRAMP_VALUE */
|
|
smfw_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);
|
|
}
|
|
if (n == 0) seq_end_event(seq);
|
|
}
|
|
}
|
|
|
|
|
|
/* smfw_send_macro -- write msg to midi file from a seq "macro" event */
|
|
/**/
|
|
private void smfw_send_macro(ptr, voice, parameter, parm_num, value)
|
|
register unsigned char *ptr;
|
|
int voice;
|
|
short parameter[];
|
|
int parm_num;
|
|
int value;
|
|
{
|
|
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) {
|
|
*loc = ((voice - 1) & 0xF) | *loc;
|
|
}
|
|
else {
|
|
code -= (nmacroparms + 2);
|
|
*loc = ((code == parm_num ? value : parameter[code]) >> 7) & 0x7F;
|
|
}
|
|
}
|
|
if (ptr[1] == MIDI_SYSEX)
|
|
smfw_exclusive(*ptr, ptr + 1);
|
|
else
|
|
smfw_msg_write(*ptr, ptr[1], ptr[2], ptr[3]);
|
|
}
|
|
|
|
|
|
/* smfw_touch -- write aftertouch msg to midi file */
|
|
/**/
|
|
private void smfw_touch(seq_type seq, int voice, int value)
|
|
{
|
|
if(debug) gprintf(TRANS, "smfw_touch %d\n", value);
|
|
smfw_deltatime();
|
|
putc(MIDI_TOUCH | (voice - 1), smfw_seq.outfile);
|
|
putc(value, smfw_seq.outfile);
|
|
}
|
|
|
|
|
|
void seq_write_smf(seq, outfile)
|
|
seq_type seq;
|
|
FILE *outfile;
|
|
{
|
|
time_type put_tick_size;
|
|
int i;
|
|
/* ticksize is milliseconds << 16, and tickrate = 24*tempo
|
|
60000ms/min / (24 * tempo) = 2500/tempo = 25
|
|
25 << 16 = 1638400 */
|
|
time_type starting_ticksize = 1638400L; /*default midifile tempo 100*/
|
|
int track_count = 0;
|
|
long track_count_marker;
|
|
register event_type event;
|
|
|
|
seti_counter = 0;
|
|
|
|
/*initialize the smfw_seq struct*/
|
|
smfw_seq.outfile = outfile;
|
|
smfw_seq.seq = seq_copy(seq);
|
|
|
|
smfw_seq.seq->cause_noteoff_fn = smfw_cause_noteoff;
|
|
smfw_seq.seq->midi_bend_fn = smfw_bend;
|
|
smfw_seq.seq->midi_ctrl_fn = smfw_ctrl;
|
|
smfw_seq.seq->midi_touch_fn = smfw_touch;
|
|
smfw_seq.seq->noteoff_fn = smfw_noteoff;
|
|
smfw_seq.seq->noteon_fn = smfw_noteon;
|
|
|
|
event = seq_events(smfw_seq.seq);
|
|
|
|
/*search for clock events up till start of score*/
|
|
while(event->ntime <= 0){
|
|
if(debug) gprintf(TRANS, "event (time:%ld)\n", event->ntime);
|
|
if(vc_ctrl(event->nvoice) == ESC_CTRL && event->value == CLOCK_VALUE) {
|
|
if(debug) gprintf(TRANS, "clock %lu at 0\n", event->u.clock.ticksize);
|
|
starting_ticksize = event->u.clock.ticksize;
|
|
break;
|
|
}
|
|
event = event->next;
|
|
}
|
|
|
|
putc(0x4D, smfw_seq.outfile); /*header: MThd*/
|
|
putc(0x54, smfw_seq.outfile);
|
|
putc(0x68, smfw_seq.outfile);
|
|
putc(0x64, smfw_seq.outfile);
|
|
putc(0x00, smfw_seq.outfile);
|
|
putc(0x00, smfw_seq.outfile);
|
|
putc(0x00, smfw_seq.outfile);
|
|
putc(0x06, smfw_seq.outfile);
|
|
putc(0x00, smfw_seq.outfile);
|
|
putc(0x01, smfw_seq.outfile); /*format 1 */
|
|
putc(0x00, smfw_seq.outfile);
|
|
|
|
track_count_marker = ftell(smfw_seq.outfile); /*number of tracks will be written later*/
|
|
putc(0x00, smfw_seq.outfile); /*will be filled by track_count_marker*/
|
|
|
|
putc(0x02, smfw_seq.outfile);/*division resolution of 600*/
|
|
putc(0x58, smfw_seq.outfile);
|
|
|
|
for(i = 0; i < 17; i++){/*for each track..*/
|
|
if(((seq_used_mask(smfw_seq.seq) >> (i - 1)) & 0x1) || (i == 0)){
|
|
if(debug) gprintf(TRANS, "write track %d \n", i);
|
|
track_count++;
|
|
clock_ticksize = starting_ticksize;
|
|
last_tick_size = starting_ticksize;
|
|
putc(0x4D, smfw_seq.outfile);/*track header: MTrk*/
|
|
putc(0x54, smfw_seq.outfile);
|
|
putc(0x72, smfw_seq.outfile);
|
|
putc(0x6B, smfw_seq.outfile);
|
|
|
|
chunk_size_marker = ftell(smfw_seq.outfile);/*size of chunk will be written later*/
|
|
putc(0x00, smfw_seq.outfile); /*will be filled by chunk_size_marker*/
|
|
putc(0x00, smfw_seq.outfile);
|
|
putc(0x00, smfw_seq.outfile);
|
|
putc(0x00, smfw_seq.outfile);
|
|
|
|
if(i == 0) { /*tempo and time signature track*/
|
|
putc(0x00, smfw_seq.outfile);/* default time sig stuff*/
|
|
putc(0xFF, smfw_seq.outfile);
|
|
putc(0x58, smfw_seq.outfile);
|
|
putc(0x04, smfw_seq.outfile);
|
|
putc(0x04, smfw_seq.outfile);
|
|
putc(0x02, smfw_seq.outfile);
|
|
putc(0x18, smfw_seq.outfile);
|
|
putc(0x08, smfw_seq.outfile);
|
|
putc(0x00, smfw_seq.outfile);
|
|
|
|
putc(0xFF, smfw_seq.outfile);/*TEMPO: inserted here in case default is used*/
|
|
putc(0x51, smfw_seq.outfile);
|
|
putc(0x03, smfw_seq.outfile);
|
|
/* ticksize is in ms<<16, so to get milliseconds per tick, it's
|
|
ticksize / 65536. To get beat durations, multiply by 24 to get
|
|
ticksize * 24 / 65536. To get microseconds, multiply by 1000:
|
|
ticksize * 24000 / 65536. Divide both constants by 64 to get
|
|
ticksize * 375 / 1024 = microseconds per quarter note.
|
|
*/
|
|
put_tick_size = scale(clock_ticksize, 375L, 1024L);
|
|
putc((int) ((put_tick_size >> 16) & 0xFF), smfw_seq.outfile);
|
|
putc((int) ((put_tick_size >> 8) & 0xFF), smfw_seq.outfile);
|
|
putc((int) (put_tick_size & 0xFF), smfw_seq.outfile);
|
|
}
|
|
smfw_seq.track = i;
|
|
smfw_dotrack(smfw_seq.seq);
|
|
}
|
|
}
|
|
if(seti_counter) gprintf(TRANS, "%d SETI events IGNORED!\n", seti_counter);
|
|
seq_stop(smfw_seq.seq);
|
|
fseek(smfw_seq.outfile, track_count_marker, 0);/*go back and insert number of tracks*/
|
|
putc(0xFF & track_count, smfw_seq.outfile);
|
|
fclose(smfw_seq.outfile);
|
|
}
|
|
|
|
|
|
/* writevarlen -- write a variable length integer to midi file */
|
|
/**/
|
|
private void writevarlen(value)
|
|
register long value;
|
|
{
|
|
register ulong buffer;
|
|
|
|
if(debug) gprintf(TRANS, "variable length quantity...");
|
|
|
|
buffer = value & 0x7f;
|
|
|
|
while((value >>= 7) > 0) {
|
|
buffer <<= 8;
|
|
buffer |= 0x80;
|
|
buffer += (value & 0x7f);
|
|
}
|
|
|
|
for(;;) {
|
|
if(debug) gprintf(TRANS, " byte ");
|
|
putc((int) (buffer & 0xFF), smfw_seq.outfile);
|
|
if (buffer & 0x80) buffer >>= 8;
|
|
else break;
|
|
}
|
|
if(debug) gprintf(TRANS, "written!\n");
|
|
}
|