main.m 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. //
  2. // main.m
  3. // DHSendMIDI
  4. //
  5. // Created by Douglas Heriot on 23/01/13.
  6. // Copyright (c) 2013 Douglas Heriot. All rights reserved.
  7. //
  8. #import <Foundation/Foundation.h>
  9. #import <SnoizeMIDI/SnoizeMIDI.h>
  10. #include <getopt.h>
  11. enum OPTION
  12. {
  13. O_VERSION = 'V',
  14. O_HELP = 'h',
  15. O_CHANNEL = 'c',
  16. O_DESTINATION = 'd',
  17. O_VERBOSE = 'v',
  18. O_NOTE_OFF = 'm',
  19. O_NOTE_ON = 'n',
  20. O_AFTERTOUCH = 'a',
  21. O_CONTROL_CHANGE = 'r',
  22. O_PROGRAM_CHANGE = 'p',
  23. O_CHANNEL_PRESSURE = 's',
  24. O_PITCH_WHEEL = 'w',
  25. };
  26. void printVersion(void);
  27. void printHelp(void);
  28. int main(int argc, char * const *argv)
  29. {
  30. @autoreleasepool
  31. {
  32. SMVoiceMessageStatus messageStatus = SMVoiceMessageStatusControl;
  33. Byte channel = 1;
  34. uint8_t data[2] = {0};
  35. NSString *destination = nil;
  36. BOOL verbose = NO;
  37. static struct option longOoptions[] = {
  38. {"version", no_argument, NULL, O_VERSION},
  39. {"help", no_argument, NULL, O_HELP},
  40. {"channel", required_argument, NULL, 'c'},
  41. {"destination", required_argument, NULL, 'd'},
  42. {"verbose", no_argument, NULL, 'v'},
  43. {"note-off", no_argument, NULL, O_NOTE_OFF},
  44. {"note-on", no_argument, NULL, O_NOTE_ON},
  45. {"aftertouch", no_argument, NULL, O_AFTERTOUCH},
  46. {"control-change", no_argument, NULL, O_CONTROL_CHANGE},
  47. {"control", no_argument, NULL, O_CONTROL_CHANGE},
  48. {"cc", no_argument, NULL, O_CONTROL_CHANGE},
  49. {"program-change", no_argument, NULL, O_PROGRAM_CHANGE},
  50. {"program", no_argument, NULL, O_PROGRAM_CHANGE},
  51. {"pc", no_argument, NULL, O_PROGRAM_CHANGE},
  52. {"channel-pressure",no_argument, NULL, O_CHANNEL_PRESSURE},
  53. {"pressure", no_argument, NULL, O_CHANNEL_PRESSURE},
  54. {"pitch-wheel", no_argument, NULL, O_PITCH_WHEEL},
  55. {"pitch", no_argument, NULL, O_PITCH_WHEEL},
  56. };
  57. const char shortOptions[] = {
  58. O_VERSION, O_HELP,
  59. O_CHANNEL, ':', O_DESTINATION, ':', O_VERBOSE,
  60. O_NOTE_ON, O_NOTE_OFF, O_AFTERTOUCH, O_CONTROL_CHANGE, O_PROGRAM_CHANGE, O_CHANNEL_PRESSURE, O_PITCH_WHEEL,
  61. '\0'};
  62. int ch;
  63. while((ch = getopt_long(argc, argv, shortOptions, longOoptions, NULL)) != -1)
  64. {
  65. switch ((enum OPTION)ch)
  66. {
  67. case O_VERSION:
  68. printVersion();
  69. exit(0);
  70. break;
  71. case O_HELP:
  72. printVersion();
  73. printHelp();
  74. exit(0);
  75. break;
  76. case O_CHANNEL:
  77. channel = atoi(optarg);
  78. // Make sure it’s within bounds 1-16
  79. channel -= 1;
  80. channel %= 16;
  81. channel += 1;
  82. break;
  83. case O_DESTINATION:
  84. destination = [NSString stringWithUTF8String:optarg];
  85. break;
  86. case O_VERBOSE:
  87. verbose = YES;
  88. break;
  89. case O_NOTE_ON:
  90. messageStatus = SMVoiceMessageStatusNoteOn;
  91. break;
  92. case O_NOTE_OFF:
  93. messageStatus = SMVoiceMessageStatusNoteOff;
  94. break;
  95. case O_AFTERTOUCH:
  96. messageStatus = SMVoiceMessageStatusAftertouch;
  97. break;
  98. case O_CONTROL_CHANGE:
  99. messageStatus = SMVoiceMessageStatusControl;
  100. break;
  101. case O_PROGRAM_CHANGE:
  102. messageStatus = SMVoiceMessageStatusProgram;
  103. break;
  104. case O_CHANNEL_PRESSURE:
  105. messageStatus = SMVoiceMessageStatusChannelPressure;
  106. break;
  107. case O_PITCH_WHEEL:
  108. messageStatus = SMVoiceMessageStatusPitchWheel;
  109. break;
  110. }
  111. }
  112. argc -= optind;
  113. argv += optind;
  114. // Create the message
  115. SMVoiceMessage *message = [[SMVoiceMessage alloc] initWithTimeStamp:0 statusByte:messageStatus];
  116. [message setTimeStampToNow];
  117. [message setChannel:channel];
  118. const NSUInteger requiredLength = message.otherDataLength;
  119. // Check the given arguments
  120. if(argc != requiredLength)
  121. {
  122. const char s[] = {requiredLength == 1 ? '\0' : 's', '\0'};
  123. fprintf(stderr, "%s message must have %lu argument%s\n", message.typeForDisplay.UTF8String, requiredLength, s);
  124. return 1;
  125. }
  126. // Get the arguments
  127. // Make sure they’re within 0-127, by &ing with 0x7F
  128. data[0] = atoi(argv[0]) & 0x7F;
  129. [message setDataByte1:data[0]];
  130. if(requiredLength > 1)
  131. {
  132. data[1] = atoi(argv[1]) & 0x7F;
  133. [message setDataByte2:data[2]];
  134. }
  135. SMPortOutputStream *os = [[SMPortOutputStream alloc] init];
  136. // Set the destination endpoints
  137. NSSet *endpoints = nil;
  138. if(destination)
  139. endpoints = [NSSet setWithObject:[SMDestinationEndpoint destinationEndpointWithName:destination]];
  140. else
  141. endpoints = [NSSet setWithArray:[SMDestinationEndpoint destinationEndpoints]];
  142. [os setEndpoints:endpoints];
  143. // Send the message
  144. [os takeMIDIMessages:@[message]];
  145. if(verbose)
  146. {
  147. printf("Sent %s: %d", message.typeForDisplay.UTF8String, data[0]);
  148. if([message otherDataLength] > 1)
  149. printf(" %d", data[1]);
  150. printf("\n");
  151. }
  152. }
  153. return 0;
  154. }
  155. void printVersion(void)
  156. {
  157. printf("DHSendMIDI 1.0\nCopyright 2013, Douglas Heriot\nhttps://github.com/DouglasHeriot/DHSendMIDI\n");
  158. }
  159. void printHelp(void)
  160. {
  161. printf("\n"
  162. "Usage: DHSendMIDI [options] byte1 [byte2]\n\n"
  163. "Options:\n"
  164. " --note-on, -n Note On\n"
  165. " --note-off, -m Note Off\n"
  166. " --aftertouch, -a Aftertouch\n"
  167. " --control-change, --cc, -c Control Change\n"
  168. " --program-change, --pc, p Program Change (only 1 byte of data)\n"
  169. " --channel-pressure, --pressure, -s Channel Pressure (only 1 byte of data)\n"
  170. " --pitch-wheel, --pitch, -w Pitch Wheel (2 bytes, making a 14-bit value)\n"
  171. "\n"
  172. " --channel, -c Channel 1-16\n"
  173. " --destination, -d Destination device\n"
  174. " Example: to send to IAC Driver, Bus 1, use\n"
  175. " --destination \"Bus 1\"\n"
  176. " Defaults to all destinations\n"
  177. "\n"
  178. " --verbose, -v Prints message being sent\n"
  179. "\n"
  180. " --version, -V Displays version\n"
  181. " --help, -h Displays this help\n"
  182. "\n"
  183. "Without any options, DHSendMIDI defaults to control change messages, on channel 1, to all destinations.\n"
  184. "\n");
  185. }