midiclock.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. //*****************************************//
  2. // midiclock.cpp
  3. //
  4. // Simple program to test MIDI clock sync. Run midiclock_in in one
  5. // console and midiclock_out in the other, make sure to choose
  6. // options that connect the clocks between programs on your platform.
  7. //
  8. // (C)2016 Refer to README.md in this archive for copyright.
  9. //
  10. //*****************************************//
  11. #include <iostream>
  12. #include <cstdlib>
  13. #include "RtMidi.h"
  14. // Platform-dependent sleep routines.
  15. #if defined(WIN32)
  16. #include <windows.h>
  17. #define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds )
  18. #else // Unix variants
  19. #include <unistd.h>
  20. #define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
  21. #endif
  22. // These functions should be embedded in a try/catch block in case of
  23. // an exception. It offers the user a choice of MIDI ports to open.
  24. // It returns false if there are no ports available.
  25. bool chooseInputPort( RtMidiIn *rtmidi );
  26. bool chooseOutputPort( RtMidiOut *rtmidi );
  27. void mycallback( double deltatime, std::vector< unsigned char > *message, void *user )
  28. {
  29. unsigned int *clock_count = reinterpret_cast<unsigned int*>(user);
  30. // Ignore longer messages
  31. if (message->size() != 1)
  32. return;
  33. unsigned int msg = message->at(0);
  34. if (msg == 0xFA)
  35. std::cout << "START received" << std::endl;
  36. if (msg == 0xFB)
  37. std::cout << "CONTINUE received" << std::endl;
  38. if (msg == 0xFC)
  39. std::cout << "STOP received" << std::endl;
  40. if (msg == 0xF8) {
  41. if (++*clock_count == 24) {
  42. double bpm = 60.0 / 24.0 / deltatime;
  43. std::cout << "One beat, estimated BPM = " << bpm <<std::endl;
  44. *clock_count = 0;
  45. }
  46. }
  47. else
  48. *clock_count = 0;
  49. }
  50. int clock_in()
  51. {
  52. RtMidiIn *midiin = 0;
  53. unsigned int clock_count = 0;
  54. try {
  55. // RtMidiIn constructor
  56. midiin = new RtMidiIn();
  57. // Call function to select port.
  58. if ( chooseInputPort( midiin ) == false ) goto cleanup;
  59. // Set our callback function. This should be done immediately after
  60. // opening the port to avoid having incoming messages written to the
  61. // queue instead of sent to the callback function.
  62. midiin->setCallback( &mycallback, &clock_count );
  63. // Don't ignore sysex, timing, or active sensing messages.
  64. midiin->ignoreTypes( false, false, false );
  65. std::cout << "\nReading MIDI input ... press <enter> to quit.\n";
  66. char input;
  67. std::cin.get(input);
  68. } catch ( RtMidiError &error ) {
  69. error.printMessage();
  70. }
  71. cleanup:
  72. delete midiin;
  73. return 0;
  74. }
  75. int clock_out()
  76. {
  77. RtMidiOut *midiout = 0;
  78. std::vector<unsigned char> message;
  79. int sleep_ms = 0, k = 0, j = 0;
  80. // RtMidiOut constructor
  81. try {
  82. midiout = new RtMidiOut();
  83. }
  84. catch ( RtMidiError &error ) {
  85. error.printMessage();
  86. exit( EXIT_FAILURE );
  87. }
  88. // Call function to select port.
  89. try {
  90. if ( chooseOutputPort( midiout ) == false ) goto cleanup;
  91. }
  92. catch ( RtMidiError &error ) {
  93. error.printMessage();
  94. goto cleanup;
  95. }
  96. // Period in ms = 100 BPM
  97. // 100*24 ticks / 1 minute, so (60*1000) / (100*24) = 25 ms / tick
  98. sleep_ms = 25;
  99. std::cout << "Generating clock at "
  100. << (60.0 / 24.0 / sleep_ms * 1000.0)
  101. << " BPM." << std::endl;
  102. // Send out a series of MIDI clock messages.
  103. // MIDI start
  104. message.clear();
  105. message.push_back( 0xFA );
  106. midiout->sendMessage( &message );
  107. std::cout << "MIDI start" << std::endl;
  108. for (j=0; j < 8; j++)
  109. {
  110. if (j > 0)
  111. {
  112. // MIDI continue
  113. message.clear();
  114. message.push_back( 0xFB );
  115. midiout->sendMessage( &message );
  116. std::cout << "MIDI continue" << std::endl;
  117. }
  118. for (k=0; k < 96; k++) {
  119. // MIDI clock
  120. message.clear();
  121. message.push_back( 0xF8 );
  122. midiout->sendMessage( &message );
  123. if (k % 24 == 0)
  124. std::cout << "MIDI clock (one beat)" << std::endl;
  125. SLEEP( sleep_ms );
  126. }
  127. // MIDI stop
  128. message.clear();
  129. message.push_back( 0xFC );
  130. midiout->sendMessage( &message );
  131. std::cout << "MIDI stop" << std::endl;
  132. SLEEP( 500 );
  133. }
  134. // MIDI stop
  135. message.clear();
  136. message.push_back( 0xFC );
  137. midiout->sendMessage( &message );
  138. std::cout << "MIDI stop" << std::endl;
  139. SLEEP( 500 );
  140. std::cout << "Done!" << std::endl;
  141. // Clean up
  142. cleanup:
  143. delete midiout;
  144. return 0;
  145. }
  146. int main( int, const char *argv[] )
  147. {
  148. std::string prog(argv[0]);
  149. if (prog.find("midiclock_in") != prog.npos) {
  150. clock_in();
  151. }
  152. else if (prog.find("midiclock_out") != prog.npos) {
  153. clock_out();
  154. }
  155. else {
  156. std::cout << "Don't know what to do as " << prog << std::endl;
  157. }
  158. return 0;
  159. }
  160. template<typename RT>
  161. bool choosePort( RT *rtmidi, const char *dir )
  162. {
  163. std::string portName;
  164. unsigned int i = 0, nPorts = rtmidi->getPortCount();
  165. if ( nPorts == 0 ) {
  166. std::cout << "No " << dir << " ports available!" << std::endl;
  167. return false;
  168. }
  169. if ( nPorts == 1 ) {
  170. std::cout << "\nOpening " << rtmidi->getPortName() << std::endl;
  171. }
  172. else {
  173. for ( i=0; i<nPorts; i++ ) {
  174. portName = rtmidi->getPortName(i);
  175. std::cout << " " << dir << " port #" << i << ": " << portName << '\n';
  176. }
  177. do {
  178. std::cout << "\nChoose a port number: ";
  179. std::cin >> i;
  180. } while ( i >= nPorts );
  181. }
  182. std::cout << "\n";
  183. rtmidi->openPort( i );
  184. return true;
  185. }
  186. bool chooseInputPort( RtMidiIn *rtmidi )
  187. {
  188. return choosePort<RtMidiIn>( rtmidi, "input" );
  189. }
  190. bool chooseOutputPort( RtMidiOut *rtmidi )
  191. {
  192. return choosePort<RtMidiOut>( rtmidi, "output" );
  193. }