/* 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() */