/* Copyright (c) 2002-2004 by Stanley M. Swanson.
Distributed under the GNU General Public License, version 2 (GPL v2). */
/* vamp_midi.c contains code and utilities for generating the output
MIDI file from tables derived from program input */
# include "globals.h"
# ifndef LINUX /* my version of VisualC++ lacks the following */
# define srandom srand
# define random rand
# define pid_t int
# endif
void lowendian(int n, char *in, char *out)
{ int j; if (qswap) { for (j=0; jMAXOUT)
{ printf("*********buffer full (intout) \n"); over_flows++; return; }
if (qswap) { for (j=0; j<4; j++) bout[nout+3-j] = u.in[j]; }
else { for (j=0; j<4; j++) bout[nout+j] = u.in[j]; }
nout += 4; }
void shout(short sh)
{ int j; union {short s; char sh[2];}u; u.s= sh;
if (nout+2>MAXOUT)
{ printf("*********buffer full (shout) \n"); over_flows++; return;}
if (qswap) { for (j=0; j<2; j++) bout[nout+1-j] = u.sh[j]; }
else { for (j=0; j<2; j++) bout[nout+j] = u.sh[j]; }
nout += 2; }
void strout(char *str, int n)
{ int j; if (nout+n>MAXOUT)
{ printf("*********buffer full(strout) \n"); over_flows++; return; }
for (j=0; j127) { printf(" %d byte out of range\n",v); v = 0; }
b = v; bout[nout] = b; nout++; }
void delta(int v) /* MIDI variable count, eventually up to 128^4 - 1 */
{ char b, ib[4]; int i, n, iv = v;
if (v<0 || v>268435455) { printf(" %d delta out of range\n",v); v = 0; }
for (i=0; i<4; i++) { n = i; ib[i] = (iv & 0x7f); iv /= 128; if (iv==0) break; }
for (i=n; i>=0; i--) { b = ib[i]; if (i>0) b = b | 128; bout[nout] = b; nout++; }
}
void command(int cmd, int chan, int note, int veloc)
{ if (nout+3>MAXOUT)
{ printf("*********buffer full(command) \n"); over_flows++; return; }
bout[nout] = 16*(cmd & 0xf) | (chan & 0xf);
bout[nout+1] = (note & 0x7f);
bout[nout+2] = (veloc & 0x7f); nout += 3; }
void change(int cmd, int chan, int prog)
{ if (nout+2>MAXOUT)
{ printf("*********buffer full(change) \n"); over_flows++; return; }
bout[nout] = 16*(cmd & 0xf) | (chan & 0xf);
bout[nout+1] = (prog & 0x7f); nout += 2; }
void midi_file_header(int org, int tracks, int delta_time, char *name)
{ int i,j,k,m,n; short s;
char filnam[MAXIN], *mhead = "MThd", i4[4], j4[4], i2[2], j2[2];
if (name) { fout = new_file(name,"wb"); }
else { printf("output file: "); n = finstring(filnam,MAXIN-4,stdin);
fout = fopen(filnam,"wb");}
/* 046.02: Windows (VisualC++) appends 0x0d to every 0 byte if "w" */
if (! fout) { printf("cannot open midi output file\n"); return; }
/* MIDI file header */
fwrite(mhead,1,4,fout);
i = 6; lowendian(4,(char*)&i,i4); fwrite(i4,1,4,fout);
s = org; /* 0: single track; 1: one or more simultaneous tracks;
2: one or more sequentially independent single tracks */
lowendian(2,(char*)&s,i2); fwrite(i2,1,2,fout);
s = tracks; /* number of tracks in file (==1 for format 0 file) */
lowendian(2,(char*)&s,i2); fwrite(i2,1,2,fout);
s = delta_time; /* ticks/quarter note>0, -SMPTE ticks/frame<0 */
lowendian(2,(char*)&s,i2); fwrite(i2,1,2,fout);
} /* midi_file_header() */
void microseconds_in_beat(float bpm)
{ int u,usec; char i4;
/* FF51 03 tttttt : set tempo, u sec/quarter note (24 bits) */
u = usec = 6.0e7/bpm; usec += 0x03000000; /* add byte count to 24 bit integer */
printf("bpm %g microseconds in beat %12d %12x\n",bpm,u,u);
delta(0); shout(0xFF51); intout(usec); /* Q = 500000 usec */
/* cakewalk puts tempo, key stuff into info track */
/* delta(0); shout(0xFF51); intout(0x0307A120); /* Q = 500000 usec */
/* delta(0); shout(0xFF51); intout(0x0303D090); /* Q = 250000 usec */
}
void midi_info(char *title)
{ int i,j,k,m,n,t=0; short s;
char *text="experimental MIDI sequences",
/* *copyleft=
"Copyright 2003 by Stanley M. Swanson under GPL, DSL, [cc]", */
*mhead = "MThd", *mtrk = "MTrk", i4[4], j4[4], i2[2], j2[2];
/* MIDI info track */
/* FF01 len text : arbitrary text */
/* FF02 len text : copyright notice */
/* FF03 len text : sequence-track name */
/* FF04 len text : instrument name */
/* FF2F 0 : end of track (required) */
/* FF58 04 nn dd cc bb : time signature nn/2**dd,
cc clocks/metronome, bb ? */
/* FF59 02 sf mi : key signature sf<0 flats, >0 sharps, ==0 key of C;
mi 0 major, 1 minor */
/* FF51 03 tttttt : set tempo, u sec/quarter note (24 bits) */
if (! fout) return;
nout = 0;
if (copyleft) { delta(0); shout(0xFF02);
i = strlen(copyleft); delta(i); strout(copyleft,i); }
i = strlen(commenta);
if (i) { delta(0); shout(0xFF01); delta(i); strout(commenta,i); t=0;
if (Q_VERBOSE>4 && V_CLASS==4) printf(" commenta strlen %d\n",i); }
i = strlen(commentb);
if (i) { delta(0); shout(0xFF01); delta(i); strout(commentb,i); t=0;
if (Q_VERBOSE>4 && V_CLASS==4) printf(" commentb strlen %d\n",i); }
delta(0);
shout(0xFF03); i = strlen(title); delta(i); strout(title,i);
if (Q_VERBOSE>4 && V_CLASS==4) printf(" strlen title %d\n",i);
/* 046.18 suppress: t==0 */
if (t) {delta(0); shout(0xFF01);
i = strlen(text); delta(i); strout(text,i);
if (Q_VERBOSE>4 && V_CLASS==4) printf(" strlen text %d\n",i); }
microseconds_in_beat(bpm);
/* delta(0); shout(0xFF58); delta(4); intout(0x04026060); */
/* 4/4, 0x60 clocks/beat, 0x60 ticks/Quarternote */
/* delta(0); shout(0xFF58); delta(4); intout(0x03024040); */
/* 3/4, 0x40 clocks/beat, 0x40 ticks/Quarternote */
/* 046.18
* delta(0); shout(0xFF58); delta(4);
* delta(meter_num);
* k = 2; if (meter_denom==2) k=1; if(meter_denom==8) k=3;
* delta(k); delta(0x60); delta(0x60);
*/
delta(0); shout(0xFF2F); delta(0); /* end of track */
printf(" info trk length %d %x %o \n",nout,nout,nout);
fwrite(mtrk,1,4,fout);
i = nout; lowendian(4,(char*)&i,i4); fwrite(i4,1,4,fout);
fwrite(bout,1,nout,fout);
} /* midi_info() */
int spawn_play(char *midifile)
{ pid_t pid=0; if (!Q_PLAY) return 0; /* 039.02 suppress with verbose<0 */
# ifdef LINUX /* do not know how to do this in Windows */
pid = fork(); /* spawn timidity to play midi file */
if (pid<0) { printf("fork error\n"); return pid; }
if (pid==0) { /* child process */
/* 032.10 execlp("timidity","timidity",midifile,NULL); */
execlp("timidity","timidity","-A","200","-B","8,12","-Od","-s","48000",
midifile,NULL);
printf("error: return from spawn in child\n"); }
# endif
return pid;
}
/* 030.25 dither: imprecision in note start and stop,
initialize in set_dither to sigma==msec (milliseconds ~ 10 to 20)
generate randomly in dither(),
apply on a per note-cluster basis in make_notes, with constraints
of not starting a new cluster before previous one ends.
chords have single start, end point, except guitar strum
*/
double tick_msec, scale_dither, trans_rand, scale_rand, sigma_rand;
int last_dither, max_dither, knt_dither, minim, dither_mode;
int pack_kount, gen_kount;
void pack_event( int *kx, int ticks, int cmd, int chan, int d1, int d2)
{ union {int i; char c[4]; } ev; int k = *kx;
ev.c[0] = cmd; ev.c[1] = chan; ev.c[2] = d1; ev.c[3] = d2;
epoch[k] = ticks; event[k] = ev.i;
pack_kount++;
if (Q_VERBOSE>6 && V_CLASS==4) printf("%3d pack %5d event %10x %3d\n",k,epoch[k],ev.i,ev.c[2]);
k++; if (k6 && V_CLASS==4)
printf("%3d epoch %5d event %10x %3d\n",j,epoch[j],ev.i,ev.c[2]);
if (epoch[j]>dnow) break; knt++; jj = j;
dur = epoch[j] - *previous;
if (dur<0) printf("bad delta for event %d epoch %d previous %d\n",
j,epoch[j],*previous);
*previous = epoch[j];
if (dur<0) continue;
ev.i = event[j];
gen_kount++;
delta(dur); command(ev.c[0], ev.c[1], ev.c[2], ev.c[3]); }
if (Q_VERBOSE>2 && V_CLASS==4)
printf("%4d midi events generated, now %d, initial %d final %d\n",
knt, now, epoch[0], epoch[k-1]);
if (jj+1now */
for (m=0,n=jj+1; n= 0.0) k = (delta +0.5); else k = (delta - 0.5);
/* dither_mode: 0 random change to time on any note/note group
1 random change if not first beat in phrase
2 melody (n==1) leads synched chords
3 melody (n==1) lags synched chords */
if (dither_mode==1 && first_beat) k = 0;
else if (dither_mode==2) { if (n>1) k = 0; else if (k>0) k = -k; }
else if (dither_mode==3) { if (n>1) k = 0; else if (k<0) k = -k; }
return k;
} /* dither() */
void make_notes(int n, int *tones, int now, int dur, int vol, int chan)
{ int k, now_start, now_end, vel = vol, delta;
if (vol<=0) return; /* 045.30 try pruning midi file */
/* 045.29 (N violin problem) attempt to have total chord volume approximate 'vol' */
if (vol>0 && n>1) { k = n; if (k>7) k = 7; delta = chord_quiet*chord_delta[k] + 0.5;
vel = vol - delta; if (vel<0) vel = 0; }
if (vel>127) vel = 127; /* keep in valid range [0...127] */
/* now_start = now + dither(n); now_end = now + dur + dither(n); */
/* change to dither start, use duration (Rosie, 045.08) */
now_start = now + dither(n); now_end = now_start + dur;
if (now_start<=last_dither) now_start = last_dither + 1;
if (now_end<=now_start + minim) { now_end = now_start + minim; }
for (k=0; k=0) { if (k>127) k = 127; delta(0); change(0xC,i,k);
printf(" midi track chan %d, instrument %d\n",i,k+1);
if (flog) fprintf(flog,"midi track chan %d, instrument %d\n",i,k+1); }
/* delta(0); command(0xB,chan,7,mix_ch);*/ /* channel volume */
}
if (pan!=64) { delta(0); command(0xB,chan,10,pan); } /* channel pan */
} /* start_midi_music_track() */
int gen_midi_music_track(int npi, int npf, int chan, int chords)
{ int i,k,n, tone,knt, dur, vol,solo,potatoes, e_beat;
int kord[20],inkord,kordur,voice,trans,left;
/* adds commands to an existing MIDI track (mostly notes) */
/* MIDI commands */
/* 8[0-F] nn vv : channel [0-F] off, nn = note, vv = velocity */
/* 9[0-F] nn vv : channel [0-F] on, nn = note, vv = velocity */
/* A[0-F] nn aa : polyphonic aftertouch, nn = note, aa = aftertouch */
/* B[0-F] cc vv : control/mode change (see table 3) */
/* C[0-F] nn : program change, nn = patch */
/* D[0-F] aa : aftertouch, aa = amount */
/* E[0-F] lsb msb : pitch wheel control lsb (0-127), msb (0-127)*128 */
/* F[0-F] ... system commands */
knt = inkord = 0; /* presume not inside a chord */
voice = 0; trans = trans_mel;
if (chords==1) { voice = 1; trans = trans_ch; }
solo = potatoes = mix_mel; if (mix_ch2 && V_CLASS==4)
printf(">> chords %d chan %d first %d last %d \n",chords,chan,npi,npf);
for (n=npi; n=lacc) kacc = iacc; }
} else if (tone == E_BAR ) /* measure bar seen */
{ left = sort_gen_events(&kgen_mm,&start_mm,now_mm);
if (tictoc_mm>0) meas_mm++; tictoc_mm = 0;
if (tictoc_mm>0 && Q_VERBOSE>1 && V_CLASS==4)
printf("measure %3d notes %4d duration %4d nout %5d\n",
meas_mm,tictoc_mm/quantum,tictoc_mm,nout);
if (Q_VERBOSE>2 && V_CLASS==4)
printf("last out %d now_mm %d left %d\n",start_mm,now_mm,left);
}
} else { /* tone>=0 */
switch (chords) { /* volume setting alternatives, 042.29 cf: get_velocities() */
case -1: vol = potatoes; break;
case 0: vol = solo; break;
case 1: vol = phrasing[kacc];
/* 02x.06 high digit flags: +i0000, change instrument;
+s000 use staccato to shorten */
if (vol>9999)
{ int i; i = vol / 10000; vol -= i*10000;
chan = i - 1; /* 031.09, 042.28 try channel change */
if (Q_VERBOSE) printf(" instr change %d %d chan %d\n",i,instrument_list[3+i],chan);
} /* (vol>9999) */
if (vol>999) { int i; i = vol / 1000; vol -= i*1000;
silence_mm = i*staccato;
if (silence_mm>(3*dur)/4) silence_mm = (3*dur)/4; }
break;
case 2: vol = veloc[nbeat]; if (off_beat) vol += deloff; else
if (down_beat) vol += deldown; else if (up_beat) vol += delup;
break;
default: if (knote_mm<3) printf("bad chord flag %d in gen_midi_ \n",chords);
vol = solo;
} /* switch (chords) */
vol += volume_delta; /* 045.03 _volume_ in events */
if (n_emph>0) { e_beat = tick_sum/ticks_in_beat; vol += emphasis[e_beat]; }
if (vol<0) vol = 0;
/* 045.24 debug if (vol>127) vol = 127; else if (vol<1) vol = 1; */
/* final rest in [chord] will cancel volume (might need vol[] )*/
if (tone==E_REST) { tone = last_mm; vol = 0;
if (knote_mm==0) vol =1; /* 031.29 initial rest */}
if (chords==2 && nbeat<16 && Q_VERBOSE>1 && V_CLASS==4)
printf("nbeat %3d %d %d %d tone %3d vol %3d\n",
nbeat,down_beat,up_beat,off_beat,tone,vol);
if (Q_VERBOSE>6 && V_CLASS==4) printf(" %4d kacc %4d tone %4d vol %4d dur\n",
kacc,tone,vol,kordur);
if (inkord) { if (knt==0) kordur = dur; if (tone>0) last_mm = tone;
tone += trans; if (tone<0) tone = 0; if (tone>127) tone = 127;
kord[knt] = tone; if (knt<18) knt++;
knote_mm++; /* first note flag for initial rests... */
} /* end if (inkord) */
else { /* default melody behavior (or non chords in vamp 0.35.26) */
if (vol>0) { knote_mm++; last_mm = tone; }
if (tone<0||tone>127) printf("bad tone %d in melody\n",tone);
tone += trans; if (tone<0) tone = 0; if (tone>127) tone = 127;
make_notes(1,&tone,now_mm,dur-staccato,vol,chan); now_mm += dur;
tictoc_mm += dur; pedometer(dur);
if (Q_VERBOSE>4 && V_CLASS==4) printf(" %3d tone %4d dur %4d vol %4d\n",n,tone,dur,vol);
} /* melody ( ! inkord ) */
} /* ( tone cf 0 ) */
gen_epoch = now_mm; /* 035.17 for event tracking */
} /* for (n=npi ... npf) music loop */
} /* gen_midi_music_track() */
int write_midi_music_track(void)
{ int i; char i4[4], *mtrk="MTrk";
/* terminate and write MIDI track */
if (kgen_mm>0) /*clean up*/
sort_gen_events(&kgen_mm,&start_mm,now_mm+4*max_dither+100);
printf(" pack %d gen %d\n",pack_kount,gen_kount);
delta(100); shout(0xFF2F); delta(0); /* end of track */
if (fout) {
fwrite(mtrk,1,4,fout);
i = nout; lowendian(4,(char*)&i,i4); fwrite(i4,1,4,fout);
fwrite(bout,1,nout,fout);
printf("MIDI track written: length %d hex %x ",nout,nout);
printf(" ticks %d measures %d\n",gen_epoch,gen_epoch/ticks_in_meas);
}
return 0;
} /* write_midi_music_track() */
/* deal with fragments (from repeats) and possible endings
"[3" for last time through and (coda != 0) */
/* also translate chord list into fragments,
vamp should work since count==0 */
void gen_midi_frags(int kchunk, int chan, int chords, int coda)
{ int ip, lp, ifrag, lfrag, kount, kf, k, koda=0, type;
if (kchunk<0 || kchunk>=chunks)
{ printf("chunk index %d out of range (gen_midi_frags)\n",kchunk);
return; }
ip = ifrag = chunk[kchunk].ix; lp = lfrag = chunk[kchunk].lx;
kount = chunk[kchunk].count; /* zero means one fragment [ip:lp-1] */
type = chunk[kchunk].type;
if (Q_VERBOSE>2 && (V_CLASS==0 || V_CLASS==4))
printf("<<<4 && (V_CLASS==4)) printf("gen_frag chord %d i %d f %d\n",k,ip,lp);
/* sanity check chunk[k].type == T_VAMP ? */
gen_midi_music_track(ip,lp,chan,chords); }
} else if (type == T_VAMP) { printf("count not zero VAMP gen_midi_frags\n");
gen_midi_music_track(ip,lp,chan,chords);
} else printf(" bad type %d (gen_midi_frags)\n",type);
} /* gen_midi_frags() */