&dA &dA &dA &d@ Program to compile MIDI release files from stage2 files &dA &dA &d@ Version as of 04/05/94 &dA &dA &d@ &dI04/05/94&d@ Capability added to change velocity &dA &dA &d@ &dI06/03/94&d@ Capability added to play extra-time cadenzas &dA &dA #define MEASDX 6000 #define LIC_ADD1 46 #define LIC_ADD0 82 str music.150000 str rec.100,slib.100,line.300,line2.300,clib.100,templines.100(200) str rec2.100,scode.100,xtime.4,saveline.40,trec.100 str files.60(200),mvtfile.12,outfile.100,infile.100 str measdata.256(MEASDX),orn.1(15),midioutfile.100 str inst.100(32),pitch.4,out.80,temp.256,temp2.256 str outbuf.1000,let.1 str mpn.100(60) str group.20,groupa.20 int speeddata(MEASDX) int speeddata2(MEASDX) int trillflag(6), mortflag(5) int i, j, k, mtpq, tinc int trkcnt, crnttrk int tcnt, xtcnt, oldc int mcount, spitch, sdur, tdur, ndur, tpitch, tsdur int lastdur, ornflag int a, b, c, d, e, f, g, x, h int p, q, r, s int cc int save_s int divcount, measnum, divpoint, tdivpoint, addoctave int divspq(32), transpose, datapoint(32), divspm(32) int convert(40) int glob_divspm, divmult(32), glob_divspq, loc_divspq, nloc_divspq int grace_percent(20), grace_pitch(20), grace_count, grace_pass(20) int grace_flag(20) int savesub1 int nn, pp, qq, dd(60), hh, ii, jj, kk, pp2 int max_pp int ppntr, elapsed_time, len_ppntr int fsize, msize int art_factor,restrike_pause int max_restrike_pause,first_restrike_pause,save_restrike_pause int restrike_factor, restrike_factor1, restrike_factor2 int tmpcnt,midi_open int bpm,bpm_cnt,bpm_cnt1,bpm_cnt2 int bpm_data(50),bpm_data1(50),bpm_data2(50) int notes_on(127), note_is_on, active_pitch(127) int spell_code, spelling(40) int play_size int eof3flag,midi_trkcnt(2),midi_type int chanst1(16,2),chanst2(16,2),chanst(16,2) int midiass1(32),midiass2(32),midiass(32) int midiform int zcnt,fac,olddiv,real_divspq,q_rec,nq_rec,ycnt,max_ycnt,rcnt int tnum, tden int rep_mark, seg_mark, fineflag, take, xcnt int trcnt,inst_flag int channel int chanpntr(16), t1 int para_change(100,3) /* parameter changes in a movement int pc_cnt /* parameter change counter int tempo_change(100,2) /* tempo changes in a movement int tempo_cnt /* tempo change counter int time_cnt,license_loc,loc_time_cnt int icode,spcode,sicode,termflag,last_time_out,delta_time int trackloc(32) int newvel, curvel &dA &dA &d@ Variables added to deal with "real-time" grace notes &dA int add_time_flag,add_time_sit(20,5),sit_cnt int sit_part,sit_mcnt,sit_divspq int sav_sit_cnt,trigger,qratio,sit_incre,savsub &dA &dA &d@ Description of add_time_sit (.,1) = part number containing situation &dA &d@ (.,2) = number of measures to situation &dA &d@ (.,3) = divspq at measure with situation &dA &d@ (.,4) = number of divisions to situation &dA &d@ (.,5) = time duration of situation (in divisions) &dA table X(100000) table Y(100000) &dA &dA &d@ Description of trillflag(6): (1) 0 = no trill &dA &d@ 1 = start on upper note &dA &d@ 2 = start on main note &dA &d@ (2) 0 = whole step trill &dA &d@ 1 = half step trill &dA &d@ (3) n = number of beats &dA &d@ (4) s = start parameter (second beat) &dA &d@ (5) t = end parameter (last beat) &dA &d@ (6) 0 = no terminating turn &dA &d@ 1 = terminating turn 1/2 step below &dA &d@ 2 = terminating turn whole step below &dA &dA &dA &d@ Description of mortflag(5): (1) 0 = no mordant &dA &d@ 1 = start on main note &dA &d@ 2 = start on lower note &dA &d@ (2) 0 = whole step mordant &dA &d@ 1 = half step mordant &dA &d@ (3) n = number of beats &dA &d@ (4) s = start parameter (second beat) &dA &d@ (5) t = end parameter (last beat) &dA &dA &dA &d@ &dA MIDI Patch Numbers &dA mpn( 1) = " 1 Acoustic Grand Piano 17 Drawbar Organ 33 Acoustic Bass " mpn( 2) = " 2 Bright Acoustic Piano 18 Percussive Organ 34 Electric Bass (finger) " mpn( 3) = " 3 Electric Grand Piano 19 Rock Organ 35 Electric Bass (pick) " mpn( 4) = " 4 Honky-tonk Piano 20 Church Organ 36 Fretless Bass " mpn( 5) = " 5 Electric Piano 1 21 Reed Organ 37 Slap Bass 1 " mpn( 6) = " 6 Electric Piano 2 22 Accordian 38 Slapp Bass 2 " mpn( 7) = " 7 Harpsichord 23 Harmonica 39 Synth Bass 1 " mpn( 8) = " 8 Clav 24 Tango Accordian 40 Synth Bass 2 " mpn( 9) = " 9 Celesta 25 Acoustic Guitar (nylon) &dA41 Violin &d@ " mpn(10) = " 10 Glockenspiel 26 Acoustic Guitar (steel) &dA42 Viola &d@ " mpn(11) = " 11 Music Box 27 Electric Guitar (jazz) &dA43 Cello &d@ " mpn(12) = " 12 Vibraphone 28 Electric Guitar (clean) &dA44 Contrabass &d@ " mpn(13) = " 13 Marimba 29 Electric Guitar (muted) 45 Tremolo Strings " mpn(14) = " 14 Xylophone 30 Overdriven Guitar &dA46 Pizzicato Strings &d@ " mpn(15) = " 15 Tubular Bells 31 Distortion Guitar &dA47 Harp &d@ " mpn(16) = " 16 Dulcimer 32 Guitar Harmonics &dI48 Timpani &d@ " mpn(17) = " " mpn(18) = " &dA49 String Ensemble 1 &d@ 65 Soprano Sax 81 Lead 1 (square) " mpn(19) = " &dA50 String Ensemble 2 &d@ 66 Alto Sax 82 Lead 2 (sawtooth) " mpn(20) = " &dA51 SynthStrings 1 &d@ 67 Teno Sax 83 Lead 3 (calliope) " mpn(21) = " &dA52 SynthStrings 2 &d@ 68 Baritone Sax 84 Lead 4 (chiff) " mpn(22) = " 53 Choir Aahs &dL69 Oboe &d@ 85 Lead 5 (charang) " mpn(23) = " 54 Voice Oohs &dL70 English Horn &d@ 86 Lead 6 (voice) " mpn(24) = " 55 Synth Voice &dL71 Bassoon &d@ 87 Lead 7 (fifths) " mpn(25) = " 56 Orchestra Hit &dL72 Clarinet &d@ 88 Lead 8 (bass + lead) " mpn(26) = " &dK57 Trumpet &d@ &dL73 Piccolo &d@ 89 Pad 1 (new age) " mpn(27) = " &dK58 Trombone &d@ &dL74 Flute &d@ 90 Pad 2 (warm) " mpn(28) = " &dK59 Tuba &d@ 75 Recorder 91 Pad 3 (polysynth) " mpn(29) = " &dK60 Muted Trumpet &d@ 76 Pan Flute 92 Pad 4 (choir) " mpn(30) = " &dK61 French Horn &d@ 77 Blown Bottle 93 Pad 5 (bowed) " mpn(31) = " 62 Brass Section 78 Shakuhachi 94 Pad 6 (metallic) " mpn(32) = " 63 SynthBrass 1 79 Whistle 95 Pad 7 (halo) " mpn(33) = " 64 SynthBrass 2 80 Ocarina 96 Pad 8 (sweep) " mpn(34) = " " mpn(35) = " 97 FX 1 (rain) 113 Tinkle Bell " mpn(36) = " 98 FX 2 (soundtrack) 114 Agogo " mpn(37) = " 99 FX 3 (crystal) 115 Steel Drums " mpn(38) = " 100 FX 4 (atmosphere) 116 Woodblock " mpn(39) = " 101 FX 5 (brightness) 117 Taiko Drum " mpn(40) = " 102 FX 6 (goblins) 118 Melodic Tom " mpn(41) = " 103 FX 7 (echoes) 119 Synth Drum " mpn(42) = " 104 FX 8 (sci-fi) 120 Reverse Cymbal " mpn(43) = " 105 Sitar 121 Guitar Fret Noise " mpn(44) = " 106 Banjo 122 Breath Noise " mpn(45) = " 107 Shamisen 123 Seashore " mpn(46) = " 108 Koto 124 Bird Tweet " mpn(47) = " 109 Kalimba 125 Telephone Ring " mpn(48) = " 110 Bagpipe 126 Helicopter " mpn(49) = " 111 Fiddle 127 Applause " mpn(50) = " 112 Shanai 128 Gunshot " &dA &dA &d@ Initialization of variables &dA convert( 1) = 10 convert( 2) = 11 convert( 3) = 12 convert( 4) = 13 convert( 5) = 14 convert( 6) = 0 convert( 7) = 12 convert( 8) = 13 convert( 9) = 14 convert(10) = 15 convert(11) = 16 convert(12) = 0 convert(13) = 14 convert(14) = 15 convert(15) = 16 convert(16) = 17 convert(17) = 18 convert(18) = 15 convert(19) = 16 convert(20) = 17 convert(21) = 18 convert(22) = 19 convert(23) = 0 convert(24) = 17 convert(25) = 18 convert(26) = 19 convert(27) = 20 convert(28) = 21 convert(29) = 0 convert(30) = 19 convert(31) = 20 convert(32) = 21 convert(33) = 22 convert(34) = 23 convert(35) = 0 convert(36) = 21 convert(37) = 22 convert(38) = 23 convert(39) = 24 convert(40) = 25 spelling( 1) = 1 spelling( 2) = 1 spelling( 3) = 2 spelling( 4) = 2 spelling( 5) = 3 spelling( 6) = 0 spelling( 7) = 1 spelling( 8) = 1 spelling( 9) = 2 spelling(10) = 3 spelling(11) = 3 spelling(12) = 0 spelling(13) = 1 spelling(14) = 2 spelling(15) = 2 spelling(16) = 3 spelling(17) = 3 spelling(18) = 1 spelling(19) = 1 spelling(20) = 2 spelling(21) = 2 spelling(22) = 3 spelling(23) = 0 spelling(24) = 1 spelling(25) = 1 spelling(26) = 2 spelling(27) = 2 spelling(28) = 3 spelling(29) = 0 spelling(30) = 1 spelling(31) = 1 spelling(32) = 2 spelling(33) = 3 spelling(34) = 3 spelling(35) = 0 spelling(36) = 1 spelling(37) = 2 spelling(38) = 2 spelling(39) = 3 spelling(40) = 3 loop for i = 1 to 127 notes_on(i) = 0 repeat loop for i = 1 to 50 bpm_data(i) = 0 bpm_data1(i) = 0 bpm_data2(i) = 0 repeat midi_open = 0 len(music) = 150000 loop for i = 1 to MEASDX measdata(i) = "" repeat mtpq = 240 /* midi tics per quarter (fixed for MIDI files) art_factor = 30 /* Articulation factor (1/f) (fixed for these files) pc_cnt = 0 tempo_cnt = 0 putc putc &dA ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» putc &dA º &d@ Program to compile MIDI0 or MIDI1 release &dA º putc &dA º &d@ files from stage2 source files &dA º putc &dA ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ &dA &dA &d@ Front End &dA putc putc To run this program, your current directory should be putc putc &dA/MUSDATA///&d@ putc putc This means that for the purpose of this task, you have already putc specified the &dAcomposer&d@, the &dAsource&d@, and the &dAwork&d@ you will be putc dealing with. getdir clib open [1,1] clib putc putc Current directory = &dA~clib k = 2 loop for i = 1 to 200 getf [1] rec if rec{1} = " " goto X1 end if rec{14,5} = "" rec = rec{1,8} rec = trm(rec) if rec = "STAGE2" --k end if rec = "DISTRIB" --k end end repeat X1: close [1] if k <> 0 putc putc Your current directory should contain at least the following putc sub-directories: putc putc DISTRIB putc STAGE2 putc putc However, a search of your current directory has failed to putc find these directories. This program is therefore unable putc to continue. Before running it again, you should check to putc see that you &dAare&d@ in the right directory, and that the sub- putc directories listed above are present. Note that the DISTRIB putc sub-directory should itself have sub-directories MIDI0 and putc MIDI1 allocated. putc putc &dAProgram Halted&d@ putc stop end slib = clib // "/" // "STAGE2" open [1,1] slib k = 0 loop getf [1] rec if rec{1} = " " goto Q0 end if rec{14,5} = "" rec = rec{1,8} ++k files(k) = trm(rec) end repeat Q0: close [1] putc putc There are ~k movement sub-directories for this work. putc Number Movement name putc ÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ loop for a = 1 to k putc .w4 ~a .t12 ~files(a) repeat Q1: putc putc Please type the &dAnumber&d@ of the movement file you wish to compile putc into a MIDI file. a = 0 getc a if a < 1 or a > k goto Q1 end slib = slib // "/" // files(a) midioutfile = files(a) if midioutfile con "." midioutfile = midioutfile{1,mpt-1} end midioutfile = midioutfile // ".MID" NXQ: putc Enter "p" to create a MIDI-PLUS representation; otherwise putc Enter "1" to create a MIDI1 representation; otherwise putc enter "0" to create a MIDI0 representation. getc rec midi_type = 1 if rec con "p" or rec con "P" midiform = 1 midi_type = 2 midioutfile = "DISTRIB/MIDIP/" // midioutfile else if rec con "1" midiform = 1 midioutfile = "DISTRIB/MIDI1/" // midioutfile else if rec con "0" midiform = 0 midioutfile = "DISTRIB/MIDI0/" // midioutfile else putc goto NXQ end end end group = "sound" groupa = "sound:" &dA &dA &d@ If midi_type = 2 (data type), we must look to see if there is &dA &d@ a data group. If so, then we should ask the user if he/she &dA &d@ wants to use this group instead of the sound group &dA if midi_type = 2 open [6,1] slib loop getf [6] rec if rec{1} = " " goto KNOW_GROUPS end rec = rec{1,12} if rec{10} <> " " rec{9} = "." end rec = trm(rec) infile = slib // "/" // rec mcount = 0 open [3,1] infile termflag = 0 perform gxrec /* activate gxrec loop for i = 1 to 11 perform gxrec repeat close [3] if rec con "data" goto GROUP_Q end repeat GROUP_Q: putc putc You have specified MIDI-PLUS representation and files belonging putc to the "data" group have been found for this movement. Enter putc a non-blank line if you want to use files from the "data" group putc instead of the "sound" group. Note: "data" group files are putc the least edited files in the set. getc line line = trm(line) if line <> "" group = "data" groupa = "data: " end KNOW_GROUPS: close [6] end putc putc Please wait while the program collects data from files belonging putc to the "~group " group . . . ... open [6,1] slib tcnt = 0 loop for i = 1 to 32 inst(i) = "" repeat trkcnt = 0 midi_trkcnt(1) = 10000 midi_trkcnt(2) = 10000 loop getf [6] rec if rec{1} = " " goto HAVE_DATA end rec = rec{1,12} if rec{10} <> " " rec{9} = "." end rec = trm(rec) infile = slib // "/" // rec mcount = 0 open [3,1] infile &dA &d@ 1 &dA &d@ 2 Typical Source File &dA &d@ 3 &dA &d@ 4 08/19/93 E. Correia &dA &d@ 5 WK#:67 MV#:1 &dA &d@ 6 Breitkopf & H\3artel, Series 1 No.5, Leipzig &dA &d@ 7 Symphony No. 5 in C Minor &dA &d@ 8 &dA &d@ 9 Flauto 1 &dA &d@ 10 0 0 . &dA &d@ 11 Group memberships: sound, parts &dA &d@ 12 sound: part 1 of 18 &dA &d@ 13 parts: part 1 of 18 &dA &d@ 14 & &dA &d@ 15 Initial conversion from stage 1 to stage 2 &dA &d@ 16 & &dA &d@ 17 $ K:-3 Q:2 T:2/4 C:4 D:Allegro con brio &dA &d@ 18 &dA &d@ 19 &dA &d@ 20 termflag = 0 perform gxrec /* activate gxrec loop for i = 1 to 9 perform gxrec repeat line = rec perform gxrec perform gxrec if rec not_con group /* "sound" or "data" if rec con "midi" goto MIDI_ASS end close [3] goto ENDL1 end eof3flag = 0 loop while rec{1,6} <> groupa /* "sound:" or "data: " perform gxrec rec = rec // pad(6) repeat eof3flag = 1 if rec con "part" mpt += 5 line2 = txt(rec,[' ']) /* i i = int(line2) line2 = txt(rec,[' ']) /* of line2 = txt(rec,[' ']) /* j j = int(line2) end if trkcnt = 0 trkcnt = j else if trkcnt <> j putc putc putc There is disagreement among the files belonging to the putc "~group " group for this movement as to the number of putc total files in the set. The first member of the "~group " putc group indicated a total of &dA~trkcnt &d@ files, whereas putc this member of the group "~infile " indicates a total of putc of &dA~j &d@ files in the set. This discrepency must be fixed putc before this program can continue. putc putc &dAProgram Halted&d@ putc stop end end crnttrk = i if inst(i) <> "" putc putc putc Two files having membership in the "~group " group claim to putc be the ~i "th" member of the group. The first is the file putc for ~inst(i) , and the second is the file for ~line . This putc discrepency must be fixed before this program can continue. putc putc &dAProgram Halted&d@ putc stop end inst(i) = line &dA &dA &d@ "T" = Track Record (part i of j) &dA trackloc(i) = tcnt ++tcnt tput [X,tcnt] T:~i ,~j loop for i = 1 to 20 ++tcnt tput [X,tcnt] dummy repeat datapoint(crnttrk) = tcnt + 1 perform gxrec rec = rec // pad(1) loop while rec{1} <> "$" perform gxrec rec = rec // pad(1) repeat rec = trm(rec) rec = rec // pad(9) fineflag = 0 zcnt = 0 &dA &dA &d@ Now start storing records in case of repeats &dA treset [Y] ycnt = 1 rep_mark = 0 seg_mark = 0 tput [Y,ycnt] ~rec &dA &dA &d@ First $ sign "Q" = Divisions per quarter Record &dA if rec con "Q:" i = int(rec{mpt+2..}) ++zcnt templines(zcnt) = "$ Q:" // chs(i) loc_divspq = i nq_rec = zcnt end if rec con "T:" tnum = int(rec{sub+2..}) tden = int(rec{sub+1..}) if tnum = 1 and tden = 1 tnum = 4 tden = 4 end if tden = 0 if tnum = 0 tnum = 2 tden = 2 else tden = 1 end end ++zcnt templines(zcnt) = "$ T:" // chs(tnum) // "/" // chs(tden) // " " end if rec con "X:" i = int(rec{sub+2..}) ++zcnt templines(zcnt) = "$ X:" // chs(i) end &dA &dA &d@ This may seem quirky, but we need to begin this bizarre process &dA &d@ by looking for grace notes at the beginning of the piece! If &dA &d@ we find them, we need to "create" some time at the beginning of &dA &d@ the piece to accomodate them &dA LOOP0: perform gxrec rec = rec // pad(17) if rec{1} = "&" loop perform gxrec rec = rec // pad(1) repeat while rec{1} <> "&" goto LOOP0 end if rec{1,2} = "tt" and rec{14} = "9" k = int(rec{3}) loop for i = 1 to k perform gxrec repeat goto LOOP0 end if rec{1} = "t" and rec{14} = "9" rec = rec{2,15} // " " // rec{17..} end ++ycnt tput [Y,ycnt] ~rec &dA &dA &d@ Convert tempo changes to "$" type records &dA if rec{1} = "S" if rec con "C0:W" i = int(rec{mpt+4..}) rec = "$ W:" // chs(i) // " " end end &dA &dA &d@ Convert dynamic changes to "$" type records &dA if rec{1} = "S" if rec con "C0:V" i = int(rec{mpt+4..}) rec = "$ V:" // chs(i) // " " end end if rec{1} = "$" if rec con "Q:" i = int(rec{mpt+2..}) ++zcnt templines(zcnt) = "$ Q:" // chs(i) loc_divspq = i nq_rec = zcnt end if rec con "T:" tnum = int(rec{sub+2..}) tden = int(rec{sub+1..}) if tnum = 1 and tden = 1 tnum = 4 tden = 4 end if tden = 0 if tnum = 0 tnum = 2 tden = 2 else tden = 1 end end ++zcnt templines(zcnt) = "$ T:" // chs(tnum) // "/" // chs(tden) // " " end if rec con "W:" i = int(rec{sub+2..}) ++zcnt templines(zcnt) = "$ W:" // chs(i) // " " end if rec con "V:" i = int(rec{sub+2..}) ++zcnt templines(zcnt) = "$ V:" // chs(i) // " " end if rec con "X:" i = int(rec{sub+2..}) ++zcnt templines(zcnt) = "$ X:" // chs(i) end end if " ABCDEFG/bgimrS" con rec{1} rec = trm(rec) ++zcnt templines(zcnt) = rec end if rec{1} = "S" if rec con "C0:S" seg_mark = ycnt end end if rec{1} <> "m" goto LOOP0 end msize = zcnt &dK &d@ putc ~rec &dA &dA &d@ Check to see if this measure "starts" with stealing grace notes &dA tdivpoint = 1 grace_count = 0 k = 0 h = 0 loop for zcnt = 1 to msize if templines(zcnt){1} = "g" and templines(zcnt){2} <> " " if tdivpoint = 1 rec2 = templines(zcnt+1) if rec2{1} = "S" and rec2 con "C1:" scode = txt(rec2,[32]) if scode con "p" ++grace_count grace_pitch(grace_count) = zcnt if scode con "t" scode = scode // pad(1) grace_percent(grace_count) = int(scode{mpt+1..}) else grace_percent(grace_count) = 0 end j = 0 loop for i = zcnt+1 to msize if "ABCDEFG" con templines(i){1} j = int(templines(i){6..}) i = msize end repeat grace_percent(grace_count) *= j if k = 0 ++h end k = 1 grace_pass(grace_count) = h end end end else if "irABCDEFG" con templines(zcnt){1} j = int(templines(zcnt){6..}) k = 0 tdivpoint += j else if templines(zcnt){1} = "b" j = int(templines(zcnt){6..}) tdivpoint -= j end end end repeat if grace_count > 0 h = 1000000 loop for i = 1 to grace_count j = grace_percent(i) if j > 0 and j < h h = j end repeat if h = 1000000 putc Normally, this code should not be executing loop for i = 1 to grace_count e = grace_pitch(i) templines(e) = "d" /* delete old grace note ++e rec2 = templines(e) if rec2{1} = "S" perform remove_grace_data if rec2 = "S" templines(e) = "d" /* delete S record else templines(e) = rec2 end ++e end HJHL: if templines(e){1,2} = "g " templines(e) = "d" /* delete chord tone ++e rec2 = templines(e) if rec2{1} = "S" perform remove_grace_data if rec2 = "S" templines(e) = "d" /* delete S record else templines(e) = rec2 end ++e end goto HJHL end repeat goto GRCM end if h > 75 k = 4 else if h > 37 k = 8 else if h > 18 k = 16 else k = 32 end end end zcnt = msize /* temporary storage a = loc_divspq * k ++zcnt templines(zcnt) = "$ Q:" // chs(a) // " " b = 0 c = 0 d = 0 loop for i = 1 to grace_count a = grace_percent(i) a *= k a += 50 a /= 100 if a = 0 a = 1 /* a = duration end grace_percent(i) = a if grace_pass(i) = b c += a else b = grace_pass(i) c = a end if d < c d = c /* max backup end repeat rec = "rest " perform add_dur (d) ++zcnt templines(zcnt) = rec /* original "rest" b = 1 c = 0 d = 1 loop for i = 1 to grace_count a = grace_percent(i) if grace_pass(i) = b c += a else rec = "back " perform add_dur (c) ++zcnt templines(zcnt) = rec /* backup command loop for j = d to i - 1 e = grace_pitch(j) rec = templines(e){2,5} a = grace_percent(j) perform add_dur (a) ++zcnt templines(zcnt) = rec /* "new note" from grace note templines(e) = "d" /* delete old grace note ++e rec2 = templines(e) if rec2{1} = "S" perform remove_grace_data templines(e) = "d" /* delete S record in original location if len(rec2) > 1 ++zcnt templines(zcnt) = rec2 end ++e end &dK &d@ if templines(e){1} = "S" &dK &d@ templines(e) = "d" /* delete S record (oh hell! why not?) &dK &d@ ++e &dK &d@ end HJHJ: if templines(e){1,2} = "g " rec{1} = " " rec{2,4} = templines(e){3,4} ++zcnt templines(zcnt) = rec /* "new chord tone" from grace note templines(e) = "d" /* delete chord tone ++e rec2 = templines(e) if rec2{1} = "S" perform remove_grace_data templines(e) = "d" /* delete S record in original location if len(rec2) > 1 ++zcnt templines(zcnt) = rec2 end ++e end &dK &d@ if templines(e){1} = "S" &dK &d@ templines(e) = "d" /* delete S record &dK &d@ ++e &dK &d@ end goto HJHJ end repeat d = i c = 0 ++b end repeat a = grace_percent(grace_count) c += a rec = "back " perform add_dur (c) ++zcnt templines(zcnt) = rec /* backup command loop for j = d to grace_count e = grace_pitch(j) rec = templines(e){2,5} a = grace_percent(j) perform add_dur (a) ++zcnt templines(zcnt) = rec /* "new note" from grace note templines(e) = "d" /* delete old grace note ++e rec2 = templines(e) if rec2{1} = "S" perform remove_grace_data templines(e) = "d" /* delete S record in original location if len(rec2) > 1 ++zcnt templines(zcnt) = rec2 end ++e end &dK &d@ if templines(e){1} = "S" &dK &d@ templines(e) = "d" /* delete S record &dK &d@ ++e &dK &d@ end HJHK: if templines(e){1,2} = "g " rec{1,5} = templines(e){2,5} ++zcnt templines(zcnt) = rec /* "new chord tone" from grace note templines(e) = "d" /* delete chord tone ++e rec2 = templines(e) if rec2{1} = "S" perform remove_grace_data templines(e) = "d" /* delete S record in original location if len(rec2) > 1 ++zcnt templines(zcnt) = rec2 end ++e end &dK &d@ if templines(e){1} = "S" &dK &d@ templines(e) = "d" /* delete S record &dK &d@ ++e &dK &d@ end goto HJHK end repeat ++zcnt templines(zcnt) = "measure 0" /* set off pre-time from rest of piece k = zcnt - msize if k > 0 datapoint(crnttrk) -= k j = datapoint(crnttrk) loop for k = msize + 1 to zcnt tput [X,j] ~templines(k) ++j templines(k) = "" /* clear temporary storage repeat end end GRCM: &dA &dA &d@ This is the end of the "quirky and bizarre process of looking for &dA &d@ grace notes at the beginning of the piece! If they are there, &dA &d@ we have created some time at the beginning of the piece to &dA &d@ accomodate them. &dA &dA &d@ At this point, we have a somewhat "raw" measure in the first &dA &d@ msize lines of templines. We need to add a second "raw" measure, &dA &d@ if there is one. Then we will process the first measure (including &dA &d@ looking for grace notes at the beginning of the second measure). &dA &d@ Once this is done, we can move the second measure to the position &dA &d@ of the first measure. &dA q_rec = nq_rec nloc_divspq = loc_divspq take = 1 BLOOP1: zcnt = msize ++zcnt templines(zcnt) = "dummy" /* in case we have to change divspq nq_rec = zcnt loc_divspq = nloc_divspq if take = 1 tget [Y,ycnt] rec else tget [Y,rcnt] rec end &dK &d@ putc At this point in the process, the last record in &dK &d@ putc Y is ~rec &dK &d@ getc if take = 2 if rec con "start-end1" or rec con ":|" take = 1 end else if take = 1 if rec con ":|" take = 2 rcnt = rep_mark end if rec con "|:" rep_mark = ycnt end else if take = 3 if rec con "start-end1" /* skip the first ending loop for i = rcnt to max_ycnt tget [Y,i] rec repeat while rec not_con ":|" and rec not_con "start-end2" rcnt = i end end end end LOOP1: if take = 1 perform gxrec if rec{1} = "S" and rec con "C0:|>" loop for i = ycnt to 1 step -1 tget [Y,i] trec if trec{1} = "m" rep_mark = i goto LOOP1 end repeat end else ++rcnt tget [Y,rcnt] rec end &dK &d@ dputc rec = ~rec /* &dA rec = rec // pad(17) if rec{1} = "S" and rec con "C0:" if rec{mpt+3} = "d" and take <> 3 /* from "getf" i.e. take = 1 take = 3 rcnt = seg_mark tget [Y,rcnt+1] rec2 if rec2{1} = "S" and rec con "C0:" k = int(rec{mpt+4..}) rec2 = "$ Q:" // chs(k) tput [Y,rcnt+1] ~rec2 end perform gxrec if rec{1,4} <> "/END" and rec{1,4} <> "/FIN" tget [Y,ycnt] rec2 if rec2{1} <> "m" putc Abnormal placement of "da capo" sound record; please putc check file ~infile . putc putc &dAProgram Halted&d@ putc stop end end ++ycnt tput [Y,ycnt] ~rec loop perform gxrec ++ycnt tput [Y,ycnt] ~rec rec = rec // pad(4) repeat while rec{1,4} <> "/END" and rec{1,4} <> "/FIN" max_ycnt = ycnt goto LOOP1 end end if rec{1,4} = "/END" or rec{1,4} = "/FIN" fsize = msize if take = 1 ++ycnt tput [Y,ycnt] ~rec max_ycnt = ycnt end goto ELOOP1 end if rec{1} = "&" loop perform gxrec rec = rec // pad(1) repeat while rec{1} <> "&" goto LOOP1 end if rec{1,2} = "tt" and "90" con rec{14} k = int(rec{3}) loop for i = 1 to k perform gxrec repeat goto LOOP1 end if rec{1} = "t" and "90" con rec{14} rec = rec{2,15} // " " // rec{17..} end if take = 1 ++ycnt tput [Y,ycnt] ~rec end &dA &dA &d@ Convert tempo changes to "$" type records &dA if rec{1} = "S" if rec con "C0:W" i = int(rec{mpt+4..}) rec = "$ W:" // chs(i) // " " end end &dA &dA &d@ Convert dynamic changes to "$" type records &dA if rec{1} = "S" if rec con "C0:V" i = int(rec{mpt+4..}) rec = "$ V:" // chs(i) // " " end end if rec{1} = "$" if rec con "Q:" i = int(rec{mpt+2..}) templines(nq_rec) = "$ Q:" // chs(i) nloc_divspq = i end if rec con "T:" i = int(rec{sub+2..}) j = int(rec{sub+1..}) if i = 1 and j = 1 i = 4 j = 4 end if j = 0 if i = 0 i = 2 j = 2 else j = 1 end end ++zcnt templines(zcnt) = "$ T:" // chs(i) // "/" // chs(j) // " " end if rec con "W:" i = int(rec{sub+2..}) ++zcnt templines(zcnt) = "$ W:" // chs(i) // " " end if rec con "V:" i = int(rec{sub+2..}) ++zcnt templines(zcnt) = "$ V:" // chs(i) // " " end if rec con "X:" i = int(rec{sub+2..}) ++zcnt templines(zcnt) = "$ X:" // chs(i) end end if take = 3 if rec{1} = "S" and rec con "C8:F" rec = rec // " " j = int(rec{mpt+4..}) if j > 0 loop for i = zcnt to 1 step -1 templines(i) = templines(i) // " " if templines(i){1} <> "S" k = int(templines(i){6..}) /* &dK PROBLEM ? &d@ k -= j /* k is neg if j is bigger rec = templines(i){1,5} perform add_dur (j) rec = rec // templines(i){9..} templines(i) = trm(rec) end if "irABCDEFG" con templines(i){1} i = 0 end repeat else k = 0 end loop ++rcnt tget [Y,rcnt] rec if "irABCDEFG" con rec{1} j = int(rec{6..}) k += j end if "mb" con rec{1} if rec{1} = "m" tput [Y,rcnt] mheavy2 tput [Y,rcnt+1] /END fineflag = 1 else j = int(rec{6..}) j -= k tput [Y,rcnt] back.w4 ~j end --rcnt goto LOOP1 else tput [Y,rcnt] d end repeat end end if " ABCDEFG/bgimrS" con rec{1} rec = trm(rec) ++zcnt templines(zcnt) = rec end if rec{1} = "S" if rec con "C0:S" seg_mark = ycnt end end if rec{1} <> "m" goto LOOP1 end fsize = zcnt ELOOP1: &dK &d@ putc ~rec &dA &dA &d@ Now we must look for renegade grace notes! (everywhere except &dA &d@ at the beginning of the measure, since this was already done.) &dA &dA &dA &d@ First see what there is, and what the size is &dA tdivpoint = 1 grace_count = 0 loop for zcnt = 1 to msize if templines(zcnt){1} = "g" and templines(zcnt){2} <> " " if tdivpoint <> 1 rec2 = templines(zcnt+1) if rec2{1} = "S" and rec2 con "C1:" scode = rec2{mpt+3..} // " " if scode con " " scode = scode{1,mpt} end if scode con "p" ++grace_count grace_pitch(grace_count) = zcnt if scode con "t" grace_percent(grace_count) = int(scode{mpt+1..}) else grace_percent(grace_count) = 0 end j = 0 loop for i = zcnt+1 to msize if "ABCDEFG" con templines(i){1} j = int(templines(i){6..}) i = msize end repeat grace_percent(grace_count) *= j end end end else if "irABCDEFG" con templines(zcnt){1} j = int(templines(zcnt){6..}) tdivpoint += j else if templines(zcnt){1} = "b" j = int(templines(zcnt){6..}) tdivpoint -= j end end end repeat &dA &dA &d@ Look also at the beginning of 2nd measure &dA tdivpoint = 1 loop for zcnt = msize + 1 to fsize if templines(zcnt){1} = "g" and templines(zcnt){2} <> " " if tdivpoint = 1 rec2 = templines(zcnt+1) if rec2{1} = "S" and rec2 con "C1:" scode = rec2{mpt+3..} // " " if scode con " " scode = scode{1,mpt} end if scode con "p" ++grace_count grace_pitch(grace_count) = zcnt if scode con "t" grace_percent(grace_count) = int(scode{mpt+1..}) else grace_percent(grace_count) = 0 end j = 0 loop for i = zcnt+1 to fsize if "ABCDEFG" con templines(i){1} j = int(templines(i){6..}) i = fsize end repeat grace_percent(grace_count) *= j end end end else if "irABCDEFG" con templines(zcnt){1} j = int(templines(zcnt){6..}) k = 0 tdivpoint += j else if templines(zcnt){1} = "b" j = int(templines(zcnt){6..}) tdivpoint -= j end end end repeat &dA &dA &d@ If "time stealing" grace notes were found ... &dA if grace_count > 0 h = 1000000 loop for i = 1 to grace_count j = grace_percent(i) if j > 0 and j < h h = j end repeat if h = 1000000 putc this code should not be executing! Type return to continue. getc loop for i = 1 to grace_count e = grace_pitch(i) templines(e) = "d" /* delete old grace note ++e if templines(e){1} = "S" templines(e) = "d" /* delete S record ++e end HJHL1: if templines(e){1,2} = "g " templines(e) = "d" /* delete chord tone ++e if templines(e){1} = "S" templines(e) = "d" /* delete S record ++e end goto HJHL1 end repeat goto GRCM1 end if h > 75 fac = 4 else if h > 37 fac = 8 else if h > 18 fac = 16 else fac = 32 end end end a = loc_divspq * fac templines(q_rec) = "$ Q:" // chs(a) // " " templines(nq_rec) = "$ Q:" // chs(nloc_divspq) // " " loop for i = 1 to grace_count a = grace_percent(i) a *= fac a += 50 a /= 100 if a = 0 a = 1 /* a = duration end grace_percent(i) = a repeat &dA &dA &d@ Now change all durations and fix grace notes &dA tdivpoint = 1 k = 0 pp = 1 loop for zcnt = 1 to msize rec = templines(zcnt) if rec{1} = "g" if rec{2} <> " " if tdivpoint <> 1 rec2 = templines(zcnt+1) if rec2{1} = "S" and rec2 con "C1:" scode = rec2{mpt+3..} // " " if scode con " " scode = scode{1,mpt} end if scode con "p" ++k a = grace_percent(k) /* new duration rec = rec{2,5} perform add_dur (a) if len(templines(zcnt)) > 8 rec = rec // templines(zcnt){9..} end templines(zcnt) = rec /* replace "fixed" grace note ++zcnt rec2 = templines(zcnt) if rec2{1} = "S" perform remove_grace_data if rec2 = "S" templines(zcnt) = "d" /* delete S record else templines(zcnt) = rec2 end ++zcnt end &dK &d@ if templines(zcnt){1} = "S" &dK &d@ templines(zcnt) = "d" &dK &d@ ++zcnt &dK &d@ end YUY: if templines(zcnt){1,2} = "g " templines(zcnt) = templines(zcnt){2,5} // rec{6..} /* &dI O K ++zcnt rec2 = templines(zcnt) if rec2{1} = "S" perform remove_grace_data if rec2 = "S" templines(zcnt) = "d" /* delete S record else templines(zcnt) = rec2 end ++zcnt end &dK &d@ if templines(zcnt){1} = "S" &dK &d@ templines(zcnt) = "d" &dK &d@ ++zcnt &dK &d@ end goto YUY else --zcnt end &dA &dA &d@ Here is where you "steal" time &dA rec = templines(olddiv) j = int(rec{6..}) j -= a if j < 0 j = 0 end rec = rec{1,5} perform add_dur (j) if len(templines(olddiv)) > 8 rec = rec{1,8} // templines(olddiv){9..} end templines(olddiv) = rec /* replace "modified" victim &dA &dA &d@ You haven't fixed durations in chords. Is this a problem? &dA if templines(olddiv+1){1} = " " loop for c = 1 to 10 if templines(olddiv+c){1} = " " rec = templines(olddiv+c) j = int(rec{6..}) j -= a if j < 0 j = 0 end rec = rec{1,5} perform add_dur (j) if len(templines(olddiv+c)) > 8 rec = rec{1,8} // templines(olddiv+c){9..} end templines(olddiv+c) = rec /* replace "modified" victim else c = 10 end repeat end end end end end else if "irABCDEFG " con rec{1} j = int(rec{6..}) j *= fac rec = rec{1,5} perform add_dur (j) if len(templines(zcnt)) > 8 rec = rec{1,8} // templines(zcnt){9..} end templines(zcnt) = rec /* replace modified record if rec{1} <> " " olddiv = zcnt tdivpoint += j end grace_pitch(pp) = zcnt /* index to last note in pass (eventually) else if rec{1} = "b" j = int(rec{6..}) j *= fac rec = rec{1,5} perform add_dur (j) if len(templines(zcnt)) > 8 rec = rec{1,8} // templines(zcnt){9..} end templines(zcnt) = rec /* replace modified record tdivpoint -= j grace_pass(pp) = zcnt /* index to backspace (new location for grace) ++pp /* new pass else if rec{1} = "S" and rec con "C1:" and rec con "m" and rec con "t" rec = rec // " " b = sub j = int(rec{sub+1..}) j *= fac rec = rec{1,b} // chs(j) // rec{sub..} templines(zcnt) = trm(rec) end end end end repeat grace_pass(pp) = zcnt /* index to "measure" record (new location for g) max_pp = pp &dA &dA &d@ Now comes the trickiest part. We need to look again at the grace &dA &d@ notes that are at the beginning of the next measure. For each &dA &d@ pass (assuming there are the same number in each measure, which may &dA &d@ not be the case), we need to remove the grace note from its current &dA &d@ location and put it after the last note (chord) in the previous &dA &d@ measure for that pass. &dA if k < grace_count pp = 1 tdivpoint = 1 loop for zcnt = msize + 1 to fsize rec = templines(zcnt) if rec{1} = "g" and rec{2} <> " " if tdivpoint = 1 rec2 = templines(zcnt+1) if rec2{1} = "S" and rec2 con "C1:" scode = rec2{mpt+3..} // " " if scode con " " scode = scode{1,mpt} end if scode con "p" ++k a = grace_percent(k) /* new duration rec = rec{2,5} perform add_dur (a) if len(templines(zcnt)) > 8 rec = rec // templines(zcnt){9..} end templines(zcnt) = rec /* replace "fixed" grace note out = rec b = grace_pitch(pp) /* index to note from which to steal rec = templines(b) c = int(rec{6..}) /* &dI O K c -= a if c < 0 c = 0 end rec = rec{1,5} perform add_dur (c) if len(templines(b)) > 8 rec = rec{1,8} // templines(b){9..} end templines(b) = rec /* replace "modified" victim &dA &dA &d@ You haven't fixed durations in chords. Is this a problem? &dA if templines(b+1){1} = " " loop for cc = 1 to 10 if templines(b+cc){1} = " " rec = templines(b+cc) c = int(rec{6..}) c -= a if c < 0 c = 0 end rec = rec{1,5} perform add_dur (c) if len(templines(b+cc)) > 8 rec = rec{1,8} // templines(b+cc){9..} end templines(b+cc) = rec /* replace "modified" victim else cc = 10 end repeat end &dA &dA &d@ Now we must move records &dA d = grace_pass(pp) /* index to "new" location for grace note loop for i = zcnt to d step -1 templines(i+1) = templines(i) /* templines(zcnt+1) has been saved as rec2 repeat templines(d) = templines(zcnt+1) ++grace_pass(pp) ++msize ++nq_rec loop for i = pp+1 to max_pp ++grace_pass(i) ++grace_pitch(i) repeat &dA &dA &d@ Check to see if "sound" record also needs to be moved &dA /* rec2 = templines(zcnt+1) already. if rec2{1} = "S" perform remove_grace_data if rec2 = "S" templines(zcnt+1) = "d" /* delete S record else loop for i = zcnt to d+1 step -1 templines(i+1) = templines(i) repeat templines(d+1) = rec2 ++grace_pass(pp) ++msize ++nq_rec loop for i = pp+1 to max_pp ++grace_pass(i) ++grace_pitch(i) repeat end end &dK &d@ templines(zcnt+1) = "d" zcnt += 2 &dA &dA &d@ Now check for chord-grace notes &dA YUYU: if templines(zcnt){1,2} = "g " templines(zcnt) = templines(zcnt){2,5} // out{6..} /* &dKPROBLEM ? rec = templines(zcnt) d = grace_pass(pp) /* index loop for i = zcnt - 1 to d step -1 templines(i+1) = templines(i) repeat templines(d) = rec ++msize ++nq_rec ++grace_pass(pp) loop for i = pp+1 to max_pp ++grace_pass(i) ++grace_pitch(i) repeat ++zcnt &dA &dA &d@ Check to see if "sound" record also needs to be moved &dA rec2 = templines(zcnt) if rec2{1} = "S" perform remove_grace_data if rec2 = "S" templines(zcnt) = "d" /* delete S record else loop for i = zcnt-1 to d+1 step -1 templines(i+1) = templines(i) repeat templines(d+1) = rec2 ++grace_pass(pp) ++msize ++nq_rec loop for i = pp+1 to max_pp ++grace_pass(i) ++grace_pitch(i) repeat end ++zcnt end &dK &d@ if templines(zcnt){1} = "S" &dK &d@ templines(zcnt) = "d" &dK &d@ ++zcnt &dK &d@ end goto YUYU else --zcnt end end end end else if "irABCDEFG" con templines(zcnt){1} j = int(templines(zcnt){6..}) tdivpoint += j else if templines(zcnt){1} = "b" j = int(templines(zcnt){6..}) tdivpoint -= j ++pp end end end repeat end &dA &dA &dA &d@ I sincerely hope you are now done with the problem of "time stealing" &dA &d@ grace notes! &dA &dA end &dKDEBUG&d@ &dK &d@ putc &dK &d@ putc Two measures as a group, after moving leading grace notes &dK &d@ loop for i = 1 to fsize &dK &d@ putc ~templines(i) &dK &d@ repeat &dK &d@ getc &dKEND DEBUG&d@ &dA &dA &d@ Now it is time to store the first measure &dA loop for i = 1 to msize - 1 ++tcnt tput [X,tcnt] ~templines(i) repeat if fsize > msize rec2 = templines(fsize) // " " if len(rec2) < 8 putc putc Possible format error in source file: entering examine mode putc examine stop end j = int(rec2{8..}) --j if j < 0 rec2 = templines(msize) // " " j = int(rec2{8..}) end ++tcnt tput [X,tcnt] measure ~j else ++tcnt tput [X,tcnt] ~templines(msize) end &dA &dA &d@ Now it is time to move the second measure to the first measure &dA j = 0 if fsize > msize loop for i = msize + 1 to fsize ++j templines(j) = templines(i) repeat q_rec = nq_rec - msize msize = j goto BLOOP1 else ++tcnt tput [X,tcnt] /END &dK &d@ putc &dK &d@ putc &dAEnd of file&d@ close [3] end goto ENDL1 MIDI_ASS: eof3flag = 2 &dA &dA &d@ Get midi assignments and channel assignments &dA &d@ loop for i = 1 to 32 midiass(i) = 0 repeat loop for i = 1 to 16 chanst(i,1) = 0 chanst(i,2) = 0 repeat &dA &d@ 1 &dA &d@ 2 Format for the midi assignment file &dA &d@ 3 &dA &d@ 4 08/19/93 E. Correia &dA &d@ 5 WK#:67 MV#:1 &dA &d@ 6 Breitkopf & H\3artel, Series 1 No.5, Leipzig &dA &d@ 7 Symphony No. 5 in C Minor &dA &d@ 8 &dA &d@ 9 Midi assignment &dA &d@ 10 &dA &d@ 11 Group memberships: midi &dA &d@ 12 midi: part 0 of 18 or part 1 of 18 &dA &d@ 13 & &dA &d@ 14 Comments here &dA &d@ 15 & &dA &d@ 16 part 1 = channel 1 &dA &d@ 17 part 2 = channel 1 &dA &d@ 18 part 3 = channel 2 &dA &d@ 19 part 4 = channel 2 &dA &d@ 20 part 5 = channel 3 &dA &d@ 21 part 6 = channel 3 &dA &d@ 22 part 7 = channel 4 &dA &d@ 23 part 8 = channel 4 &dA &d@ 24 part 9 = channel 5 &dA &d@ 25 part 10 = channel 6 &dA &d@ 26 part 11 = channel 7 &dA &d@ 27 part 12 = channel 8 &dA &d@ 28 part 13 = channel 9 &dA &d@ 29 part 14 = channel 10 &dA &d@ 30 part 15 = channel 11 &dA &d@ 31 part 16 = channel 12 &dA &d@ 32 part 17 = channel 13 &dA &d@ 33 part 18 = channel 14 &dA &d@ 34 channel 1: instrument = xx, yy &dA &d@ 35 channel 2: instrument = xx, yy &dA &d@ 36 channel 3: instrument = xx, yy &dA &d@ 37 channel 4: instrument = xx, yy &dA &d@ 38 channel 5: instrument = xx, yy &dA &d@ 39 channel 6: instrument = xx, yy &dA &d@ 40 channel 7: instrument = xx, yy &dA &d@ 41 channel 8: instrument = xx, yy &dA &d@ 42 channel 9: instrument = xx, yy &dA &d@ 43 channel 10: instrument = xx, yy &dA &d@ 44 channel 11: instrument = xx, yy &dA &d@ 45 channel 12: instrument = xx, yy &dA &d@ 46 channel 13: instrument = xx, yy &dA &d@ 47 channel 14: instrument = xx, yy &dA &d@ 48 /END loop while rec{1,5} <> "midi:" perform gxrec rec = rec // pad(5) repeat if rec con "part" mpt += 5 line = txt(rec,[' ']) /* i i = int(line) line = txt(rec,[' ']) /* of line = txt(rec,[' ']) /* j j = int(line) end if midi_type <> i + 1 goto ENDL1AA end &dA &d@ midi_type = i + 1 midi_trkcnt(midi_type) = j loop while rec{1,4} <> "part" perform gxrec rec = rec // pad(50) repeat j = 0 NXMP: p = int(rec{6..}) /* &dI O K q = int(rec{19..}) midiass(p) = q if midi_type = 1 midiass1(p) = q /* channel number else midiass2(p) = q /* channel number end ++j perform gxrec rec = rec // pad(50) if rec{1,4} = "/END" goto feof3 end if rec{1,4} = "part" goto NXMP else if j <> trkcnt goto feof3 end end NXMC: if rec{1,7} <> "channel" goto CCHK end q = int(rec{8..}) r = int(rec{25..}) ++sub s = r if sub <= len(rec) s = int(rec{sub..}) end chanst(q,1) = r /* primary instrument chanst(q,2) = s /* secondary instrument if midi_type = 1 chanst1(q,1) = r /* primary instrument chanst1(q,2) = s /* secondary instrument else chanst2(q,1) = r /* primary instrument chanst2(q,2) = s /* secondary instrument end perform gxrec rec = rec // pad(50) goto NXMC CCHK: loop for i = 1 to p q = midiass(i) /* channel number if chanst(q,1) = 0 putc No instrument assigned to channel ~q in the midi file putc goto feof3 end repeat bpm = int(rec) &dA &dA &d@ Restrike added after a certain number of MCHAN files were created &dA if rec con "restrike" if midi_type = 1 restrike_factor1 = bpm else restrike_factor2 = bpm end perform gxrec rec = rec // pad(50) bpm = int(rec) end if midi_type = 1 bpm_cnt1 = 1 bpm_data1(1) = bpm loop perform gxrec rec = rec // pad(50) if rec{1,4} = "/END" goto ENDL1AA end ++bpm_cnt1 bpm_data1(bpm_cnt1) = int(rec) repeat else bpm_cnt2 = 1 bpm_data2(1) = bpm loop perform gxrec rec = rec // pad(50) if rec{1,4} = "/END" goto ENDL1AA end ++bpm_cnt2 bpm_data2(bpm_cnt2) = int(rec) repeat end ENDL1AA: close [3] goto ENDL1 ENDL1: repeat HAVE_DATA: &dKDEBUG&d@ &dK &d@ putc &dK &d@ putc Data which is now fed to the body of the program &dK &d@ putc &dK &d@ loop for i = 1 to tcnt &dK &d@ tget [X,i] rec &dK &d@ putc ~rec &dK &d@ repeat &dK &d@ getc &dK &d@ stop &dKEND DEBUG&d@ treset [Y] tcnt = 0 xcnt = trackloc(1) /* location of start of next track loop for trcnt = 1 to trkcnt inst_flag = 1 ++xcnt tget [X,xcnt] rec rec = rec // pad(9) if rec{1} = "T" i = int(rec{3..}) j = int(rec{sub+1..}) if j <> trkcnt putc putc Program error: shoot the programmer! putc j = ~j trkcnt = ~trkcnt stop end if xcnt - 1 <> trackloc(i) putc putc Program error: shoot the programmer! putc xcnt - 1 = ~(xcnt-1) trackloc(~i ) = ~trackloc(i) stop end end loop ++xcnt tget [X,xcnt] rec repeat while rec{1} = "d" datapoint(trcnt) = tcnt + 1 --xcnt divcount = 1 transpose = 0 addoctave = 0 LOOP2: ++xcnt tget [X,xcnt] rec .t6 c .t6 d rec = rec // pad(9) if rec{1} = "$" &dA &dA &d@ "Q" = Divisions per quarter Record &dA if rec con "Q:" ++tcnt tput [Y,tcnt] ~rec{mpt..} goto LOOP2 end &dA &dA &d@ "T" = Time record &dA if rec con "T:" rec = rec{mpt..} rec = trm(rec) measdata(divcount) = measdata(divcount) // rec // ";" goto LOOP2 end &dA &dA &d@ "V" = Dynamic change record &dA if rec con "V:" rec = rec{mpt..} rec = trm(rec) measdata(divcount) = measdata(divcount) // rec // ";" goto LOOP2 end &dA &dA &d@ "W" = Tempo change record &dA if rec con "W:" rec = rec{mpt..} rec = trm(rec) measdata(divcount) = measdata(divcount) // rec // ";" goto LOOP2 end &dA &dA &d@ X = get transpose factor for this track &dA if rec con "X:" i = int(rec{mpt+2..}) if i > 900 addoctave = 1 i -= 1000 end transpose = i goto LOOP2 end goto LOOP2 end if rec{1,4} = "/END" or rec{1,4} = "/FIN" ++tcnt tput [Y,tcnt] E xcnt = trackloc(trcnt+1) /* location of start of next track goto ENDL2 end rec = trm(rec) if rec{1} = "m" perform putmar --divcount out = "M" // chs(divcount) // "\" if len(rec) > 8 out = out // rec{9..} end &dA &dA &d@ "M" = Measure record &dA ++tcnt tput [Y,tcnt] ~out ++mcount if mcount > 0 divcount = 1 end goto LOOP2 end rec = rec // pad(9) if rec{1} = "d" goto LOOP2 end if "ri" con rec{1} divcount += c goto LOOP2 end if "b" con rec{1} divcount -= c goto LOOP2 end if "CDEFGAB" con rec{1} pitch = rec{1,4} perform decode (i) j = c oldc = c if rec{9} = "-" j += 0x8000 /* tie flag end &dA &dA &d@ "N" or "P" = note record &dA &dA &d@ Check for alternate instrument &dA tget [X,xcnt+1] rec2 rec2 = rec2 // " " if rec2{1} = "S" and rec2 con "C2:" if rec2{mpt+3} = "A" inst_flag = 2 let = "P" else if rec2{mpt+3} = "b" inst_flag = 1 let = "N" else if rec2{mpt+3} = "a" let = "P" end end end else if inst_flag = 1 let = "N" else let = "P" end end orn(15) = let measdata(divcount) = measdata(divcount) // let // chs(i) // "," // chs(j) // ";" lastdur = c divcount += c rec = rec // pad(44) ii = i if rec{32..43} con ['t','r','w','~','c','M','j'] loop for i = 32 to 43 &dA &dA &d@ For "pure" version, skip editorial ornaments &dA if rec{i} = "&" and rec{i+1} = "X" and midi_type = 2 orn(i-31) = "" orn(i-30) = "" orn(i-29) = "" i += 2 else if "trw~cMj" con rec{i} /* current ornaments orn(i-31) = "trw~cmj"{mpt} else orn(i-31) = "" end end repeat end if addoctave = 1 k = divcount - lastdur measdata(k) = measdata(k) // let // chs(ii-40) // "," // chs(j) // ";" end goto LOOP2 end if rec{1} = " " /* chord tone if "CDEFGAB" con rec{2} pitch = rec{2,4} perform decode (i) if d = 0 d = oldc end j = d if rec{9} = "-" j += 0x8000 /* tie flag end &dA &dA &d@ "N" or "P" = note record &dA &dA &d@ Check for alternate instrument &dA tget [X,xcnt+1] rec2 rec2 = rec2 // " " if rec2{1} = "S" and rec2 con "C2:" if rec2{mpt+3} = "A" inst_flag = 2 let = "P" else if rec2{mpt+3} = "b" inst_flag = 1 let = "N" else if rec2{mpt+3} = "a" let = "P" end end end else if inst_flag = 1 let = "N" else let = "P" end end orn(15) = let k = divcount - lastdur measdata(k) = measdata(k) // let // chs(i) // "," // chs(j) // ";" if addoctave = 1 measdata(k) = measdata(k) // let // chs(i-40) // "," // chs(j) // ";" end goto LOOP2 end end if rec{1} = "g" /* grace note orn(15) = "g" &dA &dA &d@ Check for alternate instrument &dA tget [X,xcnt+1] rec2 rec2 = rec2 // " " if rec2{1} = "S" and rec2 con "C2:" if rec2{mpt+3} = "A" inst_flag = 2 let = "J" else if rec2{mpt+3} = "b" inst_flag = 1 let = "G" else if rec2{mpt+3} = "a" let = "J" end end end else if inst_flag = 1 let = "G" else let = "J" end end if "CDEFGAB" con rec{2} pitch = rec{2,4} perform decode (i) j = d &dA &dA &d@ "G" or "J" = grace record &dA k = divcount measdata(k) = measdata(k) // let // chs(i) // "," // chs(j) // ";" goto LOOP2 end if rec{2} = " " if "CDEFGAB" con rec{3} pitch = rec{3,4} perform decode (i) j = d &dA &dA &d@ "G" or "J" = grace record &dA k = divcount if let = "G" measdata(k) = measdata(k) // "H" // chs(i) // "," // chs(j) // ";" else measdata(k) = measdata(k) // "K" // chs(i) // "," // chs(j) // ";" end goto LOOP2 end end end if rec{1} = "S" /* sound record rec = rec // " " loop for i = 2 to len(rec) if rec{i} = "C" j = int(rec{i+1..}) i = sub if rec{i..} con " " out = rec{i..sub-1} // ";" /* out{1} = ":" end i = sub k = divcount - lastdur if j >= 32 and j <= 43 &dA &dA &d@ "trw~cmj" = sound records for ornaments &dA if orn(j-31) <> "" measdata(k) = measdata(k) // orn(j-31) // "=" // out{2..} end else &dA &dA &d@ "g" = sound records for grace notes &dA if j = 1 and orn(15) = "g" if out con "m" temp = rev(measdata(divcount)) mpt = 0 loop for b = 1 to len(temp) if "GHJK" con temp{b} goto IPIPI end repeat IPIPI: if mpt > 0 temp{b} = "RSTU"{mpt} temp2 = rev(temp{b..}) temp = rev(temp{1,b-1}) b = int(temp) temp2 = temp2 // chs(b) // "," if out con "t" b = int(out{mpt+1..}) end measdata(divcount) = temp2 // chs(b) // ";" end else measdata(divcount) = measdata(divcount) // "g=" // out{2..} end end if j = 2 if out{2} = "A" inst_flag = 2 else if out{2} = "b" inst_flag = 1 end end end end end repeat goto LOOP2 end rec = trm(rec) if rec = "" putc You have run out of data in the table. This is caused most likely putc by a misunderstanding between the database and the program. In putc particular, if one of the files with membership in the "~group " putc group specifies the &dAgroup&d@ to be larger than it actually is, i.b46 e.b46 putc "part x of y" where, in fact, there are &dAless than y files&d@ in the stage2 putc directory having membership in the "~group " group, and if the program putc somehow acquires this "false information", then at some point the putc program will look for information that is not there. This is likely putc to be the case here. Therefore, before reporting this as a bug putc &dAyou should check all files having membership in the "~group " group putc to see that the size of the group given in each file is the same as putc the actual number of files having this membership. putc putc putc &dAProgram Halted&d@ putc stop end putc putc Unidentified record putc ~rec goto LOOP2 ENDL2: repeat play_size = tcnt &dKDEBUG&d@ &dK &d@ loop for i = 1 to tcnt &dK &d@ tget [Y,i] temp &dK &d@ putf [8] ~temp &dK &d@ repeat &dK &d@ stop &dKEND DEBUG&d@ &dKDEBUG&d@ &dK &d@ perform showarr &dK &d@ getc &dK &d@ stop &dKENDDEBUG&d@ putc Done! putc &dA &dA &d@ In case there are "real time" grace notes, collect info on placement &dA &dA &d@ Description of add_time_sit (.,1) = part number containing situation &dA &d@ (.,2) = number of measures to situation &dA &d@ (.,3) = divspq at measure with situation &dA &d@ (.,4) = number of divisions to situation &dA &d@ (.,5) = time duration of situation (in divisions) &dA if add_time_flag = 1 putc putc There are realtime grace notes in this piece. sit_cnt = 0 sit_part = 1 sit_mcnt = 0 loop for i = 1 to tcnt tget [Y,i] temp if temp{1} = "E" sit_mcnt = 0 ++sit_part goto WERT end if temp{1} = "Q" sit_divspq = int(temp{3..}) goto WERT end if temp{1} = "M" ++sit_mcnt goto WERT end if temp{1} = "D" a = int(temp{2..}) if temp{sub..} con ['R','S'] ++sit_cnt add_time_sit(sit_cnt,1) = sit_part add_time_sit(sit_cnt,2) = sit_mcnt add_time_sit(sit_cnt,3) = sit_divspq add_time_sit(sit_cnt,4) = a a = 0 RS_LOOK: if temp{sub..} con ['R','S'] b = int(temp{sub+1..}) b = int(temp{sub+1..}) a += b goto RS_LOOK end add_time_sit(sit_cnt,5) = a end end WERT: repeat putc Number of situations = ~sit_cnt loop for i = 1 to sit_cnt putc Situation ~i putc part = ~add_time_sit(i,1) putc meas = ~add_time_sit(i,2) putc divspq = ~add_time_sit(i,3) putc div = ~add_time_sit(i,4) putc added time = ~add_time_sit(i,5) divisions repeat &dA &dA &d@ Bubble sort the situations by measure number &dA if sit_cnt > 1 loop for i = 1 to sit_cnt - 1 loop for j = i + 1 to sit_cnt if add_time_sit(i,2) > add_time_sit(j,2) loop for a = 1 to 5 b = add_time_sit(i,a) add_time_sit(i,a) = add_time_sit(j,a) add_time_sit(j,a) = b repeat end repeat repeat end &dA &dA &d@ Create new version of table &dA treset [X] xtcnt = 0 sav_sit_cnt = sit_cnt sit_cnt = 1 qratio = add_time_sit(sit_cnt,4) - 1 * 17280 / add_time_sit(sit_cnt,3) trigger = 0 sit_part = 1 sit_mcnt = 0 loop for i = 1 to tcnt tget [Y,i] temp if temp{1} = "E" sit_mcnt = 0 ++sit_part sit_cnt = 1 goto WERQ end if temp{1} = "Q" sit_divspq = int(temp{3..}) goto WERQ end if temp{1} = "M" ++sit_mcnt if trigger > 0 a = int(temp{2..}) a += sit_incre temp = "M" // chs(a) // temp{sub..} ++sit_cnt if sit_cnt <= sav_sit_cnt qratio = add_time_sit(sit_cnt,4) - 1 * 17280 qratio = qratio / add_time_sit(sit_cnt,3) end trigger = 0 end if sit_mcnt = add_time_sit(sit_cnt,2) /* target measure trigger = 1 end goto WERQ end if temp{1} = "D" a = int(temp{2..}) c = sub if sit_mcnt = add_time_sit(sit_cnt,2) /* target measure b = a - 1 * 17280 / sit_divspq if sit_part <> add_time_sit(sit_cnt,1) /* one of "other" parts if trigger = 1 trigger = 2 sit_incre = add_time_sit(sit_cnt,5) * 17280 / add_time_sit(sit_cnt,3) sit_incre = sit_incre * sit_divspq / 17280 end if b > qratio a = a + sit_incre temp = "D" // chs(a) // temp{c..} /* advance division counter end else /* part with "cadenza" if b = qratio temp = temp // " " if temp con "," end ++sub k = int(temp{sub..}) /* k = first increment, sub ->";" temp2 = temp{1,sub} /* first record ++sub SIT_D: if temp{sub} = "T" or temp{sub} = "U" savsub = sub if temp{sub..} con ";" end temp2 = temp2 // temp{savsub..sub} ++sub goto SIT_D end SIT_A: if temp{sub} in [' ','N','P'] temp = temp2 // temp{sub..} loop for h = 1 to len(temp) if "RSTU" con temp{h} temp{h} = "NPNP"{mpt} end repeat goto SIT_B else savsub = sub loop for h = 1 to len(temp2) if "RSTU" con temp2{h} temp2{h} = "NPNP"{mpt} end repeat sub = savsub ++xtcnt tput [X,xtcnt] ~temp2 end savsub = sub if temp{sub..} con "," end ++sub /* sub -> duration a += k k = int(temp{sub..}) /* next increment, sub ->";" temp2 = "D" // chs(a) // " " // temp{savsub..sub} /* beginning of next record ++sub /* sub ->"R" SIT_C: if temp{sub} = "T" or temp{sub} = "U" savsub = sub if temp{sub..} con ";" end temp2 = temp2 // temp{savsub..sub} ++sub goto SIT_C end goto SIT_A SIT_B: trigger = 2 sit_incre = add_time_sit(sit_cnt,5) else if b > qratio a = a + sit_incre temp = "D" // chs(a) // temp{c..} /* advance division counter end end end end goto WERQ end WERQ: ++xtcnt temp = trm(temp) tput [X,xtcnt] ~temp repeat treset [Y] j = 1 &dK &dK &d@ open [8,2] "file" &dK loop for i = 1 to xtcnt tget [X,i] temp if temp{1} = "E" and i <> xtcnt ++j datapoint(j) = i + 1 end tput [Y,i] ~temp &dK &dK &d@ putf [8] ~temp &dK repeat tcnt = xtcnt &dK &dK &d@ close [8] &dK end if midi_trkcnt(midi_type) = 10000 putc No midi assignment file &dAfor this MIDI type&d@ was found for this putc movement. This means that this compilation is probably being putc done for the first time. You will therefore have to supply the putc necessary information regarding the assignment of the various putc parts to midi channels and also specifying the primary and putc secondary MIDI instrument numbers for these channels. putc putc In order to do this properly, you will need to have a list of the putc 128 MIDI instrument numbers handy. Type to continue. getc line line = trm(line) if line <> "" putc &dAProgram Halted&d@ putc stop end OVER: loop for i = 1 to 32 midiass(i) = 0 repeat putc There are ~trkcnt sound tracks in this movement. These are listed below putc in the order of their membership in the group. The instrument putc which plays the track is also listed. You will first need to putc assign a channel number to each of these tracks. There are 16 putc possible channels, numbered 1 to 16. Type the channel number under putc the word "channel" in front of each track, and then put the cursor putc on the line with the first track and type for each line. putc You need to enter all of the lines. Do this now. putc putc &dAWARNING&d@: Do not use &dAchannel 10&d@; MIDI will over-ride your choice of putc instrument and you will hear a lot of funny percussion sounds! putc putc channel track Instrument putc ÄÄÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ loop for i = 1 to trkcnt putc .t12w3 ~i .t20 ~inst(i) repeat loop for i = 1 to trkcnt getc line line = line // pad(30) j = int(line{12..}) if j <> i putc .t80 .t1 You are not entering the lines in order putc .t80 .t1 Please start over putc .t80 putc .t80 putc .t80 putc .t80 putc .t80 putc .t80 goto OVER end p = int(line) if p >= 1 and p <= 16 midiass(i) = p else putc .t80 .t1 You have specified a channel number ~p which is out of range putc .t80 .t1 Please start over putc .t80 putc .t80 putc .t80 putc .t80 putc .t80 putc .t80 goto OVER end repeat putc If you want a list of all the General MIDI instruments on your screen putc for reference, enter a "y" now getc line if line con "y" putc &dA &dI &dA &dI &dA &dI &dA &dI &dA putc &dA &dI &dA &dI MIDI Patch Numbers &dA &dI &dA putc &dA &dI &dA &dI &dA &dI &dA &dI &dA putc loop for i = 1 to 50 putc ~mpn(i) repeat putc putc end putc Enter "z" now if you would like some advice on voice assignments getc line if line con "z" putc &dAVoicing advice (for what it's worth)&d@ putc putc Solo Trumpet try 59 putc Solo Oboe try 69 putc Solo Violin try 41 putc Solo SOPRANO try 65 (also TENOR) putc Solo ALTO try 73 (also BASS) putc Cello continuo try 64 for choruses, 43 for arias putc Harpsichord try 7 putc Organ continuo try 17 putc French Horn try 61 putc Violin Chorus try 64 putc end OVER2: loop for i = 1 to 16 chanst(i,1) = 0 chanst(i,2) = 0 repeat putc putc Now you need to assign instrument numbers to each channel. The putc channels you specified above are listed below. In the same way putc you assigned channels to the tracks, you should now assign putc instrument numbers to each channel. There are 128 possible putc instruments, numbered 1 to 128. You should enter two numbers putc for each channel: a primary instrument and a secondary instrument. putc The secondary instrument is for playing very short notes, e.b46 g., putc pizzacato strings. Since not all instruments have a separate putc number for short sounds, you may assign the same number to both putc the primary and secondary instruments. Assign these numbers now. putc putc inst1 inst2 channel putc ÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄÄÄ q = 0 loop for i = 1 to trkcnt p = midiass(i) if chanst(p,1) = 0 putc .t18w2 ~p ... loop for j = 1 to trkcnt if midiass(j) = p putc ~inst(j) ... end repeat putc chanst(p,1) = -1 ++q end repeat loop for i = 1 to q getc line line = line // pad(20) p = int(line{18..}) if p >= 1 and p <= 16 chanst(p,1) = int(line) ++sub chanst(p,2) = int(line{sub..}) end repeat loop for i = 1 to trkcnt p = midiass(i) if chanst(p,1) <= 0 putc .t80 .t1 You have not specified the instruments for channel ~p putc .t80 .t1 Please try this process again loop for i = 1 to trkcnt p = midiass(i) chanst(p,1) = 0 chanst(p,2) = 0 repeat goto OVER2 end repeat putc putc Review of channel and instrument assignments putc putc track instrument channel inst1 inst2 putc ÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄ loop for i = 1 to trkcnt putc .w3 ~i .t9 ~inst(i) .t34 ~midiass(i) .t43 ~chanst(midiass(i),1) .t52 ~chanst(midiass(i),2) repeat putc putc Enter a blank line to accept; enter a non-blank line to redo the assignments getc line line = trm(line) if line <> "" putc putc loop for i = 1 to 16 chanst(i,1) = 0 chanst(i,2) = 0 repeat goto OVER end putc putc A midi assignment file will be created with the data you have entered. putc This will relieve you of the trouble of re-entering these assignments putc the next time you compile this movement into a MIDI file. putc &dA &dA &d@ Creating a MIDI assignment file &dA open [7,1] slib loop getf [7] rec rec = rec{1,12} if rec{1} = " " putc No files have "~group " membership are in this library. Compilation putc terminated. putc stop end if rec{10} <> " " rec{9} = "." end rec = trm(rec) rec = slib // "/" // rec mcount = 0 open [4,1] rec loop for i = 1 to 20 getf [4] templines(i) repeat if templines(11) con group /* "sound" or "data" goto MAKE_MIDI_ASS else close [4] goto NXFI end repeat eof4: goto feof3 MAKE_MIDI_ASS: close [4] close [7] if midi_type = 1 outfile = slib // "/MCHAN1" else outfile = slib // "/MCHAN2" end open [4,2] outfile midi_open = 1 loop for i = 1 to 8 putf [4] ~templines(i) repeat putf [4] Midi assignment putf [4] putf [4] Group memberships: midi if midi_type = 1 putf [4] midi: part 0 of ~trkcnt else putf [4] midi: part 1 of ~trkcnt end putf [4] & putf [4] Comments here putf [4] & loop for i = 1 to trkcnt putf [4] part .w2 ~i = channel ~midiass(i) .t26 ~inst(i) repeat loop for i = 1 to trkcnt p = midiass(i) if divmult(p) = 0 putf [4] channel .w2 ~p : instrument = ~chanst(p,1) , ~chanst(p,2) divmult(p) = 1 end repeat loop for i = 1 to 16 divmult(i) = 0 repeat QBPMA: putc Please specify a "restrike factor" (legato factor) between 1 and 10. putc 1 is very legato and 10 is rather detached. Enter blank = default (6) getc restrike_factor if restrike_factor = 0 restrike_factor = 6 end if restrike_factor < 1 or restrike_factor > 10 goto QBPMA end putf [4] ~restrike_factor restrike factor QBPMB: putc Please specify a starting tempo in terms of quarter notes per minute. bpm = 0 getc bpm if bpm = 0 goto QBPMB end putf [4] ~bpm .t5 quarter notes per minute putc else if midi_trkcnt(midi_type) <> trkcnt putc putc The number of tracks specified in the Midi assignment file does not putc agree with the number of tracks specified in the files belonging to putc the "~group " group. This discrepency must be fixed before this program putc can continue. putc putc &dAProgram Halted&d@ putc stop end if midi_type = 1 loop for i = 1 to trkcnt midiass(i) = midiass1(i) repeat loop for i = 1 to 16 chanst(i,1) = chanst1(i,1) chanst(i,2) = chanst1(i,2) repeat bpm = bpm_data1(1) restrike_factor = restrike_factor1 loop for j = 1 to bpm_cnt1 bpm_data(j) = bpm_data1(j) repeat bpm_cnt = bpm_cnt1 else loop for i = 1 to trkcnt midiass(i) = midiass2(i) repeat loop for i = 1 to 16 chanst(i,1) = chanst2(i,1) chanst(i,2) = chanst2(i,2) repeat bpm = bpm_data2(1) restrike_factor = restrike_factor2 loop for j = 1 to bpm_cnt2 bpm_data(j) = bpm_data2(j) repeat bpm_cnt = bpm_cnt2 end putc putc Review of channel and instrument assignments putc putc track instrument channel inst1 inst2 putc ÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄ loop for i = 1 to trkcnt putc .w3 ~i .t9 ~inst(i) .t34 ~midiass(i) .t43 ~chanst(midiass(i),1) .t52 ~chanst(midiass(i),2) repeat putc putc Type to continue getc end goto NXP feof3: putc putc putc putc The file ~infile if eof3flag = 0 putc is supposed to belong to the "~group " group, but no record putc was found indicating the membership number in the group. putc This oversight must be corrected before this program can putc continue. else if eof3flag = 1 putc is incomplete (does not terminate properly). This oversight putc must be corrected before this program can continue. else putc has an improper format for a midi assignment file. The file putc must be corrected or deleted before this program can continue. end end putc putc &dAProgram Halted&d@ putc stop NXP: if midiform = 0 putc At the present time, we compile only the collapsed MIDI0 format. rec = "y" &dA &d@ putc There are two options with the MIDI0 representation. You can use &dA &d@ putc use the same channel assignments as MIDI1 or you can collapse all &dA &d@ putc of the tracks down to one channel. Enter a "y" on the next line &dA &d@ putc if you want to collapse everything to one channel. &dA &d@ getc rec if rec con "y" MINS: putc This will point all of the tracks on one channel. &dA &d@ putc You have chosen to collapse all of the tracks down to one channel. putc Channel 1 will be used for this purpose. Please enter the instrument putc number you would like to assign to this channel. Enter a number putc between 1 and 128 i = 0 getc i if i < 1 or i > 128 putc goto MINS end chanst(1,1) = i chanst(1,2) = i midiform = 3 loop for j = 1 to trkcnt midiass(j) = 1 repeat end end f = bpm * mtpq / 960 max_restrike_pause = mtpq * restrike_factor / 64 /* formerly mtpq * 3 / 32 if f > max_restrike_pause f = max_restrike_pause end first_restrike_pause = f &dA &dA &d@ We have a minor piece of business to take care of here. We need to &dA &d@ determine the location of all time signature changes. We also need &dA &d@ to look for tempo changes. &dA &dA &d@ For this purpose, we need only to look at one track, since we assume &dA &d@ at this point that all tracks are identical (not true for some cases, &dA &d@ by the way). Might as well look at track one. &dA time_cnt = 0 if datapoint(2) <> 0 k = datapoint(2) - 1 else k = play_size end bpm_cnt2 = 1 tmpcnt = 0 loop for tcnt = datapoint(1) to k tget [Y,tcnt] rec if rec{1} = "Q" loc_divspq = int(rec{3..}) goto XREC end if rec{1} = "D" i = int(rec{2..}) p = i ++sub if rec{sub} = "T" --i i *= 240 i /= loc_divspq i += time_cnt /* actual time of this event in midi tics ++pc_cnt para_change(pc_cnt,1) = i i = int(rec{sub+2..}) ++sub j = int(rec{sub..}) k = 0 loop j /= 2 ++k if rem <> 0 putc putc Illegal time signature ~rec putc but ... Program will continue. putc end repeat while j > 1 j = i << 8 + k para_change(pc_cnt,2) = j para_change(pc_cnt,3) = 1 /* 1 = flag for "time change" end if rec con "W:" j = int(rec{sub+2..}) i = p - 1 i *= 240 i /= loc_divspq i += time_cnt /* actual time of this event in midi tics ++pc_cnt para_change(pc_cnt,1) = i ++tempo_cnt tempo_change(tempo_cnt,1) = i if j = 0 /* ask for value (or get from file) ++bpm_cnt2 if bpm_data(bpm_cnt2) <> 0 j = bpm_data(bpm_cnt2) else putc There is a tempo change in Measure ~tmpcnt . Please enter putc a value as measured in &dAquarter notes per minute&d@. QTEMP: putc getc j if j < 15 or j > 240 putc This value (= ~j ) is out-of-range. Please enter value again. goto QTEMP end if midi_open = 1 putf [4] ~j .t5 quarter notes per minute at measure ~tmpcnt else putc putc Your "MCHAN" file is out-of-date (and out-of-data). You may putc want to add this data, or delete and recompile the file. putc Type to continue getc end end end para_change(pc_cnt,2) = j para_change(pc_cnt,3) = 2 /* 2 = flag for "tempo change" tempo_change(tempo_cnt,2) = j end goto XREC end if rec{1} = "M" i = int(rec{2..}) i *= 240 i /= loc_divspq time_cnt += i /* actual time of this event in midi tics if rec con "\" and sub < len(rec) tmpcnt = int(rec{sub+1..}) end goto XREC end XREC: repeat if midi_open = 1 putf [4] /END close [4] end &dA &dA &d@ At this point, we have all of the data stored sequentially by track &dA &d@ in the Y table. And we know what the user wants done with this data. &dA &dA &d@ Here is what we must do now: &dA &dA &d@ 1. For each output channel, we need to compile a complete MIDI &dA &d@ performance. The result will be a MIDI string, which will be &dA &d@ stored in some portion of the "music" string. There will be &dA &d@ a set of pointers and lengths to tell us where the data is. &dA &dA &d@ 2. In the case of compiling MIDI1 and collapsed MIDI0, we are &dA &d@ now nearly done. We simply need to assemble the pieces of &dA &d@ the final output sequentially and send them to the output &dA &d@ file. &dA &dA &d@ 3. In the case of compiling uncollapsed MIDI0, we have one more &dA &d@ step. We need to read through the separate MIDI channels in &dA &d@ in parallel and compile a "merged" set of instructions, which &dA &d@ can then be sent to the output. &dA ppntr = 10000 /* arbitrary value loop for channel = 1 to 16 restrike_pause = first_restrike_pause &dK &d@ putc restrike_pause set to ~restrike_pause treset [X] t1 = 0 loop for i = 1 to trkcnt /* check for compilation to this channel if midiass(i) = channel t1 = i i = trkcnt end repeat if t1 = 0 goto XCHANN end chanpntr(channel) = ppntr + 1 putc Compiling music to channel ~channel curvel = 90 &dA &dA &d@ Loop through the piece one measure at a time. Combine all of &dA &d@ the parts that go to this channel; generate detail for all of &dA &d@ the ornaments. &dA xtcnt = 0 MLOOP: /* this is the "Measure Loop" &dA &dA &d@ I. Get divspq(.) and divspm(.) for each of the parts in this measure &dA ornflag = 0 loop for crnttrk = 1 to trkcnt if midiass(crnttrk) = channel tcnt = datapoint(crnttrk) LA1: tget [Y,tcnt] line if line{1} = "Q" divspq(crnttrk) = int(line{3..}) ++tcnt goto LA1 end loop while line{1} <> "M" if line con "=" ornflag = 1 end ++tcnt tget [Y,tcnt] line repeat divspm(crnttrk) = int(line{2..}) end repeat if sub < len(line) measnum = int(line{sub+1..}) - 1 end &dA &dA &d@ II. Compute global divspm for this measure &dA loop for i = 1 to MEASDX loop for j = 1 to trkcnt if midiass(j) = channel if divspq(j) = 0 examine end g = i / divspq(j) g *= divspq(j) if g <> i j = 10000 end end repeat if j <> 10000 goto XX end repeat XX: k = i /* lowest common denominator j = divspm(t1) * 16 / divspq(t1) /* number of 64th notes in measure i *= j i >>= 4 if ornflag = 1 if k < 32 i <<= 3 else if k < 64 /* 64 divspq = 256th note resolution i <<= 2 /* i.e. trills on 64th notes else if k < 128 i <<= 1 end end end loop while i < j i <<= 1 repeat end glob_divspm = i /* divs 1 meas 64th /* i (----) x --- (----) x 16 (----) glob_divspq = i * 16 / j /* meas j 64th 4th if glob_divspq > MEASDX putc Too many divisions per quarter note at measure ~measnum for this putc program. This is usually caused by too many crazy multiples putc in one measure. 11 in time of 8 against 13 in time of 8 against putc 5 in time of 4 would probably do it. Also a measure with 256th putc notes and triples and ornaments would have a problem. putc putc You could try re-compiling the program with a larger value for MEASDX putc putc &dAProgram Halted&d@ putc stop end &dA &d@ &dA &d@ III. Check measure length in all parts, and compute divmult(.) &dA j = divspm(t1) * 16 / divspq(t1) /* number of 64th notes in measure (part t1) loop for i = 1 to trkcnt if midiass(i) = channel k = divspm(i) * 16 / divspq(i) if k <> j putc Mismatch in measure length in part ~i at measure ~measnum stop end divmult(i) = glob_divspm / divspm(i) end repeat &dA &dA &d@ IV. Clear measdata and prepare to collect data for this measure &dA loop for i = 1 to glob_divspm measdata(i) = "" repeat ++xtcnt tput [X,xtcnt] Q:~glob_divspq :~glob_divspm &dA &dA &d@ V. Compile data for measure; expand ornaments &dA loop for crnttrk = 1 to trkcnt if midiass(crnttrk) = channel tcnt = datapoint(crnttrk) - 1 LA2: ++tcnt tget [Y,tcnt] line &dK &d@ dputc ~line /* &dADEBUG&d@ &dK &dK &d@ examine if line{1} = "Q" goto LA2 end if line{1} = "D" divpoint = int(line{2..}) --divpoint divpoint *= divmult(crnttrk) ++divpoint ++sub &dA &dA &d@ Strip out any time signatures and put them at the front of measdata &dA &d@ for this division (for no reason, by the way, since they are &dA &d@ simply ignored later in the program). &dA savesub1 = sub LOOKAGA: if line con "T:" line = line // " " j = sub if line{sub..} con ";" k = sub end if measdata(divpoint) = "" measdata(divpoint) = line{j..k} else if measdata(divpoint){1} <> "T" measdata(divpoint) = line{j..k} // measdata(divpoint) end end line = line{1..j-1} // line{k+1..} goto LOOKAGA end sub = savesub1 &dA &dA &d@ Strip out any velocity changes and put them at the front of measdata &dA &d@ for this division. &dA savesub1 = sub LOOKAG: if line con "V:" line = line // " " j = sub if line{sub..} con ";" k = sub end if measdata(divpoint) = "" measdata(divpoint) = line{j..k} else if measdata(divpoint){1} <> "V" measdata(divpoint) = line{j..k} // measdata(divpoint) end end line = line{1..j-1} // line{k+1..} goto LOOKAG end sub = savesub1 &dA &dA &d@ Strip out and discard all tempo changes (W:) &dA savesub1 = sub LOOKBG: if line con "W:" line = line // " " j = sub if line{sub..} con ";" k = sub end line = line{1..j-1} // line{k+1..} goto LOOKBG end sub = savesub1 &dA &dA &d@ We are now looking for "data groups". A data group consists &dA &d@ of zero, one or more grace notes, with or without sound specs, &dA &d@ and one regular note with or without ornament specs. &dA LA4: grace_count = 0 LA3: i = sub if "GJHK" con line{i} icode = mpt spitch = int(line{i+1..}) sdur = int(line{sub+1..}) savesub1 = sub if line{sub+1,2} = "g=" /* sound is specified sub += 3 if line{sub} = "f" ++sub end if line{sub} = "t" ++grace_count grace_percent(grace_count) = int(line{sub+1..}) grace_pitch(grace_count) = spitch grace_flag(grace_count) = icode /* 1 to 4 end end sub = savesub1 if line{sub..} con ";" ++sub if sub + 2 < len(line) /*&dA if line{sub,2} = "g=" /*&dA if line{sub..} con ";" /*&dA &d@ Code added ++sub /*&dA &d@ 12-22-93 end /*&dA end /*&dA end /*&dA if sub < len(line) goto LA3 end end end trillflag(1) = 0 mortflag(1) = 0 if "NP" con line{i} icode = mpt spitch = int(line{i+1..}) sdur = int(line{sub+1..}) if sdur > 0x8000 sdur -= 0x8000 sdur *= divmult(crnttrk) sdur += 0x8000 else sdur *= divmult(crnttrk) end tsdur = sdur & 0x7fff /* duration without tie flag savesub1 = sub if "trw~c" con line{sub+1} /* trill type ornament savesub1 += 3 temp = line{savesub1..} if temp con ";" temp = temp{1..mpt} end if temp con "m" trillflag(1) = 2 else /* "u" trillflag(1) = 1 end if temp con "h" trillflag(2) = 1 else /* "w" or "j" if temp con "j" trillflag(2) = 100 /* "j" else trillflag(2) = 0 /* "w" end end if temp con "n" trillflag(3) = int(temp{mpt+1..}) else /* "n4" trillflag(3) = 4 end if temp con "s" trillflag(4) = int(temp{mpt+1..}) else /* "s25" trillflag(4) = 25 end if temp con "t" trillflag(5) = int(temp{sub+1..}) else /* "t25" trillflag(5) = 75 end trillflag(6) = 0 if temp con "e" trillflag(6) = 2 end if temp con "f" trillflag(6) = 1 end else sub = savesub1 if "m" con line{sub+1} /* mordant type ornament savesub1 += 3 temp = line{savesub1..} if temp con ";" temp = temp{1..mpt} end if temp con "b" mortflag(1) = 2 else /* "m" mortflag(1) = 1 end if temp con "h" mortflag(2) = 1 else /* "w" mortflag(2) = 0 end if temp con "n" mortflag(3) = int(temp{mpt+1..}) else /* "n3" mortflag(3) = 3 end if temp con "s" mortflag(4) = int(temp{mpt+1..}) else /* "s12" mortflag(4) = 12 end if temp con "t" mortflag(5) = int(temp{mpt+1..}) else /* "t24" mortflag(5) = 24 end end end if trillflag(1) = 0 and mortflag(1) = 0 and grace_count = 0 /* no alteration temp = "NP"{icode} // chs(spitch) // "," // chs(sdur) // ";" measdata(divpoint) = measdata(divpoint) // temp else tdur = tsdur tdivpoint = divpoint &dA &dA &d@ Start with grace notes &dA if grace_count > 0 loop for i = 1 to grace_count ndur = tsdur * grace_percent(i) ndur += 50 ndur /= 100 if grace_flag(i) < 3 /* not an extra grace chord tone tdur -= ndur end temp = "NPNP"{grace_flag(i)} temp = temp // chs(grace_pitch(i)) // "," // chs(ndur) // ";" measdata(tdivpoint) = measdata(tdivpoint) // temp if grace_flag(i) < 3 /* not an extra grace chord tone tdivpoint += ndur end repeat temp = "NP"{icode} // chs(spitch) // "," // chs(tdur) // ";" measdata(tdivpoint) = measdata(tdivpoint) // temp end &dA &dA &d@ Now do the ornaments &dA if trillflag(1) > 0 if trillflag(2) = 0 tpitch = spitch + 6 else if trillflag(2) = 1 tpitch = spitch + 5 else tpitch = spitch /* unison trill end end dd(1) = tsdur * trillflag(4) + 20 / 100 qq = trillflag(3) /* number of beats dd(qq) = 100 - trillflag(5) * tsdur + 20 / 100 ii = tdur - dd(1) - dd(qq) if ii < qq - 2 putc Not enought divisions to trill examine stop end hh = qq - 2 loop for kk = qq - 1 to 2 step -1 jj = ii / hh dd(kk) = jj ii -= dd(kk) --hh repeat ii = 1000 jj = tsdur - tdur loop for kk = 1 to qq if dd(kk) < ii ii = dd(kk) end jj += dd(kk) repeat if ii = 0 putc Can't trill examine stop end if jj <> tsdur examine end pp = tpitch if trillflag(1) = 2 pp = spitch end pp2 = pp loop for i = 1 to qq ndur = dd(i) temp = "NP"{icode} // chs(pp2) // "," // chs(ndur) // ";" measdata(tdivpoint) = measdata(tdivpoint) // temp tdivpoint += ndur if pp = tpitch pp = spitch else pp = tpitch end if i = qq - 2 and trillflag(6) > 0 pp2 = spitch - trillflag(6) - 4 /* minus 5 or 6 else pp2 = pp end repeat else if mortflag(1) > 0 if mortflag(2) = 0 tpitch = spitch - 6 else tpitch = spitch - 5 end dd(1) = tsdur * mortflag(4) / 100 qq = mortflag(3) /* number of beats dd(qq) = 100 - mortflag(5) * tsdur / 100 ii = tdur - dd(1) - dd(qq) if ii < qq - 2 putc Not enought divisions to trill examine stop end hh = qq - 2 loop for kk = qq - 1 to 2 step -1 jj = ii / hh dd(kk) = jj ii -= dd(kk) --hh repeat ii = 1000 jj = tsdur - tdur loop for kk = 1 to qq if dd(kk) < ii ii = dd(kk) end jj += dd(kk) repeat if ii = 0 putc Can't trill examine stop end if jj <> tsdur examine end pp = spitch if mortflag(1) = 2 pp = tpitch end loop for i = 1 to qq ndur = dd(i) temp = "NP"{icode} // chs(pp) // "," // chs(ndur) // ";" measdata(tdivpoint) = measdata(tdivpoint) // temp tdivpoint += ndur if pp = tpitch pp = spitch else pp = tpitch end repeat end end end if line{savesub1..} con ";" ++sub if sub < len(line) goto LA4 end end end goto LA2 end if line{1} <> "M" putc Problem examine stop end datapoint(crnttrk) = tcnt + 1 end /* end of looking at this track for this channel repeat /* end of looking through all tracks for this measure if line con "\" and mpt < len(line) line = line{mpt+1..} else line = "" end temp = "M\" // line &dA &dA &d@ VI. Now write data to table &dA loop for i = 1 to glob_divspm if measdata(i) <> "" ++xtcnt tput [X,xtcnt] D~i ~measdata(i) end repeat ++xtcnt tput [X,xtcnt] ~temp tget [Y,tcnt+1] temp if temp{1} <> "E" goto MLOOP /* goto top of "Measure Loop" end ++xtcnt tput [X,xtcnt] ~temp &dKDEBUG&d@ &dK &d@ putc Type "y" to see the table for this channel &dK &d@ getc line &dK &d@ line = line // pad(1) &dK &d@ if line{1} = "y" &dK &d@ loop for i = 1 to xtcnt &dK &d@ tget [X,i] line &dK &d@ putc .w5 ~i ~line &dK &d@ repeat &dK &d@ putc &dK &d@ end &dKEND DEBUG&d@ goto MUSIC_COMPILE &dA &dA &d@ &dA &dA &d@ &dA &d@ &dA &dA &d@ &dA New system of "sequencing" note events &d@ &dA &dA &d@ &dA &d@ &dA &dA &d@ &dA &dA &d@ &dA &dA &d@ At the moment, our data is organized as a series of &dA &dA &d@ "note on" events, each of which has a specified "duration". &dA &dA &d@ At the moment, there is one wrinkle. &dA &dA &d@ &dA &dA &d@ The wrinkle is that some "note on" events &dA &dA &d@ have a duration which specifies a "tie". In this case, &dA &dA &d@ the tie goes to the end of the measure, so we know that &dA &dA &d@ during the measure, we do not need to worry about turning &dA &dA &d@ the note off; we simply need to think about whether we &dA &dA &d@ want to restrike it at the beginning of the next measure &dA &dA &d@ (it &dAwill&d@ be in the list of notes to strike). &dA &dA &d@ &dA &dA &d@ What we do not explicitly have, and what we need &dA &dA &d@ is "note off" information. Also we need to deal with &dA &dA &d@ the problem of "re-struck notes" and the problem of &dA &dA &d@ of turning off notes which have been turned on more &dA &dA &d@ than once. &dA &dA &d@ &dA &dA &d@ Under the old system, an articulation factor was &dA &dA &d@ used to determine a time unit, "delta_tim", for each &dA &dA &d@ measure. This unit was used universally throughout the &dA &dA &d@ measure as a "duration reduction" factor for all notes. &dA &dA &d@ This system has two disadvantages, both relating to &dA &dA &d@ the size of "delta_tim". &dA &dA &d@ &dA &dA &d@ (1) Since the size of delta_tim depends on the shortest &dA &dA &d@ duration in the measure, the articulation of notes &dA &dA &d@ within the measure could vary widely, depending on &dA &dA &d@ what the shortest note was (in measures where there &dA &dA &d@ was a trill, the articulation would be practically &dA &dA &d@ eliminated). &dA &dA &d@ &dA &dA &d@ (2) In cases where delta_tim was very small, the note &dA &dA &d@ restrike feasure (note off followed by note on) &dA &dA &d@ essentially disappeared. &dA &dA &d@ &dA &dA &d@ To correct these problems, we need to uncouple the &dA &dA &d@ various "note off" time shift factors. We actually have &dA &dA &d@ two situations: (1) the articulation, which should depend &dA &dA &d@ somewhat on the length of the note being articulated, and &dA &dA &d@ (2) the restrike "pause", i.e. the length of time a note &dA &dA &d@ should be off before it is restruck. The restrike "pause" &dA &dA &d@ should be normally be a constant, but should never be &dA &dA &d@ longer than 1/2 the duration of the shortest note currently &dA &dA &d@ sounding, and should never be longer that the distance back &dA &dA &d@ to a prior (unexecuted) note-off for the sounding note. &dA &dA &d@ &dA &dA &d@ What we are talking about here is generating a time- &dA &dA &d@ specific list of note-on and note-off events based on the &dA &dA &d@ directions given above. To do this, we need to set up a &dA &dA &d@ 2-dimensional array which is 127 notes wide and divspm &dA &dA &d@ units long. We have already, in fact, allocated storage &dA &dA &d@ that could be used for this, namely measdata.256(MEASDX). &dA &dA &d@ &dA &dA &d@ We should proceed measure by measure. We first &dA &dA &d@ generate note on/off information based entirely the &dA &dA &d@ note on data we have and on the articulation algorighm &dA &dA &d@ we choose. &dA &dA &d@ &dA &dA &d@ For the purpose of generating MIDI files, we can set &dA &dA &d@ midi tics per quarter note = 240. We will need to "resolve" &dA &dA &d@ all durations onto an "integer" grid of this size. &dA &dA &d@ Under this method, we do the calculation: &dA &dA &d@ &dA &dA &d@ tinc = 1000 * mtpq / j /* thousandths of midi tics &dA &dA &d@ per global division &dA &dA &d@ &dA &dA &d@ where j = divspq, and all tinc values are resolved to the &dA &dA &d@ nearest 1000 before placing them on the midi-tic array. &dA &dA &d@ &dA &dA &d@ Addressing the question of how to determine articulation, &dA &dA &d@ I would suggest that the articulation factor slide somewhat &dA &dA &d@ with the length of note involved. If, for example, the &dA &dA &d@ articulation factor is 4 (i.e. 1/4th of the note is cut &dA &dA &d@ off), then I would apply this to all durations of 1/8th &dA &dA &d@ or less, and I would multiple the factor by 4/5ths for all &dA &dA &d@ durations of 1/4th to 1/8th+; by 16/25ths for all durations &dA &dA &d@ of 1/2half to 1/4th+; and by 64/125ths for all durations of &dA &dA &d@ whole to 1/2half+. This would give a pattern such as &dA &dA &d@ the one shown below &dA &dA &d@ &dA &dA &d@ &dA &dA &d@ Articulation factor = 1/4 &dA &dA &d@ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ &dA &dA &d@ 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 &dA &dA &d@ | | | | | | | | | | | | | | | | | &dA &dA &d@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx........ &dA &dA &d@ xxxxxxxxxxxxxxxxxxxxxxxxxxx.....xxxxxxxxxxxxxxxxxxxxxxxxxxx..... &dA &dA &d@ xxxxxxxxxxxxx...xxxxxxxxxxxxx...xxxxxxxxxxxxx...xxxxxxxxxxxxx... &dA &dA &d@ xxxxxx..xxxxxx..xxxxxx..xxxxxx..xxxxxx..xxxxxx..xxxxxx..xxxxxx.. &dA &dA &d@ xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx. &dA &dA &d@ &dA &dA &d@ Articulation factor = 1/3 &dA &dA &d@ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ &dA &dA &d@ 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 &dA &dA &d@ | | | | | | | | | | | | | | | | | &dA &dA &d@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx........... &dA &dA &d@ xxxxxxxxxxxxxxxxxxxxxxxxx.......xxxxxxxxxxxxxxxxxxxxxxxxx....... &dA &dA &d@ xxxxxxxxxxxx....xxxxxxxxxxxx....xxxxxxxxxxxx....xxxxxxxxxxxx.... &dA &dA &d@ xxxxx...xxxxx...xxxxxx..xxxxx...xxxxx...xxxxx...xxxxxx..xxxxx... &dA &dA &d@ xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx. &dA &dA &d@ &dA &dA &d@ Articulation factor = 1/2 &dA &dA &d@ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ &dA &dA &d@ 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 &dA &dA &d@ | | | | | | | | | | | | | | | | | &dA &dA &d@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx................ &dA &dA &d@ xxxxxxxxxxxxxxxxxxxxxx..........xxxxxxxxxxxxxxxxxxxxxx.......... &dA &dA &d@ xxxxxxxxxx......xxxxxxxxxx......xxxxxxxxxx......xxxxxxxxxx...... &dA &dA &d@ xxxx....xxxx....xxxx....xxxx....xxxx....xxxx....xxxx....xxxx.... &dA &dA &d@ xx..xx..xx..xx..xx..xx..xx..xx..xx..xx..xx..xx..xx..xx..xx..xx.. &dA &dA &d@ &dA &dA &d@ &dA &dA &d@ Addressing the question of the note restrike feature, I &dA &dA &d@ would suggest that a fixed value be used, which would depend &dA &dA &d@ on the number of midi tics per minute. As a first pass, I &dA &dA &d@ would suggest the restrike pause be set at 1/960th minute, &dA &dA &d@ but also no longer than a dotted 32nd note. &dA &dA &d@ &dA &dA &d@ Where g is the desired tempo in quarter notes per minute, and &dA &dA &d@ mtpq is the number of midi tics per quarter note, &dA &dA &d@ &dA &dA &d@ f = g * mtpq / 960 &dA &dA &d@ if f > mtpq * 3 / 32 &dA &dA &d@ f = mtpq * 3 / 32 &dA &dA &d@ end &dA &dA &d@ &dA &dA &d@ Some representative values: &dA &dA &d@ &dA &dA &d@ g mtpq f ~dur &dA &dA &d@ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ &dA &dA &d@ 30 240 7 128th &dA &dA &d@ 40 168 7 dotted 128th &dA &dA &d@ 50 144 7 dotted 128th &dA &dA &d@ 60 120 7 64th &dA &dA &d@ 70 120 8 64th &dA &dA &d@ 80 96 8 dotted 64th &dA &dA &d@ 100 96 9 dotted 64th &dA &dA &d@ 120 72 6 dotted 64th &dA &dA &d@ 150 72 6 dotted 64th &dA &dA &d@ 180 48 4 dotted 64th &dA &dA &d@ &dA &dA &d@ As mentioned above, the restrike "pause" should be &dA &dA &d@ normally be a constant, but should never be longer than &dA &dA &d@ 1/2 the duration of the shortest note currently sounding, &dA &dA &d@ and should never be longer that the distance back to a &dA &dA &d@ prior (unexecuted) note-off for the sounding note. &dA &dA &d@ &dA &dA &d@ &dA &dA &d@ I think we are now ready to go ahead with the programming! &dA &dA &d@ &dA &dA &d@ Step 1: place all start and stop events in the array &dA &dA &d@ &dA &dA &d@ Step 2: for each note, determine "restrike" situations &dA &dA &d@ and "non-executable" note off situations. Modify &dA &dA &d@ the array. &dA &dA &d@ &dA &dA &d@ Step 3: Read the array one midi tic at a time. Generate &dA &dA &d@ MIDI events. &dA &dA &d@ &dA &dA &dA &dA &d@ &dLÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»&d@ &dA &d@ &dLº Music Compile section º&d@ &dA &d@ &dLÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ&d@ &dA MUSIC_COMPILE: &dA &dA &d@ We are now going to assemble a genuine "part" track for this piece &dA if midiform = 1 ++ppntr music{ppntr,4} = "MTrk" ppntr += 4 music{ppntr,4} = ch4(0) /* to be filled later len_ppntr = ppntr ppntr += 4 music{ppntr} = chr(0) /* 0 time ++ppntr music{ppntr,2} = ch2(0xff03) /* ff03 = sequence or track name ppntr += 2 &dA &dA &d@ Construct name of part &dA temp = "" loop for i = 1 to trkcnt if midiass(i) = channel if temp = "" temp = trm(inst(i)) else inst(i) = trm(inst(i)) &dA &dA &d@ Look for case where numbers are used &dA k = 10000 if len(temp) = len(inst(i)) k = 0 loop for j = 1 to len(temp) if temp{j} <> inst(i){j} if k > 0 k = 10000 j = 10000 end k = j end repeat if k < 10000 if k > 1 and temp{k-1} = " " temp = temp // " " temp = temp{1,k} // " & " // inst(i){k} // temp{k+1..} temp = trm(temp) else k = 10000 end end else &dA &dA &d@ Look for case where Roman numerals are used &dA x = 0 temp = temp // pad(32) inst(i) = inst(i) // pad(32) loop for j = 1 to len(temp) if temp{j} <> inst(i){j} k = j /* first mismatch j = 10000 else if temp{j} = " " x = j /* last blank before mismatch end end repeat if x > 3 and k > 4 and k < 10000 loop for j = x + 1 to len(temp) repeat while "IVX" con temp{j} loop for f = x + 1 to len(inst(i)) repeat while "IVX" con inst(i){f} if temp{j} = " " and inst(i){f} = " " /* tricky code temp = temp{1,j} // "&" // inst(i){x..f} // temp{j+1..} temp = trm(temp) else k = 10000 end else k = 10000 end end if k = 10000 temp = trm(temp) temp = temp // " & " // inst(i) temp = trm(temp) end end end repeat i = len(temp) music{ppntr} = chr(i) /* length of name of track ++ppntr music{ppntr,i} = temp ppntr += i putc ~temp putc ... else ++ppntr end music{ppntr} = chr(0) /* 0 time ++ppntr i = 0xc0 + channel - 1 music{ppntr} = chr(i) /* Program change + channel ++ppntr i = chanst(channel,1) - 1 music{ppntr} = chr(i) /* instrument number (- 1) &dA &dA &d@ Now it is time to "lay down" the music! &dA x = 0 /* offset counter fsize = xtcnt xtcnt = 0 elapsed_time = 0 /* time since last event sicode = 1 /* start with primary instrument time_cnt = 0 loop for mcount = 1 to 2000 loop for i = 1 to 127 active_pitch(i) = 0 repeat ++xtcnt tget [X,xtcnt] line if line{1} = "E" goto END end glob_divspq = int(line{3..}) glob_divspm = int(line{sub+1..}) tinc = mtpq * 1000 / glob_divspq /* 1000ths of midi tics per global division * compile data for measure /* initialize &dA &dA &d@ 1. Set up data accumulation array &dA msize = glob_divspm * mtpq / glob_divspq /* midi tics in measure if rem <> 0 putc Possible program bug in determining divisions per measure putc putc divisions per measure = ~glob_divspm putc divisions per quarter = ~glob_divspq putc midi tics per quarter = ~mtpq putc measure number = ~mcount putc putc This program failure should be reported putc putc &dAProgram Halted&d@ putc stop end ++msize /* include beginning of next measure loop for i = 1 to msize measdata(i) = zpd(255) /* byte 255 is for new velocity repeat &dA &dA &d@ 2. Place all start-note and stop-note event in the array &dA &d@ &dA &d@ We use "BCD" to indicate the spelling of the pitch as &dA &d@ part of the note-on information. We use "a" as stop note. &dA X_REC: ++xtcnt tget [X,xtcnt] line /* rec if line{1} = "D" divpoint = int(line{2..}) if divpoint > 0 --divpoint divpoint *= tinc /* thousandths of midi tics from beginning of measure tdivpoint = divpoint divpoint += 500 divpoint /= 1000 /* midi tics from beginning of measure ++divpoint /* array location for "note on" events X_VEL: if line{sub..} con "V" newvel = int(line{sub+2..}) if newvel < 1 newvel = 1 /* use "1" as a flag for "0" end if newvel > 255 newvel = 255 end measdata(divpoint){255} = chr(newvel) goto X_VEL end X_NOTE: if line{sub..} con ['N','P'] if line{sub} = "N" icode = 0 else icode = 4 end tpitch = int(line{sub+1..}) tdur = int(line{sub+1..}) savesub1 = sub perform fix_pitch (tpitch, spell_code) i = ors(measdata(divpoint){tpitch}) & 0xf8 i += icode i += spell_code i += 0x08 measdata(divpoint){tpitch} = chr(i) /* note-on active_pitch(tpitch) = 1 &dA &dA &d@ Look for tie flag &dA if tdur & 0x8000 > 0 tdur &= 0x7fff &dA &dA &d@ Deal with ties here &dA tdur *= tinc tdur += tdivpoint tdur += 500 tdur /= 1000 ++tdur testfor tdur > msize putc Program error on note duration: measure = ~mcount stop &dK &d@ else (=) &dK &d@ putc Tie to end of measure measure = ~mcount Do not turn off else (<) &dA &dA &d@ In the case where there is a tie to another note inside the measure &dA &d@ you should decrease the note-on flag for the division where the &dA &d@ tied note ends, in anticipation that it will be increased again &dA &d@ when recipient of the tie is encountered. &dA i = ors(measdata(tdur){tpitch}) - 0x08 measdata(tdur){tpitch} = chr(i) /* decrease note-on active_pitch(tpitch) = 1 end else &dA &dA &d@ Compute articulation factor &dA f = 1000 / art_factor if tdur > glob_divspq / 2 f *= 4 f /= 5 end if tdur > glob_divspq f *= 4 f /= 5 end if tdur > glob_divspq * 2 f *= 4 f /= 5 end f = 1000 - f tdur *= tinc &dA &dA &d@ Add articulation factor &dA tdur *= f tdur /= 1000 if tdur = 0 tdur = 1 end tdur += tdivpoint tdur += 500 tdur /= 1000 ++tdur testfor tdur > msize putc Program error on note duration: measure = ~mcount stop end i = ors(measdata(tdur){tpitch+127}) + 0x08 measdata(tdur){tpitch+127} = chr(i) /* note-off active_pitch(tpitch) = 1 end sub = savesub1 goto X_NOTE end else putc divpoint = ~divpoint putc Do nothing at the moment end goto X_REC end if line{1} = "M" saveline = line line = line // " " i = int(line{3..}) if i > 0 j = i / 20 if rem = 0 putc ~i ... end end perform multi_note perform add_measure j = glob_divspm * 240 / glob_divspq time_cnt += j end if line{1} = "E" goto END end repeat END: putc ~saveline &dA &dA &d@ Add end of track &dA ++ppntr music{ppntr} = chr(0) /* 0 time ++ppntr music{ppntr,2} = ch2(0xff2f) /* end of track ppntr += 2 music{ppntr} = chr(0) if midiform = 1 &dA &dA &d@ Compute and insert length of this track &dA i = ppntr - len_ppntr - 3 music{len_ppntr,4} = ch4(i) end XCHANN: repeat k = 0 loop for channel = 1 to 16 if chanpntr(channel) <> 0 ++k end repeat &dA &dA &dA &d@ Construct MIDI file &dA &dA play_size = ppntr if midiform = 1 putc Constructing MIDI1 format &dA &d@ Header chunk: &dA4d54 6864&d@ &dI0000 0006&d@ &dK0001&d@ &dK0000&d@ &dK00f0&d@ &dA &d@ "MThd" ch4(6) 1 # 240 &dA &dA &d@ 1 = MIDI1 format &dA &d@ 6 = six track chunks in this file &dA &d@ 240 = 240 units (divisions) per quarter note ppntr = 1 music{ppntr,4} = "MThd" ppntr += 4 music{ppntr,4} = ch4(6) ppntr += 4 music{ppntr,2} = ch2(1) /* MIDI 1 ppntr += 2 music{ppntr,2} = ch2(k+3) /* Number of tracks ppntr += 2 music{ppntr,2} = ch2(240) /* Midi tics per quarter note ppntr += 2 &dA &d@ Track chunk1: &dA4d54 726b&d@ &dI0000 001c&d@ &dA &d@ "MTrk" ch4(28) &dA &dA &d@ &dL00&d@ &dGff5804&d@ ff58 = time signature 04 = length 4 &dA &d@ t &dA &d@ &dK0402&d@ 4 / 4 time 2 &dA &dA &d@ &dK1808&d@ 24 MIDI ckocks per metronome; 2 &dA &d@ 8 32nd notes per quarter. &dA &dA &d@ &dL00&d@ &dGff5103&d@ ff51 = tempo setting 03 = length 4 &dA &d@ t &dA &d@ &dK0e4e1c&d@ = 937,500 u-secs per q. (q = 64.00) 3 &dA &dA &d@ &dL00&d@ &dGff5405&d@ ff54 = SMPTE value 05 = length 4 &dA &d@ t &dA &d@ &dK40 0000 0000&d@ = start track after 64 hours! 5 &dA &dA &d@ &dL00&d@ &dGff2f00&d@ ff2f = end of track 00 = length 4 &dA &d@ t ÄÄÄÄ &dA &d@ 28 last_time_out = 0 music{ppntr,4} = "MTrk" ppntr += 4 len_ppntr = ppntr music{ppntr,4} = ch4(0) /* length to be filled in later ppntr += 4 delta_time = para_change(1,1) - last_time_out /* probably 0 last_time_out = para_change(1,1) /* probably 0 perform get_xtime (delta_time) k = len(xtime) music{ppntr,k} = xtime /* time inc (probably 0) ppntr += k &dK &d@ music{ppntr} = chr(0) /* time inc = 0 &dK &d@ ++ppntr music{ppntr,2} = ch2(0xff58) /* ff58 = time signature ppntr += 2 music{ppntr} = chr(4) ++ppntr music{ppntr,2} = ch2(para_change(1,2)) /* first time sig ppntr += 2 music{ppntr,2} = ch2(0x1808) /* 24 MIDI ckocks per metronome; ppntr += 2 /* 8 32nd notes per quarter. music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff51) /* ff51 = tempo setting ppntr += 2 music{ppntr} = chr(3) ++ppntr i = 60000000 / bpm temp = ch4(i) music{ppntr,3} = temp{2,3} /* u-sec per quarter note ppntr += 3 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff54) /* ff54 = SMPTE value ppntr += 2 music{ppntr} = chr(5) ++ppntr music{ppntr} = chr(0x40) ++ppntr music{ppntr,4} = ch4(0) ppntr += 4 if pc_cnt > 1 loop for i = 2 to pc_cnt delta_time = para_change(i,1) - last_time_out /* time increment last_time_out = para_change(i,1) /* absolute time perform get_xtime (delta_time) k = len(xtime) music{ppntr,k} = xtime /* time increment ppntr += k if para_change(i,3) = 1 /* time signature music{ppntr,2} = ch2(0xff58) /* ff58 = time signature ppntr += 2 music{ppntr} = chr(4) ++ppntr music{ppntr,2} = ch2(para_change(i,2)) /* time sig ppntr += 2 music{ppntr,2} = ch2(0x1808) /* 24 MIDI ckocks per metronome; ppntr += 2 /* 8 32nd notes per quarter. else if para_change(i,3) = 2 /* tempo change music{ppntr,2} = ch2(0xff51) /* ff51 = tempo setting ppntr += 2 music{ppntr} = chr(3) ++ppntr p = 60000000 / para_change(i,2) temp = ch4(p) music{ppntr,3} = temp{2,3} /* u-sec per quarter note ppntr += 3 end end repeat end music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff2f) /* ff2f = end of track ppntr += 2 music{ppntr} = chr(0) ++ppntr i = ppntr - len_ppntr - 4 music{len_ppntr,4} = ch4(i) /* now put in length &dA &d@ Track chunk2: &dA4d54 726b&d@ &dI0000 0021&d@ &dA &d@ "MTrk" ch4(33) &dA &dA &d@ &dL00&d@ &dGff030e&d@ ff03 = sequence or track name 0e = len 4 &dA &d@ t &dA &d@ "(C) 1994 CCARH" 14 &dA &dA &d@ &dL00&d@ &dEc040&d@ c040 = instrument choice 3 &dA &d@ t &dA &dA &d@ &dL00&d@ &dE994001&d@ 9940 = note 40 on channel 10 4 &dA &d@ t &dA &dA &d@ &dL01&d@ &dE894000&d@ 8940 = note 40 off channel 10 4 &dA &d@ t &dA &dA &dA &d@ &dL7f&d@ &dGff2f00&d@ ff2f = end of track 00 = length 4 &dA &d@ t ÄÄÄÄ &dA &d@ 33 music{ppntr,4} = "MTrk" ppntr += 4 music{ppntr,4} = ch4(33) /* length = 33 ppntr += 4 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff03) /* ff03 = sequence or track name ppntr += 2 music{ppntr} = chr(14) ++ppntr music{ppntr,14} = "(C) 1993 CCARH" /* message ppntr += 14 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xc040) /* c040 = instrument choice ppntr += 2 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0x9940) /* 9940 = note 40 on channel 10 ppntr += 2 music{ppntr} = chr(1) /* velocity = 1 ++ppntr music{ppntr} = chr(01) /* time inc = 01 tics ++ppntr music{ppntr,2} = ch2(0x8940) /* 8940 = note 40 on channel 10 ppntr += 2 music{ppntr} = chr(0) /* velocity = 0 ++ppntr music{ppntr} = chr(0x7f) /* time inc = 127 tics ++ppntr music{ppntr,2} = ch2(0xff2f) /* ff2f = end of track ppntr += 2 music{ppntr} = chr(0) ++ppntr &dA &d@ Track chunk3: &dA4d54 726b&d@ &dI0000 0016&d@ &dA &d@ "MTrk" ch4(22) &dA &dA &d@ &dL00&d@ &dGff0311&d@ ff03 = sequence or track name 11 = len 4 &dA &d@ t &dA &d@ "Lic.# ....... " 14 &dA &dA &d@ &dL00&d@ &dGff2f00&d@ ff2f = end of track 00 = length 4 &dA &d@ t ÄÄÄÄ &dA &d@ 22 music{ppntr,4} = "MTrk" ppntr += 4 music{ppntr,4} = ch4(22) /* length = 22 ppntr += 4 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff03) /* ff03 = sequence or track name ppntr += 2 music{ppntr} = chr(14) ++ppntr if midi_type = 1 music{ppntr,14} = "Lic.# ....... " else music{ppntr,14} = "Lic.# .......P" end ppntr += 6 license_loc = ppntr ppntr += 8 music{ppntr} = chr(0) /* time inc = 0 tics ++ppntr music{ppntr,2} = ch2(0xff2f) /* ff2f = end of track ppntr += 2 music{ppntr} = chr(0) &dA &dA &d@ Storing address of license_loc &dA music{LIC_ADD1} = chr(license_loc) /* store location for license number putc Output file? (enter blank line for default) getc line line = trm(line) if line = "" line = midioutfile end open [1,6] line outbuf = music{1,ppntr} write [1] outbuf i = play_size - 10000 x = 10001 loop while i > 0 if i > 1000 j = 1000 else j = i end outbuf = music{x,j} write [1] outbuf x += j i -= j repeat close [1] stop end &dA &dA &d@ &dA &dA &d@ &dA MIDI ZERO (collapsed) &dA &d@ &dA &dA if midiform = 3 putc Constructing MIDI1 format &dA &d@ Header chunk: &dA4d54 6864&d@ &dI0000 0006&d@ &dK0000&d@ &dK0001&d@ &dK00f0&d@ &dA &d@ "MThd" ch4(6) 0 1 240 &dA &dA &d@ 1 = MIDI1 format &dA &d@ 6 = six track chunks in this file &dA &d@ 240 = 240 units (divisions) per quarter note ppntr = 1 music{ppntr,4} = "MThd" ppntr += 4 music{ppntr,4} = ch4(6) ppntr += 4 music{ppntr,2} = ch2(0) /* MIDI 0 ppntr += 2 music{ppntr,2} = ch2(1) /* Number of tracks ppntr += 2 music{ppntr,2} = ch2(240) /* Midi tics per quarter note ppntr += 2 &dA &d@ Track chunk1: &dA4d54 726b&d@ &dI0000 0000&d@ (length to be filled later) &dA &d@ "MTrk" ch4(00) &dA &dA &d@ &dL00&d@ &dGff030e&d@ ff03 = sequence or track name 4 &dA &d@ t &dA &d@ "(C) 1993 CCARH" 14 &dA &dA &d@ &dL00&d@ &dGff03&dC0e&d@ ff03 = sequence or track name 4 &dA &d@ t &dA &d@ "Lic.# ....... " 14 &dA &dA &dA &d@ &dL00&d@ &dGff5804&d@ ff58 = time signature 04 = length 4 &dA &d@ t &dA &d@ &dK0402&d@ 4 / 4 time 2 &dA &dA &d@ &dK1808&d@ 24 MIDI ckocks per metronome; 2 &dA &d@ 8 32nd notes per quarter. &dA &dA &d@ &dL00&d@ &dGff5103&d@ ff51 = tempo setting 03 = length 4 &dA &d@ t &dA &d@ &dK0e4e1c&d@ = 937,500 u-secs per q. (q = 64.00) 3 &dA &dA &d@ &dL00&d@ &dGff5405&d@ ff54 = SMPTE value 05 = length 4 &dA &d@ t &dA &d@ &dK40 0000 0000&d@ = start track after 64 hours! 5 &dA &dA &d@ &dL00&d@ &dGff2f00&d@ ff2f = end of track 00 = length 4 &dA &d@ t &dA &d@ music{ppntr,4} = "MTrk" ppntr += 4 len_ppntr = ppntr music{ppntr,4} = ch4(0) /* length to be filled in later ppntr += 4 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff03) /* ff03 = sequence or track name ppntr += 2 music{ppntr} = chr(14) ++ppntr music{ppntr,14} = "(C) 1993 CCARH" /* message ppntr += 14 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff03) /* ff03 = sequence or track name ppntr += 2 music{ppntr} = chr(14) ++ppntr music{ppntr,14} = "Lic.# ....... " /* license number ppntr += 6 license_loc = ppntr ppntr += 8 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff58) /* ff58 = time signature ppntr += 2 music{ppntr} = chr(4) ++ppntr music{ppntr,2} = ch2(para_change(1,2)) /* first time sig ppntr += 2 music{ppntr,2} = ch2(0x1808) /* 24 MIDI ckocks per metronome; ppntr += 2 /* 8 32nd notes per quarter. music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff51) /* ff51 = tempo setting ppntr += 2 music{ppntr} = chr(3) ++ppntr i = 60000000 / bpm temp = ch4(i) music{ppntr,3} = temp{2,3} /* u-sec per quarter note ppntr += 3 music{ppntr} = chr(0) /* time inc = 0 ++ppntr music{ppntr,2} = ch2(0xff54) /* ff54 = SMPTE value ppntr += 2 music{ppntr} = chr(5) ++ppntr music{ppntr} = chr(0x40) ++ppntr music{ppntr,4} = ch4(0) ppntr += 4 &dA &dA &d@ Storing address of license_loc &dA music{LIC_ADD0} = chr(license_loc) /* store location for license number if LIC_ADD0 <> ppntr - 1 putc Counting error end i = ppntr - len_ppntr - 4 + play_size - 10000 music{len_ppntr,4} = ch4(i) /* now put in length of track putc Output file? (enter blank line for default) getc line line = trm(line) if line = "" line = midioutfile end open [1,6] line outbuf = music{1,ppntr-1} write [1] outbuf i = play_size - 10000 x = 10001 loop while i > 0 if i > 1000 j = 1000 else j = i end outbuf = music{x,j} write [1] outbuf x += j i -= j repeat close [1] stop end stop &dAÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» &dAº P R O C E D U R E S º &dAÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ * procedure decode (y) int i, y if "CDEFGAB" con pitch{1} y = mpt - 1 * 6 + 3 if mpt > 3 --y end /* 3 <= y <= 38 i = 2 GG: if "f*#" con pitch{i} y = y - 2 + mpt ++i goto GG end i = int(pitch{i}) i *= 40 y += i end y += transpose passback y return * &dA &dA &d@ Procedure: fix_pitch &dA &dA &d@ Input: int tpitch base 40 pitch number: 163 = C4 &dA &dA &d@ Output: int tpitch MIDI pitch number: 60 = C4 &dA &d@ int spell_code 1 = spell toward the flats &dA &d@ 2 = spell like key of F &dA &d@ 3 = spell toward the sharps &dA &dA &d@ The following table shows how spell_code is determined: &dA &dA &d@ pitch  60 61 62 63 64 65 66 67 68 69 70 71 &dA &d@ ÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ &dA &d@ code=1 ³ D D E F F G G A A B C C &dA &d@ ³ &dA &d@ code=2 ³ C C D E E F F G G A B B &dA &d@ ³ &dA &d@ code=3 ³ B B C D D E E F F G A A &dA procedure fix_pitch (tpitch, spell_code) int range,tpitch,spell_code getvalue tpitch range = tpitch - 1 / 40 range *= 12 tpitch = rem + 1 if midi_type = 2 spell_code = spelling(tpitch) else spell_code = 0 end tpitch = convert(tpitch) + range passback tpitch, spell_code return * procedure putmar str temp.300,temp2.300,temp3.80 int i,j,k,oldtcnt,pitch,pitch2,dur,dur2,savej int ii,jj,kk,savesub2,savesub3,savesub4,savesub5 str lett.1 oldtcnt = tcnt loop for i = 1 to divcount if measdata(i) <> "" ++tcnt tput [Y,tcnt] D~i ~measdata(i) measdata(i) = "" end repeat &dA &dA &d@ Fix ties internal to the measure &dA if tcnt < oldtcnt + 2 return end loop for i = oldtcnt + 1 to tcnt tget [Y,i] temp PM_LOOK: j = int(temp{2..}) savej = j PM_LOOK1: savesub1 = sub if temp{savesub1+1..} con ['N','P'] savesub5 = sub lett = temp{sub} pitch = int(temp{sub+1..}) dur = int(temp{sub+1..}) savesub2 = sub &dA &dA &d@ Addition: we need to save any grace note info that might have &dA &d@ preceded this note; or any tempo changes; or any meter changes &dA temp3 = "" if temp{savesub1+1..} con ['G','J','V','W','T'] if sub < savesub5 temp3 = temp{sub..savesub5-1} end end sub = savesub2 if dur > 0x8000 dur &= 0x7fff j += dur loop for k = i + 1 to tcnt tget [Y,k] temp2 jj = int(temp2{2..}) if jj = j PM_LOOK2: if temp2{sub+1..} con ['N','P'] savesub3 = sub pitch2 = int(temp2{sub+1..}) dur2 = int(temp2{sub+1..}) if pitch2 = pitch /* tie it here (if no ornament follows) savesub4 = sub if sub >= len(temp2) or "trw~cmj" not_con temp2{sub+1} temp2 = temp2 // " " temp2 = temp2{1..savesub3-1} // temp2{savesub4+1..} temp2 = trm(temp2) if len(temp2) < 7 temp2 = temp2 // " D" end tput [Y,k] ~temp2 dur += dur2 temp2 = temp{1..savesub1} // temp3 temp2 = temp2 // lett // chs(pitch) temp = temp2 // "," // chs(dur) // temp{savesub2..} tput [Y,i] ~temp goto PM_LOOK end end sub = savesub3 + 1 goto PM_LOOK2 end else if jj > j k = tcnt end end repeat end sub = savesub2 j = savej goto PM_LOOK1 end repeat return procedure showarr str temp.300 int i,j,k loop for i = 1 to tcnt tget [Y,i] temp if temp{1} = "D" j = int(temp{2..}) temp = temp{sub..} // " " putc .w4 ~j ... loop for j = 1 to len(temp) if "NP" con temp{j} putc ~temp{j} ... ++j k = int(temp{j..}) putc ~k ... j = sub + 1 k = int(temp{j..}) putc ~k ... j = sub goto XJ end if "GJHK" con temp{j} putc ~temp{j} ... ++j k = int(temp{j..}) putc ~k ... j = sub + 1 k = int(temp{j..}) putc ~k ... j = sub goto XJ end if temp{j+1} = "=" if "trw~cmjg" con temp{j} putc ~temp{j} ... if temp{j+1..} con ";" out = temp{j+1..sub-1} putc ~out ... end j = sub goto XJ end end if temp{j} = "T" putc ~temp{j,2} ... j += 2 k = int(temp{j..}) putc ~k /... j = sub + 1 k = int(temp{j..}) putc ~k ... j = sub goto XJ end XJ: repeat putc else putc ~temp end repeat return &dA &dA &d@ Procedures from Compile Program &dA * &dA ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ &dA &dA &d@ Here is where you determine "restrike" situations &dA &d@ and "non-executable" note off situations. &dA procedure multi_note int t1 loop for i = 1 to 127 if active_pitch(i) = 0 and notes_on(i) = 0 goto SHORTCUT end loop for p = 1 to msize speeddata(p) = ors(measdata(p){i}) speeddata2(p) = ors(measdata(p){i+127}) repeat &dA &dA &d@ Deal with first division in the measure &dA j = speeddata(1) >> 3 k = j - notes_on(i) testfor k < 0 putc putc Funny notes ending at beginning of measure on pitch ~i . It is possible putc that somewhere earlier in the piece, this pitch has a non-terminating putc tie. It is worth looking. speeddata(1) = 32 else (=) speeddata(1) = 32 else (>) speeddata(1) &= 0x07 /* code speeddata(1) += 65 /* + "A" end notes_on(i) = j note_is_on = j &dA &dA &d@ Look for cases where notes are being struck within a certain time after &dA &d@ being turned off. You are going to move the turn-off time back, &dA &d@ but you need to make sure that you don't get it too close to some &dA &d@ previous turn-on time. The "certain time" referred to above is &dA &d@ currently set equal to the restrike_pause. &dA save_restrike_pause = restrike_pause BACK_LOOK2: restrike_pause = save_restrike_pause loop for j = 1 to tempo_cnt if time_cnt = tempo_change(j,1) &dK &d@ putc New tempo at ~tempo_change(j,1) = ~tempo_change(j,2) restrike_pause = tempo_change(j,2) * mtpq / 960 if restrike_pause > max_restrike_pause restrike_pause = max_restrike_pause end &dK &d@ putc Setting restrike_pause = ~restrike_pause end repeat loop for p = 2 to msize loop for j = 1 to tempo_cnt if time_cnt + p - 1 = tempo_change(j,1) &dK &d@ putc New tempo at ~tempo_change(j,1) = ~tempo_change(j,2) restrike_pause = tempo_change(j,2) * mtpq / 960 if restrike_pause > max_restrike_pause restrike_pause = max_restrike_pause end &dK &d@ putc Setting restrike_pause = ~restrike_pause end repeat j = speeddata(p) if j > 0 /* note struck at "p" save_s = 1 BACK_LOOK: loop for q = p to save_s + 1 step -1 if p - q >= restrike_pause q = 0 else k = speeddata2(q) if k > 0 /* note turned off at "q" /* within "restrike_pause" of "p" loop for r = q-1 to 1 step -1 if q - r >= restrike_pause * 5 / 4 /* formerly restrike_pause * 2 s = p - restrike_pause r = 0 else h = speeddata(r) if r > 1 if h > 0 s = p + r / 2 r = 0 end else if h <> 32 s = p + r / 2 r = 0 end end end repeat /* "s" is best compromise location if s > 1 and q - s > 0 h = speeddata2(s) & 0xf8 /* contents of destination "s" h += k /* add contents of "q" speeddata2(s) = h /* store in "s" speeddata2(q) = 0 /* clear the contents of "q" if q = p and q - s < 2 goto BACK_LOOK2 /* special case where note-off and note-on end /* occurred simultaneously if save_s = 1 save_s = s end goto BACK_LOOK end q = 0 end end repeat end repeat &dA &dA &d@ Look for not executable "note-offs"; determine final &dA &d@ value of notes_on(.) for this pitch in this measure &dA &d@ Convert to "letter system" of note on and note off &dA loop for p = 2 to msize j = speeddata2(p) h = j >> 3 if h = 0 speeddata2(p) = 32 else k = notes_on(i) - h testfor k < 0 putc program error: trying to turn off more notes than are currently on putc pitch ~i examine stop else (=) j = 97 /* "a" always for stop speeddata2(p) = j else (>) speeddata2(p) = 32 end notes_on(i) = k end j = speeddata(p) h = j >> 3 notes_on(i) += h &dA &dA &d@ Convert to letter system of note-on/note-off &dA if h > 0 j &= 0x07 j += ors("A") speeddata(p) = j else speeddata(p) = speeddata2(p) end repeat &dA &dA &d@ Make sure "restruck" notes are turned off first &dA &d@ "note_is_on" flag for first division has been set above &dA restrike_pause = save_restrike_pause loop for j = 1 to tempo_cnt if time_cnt = tempo_change(j,1) &dK &d@ putc New tempo at ~tempo_change(j,1) = ~tempo_change(j,2) restrike_pause = tempo_change(j,2) * mtpq / 960 if restrike_pause > max_restrike_pause restrike_pause = max_restrike_pause end &dK &d@ putc Setting restrike_pause = ~restrike_pause end repeat loop for p = 2 to msize loop for j = 1 to tempo_cnt if time_cnt + p - 1 = tempo_change(j,1) &dK &d@ putc New tempo at ~tempo_change(j,1) = ~tempo_change(j,2) restrike_pause = tempo_change(j,2) * mtpq / 960 if restrike_pause > max_restrike_pause restrike_pause = max_restrike_pause end &dK &d@ putc Setting restrike_pause = ~restrike_pause end repeat s = speeddata(p) if s >= 65 and s <= 72 /* if "ABCDEFGH" con measdata(p){i} if note_is_on > 0 /* this is a "restruck" note at "p" r = 1 loop for q = p-1 to 2 /* find out where it was struck ("r") s = speeddata(q) if s >= 65 and s <= 72 /* if "ABCDEFGH" con measdata(q){i} r = q q = 0 end repeat if p - r >= 2 * restrike_pause s = p - restrike_pause /* back up "restrike_pause" else s = p + r / 2 /* half way between "p" and "r" end speeddata(s) = 97 /* "a" = turn this note off at "s" else note_is_on = 1 end end s = speeddata(p) if s >= 97 and s <= 104 /* if "abcd" con measdata(p){i} note_is_on = 0 end repeat loop for p = 1 to msize measdata(p){i} = chr(speeddata(p)) repeat SHORTCUT: repeat loop for i = 1 to msize if measdata(i) con ['A'..'H','a'..'d'] if measdata(i){255} <> chr(0) measdata(i) = measdata(i){1,127} // measdata(i){255} else measdata(i) = trm(measdata(i){1,127}) end else measdata(i) = "" end s = 0 loop for j = 1 to tempo_cnt if time_cnt + i - 1 = tempo_change(j,1) s = tempo_change(j,2) end repeat if s > 0 restrike_pause = s * mtpq / 960 if restrike_pause > max_restrike_pause restrike_pause = max_restrike_pause end &dK &d@ putc restrike_pause set to ~restrike_pause end repeat return * &dA ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ procedure add_measure int lastpoint int i, j, k, q lastpoint = 1 OUT_LOOP: &dA &dA &d@ Find an event &dA loop for p = lastpoint to msize repeat while measdata(p) = "" if p = msize and measdata(p) = "" elapsed_time += (msize - lastpoint) return end elapsed_time += p - lastpoint &dA &dA &d@ Put out elapsed_time &dA perform get_xtime (elapsed_time) ++ppntr i = len(xtime) music{ppntr,i} = xtime /* elapsed time ppntr += i &dA &dA &d@ Put out events &dA k = 0 if len(measdata(p)) = 128 newvel = ors(measdata(p){128}) if newvel = 1 newvel = 0 end measdata(p) = trm(measdata(p){1,127}) if midi_type <> 2 curvel = 90 * newvel / 100 if curvel > 127 curvel = 127 end end end loop for i = 1 to len(measdata(p)) if "ABCDEFGHa" con measdata(p){i} icode = sicode if mpt < 9 icode = mpt - 1 / 4 + 1 spcode = rem + 1 end if icode <> sicode /* this is a patch change j = channel - 1 j += 0xc0 q = chanst(channel,icode) - 1 if midiform <> 3 /* no patch changes in collapsed MIDI0 if k = 0 music{ppntr} = chr(j) /* patch change ++ppntr music{ppntr} = chr(q) /* instrument number (- 1) ++ppntr music{ppntr} = chr(0) /* new elapsed time ++ppntr else ++ppntr music{ppntr} = chr(0) /* new elapsed time ++ppntr music{ppntr} = chr(j) /* patch change ++ppntr music{ppntr} = chr(q) /* instrument number (- 1) end end sicode = icode end if k = 0 k = 1 else ++ppntr music{ppntr} = chr(0) /* elapsed time ++ppntr end j = channel - 1 if mpt < 9 /* note on j += 0x90 else /* note off j += 0x80 end music{ppntr} = chr(j) /* MIDI note command ++ppntr music{ppntr} = chr(i) /* pitch ++ppntr if midi_type = 2 if mpt < 9 /* note on music{ppntr} = chr(87+spcode) /* attack else music{ppntr} = chr(0x00) /* attack end else if mpt < 9 /* note on music{ppntr} = chr(curvel) /* attack else music{ppntr} = chr(0x00) /* attack end end end repeat if p = msize elapsed_time = 0 return end lastpoint = p + 1 if lastpoint > msize elapsed_time = 0 return else elapsed_time = 1 end goto OUT_LOOP return * &dA ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ procedure add_dur (a) int a getvalue a if a < 10 rec = rec // " " else if a < 100 rec = rec // " " end end rec = rec // chs(a) return procedure remove_grace_data int i,k str temp.100 rec2 = rec2 // " " if rec2 con "C1:" temp = rec2{mpt+3..} // " " rec2 = rec2{1,mpt-1} k = 1 RGD1: if "pfm" con temp{k} temp = temp{1,k-1} // temp{k+1..} goto RGD1 end if temp{k} = "t" i = int(temp{k+1..}) temp = temp{1,k-1} // temp{sub+1..} goto RGD1 end if temp{k} <> " " ++k goto RGD1 end if temp{1} = " " rec2 = rec2 // temp else rec2 = rec2 // "C1:" // temp end end rec2 = trm(rec2) return procedure get_xtime (a) int a,b getvalue a if a < 0x80 xtime = chr(a) return end if a < 0x4000 b = a >> 7 + 0x80 a &= 0x7f xtime = chr(b) // chr(a) return end xtime = "" b = a >> 21 if b > 0 b += 0x80 xtime = chr(b) a &= 0x1fffff return end b = a >> 14 b += 0x80 xtime = xtime // chr(b) a &= 0x3fff b = a >> 7 b += 0x80 xtime = xtime // chr(b) a &= 0x7f xtime = xtime // chr(a) return &dA &dA &d@ From the standpoint of sound, we do not want to have $ records &dA &d@ in the position of &dApreceding&d@ measure lines. This is because &dA &d@ the information contained therein really applies to the &dAnext&d@ &dA &d@ measure, which because of repeats, etc. may not happen for &dA &d@ time. Therefore we need to have a way of "switching" measure &dA &d@ and $ records when they come in this order. For the moment, &dA &d@ I propose to do this by implementing a "look ahead" process &dA &d@ which essentially is always one record ahead of where the &dA &d@ program things it is. It will be the job of this procedure &dA &d@ to (1) hand off records to the program in the "corrected" &dA &d@ order, and (2) to terminate properly, i.e. don't generate &dA &d@ a branch to eof except in error type conditions. &dA procedure gxrec str newrec.100,temp.100,midrec.100 midrec = newrec if termflag = 0 GXR: getf [3] newrec if newrec{1} = "@" goto GXR end newrec = newrec // pad(4) if newrec{1,4} = "/END" or newrec{1,4} = "/FIN" termflag = 1 end else newrec = "/END" end if newrec{1} = "m" and midrec{1} = "$" rec = newrec newrec = midrec else if midrec{1} = "$" and newrec{1} = "S" and newrec con "C0:d" rec = newrec newrec = midrec else rec = midrec end end if rec{1} = "S" and rec con "C1:" and rec con "m" add_time_flag = 1 end return eof3: putc putc putc putc The file ~infile if eof3flag = 0 putc is supposed to belong to the "~group " group, but no record putc was found indicating the membership number in the group. putc This oversight must be corrected before this program can putc continue. else if eof3flag = 1 putc is incomplete (does not terminate properly). This oversight putc must be corrected before this program can continue. else putc has an improper format for a midi assignment file. The file putc must be corrected or deleted before this program can continue. end end putc putc &dAProgram Halted&d@ putc stop return run &dA &d@ &dA &dA &d@ &dA Lines for Debugging &dA &d@ &dA examine /* &dADEBUG&d@ dputc rec = ~rec /* &dADEBUG&d@