AUPublic/AUInstrumentBase/SynthElement.cpp
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Part of Core Audio AUInstrument Base Classes |
*/ |
#include "SynthElement.h" |
#include "AUInstrumentBase.h" |
#include "AUMIDIDefs.h" |
#undef DEBUG_PRINT |
#define DEBUG_PRINT 0 |
#define DEBUG_PRINT_NOTE 0 |
#define DEBUG_PRINT_RENDER 0 |
//////////////////////////////////////////////////////////////////////////////////////////////////////////// |
MidiControls::MidiControls() |
{ |
Reset(); |
} |
void MidiControls::Reset() |
{ |
memset(mControls, 0, sizeof(mControls)); |
memset(mPolyPressure, 0, sizeof(mPolyPressure)); |
mMonoPressure = 0; |
mProgramChange = 0; |
mPitchBend = 0; |
mActiveRPN = 0; |
mActiveNRPN = 0; |
mActiveRPValue = 0; |
mActiveNRPValue = 0; |
mControls[kMidiController_Pan] = 64; |
mControls[kMidiController_Expression] = 127; |
mPitchBendDepth = 24 << 7; |
mFPitchBendDepth = 24.0f; |
mFPitchBend = 0.0f; |
} |
SynthElement::SynthElement(AUInstrumentBase *audioUnit, UInt32 inElement) |
: AUElement(audioUnit), mIndex(inElement) |
{ |
} |
SynthElement::~SynthElement() |
{ |
} |
SynthGroupElement::SynthGroupElement(AUInstrumentBase *audioUnit, UInt32 inElement, MIDIControlHandler *inHandler) |
: SynthElement(audioUnit, inElement), |
mCurrentAbsoluteFrame(-1), |
mMidiControlHandler(inHandler), |
mSustainIsOn(false), mSostenutoIsOn(false), mOutputBus(0), mGroupID(kUnassignedGroup) |
{ |
for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i) |
mNoteList[i].mState = (SynthNoteState) i; |
} |
SynthGroupElement::~SynthGroupElement() |
{ |
delete mMidiControlHandler; |
} |
void SynthGroupElement::SetGroupID (MusicDeviceGroupID inGroup) |
{ |
// can't re-assign a group once its been assigned |
if (mGroupID != kUnassignedGroup) throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement); |
mGroupID = inGroup; |
} |
void SynthGroupElement::Reset() |
{ |
#if DEBUG_PRINT |
printf("SynthGroupElement::Reset\n"); |
#endif |
mMidiControlHandler->Reset(); |
for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i) |
mNoteList[i].Empty(); |
} |
SynthPartElement::SynthPartElement(AUInstrumentBase *audioUnit, UInt32 inElement) |
: SynthElement(audioUnit, inElement) |
{ |
} |
// Return the SynthNote with the given inNoteID, if found. If unreleasedOnly is true, only look for |
// attacked and sostenutoed notes, otherwise search all states. Return state of found note via outNoteState. |
SynthNote *SynthGroupElement::GetNote(NoteInstanceID inNoteID, bool unreleasedOnly, UInt32 *outNoteState) |
{ |
#if DEBUG_PRINT_RENDER |
printf("SynthGroupElement::GetNote %d, unreleased = %d\n", inNoteID, unreleasedOnly); |
#endif |
const UInt32 lastNoteState = unreleasedOnly ? |
(mSostenutoIsOn ? kNoteState_Sostenutoed : kNoteState_Attacked) |
: kNoteState_Released; |
SynthNote *note = NULL; |
// Search for notes in each successive state |
for (UInt32 noteState = kNoteState_Attacked; noteState <= lastNoteState; ++noteState) |
{ |
if (outNoteState) *outNoteState = noteState; // even if we find nothing |
note = mNoteList[noteState].mHead; |
while (note && note->mNoteID != inNoteID) |
{ |
#if DEBUG_PRINT_RENDER |
printf(" checking %p id: %d\n", note, note->mNoteID); |
#endif |
note = note->mNext; |
} |
if (note) |
{ |
#if DEBUG_PRINT_RENDER |
printf(" found %p\n", note); |
#endif |
break; |
} |
} |
return note; |
} |
void SynthGroupElement::NoteOn(SynthNote *note, |
SynthPartElement *part, |
NoteInstanceID inNoteID, |
UInt32 inOffsetSampleFrame, |
const MusicDeviceNoteParams &inParams) |
{ |
#if DEBUG_PRINT_NOTE |
printf("SynthGroupElement::NoteOn %d\n", inNoteID); |
#endif |
// TODO: CONSIDER FIXING this to not need to initialize mCurrentAbsoluteFrame to -1. |
UInt64 absoluteFrame = (mCurrentAbsoluteFrame == -1) ? inOffsetSampleFrame : mCurrentAbsoluteFrame + inOffsetSampleFrame; |
if (note->AttackNote(part, this, inNoteID, absoluteFrame, inOffsetSampleFrame, inParams)) { |
mNoteList[kNoteState_Attacked].AddNote(note); |
} |
} |
void SynthGroupElement::NoteOff(NoteInstanceID inNoteID, UInt32 inFrame) |
{ |
#if DEBUG_PRINT_NOTE |
printf("SynthGroupElement::NoteOff %d\n", inNoteID); |
#endif |
UInt32 noteState = kNoteState_Attacked; |
SynthNote *note = GetNote(inNoteID, true, ¬eState); // asking for unreleased only |
if (note) |
{ |
#if DEBUG_PRINT_NOTE |
printf(" old note state: %d\n", note->mState); |
#endif |
if (noteState == kNoteState_Attacked) |
{ |
mNoteList[noteState].RemoveNote(note); |
if (mSustainIsOn) { |
mNoteList[kNoteState_ReleasedButSustained].AddNote(note); |
} else { |
note->Release(inFrame); |
mNoteList[kNoteState_Released].AddNote(note); |
} |
#if DEBUG_PRINT_NOTE |
printf(" new note state: %d\n", note->mState); |
#endif |
} |
else /* if (noteState == kNoteState_Sostenutoed) */ |
{ |
mNoteList[kNoteState_Sostenutoed].RemoveNote(note); |
mNoteList[kNoteState_ReleasedButSostenutoed].AddNote(note); |
} |
} |
} |
void SynthGroupElement::NoteEnded(SynthNote *inNote, UInt32 inFrame) |
{ |
#if DEBUG_PRINT_NOTE |
printf("SynthGroupElement::NoteEnded: id %d state %d\n", inNote->mNoteID, inNote->mState); |
#endif |
if (inNote->IsSounding()) { |
SynthNoteList *list = &mNoteList[inNote->GetState()]; |
list->RemoveNote(inNote); |
} |
GetAUInstrument()->AddFreeNote(inNote); |
} |
void SynthGroupElement::NoteFastReleased(SynthNote *inNote) |
{ |
#if DEBUG_PRINT_NOTE |
printf("SynthGroupElement::NoteFastReleased id %d state %d\n", inNote->mNoteID, inNote->mState); |
#endif |
if (inNote->IsActive()) { |
mNoteList[inNote->GetState()].RemoveNote(inNote); |
GetAUInstrument()->DecNumActiveNotes(); |
mNoteList[kNoteState_FastReleased].AddNote(inNote); |
} |
else { |
Assert(true, "ASSERT FAILED: Attempting to fast-release non-active note"); |
} |
} |
bool SynthGroupElement::ChannelMessage(UInt16 controllerID, UInt16 inValue) |
{ |
bool handled = true; |
#if DEBUG_PRINT |
printf("SynthGroupElement::ChannelMessage(0x%x, %u)\n", controllerID, inValue); |
#endif |
// Sustain and sostenuto are "pedal events", and are handled during render cycle |
if (controllerID <= kMidiController_RPN_MSB && controllerID != kMidiController_Sustain && controllerID != kMidiController_Sostenuto) |
handled = mMidiControlHandler->SetController(controllerID, UInt8(inValue)); |
else |
{ |
switch (controllerID) |
{ |
case kMidiMessage_ProgramChange: |
handled = mMidiControlHandler->SetProgramChange(inValue); |
break; |
case kMidiMessage_PitchWheel: |
handled = mMidiControlHandler->SetPitchWheel(inValue); |
break; |
case kMidiMessage_ChannelPressure: |
#if DEBUG_PRINT |
printf("SynthGroupElement::ChannelMessage: Channel Pressure %u\n", inValue); |
#endif |
handled = mMidiControlHandler->SetChannelPressure(UInt8(inValue)); |
break; |
case kMidiMessage_PolyPressure: |
{ UInt8 inKey = inValue >> 7; |
UInt8 val = inValue & 0x7f; |
handled = mMidiControlHandler->SetPolyPressure(inKey, val); |
break; |
} |
default: |
handled = false; |
break; |
} |
} |
return handled; |
} |
void SynthGroupElement::SostenutoOn(UInt32 inFrame) |
{ |
#if DEBUG_PRINT |
printf("SynthGroupElement::SostenutoOn\n"); |
#endif |
if (!mSostenutoIsOn) { |
mMidiControlHandler->SetController(kMidiController_Sostenuto, 127); |
mSostenutoIsOn = true; |
mNoteList[kNoteState_Sostenutoed].TransferAllFrom(&mNoteList[kNoteState_Attacked], inFrame); |
} |
} |
void SynthGroupElement::SostenutoOff(UInt32 inFrame) |
{ |
#if DEBUG_PRINT |
printf("SynthGroupElement::SostenutoOff\n"); |
#endif |
if (mSostenutoIsOn) { |
mMidiControlHandler->SetController(kMidiController_Sostenuto, 0); |
mSostenutoIsOn = false; |
mNoteList[kNoteState_Attacked].TransferAllFrom(&mNoteList[kNoteState_Sostenutoed], inFrame); |
if (mSustainIsOn) |
mNoteList[kNoteState_ReleasedButSustained].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSostenutoed], inFrame); |
else |
mNoteList[kNoteState_Released].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSostenutoed], inFrame); |
} |
} |
void SynthGroupElement::SustainOn(UInt32 inFrame) |
{ |
#if DEBUG_PRINT |
// printf("SynthGroupElement::SustainOn\n"); |
#endif |
if (!mSustainIsOn) { |
mMidiControlHandler->SetController(kMidiController_Sustain, 127); |
mSustainIsOn = true; |
} |
} |
void SynthGroupElement::SustainOff(UInt32 inFrame) |
{ |
#if DEBUG_PRINT |
// printf("SynthGroupElement::SustainOff\n"); |
#endif |
if (mSustainIsOn) { |
mMidiControlHandler->SetController(kMidiController_Sustain, 0); |
mSustainIsOn = false; |
mNoteList[kNoteState_Released].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSustained], inFrame); |
} |
} |
void SynthGroupElement::AllNotesOff(UInt32 inFrame) |
{ |
#if DEBUG_PRINT |
printf("SynthGroupElement::AllNotesOff\n"); |
#endif |
SynthNote *note; |
for (UInt32 i=0 ; i<=kNoteState_Sostenutoed; ++i) |
{ |
UInt32 newState = (i == kNoteState_Attacked) ? |
kNoteState_Released : kNoteState_ReleasedButSostenutoed; |
note = mNoteList[i].mHead; |
while (note) |
{ |
SynthNote *nextNote = note->mNext; |
mNoteList[i].RemoveNote(note); |
note->Release(inFrame); |
mNoteList[newState].AddNote(note); |
note = nextNote; |
} |
} |
} |
void SynthGroupElement::AllSoundOff(UInt32 inFrame) |
{ |
#if DEBUG_PRINT |
printf("SynthGroupElement::AllSoundOff\n"); |
#endif |
SynthNote *note; |
for (UInt32 i=0 ; i<kNumberOfActiveNoteStates; ++i) |
{ |
note = mNoteList[i].mHead; |
while (note) |
{ |
SynthNote *nextNote = note->mNext; |
mNoteList[i].RemoveNote(note); |
note->FastRelease(inFrame); |
mNoteList[kNoteState_FastReleased].AddNote(note); |
GetAUInstrument()->DecNumActiveNotes(); |
note = nextNote; |
} |
} |
} |
void SynthGroupElement::ResetAllControllers(UInt32 inFrame) |
{ |
#if DEBUG_PRINT |
printf("SynthGroupElement::ResetAllControllers\n"); |
#endif |
mMidiControlHandler->Reset(); |
} |
OSStatus SynthGroupElement::Render(SInt64 inAbsoluteSampleFrame, UInt32 inNumberFrames, AUScope &outputs) |
{ |
// Avoid duplicate calls at same sample offset |
if (inAbsoluteSampleFrame != mCurrentAbsoluteFrame) |
{ |
mCurrentAbsoluteFrame = inAbsoluteSampleFrame; |
AudioBufferList* buffArray[16]; |
UInt32 numOutputs = outputs.GetNumberOfElements(); |
for (UInt32 outBus = 0; outBus < numOutputs && outBus < 16; ++outBus) |
{ |
buffArray[outBus] = &GetAudioUnit()->GetOutput(outBus)->GetBufferList(); |
} |
for (UInt32 i=0 ; i<kNumberOfSoundingNoteStates; ++i) |
{ |
SynthNote *note = mNoteList[i].mHead; |
while (note) |
{ |
#if DEBUG_PRINT_RENDER |
printf("SynthGroupElement::Render: state %d, note %p\n", i, note); |
#endif |
SynthNote *nextNote = note->mNext; |
OSStatus err = note->Render(inAbsoluteSampleFrame, inNumberFrames, buffArray, numOutputs); |
if (err) return err; |
note = nextNote; |
} |
} |
} |
return noErr; |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-02-19