mirror of
https://github.com/thestk/stk
synced 2026-01-11 20:11:52 +00:00
266 lines
6.8 KiB
C++
266 lines
6.8 KiB
C++
/***************************************************/
|
|
/*
|
|
Simple realtime MIDI to SKINI parser.
|
|
|
|
This object takes MIDI from the input stream
|
|
(via the RtMidi class), parses it, and turns it
|
|
into SKINI messages.
|
|
|
|
by Perry R. Cook and Gary P. Scavone, 1995 - 2004.
|
|
*/
|
|
/***************************************************/
|
|
|
|
#include "RtMidi.h"
|
|
#include "SKINImsg.h"
|
|
#include <iostream>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
void usage(void) {
|
|
std::cout << "\nuseage: Md2Skini <flag(s)>\n\n";
|
|
std::cout << " With no arguments, Md2Skini converts MIDI input to SKINI\n";
|
|
std::cout << " format and sends the output directly to stdout.\n";
|
|
std::cout << " With flag = -f <filename>, the output stream is simultaneously\n";
|
|
std::cout << " written to the file specified by the optional <filename>\n";
|
|
std::cout << " (default = test.ski).\n";
|
|
std::cout << " With flag = -c, MIDI control change messages will not be\n";
|
|
std::cout << " converted to SKINI-specific named controls.\n";
|
|
std::cout << " A MIDI input port can be specified with flag = -p portNumber.\n" << std::endl;
|
|
exit(0);
|
|
}
|
|
|
|
#include <signal.h>
|
|
static void finish( int ignore ){ std::cout << "Type 'Exit' to quit." << std::endl; }
|
|
bool parseSkiniControl = true;
|
|
|
|
void midiCallback( double deltatime, std::vector< unsigned char > *bytes, void *userData )
|
|
{
|
|
if ( bytes->size() < 2 ) return;
|
|
|
|
// Parse the MIDI bytes ... only keep MIDI channel messages.
|
|
if ( bytes->at(0) > 239 ) return;
|
|
|
|
long type = bytes->at(0) & 0xF0;
|
|
int channel = bytes->at(0) & 0x0F;
|
|
long databyte1 = bytes->at(1);
|
|
long databyte2 = 0;
|
|
if ( ( type != 0xC0 ) && ( type != 0xD0 ) ) {
|
|
if ( bytes->size() < 3 ) return;
|
|
databyte2 = bytes->at(2);
|
|
}
|
|
|
|
std::string typeName;
|
|
switch( type ) {
|
|
case __SK_NoteOn_:
|
|
if ( databyte2 == 0 ) {
|
|
typeName = "NoteOff\t\t";
|
|
databyte2 = 64;
|
|
}
|
|
else typeName = "NoteOn\t\t";
|
|
break;
|
|
|
|
case __SK_NoteOff_:
|
|
typeName = "NoteOff\t\t";
|
|
break;
|
|
|
|
case __SK_PolyPressure_:
|
|
typeName = "PolyPressure\t";
|
|
break;
|
|
|
|
case __SK_ProgramChange_:
|
|
typeName = "ProgramChange\t";
|
|
break;
|
|
|
|
case __SK_ChannelPressure_:
|
|
typeName = "ChannelPressure\t";
|
|
break;
|
|
|
|
case __SK_PitchBend_:
|
|
typeName = "PitchBend\t";
|
|
break;
|
|
|
|
case __SK_ControlChange_:
|
|
|
|
if ( parseSkiniControl != true ) {
|
|
typeName = "ControlChange\t";
|
|
goto output;
|
|
}
|
|
|
|
switch( databyte1 ) {
|
|
case __SK_PitchChange_:
|
|
typeName = "PitchChange\t";
|
|
goto output;
|
|
|
|
case __SK_Volume_:
|
|
typeName = "Volume\t";
|
|
goto output;
|
|
|
|
case __SK_ModWheel_:
|
|
typeName = "ModWheel\t";
|
|
goto output;
|
|
|
|
case __SK_Breath_:
|
|
typeName = "Breath\t\t";
|
|
goto output;
|
|
|
|
case __SK_FootControl_:
|
|
typeName = "FootControl\t";
|
|
goto output;
|
|
|
|
case __SK_Portamento_:
|
|
typeName = "Portamento\t";
|
|
goto output;
|
|
|
|
case __SK_Balance_:
|
|
typeName = "Balance\t";
|
|
goto output;
|
|
|
|
case __SK_Pan_:
|
|
typeName = "Pan\t\t";
|
|
goto output;
|
|
|
|
case __SK_Sustain_:
|
|
typeName = "Sustain\t";
|
|
goto output;
|
|
|
|
case __SK_Expression_:
|
|
typeName = "Expression\t";
|
|
goto output;
|
|
|
|
default:
|
|
typeName = "ControlChange\t";
|
|
goto output;
|
|
}
|
|
|
|
default:
|
|
typeName = "Unknown\t";
|
|
}
|
|
|
|
output:
|
|
|
|
FILE *file = (FILE *) userData;
|
|
if ( type == 0xC0 || type == 0xD0 || type == 0xE0 ) { // program change, channel pressure, or pitchbend
|
|
fprintf( stdout, "%s %.3f %d %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte1 );
|
|
if ( file != NULL )
|
|
fprintf( file, "%s %.3f %d %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1 );
|
|
}
|
|
else if ( type == 0xB0 ) { // control change
|
|
if ( typeName == "ControlChange\t" ) {
|
|
fprintf( stdout, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte1, (float)databyte2 );
|
|
if ( file != NULL )
|
|
fprintf( file, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1, (float)databyte2 );
|
|
}
|
|
else {
|
|
fprintf( stdout, "%s %.3f %d %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte2 );
|
|
if ( file != NULL )
|
|
fprintf( file, "%s %.3f %d %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte2 );
|
|
}
|
|
}
|
|
else { // noteon, noteoff, aftertouch, and unknown
|
|
fprintf( stdout, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), 0.0, channel, (float)databyte1, (float)databyte2 );
|
|
if ( file != NULL )
|
|
fprintf( file, "%s %.3f %d %.1f %.1f\n", typeName.c_str(), deltatime, channel, (float)databyte1, (float)databyte2 );
|
|
}
|
|
|
|
fflush( stdout );
|
|
}
|
|
|
|
int main( int argc,char *argv[] )
|
|
{
|
|
FILE *file = NULL;
|
|
std::string fileName;
|
|
RtMidiIn *midiin = 0;
|
|
unsigned int port = 0;
|
|
std::string input;
|
|
|
|
if ( argc > 5 ) usage();
|
|
|
|
// Parse the command-line arguments.
|
|
int i = 1;
|
|
while ( i < argc ) {
|
|
if (argv[i][0] == '-') {
|
|
switch(argv[i][1]) {
|
|
|
|
case 'f':
|
|
if ( (i+1 < argc) && argv[i+1][0] != '-' ) {
|
|
i++;
|
|
fileName = argv[i];
|
|
if ( fileName.find( ".ski" ) == std::string::npos ) fileName.append( ".ski" );
|
|
}
|
|
else fileName = "test.ski";
|
|
file = fopen( fileName.c_str(), "wb" );
|
|
break;
|
|
|
|
case 'p':
|
|
if ( i++ >= argc) usage();
|
|
port = (unsigned int) atoi( argv[i] );
|
|
break;
|
|
|
|
case 'c':
|
|
parseSkiniControl = false;
|
|
break;
|
|
|
|
default:
|
|
usage();
|
|
break;
|
|
}
|
|
}
|
|
else usage();
|
|
i++;
|
|
}
|
|
|
|
try {
|
|
midiin = new RtMidiIn();
|
|
}
|
|
catch (RtMidiError &error) {
|
|
error.printMessage();
|
|
if ( file != NULL ) fclose( file );
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Check available ports vs. specified.
|
|
unsigned int nPorts = midiin->getPortCount();
|
|
if ( nPorts == 0 ) {
|
|
std::cout << "No MIDI ports available!\n";
|
|
goto cleanup;
|
|
}
|
|
else if ( port >= nPorts ) {
|
|
std::cout << "Invalid port specifier!\n";
|
|
goto cleanup;
|
|
}
|
|
|
|
// Open the port.
|
|
try {
|
|
midiin->openPort( port );
|
|
}
|
|
catch (RtMidiError &error) {
|
|
error.printMessage();
|
|
goto cleanup;
|
|
}
|
|
|
|
// Set our callback function. This should be done immediately after
|
|
// opening the port to avoid having incoming messages written to the
|
|
// queue instead of sent to the callback function.
|
|
midiin->setCallback( &midiCallback, file );
|
|
|
|
// We'll ignore sysex, timing, and active sensing messages.
|
|
midiin->ignoreTypes( true, true, true );
|
|
|
|
// Install an interrupt handler function.
|
|
(void) signal(SIGINT, finish);
|
|
|
|
std::cout << "\nReading MIDI input ... type 'Exit' to quit.\n";
|
|
while ( input != "Exit" && input != "exit" ) {
|
|
input.erase();
|
|
std::cin >> input;
|
|
std::cout << input << std::endl;
|
|
}
|
|
|
|
cleanup:
|
|
delete midiin;
|
|
if ( file != NULL ) fclose( file );
|
|
|
|
std::cout << "Md2Skini finished ... bye!" << std::endl;
|
|
return 0;
|
|
}
|