main.m 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. if(channel == 0)
  79. channel = 1; // you probably meant 1
  80. // Make sure it’s within bounds 1-16
  81. channel -= 1;
  82. channel %= 16;
  83. channel += 1;
  84. break;
  85. case O_DESTINATION:
  86. destination = [NSString stringWithUTF8String:optarg];
  87. break;
  88. case O_VERBOSE:
  89. verbose = YES;
  90. break;
  91. case O_NOTE_ON:
  92. messageStatus = SMVoiceMessageStatusNoteOn;
  93. break;
  94. case O_NOTE_OFF:
  95. messageStatus = SMVoiceMessageStatusNoteOff;
  96. break;
  97. case O_AFTERTOUCH:
  98. messageStatus = SMVoiceMessageStatusAftertouch;
  99. break;
  100. case O_CONTROL_CHANGE:
  101. messageStatus = SMVoiceMessageStatusControl;
  102. break;
  103. case O_PROGRAM_CHANGE:
  104. messageStatus = SMVoiceMessageStatusProgram;
  105. break;
  106. case O_CHANNEL_PRESSURE:
  107. messageStatus = SMVoiceMessageStatusChannelPressure;
  108. break;
  109. case O_PITCH_WHEEL:
  110. messageStatus = SMVoiceMessageStatusPitchWheel;
  111. break;
  112. }
  113. }
  114. argc -= optind;
  115. argv += optind;
  116. // Create the message
  117. SMVoiceMessage *message = [[SMVoiceMessage alloc] initWithTimeStamp:0 statusByte:messageStatus];
  118. [message setChannel:channel];
  119. const NSUInteger requiredLength = message.otherDataLength;
  120. // Check the given arguments
  121. if(argc != requiredLength)
  122. {
  123. const char s[] = {requiredLength == 1 ? '\0' : 's', '\0'};
  124. fprintf(stderr, "%s message must have %lu argument%s\n", message.typeForDisplay.UTF8String, requiredLength, s);
  125. return 1;
  126. }
  127. // Get the arguments
  128. // Make sure they’re within 0-127, by &ing with 0x7F
  129. data[0] = atoi(argv[0]) & 0x7F;
  130. [message setDataByte1:data[0]];
  131. if(requiredLength > 1)
  132. {
  133. data[1] = atoi(argv[1]) & 0x7F;
  134. [message setDataByte2:data[1]];
  135. }
  136. SMPortOutputStream *os = [[SMPortOutputStream alloc] init];
  137. // Set the destination endpoints
  138. NSSet *endpoints = nil;
  139. if(destination)
  140. endpoints = [NSSet setWithObject:[SMDestinationEndpoint destinationEndpointWithName:destination]];
  141. else
  142. endpoints = [NSSet setWithArray:[SMDestinationEndpoint destinationEndpoints]];
  143. [os setEndpoints:endpoints];
  144. // Send the message
  145. [os takeMIDIMessages:@[message]];
  146. if(verbose)
  147. {
  148. printf("Sent %s: %d", message.typeForDisplay.UTF8String, data[0]);
  149. if([message otherDataLength] > 1)
  150. printf(" %d", data[1]);
  151. printf("\n");
  152. }
  153. }
  154. return 0;
  155. }
  156. void printVersion(void)
  157. {
  158. printf("DHSendMIDI 1.1\nCopyright 2013, Douglas Heriot\nCopyright 2017, Manoel Trapier, https://github.com/godzil/DHSendMIDI\n");
  159. }
  160. void printHelp(void)
  161. {
  162. printf("\n"
  163. "Usage: DHSendMIDI [options] byte1 [byte2]\n\n"
  164. "Options:\n"
  165. " --note-on, -n Note On\n"
  166. " --note-off, -m Note Off\n"
  167. " --aftertouch, -a Aftertouch\n"
  168. " --control-change, --cc, -c Control Change\n"
  169. " --program-change, --pc, p Program Change (only 1 byte of data)\n"
  170. " --channel-pressure, --pressure, -s Channel Pressure (only 1 byte of data)\n"
  171. " --pitch-wheel, --pitch, -w Pitch Wheel (2 bytes, making a 14-bit value)\n"
  172. "\n"
  173. " --channel, -c Channel 1-16\n"
  174. " --destination, -d Destination device\n"
  175. " Example: to send to IAC Driver, Bus 1, use\n"
  176. " --destination \"Bus 1\"\n"
  177. " Defaults to all destinations\n"
  178. "\n"
  179. " --verbose, -v Prints message being sent\n"
  180. "\n"
  181. " --version, -V Displays version\n"
  182. " --help, -h Displays this help\n"
  183. "\n"
  184. "Without any options, DHSendMIDI defaults to control change messages, on channel 1, to all destinations.\n"
  185. "\n");
  186. }