The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
8.2KB

  1. /*!
  2. @file AudioUnitSDK/AUMIDIBase.cpp
  3. @copyright © 2000-2021 Apple Inc. All rights reserved.
  4. */
  5. #include <AudioUnitSDK/AUMIDIBase.h>
  6. #include <CoreMIDI/CoreMIDI.h>
  7. namespace ausdk {
  8. // MIDI CC data bytes
  9. constexpr uint8_t kMIDIController_AllSoundOff = 120u;
  10. constexpr uint8_t kMIDIController_ResetAllControllers = 121u;
  11. constexpr uint8_t kMIDIController_AllNotesOff = 123u;
  12. OSStatus AUMIDIBase::DelegateGetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
  13. AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable)
  14. {
  15. (void)inScope;
  16. (void)inElement;
  17. (void)outDataSize;
  18. (void)outWritable;
  19. switch (inID) { // NOLINT if/else?!
  20. #if AUSDK_HAVE_XML_NAMES
  21. case kMusicDeviceProperty_MIDIXMLNames:
  22. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  23. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  24. AUSDK_Require(GetXMLNames(nullptr) == noErr, kAudioUnitErr_InvalidProperty);
  25. outDataSize = sizeof(CFURLRef);
  26. outWritable = false;
  27. return noErr;
  28. #endif
  29. #if AUSDK_HAVE_MIDI_MAPPING
  30. case kAudioUnitProperty_AllParameterMIDIMappings:
  31. AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
  32. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  33. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  34. outWritable = true;
  35. outDataSize = sizeof(AUParameterMIDIMapping) * mMIDIMapper->GetNumberMaps();
  36. return noErr;
  37. case kAudioUnitProperty_HotMapParameterMIDIMapping:
  38. case kAudioUnitProperty_AddParameterMIDIMapping:
  39. case kAudioUnitProperty_RemoveParameterMIDIMapping:
  40. AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
  41. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  42. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  43. outWritable = true;
  44. outDataSize = sizeof(AUParameterMIDIMapping);
  45. return noErr;
  46. #endif
  47. default:
  48. return kAudioUnitErr_InvalidProperty;
  49. }
  50. }
  51. OSStatus AUMIDIBase::DelegateGetProperty(
  52. AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData)
  53. {
  54. (void)inScope;
  55. (void)inElement;
  56. (void)outData;
  57. switch (inID) { // NOLINT if/else?!
  58. #if AUSDK_HAVE_XML_NAMES
  59. case kMusicDeviceProperty_MIDIXMLNames:
  60. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  61. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  62. return GetXMLNames(static_cast<CFURLRef*>(outData));
  63. #endif
  64. #if AUSDK_HAVE_MIDI_MAPPING
  65. case kAudioUnitProperty_AllParameterMIDIMappings: {
  66. AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
  67. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  68. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  69. AUParameterMIDIMapping* const maps = (static_cast<AUParameterMIDIMapping*>(outData));
  70. mMIDIMapper->GetMaps(maps);
  71. return noErr;
  72. }
  73. case kAudioUnitProperty_HotMapParameterMIDIMapping: {
  74. AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
  75. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  76. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  77. AUParameterMIDIMapping* const map = (static_cast<AUParameterMIDIMapping*>(outData));
  78. mMIDIMapper->GetHotParameterMap(*map);
  79. return noErr;
  80. }
  81. #endif
  82. default:
  83. return kAudioUnitErr_InvalidProperty;
  84. }
  85. }
  86. OSStatus AUMIDIBase::DelegateSetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
  87. AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
  88. {
  89. (void)inScope;
  90. (void)inElement;
  91. (void)inData;
  92. (void)inDataSize;
  93. switch (inID) {
  94. #if AUSDK_HAVE_MIDI_MAPPING
  95. case kAudioUnitProperty_AddParameterMIDIMapping: {
  96. AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
  97. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  98. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  99. const auto* const maps = static_cast<const AUParameterMIDIMapping*>(inData);
  100. mMIDIMapper->AddParameterMapping(
  101. maps, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance);
  102. mAUBaseInstance.PropertyChanged(
  103. kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0);
  104. return noErr;
  105. }
  106. case kAudioUnitProperty_RemoveParameterMIDIMapping: {
  107. AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
  108. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  109. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  110. const auto* const maps = static_cast<const AUParameterMIDIMapping*>(inData);
  111. bool didChange = false;
  112. mMIDIMapper->RemoveParameterMapping(
  113. maps, (inDataSize / sizeof(AUParameterMIDIMapping)), didChange);
  114. if (didChange) {
  115. mAUBaseInstance.PropertyChanged(
  116. kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0);
  117. }
  118. return noErr;
  119. }
  120. case kAudioUnitProperty_HotMapParameterMIDIMapping: {
  121. AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
  122. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  123. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  124. const auto& map = *static_cast<const AUParameterMIDIMapping*>(inData);
  125. mMIDIMapper->SetHotMapping(map);
  126. return noErr;
  127. }
  128. case kAudioUnitProperty_AllParameterMIDIMappings: {
  129. AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
  130. AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
  131. AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
  132. const auto* const mappings = static_cast<const AUParameterMIDIMapping*>(inData);
  133. mMIDIMapper->ReplaceAllMaps(
  134. mappings, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance);
  135. return noErr;
  136. }
  137. #endif
  138. default:
  139. return kAudioUnitErr_InvalidProperty;
  140. }
  141. }
  142. constexpr uint8_t MIDIStatusNibbleValue(uint8_t status) noexcept { return (status & 0xF0U) >> 4u; }
  143. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  144. // AUMIDIBase::HandleMIDIEvent
  145. //
  146. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  147. OSStatus AUMIDIBase::HandleMIDIEvent(
  148. UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
  149. {
  150. if (!mAUBaseInstance.IsInitialized()) {
  151. return kAudioUnitErr_Uninitialized;
  152. }
  153. #if AUSDK_HAVE_MIDI_MAPPING
  154. // you potentially have a choice to make here - if a param mapping matches, do you still want to
  155. // process the MIDI event or not. The default behaviour is to continue on with the MIDI event.
  156. if (mMIDIMapper) {
  157. if (mMIDIMapper->HandleHotMapping(status, channel, data1, mAUBaseInstance)) {
  158. mAUBaseInstance.PropertyChanged(
  159. kAudioUnitProperty_HotMapParameterMIDIMapping, kAudioUnitScope_Global, 0);
  160. } else {
  161. mMIDIMapper->FindParameterMapEventMatch(
  162. status, channel, data1, data2, inStartFrame, mAUBaseInstance);
  163. }
  164. }
  165. #endif
  166. switch (MIDIStatusNibbleValue(status)) {
  167. case kMIDICVStatusNoteOn:
  168. if (data2 != 0u) {
  169. return HandleNoteOn(channel, data1, data2, inStartFrame);
  170. } else {
  171. // zero velocity translates to note off
  172. return HandleNoteOff(channel, data1, data2, inStartFrame);
  173. }
  174. case kMIDICVStatusNoteOff:
  175. return HandleNoteOff(channel, data1, data2, inStartFrame);
  176. default:
  177. return HandleNonNoteEvent(status, channel, data1, data2, inStartFrame);
  178. }
  179. }
  180. OSStatus AUMIDIBase::HandleNonNoteEvent(
  181. UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
  182. {
  183. switch (MIDIStatusNibbleValue(status)) {
  184. case kMIDICVStatusPitchBend:
  185. return HandlePitchWheel(channel, data1, data2, inStartFrame);
  186. case kMIDICVStatusProgramChange:
  187. return HandleProgramChange(channel, data1);
  188. case kMIDICVStatusChannelPressure:
  189. return HandleChannelPressure(channel, data1, inStartFrame);
  190. case kMIDICVStatusControlChange: {
  191. switch (data1) {
  192. case kMIDIController_AllNotesOff:
  193. return HandleAllNotesOff(channel);
  194. case kMIDIController_ResetAllControllers:
  195. return HandleResetAllControllers(channel);
  196. case kMIDIController_AllSoundOff:
  197. return HandleAllSoundOff(channel);
  198. default:
  199. return HandleControlChange(channel, data1, data2, inStartFrame);
  200. }
  201. }
  202. case kMIDICVStatusPolyPressure:
  203. return HandlePolyPressure(channel, data1, data2, inStartFrame);
  204. default:
  205. return noErr;
  206. }
  207. }
  208. OSStatus AUMIDIBase::SysEx(const UInt8* inData, UInt32 inLength)
  209. {
  210. if (!mAUBaseInstance.IsInitialized()) {
  211. return kAudioUnitErr_Uninitialized;
  212. }
  213. return HandleSysEx(inData, inLength);
  214. }
  215. } // namespace ausdk