midi-writer.cpp

Go to the documentation of this file.
00001 /*
00002  * midi-writer.cpp
00003  *
00004  * Copyright (C) 2006  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  * Given a midi event stream on STDIN, writes out a midi file
00008  */
00009 
00010 // includes --------------------------------------------------------------------
00011 #include <iostream>
00012 
00013 #include "wave-midi/midi_file.h"
00014 
00015 
00016 // statics
00017 typedef const char * cc_ptr_t;
00018 
00019 struct stat_entry_t {
00020         const char                     *name;
00021         media::MidiEvent::eStatus       status;
00022 };
00023 
00024 
00025 #define STAT_ENTRY(name,status) { #name , media::MidiEvent::status } ,
00026 
00027 static const stat_entry_t s_statusTable[] = {
00028 
00029         // order by expected occurance
00030         STAT_ENTRY(note-on,     eNoteOn)
00031         STAT_ENTRY(note-off,    eNoteOff)
00032         STAT_ENTRY(program,     eProgram)
00033 
00034         // must be last!
00035         { 0, media::MidiEvent::eInvalid }
00036 };
00037 
00038 
00039 ////////////////////////////////////////////////////////////////////////////////
00040 //
00041 //      helper methods
00042 //
00043 ////////////////////////////////////////////////////////////////////////////////
00044 
00045 static const char *
00046 getNextToken
00047 (
00048 IO cc_ptr_t& p
00049 )
00050 {
00051         ASSERT(p, "null");
00052 
00053         static const int s_max = 1024;
00054         static char s_buffer[s_max];
00055         const char * max = s_buffer + s_max;
00056 
00057         char * q = s_buffer;            // points to current point in buffer
00058 
00059         // skip leading whitespace
00060         while (isspace(*p)) {
00061                 // keep going
00062                 ++p;
00063         }
00064 
00065         // copy characters until end, or whitespace, or comment
00066         while (0 != *p && !isspace(*p) && '#' != *p) {
00067                 ASSERT(q < max, "Ran out of room in buffer");
00068 
00069                 // not whitespace or comment--copy over!
00070                 *q = *p;
00071 
00072                 // increment both strings
00073                 ++q;
00074                 ++p;
00075         }
00076 
00077         // null-terminate and return
00078         *q = 0;
00079         return s_buffer;
00080 }
00081 
00082 
00083 
00084 static void
00085 addMeta
00086 (
00087 IN media::MidiFile * mf,
00088 IN const char * p
00089 )
00090 {
00091         ASSERT(mf, "null");
00092         ASSERT(p, "null");
00093 
00094         const char * key = getNextToken(p);
00095         if (!strcmp("ppqn", key)) {
00096                 int ppqn = atoi(getNextToken(p));
00097                 mf->setPpqn(ppqn);
00098         } else if (!strcmp("bpm", key)) {
00099                 float bpm = atof(getNextToken(p));
00100                 mf->setTempo(bpm);
00101         } else {
00102                 ASSERT(false, "unknown meta key: '%s'", key);
00103         }
00104 }
00105 
00106 
00107 
00108 static media::MidiEvent::eStatus
00109 getStatus
00110 (
00111 IN const char * type
00112 )
00113 {
00114         ASSERT(type, "null");
00115 
00116         const stat_entry_t * p = s_statusTable;
00117         while (p->name) {
00118                 if (!strcmp(type, p->name)) {
00119                         return p->status;
00120                 }
00121                 ++p;
00122         }
00123 
00124         ASSERT(false, "unrecognized event type: '%s'", type);
00125         return p->status;
00126 }
00127 
00128 
00129 
00130 static void
00131 addEvent
00132 (
00133 IN media::MidiFile * mf,
00134 IN const char * p
00135 )
00136 {
00137         ASSERT(mf, "null");
00138         ASSERT(p, "null");
00139 
00140         // extract raw data
00141         long tick = atol(getNextToken(p));
00142         ASSERT(tick >= 0, "Bad tick value: %ld", tick);
00143 
00144         const char * type = getNextToken(p);
00145 
00146         // determine status
00147         media::MidiEvent me;
00148         me.SetStatus(getStatus(type));
00149 
00150         int channel = atoi(getNextToken(p));
00151         ASSERT(channel >= 0 && channel < 16, "bad channel: %d", channel);
00152         int p1 = atoi(getNextToken(p));
00153         ASSERT(p1 >= 0 && p1 < 128, "bad parameter 1: %d", p1);
00154         int p2 = atoi(getNextToken(p));
00155         ASSERT(p2 >= 0 && p2 < 128, "bad parameter 2: %d", p2);
00156 
00157         me.SetChannel(channel);
00158         me.SetByte0((byte_t) p1);
00159         me.SetByte1((byte_t) p2);
00160 
00161         mf->addEvent(tick, me);
00162 }
00163 
00164 
00165 
00166 static void
00167 addEvents
00168 (
00169 IN media::MidiFile * mf,
00170 IO std::istream& stream
00171 )
00172 {
00173         ASSERT(mf, "null");
00174 
00175         // keep reading until end of stream
00176         std::string line;
00177         while (stream.good()) {
00178                 std::getline(stream, line);
00179                 const char * p = line.c_str();
00180 
00181                 // get first token (meta or event)
00182                 const char * token = getNextToken(p);
00183                 if (!*token) {
00184                         continue;       // skip line
00185                 }
00186 
00187                 // what sort of line is this?
00188                 if (!strcmp("meta", token)) {
00189                         addMeta(mf, p);
00190                 } else if (!strcmp("event", token)) {
00191                         addEvent(mf, p);
00192                 } else {
00193                         ASSERT(false, "unknown token: '%s'", token);
00194                 }
00195         }
00196 }
00197 
00198 
00199 
00200 ////////////////////////////////////////////////////////////////////////////////
00201 //
00202 //      entry point
00203 //
00204 ////////////////////////////////////////////////////////////////////////////////
00205 
00206 int
00207 main
00208 (
00209 IN int argc,
00210 IN const char * argv[]
00211 )
00212 {
00213         ASSERT(2 == argc,
00214             "usage: midi-writer <filename>");
00215         const char * filename = argv[1];
00216 
00217         // create new midi file
00218         smart_ptr<media::MidiFile> mf = media::MidiFile::create();
00219         ASSERT(mf, "could not create midi file object?");
00220 
00221         // add events
00222         addEvents(mf, std::cin);
00223 
00224         // write output
00225         mf->write(filename);
00226 
00227         return 0;
00228 }
00229