232 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//*****************************************//
 | 
						|
//  midiclock.cpp
 | 
						|
//
 | 
						|
//  Simple program to test MIDI clock sync.  Run midiclock_in in one
 | 
						|
//  console and midiclock_out in the other, make sure to choose
 | 
						|
//  options that connect the clocks between programs on your platform.
 | 
						|
//
 | 
						|
//  (C)2016 Refer to README.md in this archive for copyright.
 | 
						|
//
 | 
						|
//*****************************************//
 | 
						|
 | 
						|
#include <iostream>
 | 
						|
#include <cstdlib>
 | 
						|
#include "RtMidi.h"
 | 
						|
 | 
						|
// Platform-dependent sleep routines.
 | 
						|
#if defined(WIN32)
 | 
						|
  #include <windows.h>
 | 
						|
  #define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) 
 | 
						|
#else // Unix variants
 | 
						|
  #include <unistd.h>
 | 
						|
  #define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
 | 
						|
#endif
 | 
						|
 | 
						|
// These functions should be embedded in a try/catch block in case of
 | 
						|
// an exception.  It offers the user a choice of MIDI ports to open.
 | 
						|
// It returns false if there are no ports available.
 | 
						|
bool chooseInputPort( RtMidiIn *rtmidi );
 | 
						|
bool chooseOutputPort( RtMidiOut *rtmidi );
 | 
						|
 | 
						|
void mycallback( double deltatime, std::vector< unsigned char > *message, void *user )
 | 
						|
{
 | 
						|
  unsigned int *clock_count = reinterpret_cast<unsigned int*>(user);
 | 
						|
 | 
						|
  // Ignore longer messages
 | 
						|
  if (message->size() != 1)
 | 
						|
    return;
 | 
						|
 | 
						|
  unsigned int msg = message->at(0);
 | 
						|
  if (msg == 0xFA)
 | 
						|
    std::cout << "START received" << std::endl;
 | 
						|
  if (msg == 0xFB)
 | 
						|
    std::cout << "CONTINUE received" << std::endl;
 | 
						|
  if (msg == 0xFC)
 | 
						|
    std::cout << "STOP received" << std::endl;
 | 
						|
  if (msg == 0xF8) {
 | 
						|
    if (++*clock_count == 24) {
 | 
						|
      double bpm = 60.0 / 24.0 / deltatime;
 | 
						|
      std::cout << "One beat, estimated BPM = " << bpm <<std::endl;
 | 
						|
      *clock_count = 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
    *clock_count = 0;
 | 
						|
}
 | 
						|
 | 
						|
int clock_in()
 | 
						|
{
 | 
						|
  RtMidiIn *midiin = 0;
 | 
						|
  unsigned int clock_count = 0;
 | 
						|
 | 
						|
  try {
 | 
						|
 | 
						|
    // RtMidiIn constructor
 | 
						|
    midiin = new RtMidiIn();
 | 
						|
 | 
						|
    // Call function to select port.
 | 
						|
    if ( chooseInputPort( midiin ) == false ) 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( &mycallback, &clock_count );
 | 
						|
 | 
						|
    // Don't ignore sysex, timing, or active sensing messages.
 | 
						|
    midiin->ignoreTypes( false, false, false );
 | 
						|
 | 
						|
    std::cout << "\nReading MIDI input ... press <enter> to quit.\n";
 | 
						|
    char input;
 | 
						|
    std::cin.get(input);
 | 
						|
 | 
						|
  } catch ( RtMidiError &error ) {
 | 
						|
    error.printMessage();
 | 
						|
  }
 | 
						|
 | 
						|
 cleanup:
 | 
						|
 | 
						|
  delete midiin;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int clock_out()
 | 
						|
{
 | 
						|
  RtMidiOut *midiout = 0;
 | 
						|
  std::vector<unsigned char> message;
 | 
						|
  int sleep_ms = 0, k = 0, j = 0;
 | 
						|
 | 
						|
  // RtMidiOut constructor
 | 
						|
  try {
 | 
						|
    midiout = new RtMidiOut();
 | 
						|
  }
 | 
						|
  catch ( RtMidiError &error ) {
 | 
						|
    error.printMessage();
 | 
						|
    exit( EXIT_FAILURE );
 | 
						|
  }
 | 
						|
 | 
						|
  // Call function to select port.
 | 
						|
  try {
 | 
						|
    if ( chooseOutputPort( midiout ) == false ) goto cleanup;
 | 
						|
  }
 | 
						|
  catch ( RtMidiError &error ) {
 | 
						|
    error.printMessage();
 | 
						|
    goto cleanup;
 | 
						|
  }
 | 
						|
 | 
						|
  // Period in ms = 100 BPM
 | 
						|
  // 100*24 ticks / 1 minute, so (60*1000) / (100*24) = 25 ms / tick
 | 
						|
  sleep_ms = 25;
 | 
						|
  std::cout << "Generating clock at "
 | 
						|
            << (60.0 / 24.0 / sleep_ms * 1000.0)
 | 
						|
            << " BPM." << std::endl;
 | 
						|
 | 
						|
  // Send out a series of MIDI clock messages.
 | 
						|
  // MIDI start
 | 
						|
  message.clear();
 | 
						|
  message.push_back( 0xFA );
 | 
						|
  midiout->sendMessage( &message );
 | 
						|
  std::cout << "MIDI start" << std::endl;
 | 
						|
 | 
						|
  for (j=0; j < 8; j++)
 | 
						|
  {
 | 
						|
    if (j > 0)
 | 
						|
    {
 | 
						|
      // MIDI continue
 | 
						|
      message.clear();
 | 
						|
      message.push_back( 0xFB );
 | 
						|
      midiout->sendMessage( &message );
 | 
						|
      std::cout << "MIDI continue" << std::endl;
 | 
						|
    }
 | 
						|
 | 
						|
    for (k=0; k < 96; k++) {
 | 
						|
      // MIDI clock
 | 
						|
      message.clear();
 | 
						|
      message.push_back( 0xF8 );
 | 
						|
      midiout->sendMessage( &message );
 | 
						|
      if (k % 24 == 0)
 | 
						|
        std::cout << "MIDI clock (one beat)" << std::endl;
 | 
						|
      SLEEP( sleep_ms );
 | 
						|
    }
 | 
						|
 | 
						|
    // MIDI stop
 | 
						|
    message.clear();
 | 
						|
    message.push_back( 0xFC );
 | 
						|
    midiout->sendMessage( &message );
 | 
						|
    std::cout << "MIDI stop" << std::endl;
 | 
						|
    SLEEP( 500 );
 | 
						|
  }
 | 
						|
 | 
						|
  // MIDI stop
 | 
						|
  message.clear();
 | 
						|
  message.push_back( 0xFC );
 | 
						|
  midiout->sendMessage( &message );
 | 
						|
  std::cout << "MIDI stop" << std::endl;
 | 
						|
 | 
						|
  SLEEP( 500 );
 | 
						|
 | 
						|
  std::cout << "Done!" << std::endl;
 | 
						|
 | 
						|
  // Clean up
 | 
						|
 cleanup:
 | 
						|
  delete midiout;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int main( int, const char *argv[] )
 | 
						|
{
 | 
						|
  std::string prog(argv[0]);
 | 
						|
  if (prog.find("midiclock_in") != prog.npos) {
 | 
						|
    clock_in();
 | 
						|
  }
 | 
						|
  else if (prog.find("midiclock_out") != prog.npos) {
 | 
						|
    clock_out();
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    std::cout << "Don't know what to do as " << prog << std::endl;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
template<typename RT>
 | 
						|
bool choosePort( RT *rtmidi, const char *dir )
 | 
						|
{
 | 
						|
  std::string portName;
 | 
						|
  unsigned int i = 0, nPorts = rtmidi->getPortCount();
 | 
						|
  if ( nPorts == 0 ) {
 | 
						|
    std::cout << "No " << dir << " ports available!" << std::endl;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( nPorts == 1 ) {
 | 
						|
    std::cout << "\nOpening " << rtmidi->getPortName() << std::endl;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    for ( i=0; i<nPorts; i++ ) {
 | 
						|
      portName = rtmidi->getPortName(i);
 | 
						|
      std::cout << "  " << dir << " port #" << i << ": " << portName << '\n';
 | 
						|
    }
 | 
						|
 | 
						|
    do {
 | 
						|
      std::cout << "\nChoose a port number: ";
 | 
						|
      std::cin >> i;
 | 
						|
    } while ( i >= nPorts );
 | 
						|
  }
 | 
						|
 | 
						|
  std::cout << "\n";
 | 
						|
  rtmidi->openPort( i );
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool chooseInputPort( RtMidiIn *rtmidi )
 | 
						|
{
 | 
						|
  return choosePort<RtMidiIn>( rtmidi, "input" );
 | 
						|
}
 | 
						|
 | 
						|
bool chooseOutputPort( RtMidiOut *rtmidi )
 | 
						|
{
 | 
						|
  return choosePort<RtMidiOut>( rtmidi, "output" );
 | 
						|
}
 |