diff options
| author | pepper <peppersclothescult@gmail.com> | 2015-01-19 00:02:46 -0800 |
|---|---|---|
| committer | pepper <peppersclothescult@gmail.com> | 2015-01-19 00:02:46 -0800 |
| commit | 760d4d5a0fc89e5b14681879577a80e79795e4a3 (patch) | |
| tree | 3698995f434a00a904f9fdff64a739eb21f53fdb /midicsv-1.1/midicsv.c | |
Diffstat (limited to 'midicsv-1.1/midicsv.c')
| -rw-r--r-- | midicsv-1.1/midicsv.c | 510 |
1 files changed, 510 insertions, 0 deletions
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; +} |
