./midicsv-1.1/0000755000175000017500000000000012567433513012113 5ustar kamalkamal./midicsv-1.1/test.mid0000644000175000017500000012171712567432130013570 0ustar kamalkamalMThdMTrk!Klaviersonate C-Dur KV 3301. Satz;Copyright 1997 von Bernd Krger. http://www.piano-midi.deMozartAllegro moderatoUpdate am 31.3.2000 Update am2.12.2001 Normierung: 23.12.2002 Update am 1.2.2006 Update am 16.6.2013 Dauer: 8:58 Minuten XYQAllegro moderatopQ hQ 7hQ *hQ 7xQ .pQ apQ XQ 7hQ *hQ 7xQ āpQ IpQ 7pQ xQ +xQ 0pQ סQ 2#\Q pQ lxQ 6xQ 0 Q spQ 7`Q pQ `Q āpQ pQ `Q ypQ 2#pQ @Q 2#PQ s0Q 7PQ s0QxQ 7hQxQ 7`Q 7xQ spQ DpQ +xQzxQ *`Q,xQ apQxQ pQ 7pQ pQ 7xQxQ ΃`Q āpQ pQ I`QxQ pQhxQ 6pQhpQ 6hQxQ сpQ 7pQ *pQ 7xQxQ pQ pQ pQ 2#0Q ΃`Q 7QL(Q 7xQ 7pQ *`Q 7QPQ 7@Q1xQ 7hQ 7PQ LPQ bPQ x6PQ *PQ (Q s(QpQHQ 7XQ 6pQ1`Q bpQ spQ1`Q 7hQ bXQ *`Q `Q 7`Q *4Q 7,Q 7 Q L Q b(QIPQ x6(Q spQ^4pQ 7@Q x68Q x6(Q x6HQ *HQ HQ sHQ ΁ Q 4Q 7H(JL?H(JL?pCpE6Q @l>xQ<>GV4GCQP<>CTP<>GT6x>GxC8;8x;Ch[ bdca426d104a26ac9dcb070447587523OOwOxOGhOMGH(JL?H(JL?pCpE6Q @l>xQ<>GV4GCQP<>CTP<>GT6x>GxC8;8x;ChJ3hJK5xKL6xLN7xNO8xOG5xGJ6xJH5xHxG3xGH6xHJ7xJL8xLD2xDG6xGE3xExD3xDE6xEG6xGH8xHB2xBE6xEC3xCLHxLB6xBE8xEC3xCLHxLB3xBE7xEC2xCKGxKB3xBE7xEC3xCKCxKB1xBCHxCE@(J8H#H:J#J8H#H9J#J5H#H;J#J8H#H?J#G=H(GH;(H8F#F:H#H8F#F9H#H5F#F;H#H8F#F?H#E=F(EF;RJM@MKHBKJ9JH?pHG6xGO6xOxO8xOxN9xNxMH(JL?H(JL?xPQ8xQQ<7<> JdHxP9xPQ>xQS@xSTFxTL>xLOBxOM>xMxL7xLMxOQAxQI9xIL?xLJ9xJxI1xIJ4xJL6xLM9xMG1xGJ2xJH1xHQFxQG1xGJ3xJH1xHQFxQG1xGxJ1(J8H#H:J#J8H#H9J#J5H#H;J#J8H#H?J#G=H(GH;(H8F#F:H#H8F#F9H#H5F#F;H#H8F#F?H#E=F(EF;RJM@MKHBKJ9JH?pHG6xGO6xOxO8xOxN9xNxMH(JL?H(JL?xPQ8xQQ<7<> JdHxP9xPQ>xQS@xSTFxTL>xLOBxOM>xMxL7xLMxOQAxQI9xIL?xLJ9xJxI1xIJ4xJL6xLM9xMG1xGJ2xJH1xHQFxQG1xGJ3xJH1xHQFxQG1xGxJ16;6x;7.x7>>2;2x;7.x7><0x<0+x040x47+x7<9x<@/x@C4xC@.x@A%xA<x<A%xA<x<A%xA<x<A!xA<x<@%x@<x<@%x@<x<@%x@<x<@ x@<x<@0<0x<7*x7@@-<-x<7(x7@>0A0x>7*x7AA->-x>7(x7A<8@8`@<`7-@-p@77)>)p>7<.7.p7<;)7)p7;<:`<`@070p7@7->-p>7)x>x7-x7;+x;>)x>x7,x7<(x<@+x@<(x<7,x7<(x<@+x@<(x<69x696x9<1x<x69x696x9<3x<x79x7;3x;>5<><>2<><79x7;'x;>*<><>%<><7,x7;)x;>)x>x7,x7;)x;>)x>x7+x7<(x<@(x@<(x<7-x7<&x<@*x@<(x<64<4>>94@96<>7=P7;6P;C3PC9=P9<5P<C2PC;=P;>9P>C1PC7=P7;0P;C-PC<+P<@(P@C%PC<+P<@(P@C&PC<+P<@(P@C&PC<+P<@)P@C'PC79P7;2P;C2PC99P9<3P<C0PC;9P;>4P>C.PC79P7;2P;C/PC<&P<@$P@C$PC<&P<@$P@C$PC<-P<@+P@C(PC<-P<@,P@C)PC7=x7h0=x0h2?x2x65359?`9364(70p740$4+p40@2,;,p;<,`<27.;.x7x;+,x+6(>(p67+p7>p0+7+`70p4+1+p142+7+p76)p62>M4>@F@BBBCGxCB84<><<8<<<;4<;<99x9x;>x;xFx>x?Bx?x@H*@F0+9+p902+;+p;<+p<276x>;0x;78x7<-x<@1x@6x>;/x;78x7<2x<@6x@<1x<;8>878p7>;H)pHG,pGH+pHG-pGB46x>;1x;76x7<2x<@6x@<2x<7:x7;2x;>6x>;6x;76x7<2x<@6x@<2x<;:>:7:p7>;H+pHG-pGH+pHG-pGB0Mx>h7);)2)x2;7h2)6)<)x<62h7H<72C<27H<72C<2;H<;7C<7;H<;7B<7>Hx>h;-2-7-x72;h6-<-2-x2<6h;.7.p7;7.x7x+.x+h[@2w@<$x<@+x@<&x<@3x@<(x<@-x@<+x<@3x@<-x<@3x@<(x<@*x@<x<@!x@<x<@3x@<$x<@+x@<$x<@+x@<$x<A(xA<$x<@&x@<"x<@'x@6;6x;7.x7>>2;2x;7.x7><0x<0+x040x47+x7<9x<@/x@C4xC@.x@A%xA<x<A%xA<x<A%xA<x<A!xA<x<@%x@<x<@%x@<x<@%x@<x<@ x@<x<@0<0x<7*x7@@-<-x<7(x7@>0A0x>7*x7AA->-x>7(x7A<8@8`@<`7-@-p@77)>)p>7<.7.p7<;)7)p7;<:`<`@070p7@7->-p>7)x>x7-x7;+x;>)x>x7,x7<(x<@+x@<(x<7,x7<(x<@+x@<(x<69x696x9<1x<x69x696x9<3x<x79x7;3x;>5<><>2<><79x7;'x;>*<><>%<><7,x7;)x;>)x>x7,x7;)x;>)x>x7+x7<(x<@(x@<(x<7-x7<&x<@*x@<(x<64<4>>94@96<>7=P7;6P;C3PC9=P9<5P<C2PC;=P;>9P>C1PC7=P7;0P;C-PC<+P<@(P@C%PC<+P<@(P@C&PC<+P<@(P@C&PC<+P<@)P@C'PC79P7;2P;C2PC99P9<3P<C0PC;9P;>4P>C.PC79P7;2P;C/PC<&P<@$P@C$PC<&P<@$P@C$PC<-P<@+P@C(PC<-P<@,P@C)PC7=x7h0=x0h2?x2x65359?`9364(70p740$4+p40@2,;,p;<,`<27.;.x7x;+,x+6(>(p67+p7>p0+7+`70p4+1+p142+7+p76)p62>M4>@F@BBBCGxCB84<><<8<<<;4<;<99x9x;>x;xFx>x?Bx?x@H*@F0+9+p902+;+p;<+p<276x>;0x;78x7<-x<@1x@6x>;/x;78x7<2x<@6x@<1x<;8>878p7>;H)pHG,pGH+pHG-pGB46x>;1x;76x7<2x<@6x@<2x<7:x7;2x;>6x>;6x;76x7<2x<@6x@<2x<;:>:7:p7>;H+pHG-pGH+pHG-pGB0Mx>h7);)2)x2;7h2)6)<)x<62h7H<72C<27H<72C<2;H<;7C<7;H<;7B<7>Hx>h;-2-7-x72;h6-<-2-x2<6h;.7.p7;7.x7x+.x+hC(xCG$xGC$xCGxGC(xCG$xGA(xAD$xD@(x@C$xC@&x@C xC@(x@C$xC>(x>A$xA<(x<@#x@<(x<@#x@<(x<@#x@<(x<?#x?;&x;>#x><&x<>#x>;&x;>#x><&x<>#x>;&x;>#x><&x<?#x?;&x;>$x><&x<?#x?;+>+`>;`26x2;2x;76x7;2x;26x291x966x692x97.x7;+x;>-x>;(x;72p7p7(x7; x;>$x>;x;7(p7p7(x7;$x;>(x>;&x;7,p7p8@p8;@p;>Ep>AApA;Ep;9?p98Cp8@?p@9&x9<#x<@#x@<#x<9&x9<#x<A#xA<#x<7&x7>#x>A#xA>#x>7+x7<'x<@'x@<'x<50x5<'x<A6xA<+x<56x590x9>;x>93x94;x49.x9=3x=9-x948x49-x9=2x=9-x92$x29 x9< x<9 x92$x27!x7;!x;7!x70&x07$x7<$x<7$x70&x07"x7<"x<7"x75+x58&x8<&x<8&x85+x58&x8<&x<8&x86+x69-x9<0x<9-x96+x69'x9<'x<9'x97'd7 7)d7 9-7-d79 ;.7.d7; <272d7< C070d7C A.7.d7A ?,7,d7? >-7-d7> ;070d7; 9373d79 ;676d7; <979d7< C=7=d7C A@7@d7A ?C6Cd6? 7G>Gp>7P>&7&p7>x=&x=>&7&p7>x=&x=>&7&p7>x=(x=7->-x>=1x=776>6x>=;x=7>?7?`7>x96<9<;6<;<<6<<<>4<><<7<<<;;<;<<><<<>A<><=C<=<>G<>6;6x;7.x7>>3;3x;7,x7><0x<0-x040x47-x7<9x<@0x@C4xC@0x@A%xA<x<A%xA<x<A%xA<x<A xA<x<@%x@<x<@%x@<x<@%x@<x<@!x@<x<@3<3x<7-x7@@3<3x<7-x7@A3>3x>7+x7AA0>0x>7-x7A@8<8`<@`@.7.p7@>(7(p7><.7.p7<;*7*p7;<:`<`@.7.p7@>*7*p7><-7-p7<;-7-p7;0><0<(x>x7-x7;+x;>)x>x7,x7<(x<@+x@<)x<7,x7<(x<@+x@<(x<69x693x9<6x<x69x693x9<6x<x59x594x9;9x;>4x>A>pA@+x@>-x><,x<@'x@C)xCx<,x<@'x@C)xCx<+x<A%xAE(xEA&xA<-x<A'xAE)xEA(xA;>>>A>C>@C;>A<=P<@9P@H5PH>=P>A9PAH5PH@=P@C9PCH5PH<=P<@0P@H0PHA+PAE(PEH&PHA+PAE(PEH'PHA+PAE)PEH(PHA+PAE)PEH(PH<9P<@7P@H2PH>9P>A6PAH3PH@9P@C6PCH3PH<9P<@6P@H1PHA&PAE$PEH$PHA&PAE$PEH$PHA+PAE'PEH'PHA+PAE(PEH(PH<@x<h5@x5h7;8;`8>;93<3p<95+9+p95@7,@,p@A,`A7@.<.x<x@0,x0C+;+p;<+p<Cp5+<+`<5p6(9(p96<+7+p<;(p;7CM4CEDEG?GHExHG89x>x@>x@xACxAxBExBxCFxCxDHxDxEMpE50>0p>570@0p@A0pA7<xC;;4CAxC;,p>957,@,<,p<@>+A+pA>7<rC;;4CArC;,p>957,@,<,p<@>)A)pA>7(x><$x<@'x@<$x<A(xA<"x<@(x@<&x<A)xA<"x<@)x@<%x<A(xA(x>A$xA<(x<@#x@<(x<@#x@<(x<@#x@<(x<?#x?;&x;>#x><&x<>#x>;&x;>#x><&x<>#x>;&x;>#x><&x<?#x?;&x;>$x><&x<?#x?;+>+`>;`26x2;2x;76x7;2x;26x291x966x692x97.x7;+x;>-x>;(x;72p7p7(x7; x;>$x>;x;7(p7p7(x7;$x;>(x>;&x;7,p7p8@p8;@p;>Ep>AApA;Ep;9?p98Cp8@?p@9&x9<#x<@#x@<#x<9&x9<#x<A#xA<#x<7&x7>#x>A#xA>#x>7+x7<'x<@'x@<'x<50x5<'x<A6xA<+x<56x590x9>;x>93x94;x49.x9=3x=9-x948x49-x9=2x=9-x92$x29 x9< x<9 x92$x27!x7;!x;7!x70&x07$x7<$x<7$x70&x07"x7<"x<7"x75+x58&x8<&x<8&x85+x58&x8<&x<8&x86+x69-x9<0x<9-x96+x69'x9<'x<9'x97'd7 7)d7 9-7-d79 ;.7.d7; <272d7< C070d7C A.7.d7A ?,7,d7? >-7-d7> ;070d7; 9373d79 ;676d7; <979d7< C=7=d7C A@7@d7A ?C6Cd6? 7G>Gp>7P>&7&p7>x=&x=>&7&p7>x=&x=>&7&p7>x=(x=7->-x>=1x=776>6x>=;x=7>?7?`7>x96<9<;6<;<<6<<<>4<><<7<<<;;<;<<><<<>A<><=C<=<>G<>6;6x;7.x7>>3;3x;7,x7><0x<0-x040x47-x7<9x<@0x@C4xC@0x@A%xA<x<A%xA<x<A%xA<x<A xA<x<@%x@<x<@%x@<x<@%x@<x<@!x@<x<@3<3x<7-x7@@3<3x<7-x7@A3>3x>7+x7AA0>0x>7-x7A@8<8`<@`@.7.p7@>(7(p7><.7.p7<;*7*p7;<:`<`@.7.p7@>*7*p7><-7-p7<;-7-p7;0><0<(x>x7-x7;+x;>)x>x7,x7<(x<@+x@<)x<7,x7<(x<@+x@<(x<69x693x9<6x<x69x693x9<6x<x59x594x9;9x;>4x>A>pA@+x@>-x><,x<@'x@C)xCx<,x<@'x@C)xCx<+x<A%xAE(xEA&xA<-x<A'xAE)xEA(xA;>>>A>C>@C;>A<=P<@9P@H5PH>=P>A9PAH5PH@=P@C9PCH5PH<=P<@0P@H0PHA+PAE(PEH&PHA+PAE(PEH'PHA+PAE)PEH(PHA+PAE)PEH(PH<9P<@7P@H2PH>9P>A6PAH3PH@9P@C6PCH3PH<9P<@6P@H1PHA&PAE$PEH$PHA&PAE$PEH$PHA+PAE'PEH'PHA+PAE(PEH(PH<@x<h5@x5h7;8;`8>;93<3p<95+9+p95@7,@,p@A,`A7@.<.x<x@0,x0C+;+p;<+p<Cp5+<+`<5p6(9(p96<+7+p<;(p;7CM4CEDEG?GHExHG89x>x@>x@xACxAxBExBxCFxCxDHxDxEMpE50>0p>570@0p@A0pA7<xC;;4CAxC;,p>957,@,<,p<@>+A+pA>7<rC;;4CArC;,p>957,@,<,p<@>)A)pA>7(x><$x<@'x@<$x<A(xA<"x<@(x@<&x<A)xA<"x<@)x@<%x<A(xA@#@z@o@6@9@%@M@#@O@Z@O@%@U@)@G@)@=@@G@f@q@ @Q@|@[@!@@5@Z@@:@j@\@x@i@m@i@)@0@B@j@'@(@*@%@ @M@h@ @@;@,@ @@R@@@4@f@Q@@]@@P@G@n@M@@Z@!@Q@&@L@+@2@'@4@*@E@+@#@5@1@F@@~@e@%@Q@@I@G@r@|@l@>@4@@Y@z@i@#@.@@W@+@.@ @X@$@:@@d@ @K@%@K@)@@@D@ @n@:@y@b@@@F@#@R@@J@,@<@@$@@T@(@G@)@3@1@ @@!@!@@@'@K@4@1@c@@2@.@'@B@A@@@E@>@#@z@o@6@9@%@M@#@O@Z@O@%@U@)@G@)@=@@G@f@q@ @Q@|@[@!@@5@Z@@:@j@\@x@i@m@i@)@0@B@j@'@(@*@%@ @M@h@ @@;@,@ @@R@@@4@f@Q@@]@@P@G@n@M@@Z@!@Q@&@L@+@2@'@4@*@E@+@#@5@1@F@@~@e@%@Q@@I@G@r@|@l@>@4@@Y@z@i@#@.@@W@+@.@ @X@$@:@@d@ @K@%@K@)@@@D@ @n@:@y@b@@-@9@2@B@E@&@9@=@@>@8@9@=@@E@E@7@<@#@M@.@=@3@=@3@7@3@H@:@4@A@)@6@;@i@8@+@<@6@)@o@y@U@f@W@$@F@@S@@1@1@2@-@*@8@@N@)@1@5@6@@C@:@-@!@1@/@G@@C@4@~@H@N@)@K@!@G@2@'@+@'@9@@K@@@O@L@&@%@K@"@Q@@W@!@K@(@K@(@H@(@L@@*@/@4@0@-@'@O@3@E@)@X@ @U@@;@5@8@!@G@3@#@(@&@*@L@@D@B@@^@h@M@@'@ @.@2@?@1@8@H@6@@9@*@,@7@"@:@)@?@@K@@W@$@D@!@W@R@X@*@G@3@8@5@B@C@'@s@U@@N@ @R@N@ @&@/@%@$@@X@.@B@#@S@s@_@@p@l@t@"@G@#@K@@)@I@'@&@B@.@@@ @P@(@@o@#@V@A@D@@U@X@@G@x@E@(@L@N@$@)@=@3@7@/@A@@K@v@l@ @Q@)@K@"@6@#@N@`@v@!@P@'@E@6@ @,@D@&@M@O@#@!@D@,@:@&@M@&@H@x@m@)@A@@W@@c@/@,@@]@%@L@'@C@&@@#@X@+@F@P@@@X@@F@/@@v@;@+@G@:@2@2@C@ @T@9@3@1@@]@)@@T@?@1@F@"@@@1@@\@@a@ @E@%@K@h@~@@9@2@B@E@&@9@=@@@@6@9@=@@E@@@<@<@#@M@.@=@3@=@3@7@3@H@:@4@A@)@6@;@i@8@+@<@6@)@o@y@U@f@W@$@F@@S@@1@1@2@-@*@8@@N@)@1@5@6@@C@:@-@!@1@/@G@@C@4@~@H@N@)@K@!@G@2@'@+@'@9@@K@@@O@L@&@%@K@"@Q@@W@!@K@(@K@(@H@(@L@@*@/@4@0@-@'@O@3@E@)@X@ @U@@;@5@8@!@G@3@#@(@&@*@L@@D@B@@^@h@M@@'@ @.@2@?@1@8@H@6@@9@*@,@7@"@:@)@?@@K@@W@$@D@!@W@R@X@*@G@3@8@5@B@C@'@s@U@@N@ @R@N@ @&@/@%@$@@X@.@B@#@S@s@_@@p@l@t@"@G@#@K@@)@I@'@&@B@.@@@ @P@(@@o@#@V@A@D@@U@X@@G@x@E@(@L@N@$@)@=@3@7@/@A@@K@v@l@ @Q@)@K@"@6@#@N@`@v@!@P@'@E@6@ @,@D@&@M@O@#@!@D@,@:@&@M@&@H@x@m@)@A@@W@@c@/@,@@]@%@L@'@C@&@@#@X@+@F@P@@@X@@F@/@@v@;@+@G@:@2@2@C@ @T@9@3@1@@]@)@@T@?@1@F@"@@@1@@\@@a@ @E@%@K@h@~@/MTrk2*Mozart: Klaviersonate C-Dur KV 330 1. Satz/MTrk( Copyright 1997 by Bernd Krger/MTrk http://www.piano-midi.de/MTrkEdition: 2013-06-16/./midicsv-1.1/README0000644000175000017500000001210210014521601012744 0ustar kamalkamal MIDI File CSV Editing Tools by John Walker http://www.fourmilab.ch/ This distribution contains tools which permit you to convert standard MIDI files to a CSV (Comma-Separated Format) equivalent which preserves all the information in the MIDI file and then translate such CSV files back to standard MIDI files. Exporting MIDI files as CSV makes it easy to manipulate them with speadsheets, database utilities, or programs in languages such as Perl and C. The following files are included in the distribution: C Source Code csv.c CSV input parser csv.h Definitions for csv.c csvmidi.c CSV to MIDI translator getopt.c Command line option parser getopt.h Definitions for getopt.c midicsv.c MIDI to CSV translator midifile.h MIDI file definitions midio.c MIDI file I/O routines midio.h Definitions for midio.c types.h Common type definitions version.h Version number definition Build Utilities Makefile Make file Sample MIDI Manipulation Programs and Data acomp.pl Simple-minded algorithmic composition example. bad.csv CSV file chock full of errors to test csvmidi error detection and recovery. ce3k.csv Sample MIDI file in CSV format: "Close Encounters of the Third Kind" count_events.pl Perl program which counts events by type in a CSV MIDI file and prints the results. chorus.pl Perl program which adds a one octave lower chorus to all notes in a MIDI file. drummer.pl Dumbest possible drum machine; illustrates MIDI generation from scratch. exchannel.pl Perl program which extracts all events on a given channel (General MIDI percussion channel as supplied), passing through meta-events unchanged. general_midi.pl Perl include file which defines hashes that allow General MIDI patch and percussion note assignments to be specified symbolically. torture.pl Perl program to generate csvmidi/midicsv "torture test". Include all event types and verifies handling of long strings and byte sequences with arbitrary content. transpose.pl Perl program which transposes a MIDI file, shifting all notes down one octave test.mid Sample MIDI file: "Silent Running" by Mike and the Mechanics Documentation README This file csvmidi.1 csvmidi manual page log.txt Development log midicsv.1 midicsv manual page midicsv.5 MIDI CSV representation file format documentation WIN32 Executable Files Csvmidi.exe Csvmidi WIN32 executable Midicsv.exe Midicsv WIN32 executable WIN32 Build Files (for Microsoft Visual Studio .NET) Midicsv.sln Solution (Workspace) for Csvmidi and Midicsv Csvmidi.vcproj Csvmidi project file Midicsv.vcproj Midicsv project file W32test.bat Test script for WIN32 programs BUILDING AND INSTALLATION I can't bring myself to burden such a small, simple set of programs as this with a grotesque Autoconf script which would dwarf the source code. Just edit the Makefile and change the C compiler and options if necessary, and the install directory tree if you wish to use the install target. The build the program with: make and run the self-test with: make check which should report "All tests passed." if all is well. If you're patient, have Perl installed on your system, and have lots of free disc space, you can run the "torture test" with: make torture You may then proceed to install the programs and manual pages with make install which, unless you've changed the INSTALL_DEST directory in the Makefile, will install into /usr/local, which will require you to perform the installation with super-user privilege. COMPATIBILITY NOTES These programs were originally developed on an MS-DOS system with a compiler in which the "int" type was 16 bits, but it's been a long time since they've been compiled with such a compiler so it's unlikely they'll work with 16 bit ints without some tweaking. The programs don't care whether the "char" type is signed or unsigned. I've not tested the programs on a system in which "int" or "long" is wider than 32 bits, but I don't anticipate any problems. On systems such as WIN32 which distinguish text from binary files, it is essential that midicsv.c open its input file and csvmidi.c open its output file in *binary* mode, otherwise the MIDI file will be corrupted due to end of line sequence translation. If input and output file names are specified explicitly, the "b" option supplied in the fopen() call should take care of this (and cause no damage on modern Unix systems, which ignore this option). When reading or writing to standard input/output, however, the open file must be explicitly set to binary mode, and the means for doing this vary among development systems. The midicsv.c and csvmidi.c contain code conditional on _WIN32 which sets binary file mode with the mechanism provided by the Microsoft Visual C/C++ library; if you're building with a different compiler and library, you may need to change this code accordingly. ./midicsv-1.1/version.h0000644000175000017500000000004610744716750013753 0ustar kamalkamal #define VERSION "1.1 (January 2008)" ./midicsv-1.1/W32test.bat0000644000175000017500000000035510002106341014033 0ustar kamalkamal@Rem Test release versions of Midicsv and Csvmidi with @Rem an identity transform. Release\Midicsv test.mid w.csv Release\Csvmidi w.csv w.mid Rem The following comparison should not find any differences cmp test.mid w.mid ./midicsv-1.1/csv.c0000644000175000017500000000662610012523265013047 0ustar kamalkamal/* Comma separated value database format scanner This function implements a somewhat extended flavour of CSV. In addition to the standard quoted fields, permitting embedded commas, with embedded quotes represented as "", backslash escaped characters expressed as three octal digits are also permitted, with a double backslash representing an embedded backslash. This is necessary to permit fields which include end-of-line delimiters which would otherwise truncate the record when it is read. */ #include #include #include #include #include "csv.h" #define BufferInitial 256 /* Initial buffer size */ #define BufferExpansion 1024 /* Buffer expansion increment */ #define EOS '\0' #define FALSE 0 #define TRUE 1 static const char *csptr; /* CSV scan pointer */ /* CSVSCANINIT -- Initialise scanning of a CSV record. */ void CSVscanInit(const char *s) { csptr = s; } /* CSVSCANFIELD -- Scan next field from a CSV record. The actual length of the field is placed in the global variable CSVfieldLength. If the field is not quoted, leading and trailing spaces are discarded. The argument b_f is a pointer to a */ int CSVfieldLength = 0; /* Length of CSV field scanned */ #define f (*b_f) #define flen (*b_flen) static void expand_buf(char **b_f, int *b_flen) { if ((f == NULL) || (flen == 0)) { f = (char *) malloc(BufferInitial); if (f == NULL) { fprintf(stderr, "Unable to allocate %d byte CSV field buffer.\n", BufferInitial); abort(); } flen = BufferInitial; } else { flen += BufferExpansion; f = (char *) realloc(f, flen); if (f == NULL) { fprintf(stderr, "Unable to expand CSV field buffer to %d bytes.\n", flen); abort(); } } } #define store(c) if (CSVfieldLength >= (flen - 1)) \ { expand_buf(b_f, b_flen); } \ f[CSVfieldLength] = c; \ CSVfieldLength++; int CSVscanField(char **b_f, int *b_flen) { int foundfield = FALSE, quoted = FALSE; CSVfieldLength = 0; if (*csptr != EOS) { foundfield = TRUE; while ((*csptr != EOS) && isspace(*csptr)) { csptr++; } if (*csptr == '"') { quoted = TRUE; csptr++; while (*csptr != EOS) { if (*csptr == '"') { if (csptr[1] == '"') { store('"'); csptr += 2; } else { csptr++; break; } } else if (*csptr == '\\') { if (csptr[1] == '\\') { store('\\'); csptr += 2; } else { unsigned int v = 0; int i; for (i = 0; i < 3; i++) { csptr++; if ((*csptr >= '0') && (*csptr <= '7')) { v = (v << 3) | (*csptr - '0'); } else { csptr--; } } csptr++; store((char) v); } } else { char c = *csptr++; store(c); } } } while (*csptr != ',' && *csptr != EOS) { char c = *csptr++; store(c); } if (*csptr == ',') { csptr++; } } f[CSVfieldLength] = EOS; /* Append C string terminator */ /* If the field wasn't quoted, elide any trailing spaces. */ if (foundfield && !quoted) { while (CSVfieldLength > 0 && isspace(f[CSVfieldLength - 1])) { f[--CSVfieldLength] = EOS; } } return foundfield; } #undef f #undef flen ./midicsv-1.1/midio.c0000644000175000017500000000541107774316213013362 0ustar kamalkamal/* MIDI File Input/Output Utilities */ #include #include #include "types.h" #include "midifile.h" #include "midio.h" /* Low level input functions. */ /* READLONG -- Read long from a file (byte-order independent) */ long readlong(FILE *fp) { unsigned char c[4]; fread((char *) c, 1, sizeof c, fp); return (long) ((c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]); } /* READSHORT -- Read short from a file (byte-order independent) */ short readshort(FILE *fp) { unsigned char c[2]; fread((char *) c, 1, sizeof c, fp); return (short) ((c[0] << 8) | c[1]); } /* READVARLEN -- Parse variable length value from MIDI file */ vlint readVarLen(FILE *fp) { long value; int ch; if ((value = getc(fp)) & 0x80) { value &= 0x7F; do { value = (value << 7) | ((ch = getc(fp)) & 0x7F); } while (ch & 0x80); } return value; } /* High level input functions. */ /* READMIDIFILEHEADER -- Read file header structure. */ void readMidiFileHeader(FILE *fp, struct mhead *h) { fread(h->chunktype, sizeof h->chunktype, 1, fp); h->length = readlong(fp); h->format = readshort(fp); h->ntrks = readshort(fp); h->division = readshort(fp); } /* READMIDITRACKHEADER -- Read track header structure. */ void readMidiTrackHeader(FILE *fp, struct mtrack *t) { fread(t->chunktype, sizeof t->chunktype, 1, fp); t->length = readlong(fp); } /* Low level output functions. */ /* WRITELONG -- Write a long to a file in big-endian order */ void writelong(FILE *fp, const long l) { putc((l >> 24) & 0xFF, fp); putc((l >> 16) & 0xFF, fp); putc((l >> 8) & 0xFF, fp); putc(l & 0xFF, fp); } /* WRITESHORT -- Write a short to a file in big-endian order */ void writeshort(FILE *fp, const short s) { putc((s >> 8) & 0xFF, fp); putc(s & 0xFF, fp); } /* WRITEVARLEN -- Write variable length value to MIDI file */ void writeVarLen(FILE *fp, const vlint v) { vlint value = v; long buffer; buffer = value & 0x7F; while ((value >>= 7) > 0) { buffer <<= 8; buffer |= 0x80; buffer += (value & 0x7F); } while (1) { putc((int) (buffer & 0xFF), fp); if (buffer & 0x80) { buffer >>= 8; } else { break; } } } /* High level output functions. */ /* WRITEMIDIFILEHEADER -- Write file header structure. */ void writeMidiFileHeader(FILE *fp, struct mhead *h) { fwrite(h->chunktype, sizeof h->chunktype, 1, fp); writelong(fp, h->length); writeshort(fp, h->format); writeshort(fp, h->ntrks); writeshort(fp, h->division); } /* WRITEMIDITRACKHEADER -- Write track header structure. */ void writeMidiTrackHeader(FILE *fp, struct mtrack *t) { fwrite(t->chunktype, sizeof t->chunktype, 1, fp); writelong(fp, t->length); } ./midicsv-1.1/Makefile0000644000175000017500000000637110744722234013556 0ustar kamalkamal CC = gcc CFLAGS = -g -Wall INSTALL_DEST = /usr/local # You shouldn't need to change anything after this line VERSION = 1.1 PROGRAMS = midicsv csvmidi MANPAGES = $(PROGRAMS:%=%.1) midicsv.5 DOC = README log.txt BUILD = Makefile SOURCE = csv.c csvmidi.c midicsv.c midio.c getopt.c getopt.h HEADERS = csv.h midifile.h midio.h types.h version.h EXAMPLES = test.mid bad.csv ce3k.csv acomp.pl chorus.pl \ count_events.pl drummer.pl exchannel.pl general_midi.pl \ transpose.pl torture.pl WIN32EXE = Midicsv.exe Csvmidi.exe WIN32 = $(WIN32EXE) Midicsv.sln Midicsv.vcproj Csvmidi.vcproj W32test.bat DISTRIBUTION = $(DOC) $(BUILD) $(SOURCE) $(MANPAGES) $(HEADERS) $(EXAMPLES) $(WIN32) all: $(PROGRAMS) MIDICSV_OBJ = midicsv.o midio.o getopt.o midicsv: $(MIDICSV_OBJ) $(CC) $(CFLAGS) -o midicsv midicsv.o midio.o getopt.o Midicsv.exe: $(MIDICSV_OBJ:%.o=%.c) @echo 'Yar! Midicsv.exe needs to be rebuilt on WIN32!' @exit 1 CSVMIDI_OBJ = csvmidi.o midio.o csv.o getopt.o csvmidi: $(CSVMIDI_OBJ) $(CC) $(CFLAGS) -o csvmidi csvmidi.o midio.o csv.o getopt.o Csvmidi.exe: $(CSVMIDI_OBJ:%.o=%.c) @echo 'Yar! Csvmidi.exe needs to be rebuilt on WIN32!' @exit 1 check: all @./midicsv test.mid /tmp/test.csv @./csvmidi /tmp/test.csv /tmp/w.mid @./midicsv /tmp/w.mid /tmp/w1.csv @-cmp -s test.mid /tmp/w.mid ; if test $$? -ne 0 ; then \ echo '** midicsv/csvmidi: MIDI file comparison failed. **' ; else \ diff -q /tmp/test.csv /tmp/w1.csv ; if test $$? -ne 0 ; then \ echo '** midicsv/csvmidi: CSV file comparison failed. **' ; else \ echo 'All tests passed.' ; fi ; fi @rm -f /tmp/test.csv /tmp/w.mid /tmp/w1.csv pipetest: all ./midicsv test.mid | tee /tmp/test.csv | ./csvmidi | ./midicsv - /tmp/w1.csv diff /tmp/test.csv /tmp/w1.csv rm /tmp/test.csv /tmp/w1.csv torture: all perl torture.pl | ./csvmidi | tee /tmp/w.mid | ./midicsv | ./csvmidi >/tmp/w1.mid @cmp /tmp/w.mid /tmp/w1.mid ; if test $$? -ne 0 ; then \ echo '** midicsv/csvmidi: Torture test CSV file comparison failed. **' ; else \ echo 'Torture test passed.' ; fi @rm /tmp/w.mid /tmp/w1.mid install: all install -d -m 755 $(INSTALL_DEST)/bin install -m 755 $(PROGRAMS) $(INSTALL_DEST)/bin install -d -m 755 $(INSTALL_DEST)/man/man1 install -m 644 midicsv.1 csvmidi.1 $(INSTALL_DEST)/man/man1 install -d -m 755 $(INSTALL_DEST)/man/man5 install -m 644 midicsv.5 $(INSTALL_DEST)/man/man5 uninstall: rm -f $(INSTALL_DEST)/bin/csvmidi $(INSTALL_DEST)/bin/midicsv rm -f $(INSTALL_DEST)/man/man1/csvmidi.1 $(INSTALL_DEST)/man/man1/midicsv.1 rm -f $(INSTALL_DEST)/man/man5/midicsv.5 dist: $(WIN32EXE) rm -f midicsv*.tar midicsv*.tar.gz tar cfv midicsv.tar $(DISTRIBUTION) mkdir midicsv-$(VERSION) ( cd midicsv-$(VERSION) ; tar xfv ../midicsv.tar ) rm -f midicsv.tar tar cfv midicsv-$(VERSION).tar midicsv-$(VERSION) gzip midicsv-$(VERSION).tar rm -rf midicsv-$(VERSION) rm -f midicsv-$(VERSION).zip zip midicsv-$(VERSION).zip $(WIN32EXE) # Zipped archive for building WIN32 version winarch: rm -f midicsv.zip zip midicsv.zip $(DISTRIBUTION) # Publish distribution on Web page (Fourmilab specific) WEBDIR = $(HOME)/ftp/webtools/midicsv publish: dist cp -p midicsv-$(VERSION).tar.gz midicsv-$(VERSION).zip $(WEBDIR) clean: rm -f $(PROGRAMS) *.o *.bak core core.* *.out midicsv.zip ./midicsv-1.1/acomp.pl0000644000175000017500000000347010014510175013534 0ustar kamalkamal # Incredibly dumb algorithmic composer require 'general_midi.pl'; $instrument = $GM_Patch{'Distortion Guitar'}; $tonespan = 32; $num_notes = 120; $percussion = $GM_Percussion{'Ride Cymbal 1'}; $beat = 6; print << "EOD"; 0, 0, Header, 1, 1, 480 1, 0, Start_track 1, 0, Tempo, 500000 1, 0, Program_c, 1, $instrument EOD $time = 0; srand(time()); for ($i = 0; $i < $num_notes; $i++) { $n = 60 + int((rand() * $tonespan) - int($tonespan / 2)); $notelength = 120 + (60 * int(rand() * 6)); ¬e(1, $n, $notelength, 127); if (($i % $beat) == 0) { print("1, $time, Note_on_c, 9, $percussion, 127\n"); } elsif (($i % $beat) == ($beat - 1)) { print("1, $time, Note_off_c, 9, $percussion, 0\n"); } } # Cymbal crash at end $cymbal = $GM_Percussion{'Crash Cymbal 2'}; print("1, $time, Note_on_c, 9, $cymbal, 127\n"); $time += 480; print("1, $time, Note_off_c, 9, $cymbal, 0\n"); # Audience applause $time += 480; print("1, $time, Program_c, 1, $GM_Patch{'Applause'}\n"); print("1, $time, Note_on_c, 1, 60, 100\n"); for ($i = 16; $i <= 32; $i++) { $time += 120; $v = int(127 * ($i / 32)); print("1, $time, Poly_aftertouch_c, 1, 60, $v\n"); } for ($i = 32; $i >= 0; $i--) { $time += 240; $v = int(127 * ($i / 32)); print("1, $time, Poly_aftertouch_c, 1, 60, $v\n"); } print("1, $time, Note_off_c, 1, 60, 0\n"); print << "EOD"; 1, $time, End_track 0, 0, End_of_file EOD sub note { # ¬e($channel, $note_number, $duration [, $velocity]) local ($channel, $which, $duration, $vel) = @_; if (!defined($vel)) { $vel = 127; } print("1, $time, Note_on_c, $channel, $which, $vel\n"); $time += $duration; print("1, $time, Note_off_c, $channel, $which, 0\n"); } ./midicsv-1.1/midio.h0000644000175000017500000000103207774316152013364 0ustar kamalkamal/* MIDI I/O Function Definitions */ extern long readlong(FILE *fp); extern short readshort(FILE *fp); extern vlint readVarLen(FILE *fp); extern void readMidiFileHeader(FILE *fp, struct mhead *h); extern void readMidiTrackHeader(FILE *fp, struct mtrack *t); extern void writelong(FILE *fp, const long l); extern void writeshort(FILE *fp, const short s); extern void writeVarLen(FILE *fp, const vlint v); extern void writeMidiFileHeader(FILE *fp, struct mhead *h); extern void writeMidiTrackHeader(FILE *fp, struct mtrack *t); ./midicsv-1.1/types.h0000644000175000017500000000042507774043613013433 0ustar kamalkamal/* Type definitions for MIDI tools */ typedef unsigned char byte; /* MIDI data stream byte */ typedef unsigned long vlint; /* Variable length integer: this must be an unsigned type of at least 32 bits (longer is OK). */ ./midicsv-1.1/general_midi.pl0000644000175000017500000000766610014500517015066 0ustar kamalkamal # The following hashes can be used to reference General MIDI # patch (instrument) number and percussion note assignments # by name. %GM_Patch = ( 'Acoustic Grand Piano', 0, 'Bright Acoustic Piano', 1, 'Electric Grand Piano', 2, 'Honky-tonk Piano', 3, 'Electric Piano 1', 4, 'Electric Piano 2', 5, 'Harpsichord', 6, 'Clavinet', 7, 'Celesta', 8, 'Glockenspiel', 9, 'Music Box', 10, 'Vibraphone', 11, 'Marimba', 12, 'Xylophone', 13, 'Tubular Bells', 14, 'Dulcimer', 15, 'Drawbar Organ', 16, 'Percussive Organ', 17, 'Rock Organ', 18, 'Church Organ', 19, 'Reed Organ', 20, 'Acordion', 21, 'Harmonica', 22, 'Tango Accordion', 23, 'Acoustic Guitar (nylon)', 24, 'Acoustic Guitar (steel)', 25, 'Electric Guitar (jazz)', 26, 'Electric Guitar (clean)', 27, 'Electric Guitar (muted)', 28, 'Overdriven Guitar', 29, 'Distortion Guitar', 30, 'Guitar Harmonics', 31, 'Acoustic Bass', 32, 'Electric Bass (finger)', 33, 'Electric Bass (pick)', 34, 'Fretless Bass', 35, 'Slap Bass 1', 36, 'Slap Bass 2', 37, 'Synth Bass 1', 38, 'Synth Bass 2', 39, 'Violin', 40, 'Viola', 41, 'Cello', 42, 'Contrabass', 43, 'Tremolo Strings', 44, 'Pizzicato Strings', 45, 'Orchestral Harp', 46, 'Timpani', 47, 'String Ensemble 1', 48, 'String Ensemble 2', 49, 'SynthStrings 1', 50, 'SynthStrings 2', 51, 'Choir Aahs', 52, 'Voice Oohs', 53, 'Synth Voice', 54, 'Orchestra Hit', 55, 'Trumpet', 56, 'Trombone', 57, 'Tuba', 58, 'Muted Trumpet', 59, 'French Horn', 60, 'Brass Section', 61, 'Synth Brass 1', 62, 'Synth Brass 2', 63, 'Soprano Sax', 64, 'Alto Sax', 65, 'Tenor Sax', 66, 'Baritone Sax', 67, 'Oboe', 68, 'English Horn', 69, 'Bassoon', 70, 'Clarinet', 71, 'Piccolo', 72, 'Flute', 73, 'Recorder', 74, 'Pan Flute', 75, 'Blown Bottle', 76, 'Shakuhachi', 77, 'Whistle', 78, 'Ocarina', 79, 'Lead 1 (square)', 80, 'Lead 2 (sawtooth)', 81, 'Lead 3 (calliope)', 82, 'Lead 4 (chiff)', 83, 'Lead 5 (charang)', 84, 'Lead 6 (voice)', 85, 'Lead 7 (fifths)', 86, 'Lead 8 (bass+lead', 87, 'Pad 1 (new age)', 88, 'Pad 2 (warm)', 89, 'Pad 3 (polysynth)', 90, 'Pad 4 (choir)', 91, 'Pad 5 (bowed)', 92, 'Pad 6 (metallic)', 93, 'Pad 7 (halo)', 94, 'Pad 8 (sweep)', 95, 'FX 1 (train)', 96, 'FX 2 (soundtrack)', 97, 'FX 3 (crystal)', 98, 'FX 4 (atmosphere)', 99, 'FX 5 (brightness)', 100, 'FX 6 (goblins)', 101, 'FX 7 (echoes)', 102, 'FX 8 (sci-fi)', 103, 'Sitar', 104, 'Banjo', 105, 'Shamisen', 106, 'Koto', 107, 'Kalimba', 108, 'Bagpipe', 109, 'Fiddle', 110, 'Shanai', 111, 'Tinkle Bell', 112, 'Agogo', 113, 'Steel Drums', 114, 'Woodblock', 115, 'Tailo Drum', 116, 'Melodic Drum', 117, 'Synth Drum', 118, 'Reverse Cymbal', 119, 'Guitar Fret Noise', 120, 'Breath Noise', 121, 'Seashore', 122, 'Bird Tweet', 123, 'Telephone Ring', 124, 'Helicopter', 125, 'Applause', 126, 'Gunshot', 127 ); %GM_Percussion = ( 'Acoustic Bass Drum', 35, 'Bass Drum 1', 36, 'Side Stick', 37, 'Acoustic Snare', 38, 'Hand Clap', 39, 'Electric Snare', 40, 'Low Floor Tom', 41, 'Closed Hi-Hat', 42, 'High Floor Tom', 43, 'Pedal Hi-Hat', 44, 'Low Tom', 45, 'Open Hi-Hat', 46, 'Low-Mid Tom', 47, 'High-Mid Tom', 48, 'Crash Cymbal 1', 49, 'High Tom', 50, 'Ride Cymbal 1', 51, 'Chinese Cymbal', 52, 'Ride Bell', 53, 'Tambourine', 54, 'Splash Cymbal', 55, 'Cowbell', 56, 'Crash Cymbal 2', 57, 'Vibraslap', 58, 'Ride Cymbal 2', 59, 'High Bongo', 60, 'Low Bongo', 61, 'Mute Hi Conga', 62, 'Open Hi Conga', 63, 'Low Conga', 64, 'High Timbale', 65, 'Low Timbale', 66, 'High Agogo', 67, 'Low Agogo', 68, 'Cabasa', 69, 'Maracas', 70, 'Short Whistle', 71, 'Long Whistle', 72, 'Short Guiro', 73, 'Long Guiro', 74, 'Claves', 75, 'Hi Woodblock', 76, 'Low Woodblock', 77, 'Mute Cuica', 78, 'Open Cuica', 79, 'Mute Triangle', 80, 'Open Triangle', 81 ); 1; ./midicsv-1.1/csv.h0000644000175000017500000000031410012015501013025 0ustar kamalkamal/* CSV Parsing Function Definitions */ extern void CSVscanInit(const char *s); extern int CSVscanField(char **b_f, int *b_flen); extern int CSVfieldLength; /* Length of CSV field scanned */ ./midicsv-1.1/bad.csv0000644000175000017500000000236310013437016013344 0ustar kamalkamal0, 0, Header, 1, 2, 480 1, 0, Start_track 1, 0, Title_t, "Close Encounters" 1, 0, Text_t, "Sample for MIDIcsv Distribution" 1, 0, Copyright_t, "This file is in the public domain" 1, 0, Time_signature, 4, 2, 24, 8 1, 0, Tempo, 500000 1, 0, End_track 2, -11, Start_track 2, 0, Instrument_name_t, "Church Organ" 2, 0, Program_c, 1, 19 2, -6, Note_on_c, 1, 79, 81 2, 100, BogusBogusBogus, 1, 111 2, 960, Note_off_c, 1, 79, 0 2, 960, Note_on_c, 1, 81, 81 2, 1920, Note_off_c, 1, 81, 0 4, 1920, Note_on_c, 1, 77, 81 2, 2880, Note_off_c, 1, 77, 0 2, 2880, Note_on_c, -3, 300, 81 2, 2880, Pitch_bend_c, 1, 99999 999, -17, Note_off_c, 1, 65, 0 2, 3840 2, 3840, Note_off_c, 1, 65, 0 2, 3840, Note_on_c, 2, 3840, Note_on_c, 1, 72 2, 3840, Note_on_c, 1, 72, 81 2, 3900, system_exclusive, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9 2, 3900, system_exclusive_packet, 10, 1, 2, 3, 4, 5, 6, 7 2, 3900, sequencer_specific, 4, 1, 2, 3, 4 2, 3900, sequencer_specific, 4, 1, 2, 3 2,3900, unknown_meta_event, 121, 5, 1, 2, 3, 4, 5 2,3900, unknown_meta_event, 121, 5, 1, 2, 3 # The following event is actually # 4, 3030, Key_signature, -4, "major" # coded as an Unknown_meta_event. 2, 3900, Unknown_meta_event, 89, 2, 252, 0 2, 4800, Note_off_c, 1, 72, 0 2, 4800, End_track 0, 0, End_of_file ./midicsv-1.1/midicsv.50000644000175000017500000004165410031656715013644 0ustar kamalkamal'\" t .TH MIDICSV 5 "29 MAR 2004" .UC 4 .SH NAME midicsv \- MIDI Comma-Separated Value (CSV) file format .SH DESCRIPTION The .B midicsv and .B csvmidi programs permit you to intertranslate standard MIDI files and comma-separated value (CSV) files. These CSV files preserve all information in the MIDI file, and may be loaded into spreadsheet and database programs or easily manipulated with text processing tools. This document describes the CSV representation of MIDI files written by .B midicsv and read by .BR csvmidi . Readers are assumed to understand the structure, terminology, and contents of MIDI files\-please refer to a MIDI file reference for details. .SH "RECORD STRUCTURE" Each record in the CSV representation of a MIDI contains at least three fields: .TP 10 .B Track Numeric field identifying the track to which this record belongs. Tracks of MIDI data are numbered starting at 1. Track 0 is reserved for file header, information, and end of file records. .TP .B Time Absolute time, in terms of MIDI clocks, at which this event occurs. Meta-events for which time is not meaningful (for example, song title, copyright information, etc.) have an absolute time of 0. .TP .B Type Name identifying the type of the record. Record types are text consisting of upper and lower case letters and the underscore (``_''), contain no embedded spaces, and are not enclosed in quotes. .B csvmidi ignores upper/lower case in the .B Type field; the specifications .RB `` Note_on_c '', .RB `` Note_On_C '', and .RB `` NOTE_ON_C '' are considered identical. .PP Records in the CSV file are sorted first by the track number, then by time. Out of order records will be discarded with an error message from .BR csvmidi . Following the three required fields are parameter fields which depend upon the .BR Type ; some .BR Type s take no parameters. Each .B Type and its parameter fields is discussed below. .PP Any line with an initial nonblank character of .RB `` # '' or .RB `` ; '' is ignored; either delimiter may be used to introduce comments in a CSV file. Only full-line comments are permitted; you cannot use these delimiters to terminate scanning of a regular data record. Completely blank lines are ignored. .SS "File Structure Records" .TP 5 .BI "0, 0, Header" ", format, nTracks, division" The first record of a CSV MIDI file is always the .B Header record. Parameters are .IR format : the MIDI file type (0, 1, or 2), .IR nTracks : the number of tracks in the file, and .IR division : the number of clock pulses per quarter note. The .B Track and .B Time fields are always zero. .TP .B "0, 0, End_of_file" The last record in a CSV MIDI file is always an .B End_of_file record. Its .B Track and .B Time fields are always zero. .TP .IB "Track, " "0, Start_track" A .B Start_track record marks the start of a new track, with the .I Track field giving the track number. All records between the .B Start_track record and the matching .B End_track will have the same .I Track field. .TP .IB "Track, Time, " "End_track" An .B End_track marks the end of events for the specified .IR Track . The .I Time field gives the total duration of the track, which will be identical to the .I Time in the last event before the .BR End_track . .SS "File Meta-Events" The following events occur within MIDI tracks and specify various kinds of information and actions. They may appear at any time within the track. Those which provide general information for which time is not relevant usually appear at the start of the track with .B Time zero, but this is not a requirement. .PP Many of these meta-events include a text string argument. Text strings are output in CSV records enclosed in ASCII double quote (") characters. Quote characters embedded within strings are represented by two consecutive quotes. Non-graphic characters in the ISO 8859/1 Latin 1 set are output as a backslash followed by their three digit octal character code. Two consecutive backslashes denote a literal backslash in the string. Strings in MIDI files can be extremely long, theoretically as many as .if t 2\s-2\v'-0.4m'28\v'0.4m'\s+2\-1 .if n 2**28-1 characters; programs which process MIDI CSV files should take care to avoid buffer overflows or truncation resulting from lines containing long string items. All meta-events which take a text argument are identified by a suffix of .RB `` _t ''. .TP .IB "Track, Time, " "Title_t, """ Text """ The .I Text specifies the title of the track or sequence. The first .B Title meta-event in a type 0 MIDI file, or in the first track of a type 1 file gives the name of the work. Subsequent .B Title meta-events in other tracks give the names of those tracks. .TP .IB "Track, Time, " "Copyright_t, """ Text """ The .I Text specifies copyright information for the sequence. This is usually placed at time 0 of the first track in the sequence. .TP .IB "Track, Time, " "Instrument_name_t, """ Text """ The .I Text names the instrument intended to play the contents of this track, This is usually placed at time 0 of the track. Note that this meta-event is simply a description; MIDI synthesisers are not required (and rarely if ever) respond to it. This meta-event is particularly useful in sequences prepared for synthesisers which do not conform to the General MIDI patch set, as it documents the intended instrument for the track when the sequence is used on a synthesiser with a different patch set. .TP .IB "Track, Time, " "Marker_t, """ Text """ The .I Text marks a point in the sequence which occurs at the given .IR Time , for example "Third\ Movement". .TP .IB "Track, Time, " "Cue_point_t, """ Text """ The .I Text identifies synchronisation point which occurs at the specified .IR Time , for example, "Door\ slams". .TP .IB "Track, Time, " "Lyric_t, """ Text """ The .I Text gives a lyric intended to be sung at the given .IR Time . Lyrics are often broken down into separate syllables to time-align them more precisely with the sequence. .TP .IB "Track, Time, " "Text_t, """ Text """ This meta-event supplies an arbitrary .I Text string tagged to the .I Track and .IR Time . It can be used for textual information which doesn't fall into one of the more specific categories given above. .TP .IB "Track, " "0, Sequence_number, " Number This meta-event specifies a sequence .I Number between 0 and 65535, used to arrange multiple tracks in a type 2 MIDI file, or to identify the sequence in which a collection of type 0 or 1 MIDI files should be played. The .B Sequence_number meta-event should occur at .B Time zero, at the start of the track. .TP .IB "Track, Time, " "MIDI_port, " Number This meta-event specifies that subsequent events in the .B Track should be sent to MIDI port (bus) .IR Number , between 0 and 255. This meta-event usually appears at the start of a track with .B Time zero, but may appear within a track should the need arise to change the port while the track is being played. .TP .IB "Track, Time, " "Channel_prefix, " Number This meta-event specifies the MIDI channel that subsequent meta-events and .B System_exclusive events pertain to. The channel .I Number specifies a MIDI channel from 0 to 15. In fact, the .I Number may be as large as 255, but the consequences of specifying a channel number greater than 15 are undefined. .TP .IB "Track, Time, " "Time_signature, " "Num, Denom, Click, NotesQ" The time signature, metronome click rate, and number of 32nd notes per MIDI quarter note (24 MIDI clock times) are given by the numeric arguments. .I Num gives the numerator of the time signature as specified on sheet music. .I Denom specifies the denominator as a negative power of two, for example 2 for a quarter note, 3 for an eighth note, etc. .I Click gives the number of MIDI clocks per metronome click, and .I NotesQ the number of 32nd notes in the nominal MIDI quarter note time of 24 clocks (8 for the default MIDI quarter note definition). .TP .IB "Track, Time, " "Key_signature, " "Key, Major/Minor" The key signature is specified by the numeric .I Key value, which is 0 for the key of C, a positive value for each sharp above C, or a negative value for each flat below C, thus in the inclusive range \-7 to 7. The .I Major/Minor field is a quoted string which will be .B """major""" for a major key and .B """minor""" for a minor key. .TP .IB "Track, Time, " "Tempo, " "Number" The tempo is specified as the .I Number of microseconds per quarter note, between 1 and 16777215. A value of 500000 corresponds to 120 quarter notes ("beats") per minute. To convert beats per minute to a .B Tempo .IR value , take the quotient from dividing 60,000,000 by the beats per minute. .TP .IB "Track, " "0, SMPTE_offset, " "Hour, Minute, Second, Frame, FracFrame" This meta-event, which must occur with a zero .B Time at the start of a track, specifies the SMPTE time code at which it should start playing. The .I FracFrame field gives the fractional frame time (0 to 99). .TP .IB "Track, Time, " "Sequencer_specific, " "Length, Data, ..." The .B Sequencer_specific meta-event is used to store vendor-proprietary data in a MIDI file. The .I Length can be any value between 0 and .if t 2\s-2\v'-0.4m'28\v'0.4m'\s+2\-1, .if n 2**28-1, specifying the number of .I Data bytes (between 0 and 255) which follow. .B Sequencer_specific records may be very long; programs which process MIDI CSV files should be careful to protect against buffer overflows and truncation of these records. .TP .IB "Track, Time, " "Unknown_meta_event, " "Type, Length, Data, ..." If .B midicsv encounters a meta-event with a code not defined by the standard MIDI file specification, it outputs an unknown meta-event record in which .I Type gives the numeric meta-event type code, .I Length the number of data bytes in the meta-event, which can be any value between 0 and .if t 2\s-2\v'-0.4m'28\v'0.4m'\s+2\-1, .if n 2**28-1, followed by the .I Data bytes. Since meta-events include their own length, it is possible to parse them even if their type and meaning are unknown. .B csvmidi will reconstruct unknown meta-events with the same type code and content as in the original MIDI file. .SS "Channel Events" These events are the ``meat and potatoes'' of MIDI files: the actual notes and modifiers that command the instruments to play the music. Each has a MIDI channel number as its first argument, followed by event-specific parameters. To permit programs which process CSV files to easily distinguish them from meta-events, names of channel events all have a suffix of .RB `` _c ''. .TP .IB "Track, Time, " "Note_on_c, " "Channel, Note, Velocity" Send a command to play the specified .I Note (Middle C is defined as .I Note number 60; all other notes are relative in the MIDI specification, but most instruments conform to the well-tempered scale) on the given .I Channel with .I Velocity (0 to 127). A .B Note_on_c event with .I Velocity zero is equivalent to a .BR Note_off_c . .TP .IB "Track, Time, " "Note_off_c, " "Channel, Note, Velocity" Stop playing the specified .I Note on the given .IR Channel . The .I Velocity should be zero, but you never know what you'll find in a MIDI file. .TP .IB "Track, Time, " "Pitch_bend_c, " "Channel, Value" Send a pitch bend command of the specified .I Value to the given .IR Channel . The pitch bend .I Value is a 14 bit unsigned integer and hence must be in the inclusive range from 0 to 16383. .TP .IB "Track, Time, " "Control_c, " "Channel, Control_num, Value" Set the controller .I Control_num on the given .I Channel to the specified .IR Value . .I Control_num and .I Value must be in the inclusive range 0 to 127. The assignment of .I Control_num values to effects differs from instrument to instrument. The General MIDI specification defines the meaning of controllers 1 (modulation), 7 (volume), 10 (pan), 11 (expression), and 64 (sustain), but not all instruments and patches respond to these controllers. Instruments which support those capabilities usually assign reverberation to controller 91 and chorus to controller 93. .TP .IB "Track, Time, " "Program_c, " "Channel, Program_num" Switch the specified .I Channel to program (patch) .IR Program_num , which must be between 0 and 127. The program or patch selects which instrument and associated settings that channel will emulate. The General MIDI specification provides a standard set of instruments, but synthesisers are free to implement other sets of instruments and many permit the user to create custom patches and assign them to program numbers. .TP \ Apparently due to instrument manufacturers' skepticism about musicians' ability to cope with the number zero, many instruments number patches from 1 to 128 rather than the 0 to 127 used within MIDI files. When interpreting .I Program_num values, note that they may be one less than the patch numbers given in an instrument's documentation. .TP .IB "Track, Time, " "Channel_aftertouch_c, " "Channel, Value" When a key is held down after being pressed, some synthesisers send the pressure, repeatedly if it varies, until the key is released, but do not distinguish pressure on different keys played simultaneously and held down. This is referred to as ``monophonic'' or ``channel'' aftertouch (the latter indicating it applies to the .I Channel as a whole, not individual note numbers on that channel). The pressure .I Value (0 to 127) is typically taken to apply to the last note played, but instruments are not guaranteed to behave in this manner. .TP .IB "Track, Time, " "Poly_aftertouch_c, " "Channel, Note, Value" Polyphonic synthesisers (those capable of playing multiple notes simultaneously on a single channel), often provide independent aftertouch for each note. This event specifies the aftertouch pressure .I Value (0 to 127) for the specified .I Note on the given .IR Channel . .SS "System Exclusive Events" System Exclusive events permit storing vendor-specific information to be transmitted to that vendor's products. .TP .IB "Track, Time, " "System_exclusive, " "Length, Data, ..." The .I Length bytes of .I Data (0 to 255) are sent at the specified .I Time to the MIDI channel defined by the most recent .B Channel_prefix event on the .IR Track , as a System Exclusive message. Note that .I Length can be any value between 0 and .if t 2\s-2\v'-0.4m'28\v'0.4m'\s+2\-1. .if n 2**28-1. Programs which process MIDI CSV files should be careful to protect against buffer overflows and truncation of these records. .TP .IB "Track, Time, " "System_exclusive_packet, " "Length, Data, ..." The .I Length bytes of .I Data (0 to 255) are sent at the specified .I Time to the MIDI channel defined by the most recent .B Channel_prefix event on the .IR Track . The .I Data bytes are simply blasted out to the MIDI bus without any prefix. This message is used by MIDI devices which break up long system exclusive message into small packets, spaced out in time to avoid overdriving their modest microcontrollers. Note that .I Length can be any value between 0 and .if t 2\s-2\v'-0.4m'28\v'0.4m'\s+2\-1. .if n 2**28-1. Programs which process MIDI CSV files should be careful to protect against buffer overflows and truncation of these records. .SH EXAMPLES The following CSV file defines the five-note motif from the film .I "Close Encounters of the Third Kind" using an organ patch from the General MIDI instrument set. When processed by .B midicsv and sent to a synthesiser which conforms to General MIDI, the sequence will be played. .PP .RS 5 .nf 23 0, 0, Header, 1, 2, 480 1, 0, Start_track 1, 0, Title_t, "Close Encounters" 1, 0, Text_t, "Sample for MIDIcsv Distribution" 1, 0, Copyright_t, "This file is in the public domain" 1, 0, Time_signature, 4, 2, 24, 8 1, 0, Tempo, 500000 1, 0, End_track 2, 0, Start_track 2, 0, Instrument_name_t, "Church Organ" 2, 0, Program_c, 1, 19 2, 0, Note_on_c, 1, 79, 81 2, 960, Note_off_c, 1, 79, 0 2, 960, Note_on_c, 1, 81, 81 2, 1920, Note_off_c, 1, 81, 0 2, 1920, Note_on_c, 1, 77, 81 2, 2880, Note_off_c, 1, 77, 0 2, 2880, Note_on_c, 1, 65, 81 2, 3840, Note_off_c, 1, 65, 0 2, 3840, Note_on_c, 1, 72, 81 2, 4800, Note_off_c, 1, 72, 0 2, 4800, End_track 0, 0, End_of_file .RE .SH BUGS .PP The CSV representation of a MIDI file is simply a text-oriented encoding of its contents. If the input to .B midicsv contains errors which violate the MIDI standard, the resulting CSV file will faithfully replicate these errors. Similarly, the CSV input to .B csvmidi must not only consist of records which conform to the syntax given in this document, the input as a whole must also be a .I semantically correct MIDI file. Programs which wish to use .B csvmidi to generate MIDI files from scratch should be careful to conform to the structure required of MIDI files. When in doubt, use .B midicsv to dump a sequence comparable to the one your program will create and use its structure as a template for your own. .PP Please report errors to .BR bugs@fourmilab.ch . .SH "SEE ALSO" .PD .BR csvmidi (1), .BR midicsv (1) .ne 10 .SH AUTHOR .ce 2 John Walker http://www.fourmilab.ch/ .PP This software is in the public domain. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, without any conditions or restrictions. This software is provided ``as is'' without express or implied warranty. ./midicsv-1.1/log.txt0000644000175000017500000004051710744722701013437 0ustar kamalkamal MIDICSV / CSVMIDI Development Log 2003 December 29 Changed declarations from "char" to "byte" where necessary so everything works correctly on platforms where char is signed without the need to compile with an option to force unsigned char. Moved definitions of "byte" and "vlint" to a new types.h file and included it in files which need these definitions. Changed some code which used "long" where a variable length integer appears in the MIDI file to use "vlint" instead. This doesn't change functionality, but improves documentation in the code. Fixed several instances in the Makefile where test cases assumed the current directory was on the PATH, Reversed argument order of the (presently unused) function writeVarLen in midio.c to agree with the other write functions in this file. Integrated code from XD to set the input file (midicsv.c) and output file (csvmidi.c) mode to binary when reading or writing standard input/output on a WIN32 platform. This code sets the mode using the _setmode() function of the Microsoft Visual C library--it may have to be changed if you build using another WIN32 compiler. Added a -v option to csvmidi.c and verbose output of the file header and track information corresponding to that generated by midicsv.c. 2003 December 30 Created manual pages csvmidi.1 and midicsv.1 (first cut) for the programs. Added an "install" target to the Makefile with default installation in the /usr/local tree. Created a README file which will eventually contain all the gory details and dark underside of the distribution. Cleaned up the "check" case in the Makefile to use a version of the Mike and the Mechanics "Silent Running" sequence which has run the midicsv/csvmidi gauntlet and is hence invariant. This permits verifying both text and binary equality of the CSV and MIDI files from encoding and decoding this file. If the check passes, the only output is now "All tests passed.". The check target now cleans up the temporary files it creates along the way. Integrated the local getopt() from Base64 to permit building on Win32. I modified getopt.c to remove Autoconf trash from the includes. We always use our own getopt()--never the one from the system (if any). Csvmidi printed nonsense track numbers in verbose output. Fixed. Added a version number definition in version.h and included the version number in the "-u" option output from midicsv.c and csvmidi.c. 2004 January 1 Fixed some harmless compiler warnings in csvmidi.c and midicsv.c when WIN32 binaries were built with Microsoft Visual C 5.0. Copied the built WIN32 executables: Csvmidi.exe and Midicsv.exe into the release directory, along with the Workspace and Project files: Miditools.dsw, Csvmidi.dsp, and Midicsv.dsp and added to the list of files included in the distribution by the Makefile. 2004 January 3 Text fields in file meta-events which contained zero bytes were incorrectly truncated at the zero byte by midicsv.c. Since these fields are specified in MIDI as a length followed by arbitrary bytes, zero is permissible in such fields. I modified textcsv() in midicsv.c to permit zero bytes, which are output as octal escapes like other control characters. If a MIDI text field in CSV contained a zero byte (duly quoted as \000), csdmidi.c would truncate the MIDI text field at the zero byte, interpreting it as a C string terminator. I modified CSVscanField() in csv.c to store the length of the field it scans in a global variable CSVfieldLength and modified csvmidi.c to use this field to determine how many text bytes to write, rather than incorrectly applying strlen() to the text to determine its length. Modified CSVscanField() in csv.h to take a second argument which gives the length of the field buffer and refuse to store outside the buffer. Characters which would overflow the buffer are discarded, and the buffer is guaranteed to be terminated by a zero byte for those who count on treating it as a C string. The actual field length, including characters dropped, may be obtained from CSVfieldLength (see previous paragraph), and may thus be used to determine whether a truncation has occurred. Modified textcsv() in midicsv.c to quote only non-graphic characters in ISO 8859-1. This permits ISO accented and punctuation characters to appear in text strings without being quoted as octal sequences. If CSV_Quote_ISO is not defined, this function reverts to its previous behaviour--octal quoting all characters which aren't in the 7-bit ASCII graphic character set. Rewrote getCSVline() in csvmidi.c to dynamically allocate the CSV line input buffer and expand it as required to accommodate arbitrarily long lines (assuming they fit in available memory). This avoids the need for a long compiled-in input buffer and worries about possible truncation when reading outrageously long system exclusive byte dump records. 2004 January 12 Modified the "dist" target in the Makefile to create an archive whose name includes the version number (specified by "VERSION" in the Makefile, regrettably uncoupled to version.h, and which creates an eponymous directory into which its files are extracted. Created a logo for the Web page, which is maintained in the subdirectory IMGWORK. 2004 January 17 Ported the WIN32 build to Visual C++ .NET, building on Ovni. As usual, I had to add "libc.lib" to the list of explicitly included libraries in the Project/Properties/Linker/Input/Additional Dependencies item and "libcd.lib" to the .../Ignore Specific Libraries list to get around the "Library is Corrupt" dagger in the back from .NET. Fixed three warnings in the Meta(SetTempoMetaEvent) case in midicsv.c where 8 bit values weren't explicitly cast to byte before being stored into the temporary track item array. While I was at it, I fixed several other places where a (char) cast was inadvertently used on a value being stored into a byte type. None of these could cause any problems apart from compiler warnings. Imported the "Midicsv.sln" solution file and the two projects, "Csvmidi.vcproj" and "Midicsv.vcproj" from the .NET build environment, along with the generated Release executables. Modified the WIN32 file list in the Makefile to include the .NET .sln and .vcproj files in the distribution instead of the .dsw and .dsp equivalents from Visual C 5.0. Created a rudimentary round-trip test for WIN32 builds in W32test.bat, which I added to the WIN32 file list in the Makefile. 2004 January 25 Changed the ce3k.csv sample file to use a General MIDI organ patch for the track rather than a piano. Much work documenting CSV message formats in midicsv.5; much work remains. 2004 February 6 Completed documentation of CSV message formats in midicsv.5. 2004 February 7 Key signature meta-events for flat keys were not treated as signed values, but rather output as two's complement unsigned bytes between 128 and 255. I fixed midicsv.c to output these values as signed integers. The test for major and minor key indicators in the Key_signature record in csvmidi.c was backwards. In addition, the process of assembling the MIDI output event overwrote the major/minor string, causing the signature to always be considered as minor. Both were fixed. 2004 February 8 Implemented range checking for all fields in csvmidi.c. The field checking for items such as time signature and SMPTE offsets is permissive in the sense that any value which fits into the MIDI file binary field (in all such cases, as it happens, one byte) are accepted without warnings. The key signature field is, however, required to specify a key in the range -7 to 7 and a major/minor indicator of "major" or "minor" (case-insensitive). The "install" target in the Makefile neglected to copy the file format document, midicsv.5, to the corresponding manual page directory. I further modified the install target to use a more or less standard "install" command to guarantee the target directories are created. Added an "uninstall" target to the Makefile which deletes the files copied by "install". Updated the README file to reflect name changes due to the port of the WIN32 build to Visual Studio .NET. 2004 February 9 Darned if I knew you could carry a running status across a file meta-event or Sysex! Well, you can, and I managed to stumble over a MIDI file (one, among hundreds I've tested with) which does it. As I'd written the code, I preserved any byte with the high bit set as the running status, which caused the 0xFF to be saved as the running status after a meta-event, so if the next item didn't begin with a status byte, it would be misinterpreted as a meta-event, with disastrous consequences downstream. I modified midicsv.c to only save genuine channel status events (0x00-0xEF) as running status. Eliminated some obsolete code in midicsv.c associated with the way we used to output text in meta-events before the advent of textcsv(). 2004 February 10 It turns out textcsv() in midicsv.c did not actually handle text in meta-events with full generality (i.e. up to 2^28 bytes in length). I rewrote the function and code that calls it to entirely eliminate all intermediate memory use. The revised textcsv() is passed the output stream pointer and writes characters directly to it, quoting as required on the fly. Well, csvmidi.c had its own weaknesses when it came to really long strings. I modified CSVscanField() in csv.c to work with a dynamically allocated buffer (which can either be provided by that function on the first call or supplied by the caller) which is passed as a pointer to the pointer to the buffer, along with a pointer to its length. The buffer is expanded as required, according to the BufferInitial and BufferExpansion definitions in csv.c which are set to 256 and 1024 bytes respectively. Calls to CSVscanField() in csvmidi.c were modified accordingly, with the field buffer f now dynamically allocated and its length kept in flen. To test this, I ran the entire test suite with an initial field buffer length of 8 bytes and expansion increment of 4 bytes and everything worked fine. If the realloc() to dynamically expand the track assembly buffer in outbyte() in csvmidi.c failed, the code would attempt to store through a NULL pointer. I added a check for failure of the realloc() which issues a message to standard error and exits with a return code of 2 in case the allocation fails. Also, the trackbufl (current track buffer length) was declared as a long, a heritage from the 1988-vintage progenitor of this code which ran on a 16 bit MS-DOS system. I changed it to a more conventional int, which avoids worries about printf format phrase compatibilities. Code in csvmidi.c sloppily reused the CSV field scanning buffer to assemble MIDI parameter bytes for meta-events. This actually did no harm, since in every case all fields have been scanned prior to this operation, but it's tacky and looked even worse now that the field buffer is dynamically allocated within csv.c. I changed all the field assembly code to use a small static buffer of[], which is dedicated to this purpose. Note that Sysex and Sequencer Specific events, which may have arbitrary amounts of data, do not use this static buffer but emit the data on the fly, avoiding worries about overflow. Rebuilt the WIN32 executables. Everything built without any warnings and passed the regression test. 2004 February 11 Wrote the first cut of torture.pl, a program to generate the torture test for csvmidi and midicsv. The test is generated programmatically because we want to include some very long byte array (Sysex, etc.) events and text strings, and cranking them out by a Perl program avoid the need to include a huge torture test CSV file in the distribution. The current version of the torture test includes hard-coded examples of every kind of event we recognise, including an Unknown_meta_event used to test pass-through of unknowns and another used to fake a Key_signature, which confirms handling of unknown meta-events is compatible with known ones. The programmed part outputs a 5123 byte System_exclusive, a 11213 byte ASCII text string, a 74219 byte arbitrary string (all byte values from 0 to 255), a 3497861 byte Sequencer_specific, and finally a 4256233 byte arbitrary string, all pseudorandomly generated. The pseudorandom generator is seeded with a constant valuf of 1234 so the test is reproducible and output can be saved for regression testing. Missing or bad fields on records with an arbitrary number of byte srguments (such as SysEx) were erroneously reported as field 4 regardless of the actual field in error. I created a new xfields() function in csvmidi.c which accepts the start of the fields to be parsed as an argument, used that for variable length byte argument parsing, and created a wrapper nfields() which passes 4 for the usual case of a fixed number of numeric arguments after the Type field. Added the ability to csvmidi.c to recover from missing or unparseable fields in CSV records which contain a variable number of byte arguments (SysEx for example). The handlers for these events now call a new function, checkBytes(), to pre-parse and error check the byte list before emitting the event code and length to the output MIDI file. If an error is detected, they can now ignore the erroneous record with no damage to the MIDI file. This makes all CSV syntax errors now recoverable. Added a "torture" target to the Makefile to run the torture test. Included the "bad.csv" file in the distribution. This is a hand-crafted CSV file full of errors to verify csvmidi's error detection and recovery. Modified the comment detection code in csvmidi.c to permit white space before the comment delimiter. Previously, it had to appear in column one; now it must simply be the first nonblank character on the line. Added range checking for all arguments of the Header record in csvmidi.c. Built with GCC 3.2.2 and re-tested to make sure no warnings or problems were manifest. All went well. The sscanf() function in the GCC library apparently calls the equivalent of strlen() on its argument before parsing it. The "sscanf(s, "%3o")" used in csv.c to parse backslash-escaped octal characters, and string parsing slowed down enormously for long strings with many escaped characters (as produced by the torture test). I rewrote the octal escape parser to scan the digits with in-line code, and string parsing sped up for strings in the megabyte range by more than a factor of 1000. 2004 February 12 Added documentation of CSV comment syntax to midicsv.5 file format manual page. Rebuilt WIN32 executables and imported Release binaries into development directory. 2004 February 13 Integrated HTML versions of manual pages produced by man2html (with substantial hand patching of the output) into the Web page. 2004 February 14 To simplify bulk (or, more precisely, near-blind) processing of CSV, I added a suffix of "_c" to all channel event messages (note on, note off, program, pitch bend, etc.) and "_t" to all meta-events which take a text string argument. Missing or erroneous numeric CSV fields detected by the xfields() function in csvmidi.c generated error messages with indentation inconsistent with error messages reported elsewhere. Fixed. 2004 February 17 Added two targets to the Makefile which cause the build of the distribution archives to fail if the WIN32 executables are out of date with respect to the source code. Added a new general_midi.pl file to the distribution which defines two hashes, %GM_Patch and %GM_Percussion, which permit specifying General MIDI patch numbers and percussion note numbers as descriptive strings. Integrated two new demo programs, drummer.pl, a rudimentary drum machine, and acomp.pl, a moronic algorithmic composer, to serve as examples of ab ovo creation of MIDI files using Perl and csvmidi. 2004 February 18 Added a status check and go/no-go results report to the "torture" target in the Makefile. 2008 January 20 Updated version to "Version 1.1 (January 2008)". Both midicsv.c and csvmidi.c handled the two byte argument to a Pitch bend event in reverse order. The 14 bit pitch bend value (with 8192 indicating no bend) is supposed to be sent as two 7 bit values with the least significant byte first, but the code processed the bytes with the most significant byte first. Since both programs had the same error, a "round trip" from MIDI to CSV and back to MIDI would not damage the file. I fixed both programs to process the bytes in the correct order. (Reported by Pete Goodeve.) Fixed a GCC 4.1.2 -Wall quibble about the signedness of a byte pointer value in csvmidi.c. ./midicsv-1.1/exchannel.pl0000644000175000017500000000073410013513466014407 0ustar kamalkamal # Extract events for a channel from a MIDI CSV file. # All non-channel events are output unchanged. # Comments are discarded. $which_channel = 9; # Extract General MIDI percussion for demo while ($a = <>) { if (!($a =~ m/\s*[\#\;]/)) { # Ignore comment lines if ($a =~ m/\s*\d+\s*,\s*\d+\s*,\s*\w+_c\s*,\s*(\d+)/) { if ($1 == $which_channel) { print($a); } } else { print($a); } } } ./midicsv-1.1/Csvmidi.vcproj0000644000175000017500000000665110002105160014716 0ustar kamalkamal ./midicsv-1.1/midifile.h0000644000175000017500000000424006610173405014037 0ustar kamalkamal/* MIDI File Definitions */ /* MIDI command codes */ typedef enum { /* Channel voice messages */ NoteOff = 0x80, NoteOn = 0x90, PolyphonicKeyPressure = 0xA0, ControlChange = 0xB0, ProgramChange = 0xC0, ChannelPressure = 0xD0, PitchBend = 0xE0, /* Channel mode messages */ ChannelMode = 0xB8, /* System messages */ SystemExclusive = 0xF0, SystemCommon = 0xF0, SystemExclusivePacket = 0xF7, SystemRealTime = 0xF8, SystemStartCurrentSequence = 0xFA, SystemContinueCurrentSequence = 0xFB, SystemStop = 0xFC, /* MIDI file-only messages */ FileMetaEvent = 0xFF } midi_command; /* MIDI file meta-event codes */ typedef enum { SequenceNumberMetaEvent = 0, TextMetaEvent = 1, CopyrightMetaEvent = 2, TrackTitleMetaEvent = 3, TrackInstrumentNameMetaEvent = 4, LyricMetaEvent = 5, MarkerMetaEvent = 6, CuePointMetaEvent = 7, ChannelPrefixMetaEvent = 0x20, PortMetaEvent = 0x21, EndTrackMetaEvent = 0x2F, SetTempoMetaEvent = 0x51, SMPTEOffsetMetaEvent = 0x54, TimeSignatureMetaEvent = 0x58, KeySignatureMetaEvent = 0x59, SequencerSpecificMetaEvent = 0x7F } midifile_meta_event; /* The following structures are for in-memory manipulation of MIDI file components and must not be used for reading or writing MIDI files to external media. MIDI files must be written in big-endian byte order with no padding to word boundaries and I/O code must comply with this format regardless of the host's in-memory representation. */ /* MIDI file header */ #define MIDI_Header_Sentinel "MThd" struct mhead { char chunktype[4]; /* Chunk type: "MThd" */ long length; /* Length: 6 */ short format; /* File format */ short ntrks; /* Number of tracks in file */ short division; /* Time division */ }; /* MIDI track header */ #define MIDI_Track_Sentinel "MTrk" struct mtrack { char chunktype[4]; /* Chunk type: "MTrk" */ long length; /* Length of track */ }; ./midicsv-1.1/getopt.c0000644000175000017500000001117107774306115013563 0ustar kamalkamal/* Date: Tue, 25 Dec 84 19:20:50 EST From: Keith Bostic To: genrad!sources Subject: public domain getopt(3) There have recently been several requests for a public domain version of getopt(3), recently. Thought this might be worth reposting. Keith Bostic ARPA: keith@seismo UUCP: seismo!keith ====================================================================== In April of this year, Henry Spencer (utzoo!henry) released a public domain version of getopt (USG, getopt(3)). Well, I've been trying to port some USG dependent software and it didn't seem to work. The problem ended up being that the USG version of getopt has some external variables that aren't mentioned in the documentation. Anyway, to fix these problems, I rewrote the public version of getopt. It has the following advantages: -- it includes those "unknown" variables -- it's smaller/faster 'cause it doesn't use the formatted output conversion routines in section 3 of the UNIX manual. -- the error messages are the same as S5's. -- it has the same side-effects that S5's has. -- the posted bug on how the error messages are flushed has been implemented. (posting by Tony Hansen; pegasus!hansen) I won't post the man pages since Henry already did; a special note, it's not documented in the S5 manual that the options ':' and '?' are illegal. It should be obvious, but I thought I'd mention it... This software was derived from binaries of S5 and the S5 man page, and is (I think?) totally (I'm pretty sure?) compatible with S5 and backward compatible to Henry's version. Keith Bostic ARPA: keith@seismo UUCP: seismo!keith *UNIX is a trademark of Bell Laboratories Further modified by John Walker on 2001-02-19 to accept GNU-style "--xxx" options. The original code considered any option beginning with "--" as the end of the options list. I changed the logic so that only "--" without a suffix is treated as an end of option marker. .. cut along the dotted line ......................................... */ #include #include #include "getopt.h" /* * get option letter from argument vector */ int optind = 1, /* index into parent argv vector */ optopt; /* character checked for validity */ char *optarg; /* argument associated with option */ #define BADCH (int)'?' #define EMSG "" #define tell(s) fputs(*nargv,stderr);fputs(s,stderr); \ fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); int Getopt(int nargc, char *nargv[], char *ostr) { static char *place = EMSG; /* option letter processing */ static char *lastostr = (char *) 0; register char *oli; /* option letter list index */ char *index(); /* LANCE PATCH: dynamic reinitialization */ if (ostr != lastostr) { lastostr = ostr; place = EMSG; } if(!*place) { /* update scanning pointer */ if((optind >= nargc) || (*(place = nargv[optind]) != '-') || ! *++place) { place = EMSG; return(EOF); } /* Test for "--" as terminator of options. Note that options which begin with "--" remain acceptable (for example, "--help"); only "--" by itself terminates the option list. */ if (*place == '-' && place[1] == 0) { ++optind; return(EOF); } } /* option letter okay? */ if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) { if(!*place) ++optind; tell(": illegal option -- "); } if (*++oli != ':') { /* don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* need an argument */ if (*place) optarg = place; /* no white space */ else if (nargc <= ++optind) { /* no arg */ place = EMSG; tell(": option requires an argument -- "); } else optarg = nargv[optind]; /* white space */ place = EMSG; ++optind; } return(optopt); /* dump back option letter */ } ./midicsv-1.1/torture.pl0000644000175000017500000001103510014502441014132 0ustar kamalkamal # Generate "torture test" for MidiCSV / CSVmidi # The output is a CSV file written on standard output # which should be fed to through CSVMIDI and MIDICSV # and the output compared. srand(1234); # Make pseudorandom sequences repeatable print << 'EOD'; ; ; MIDIcsv Torture Test # 0, 0, Header, 1, 4, 480 1, 0, Start_track 1, 0, TiTlE_t, "MidiCSV Torture Test" 1,0, copyRight_t, " 1808 L. van Beethoven. This document is in the public domain." 1, 0, SMPTE_offset, 96, 0, 0, 0, 0 1, 0, key_signature, 0, minor 1, 0, Time_signature, 4, 2, 24, 8 1, 100, Text_t, "The tempo varies somewhat in this passage." 1, 482, Tempo, 335977 1, 961, Tempo, 333493 1, 1442, Tempo, 333319 1, 10578, End_track 2, 0, Start_track 2, 0, Sequence_number, 64000 2, 0, MIDI_port, 0 2, 0, Instrument_Name_t, "Brass Section" 2, 0, Program_c, 1, 61 2, 259, marker_T, "Bom bom bom..." 2, 259, Lyric_t, "Bom" 2, 259, Note_on_c, 1, 67, 104 2, 501, Note_off_c, 1, 67, 0 2, 525, Lyric_t, "Bom" 2, 525, Note_on_c, 1, 67, 104 2, 740, Note_on_c, 1, 67, 0 2, 764, Lyric_t, "Bom" 2, 764, Note_on_c, 1, 67, 104 2, 978, Note_on_c, 1, 67, 0 2, 1000, Marker_t, "...BOM!" 2, 1003, Lyric_t, "BOMMMMM!" 2, 1003, Note_on_c, 1, 63, 104 2, 4809, Note_on_c, 1, 63, 0 # Second 4 notes 2, 4850, Cue_point_t, "Conductor falls off podium" 2, 5047, Instrument_name_t, """Rock"" Organ" 2, 5048, Program_c, 1, 18 2, 5048, Note_on_c, 1, 65, 104 2, 5100, pitch_bend_c, 1, 10000 2, 5100, channel_aftertouch_c, 1, 127 2, 5200, pitch_bend_c, 1, 4000 2, 5289, note_off_c, 1, 65, 0 2, 5300, Control_c, 1, 91, 120 2,5300, Control_c, 1, 7, 40 2, 5311, Note_on_c, 1, 65, 104 2,5400,pitch_BEND_c,1,0 2, 5526, Note_on_c, 1, 65, 0 2, 5540, control_c, 1, 7, 127 2,5540, control_c, 1, 10, 0 2, 5549, Pitch_Bend_c, 1,8192 2, 5549, Note_on_c, 1, 65, 104 2, 5766, Note_on_c, 1, 65, 0 2,5790, control_c, 1, 10, 127 2, 5790, Note_on_c, 1, 62, 104 2, 9000, Poly_aftertouch_c, 1, 62, 127 2, 10578, Note_off_c, 1, 62, 0 2, 10578, End_track 3, 0, Start_track 3, 10578, End_track 4, 0, Start_Track 4, 10, Channel_prefix, 1 4, 400, System_exclusive, 10, 0, 255, 127, 30, 96, 255, 224, 108, 31, 0 4, 510, System_exclusive, 4, 0, 121, 31, 19 4, 1071, channel_prefix, 15 4, 1071, System_exclusive_packet, 10, 255, 18, 33, 111, 0, 77, 201, 7, 4, 255 4, 1076, System_exclusive_packet, 0 4, 1108, System_exclusive_packet, 3, 0, 0, 0 4, 2000, Sequencer_specific, 10, 80, 211, 54, 71, 229, 0, 13, 128, 12, 40 4, 2020, Sequencer_specific, 0 4, 3000, Unknown_meta_event, 121, 9, 39, 201, 118, 6, 91, 223, 0, 78, 56 ; The following event is actually ; 4, 3030, Key_signature, -4, "major" # coded as an Unknown_meta_event. 4, 3100, Unknown_meta_event, 89, 2, 252, 0 EOD # Now programmatically generate some long strings # and byte vectors to push the limits. # Moderately long Sysex $n = 5123; print("4, 4000, System_exclusive, $n"); &obytes($n); print("\n"); # Moderately long pure ASCII text string $n = 11213; print("4, 4100, Text_t, \""); for ($i = 0; $i < $n; $i ++) { $r = chr(ord(' ') + int(rand(127 - ord(' ')))); if ($r eq '"') { print('"'); } elsif ($r eq '\\') { print('\\'); } print("$r"); } print("\"\n"); # Rather long arbitrary text string print("4, 4200, Text_t, "); &ostring(74219); print("\n"); # Really long Sequence_specific $n = 3497861; # 250,000th prime! print("4, 4300, Sequencer_specific, $n"); &obytes($n); print("\n"); # Really long arbitrary text string print("4, 4400, Lyric_t, "); &ostring(4256233); # 300,000th prime! print("\n"); # Wind up the track and the file with canned data print << 'EOD'; 4,10500, End_track 0, 0, End_of_file EOD # Generate a random character string containing all # byte values from 0 through 255. The argument gives # the length in bytes to generate. sub ostring { local ($howlong) = @_; local ($i, $r); print('"'); for ($i = 0; $i < $howlong; $i ++) { $r = chr(int(rand(256))); if ($r eq '"') { print('"'); } elsif ($r eq '\\') { print('\\'); } elsif ((ord($r) < ord(' ')) || ((ord($r) >= 127) && (ord($r) <= 160))) { printf("\\%03o", ord($r)); next; } print("$r"); } print('"'); } # Generate a byte sequence whose length is # given by the argument. sub obytes { local ($howlong) = @_; local ($i, $r); for ($i = 0; $i < $howlong; $i ++) { $r = int(rand(256)); print(", $r"); } } ./midicsv-1.1/drummer.pl0000644000175000017500000000232710014502305014104 0ustar kamalkamal require 'general_midi.pl'; # Repeats, Note, # Duration, Velocity @track = (4, $GM_Percussion{'Acoustic Bass Drum'}, 480, 127, 4, $GM_Percussion{'Low-Mid Tom'}, 240, 127, 1, 0, 120, 0, 2, $GM_Percussion{'Hand Clap'}, 240, 127, 1, 0, 240, 0 ); print << "EOD"; 0, 0, Header, 1, 1, 480 1, 0, Start_track 1, 0, Tempo, 500000 EOD $time = 0; &loop(4, @track); print << "EOD"; 1, $time, End_track 0, 0, End_of_file EOD sub note { # ¬e($note_number, $duration [, $velocity]) local ($which, $duration, $vel) = @_; if ($which > 0) { if (!defined($vel)) { $vel = 127; } print("1, $time, Note_on_c, 9, $which, $vel\n"); } $time += $duration; if ($which > 0) { print("1, $time, Note_off_c, 9, $which, 0\n"); } } sub loop { # &loop($ntimes, @track) local ($loops, @tr) = @_; local ($i, $r); for ($i = 0; $i < $loops; $i++) { local @t = @tr; while ($#t > 0) { local ($repeats, $note, $duration, $velocity) = splice(@t, 0, 4); for ($r = 0; $r < $repeats; $r++) { ¬e($note, $duration, $velocity); } } } } ./midicsv-1.1/csvmidi.10000644000175000017500000000625610011734442013627 0ustar kamalkamal'\" t .TH CSVMIDI 1 "9 FEB 2004" .UC 4 .SH NAME csvmidi \- encode CSV file as MIDI .SH SYNOPSIS .B csvmidi [ .B \-u .B \-v .B \-x .B \-z ] [ .I infile [ .I outfile ] ] .SH DESCRIPTION .B csvmidi reads a CSV (Comma-Separated Value) file in the format written by .B midicsv and creates the equivalent standard MIDI file. .SH OPTIONS .TP 10 .B \-u Print how-to-call information. .TP .B \-v Print verbose debugging information on standard error. The MIDI file header is dumped, along with the length of each track in the file. .TP .B \-x MIDI streams support a rudimentary form of compression in which successive events with the same ``status'' (event type and channel) may omit the status byte. By default .B csvmidi avails itself of this compression. If the .B \-x option is specified, the status byte is emitted for all events\-it is never compressed even when the MIDI standard permits it to be. .TP .B \-z Most errors detected in CSV records cause a warning message to be displayed on standard error and the record ignored. The .B \-z option causes .B csvmidi to immediately terminate processing when the first error is detected. .SH "EXIT STATUS" If no errors or warnings are detected .B csvmidi exits with status 0. A status of of 1 is returned if one or more errors were detected in the CSV input file, while a status of 2 indicates a syntax error on the command line or inability to open the input or output file. .SH FILES If no .I infile is specified or .I infile is .RB `` \- '', .B csvmidi reads its input from standard input; if no .I outfile is given or .I outfile is .RB `` \- '', MIDI output is written to standard output. The input and output are processed in a strictly serial manner; consequently .B csvmidi may be used in pipelines without restrictions. .SH BUGS .PP .B csvmidi assumes its input is in the format written by .BR midicsv . If supplied a CSV file with well-formed records which nonetheless makes no semantic sense as a MIDI file, the results will, in all likelihood, simply perplex any program or instrument to which it's sent. .B csvmidi checks for missing fields and range checks all numeric values, but does not perform higher-level consistency checking (for example, making sure that every note on event is paired with a subsequent note off). That level of verification, if required, should be done on the CSV file before it is processed by .BR csvmidi . .PP Exporting a file to CSV with .B midicsv and then importing it with .B csvmidi is not guaranteed to create an identical MIDI file. MIDI files support compression modes which are not obligatory. A MIDI file exported to CSV and then re-imported should, however, be .I equivalent to the original file and should, if exported to CSV, be identical to the CSV exported from the original file. .PP Please report problems to .BR bugs@fourmilab.ch . .SH "SEE ALSO" .PD .BR midicsv (1), .BR midicsv (5) .ne 10 .SH AUTHOR .ce 2 John Walker http://www.fourmilab.ch/ .PP This software is in the public domain. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, without any conditions or restrictions. This software is provided ``as is'' without express or implied warranty. ./midicsv-1.1/Midicsv.sln0000644000175000017500000000247510002105152014210 0ustar kamalkamalMicrosoft Visual Studio Solution File, Format Version 7.00 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Csvmidi", "Csvmidi.vcproj", "{FA22F5FC-B0B2-4626-B402-7EE4E023CA5B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Midicsv", "Midicsv.vcproj", "{2A76FE95-C368-4A6F-BFB7-EAF1DB195CCB}" EndProject Global GlobalSection(SolutionConfiguration) = preSolution ConfigName.0 = Debug ConfigName.1 = Release EndGlobalSection GlobalSection(ProjectDependencies) = postSolution EndGlobalSection GlobalSection(ProjectConfiguration) = postSolution {FA22F5FC-B0B2-4626-B402-7EE4E023CA5B}.Debug.ActiveCfg = Debug|Win32 {FA22F5FC-B0B2-4626-B402-7EE4E023CA5B}.Debug.Build.0 = Debug|Win32 {FA22F5FC-B0B2-4626-B402-7EE4E023CA5B}.Release.ActiveCfg = Release|Win32 {FA22F5FC-B0B2-4626-B402-7EE4E023CA5B}.Release.Build.0 = Release|Win32 {2A76FE95-C368-4A6F-BFB7-EAF1DB195CCB}.Debug.ActiveCfg = Debug|Win32 {2A76FE95-C368-4A6F-BFB7-EAF1DB195CCB}.Debug.Build.0 = Debug|Win32 {2A76FE95-C368-4A6F-BFB7-EAF1DB195CCB}.Release.ActiveCfg = Release|Win32 {2A76FE95-C368-4A6F-BFB7-EAF1DB195CCB}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection GlobalSection(ExtensibilityAddIns) = postSolution EndGlobalSection EndGlobal ./midicsv-1.1/Midicsv.vcproj0000644000175000017500000000674510002105161014723 0ustar kamalkamal ./midicsv-1.1/midicsv.c0000644000175000017500000002720210744716774013730 0ustar kamalkamal/* Encode MIDI file into CSV format Designed and implemented in December of 1995 by John Walker. Revised and updated by John Walker in October 1998 and February 2004. http://www.fourmilab.ch/ This program is in the public domain. */ #include #include #include #ifdef _WIN32 #include #include #endif #include "version.h" #include "types.h" #include "midifile.h" #include "midio.h" #include "getopt.h" #define FALSE 0 #define TRUE 1 static char *progname; /* Program name string */ static int verbose = FALSE; /* Debug output */ /* VLENGTH -- Parse variable length item from in-memory track */ static vlint vlength(byte **trk, long *trklen) { vlint value; byte ch; byte *cp = *trk; trklen--; if ((value = *cp++) & 0x80) { value &= 0x7F; do { value = (value << 7) | ((ch = *cp++) & 0x7F); trklen--; } while (ch & 0x80); } #ifdef DUMP fprintf(stderr, "Time lapse: %d bytes, %d\n", cp - *trk, value); #endif *trk = cp; return value; } /* TEXTCSV -- Convert text field to CSV, quoting as necessary. If CSV_Quote_ISO if defined ISO 8859-1 graphic characters are permitted in text strings without octal quoting. Otherwise all characters other than 7-bit ASCII graphics are output as octal. Output is appended directly to output stream fo. */ #define CSV_Quote_ISO static void textcsv(FILE *fo, const byte *t, const int len) { byte c; int i; putc('"', fo); for (i = 0; i < len; i++) { c = *t++; if ((c < ' ') || #ifdef CSV_Quote_ISO ((c > '~') && (c <= 160)) #else (c > '~') #endif ) { putc('\\', fo); fprintf(fo, "%03o", c); } else { if (c == '"') { putc('"', fo); } else if (c == '\\') { putc('\\', fo); } putc(c, fo); } } putc('"', fo); } /* TRACKCSV -- Compile track into CSV written to fo. */ static void trackcsv(FILE *fo, const int trackno, byte *trk, long trklen, const int ppq) { int levt = 0, evt, channel, note, vel, control, value, type; vlint len; byte *titem; vlint abstime = 0; /* Absolute time in track */ #ifdef XDD byte *strk = trk; #endif while (trklen > 0) { vlint tlapse = vlength(&trk, &trklen); abstime += tlapse; fprintf(fo, "%d, %ld, ", trackno, abstime); /* Handle running status; if the next byte is a data byte, reuse the last command seen in the track. */ if (*trk & 0x80) { #ifdef XDD fprintf(fo, " (Trk: %02x NS: %02X : %d) ", *trk, evt, trk - strk); #endif evt = *trk++; /* One subtlety: we only save channel voice messages for running status. System messages and file meta-events (all of which are in the 0xF0-0xFF range) are not saved, as it is possible to carry a running status across them. You may have never seen this done in a MIDI file, but I have. */ if ((evt & 0xF0) != 0xF0) { levt = evt; } trklen--; } else { evt = levt; #ifdef XDD fprintf(fo, " (Trk: %02x RS: %02X : %d) ", *trk, evt, trk - strk); #endif } channel = evt & 0xF; /* Channel messages */ switch (evt & 0xF0) { case NoteOff: /* Note off */ if (trklen < 2) { return; } trklen -= 2; note = *trk++; vel = *trk++; fprintf(fo, "Note_off_c, %d, %d, %d\n", channel, note, vel); continue; case NoteOn: /* Note on */ if (trklen < 2) { return; } trklen -= 2; note = *trk++; vel = *trk++; /* A note on with a velocity of 0 is actually a note off. We do not translate it to a Note_off record in order to preserve the original structure of the MIDI file. */ fprintf(fo, "Note_on_c, %d, %d, %d\n", channel, note, vel); continue; case PolyphonicKeyPressure: /* Aftertouch */ if (trklen < 2) { return; } trklen -= 2; note = *trk++; vel = *trk++; fprintf(fo, "Poly_aftertouch_c, %d, %d, %d\n", channel, note, vel); continue; case ControlChange: /* Control change */ if (trklen < 2) { return; } trklen -= 2; control = *trk++; value = *trk++; fprintf(fo, "Control_c, %d, %d, %d\n", channel, control, value); continue; case ProgramChange: /* Program change */ if (trklen < 1) { return; } trklen--; note = *trk++; fprintf(fo, "Program_c, %d, %d\n", channel, note); continue; case ChannelPressure: /* Channel pressure (aftertouch) */ if (trklen < 1) { return; } trklen--; vel = *trk++; fprintf(fo, "Channel_aftertouch_c, %d, %d\n", channel, vel); continue; case PitchBend: /* Pitch bend */ if (trklen < 1) { return; } trklen--; value = *trk++; value = value | ((*trk++) << 7); fprintf(fo, "Pitch_bend_c, %d, %d\n", channel, value); continue; default: break; } switch (evt) { /* System exclusive messages */ case SystemExclusive: case SystemExclusivePacket: len = vlength(&trk, &trklen); fprintf(fo, "System_exclusive%s, %lu", evt == SystemExclusivePacket ? "_packet" : "", len); { vlint i; for (i = 0; i < len; i++) { fprintf(fo, ", %d", *trk++); } fprintf(fo, "\n"); } break; /* File meta-events */ case FileMetaEvent: if (trklen < 2) { return; } trklen -= 2; type = *trk++; len = vlength(&trk, &trklen); titem = trk; trk += len; trklen -= len; switch (type) { case SequenceNumberMetaEvent: fprintf(fo, "Sequence_number, %d\n", (titem[0] << 8) | titem[1]); break; case TextMetaEvent: #ifdef XDD fprintf(fo, " (Len=%ld Trk=%02x) ", len, *trk); #endif fputs("Text_t, ", fo); textcsv(fo, titem, len); putc('\n', fo); break; case CopyrightMetaEvent: fputs("Copyright_t, ", fo); textcsv(fo, titem, len); putc('\n', fo); break; case TrackTitleMetaEvent: fputs("Title_t, ", fo); textcsv(fo, titem, len); putc('\n', fo); break; case TrackInstrumentNameMetaEvent: fputs("Instrument_name_t, ", fo); textcsv(fo, titem, len); putc('\n', fo); break; case LyricMetaEvent: fputs("Lyric_t, ", fo); textcsv(fo, titem, len); putc('\n', fo); break; case MarkerMetaEvent: fputs("Marker_t, ", fo); textcsv(fo, titem, len); putc('\n', fo); break; case CuePointMetaEvent: fputs("Cue_point_t, ", fo); textcsv(fo, titem, len); putc('\n', fo); break; case ChannelPrefixMetaEvent: fprintf(fo, "Channel_prefix, %d\n", titem[0]); break; case PortMetaEvent: fprintf(fo, "MIDI_port, %d\n", titem[0]); break; case EndTrackMetaEvent: fprintf(fo, "End_track\n"); trklen = -1; break; case SetTempoMetaEvent: fprintf(fo, "Tempo, %d\n", (titem[0] << 16) | (titem[1] << 8) | titem[2]); break; case SMPTEOffsetMetaEvent: fprintf(fo, "SMPTE_offset, %d, %d, %d, %d, %d\n", titem[0], titem[1], titem[2], titem[3], titem[4]); break; case TimeSignatureMetaEvent: fprintf(fo, "Time_signature, %d, %d, %d, %d\n", titem[0], titem[1], titem[2], titem[3]); break; case KeySignatureMetaEvent: fprintf(fo, "Key_signature, %d, \"%s\"\n", ((signed char) titem[0]), titem[1] ? "minor" : "major"); break; case SequencerSpecificMetaEvent: fprintf(fo, "Sequencer_specific, %lu", len); { vlint i; for (i = 0; i < len; i++) { fprintf(fo, ", %d", titem[i]); } fprintf(fo, "\n"); } break; default: if (verbose) { fprintf(stderr, "Unknown meta event type 0x%02X, %ld bytes of data.\n", type, len); } fprintf(fo, "Unknown_meta_event, %d, %lu", type, len); { vlint i; for (i = 0; i < len; i++) { fprintf(fo, ", %d", titem[i]); } fprintf(fo, "\n"); } break; } break; default: if (verbose) { fprintf(stderr, "Unknown event type 0x%02X.\n", evt); } fprintf(fo, "Unknown_event, %02Xx\n", evt); break; } } } /* Main program. */ int main(int argc, char *argv[]) { struct mhead mh; FILE *fp, *fo; long track1; int i, n, track1l; fp = stdin; fo = stdout; progname = argv[0]; while ((n = getopt(argc, argv, "uv")) != -1) { switch (n) { case 'u': fprintf(stderr, "Usage: %s [ options ] [ midi_file ] [ csv_file ]\n", progname); fprintf(stderr, " Options:\n"); fprintf(stderr, " -u Print this message\n"); fprintf(stderr, " -v Verbose: dump header and track information\n"); fprintf(stderr, "Version %s\n", VERSION); return 0; case 'v': verbose = TRUE; break; case '?': fprintf(stderr, "%s: undefined option -%c specified.\n", progname, n); return 2; } } i = 0; while (optind < argc) { switch (i++) { case 0: if (strcmp(argv[optind], "-") != 0) { fp = fopen(argv[optind], "rb"); if (fp == NULL) { fprintf(stderr, "%s: Unable to to open MIDI input file %s\n", progname, argv[optind]); return 2; } } break; case 1: if (strcmp(argv[optind], "-") != 0) { fo = fopen(argv[optind], "w"); if (fo == NULL) { fprintf(stderr, "%s: Unable to to create CSV output file %s\n", progname, argv[optind]); return 2; } } break; } optind++; } #ifdef _WIN32 /* If input is from standard input, set the input file mode to binary. */ if (fp == stdin) { _setmode(_fileno(fp), _O_BINARY); } #endif /* Read and validate header */ readMidiFileHeader(fp, &mh); if (memcmp(mh.chunktype, "MThd", sizeof mh.chunktype) != 0) { fprintf(stderr, "%s is not a Standard MIDI File.\n", argv[1]); return 2; } if (verbose) { fprintf(stderr, "Format %d MIDI file. %d tracks, %d ticks per quarter note.\n", mh.format, mh.ntrks, mh.division); } /* Output header */ fprintf(fo, "0, 0, Header, %d, %d, %d\n", mh.format, mh.ntrks, mh.division); /* Process tracks */ for (i = 0; i < mh.ntrks; i++) { struct mtrack mt; byte *trk; if (i == 0) { track1 = ftell(fp); } readMidiTrackHeader(fp, &mt); if (memcmp(mt.chunktype, "MTrk", sizeof mt.chunktype) != 0) { fprintf(stderr, "Track %d header is invalid.\n", i + 1); return 2; } if (verbose) { fprintf(stderr, "Track %d: length %ld.\n", i + 1, mt.length); } fprintf(fo, "%d, 0, Start_track\n", i + 1); trk = (byte *) malloc(mt.length); if (trk == NULL) { fprintf(stderr, "%s: Cannot allocate %ld bytes for track.\n", progname, mt.length); return 2; } fread((char *) trk, (int) mt.length, 1, fp); if (i == 0) { track1l = (int) (ftell(fp) - track1); } trackcsv(fo, i + 1, trk, mt.length, mh.division); free(trk); } fprintf(fo, "0, 0, End_of_file\n"); return 0; } ./midicsv-1.1/transpose.pl0000644000175000017500000000166110013450142014447 0ustar kamalkamal # Transpose all notes in a CSV MIDI file # This Perl program is an example of how simple it can # be to transform MIDI files in CSV format. This program # filters a CSV MIDI file from standard input to standard # output, shifting all notes by the value given as # $offset. Notes on the $percussion channel are not # shifted. $offset = -12; $percussion = 9; while ($a = <>) { # Recognise Note_on_c and Note_off_c records and crack into: # $1 Start of record # $2 Channel number # $3 Note number # $a Balance of record if ($a =~ s/(\d+,\s*\d+,\s*Note_\w+,\s*(\d+),\s*)(\d+)//) { $n = $3; if ($2 != $percussion) { $n += $offset; } if ($n < 0) { next; } $a = "$1$n$a"; } print($a); } ./midicsv-1.1/getopt.h0000644000175000017500000000050407244227276013570 0ustar kamalkamal extern int optind, optopt; extern char *optarg; /* We do the following naming side-step to permit testing our local getopt() on systems which include getopt() and declare it incompatibly in stdio.h or stdlib.h. */ extern int Getopt(int nargc, char *nargv[], char *ostr); #define getopt(a, b, c) Getopt(a, b, c) ./midicsv-1.1/csvmidi.c0000644000175000017500000005412010744720124013707 0ustar kamalkamal/* Encode CSV file into MIDI The CSV file must be in the format generated by midicsv--while you can add and delete events and change their contents, the overall organisation of the file must be the same. Designed and implemented in October of 1998 by John Walker. Revised and updated in February of 2004 by the same perp. http://www.fourmilab.ch/ This program is in the public domain. */ #define PROG "csvmidi" #include #include #include #include #ifdef _WIN32 #include #include #define strcasecmp _stricmp #endif #include "version.h" #include "types.h" #include "midifile.h" #include "midio.h" #include "csv.h" #include "getopt.h" /* List of comment delimiters recognised in CSV files. These are whole-line comments which must be marked by one of the following characters as the first nonblank character of a record. Rest-of-line comments are not implemented. Since the track number always begins a data record, any non-numeric character may be used as a comment delimiter. */ #define COMMENT_DELIMITERS "#;" #define FALSE 0 #define TRUE 1 #define ELEMENTS(array) (sizeof(array)/sizeof((array)[0])) /* Codes for control items. */ typedef enum { Header, Start_track, End_of_file } controlMessages; /* The following table lists all possible item codes which may appear in a CSV-encoded MIDI file. These should be listed in order of frequency of occurrence since the list is searched linearly. */ struct mitem { char *name; int icode; }; #define EVENT 0 #define META 0x100 #define MARKER 0x200 #define Event(x) ((x) | EVENT) #define Meta(x) ((x) | META) #define Marker(x) ((x) | MARKER) static struct mitem mitems[] = { { "Note_on_c", Event(NoteOn) }, { "Note_off_c", Event(NoteOff) }, { "Pitch_bend_c", Event(PitchBend) }, { "Control_c", Event(ControlChange) }, { "Program_c", Event(ProgramChange) }, { "Poly_aftertouch_c", Event(PolyphonicKeyPressure) }, { "Channel_aftertouch_c", Event(ChannelPressure) }, { "System_exclusive", Event(SystemExclusive) }, { "System_exclusive_packet", Event(SystemExclusivePacket) }, { "Sequence_number", Meta(SequenceNumberMetaEvent) }, { "Text_t", Meta(TextMetaEvent) }, { "Copyright_t", Meta(CopyrightMetaEvent) }, { "Title_t", Meta(TrackTitleMetaEvent) }, { "Instrument_name_t", Meta(TrackInstrumentNameMetaEvent) }, { "Lyric_t", Meta(LyricMetaEvent) }, { "Marker_t", Meta(MarkerMetaEvent) }, { "Cue_point_t", Meta(CuePointMetaEvent) }, { "Channel_prefix", Meta(ChannelPrefixMetaEvent) }, { "MIDI_port", Meta(PortMetaEvent) } , { "End_track", Meta(EndTrackMetaEvent) }, { "Tempo", Meta(SetTempoMetaEvent) }, { "SMPTE_offset", Meta(SMPTEOffsetMetaEvent) }, { "Time_signature", Meta(TimeSignatureMetaEvent) }, { "Key_signature", Meta(KeySignatureMetaEvent) }, { "Sequencer_specific", Meta(SequencerSpecificMetaEvent) }, { "Unknown_meta_event", Meta(0xFF) }, { "Header", Marker(Header) }, { "Start_track", Marker(Start_track) }, { "End_of_file", Marker(End_of_file) }, }; static char *progname; /* Program name string */ static int verbose = FALSE; /* Debug output */ static int zerotol = FALSE; /* Any warning terminates processing */ static int errors = 0; /* Errors and warnings detected */ static char *s = NULL; /* Dynamically expandable CSV input buffer */ /* OUTBYTE -- Store byte in track buffer. */ static byte of[10]; static byte *trackbuf = NULL, *trackbufp; static int trackbufl; static char *f = NULL; static int flen = 0; #define Warn(msg) { errors++; fprintf(stderr, "%s: Error on line %d:\n %s\n %s.\n", PROG, lineno, s, msg); if (zerotol) { exit(1); } } static void outbyte(const byte c) { long l = trackbufp - trackbuf; if (l >= trackbufl) { trackbuf = realloc(trackbuf, trackbufl += 16384); if (trackbuf == NULL) { fprintf(stderr, "%s: Unable to allocate memory to expand track buffer to %d bytes.\n", PROG, trackbufl); exit(2); } trackbufp = trackbuf + l; } *trackbufp++ = c; } /* OUTEVENT -- Output event, optimising repeats. */ static long abstime, tabstime = 0; static void outVarLen(const vlint value); static int optimiseStatus = TRUE, lastStatus = -1; static void outevent(const byte c) { outVarLen(abstime - tabstime); tabstime = abstime; if (!optimiseStatus || (c != lastStatus)) { outbyte(c); lastStatus = c; } } /* OUTMETA -- Output file meta-event. */ static void outmeta(const byte c) { /* Running status may not be used by file meta-events, and meta-events cancel any running status. */ lastStatus = -1; outevent(FileMetaEvent); outbyte(c); } /* OUTSHORT -- Output two-byte value to track buffer. */ static void outshort(const short v) { outbyte((byte) ((v >> 8) & 0xFF)); outbyte((byte) (v & 0xFF)); } /* OUTBYTES -- Output a linear array of bytes. */ static void outbytes(const byte *s, const int n) { int i; outVarLen(n); for (i = 0; i < n; i++) { outbyte(*s++); } } /* OUTVARLEN -- Output variable length number. */ static void outVarLen(const vlint v) { vlint value = v; long buffer; buffer = value & 0x7F; while ((value >>= 7) > 0) { buffer <<= 8; buffer |= 0x80; buffer += (value & 0x7F); } while (TRUE) { outbyte((byte) (buffer & 0xFF)); if (buffer & 0x80) { buffer >>= 8; } else { break; } } } /* XFIELDS -- Parse one or more numeric fields. Returns FALSE if all fields were scanned successfully, TRUE if an error occurred. The fbias argument gives the absolute field number of the first field to be parsed; it is used solely to identify fields in error messages. */ #define MAX_NFIELDS 10 static int lineno = 0; static long nfld[MAX_NFIELDS]; static char *csvline; static int xfields(const int n, const int fbias) { int i; #ifndef ndebug if (n > MAX_NFIELDS) { fprintf(stderr, "%s: Internal error: nfields(%d) exceeds max of %d.\n", PROG, n, MAX_NFIELDS); abort(); } #endif for (i = 0; i < n; i++) { if (!CSVscanField(&f, &flen)) { errors++; fprintf(stderr, "%s: Error on line %d:\n %s\n Missing field %d.\n", PROG, lineno, s, fbias + i); return TRUE; } if (sscanf(f, "%ld", &nfld[i]) != 1) { errors++; fprintf(stderr, "%s: Error on line %d:\n %s\n Invalid field %d.\n", PROG, lineno, s, fbias + i); return TRUE; } } return FALSE; } /* XFIELDS -- Parse one or more numeric fields. This is a wrapper for xfields which specifies the default starting field of 4 used by most events. */ static int nfields(const int n) { return xfields(n, 4); } /* CHECKBYTES -- Pre-parse a sequence of arguments representing a byte vector. If an error is detected, return TRUE. Otherwise, reset the CSV parser to the first byte of the sequence and return FALSE. This permits code which handles arbitrary length byte vectors to recover from syntax errors and ignore the line prior to the irreversible step of emitting the event type and length. */ static int checkBytes(const int fieldno, const int length) { int i; for (i = 0; i < length; i++) { if (xfields(1, i + fieldno)) { if (zerotol) { exit(1); } return TRUE; } } /* The preliminary parsing pass passed. (Got that?) Now restart the CSV parser at the start of the line and ignore fields prior to the first byte of the data, leaving them ready to be processed by the actual translation code. */ CSVscanInit(s); for (i = 0; i < (fieldno - 1); i++) { CSVscanField(&f, &flen); } return FALSE; } /* GETCSVLINE -- Get next line from CSV file. Reads into a dynamically allocated buffer s which is expanded as required to accommodate longer lines. All standard end of line sequences are handled transparently, and the line is returned with the end line sequence stripped with a C string terminator (zero byte) appended. */ static int getCSVline(FILE *fp) { static int sl = 0; int c, ll = -1; if (s == NULL) { sl = 1024; s = (char *) malloc(sl); if (s == NULL) { fprintf(stderr, "%s: Unable to allocate %d byte CSV input line buffer.\n", PROG, sl); exit(2); } } while ((c = getc(fp)) >= 0) { /* Test for end of line sequence. We accept either a carriage return or line feed as an end of line delimiter, optionally followed by no more than one of the other delimiter. */ if (c == '\n') { c = getc(fp); if (c != '\r') { ungetc(c, fp); } break; } if (c == '\r') { c = getc(fp); if (c != '\n') { ungetc(c, fp); } break; } /* Increment line length, expand buffer if necessary, and store character in buffer. */ ll++; if (ll >= (sl - 1)) { sl += 1024; s = (char *) realloc(s, sl); if (s == NULL) { fprintf(stderr, "%s: Unable to expand CSV input line buffer to %d bytes.\n", PROG, sl); exit(2); } } s[ll] = c; } /* If we got a line, append a C string terminator to it. */ if (ll >= 0) { s[ll + 1] = 0; } return (ll >= 0); } /* CLAMP -- Constrain a numeric value to be within a specified inclusive range. If the value is outside the range an error message is issued and the value is forced to the closest limit of the range. */ static void clamp(long *value, const long minval, const long maxval, const char *fieldname) { if (((*value) < minval) || ((*value) > maxval)) { char errm[256]; sprintf(errm, "%s out of range. Value (%ld) outside limits of %ld to %ld", fieldname, *value, minval, maxval); Warn(errm); if ((*value) < minval) { *value = minval; } else { *value = maxval; } } } /* Main program */ int main(int argc, char *argv[]) { struct mhead mh; struct mtrack mt; FILE *fp = stdin, *fo = stdout; int i, n, track, rtype, etype, headerseen = FALSE, eofseen = FALSE, intrack = 0, ntrack = 0; char errm[256]; long tl; /* Parse command line arguments. */ progname = argv[0]; csvline = s; while ((n = getopt(argc, argv, "uvxz")) != -1) { switch (n) { case 'u': fprintf(stderr, "Usage: %s [ options ] [ csv_file ] [ midi_file ]\n", progname); fprintf(stderr, " Options:\n"); fprintf(stderr, " -u Print this message\n"); fprintf(stderr, " -v Verbose: dump header and track information\n"); fprintf(stderr, " -x Expand running status\n"); fprintf(stderr, " -z Abort on any warning message\n"); fprintf(stderr, "Version %s\n", VERSION); return 0; case 'v': verbose = TRUE; break; case 'x': optimiseStatus = FALSE; break; case 'z': zerotol = TRUE; break; case '?': fprintf(stderr, "%s: undefined option -%c specified.\n", PROG, n); return 2; } } /* Open input and output files, if supplied. Otherwise standard input and output are used. A file name of "-" is equivalent to no specification. */ i = 0; while (optind < argc) { switch (i++) { case 0: if (strcmp(argv[optind], "-") != 0) { fp = fopen(argv[optind], "r"); if (fp == NULL) { fprintf(stderr, "%s: Unable to to open CSV input file %s\n", PROG, argv[optind]); return 2; } } break; case 1: if (strcmp(argv[optind], "-") != 0) { fo = fopen(argv[optind], "wb"); if (fo == NULL) { fprintf(stderr, "%s: Unable to to create MIDI output file %s\n", PROG, argv[optind]); return 2; } } break; } optind++; } #ifdef _WIN32 /* If output is to standard output, set the output file mode to binary. */ if (fo == stdout) { _setmode(_fileno(fo), _O_BINARY); } #endif /* Allocate initial track assembly buffer. The buffer is expanded as necessary. */ trackbufp = trackbuf = (byte *) malloc(trackbufl = 16384); memcpy(mt.chunktype, MIDI_Track_Sentinel, sizeof mt.chunktype); #define Nfields(n) { if (nfields(n)) { if (zerotol) { exit(1); } else { continue; } } } #define Clamp(fld, low, high, what) clamp(&nfld[fld - 4], low, high, "Field " # fld " (" what ")") while (getCSVline(fp)) { char *p = s + strlen(s); lineno++; /* Trim any white space from the end of the record. */ while (p >= s && isspace(*(p - 1))) { *(--p) = 0; } /* Test for and ignore blank lines and comments. A comment is any line whose first character (ignoring leading white space) is one of the COMMENT_DELIMITERS). Note that we do not permit in-line comments, although the user is free to supply extra CSV fields which we ignore for that purpose. */ p = s; while ((*p != 0) && isspace(*p)) { p++; } if ((*p == 0) || (strchr(COMMENT_DELIMITERS, *p) != NULL)) { continue; } CSVscanInit(s); /* Scan track, absolute time, and record type. These are present in all records. */ if (CSVscanField(&f, &flen)) { track = atoi(f); } else { Warn("Missing track number (field 1)"); continue; } tl = track; clamp(&tl, 0, 65535, "Field 1 (Track)"); track = (int) tl; if (CSVscanField(&f, &flen)) { abstime = atol(f); } else { Warn("Missing absolute time (field 2)"); continue; } clamp(&abstime, 0, 0x7FFFFFFF, "Field 2 (Time)"); if (!CSVscanField(&f, &flen)) { Warn("Missing record type (field 3)"); continue; } /* Look up record type and dispatch to appropriate handler. */ rtype = -1; for (i = 0; i < ELEMENTS(mitems); i++) { if (strcasecmp(f, mitems[i].name) == 0) { rtype = mitems[i].icode; break; } } if (rtype < 0) { sprintf(errm, "Unknown record type: \"%s\"", f); Warn(errm); continue; } etype = rtype & 0xFF; /* File structure pseudo-events. These records do not correspond to items in the MIDI file. */ switch (rtype) { case Marker(Header): if (headerseen) { Warn("Duplicate header record"); continue; } Nfields(3); memcpy(mh.chunktype, MIDI_Header_Sentinel, sizeof mh.chunktype); mh.length = 6; clamp(&(nfld[0]), 0, 2, "Field 4 (Format)"); mh.format = (short) nfld[0]; clamp(&(nfld[1]), 0, 65535, "Field 5 (Number of tracks)"); mh.ntrks = (short) nfld[1]; clamp(&(nfld[2]), 0, 65535, "Field 6 (Pulses per quarter note)"); mh.division = (short) nfld[2]; writeMidiFileHeader(fo, &mh); if (verbose) { fprintf(stderr, "Format %d MIDI file. %d tracks, %d ticks per quarter note.\n", mh.format, mh.ntrks, mh.division); } headerseen = TRUE; continue; case Marker(Start_track): if (intrack != 0) { Warn("Previous track end missing"); continue; } intrack = track; tabstime = 0; continue; case Marker(End_of_file): eofseen = TRUE; if (intrack != 0) { Warn("Last track end missing"); } continue; } /* Verify events occur within Start_track / End_track brackets and that track number is correct. */ if (track != intrack) { if (intrack == 0) { Warn("Event not within track"); continue; } else { Warn("Incorrect track number in event"); continue; } } /* Make sure absolute time isn't less than that in previous event in this track. */ if (abstime < tabstime) { Warn("Events out of order; this event is before the previous."); continue; } switch (rtype) { #define ClampChannel Clamp(4, 0, 15, "Channel") case Event(NoteOff): case Event(NoteOn): case Event(PolyphonicKeyPressure): case Event(ControlChange): Nfields(3); ClampChannel; Clamp(5, 0, 127, "Note number"); Clamp(6, 0, 127, "Value"); outevent((byte) (etype | nfld[0])); outbyte((byte) nfld[1]); outbyte((byte) nfld[2]); break; case Event(ProgramChange): case Event(ChannelPressure): Nfields(2); ClampChannel; Clamp(5, 0, 127, "Value"); outevent((byte) (etype | nfld[0])); outbyte((byte) nfld[1]); break; case Event(PitchBend): Nfields(2); ClampChannel; Clamp(5, 0, (1 << 14) - 1, "Value"); outevent((byte) (etype | nfld[0])); /* Note that the pitch bend event consists of two bytes each containing 7 bits of data with a zero MSB. Consequently, we cannot use outshort() for the pitch bend data and must generate and emit the two 7 bit values here. The values are output with the least significant 7 bits first, followed by the most significant 7 bits. */ outbyte((byte) (nfld[1] & 0x7F)); outbyte((byte) ((nfld[1] >> 7) & 0x7F)); break; /* System-exclusive messages */ case Event(SystemExclusive): case Event(SystemExclusivePacket): Nfields(1); Clamp(4, 0, (1 << 28) - 1, "Length"); lastStatus = -1; /* Running status not used for sysex, and sysex cancels any running status in effect. */ n = nfld[0]; if (checkBytes(5, n)) { continue; } outevent((byte) etype); outVarLen(n); /* Length of following data */ for (i = 0; i < n; i++) { /* We must call xfields() inside the loop for each individual system exclusive field since the number of fields is unlimited and may exceed MAX_NFIELDS. */ if (xfields(1, i + 5)) { abort(); /* Can't happen, thanks to checkBytes() above */ } clamp(&nfld[0], 0, 255, "Sysex data byte"); outbyte((byte) nfld[0]); } break; /* File meta-events */ case Meta(SequenceNumberMetaEvent): Nfields(1); Clamp(4, 0, (1 << 16) - 1, "Sequence number"); outmeta((byte) etype); outbyte(2); outshort((short) nfld[0]); break; case Meta(TextMetaEvent): case Meta(CopyrightMetaEvent): case Meta(TrackTitleMetaEvent): case Meta(TrackInstrumentNameMetaEvent): case Meta(LyricMetaEvent): case Meta(MarkerMetaEvent): case Meta(CuePointMetaEvent): if (!CSVscanField(&f, &flen)) { Warn("Missing field 4."); } outmeta((byte) etype); outbytes((byte *) f, CSVfieldLength); break; case Meta(ChannelPrefixMetaEvent): case Meta(PortMetaEvent): Nfields(1); Clamp(4, 0, 255, "Number"); outmeta((byte) etype); of[0] = (byte) nfld[0]; outbytes(of, 1); break; case Meta(EndTrackMetaEvent): outmeta((byte) etype); outbyte(0); /* All meta events must include length */ mt.length = trackbufp - trackbuf; writeMidiTrackHeader(fo, &mt); if (verbose) { fprintf(stderr, "Track %d: length %ld.\n", ntrack + 1, mt.length); } fwrite(trackbuf, trackbufp - trackbuf, 1, fo); trackbufp = trackbuf; tabstime = 0; lastStatus = -1; intrack = 0; ntrack++; break; case Meta(SetTempoMetaEvent): Nfields(1); Clamp(4, 0, (1 << 24) - 1, "Value"); outmeta((byte) etype); of[0] = (byte) ((nfld[0] >> 16) & 0xFF); of[1] = (byte) ((nfld[0] >> 8) & 0xFF); of[2] = (byte) (nfld[0] & 0xFF); outbytes(of, 3); break; case Meta(SMPTEOffsetMetaEvent): Nfields(5); Clamp(4, 0, 255, "Hour"); Clamp(5, 0, 255, "Minute"); Clamp(6, 0, 255, "Second"); Clamp(7, 0, 255, "Frame"); Clamp(8, 0, 255, "FrameFraction"); outmeta((byte) etype); for (i = 0; i < 5; i++) { of[i] = (byte) nfld[i]; } outbytes(of, 5); break; case Meta(TimeSignatureMetaEvent): Nfields(4); Clamp(4, 0, 255, "Numerator"); Clamp(5, 0, 255, "Denominator"); Clamp(6, 0, 255, "Click"); Clamp(7, 0, 255, "NotesPerQuarter"); outmeta((byte) etype); for (i = 0; i < 4; i++) { of[i] = (byte) nfld[i]; } outbytes(of, 4); break; case Meta(KeySignatureMetaEvent): Nfields(1); if (!CSVscanField(&f, &flen)) { Warn("Missing field 5"); } outmeta((byte) etype); Clamp(4, -7, 7, "Key"); of[0] = (byte) nfld[0]; if (strcasecmp(f, "major") == 0) { of[1] = 0; } else if (strcasecmp(f, "minor") == 0) { of[1] = 1; } else { sprintf(errm, "Field 5 has invalid major/minor indicator \"%s\"", f); Warn(errm); of[1] = 0; } outbytes(of, 2); break; case Meta(SequencerSpecificMetaEvent): Nfields(1); Clamp(4, 0, (1 << 28) - 1, "Length"); n = nfld[0]; if (checkBytes(5, n)) { continue; } outmeta((byte) etype); outVarLen(n); /* Length of following data */ for (i = 0; i < n; i++) { if (xfields(1, i + 5)) { /* Scan data field */ abort(); /* Can't happen, thanks to checkBytes() above */ } clamp(&nfld[0], 0, 255, "Sequencer specific data byte"); outbyte((byte) nfld[0]); } break; /* The code 0xFF indicates an unknown file meta event. Since meta events include a length field, such events can be preserved in a CSV file by including the unknown event code in the record. */ case Meta(0xFF): Nfields(2); /* Get type and length */ Clamp(4, 0, 255, "UnknownMetaType"); Clamp(5, 0, (1 << 28) - 1, "UnknownMetaLength"); etype = nfld[0]; n = nfld[1]; if (checkBytes(6, n)) { continue; } /* We output the length as a variable-length quantity on the assumption that any meta event which can have data longer than 127 bytes will use a variable length len field. */ outmeta((byte) etype); outVarLen(n); for (i = 0; i < n; i++) { if (xfields(1, i + 6)) { /* Scan data field */ abort(); /* Can't happen, thanks to checkBytes() above */ } clamp(&nfld[0], 0, 255, "Unknown meta data byte"); outbyte((byte) nfld[0]); } break; } } fclose(fp); if (!eofseen) { fprintf(stderr, "%s: Missing End_of_file record.\n", PROG); return 1; } return ((errors > 0) ? 1 : 0); } ./midicsv-1.1/README.debian+dfsg0000664000175000017500000000142712567433513015141 0ustar kamalkamalThis archive was repacked by Kamal Mostafa from the original http://www.fourmilab.ch/webtools/midicsv/midicsv-1.1.tar.gz in order to comply with the Debian Free Software Guidelines (DFSG). Changes from the original tarball: The pre-built .exe Windows executables were removed. The file ./test.mid (a likely copyright-restricted song) was replaced with the contents of a selection from http://www.piano-midi.de/midis/mozart/ [Mozart's Sonata No. 10 C major, KV 330 (1783) part 1], redistributed under the "CC-BY-SA 3.0 Germany" license with attribution: File: test.mid (source: http://www.piano-midi.de/midis/mozart/mz_333_1.mid) License: CC-BY-SA [http://creativecommons.org/licenses/by-sa/3.0/de/deed.en] Name: Bernd Krueger Source: http://www.piano-midi.de ./midicsv-1.1/chorus.pl0000644000175000017500000000123210013447156013741 0ustar kamalkamal # Chorus all notes in a CSV MIDI file $offset = -12; $percussion = 9; while ($a = <>) { print($a); # Recognise Note_on_c and Note_off_c records and crack into: # $1 Start of record # $2 Channel number # $3 Note number # $a Balance of record if ($a =~ s/(\d+,\s*\d+,\s*Note_\w+,\s*(\d+),\s*)(\d+)//) { if ($2 != $percussion) { $n = $3; $n += $offset; if ($n < 0) { next; } $a = "$1$n$a"; print($a); } } } ./midicsv-1.1/ce3k.csv0000644000175000017500000000122610013436737013451 0ustar kamalkamal0, 0, Header, 1, 2, 480 1, 0, Start_track 1, 0, Title_t, "Close Encounters" 1, 0, Text_t, "Sample for MIDIcsv Distribution" 1, 0, Copyright_t, "This file is in the public domain" 1, 0, Time_signature, 4, 2, 24, 8 1, 0, Tempo, 500000 1, 0, End_track 2, 0, Start_track 2, 0, Instrument_name_t, "Church Organ" 2, 0, Program_c, 1, 19 2, 0, Note_on_c, 1, 79, 81 2, 960, Note_off_c, 1, 79, 0 2, 960, Note_on_c, 1, 81, 81 2, 1920, Note_off_c, 1, 81, 0 2, 1920, Note_on_c, 1, 77, 81 2, 2880, Note_off_c, 1, 77, 0 2, 2880, Note_on_c, 1, 65, 81 2, 3840, Note_off_c, 1, 65, 0 2, 3840, Note_on_c, 1, 72, 81 2, 4800, Note_off_c, 1, 72, 0 2, 4800, End_track 0, 0, End_of_file ./midicsv-1.1/count_events.pl0000644000175000017500000000060210013451072015142 0ustar kamalkamal # Count number of events by type in CSV MIDI file # and report. while ($a = <>) { if (!($a =~ m/\s*[\#\;]/)) { # Ignore comment lines if ($a =~ m/\d+\s*,\s*\d+\s*,\s*(\w+)/) { $events{$1}++; } else { print("Cannot parse: $a"); } } } foreach $k (sort(keys(%events))) { printf("%9d %s\n", $events{$k}, $k); } ./midicsv-1.1/midicsv.10000644000175000017500000000351410011735000013610 0ustar kamalkamal'\" t .TH MIDICSV 1 "9 FEB 2004" .UC 4 .SH NAME midicsv \- translate MIDI file to CSV .SH SYNOPSIS .B midicsv [ .B \-u .B \-v ] [ .I infile [ .I outfile ] ] .SH DESCRIPTION .B midicsv reads a standard MIDI file and decodes it into a CSV (Comma-Separated Value) file which preserves all the information in the MIDI file. The ASCII CSV file may be loaded into a spreadsheet or database application, or processed by a program to transform the MIDI data (for example, to key transpose a composition or extract a track from a multi-track sequence). A CSV file in the format created by .B midicsv may be converted back into a standard MIDI file with the .B csvmidi program. .SH OPTIONS .TP 10 .B \-u Print how-to-call information. .TP .B \-v Print verbose debugging information on standard error. The MIDI file header is dumped, along with the length of each track in the file. .SH FILES If no .I infile is specified or .I infile is .RB `` \- '', .B midicsv reads its input from standard input; if no .I outfile is given or .I outfile is .RB `` \- '', CSV output is written to standard output. The input and output are processed in a strictly serial manner; consequently .B midicsv may be used in pipelines without restrictions. .SH BUGS .PP .B midicsv assumes its input is a well-formed standard MIDI file; while some error checking is performed, gross errors in the input file may cause .B midicsv to crash. .PP Please report problems to .BR bugs@fourmilab.ch . .SH "SEE ALSO" .PD .BR csvmidi (1), .BR midicsv (5) .ne 10 .SH AUTHOR .ce 2 John Walker http://www.fourmilab.ch/ .PP This software is in the public domain. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, without any conditions or restrictions. This software is provided ``as is'' without express or implied warranty.