//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Nov 27 16:15:50 PST 1999
// Last Modified: Mon Nov 29 14:07:19 PST 1999
// Last Modified: Tue Nov 30 17:05:16 PST 1999 (added MIDI input control)
// Filename: ...sig/doc/examples/all/midiperform/midiperform.cpp
// Syntax: C++
//
// Description: plays a MIDI file with basic impulse conducting controls.
//
#include "MidiPerform.h"
#include "MidiInput.h"
#include "MidiMessage.h"
#include "Options.h"
#include "KeyboardInput.h"
#include "Idler.h"
#ifndef OLDCPP
#include <iostream>
using namespace std;
#else
#include <iostream.h>
#endif
// function declarations:
int checkKeyboard(void);
void checkOptions(Options& opts);
void example(void);
void keyboardCommand(int command);
void processMidiCommand(MidiMessage& message);
void usage(const char* command);
// Global variables:
SigTimer keyboardTimer; // for polling the keyboard for keys
KeyboardInput keyboard; // for computer keyboard controls
MidiPerform performance; // performance interface
Idler eventIdler; // for multi-tasking courtesy
// command-line variables
int tempoMethod = TEMPO_METHOD_AUTOMATIC; // -a -c -t options
int outport = 0; // -p option
int inport = 0; // -p option
int maxamp = 64; // -m option
///////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
Options options(argc, argv);
checkOptions(options);
keyboardTimer.setPeriod(10);
int command = 0;
MidiInput midiin;
midiin.setPort(inport);
midiin.open();
MidiMessage midimessage;
performance.read(options.getArg(1));
performance.setPort(outport);
performance.setMaxAmp(maxamp);
performance.open();
performance.setTempoMethod(tempoMethod);
performance.play();
while (command != 'Q') {
while (midiin.getCount() > 0) {
midimessage = midiin.extract();
processMidiCommand(midimessage);
}
performance.xcheck();
eventIdler.sleep();
if (keyboardTimer.expired()) {
keyboardTimer.reset();
command = checkKeyboard();
if (command == 'Q') {
break;
} else {
keyboardCommand(command);
}
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// checkKeyboard -- check the keyboard for a key hit and return it
// if there was any, otherwise returns -1.
//
int checkKeyboard(void) {
int output = -1;
if (keyboard.hit()) {
output = keyboard.getch();
}
return output;
}
//////////////////////////////
//
// checkOptions -- handle command-line options.
//
void checkOptions(Options& opts) {
opts.define("a|auto|automatic=b");
opts.define("c|const|constant=b");
opts.define("t|tempo|tempo-average=i:1");
opts.define("m|max|max-amplitude=i:64");
opts.define("p|port|out-port=i:0");
opts.define("i|inport|in-port=i:0");
opts.define("1|z|channel-collapse=b");
opts.define("author=b");
opts.define("version=b");
opts.define("example=b");
opts.define("help=b");
opts.process();
if (opts.getBoolean("author")) {
cout << "Written by Craig Stuart Sapp, "
<< "craig@ccrma.stanford.edu, Nov 1999" << endl;
exit(0);
}
if (opts.getBoolean("version")) {
cout << "midiplay version 1.0" << endl;
cout << "compiled: " << __DATE__ << endl;
}
if (opts.getBoolean("help")) {
usage(opts.getCommand());
exit(0);
}
if (opts.getBoolean("example")) {
example();
exit(0);
}
// can only have one output filename
if (opts.getArgCount() != 1) {
cout << "Error: need one input MIDI file for performance." << endl;
usage(opts.getCommand());
exit(1);
}
// figure out the tempo performance method
if (opts.getBoolean("automatic")) {
tempoMethod = TEMPO_METHOD_AUTOMATIC;
} else if (opts.getBoolean("constant")) {
tempoMethod = TEMPO_METHOD_CONSTANT;
} else {
switch (opts.getInteger("tempo-average")) {
case 1: tempoMethod = TEMPO_METHOD_ONEBACK; break;
case 2: tempoMethod = TEMPO_METHOD_TWOBACK; break;
case 3: tempoMethod = TEMPO_METHOD_THREEBACK; break;
case 4: tempoMethod = TEMPO_METHOD_FOURBACK; break;
default: tempoMethod = TEMPO_METHOD_ONEBACK; break;
}
}
outport = opts.getInteger("out-port");
inport = opts.getInteger("in-port");
maxamp = opts.getInteger("max-amplitude");
performance.channelCollapse(opts.getBoolean("channel-collapse"));
}
//////////////////////////////
//
// example -- gives example calls to the midiplay program.
//
void example(void) {
cout <<
"# textmidi examples: \n"
" midiplay midifile.mid \n"
<< endl;
}
//////////////////////////////
//
// keyboardCommand -- process a computer keyboard command character.
//
void keyboardCommand(int command) {
switch (command) {
case ' ':
performance.beat();
break;
case 'b': // print beat location in performance
cout << "Current beat is: " << performance.getBeatLocation() << endl;
break;
case 'z': // toggle MIDI collapsing
{
int setting = performance.channelCollapse();
setting = !setting;
performance.channelCollapse(setting);
if (setting) {
cout << "All notes going to channel 1" << endl;
} else {
cout << "All channel settings are unmodified" << endl;
}
}
break;
case '[': // set performance volume lower
{
double amp = performance.getAmp();
amp /= 1.15;
performance.setAmp(amp);
cout << "Amplitude scaling = " << performance.getAmp() << endl;
}
break;
case ']': // set performance volume higher
{
double amp = performance.getAmp();
amp *= 1.15;
performance.setAmp(amp);
cout << "Amplitude scaling = " << performance.getAmp() << endl;
}
break;
case 't': // current tempo display
cout << "The Current tempo is: " << performance.getTempo() << endl;
break;
case '-': // slow down the tempo
{
double tempo = performance.getTempo();
tempo /= 1.03;
performance.setTempo(tempo);
cout << "The Tempo was set on the keyboard to : " << tempo << endl;
}
break;
case '=': // speed up the tempo
{
double tempo = performance.getTempo();
tempo *= 1.03;
performance.setTempo(tempo);
cout << "The Tempo was set on the keyboard to : " << tempo << endl;
}
break;
case '0': // automatic tempo control
performance.setTempoMethod(TEMPO_METHOD_AUTOMATIC);
cout << "Automatic Tempo Setting at tempo = "
<< performance.getTempo() << endl;
break;
case '9': // constant tempo control
performance.setTempoMethod(TEMPO_METHOD_CONSTANT);
cout << "Constant Tempo Contro at tempo = "
<< performance.getTempo() << endl;
break;
case '1': // 1 beat history average tempo
performance.setTempoMethod(TEMPO_METHOD_ONEBACK);
cout << "One beat tempo history" << endl;
break;
case '2': // 2 beat history average tempo
performance.setTempoMethod(TEMPO_METHOD_TWOBACK);
cout << "Two beat tempo history" << endl;
break;
case '3': // 3 beat history average tempo
performance.setTempoMethod(TEMPO_METHOD_THREEBACK);
cout << "Three beat tempo history" << endl;
break;
case '4': // 4 beat history average tempo
performance.setTempoMethod(TEMPO_METHOD_FOURBACK);
cout << "Four beat tempo history" << endl;
break;
}
}
//////////////////////////////
//
// processMidiCommand -- how to run the textmidi program on the command line.
//
void processMidiCommand(MidiMessage& message) {
if (message.p0() != 0x90 || message.p2() == 0) {
return;
}
switch (message.p1()) {
case 60: // Middle C = beat
keyboardCommand(' ');
break;
case 61: // C# = amplitude control
{
double amp = performance.getAmp();
amp = amp * message.p2() / 64;
if (amp < 0) {
amp = 0;
} else if (amp > 127) {
amp = 127;
}
performance.setAmp((int)amp);
}
break;
case 71: // B = 1 beat tempo follow
keyboardCommand('1');
break;
case 72: // C = 2 beat tempo follow
keyboardCommand('2');
break;
case 73: // C# = 3 beat tempo follow
keyboardCommand('3');
break;
case 74: // D = 4 beat tempo follow
keyboardCommand('4');
break;
case 79: // G = constant tempo follow
keyboardCommand('9');
break;
case 80: // G# = automatic
keyboardCommand('0');
break;
case 62: // amplitude decrease
keyboardCommand('[');
break;
case 63: // amplitude increase
keyboardCommand(']');
break;
case 64: // tempo decrease
keyboardCommand('-');
break;
case 65: // tempo increase
keyboardCommand('=');
break;
}
}
//////////////////////////////
//
// usage -- how to run the textmidi program on the command line.
//
void usage(const char* command) {
cout <<
" \n"
"Plays a MIDI file. \n"
" \n"
"Usage: " << command << " midifile \n"
" \n"
"Options: \n"
" --options = list of all options, aliases and default values. \n"
" \n"
" \n"
<< endl;
}
// md5sum: fe33e4858d695ca9ebe7cb4c17c901f6 midiperform.cpp [20050403]