Unit LilyBiep; INTERFACE Uses Algemeen, MidiDoos; function basis(getal : integer) : integer; function macht(getal : integer) : integer; function MaxTime(MF : Midi) : integer; procedure TimeSigs(MF : Midi; max: integer; var slagen : gegevens); procedure Get_Measures(slagen : gegevens; var maat : gegevens); procedure KeySigs(MF : Midi; var sleutel : gegevens; verstek : integer); function LilyNote(noten : samen; mol : boolean) : string; function LilyTime(getal,tikken : integer; fout : boolean) : string; procedure LilyScore(tikken : integer; var noot : gegevens); function Lilyriek(MF : Midi; var punt : integer; var rij : gegevens) : boolean; procedure LilyText(tikken : integer; var rij : gegevens); function gelijk(een,twee : samen; vlgs : string) : boolean; function kopie(een : samen; vlgs : string) : samen; function kopieer(een : gegevens; vlgs : string) : gegevens; function Notatie(MF : Midi; var punt : integer; var noot : gegevens) : boolean; function BassKey(noot : gegevens) : boolean; function Key_information(MF : Midi; blah : boolean) : integer; procedure drukaf(noot: gegevens; vlgs : string); procedure Krimpen(var noot : gegevens); procedure Aanpassen(var noot : gegevens); function toch(getal,tikken : integer) : boolean; function Triool(getal,tikken : integer) : string; IMPLEMENTATION function basis(getal : integer) : integer; { Notelength base } var r,t : integer; begin t := getal; while true do begin r := t mod 2; if r > 0 then Break; t := t div 2; end; basis := t; end; function macht(getal : integer) : integer; { Notelength 2-th power } var r,t,m : integer; begin t := getal; m := 0; while true do begin r := t mod 2; if r > 0 then Break; t := t div 2; m := m + 1; end; macht := m; end; function MaxTime(MF : Midi) : integer; var aantal,punt,p,trk : integer; tijd,max : integer; OK : boolean; begin punt := 14; max := 0; for trk := 0 to MF.Tracks-1 do begin MF.Check_Track_Header(punt,aantal,OK); p := punt; tijd := 0; while p < punt+aantal do begin tijd := tijd + MF.Get_Delta_Time(p); MF.Skip_Any_Event(p); end; if max < tijd then max := tijd; punt := punt + aantal; end; MaxTime := max; end; procedure TimeSigs(MF : Midi; max: integer; var slagen : gegevens); { TimeSig } var aantal,punt,p,k,L : integer; trk,tel,tijd,T,N : integer; riedel : bytes; OK : boolean; begin punt := 14; tel := 0; for trk := 0 to MF.Tracks-1 do begin MF.Check_Track_Header(punt,aantal,OK); tijd := 0; p := punt; while p < punt+aantal do begin tijd := tijd + MF.Get_Delta_Time(p); MF.Data_Any_Event(p,riedel); { TimeSig only } if not (Length(riedel) = 6) then Continue; if not ((riedel[0] = $FF) and (riedel[1] = $58)) then Continue; SetLength(slagen,tel+1); slagen[tel].stamp:= tijd; SetLength(slagen[tel].event,1); slagen[tel].event[0] := $FD; slagen[tel].getal := MF.Ticks*4*riedel[2] div twee(riedel[3]); T := riedel[2]; N := riedel[3]; N := twee(N); slagen[tel].tekst := '\time ' + Letterlijk(T) + '/' + Letterlijk(N); tel := tel + 1; end; punt := punt + aantal; end; if tel = 0 then begin SetLength(slagen,tel+1); slagen[tel].stamp:= 0; SetLength(slagen[tel].event,1); slagen[tel].event[0] := $FD; slagen[tel].getal := MF.Ticks*4; slagen[tel].tekst := '\time 4/4'; Writeln('Default Time Signature 4/4 assumed'); end; Straight(slagen); tel := 1; L := Length(slagen); for k := 1 to L-1 do begin if slagen[k-1].stamp = slagen[k].stamp then Continue; slagen[tel] := kopie(slagen[k],'segt'); tel := tel + 1; end; SetLength(slagen,tel+1); slagen[tel].stamp := max; end; procedure Get_Measures(slagen : gegevens; var maat : gegevens); var p,tel,L,tijd : integer; begin { More than enough space for Measures } L := 2 * 3600; SetLength(maat,L); maat[0].stamp := 0; SetLength(maat[0].event,1); maat[0].event[0] := $FF; tel := 1; tijd := 0; L := Length(slagen); for p := 1 to L-1 do begin while tijd < slagen[p].stamp do begin tijd := tijd + slagen[p-1].getal; maat[tel].stamp := tijd; SetLength(maat[tel].event,1); maat[tel].event[0] := $FF; tel := tel + 1; end; end; SetLength(maat,tel); end; function LilyKey(sf,mi : byte) : string; const toon : array[0..1,-7..+7] of string = (('b','ges','des','as','es','bes','f' ,'c','g','d','a','e','b','fis','cis'), ('gis','es','bes','f','c','g','d' ,'a','e','b','fis','cis','gis','es','bes')); aard : array[0..1] of string = ('\major','\minor'); var getal : integer; begin getal := sf; if (sf and $F0) > 0 then getal := integer(sf)-$FF-1; LilyKey := '\key ' + toon[mi,getal] + ' ' + aard[mi]; end; procedure KeySigs(MF : Midi; var sleutel : gegevens; verstek : integer); { KeySig } const aard : array[0..11] of string = ('C','Des','D','Es','E','F','Fis','G','As','A','Bes','B'); voorteken : array[0..11] of integer = ( 0,-5,+2,-3,+4,-1,+6,+1,-4,+3,-2,+5 ); { c c# d d# e f f# g g# a a# b } var aantal,punt,p,k,L : integer; trk,tel,tijd,sf,G : integer; riedel : bytes; OK : boolean; begin punt := 14; tel := 0; for trk := 0 to MF.Tracks-1 do begin MF.Check_Track_Header(punt,aantal,OK); tijd := 0; p := punt; while p < punt+aantal do begin tijd := tijd + MF.Get_Delta_Time(p); MF.Data_Any_Event(p,riedel); { KeySig only } if not (Length(riedel) = 4) then Continue; if not ((riedel[0] = $FF) and (riedel[1] = $59)) then Continue; SetLength(sleutel,tel+1); sleutel[tel].stamp:= tijd; SetLength(sleutel[tel].event,1); sleutel[tel].event[0] := $FE; sleutel[tel].tekst := LilyKey(riedel[2],riedel[3]); sf := riedel[2]; G := integer(sf); if (sf and $F0) > 0 then G := integer(sf)-$FF-1; sleutel[tel].getal := G; tel := tel + 1; end; punt := punt + aantal; end; if tel = 0 then begin SetLength(sleutel,tel+1); sleutel[tel].stamp:= 0; SetLength(sleutel[tel].event,1); sleutel[tel].event[0] := $FE; G := voorteken[verstek]; if G > 0 then sf := byte(G) else sf := byte(G+$FF+1); sleutel[tel].tekst := LilyKey(sf,0); sleutel[tel].getal := G; Writeln('Default Key Signature ',aard[verstek],' major assumed'); end; Straight(sleutel); tel := 1; L := Length(sleutel); for k := 1 to L-1 do begin if sleutel[k-1].stamp = sleutel[k].stamp then Continue; sleutel[tel] := kopie(sleutel[k],'segt'); tel := tel + 1; end; SetLength(sleutel,tel); end; function LilyNote(noten : samen; mol : boolean) : string; const kruis : array[0..11] of string = ('c','cis','d','dis','e','f','fis','g','gis','a','ais','b'); bemol : array[0..11] of string = ('c','des','d','es','e','f','ges','g','as','a','bes','b'); var note : byte; boven,k,L,m : integer; Okt,t,s : string; begin s := ''; L := Length(noten.event); if L > 0 then for k := 0 to L-1 do begin note := noten.event[k]; boven := (note div 12) - 4; Okt := ''; if boven > 0 then begin for m := 1 to boven do Okt := Okt + ''''; end; if boven < 0 then begin for m := 1 to -boven do Okt := Okt + ','; end; if mol then t := bemol[note mod 12] + Okt else t := kruis[note mod 12] + Okt; s := s + t; if k < L-1 then s := s + ' '; end; if L = 0 then s := ''; LilyNote := s; end; function Eenheden(veel : integer) : string; var k : integer; S : string; begin S := ''; for k := 0 to veel-1 do S := S + ' #1'; Eenheden := S; end; function toch(getal,tikken : integer) : boolean; var k : integer; OK,geval : boolean; begin OK := ((tikken mod 3) = 0); geval := false; for k := 0 to 5 do geval := geval or ((4*tikken mod (3*twee(k)) = 0) and (getal = (4*tikken div (3*twee(k))))); toch := OK and geval; end; function Triool(getal,tikken : integer) : string; var k : integer; uit : string; begin uit := ''; for k := 0 to 5 do if getal = (4*tikken div (3*twee(k))) then uit := ' \times2/3{#' + Letterlijk(2*twee(k)) + '}'; Triool := uit; end; function LilyTime(getal,tikken : integer; fout : boolean) : string; var B,G,M,L,T,k,E,was : integer; ly : string; begin LilyTime := ''; if getal = 0 then Exit; B := basis(tikken); if ((getal mod B) > 0) and (not toch(getal,tikken)) then begin if fout then LilyTime := '^"Error"'; Exit; end; G := getal div B; M := macht(tikken); L := log2(G); ly := ''; T := 0; for k := L-1 downto 0 do begin was := T; T := (G shr k) and 1; if T = 1 then if M-k+2 >= 0 then begin E := twee(M-k+2); if (T = was) and (M-k+2 > 0) then ly := ly + '.' else ly := ly + ' #' + Letterlijk(E); end else ly := ly + Eenheden(twee(-(M-k+2))); end; if toch(getal,tikken) then ly := Triool(getal,tikken); LilyTime := Copy(ly,2,Length(ly)-1); end; procedure LilyScore(tikken : integer; var noot : gegevens); var k,L,t : integer; uit,ly : string; letop,verder,bemol : boolean; begin bemol := false; L := Length(noot); for k := 1 to L-1 do begin verder := (Length(noot[k-1].event) = 1); if verder then verder := (noot[k-1].event[0] > $F0); if verder then begin letop := (noot[k-1].event[0] = $FE); if letop then bemol := (noot[k-1].getal < 0); Continue; end; noot[k-1].tekst := ''; uit := LilyNote(noot[k-1],bemol); if Length(uit) = 0 then uit := 'r'; t := noot[k].stamp - noot[k-1].stamp; if t = 0 then Continue; ly := LilyTime(t,tikken,true); ly := vervang(ly,' ','~'); if Pos(' ',uit) > 0 then uit := '<' + uit + '>'; uit := vervang(ly,'#',uit); if gelijk(noot[k],noot[k-1],'e') and (Copy(uit,1,1) <> 'r') then uit := uit + '~'; if (noot[k-1].getal = 1) then uit := uit + '~'; uit := vervang(uit,'~~','~'); noot[k-1].tekst := uit; end; end; function gelijk(een,twee : samen; vlgs : string) : boolean; var OK : boolean; k,A,B : integer; S : set of char; begin S := []; for k := 1 to Length(vlgs) do S := S + [ char(vlgs[k]) ]; OK := true; if 's' in S then OK := OK and (een.stamp = twee.stamp); if 'e' in S then begin A := Length(een.event); B := Length(twee.event); OK := OK and (A = B); if OK then for k := 0 to B-1 do OK := OK and (een.event[k] = twee.event[k]); end; if 'g' in S then OK := OK and (een.getal = twee.getal); if 't' in S then OK := OK and (een.tekst = twee.tekst); gelijk := OK; end; function kopie(een : samen; vlgs : string) : samen; var twee : samen; k,L : integer; S : set of char; begin S := []; for k := 1 to Length(vlgs) do S := S + [ char(vlgs[k]) ]; if 's' in S then twee.stamp := een.stamp; if 'e' in S then begin L := Length(een.event); SetLength(twee.event,L); for k := 0 to L-1 do twee.event[k] := een.event[k]; end; if 'g' in S then twee.getal := een.getal; if 't' in S then twee.tekst := een.tekst; kopie := twee; end; function kopieer(een : gegevens; vlgs : string) : gegevens; var L,k : integer; twee : gegevens; begin L := Length(een); SetLength(twee,L); for k := 0 to L-1 do twee[k] := kopie(een[k],vlgs); kopieer := twee; end; function Notatie(MF : Midi; var punt : integer; var noot : gegevens) : boolean; var aantal,p,tel : integer; tijd,t : integer; riedel : bytes; noten : verzameling; OK,muziek,muzikaal : boolean; begin SetLength(riedel,0); Schoonmaken(noten); MF.Check_Track_Header(punt,aantal,OK); SetLength(noot,aantal); tel := 0; tijd := 0; muzikaal := false; p := punt; while p < punt+aantal do begin SetLength(riedel,0); noot[tel].stamp := tijd; riedel := Akkoord(noten,0); noot[tel].event := riedel; tel := tel + 1; t := MF.Get_Delta_Time(p); tijd := tijd + t; MF.Data_Any_Event(p,riedel); muziek := ((riedel[0] shr 4) = $8) or ((riedel[0] shr 4) = $9); muziek := muziek and not (riedel[0] and $0F = 9); if muziek then Cumulatief(riedel[0] and $F0 ,riedel[1],riedel[2],noten); if muziek then muzikaal := true; end; SetLength(noot,tel); punt := punt + aantal; Notatie := muzikaal; end; function BassKey(noot : gegevens) : boolean; var L,E,k,i,n : integer; hoog,laag : integer; bas : boolean; begin L := Length(noot); hoog := 0; laag := 0; for k := 0 to L-1 do begin E := Length(noot[k].event); for i := 0 to E-1 do begin n := noot[k].event[i]; if n > 59 then hoog := hoog + (n - 60); if n < 61 then laag := laag + (60 - n); end; end; bas := false; if laag > hoog then bas := true; BassKey := bas; end; procedure drukaf(noot: gegevens; vlgs : string); var L,k,N,i : integer; R : bytes; S : set of char; begin S := []; for k := 1 to Length(vlgs) do S := S + [ char(vlgs[k]) ]; SetLength(R,0); L := Length(noot); for k := 0 to L-1 do begin if 's' in S then Write('stamp = ',noot[k].stamp); if 'e' in S then begin R := noot[k].event; N := Length(R); Write('; event = '); for i := 0 to N-1 do Write(' ',R[i]); end; if 'g' in S then Write('; getal = ',noot[k].getal); if 't' in S then Write('; tekst = ',noot[k].tekst); Writeln; end; end; procedure Krimpen(var noot : gegevens); var tel,L,k : integer; vorig : samen; letop : boolean; begin L := Length(noot); tel := 0; for k := 1 to L-1 do begin vorig := kopie(noot[k-1],'se'); letop := (vorig.stamp = noot[k].stamp) and not (Length(vorig.event) = 0); if letop then Continue; noot[tel] := kopie(vorig,'se'); tel := tel + 1; end; noot[tel] := kopie(noot[L-1],'se'); SetLength(noot,tel+1); L := Length(noot); tel := 1; for k := 1 to L-1 do begin vorig := kopie(noot[k-1],'se'); letop := gelijk(vorig,noot[k],'e'); if letop then Continue; noot[tel] := kopie(noot[k],'se'); tel := tel + 1; end; SetLength(noot,tel); end; procedure Aanpassen(var noot : gegevens); var tel,L,k,tijd : integer; vorig : samen; letop : boolean; begin L := Length(noot); tel := 1; for k := 1 to L-1 do begin vorig := kopie(noot[k-1],'se'); letop := false; if Length(noot[k].event) > 0 then letop := (vorig.stamp = noot[k].stamp) and (noot[k].event[0] = $FF); if letop then Continue; noot[tel] := kopie(noot[k],'se'); tel := tel + 1; end; SetLength(noot,tel); L := Length(noot); for k := 0 to L-1 do noot[k].getal := 0; for k := 1 to L-1 do begin vorig := kopie(noot[k-1],'seg'); letop := false; if Length(noot[k].event) > 0 then letop := (noot[k].event[0] = $FF); tijd := noot[k].stamp; if letop then begin noot[k] := kopie(vorig,'eg'); noot[k].stamp := tijd; noot[k-1].getal := 1; end; end; L := Length(noot); for k := 1 to L-1 do begin letop := (Length(noot[k].event) = 0); if letop then Continue; noot[k-1].getal := 1; end; L := Length(noot); for k := 0 to L-1 do begin letop := (Length(noot[k].event) = 0); if letop then noot[k].getal := 0; end; L := Length(noot); for k := 1 to L-1 do begin letop := (noot[k-1].getal = 1) and (Length(Gemeen(noot[k].event,noot[k-1].event)) = 0); if letop then noot[k-1].getal := 0; end; end; function LilyGreep(riedel : bytes) : string; var k,L : integer; S : string; CRLF : boolean; begin L := Length(riedel); SetLength(S,L-2); for k := 2 to L-1 do S[k-1] := char(riedel[k]); L := L - 2; CRLF := (L > 1); if CRLF then CRLF := (S[L-1] = char(13)) and (S[L] = char(10)); if CRLF then SetLength(S,L-2); L := Length(S); if L > 1 then if not (S[L] = ' ') then S := S + '-' else S := Copy(S,1,L-1); LilyGreep := S; end; function Lilyriek(MF : Midi; var punt : integer; var rij : gegevens) : boolean; var p,t,aantal,tijd,tel : integer; riedel : bytes; greep : string; OK,zang : boolean; begin MF.Check_Track_Header(punt,aantal,OK); tijd := 0; tel := 0; SetLength(rij,aantal); p := punt; OK := false; while p < punt+aantal do begin t := MF.Get_Delta_Time(p); tijd := tijd + t; MF.Data_Any_Event(p,riedel); zang := (riedel[0] = $FF) and (riedel[1] = $05); if not zang then Continue; greep := LilyGreep(riedel); rij[tel].stamp := tijd; rij[tel].tekst := greep; tel := tel + 1; OK := true; end; punt := p; SetLength(rij,tel); Lilyriek := OK; end; procedure LilyText(tikken : integer; var rij : gegevens); var tel,p,t,q : integer; ly,greep : string; begin tel := Length(rij); q := 1; if tel > 0 then for p := 1 to tel-1 do begin if rij[p].stamp = rij[p-1].stamp then q := q-1; rij[q] := kopie(rij[p],'segt'); q := q + 1; end; tel := q; SetLength(rij,tel); if tel > 0 then for p := 1 to tel-1 do begin t := rij[p].stamp - rij[p-1].stamp; ly := LilyTime(t,tikken,false); greep := rij[p-1].tekst; if Length(greep) = 0 then greep := '_'; ly := vervang(ly,'#',greep); rij[p-1].tekst := ly; end; SetLength(rij,tel-1); end; function tekens(ken : integer) : string; const kruis : array[0..11] of integer = ( 0, -5, +2, -3, +4, -1, +6, +1, -4, +3, -2, +5); { 'C','Des','D','Es','E','F','Fis','G','As','A','Bes','B' } var L,k : integer; t,h : string; begin L := kruis[ken]; for k := 0 to abs(L)-1 do begin t := '#'; if L < 0 then t := 'b'; h := h + t; end; tekens := h; end; function ladder(hoog : integer) : bytes; const tonen : array[0..6] of byte = (60,62,64,65,67,69,71); { c d e f g a b } var uit : bytes; k : integer; begin SetLength(uit,7); for k := 0 to 6 do uit[k] := (tonen[k] + hoog) mod 12; ladder := uit; end; function Key_information(MF : Midi; blah : boolean) : integer; const aard : array[0..11] of string = ('C','Des','D','Es','E','F','Fis','G','As','A','Bes','B'); var rij,vgl : gegevens; reden : samen; punt,aantal,trk,p : integer; k,i,totaal,som,G : integer; riedel : bytes; OK,muziek : boolean; pijl : string; begin SetLength(rij,0); punt := 14; for trk := 0 to MF.Tracks-1 do begin MF.Check_Track_Header(punt,aantal,OK); p := punt; while p < punt + aantal do begin MF.Skip_Delta_Time(p); MF.Data_Any_Event(p,riedel); muziek := ((riedel[0] shr 4) = $8) or ((riedel[0] shr 4) = $9); muziek := muziek and not (riedel[0] and $0F = 9); if muziek then opslaan(rij,integer(riedel[1]) mod 12); end; punt := punt + aantal; end; for k := 0 to 11 do opslaan(rij,k); for k := 0 to 11 do rij[k].getal := rij[k].getal-1; Straight(rij); totaal := 0; for k := 0 to 11 do totaal := totaal + rij[k].getal; SetLength(vgl,12); for k := 0 to 11 do begin som := 0; riedel := ladder(k); for i := 0 to 6 do begin reden := rij[riedel[i]]; som := som + reden.getal; end; vgl[k].stamp := totaal - som; vgl[k].getal := k; end; Straight(vgl); if blah then begin Writeln; Writeln('Distant':7,' Distant':9,' Key':5,' ':9); Writeln('# notes':7,' % notes':9,' ':5,'Signature':9); Writeln('-------':7,' -------':9,' ---':5,'---------':9); for k := 0 to 11 do begin G := vgl[k].getal; P := Round(100*vgl[k].stamp/totaal); pijl := ' '; if k = 0 then pijl := ' <== on top'; Writeln(vgl[k].stamp:7,P:9,aard[G]:5,tekens(G):9,pijl); end; end; Key_information := vgl[0].getal; end; END.