mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-04 17:49:45 +02:00
449 lines
13 KiB
C
449 lines
13 KiB
C
/*
|
|
* seqmread.c
|
|
*
|
|
* Convert a MIDI file to a seq.
|
|
*/
|
|
|
|
/* Copyright 1989 Carnegie Mellon University */
|
|
|
|
/*****************************************************************************
|
|
* Change Log
|
|
* Date | who : Change
|
|
*-----------+-----------------------------------------------------------------
|
|
* 17-Feb-92 | GWL : only one stdio.h
|
|
* : fix to satisfy compiler:
|
|
void returns, time_type giotime(), int filegetc()
|
|
*****************************************************************************/
|
|
|
|
#include "switches.h"
|
|
#include "stdio.h"
|
|
#include "cext.h"
|
|
#include "cmdline.h"
|
|
#include "midifns.h" /* to get time_type */
|
|
#include "timebase.h"
|
|
#include "moxc.h" /* to get debug declared */
|
|
#include "seq.h"
|
|
#include "seqread.h" /* to get scale */
|
|
#include "seqmread.h"
|
|
#include "userio.h"
|
|
#include "ctype.h"
|
|
|
|
#include "midifile.h"
|
|
#include "tempomap.h"
|
|
|
|
int filegetc();
|
|
void initfuncs();
|
|
void prtime();
|
|
void snding_free();
|
|
|
|
typedef struct snding_struct {
|
|
struct snding_struct *next;
|
|
event_type event_ptr;
|
|
int pitch;
|
|
int channel;
|
|
} snding_node, *snding_type;
|
|
|
|
#define snding_alloc() (snding_type) memget(sizeof(snding_node))
|
|
#define snding_free(s) memfree(s, sizeof(snding_node))
|
|
|
|
snding_type snding_list = NULL;
|
|
|
|
tempomap_type the_tempomap;
|
|
event_type initial_clock; /* remember the first clock event */
|
|
long prev_ticksize; /* remember the previous ticksize */
|
|
int sysex_id = 0;
|
|
|
|
|
|
void smf_noteoff();
|
|
void smf_error();
|
|
void smf_header();
|
|
void smf_trackstart();
|
|
void smf_trackend();
|
|
void smf_noteon();
|
|
void smf_pressure();
|
|
void smf_parameter();
|
|
void smf_pitchbend();
|
|
void smf_program();
|
|
void smf_chanpressure();
|
|
void smf_sysex();
|
|
void smf_metamisc();
|
|
void smf_metaseq();
|
|
void smf_metaeot();
|
|
void smf_timesig();
|
|
void smf_smpte();
|
|
void smf_tempo();
|
|
void smf_keysig();
|
|
void smf_metaspecial();
|
|
void smf_metatext();
|
|
void smf_arbitrary();
|
|
|
|
private seq_type the_score;
|
|
|
|
static FILE *F;
|
|
|
|
int filegetc()
|
|
{
|
|
/* int temp = getc(F);
|
|
printf(" %x ", temp);*/
|
|
return(int)(getc(F));
|
|
}
|
|
|
|
void seq_read_smf(seq, fp)
|
|
seq_type seq;
|
|
FILE *fp;
|
|
{
|
|
F = fp;
|
|
initfuncs();
|
|
sysex_id = 0; /* sysex in seq has to correspond to a symbol */
|
|
the_score = seq; /* current sequence is a global within this module */
|
|
if (!seq) return;
|
|
the_tempomap = tempomap_create();
|
|
/* insert an initial clock to correspond to the default midifile tempo
|
|
(tempomap_create creates a corresponding initial entry in the tempomap)
|
|
(see smf_tempo for explanation of the scale() call)
|
|
*/
|
|
initial_clock = insert_clock(the_score, 0L, 0, 500L << 16);
|
|
/* scale(24 * 500000, 1 << 16, 24000) */
|
|
if (!the_tempomap) return;
|
|
Mf_getc = filegetc;
|
|
midifile();
|
|
/* fmac_close(F); -- do not close the file because the caller might try to
|
|
* close it (in fact XLISP insists on closing it as a side effect of
|
|
* garbage collection.
|
|
*/
|
|
gprintf(TRANS, "\nLoaded Midi file with %ld note(s), %ld ctrl(s).\n\n",
|
|
seq_notecount(seq), seq_ctrlcount(seq));
|
|
seq_reset(seq);
|
|
while (snding_list) {
|
|
snding_type snding = snding_list;
|
|
snding_list = snding_list->next;
|
|
gprintf(TRANS, "Note-on (key %d, chan %d) has no matching noteoff\n",
|
|
snding->pitch, snding->channel + 1);
|
|
snding_free(snding);
|
|
}
|
|
tempomap_free(the_tempomap);
|
|
}
|
|
|
|
|
|
/* gio_time -- get the time in millisec for Adagio */
|
|
/*
|
|
* Since Adagio times are (in their precise form) 1/256 ms, we want
|
|
* a similar time for midifiles, whose natural unit would be microseconds.
|
|
* We'll shift the microsecond time by 2 to get 1/250 ms = 4 us units
|
|
* and convert using the scale function when necessary.
|
|
* Real time is the time of the last tempo change (last_tempo_time)
|
|
* which is in 4us units + elapsed time.
|
|
* Elapsed time is the elapsed beats times the beat duration.
|
|
* Elapsed beats is Mf_currtime - last_tempo_beat.
|
|
* Beat duration is the specified tempo / division, where specified tempo
|
|
* is in microseconds, and division is parts per quarternote.
|
|
*/
|
|
unsigned long divisions = 24L;
|
|
|
|
time_type gio_time()
|
|
{
|
|
return (tempomap_lookup(the_tempomap, Mf_currtime) + 125L) / 250L;
|
|
}
|
|
|
|
|
|
void smf_header(format,ntrks,division)
|
|
{
|
|
/* gprintf(TRANS, "Header format=%d ntrks=%d division=%d\n",
|
|
format,ntrks,division); */
|
|
if (format > 1) gprintf(TRANS,
|
|
"Warning: format %d midi file may not work.\n",
|
|
format);
|
|
divisions = division;
|
|
/* adjust the initial tempochange */
|
|
the_tempomap->entries->tempo = 500000L / division;
|
|
}
|
|
|
|
|
|
void smf_trackstart()
|
|
{
|
|
/* gprintf(TRANS, "Track start\n"); */
|
|
}
|
|
|
|
void smf_trackend()
|
|
{
|
|
/* gprintf(TRANS, "Track end\n"); */
|
|
}
|
|
|
|
void smf_noteon(chan,pitch,vol)
|
|
{
|
|
snding_type snding;
|
|
if (vol == 0) { /* convert to a noteoff */
|
|
smf_noteoff(chan, pitch, 0);
|
|
return;
|
|
}
|
|
/* prtime();
|
|
gprintf(TRANS, "Note on, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol);
|
|
*/
|
|
/* get ready to remember the sounding note */
|
|
snding = snding_alloc();
|
|
snding->next = snding_list;
|
|
snding_list = snding;
|
|
/* enter an event into score and remember it */
|
|
snding->event_ptr = insert_note(the_score, gio_time(), 0,
|
|
chan + 1, pitch, 0L, vol);
|
|
snding->pitch = pitch;
|
|
snding->channel = chan;
|
|
}
|
|
|
|
void smf_noteoff(chan,pitch,vol)
|
|
{
|
|
snding_type *snding_ptr;
|
|
register snding_type snding;
|
|
/* prtime();
|
|
gprintf(TRANS, "Note off, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol);
|
|
*/ /* search for the snding record */
|
|
for (snding_ptr = &snding_list;
|
|
(snding = *snding_ptr) &&
|
|
((snding->pitch != pitch) || (snding->channel != chan));
|
|
snding_ptr = &(snding->next)) /* printf("* search *\n") */;
|
|
if (!snding) {
|
|
gprintf(TRANS, "Note off %d, channel %d ignored: no note on\n",
|
|
pitch, chan + 1);
|
|
} else {
|
|
event_type event = snding->event_ptr;
|
|
event->u.note.ndur += (gio_time() - event->ntime) << 8;
|
|
/* free the snding record */
|
|
*snding_ptr = snding->next;
|
|
snding_free(snding);
|
|
}
|
|
}
|
|
|
|
|
|
void smf_pressure(chan,pitch,press)
|
|
{
|
|
prtime();
|
|
gprintf(TRANS, "Pressure, chan=%d pitch=%d press=%d (IGNORED)\n",
|
|
chan + 1, pitch, press);
|
|
}
|
|
|
|
void smf_parameter(chan,control,value)
|
|
{
|
|
int ctrl = 0;
|
|
/* prtime();
|
|
gprintf(TRANS, "Parameter, chan=%d c1=%d c2=%d\n",chan+1,control,value);
|
|
*/ /* see if the control is one of the standard Adagio controls that
|
|
can be encoded in a special way. If not, ctrl remains at zero.
|
|
*/
|
|
switch (control) {
|
|
case PORTASWITCH: ctrl = PSWITCH_CTRL; break;
|
|
case MODWHEEL: ctrl = MODWHEEL_CTRL; break;
|
|
case VOLUME: ctrl = VOLUME_CTRL; break;
|
|
}
|
|
if (ctrl) /* then do special ctrl insert and save storage */
|
|
insert_ctrl(the_score, gio_time(), 0, ctrl, chan + 1, value);
|
|
else insert_macctrl(the_score, gio_time(), 0, control, chan + 1, value);
|
|
}
|
|
|
|
|
|
/* smf_pitchbend -- handle a pitch bend event */
|
|
/*
|
|
* NOTE: the midifile code from Tim Thompson has the msb and lsb bytes swapped.
|
|
* Thus the parameter msb is really the low order byte and lsb is high order.
|
|
*/
|
|
void smf_pitchbend(chan,msb,lsb)
|
|
{
|
|
/* prtime();
|
|
gprintf(TRANS, "Pitchbend, chan=%d msb=%d lsb=%d\n",chan+1,msb,lsb); */
|
|
insert_ctrl(the_score, gio_time(), 0, BEND_CTRL, chan + 1,
|
|
((lsb << 7) + msb) >> 6);
|
|
}
|
|
|
|
void smf_program(chan,program)
|
|
{
|
|
/* prtime();
|
|
gprintf(TRANS, "Program, chan=%d program=%d\n",chan+1,program); */
|
|
insert_ctrl(the_score, gio_time(), 0, PROGRAM_CTRL, chan + 1, program);
|
|
}
|
|
|
|
void smf_chanpressure(chan,press)
|
|
{
|
|
/* prtime();
|
|
gprintf(TRANS, "Channel pressure, chan=%d pressure=%d\n",chan+1,press);
|
|
*/
|
|
insert_ctrl(the_score, gio_time(), 0, TOUCH_CTRL, chan + 1, press);
|
|
}
|
|
|
|
void smf_sysex(leng,mess)
|
|
int leng;
|
|
char *mess;
|
|
{
|
|
char symb[10];
|
|
def_type defn;
|
|
int i;
|
|
sprintf(symb, "X%d", sysex_id++);
|
|
if (leng > 255) {
|
|
gprintf(TRANS, "sysex too long (%d bytes), ignored\n", leng - 2);
|
|
return;
|
|
}
|
|
/* need to end up with a prefix of [0][length], so add 2 to length;
|
|
note that this will copy past the end of the message -- this is
|
|
slightly dangerous and definitely crufty:
|
|
*/
|
|
defn = insert_def(the_score, symb, (unsigned char *) mess, leng + 2);
|
|
/* now fix up the definition by inserting the prefix bytes: */
|
|
for (i = leng + 1; i > 1; i--)
|
|
defn->definition[i] = defn->definition[i - 2];
|
|
defn->definition[0] = 0;
|
|
defn->definition[1] = leng;
|
|
insert_macro(the_score, gio_time(), 0, defn, 1, 0, NULL);
|
|
/* prtime();
|
|
gprintf(TRANS, "Sysex, leng=%d (IGNORED)\n",leng); */
|
|
}
|
|
|
|
void smf_metamisc(type,leng,mess)
|
|
char *mess;
|
|
{
|
|
prtime();
|
|
gprintf(TRANS,
|
|
"Meta event, unrecognized, type=0x%02x leng=%d (IGNORED)\n",
|
|
type, leng);
|
|
}
|
|
|
|
void smf_metaspecial(type,leng,mess)
|
|
char *mess;
|
|
{
|
|
prtime();
|
|
gprintf(TRANS,
|
|
"Meta event, sequencer-specific, type=0x%02x leng=%d (IGNORED)\n",
|
|
type, leng);
|
|
}
|
|
|
|
void smf_metatext(type,leng,mess)
|
|
char *mess;
|
|
{
|
|
static char *ttype[] = {
|
|
NULL,
|
|
"Text Event", /* type=0x01 */
|
|
"Copyright Notice", /* type=0x02 */
|
|
"Sequence/Track Name",
|
|
"Instrument Name", /* ... */
|
|
"Lyric",
|
|
"Marker",
|
|
"Cue Point", /* type=0x07 */
|
|
"Unrecognized"
|
|
};
|
|
int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1;
|
|
|
|
if ( type < 1 || type > unrecognized )
|
|
type = unrecognized;
|
|
}
|
|
|
|
void smf_metaseq(num)
|
|
{
|
|
prtime();
|
|
gprintf(TRANS, "Meta event, sequence number = %d (IGNORED)\n",num);
|
|
}
|
|
|
|
void smf_metaeot()
|
|
{
|
|
/* prtime();
|
|
gprintf(TRANS, "Meta event, end of track\n"); */
|
|
}
|
|
|
|
void smf_keysig(sf,mi)
|
|
{
|
|
/* prtime();
|
|
gprintf(TRANS, "Key signature, sharp/flats=%d minor=%d\n",sf,mi); */
|
|
}
|
|
|
|
/* smf_tempo -- handle a midifile tempo change */
|
|
/*
|
|
* NOTE: if divisions is positive, it gives time units per quarter, and
|
|
* tempo is microsec per division. The product is microsec per quarter.
|
|
* To convert to ticksize (parameter to insert_clock), we divide by 24*1000
|
|
* to get units of millisec and 24ths of quarter notes. insert_clock
|
|
* expects this to have a 16 bit fractional part.
|
|
*/
|
|
void smf_tempo(tempo)
|
|
long tempo;
|
|
{
|
|
time_type ctime = gio_time();
|
|
long ticksize = scale(tempo, 1024L, 375L);
|
|
/* (tempo / 24000) << 16; microsec/clock converted to ms/quarter, shifted 16*/
|
|
|
|
/* prtime();
|
|
gprintf(TRANS, "Tempo, microseconds-per-MIDI-quarter-note = %ld\n",tempo);
|
|
*/
|
|
tempomap_insert(the_tempomap, Mf_currtime, tempo / divisions);
|
|
if (ctime == 0) {
|
|
/* we already have a clock event at t=0 -> fix it */
|
|
initial_clock->u.clock.ticksize = ticksize;
|
|
} else { /* we need a new one */
|
|
/* NOTE: after the first clock, insert clock events 1/2 tick early
|
|
to make sure ticksize is set before clock_tick() wakes up and
|
|
reads it.
|
|
*/
|
|
insert_clock(the_score, ctime - (prev_ticksize >> 17), 0, ticksize);
|
|
prev_ticksize = ticksize;
|
|
}
|
|
}
|
|
|
|
void smf_timesig(nn,dd,cc,bb)
|
|
{
|
|
/* int denom = 1;
|
|
while ( dd-- > 0 )
|
|
denom *= 2;
|
|
prtime();
|
|
gprintf(TRANS,
|
|
"Time signature=%d/%d MIDI-clocks/click=%d 32nd-notes/24-MIDI-clocks=%d\n",
|
|
nn,denom,cc,bb); */
|
|
}
|
|
|
|
void smf_smpte(hr,mn,se,fr,ff)
|
|
{
|
|
prtime();
|
|
gprintf(TRANS,
|
|
"SMPTE, hour=%d minute=%d second=%d frame=%d fract-frame=%d (IGNORED)\n",
|
|
hr, mn, se, fr, ff);
|
|
}
|
|
|
|
void smf_arbitrary(leng,mess)
|
|
char *mess;
|
|
{
|
|
prtime();
|
|
gprintf(TRANS, "Arbitrary bytes, leng=%d (IGNORED)\n",leng);
|
|
}
|
|
|
|
void smf_error(msg)
|
|
char *msg;
|
|
{
|
|
gprintf(ERROR, msg);
|
|
}
|
|
|
|
|
|
void prtime()
|
|
{
|
|
gprintf(TRANS, "Time=%ld/%ld ",Mf_currtime, gio_time());
|
|
}
|
|
|
|
void initfuncs()
|
|
{
|
|
Mf_error = smf_error;
|
|
Mf_header = smf_header;
|
|
Mf_starttrack = smf_trackstart;
|
|
Mf_endtrack = smf_trackend;
|
|
Mf_on = smf_noteon;
|
|
Mf_off = smf_noteoff;
|
|
Mf_pressure = smf_pressure;
|
|
Mf_controller = smf_parameter;
|
|
Mf_pitchbend = smf_pitchbend;
|
|
Mf_program = smf_program;
|
|
Mf_chanpressure = smf_chanpressure;
|
|
Mf_sysex = smf_sysex;
|
|
Mf_metamisc = smf_metamisc;
|
|
Mf_seqnum = smf_metaseq;
|
|
Mf_eot = smf_metaeot;
|
|
Mf_timesig = smf_timesig;
|
|
Mf_smpte = smf_smpte;
|
|
Mf_tempo = smf_tempo;
|
|
Mf_keysig = smf_keysig;
|
|
Mf_sqspecific = smf_metaspecial;
|
|
Mf_text = smf_metatext;
|
|
Mf_arbitrary = smf_arbitrary;
|
|
}
|