summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpepper <peppersclothescult@gmail.com>2015-01-19 00:02:46 -0800
committerpepper <peppersclothescult@gmail.com>2015-01-19 00:02:46 -0800
commit760d4d5a0fc89e5b14681879577a80e79795e4a3 (patch)
tree3698995f434a00a904f9fdff64a739eb21f53fdb
-rw-r--r--.midisep.pl.swobin0 -> 16384 bytes
-rw-r--r--.midisep.pl.swpbin0 -> 12288 bytes
-rw-r--r--example.midbin0 -> 21504 bytes
-rwxr-xr-xmidicsv-1.1/Csvmidi.exebin0 -> 53248 bytes
-rw-r--r--midicsv-1.1/Csvmidi.vcproj134
-rw-r--r--midicsv-1.1/Makefile103
-rwxr-xr-xmidicsv-1.1/Midicsv.exebin0 -> 45056 bytes
-rw-r--r--midicsv-1.1/Midicsv.sln27
-rw-r--r--midicsv-1.1/Midicsv.vcproj137
-rw-r--r--midicsv-1.1/README134
-rw-r--r--midicsv-1.1/W32test.bat7
-rw-r--r--midicsv-1.1/acomp.pl69
-rw-r--r--midicsv-1.1/bad.csv39
-rw-r--r--midicsv-1.1/ce3k.csv23
-rw-r--r--midicsv-1.1/chorus.pl28
-rw-r--r--midicsv-1.1/count_events.pl18
-rw-r--r--midicsv-1.1/csv.c146
-rw-r--r--midicsv-1.1/csv.h9
-rw-r--r--midicsv-1.1/csv.obin0 -> 10752 bytes
-rwxr-xr-xmidicsv-1.1/csvmidibin0 -> 39848 bytes
-rw-r--r--midicsv-1.1/csvmidi.1128
-rw-r--r--midicsv-1.1/csvmidi.c854
-rw-r--r--midicsv-1.1/csvmidi.obin0 -> 40528 bytes
-rw-r--r--midicsv-1.1/drummer.pl59
-rw-r--r--midicsv-1.1/exchannel.pl18
-rw-r--r--midicsv-1.1/general_midi.pl187
-rw-r--r--midicsv-1.1/getopt.c117
-rw-r--r--midicsv-1.1/getopt.h10
-rw-r--r--midicsv-1.1/getopt.obin0 -> 8816 bytes
-rw-r--r--midicsv-1.1/log.txt407
-rwxr-xr-xmidicsv-1.1/midicsvbin0 -> 27808 bytes
-rw-r--r--midicsv-1.1/midicsv.182
-rw-r--r--midicsv-1.1/midicsv.5597
-rw-r--r--midicsv-1.1/midicsv.c510
-rw-r--r--midicsv-1.1/midicsv.obin0 -> 25200 bytes
-rw-r--r--midicsv-1.1/midifile.h90
-rw-r--r--midicsv-1.1/midio.c136
-rw-r--r--midicsv-1.1/midio.h17
-rw-r--r--midicsv-1.1/midio.obin0 -> 9720 bytes
-rw-r--r--midicsv-1.1/test.midbin0 -> 44298 bytes
-rw-r--r--midicsv-1.1/torture.pl173
-rw-r--r--midicsv-1.1/transpose.pl34
-rw-r--r--midicsv-1.1/types.h10
-rw-r--r--midicsv-1.1/version.h2
-rwxr-xr-xmidisep.pl161
45 files changed, 4466 insertions, 0 deletions
diff --git a/.midisep.pl.swo b/.midisep.pl.swo
new file mode 100644
index 0000000..bc725f6
--- /dev/null
+++ b/.midisep.pl.swo
Binary files differ
diff --git a/.midisep.pl.swp b/.midisep.pl.swp
new file mode 100644
index 0000000..8ece1c5
--- /dev/null
+++ b/.midisep.pl.swp
Binary files differ
diff --git a/example.mid b/example.mid
new file mode 100644
index 0000000..bf3bfc0
--- /dev/null
+++ b/example.mid
Binary files differ
diff --git a/midicsv-1.1/Csvmidi.exe b/midicsv-1.1/Csvmidi.exe
new file mode 100755
index 0000000..febf438
--- /dev/null
+++ b/midicsv-1.1/Csvmidi.exe
Binary files differ
diff --git a/midicsv-1.1/Csvmidi.vcproj b/midicsv-1.1/Csvmidi.vcproj
new file mode 100644
index 0000000..5b76257
--- /dev/null
+++ b/midicsv-1.1/Csvmidi.vcproj
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.00"
+ Name="Csvmidi"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32,_DEBUG,_CONSOLE"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Debug/Csvmidi.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/MACHINE:I386"
+ AdditionalDependencies="libc.lib"
+ OutputFile=".\Debug/Csvmidi.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="TRUE"
+ IgnoreAllDefaultLibraries="FALSE"
+ IgnoreDefaultLibraryNames="libcd.lib"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/Csvmidi.pdb"
+ SubSystem="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Debug/Csvmidi.tlb"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32,NDEBUG,_CONSOLE"
+ StringPooling="TRUE"
+ RuntimeLibrary="4"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Release/Csvmidi.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/MACHINE:I386"
+ AdditionalDependencies="libc.lib"
+ OutputFile=".\Release/Csvmidi.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ ProgramDatabaseFile=".\Release/Csvmidi.pdb"
+ SubSystem="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Release/Csvmidi.tlb"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ </Configurations>
+ <Files>
+ <File
+ RelativePath=".\csv.c">
+ </File>
+ <File
+ RelativePath=".\csvmidi.c">
+ </File>
+ <File
+ RelativePath=".\getopt.c">
+ </File>
+ <File
+ RelativePath=".\midio.c">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/midicsv-1.1/Makefile b/midicsv-1.1/Makefile
new file mode 100644
index 0000000..75703f9
--- /dev/null
+++ b/midicsv-1.1/Makefile
@@ -0,0 +1,103 @@
+
+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
diff --git a/midicsv-1.1/Midicsv.exe b/midicsv-1.1/Midicsv.exe
new file mode 100755
index 0000000..649d3da
--- /dev/null
+++ b/midicsv-1.1/Midicsv.exe
Binary files differ
diff --git a/midicsv-1.1/Midicsv.sln b/midicsv-1.1/Midicsv.sln
new file mode 100644
index 0000000..1ac04e3
--- /dev/null
+++ b/midicsv-1.1/Midicsv.sln
@@ -0,0 +1,27 @@
+Microsoft 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
diff --git a/midicsv-1.1/Midicsv.vcproj b/midicsv-1.1/Midicsv.vcproj
new file mode 100644
index 0000000..a9d9361
--- /dev/null
+++ b/midicsv-1.1/Midicsv.vcproj
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.00"
+ Name="Midicsv"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ InlineFunctionExpansion="1"
+ PreprocessorDefinitions="WIN32,NDEBUG,_CONSOLE"
+ StringPooling="TRUE"
+ RuntimeLibrary="4"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Release/Midicsv.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/MACHINE:I386"
+ AdditionalDependencies="libc.lib"
+ OutputFile=".\Release/Midicsv.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ IgnoreDefaultLibraryNames="libcd.lib"
+ ProgramDatabaseFile=".\Release/Midicsv.pdb"
+ SubSystem="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Release/Midicsv.tlb"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32,_DEBUG,_CONSOLE"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Debug/Midicsv.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/MACHINE:I386"
+ AdditionalDependencies="libc.lib"
+ OutputFile=".\Debug/Midicsv.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="TRUE"
+ IgnoreDefaultLibraryNames="libcd.lib"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/Midicsv.pdb"
+ SubSystem="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Debug/Midicsv.tlb"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ </Configurations>
+ <Files>
+ <File
+ RelativePath=".\getopt.c">
+ </File>
+ <File
+ RelativePath=".\getopt.h">
+ </File>
+ <File
+ RelativePath=".\midicsv.c">
+ </File>
+ <File
+ RelativePath=".\midifile.h">
+ </File>
+ <File
+ RelativePath=".\midio.c">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/midicsv-1.1/README b/midicsv-1.1/README
new file mode 100644
index 0000000..06328dc
--- /dev/null
+++ b/midicsv-1.1/README
@@ -0,0 +1,134 @@
+
+ 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.
+
diff --git a/midicsv-1.1/W32test.bat b/midicsv-1.1/W32test.bat
new file mode 100644
index 0000000..e08c717
--- /dev/null
+++ b/midicsv-1.1/W32test.bat
@@ -0,0 +1,7 @@
+@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
diff --git a/midicsv-1.1/acomp.pl b/midicsv-1.1/acomp.pl
new file mode 100644
index 0000000..dd36f5c
--- /dev/null
+++ b/midicsv-1.1/acomp.pl
@@ -0,0 +1,69 @@
+
+ # 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));
+ &note(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 { # &note($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");
+ }
diff --git a/midicsv-1.1/bad.csv b/midicsv-1.1/bad.csv
new file mode 100644
index 0000000..e6b79ed
--- /dev/null
+++ b/midicsv-1.1/bad.csv
@@ -0,0 +1,39 @@
+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, -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
diff --git a/midicsv-1.1/ce3k.csv b/midicsv-1.1/ce3k.csv
new file mode 100644
index 0000000..de66411
--- /dev/null
+++ b/midicsv-1.1/ce3k.csv
@@ -0,0 +1,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
diff --git a/midicsv-1.1/chorus.pl b/midicsv-1.1/chorus.pl
new file mode 100644
index 0000000..00b398f
--- /dev/null
+++ b/midicsv-1.1/chorus.pl
@@ -0,0 +1,28 @@
+
+ # 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);
+ }
+ }
+ }
diff --git a/midicsv-1.1/count_events.pl b/midicsv-1.1/count_events.pl
new file mode 100644
index 0000000..377dcd4
--- /dev/null
+++ b/midicsv-1.1/count_events.pl
@@ -0,0 +1,18 @@
+
+ # 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);
+ }
diff --git a/midicsv-1.1/csv.c b/midicsv-1.1/csv.c
new file mode 100644
index 0000000..ada7d75
--- /dev/null
+++ b/midicsv-1.1/csv.c
@@ -0,0 +1,146 @@
+/*
+
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#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
diff --git a/midicsv-1.1/csv.h b/midicsv-1.1/csv.h
new file mode 100644
index 0000000..6d7e54e
--- /dev/null
+++ b/midicsv-1.1/csv.h
@@ -0,0 +1,9 @@
+/*
+
+ 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 */
diff --git a/midicsv-1.1/csv.o b/midicsv-1.1/csv.o
new file mode 100644
index 0000000..3af2ce6
--- /dev/null
+++ b/midicsv-1.1/csv.o
Binary files differ
diff --git a/midicsv-1.1/csvmidi b/midicsv-1.1/csvmidi
new file mode 100755
index 0000000..29165ea
--- /dev/null
+++ b/midicsv-1.1/csvmidi
Binary files differ
diff --git a/midicsv-1.1/csvmidi.1 b/midicsv-1.1/csvmidi.1
new file mode 100644
index 0000000..964e2df
--- /dev/null
+++ b/midicsv-1.1/csvmidi.1
@@ -0,0 +1,128 @@
+'\" 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.
diff --git a/midicsv-1.1/csvmidi.c b/midicsv-1.1/csvmidi.c
new file mode 100644
index 0000000..3e3d94d
--- /dev/null
+++ b/midicsv-1.1/csvmidi.c
@@ -0,0 +1,854 @@
+/*
+
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef _WIN32
+#include <fcntl.h>
+#include <io.h>
+#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);
+}
diff --git a/midicsv-1.1/csvmidi.o b/midicsv-1.1/csvmidi.o
new file mode 100644
index 0000000..1b768e1
--- /dev/null
+++ b/midicsv-1.1/csvmidi.o
Binary files differ
diff --git a/midicsv-1.1/drummer.pl b/midicsv-1.1/drummer.pl
new file mode 100644
index 0000000..8299e8b
--- /dev/null
+++ b/midicsv-1.1/drummer.pl
@@ -0,0 +1,59 @@
+
+ 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 { # &note($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++) {
+ &note($note, $duration, $velocity);
+ }
+ }
+ }
+ }
diff --git a/midicsv-1.1/exchannel.pl b/midicsv-1.1/exchannel.pl
new file mode 100644
index 0000000..b1fcde8
--- /dev/null
+++ b/midicsv-1.1/exchannel.pl
@@ -0,0 +1,18 @@
+
+ # 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);
+ }
+ }
+ }
diff --git a/midicsv-1.1/general_midi.pl b/midicsv-1.1/general_midi.pl
new file mode 100644
index 0000000..a56ea65
--- /dev/null
+++ b/midicsv-1.1/general_midi.pl
@@ -0,0 +1,187 @@
+
+ # 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;
diff --git a/midicsv-1.1/getopt.c b/midicsv-1.1/getopt.c
new file mode 100644
index 0000000..1564e53
--- /dev/null
+++ b/midicsv-1.1/getopt.c
@@ -0,0 +1,117 @@
+/*
+Date: Tue, 25 Dec 84 19:20:50 EST
+From: Keith Bostic <harvard!seismo!keith>
+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 <stdio.h>
+
+#include <string.h>
+
+#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 */
+}
diff --git a/midicsv-1.1/getopt.h b/midicsv-1.1/getopt.h
new file mode 100644
index 0000000..50927bd
--- /dev/null
+++ b/midicsv-1.1/getopt.h
@@ -0,0 +1,10 @@
+
+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)
diff --git a/midicsv-1.1/getopt.o b/midicsv-1.1/getopt.o
new file mode 100644
index 0000000..d0868b0
--- /dev/null
+++ b/midicsv-1.1/getopt.o
Binary files differ
diff --git a/midicsv-1.1/log.txt b/midicsv-1.1/log.txt
new file mode 100644
index 0000000..880e240
--- /dev/null
+++ b/midicsv-1.1/log.txt
@@ -0,0 +1,407 @@
+
+ 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.
diff --git a/midicsv-1.1/midicsv b/midicsv-1.1/midicsv
new file mode 100755
index 0000000..bdf9b64
--- /dev/null
+++ b/midicsv-1.1/midicsv
Binary files differ
diff --git a/midicsv-1.1/midicsv.1 b/midicsv-1.1/midicsv.1
new file mode 100644
index 0000000..d367430
--- /dev/null
+++ b/midicsv-1.1/midicsv.1
@@ -0,0 +1,82 @@
+'\" 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.
diff --git a/midicsv-1.1/midicsv.5 b/midicsv-1.1/midicsv.5
new file mode 100644
index 0000000..2ef101b
--- /dev/null
+++ b/midicsv-1.1/midicsv.5
@@ -0,0 +1,597 @@
+'\" 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.
diff --git a/midicsv-1.1/midicsv.c b/midicsv-1.1/midicsv.c
new file mode 100644
index 0000000..04f43de
--- /dev/null
+++ b/midicsv-1.1/midicsv.c
@@ -0,0 +1,510 @@
+/*
+
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <fcntl.h>
+#include <io.h>
+#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;
+}
diff --git a/midicsv-1.1/midicsv.o b/midicsv-1.1/midicsv.o
new file mode 100644
index 0000000..f9c4124
--- /dev/null
+++ b/midicsv-1.1/midicsv.o
Binary files differ
diff --git a/midicsv-1.1/midifile.h b/midicsv-1.1/midifile.h
new file mode 100644
index 0000000..2d96588
--- /dev/null
+++ b/midicsv-1.1/midifile.h
@@ -0,0 +1,90 @@
+/*
+
+ 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 */
+};
diff --git a/midicsv-1.1/midio.c b/midicsv-1.1/midio.c
new file mode 100644
index 0000000..b5d9f08
--- /dev/null
+++ b/midicsv-1.1/midio.c
@@ -0,0 +1,136 @@
+/*
+
+ MIDI File Input/Output Utilities
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#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);
+}
diff --git a/midicsv-1.1/midio.h b/midicsv-1.1/midio.h
new file mode 100644
index 0000000..1ec40b0
--- /dev/null
+++ b/midicsv-1.1/midio.h
@@ -0,0 +1,17 @@
+/*
+
+ 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);
diff --git a/midicsv-1.1/midio.o b/midicsv-1.1/midio.o
new file mode 100644
index 0000000..0021537
--- /dev/null
+++ b/midicsv-1.1/midio.o
Binary files differ
diff --git a/midicsv-1.1/test.mid b/midicsv-1.1/test.mid
new file mode 100644
index 0000000..d1939c8
--- /dev/null
+++ b/midicsv-1.1/test.mid
Binary files differ
diff --git a/midicsv-1.1/torture.pl b/midicsv-1.1/torture.pl
new file mode 100644
index 0000000..f37e44d
--- /dev/null
+++ b/midicsv-1.1/torture.pl
@@ -0,0 +1,173 @@
+
+# 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");
+ }
+ }
diff --git a/midicsv-1.1/transpose.pl b/midicsv-1.1/transpose.pl
new file mode 100644
index 0000000..d2958cb
--- /dev/null
+++ b/midicsv-1.1/transpose.pl
@@ -0,0 +1,34 @@
+
+ # 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);
+ }
diff --git a/midicsv-1.1/types.h b/midicsv-1.1/types.h
new file mode 100644
index 0000000..3d16657
--- /dev/null
+++ b/midicsv-1.1/types.h
@@ -0,0 +1,10 @@
+/*
+
+ 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). */
diff --git a/midicsv-1.1/version.h b/midicsv-1.1/version.h
new file mode 100644
index 0000000..548380e
--- /dev/null
+++ b/midicsv-1.1/version.h
@@ -0,0 +1,2 @@
+
+#define VERSION "1.1 (January 2008)"
diff --git a/midisep.pl b/midisep.pl
new file mode 100755
index 0000000..ebd2f9d
--- /dev/null
+++ b/midisep.pl
@@ -0,0 +1,161 @@
+#!/usr/bin/perl
+use Data::Dumper;
+use Cwd;
+
+our $DEBUG = 1;
+
+our $MIDICSV_BIN = Cwd::abs_path("midicsv-1.1/midicsv");
+our $CSVMIDI_BIN = Cwd::abs_path("midicsv-1.1/csvmidi");
+
+our $CHANNEL_EVENTS = [
+ 'Note_on_c', 'Note_off_c', 'Pitch_bend_c', 'Control_c',
+ 'Program_c', 'Channel_aftertouch_c', 'Poly_aftertouch_c',
+];
+
+our $SYSEX_EVENTS = [
+ 'System_exclusive', 'System_exclusive_packet'
+];
+
+
+sub usage{
+ print STDERR sprintf('$>%s midifile [output_dir]'."\n", $0);
+ exit 1;
+}
+
+
+
+sub error{
+ my $msg = shift;
+ print STDERR $msg . "\n";
+ exit(1);
+}
+
+sub warn_sysex_found{
+ my $event = shift;
+ my $warning = sprintf("Sysex event found on channel %s\n", $event->{channel});
+ print STDERR $warning;
+}
+
+
+sub correct_bad_note_ons{
+ my $event = shift;
+ if ($event->{type} =~ /Note_on/i && $event->{data}->[-1] == 0) {
+ $event->{type} = "Note_off_c";
+ $event->{data}->[2] =~ s/on/off/i;
+ }
+ return $event;
+}
+
+sub file_to_list{
+ my $file = shift;
+ my $total_channels = shift;
+ my $csvdata = `$MIDICSV_BIN $file`;
+ my $list = [
+ map {
+ chomp($_);
+ my $ctx = {};
+
+ my $line = [ split(',', $_ ) ];
+ $ctx->{data} = $line;
+ $ctx->{type} = $line->[2];
+ if (grep { $ctx->{type} =~ /$_/i } @$CHANNEL_EVENTS ){
+ $ctx->{channel} = ($line->[3] =~ m/([0-9]+)/g)[0];
+ if (!(grep { $ctx->{channel} == $_ } @$total_channels )){
+ push(@$total_channels, $ctx->{channel});
+ }
+ }
+# [ @line ];
+ $ctx;
+ } split('\n', $csvdata) ];
+ return $list;
+}
+
+sub add_to_all_outputs{
+ my $event = shift;
+ my $output_files = shift;
+ foreach my $events (values %$output_files){
+ push (@$events, join(',' , @{$event->{data}}));
+ };
+}
+
+sub add_to_single_output{
+ my $event = shift;
+ my $output_files = shift;
+ push(@{$output_files->{$event->{channel}}}, join(',', @{$event->{data}}));
+}
+
+
+sub process_event_list{
+ my $event_list = shift;
+ my $total_channels = shift;
+ my $output_data = {};
+
+ if ($event_list->[0]->{type} !~ /header/i){
+ error("Bad header");
+ }
+ #marker header as type0
+ $event_list->[0]->{data}->[3] =~ s/[0-9]+/0/g;
+
+ #check if file has more than one track/composition
+ my $tracks_count = $header->{data}->[4];
+ if ($tracks_count > 1){
+ error("Multiple tracks in same file, not yet implemented.");
+ }
+ foreach my $channel(@$total_channels){
+ $output_data->{$channel} = [];
+ }
+
+ #main loop
+ foreach $event(@$event_list){
+ if ( grep { $event->{type} =~ /$_/i } @$CHANNEL_EVENTS ){
+ $event = correct_bad_note_ons($event);
+ add_to_single_output( $event, $output_data );
+ }
+ elsif ( grep { $event->{type} =~ /$_/i} @$SYSEX_EVENTS ){
+ #consider adding special handle for sysex events
+ #currently just ignores them
+ warn_sysex_found ($event);
+ }
+ else {
+ add_to_all_outputs( $event, $output_data);
+ }
+ };
+
+ return $output_data;
+}
+
+
+sub data_to_files{
+ my $output_data = shift;
+ my $filename_prefix = shift;
+ foreach my $channel (keys %$output_data){
+ my $midi_filename = sprintf("%s-%s.mid", $filename_prefix, $channel);
+ my $csv_filename = sprintf("/tmp/%s-%s.csv", $filename_prefix, $channel);
+ open F, ">$csv_filename" or die $!;
+ print F (join("\n", @{$output_data->{$channel}}));
+ close(F);
+ system("$CSVMIDI_BIN $csv_filename $midi_filename");
+ unlink($csv_filename);
+ }
+}
+
+sub main{
+ if (scalar(@ARGV) < 1){
+ if ($DEBUG){
+ print STDERR "DEBUG: using example.mid as default midifile\n";
+ }else{
+ usage;
+ }
+ }
+
+ my $file = $ARGV[0] || "example.mid";
+ my $output_dir = $ARGV[2] || "./";
+ our $total_channels = [];
+
+ our $filename_prefix = $output_dir . ($file =~ m/^(.+)\./g)[0];
+ my $event_list = file_to_list ($file, $total_channels);
+ my $output_data = process_event_list ($event_list, $total_channels);
+ data_to_files($output_data, $filename_prefix);
+#
+};
+main;