1 /***************************************************/ 2 /* 3 Simple realtime MIDI to SKINI parser. 4 5 This object takes MIDI from the input stream 6 (via the RtMidi class), parses it, and turns it 7 into SKINI messages. 8 9 by Perry R. Cook and Gary P. Scavone, 1995 - 2004. 10 */ 11 /***************************************************/ 12 13 #include "RtMidi.h" 14 #include "SKINImsg.h" 15 #include <iostream> 16 #include <stdlib.h> 17 #include <stdio.h> 18 19 void usage(void) { 20 std::cout << "\nuseage: Md2Skini <flag(s)>\n\n"; 21 std::cout << " With no arguments, Md2Skini converts MIDI input to SKINI\n"; 22 std::cout << " format and sends the output directly to stdout.\n"; 23 std::cout << " With flag = -f <filename>, the output stream is simultaneously\n"; 24 std::cout << " written to the file specified by the optional <filename>\n"; 25 std::cout << " (default = test.ski).\n"; 26 std::cout << " With flag = -c, MIDI control change messages will not be\n"; 27 std::cout << " converted to SKINI-specific named controls.\n"; 28 std::cout << " A MIDI input port can be specified with flag = -p portNumber.\n" << std::endl; 29 exit(0); 30 } 31 32 #include <signal.h> 33 static void finish( int ignore ){ std::cout << "Type 'Exit' to quit." << std::endl; } 34 bool parseSkiniControl = true; 35 36 void midiCallback( double deltatime, std::vector< unsigned char > *bytes, void *userData ) 37 { 38 if ( bytes->size() < 2 ) return; 39 40 // Parse the MIDI bytes ... only keep MIDI channel messages. 41 if ( bytes->at(0) > 239 ) return; 42 43 register long type = bytes->at(0) & 0xF0; 44 register int channel = bytes->at(0) & 0x0F; 45 register long databyte1 = bytes->at(1); 46 register long databyte2 = 0; 47 if ( ( type != 0xC0 ) && ( type != 0xD0 ) ) { 48 if ( bytes->size() < 3 ) return; 49 databyte2 = bytes->at(2); 50 } 51 52 std::string typeName; 53 switch( type ) { 54 case __SK_NoteOn_: 55 if ( databyte2 == 0 ) { 56 typeName = "NoteOff\t\t"; 57 databyte2 = 64; 58 } 59 else typeName = "NoteOn\t\t"; 60 break; 61 62 case __SK_NoteOff_: 63 typeName = "NoteOff\t\t"; 64 break; 65 66 case __SK_PolyPressure_: 67 typeName = "PolyPressure\t"; 68 break; 69 70 case __SK_ProgramChange_: 71 typeName = "ProgramChange\t"; 72 break; 73 74 case __SK_ChannelPressure_: 75 typeName = "ChannelPressure\t"; 76 break; 77 78 case __SK_PitchBend_: 79 typeName = "PitchBend\t"; 80 break; 81 82 case __SK_ControlChange_: 83 84 if ( parseSkiniControl != true ) { 85 typeName = "ControlChange\t"; 86 goto output; 87 } 88 89 switch( databyte1 ) { 90 case __SK_PitchChange_: 91 typeName = "PitchChange\t"; 92 goto output; 93 94 case __SK_Volume_: 95 typeName = "Volume\t"; 96 goto output; 97 98 case __SK_ModWheel_: 99 typeName = "ModWheel\t"; 100 goto output; 101 102 case __SK_Breath_: 103 typeName = "Breath\t\t"; 104 goto output; 105 106 case __SK_FootControl_: 107 typeName = "FootControl\t"; 108 goto output; 109 110 case __SK_Portamento_: 111 typeName = "Portamento\t"; 112 goto output; 113 114 case __SK_Balance_: 115 typeName = "Balance\t"; 116 goto output; 117 118 case __SK_Pan_: 119 typeName = "Pan\t\t"; 120 goto output; 121 122 case __SK_Sustain_: 123 typeName = "Sustain\t"; 124 goto output; 125 126 case __SK_Expression_: 127 typeName = "Expression\t"; 128 goto output; 129 130 default: 131 typeName = "ControlChange\t"; 132 goto output; 133 } 134 135 default: 136 typeName = "Unknown\t"; 137 } 138 139 output: 140 141 FILE *file = (FILE *) userData; 142 if ( type == 0xC0 || type == 0xD0 || type == 0xE0 ) { // program change, channel pressure, or pitchbend 143 fprintf( stdout, "%s %.3f %d %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte1 ); 144 if ( file != NULL ) 145 fprintf( file, "%s %.3f %d %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1 ); 146 } 147 else if ( type == 0xB0 ) { // control change 148 if ( typeName == "ControlChange\t" ) { 149 fprintf( stdout, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte1, (float)databyte2 ); 150 if ( file != NULL ) 151 fprintf( file, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1, (float)databyte2 ); 152 } 153 else { 154 fprintf( stdout, "%s %.3f %d %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte2 ); 155 if ( file != NULL ) 156 fprintf( file, "%s %.3f %d %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte2 ); 157 } 158 } 159 else { // noteon, noteoff, aftertouch, and unknown 160 fprintf( stdout, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte1, (float)databyte2 ); 161 if ( file != NULL ) 162 fprintf( file, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1, (float)databyte2 ); 163 } 164 165 fflush( stdout ); 166 } 167 168 int main( int argc,char *argv[] ) 169 { 170 FILE *file = NULL; 171 std::string fileName; 172 RtMidiIn *midiin = 0; 173 unsigned int port = 0; 174 std::string input; 175 176 if ( argc > 5 ) usage(); 177 178 // Parse the command-line arguments. 179 int i = 1; 180 while ( i < argc ) { 181 if (argv[i][0] == '-') { 182 switch(argv[i][1]) { 183 184 case 'f': 185 if ( (i+1 < argc) && argv[i+1][0] != '-' ) { 186 i++; 187 fileName = argv[i]; 188 if ( fileName.find( ".ski" ) == std::string::npos ) fileName.append( ".ski" ); 189 } 190 else fileName = "test.ski"; 191 file = fopen( fileName.c_str(), "wb" ); 192 break; 193 194 case 'p': 195 if ( i++ >= argc) usage(); 196 port = (unsigned int) atoi( argv[i] ); 197 break; 198 199 case 'c': 200 parseSkiniControl = false; 201 break; 202 203 default: 204 usage(); 205 break; 206 } 207 } 208 else usage(); 209 i++; 210 } 211 212 try { 213 midiin = new RtMidiIn(); 214 } 215 catch (RtMidiError &error) { 216 error.printMessage(); 217 if ( file != NULL ) fclose( file ); 218 exit(EXIT_FAILURE); 219 } 220 221 // Check available ports vs. specified. 222 unsigned int nPorts = midiin->getPortCount(); 223 if ( nPorts == 0 ) { 224 std::cout << "No MIDI ports available!\n"; 225 goto cleanup; 226 } 227 else if ( port >= nPorts ) { 228 std::cout << "Invalid port specifier!\n"; 229 goto cleanup; 230 } 231 232 // Open the port. 233 try { 234 midiin->openPort( port ); 235 } 236 catch (RtMidiError &error) { 237 error.printMessage(); 238 goto cleanup; 239 } 240 241 // Set our callback function. This should be done immediately after 242 // opening the port to avoid having incoming messages written to the 243 // queue instead of sent to the callback function. 244 midiin->setCallback( &midiCallback, file ); 245 246 // We'll ignore sysex, timing, and active sensing messages. 247 midiin->ignoreTypes( true, true, true ); 248 249 // Install an interrupt handler function. 250 (void) signal(SIGINT, finish); 251 252 std::cout << "\nReading MIDI input ... type 'Exit' to quit.\n"; 253 while ( input != "Exit" && input != "exit" ) { 254 input.erase(); 255 std::cin >> input; 256 std::cout << input << std::endl; 257 } 258 259 cleanup: 260 delete midiin; 261 if ( file != NULL ) fclose( file ); 262 263 std::cout << "Md2Skini finished ... bye!" << std::endl; 264 return 0; 265 } 266