mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
251 lines
8.2 KiB
C++
251 lines
8.2 KiB
C++
/*!
|
|
@file AudioUnitSDK/AUMIDIBase.cpp
|
|
@copyright © 2000-2021 Apple Inc. All rights reserved.
|
|
*/
|
|
#include <AudioUnitSDK/AUMIDIBase.h>
|
|
#include <CoreMIDI/CoreMIDI.h>
|
|
|
|
namespace ausdk {
|
|
|
|
// MIDI CC data bytes
|
|
constexpr uint8_t kMIDIController_AllSoundOff = 120u;
|
|
constexpr uint8_t kMIDIController_ResetAllControllers = 121u;
|
|
constexpr uint8_t kMIDIController_AllNotesOff = 123u;
|
|
|
|
OSStatus AUMIDIBase::DelegateGetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
|
|
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable)
|
|
{
|
|
(void)inScope;
|
|
(void)inElement;
|
|
(void)outDataSize;
|
|
(void)outWritable;
|
|
|
|
switch (inID) { // NOLINT if/else?!
|
|
#if AUSDK_HAVE_XML_NAMES
|
|
case kMusicDeviceProperty_MIDIXMLNames:
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
AUSDK_Require(GetXMLNames(nullptr) == noErr, kAudioUnitErr_InvalidProperty);
|
|
outDataSize = sizeof(CFURLRef);
|
|
outWritable = false;
|
|
return noErr;
|
|
#endif
|
|
|
|
#if AUSDK_HAVE_MIDI_MAPPING
|
|
case kAudioUnitProperty_AllParameterMIDIMappings:
|
|
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
outWritable = true;
|
|
outDataSize = sizeof(AUParameterMIDIMapping) * mMIDIMapper->GetNumberMaps();
|
|
return noErr;
|
|
|
|
case kAudioUnitProperty_HotMapParameterMIDIMapping:
|
|
case kAudioUnitProperty_AddParameterMIDIMapping:
|
|
case kAudioUnitProperty_RemoveParameterMIDIMapping:
|
|
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
outWritable = true;
|
|
outDataSize = sizeof(AUParameterMIDIMapping);
|
|
return noErr;
|
|
#endif
|
|
|
|
default:
|
|
return kAudioUnitErr_InvalidProperty;
|
|
}
|
|
}
|
|
|
|
OSStatus AUMIDIBase::DelegateGetProperty(
|
|
AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData)
|
|
{
|
|
(void)inScope;
|
|
(void)inElement;
|
|
(void)outData;
|
|
|
|
switch (inID) { // NOLINT if/else?!
|
|
#if AUSDK_HAVE_XML_NAMES
|
|
case kMusicDeviceProperty_MIDIXMLNames:
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
return GetXMLNames(static_cast<CFURLRef*>(outData));
|
|
#endif
|
|
|
|
#if AUSDK_HAVE_MIDI_MAPPING
|
|
case kAudioUnitProperty_AllParameterMIDIMappings: {
|
|
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
AUParameterMIDIMapping* const maps = (static_cast<AUParameterMIDIMapping*>(outData));
|
|
mMIDIMapper->GetMaps(maps);
|
|
return noErr;
|
|
}
|
|
|
|
case kAudioUnitProperty_HotMapParameterMIDIMapping: {
|
|
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
AUParameterMIDIMapping* const map = (static_cast<AUParameterMIDIMapping*>(outData));
|
|
mMIDIMapper->GetHotParameterMap(*map);
|
|
return noErr;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
return kAudioUnitErr_InvalidProperty;
|
|
}
|
|
}
|
|
|
|
OSStatus AUMIDIBase::DelegateSetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
|
|
AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
|
|
{
|
|
(void)inScope;
|
|
(void)inElement;
|
|
(void)inData;
|
|
(void)inDataSize;
|
|
|
|
switch (inID) {
|
|
|
|
#if AUSDK_HAVE_MIDI_MAPPING
|
|
case kAudioUnitProperty_AddParameterMIDIMapping: {
|
|
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
const auto* const maps = static_cast<const AUParameterMIDIMapping*>(inData);
|
|
mMIDIMapper->AddParameterMapping(
|
|
maps, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance);
|
|
mAUBaseInstance.PropertyChanged(
|
|
kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0);
|
|
return noErr;
|
|
}
|
|
|
|
case kAudioUnitProperty_RemoveParameterMIDIMapping: {
|
|
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
const auto* const maps = static_cast<const AUParameterMIDIMapping*>(inData);
|
|
bool didChange = false;
|
|
mMIDIMapper->RemoveParameterMapping(
|
|
maps, (inDataSize / sizeof(AUParameterMIDIMapping)), didChange);
|
|
if (didChange) {
|
|
mAUBaseInstance.PropertyChanged(
|
|
kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0);
|
|
}
|
|
return noErr;
|
|
}
|
|
|
|
case kAudioUnitProperty_HotMapParameterMIDIMapping: {
|
|
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
const auto& map = *static_cast<const AUParameterMIDIMapping*>(inData);
|
|
mMIDIMapper->SetHotMapping(map);
|
|
return noErr;
|
|
}
|
|
|
|
case kAudioUnitProperty_AllParameterMIDIMappings: {
|
|
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
|
|
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
|
|
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
|
|
const auto* const mappings = static_cast<const AUParameterMIDIMapping*>(inData);
|
|
mMIDIMapper->ReplaceAllMaps(
|
|
mappings, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance);
|
|
return noErr;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
return kAudioUnitErr_InvalidProperty;
|
|
}
|
|
}
|
|
|
|
constexpr uint8_t MIDIStatusNibbleValue(uint8_t status) noexcept { return (status & 0xF0U) >> 4u; }
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// AUMIDIBase::HandleMIDIEvent
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
OSStatus AUMIDIBase::HandleMIDIEvent(
|
|
UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
|
|
{
|
|
if (!mAUBaseInstance.IsInitialized()) {
|
|
return kAudioUnitErr_Uninitialized;
|
|
}
|
|
|
|
#if AUSDK_HAVE_MIDI_MAPPING
|
|
// you potentially have a choice to make here - if a param mapping matches, do you still want to
|
|
// process the MIDI event or not. The default behaviour is to continue on with the MIDI event.
|
|
if (mMIDIMapper) {
|
|
if (mMIDIMapper->HandleHotMapping(status, channel, data1, mAUBaseInstance)) {
|
|
mAUBaseInstance.PropertyChanged(
|
|
kAudioUnitProperty_HotMapParameterMIDIMapping, kAudioUnitScope_Global, 0);
|
|
} else {
|
|
mMIDIMapper->FindParameterMapEventMatch(
|
|
status, channel, data1, data2, inStartFrame, mAUBaseInstance);
|
|
}
|
|
}
|
|
#endif
|
|
switch (MIDIStatusNibbleValue(status)) {
|
|
case kMIDICVStatusNoteOn:
|
|
if (data2 != 0u) {
|
|
return HandleNoteOn(channel, data1, data2, inStartFrame);
|
|
} else {
|
|
// zero velocity translates to note off
|
|
return HandleNoteOff(channel, data1, data2, inStartFrame);
|
|
}
|
|
|
|
case kMIDICVStatusNoteOff:
|
|
return HandleNoteOff(channel, data1, data2, inStartFrame);
|
|
|
|
default:
|
|
return HandleNonNoteEvent(status, channel, data1, data2, inStartFrame);
|
|
}
|
|
}
|
|
|
|
OSStatus AUMIDIBase::HandleNonNoteEvent(
|
|
UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
|
|
{
|
|
switch (MIDIStatusNibbleValue(status)) {
|
|
case kMIDICVStatusPitchBend:
|
|
return HandlePitchWheel(channel, data1, data2, inStartFrame);
|
|
|
|
case kMIDICVStatusProgramChange:
|
|
return HandleProgramChange(channel, data1);
|
|
|
|
case kMIDICVStatusChannelPressure:
|
|
return HandleChannelPressure(channel, data1, inStartFrame);
|
|
|
|
case kMIDICVStatusControlChange: {
|
|
switch (data1) {
|
|
case kMIDIController_AllNotesOff:
|
|
return HandleAllNotesOff(channel);
|
|
|
|
case kMIDIController_ResetAllControllers:
|
|
return HandleResetAllControllers(channel);
|
|
|
|
case kMIDIController_AllSoundOff:
|
|
return HandleAllSoundOff(channel);
|
|
|
|
default:
|
|
return HandleControlChange(channel, data1, data2, inStartFrame);
|
|
}
|
|
}
|
|
|
|
case kMIDICVStatusPolyPressure:
|
|
return HandlePolyPressure(channel, data1, data2, inStartFrame);
|
|
|
|
default:
|
|
return noErr;
|
|
}
|
|
}
|
|
|
|
OSStatus AUMIDIBase::SysEx(const UInt8* inData, UInt32 inLength)
|
|
{
|
|
if (!mAUBaseInstance.IsInitialized()) {
|
|
return kAudioUnitErr_Uninitialized;
|
|
}
|
|
|
|
return HandleSysEx(inData, inLength);
|
|
}
|
|
|
|
} // namespace ausdk
|