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" ); | ||
|  | } |