844 lines
24 KiB
C++
844 lines
24 KiB
C++
/*
|
|
File: AUInstrumentBase.cpp
|
|
Abstract: AUInstrumentBase.h
|
|
Version: 1.1
|
|
|
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
|
Inc. ("Apple") in consideration of your agreement to the following
|
|
terms, and your use, installation, modification or redistribution of
|
|
this Apple software constitutes acceptance of these terms. If you do
|
|
not agree with these terms, please do not use, install, modify or
|
|
redistribute this Apple software.
|
|
|
|
In consideration of your agreement to abide by the following terms, and
|
|
subject to these terms, Apple grants you a personal, non-exclusive
|
|
license, under Apple's copyrights in this original Apple software (the
|
|
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
|
Software, with or without modifications, in source and/or binary forms;
|
|
provided that if you redistribute the Apple Software in its entirety and
|
|
without modifications, you must retain this notice and the following
|
|
text and disclaimers in all such redistributions of the Apple Software.
|
|
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
|
be used to endorse or promote products derived from the Apple Software
|
|
without specific prior written permission from Apple. Except as
|
|
expressly stated in this notice, no other rights or licenses, express or
|
|
implied, are granted by Apple herein, including but not limited to any
|
|
patent rights that may be infringed by your derivative works or by other
|
|
works in which the Apple Software may be incorporated.
|
|
|
|
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
|
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
|
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
|
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
|
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
|
|
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
|
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
|
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
|
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
|
|
|
*/
|
|
#include "AUInstrumentBase.h"
|
|
#include "AUMIDIDefs.h"
|
|
|
|
#if DEBUG
|
|
#define DEBUG_PRINT 0
|
|
#define DEBUG_PRINT_NOTE 0
|
|
#define DEBUG_PRINT_RENDER 0
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const UInt32 kEventQueueSize = 1024;
|
|
|
|
AUInstrumentBase::AUInstrumentBase(
|
|
AudioComponentInstance inInstance,
|
|
UInt32 numInputs,
|
|
UInt32 numOutputs,
|
|
UInt32 numGroups,
|
|
UInt32 numParts)
|
|
: MusicDeviceBase(inInstance, numInputs, numOutputs, numGroups),
|
|
mAbsoluteSampleFrame(0),
|
|
mEventQueue(kEventQueueSize),
|
|
mNumNotes(0),
|
|
mNumActiveNotes(0),
|
|
mMaxActiveNotes(0),
|
|
mNotes(0),
|
|
mNoteSize(0),
|
|
mInitNumPartEls(numParts)
|
|
{
|
|
#if DEBUG_PRINT
|
|
printf("new AUInstrumentBase\n");
|
|
#endif
|
|
mFreeNotes.mState = kNoteState_Free;
|
|
SetWantsRenderThreadID(true);
|
|
}
|
|
|
|
|
|
AUInstrumentBase::~AUInstrumentBase()
|
|
{
|
|
#if DEBUG_PRINT
|
|
printf("delete AUInstrumentBase\n");
|
|
#endif
|
|
}
|
|
|
|
AUElement * AUInstrumentBase::CreateElement(AudioUnitScope inScope, AudioUnitElement element)
|
|
{
|
|
switch (inScope)
|
|
{
|
|
case kAudioUnitScope_Group:
|
|
return new SynthGroupElement(this, element, new MidiControls);
|
|
case kAudioUnitScope_Part:
|
|
return new SynthPartElement (this, element);
|
|
}
|
|
return MusicDeviceBase::CreateElement(inScope, element);
|
|
}
|
|
|
|
void AUInstrumentBase::CreateExtendedElements()
|
|
{
|
|
Parts().Initialize(this, kAudioUnitScope_Part, mInitNumPartEls);
|
|
}
|
|
|
|
AUScope * AUInstrumentBase::GetScopeExtended (AudioUnitScope inScope)
|
|
{
|
|
if (inScope == kAudioUnitScope_Part)
|
|
return &mPartScope;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void AUInstrumentBase::SetNotes(UInt32 inNumNotes, UInt32 inMaxActiveNotes, SynthNote* inNotes, UInt32 inNoteDataSize)
|
|
{
|
|
#if DEBUG_PRINT_NOTE
|
|
printf("AUInstrumentBase::SetNotes %d %d %p %d\n", inNumNotes, inMaxActiveNotes, inNotes, inNoteDataSize);
|
|
#endif
|
|
mNumNotes = inNumNotes;
|
|
mMaxActiveNotes = inMaxActiveNotes;
|
|
mNoteSize = inNoteDataSize;
|
|
mNotes = inNotes;
|
|
|
|
for (UInt32 i=0; i<mNumNotes; ++i)
|
|
{
|
|
SynthNote *note = GetNote(i);
|
|
note->Reset();
|
|
mFreeNotes.AddNote(note);
|
|
}
|
|
}
|
|
|
|
UInt32 AUInstrumentBase::CountActiveNotes()
|
|
{
|
|
// debugging tool.
|
|
UInt32 sum = 0;
|
|
for (UInt32 i=0; i<mNumNotes; ++i)
|
|
{
|
|
SynthNote *note = GetNote(i);
|
|
if (note->GetState() <= kNoteState_Released)
|
|
sum++;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
void AUInstrumentBase::AddFreeNote(SynthNote* inNote)
|
|
{
|
|
// Fast-released notes are already considered inactive and have already decr'd the active count
|
|
if (inNote->GetState() < kNoteState_FastReleased) {
|
|
DecNumActiveNotes();
|
|
}
|
|
#if DEBUG_PRINT_NOTE
|
|
else {
|
|
printf("AUInstrumentBase::AddFreeNote: adding fast-released note %p\n", inNote);
|
|
}
|
|
printf("AUInstrumentBase::AddFreeNote (%p) mNumActiveNotes %lu\n", inNote, mNumActiveNotes);
|
|
#endif
|
|
mFreeNotes.AddNote(inNote);
|
|
}
|
|
|
|
OSStatus AUInstrumentBase::Initialize()
|
|
{
|
|
/*
|
|
TO DO:
|
|
Currently ValidFormat will check and validate that the num channels is not being
|
|
changed if the AU doesn't support the SupportedNumChannels property - which is correct
|
|
|
|
What needs to happen here is that IFF the AU does support this property, (ie, the AU
|
|
can be configured to have different num channels than its original configuration) then
|
|
the state of the AU at Initialization needs to be validated.
|
|
|
|
This is work still to be done - see AUEffectBase for the kind of logic that needs to be applied here
|
|
*/
|
|
|
|
// override to call SetNotes
|
|
|
|
mNoteIDCounter = 128; // reset this every time we initialise
|
|
mAbsoluteSampleFrame = 0;
|
|
return noErr;
|
|
}
|
|
|
|
void AUInstrumentBase::Cleanup()
|
|
{
|
|
mFreeNotes.Empty();
|
|
}
|
|
|
|
|
|
OSStatus AUInstrumentBase::Reset( AudioUnitScope inScope,
|
|
AudioUnitElement inElement)
|
|
{
|
|
#if DEBUG_PRINT
|
|
printf("AUInstrumentBase::Reset\n");
|
|
#endif
|
|
if (inScope == kAudioUnitScope_Global)
|
|
{
|
|
// kill all notes..
|
|
mFreeNotes.Empty();
|
|
for (UInt32 i=0; i<mNumNotes; ++i)
|
|
{
|
|
SynthNote *note = GetNote(i);
|
|
if (note->IsSounding())
|
|
note->Kill(0);
|
|
note->ListRemove();
|
|
mFreeNotes.AddNote(note);
|
|
}
|
|
mNumActiveNotes = 0;
|
|
mAbsoluteSampleFrame = 0;
|
|
|
|
// empty lists.
|
|
UInt32 numGroups = Groups().GetNumberOfElements();
|
|
for (UInt32 j = 0; j < numGroups; ++j)
|
|
{
|
|
SynthGroupElement *group = (SynthGroupElement*)Groups().GetElement(j);
|
|
group->Reset();
|
|
}
|
|
}
|
|
return MusicDeviceBase::Reset(inScope, inElement);
|
|
}
|
|
|
|
void AUInstrumentBase::PerformEvents(const AudioTimeStamp& inTimeStamp)
|
|
{
|
|
#if DEBUG_PRINT_RENDER
|
|
printf("AUInstrumentBase::PerformEvents\n");
|
|
#endif
|
|
SynthEvent *event;
|
|
SynthGroupElement *group;
|
|
|
|
while ((event = mEventQueue.ReadItem()) != NULL)
|
|
{
|
|
#if DEBUG_PRINT_RENDER
|
|
printf("event %08X %d\n", event, event->GetEventType());
|
|
#endif
|
|
switch(event->GetEventType())
|
|
{
|
|
case SynthEvent::kEventType_NoteOn :
|
|
RealTimeStartNote(GetElForGroupID (event->GetGroupID()), event->GetNoteID(),
|
|
event->GetOffsetSampleFrame(), *event->GetParams());
|
|
break;
|
|
case SynthEvent::kEventType_NoteOff :
|
|
RealTimeStopNote(event->GetGroupID(), event->GetNoteID(),
|
|
event->GetOffsetSampleFrame());
|
|
break;
|
|
case SynthEvent::kEventType_SustainOn :
|
|
group = GetElForGroupID (event->GetGroupID());
|
|
group->SustainOn(event->GetOffsetSampleFrame());
|
|
break;
|
|
case SynthEvent::kEventType_SustainOff :
|
|
group = GetElForGroupID (event->GetGroupID());
|
|
group->SustainOff(event->GetOffsetSampleFrame());
|
|
break;
|
|
case SynthEvent::kEventType_SostenutoOn :
|
|
group = GetElForGroupID (event->GetGroupID());
|
|
group->SostenutoOn(event->GetOffsetSampleFrame());
|
|
break;
|
|
case SynthEvent::kEventType_SostenutoOff :
|
|
group = GetElForGroupID (event->GetGroupID());
|
|
group->SostenutoOff(event->GetOffsetSampleFrame());
|
|
break;
|
|
case SynthEvent::kEventType_AllNotesOff :
|
|
group = GetElForGroupID (event->GetGroupID());
|
|
group->AllNotesOff(event->GetOffsetSampleFrame());
|
|
break;
|
|
case SynthEvent::kEventType_AllSoundOff :
|
|
group = GetElForGroupID (event->GetGroupID());
|
|
group->AllSoundOff(event->GetOffsetSampleFrame());
|
|
break;
|
|
case SynthEvent::kEventType_ResetAllControllers :
|
|
group = GetElForGroupID (event->GetGroupID());
|
|
group->ResetAllControllers(event->GetOffsetSampleFrame());
|
|
break;
|
|
}
|
|
|
|
mEventQueue.AdvanceReadPtr();
|
|
}
|
|
}
|
|
|
|
|
|
OSStatus AUInstrumentBase::Render( AudioUnitRenderActionFlags & ioActionFlags,
|
|
const AudioTimeStamp & inTimeStamp,
|
|
UInt32 inNumberFrames)
|
|
{
|
|
PerformEvents(inTimeStamp);
|
|
|
|
AUScope &outputs = Outputs();
|
|
UInt32 numOutputs = outputs.GetNumberOfElements();
|
|
for (UInt32 j = 0; j < numOutputs; ++j)
|
|
{
|
|
GetOutput(j)->PrepareBuffer(inNumberFrames); // AUBase::DoRenderBus() only does this for the first output element
|
|
AudioBufferList& bufferList = GetOutput(j)->GetBufferList();
|
|
for (UInt32 k = 0; k < bufferList.mNumberBuffers; ++k)
|
|
{
|
|
memset(bufferList.mBuffers[k].mData, 0, bufferList.mBuffers[k].mDataByteSize);
|
|
}
|
|
}
|
|
UInt32 numGroups = Groups().GetNumberOfElements();
|
|
for (UInt32 j = 0; j < numGroups; ++j)
|
|
{
|
|
SynthGroupElement *group = (SynthGroupElement*)Groups().GetElement(j);
|
|
OSStatus err = group->Render((SInt64)inTimeStamp.mSampleTime, inNumberFrames, outputs);
|
|
if (err) return err;
|
|
}
|
|
mAbsoluteSampleFrame += inNumberFrames;
|
|
return noErr;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// AUInstrumentBase::ValidFormat
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
bool AUInstrumentBase::ValidFormat( AudioUnitScope inScope,
|
|
AudioUnitElement inElement,
|
|
const CAStreamBasicDescription & inNewFormat)
|
|
{
|
|
// if the AU supports this, then we should just let this go through to the Init call
|
|
if (SupportedNumChannels (NULL))
|
|
return MusicDeviceBase::ValidFormat(inScope, inElement, inNewFormat);
|
|
|
|
bool isGood = MusicDeviceBase::ValidFormat (inScope, inElement, inNewFormat);
|
|
if (!isGood) return false;
|
|
|
|
// if we get to here, then the basic criteria is that the
|
|
// num channels cannot change on an existing bus
|
|
AUIOElement *el = GetIOElement (inScope, inElement);
|
|
return (el->GetStreamFormat().NumberChannels() == inNewFormat.NumberChannels());
|
|
}
|
|
|
|
|
|
bool AUInstrumentBase::StreamFormatWritable( AudioUnitScope scope,
|
|
AudioUnitElement element)
|
|
{
|
|
return IsInitialized() ? false : true;
|
|
}
|
|
|
|
OSStatus AUInstrumentBase::RealTimeStartNote( SynthGroupElement *inGroup,
|
|
NoteInstanceID inNoteInstanceID,
|
|
UInt32 inOffsetSampleFrame,
|
|
const MusicDeviceNoteParams &inParams)
|
|
{
|
|
return noErr;
|
|
}
|
|
|
|
SynthPartElement * AUInstrumentBase::GetPartElement (AudioUnitElement inPartElement)
|
|
{
|
|
AUScope & parts = Parts();
|
|
unsigned int numEls = parts.GetNumberOfElements();
|
|
for (unsigned int i = 0; i < numEls; ++i) {
|
|
SynthPartElement* el = reinterpret_cast<SynthPartElement*>(parts.GetElement(i));
|
|
if (el->GetIndex() == inPartElement) {
|
|
return el;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
SynthGroupElement * AUInstrumentBase::GetElForGroupID (MusicDeviceGroupID inGroupID)
|
|
{
|
|
AUScope & groups = Groups();
|
|
unsigned int numEls = groups.GetNumberOfElements();
|
|
SynthGroupElement* unassignedEl = NULL;
|
|
|
|
for (unsigned int i = 0; i < numEls; ++i) {
|
|
SynthGroupElement* el = reinterpret_cast<SynthGroupElement*>(groups.GetElement(i));
|
|
if (el->GroupID() == inGroupID)
|
|
return el;
|
|
if (el->GroupID() == SynthGroupElement::kUnassignedGroup) {
|
|
unassignedEl = el;
|
|
break; // we fill this up from the start of the group scope vector
|
|
}
|
|
}
|
|
if (unassignedEl) {
|
|
unassignedEl->SetGroupID(inGroupID);
|
|
return unassignedEl;
|
|
}
|
|
throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement);
|
|
}
|
|
|
|
OSStatus AUInstrumentBase::RealTimeStopNote(
|
|
MusicDeviceGroupID inGroupID,
|
|
NoteInstanceID inNoteInstanceID,
|
|
UInt32 inOffsetSampleFrame)
|
|
{
|
|
#if DEBUG_PRINT
|
|
printf("AUInstrumentBase::RealTimeStopNote ch %d id %d\n", inGroupID, inNoteInstanceID);
|
|
#endif
|
|
|
|
SynthGroupElement *gp = (inGroupID == kMusicNoteEvent_Unused
|
|
? GetElForNoteID (inNoteInstanceID)
|
|
: GetElForGroupID(inGroupID));
|
|
if (gp)
|
|
{
|
|
gp->NoteOff (inNoteInstanceID, inOffsetSampleFrame);
|
|
}
|
|
|
|
return noErr;
|
|
}
|
|
|
|
SynthGroupElement * AUInstrumentBase::GetElForNoteID (NoteInstanceID inNoteID)
|
|
{
|
|
#if DEBUG_PRINT
|
|
printf("GetElForNoteID id %u\n", inNoteID);
|
|
#endif
|
|
AUScope & groups = Groups();
|
|
unsigned int numEls = groups.GetNumberOfElements();
|
|
|
|
for (unsigned int i = 0; i < numEls; ++i) {
|
|
SynthGroupElement* el = reinterpret_cast<SynthGroupElement*>(groups.GetElement(i));
|
|
if (el->GetNote(inNoteID) != NULL) // searches for any note state
|
|
return el;
|
|
}
|
|
throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement);
|
|
}
|
|
|
|
OSStatus AUInstrumentBase::StartNote( MusicDeviceInstrumentID inInstrument,
|
|
MusicDeviceGroupID inGroupID,
|
|
NoteInstanceID * outNoteInstanceID,
|
|
UInt32 inOffsetSampleFrame,
|
|
const MusicDeviceNoteParams &inParams)
|
|
{
|
|
OSStatus err = noErr;
|
|
|
|
NoteInstanceID noteID;
|
|
if (outNoteInstanceID) {
|
|
noteID = NextNoteID();
|
|
*outNoteInstanceID = noteID;
|
|
} else
|
|
noteID = (UInt32)inParams.mPitch;
|
|
|
|
#if DEBUG_PRINT
|
|
printf("AUInstrumentBase::StartNote ch %u, key %u, offset %u\n", inGroupID, (unsigned) inParams.mPitch, inOffsetSampleFrame);
|
|
#endif
|
|
|
|
if (InRenderThread ())
|
|
{
|
|
err = RealTimeStartNote(
|
|
GetElForGroupID(inGroupID),
|
|
noteID,
|
|
inOffsetSampleFrame,
|
|
inParams);
|
|
}
|
|
else
|
|
{
|
|
SynthEvent *event = mEventQueue.WriteItem();
|
|
if (!event) return -1; // queue full
|
|
|
|
event->Set(
|
|
SynthEvent::kEventType_NoteOn,
|
|
inGroupID,
|
|
noteID,
|
|
inOffsetSampleFrame,
|
|
&inParams
|
|
);
|
|
|
|
mEventQueue.AdvanceWritePtr();
|
|
}
|
|
return err;
|
|
}
|
|
|
|
OSStatus AUInstrumentBase::StopNote( MusicDeviceGroupID inGroupID,
|
|
NoteInstanceID inNoteInstanceID,
|
|
UInt32 inOffsetSampleFrame)
|
|
{
|
|
#if DEBUG_PRINT
|
|
printf("AUInstrumentBase::StopNote ch %u, id %u, offset %u\n", (unsigned)inGroupID, (unsigned)inNoteInstanceID, inOffsetSampleFrame);
|
|
#endif
|
|
OSStatus err = noErr;
|
|
|
|
if (InRenderThread ())
|
|
{
|
|
err = RealTimeStopNote(
|
|
inGroupID,
|
|
inNoteInstanceID,
|
|
inOffsetSampleFrame);
|
|
}
|
|
else
|
|
{
|
|
SynthEvent *event = mEventQueue.WriteItem();
|
|
if (!event) return -1; // queue full
|
|
|
|
event->Set(
|
|
SynthEvent::kEventType_NoteOff,
|
|
inGroupID,
|
|
inNoteInstanceID,
|
|
inOffsetSampleFrame,
|
|
NULL
|
|
);
|
|
|
|
mEventQueue.AdvanceWritePtr();
|
|
}
|
|
return err;
|
|
}
|
|
|
|
OSStatus AUInstrumentBase::SendPedalEvent(MusicDeviceGroupID inGroupID, UInt32 inEventType, UInt32 inOffsetSampleFrame)
|
|
{
|
|
|
|
if (InRenderThread ())
|
|
{
|
|
SynthGroupElement *group = GetElForGroupID(inGroupID);
|
|
if (!group)
|
|
return kAudioUnitErr_InvalidElement;
|
|
|
|
switch (inEventType)
|
|
{
|
|
case SynthEvent::kEventType_SustainOn :
|
|
group->SustainOn(inOffsetSampleFrame);
|
|
break;
|
|
case SynthEvent::kEventType_SustainOff :
|
|
group->SustainOff(inOffsetSampleFrame);
|
|
break;
|
|
case SynthEvent::kEventType_SostenutoOn :
|
|
group->SostenutoOn(inOffsetSampleFrame);
|
|
break;
|
|
case SynthEvent::kEventType_SostenutoOff :
|
|
group->SostenutoOff(inOffsetSampleFrame);
|
|
break;
|
|
case SynthEvent::kEventType_AllNotesOff :
|
|
group->AllNotesOff(inOffsetSampleFrame);
|
|
mNumActiveNotes = CountActiveNotes();
|
|
break;
|
|
case SynthEvent::kEventType_AllSoundOff :
|
|
group->AllSoundOff(inOffsetSampleFrame);
|
|
mNumActiveNotes = CountActiveNotes();
|
|
break;
|
|
case SynthEvent::kEventType_ResetAllControllers :
|
|
group->ResetAllControllers(inOffsetSampleFrame);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SynthEvent *event = mEventQueue.WriteItem();
|
|
if (!event) return -1; // queue full
|
|
|
|
event->Set(inEventType, inGroupID, 0, 0, NULL);
|
|
|
|
mEventQueue.AdvanceWritePtr();
|
|
}
|
|
return noErr;
|
|
}
|
|
|
|
OSStatus AUInstrumentBase::HandleControlChange( UInt8 inChannel,
|
|
UInt8 inController,
|
|
UInt8 inValue,
|
|
UInt32 inStartFrame)
|
|
{
|
|
#if DEBUG_PRINT
|
|
printf("AUInstrumentBase::HandleControlChange ch %u ctlr: %u val: %u frm: %u\n", inChannel, inController, inValue, inStartFrame);
|
|
#endif
|
|
SynthGroupElement *gp = GetElForGroupID(inChannel);
|
|
if (gp)
|
|
{
|
|
gp->ChannelMessage(inController, inValue);
|
|
}
|
|
else
|
|
return kAudioUnitErr_InvalidElement;
|
|
switch (inController)
|
|
{
|
|
case kMidiController_Sustain :
|
|
if (inValue >= 64)
|
|
SendPedalEvent(inChannel, SynthEvent::kEventType_SustainOn, inStartFrame);
|
|
else
|
|
SendPedalEvent(inChannel, SynthEvent::kEventType_SustainOff, inStartFrame);
|
|
break;
|
|
case kMidiController_Sostenuto :
|
|
if (inValue >= 64)
|
|
SendPedalEvent(inChannel, SynthEvent::kEventType_SostenutoOn, inStartFrame);
|
|
else
|
|
SendPedalEvent(inChannel, SynthEvent::kEventType_SostenutoOff, inStartFrame);
|
|
break;
|
|
case kMidiController_OmniModeOff:
|
|
case kMidiController_OmniModeOn:
|
|
case kMidiController_MonoModeOn:
|
|
case kMidiController_MonoModeOff:
|
|
HandleAllSoundOff(inChannel);
|
|
break;
|
|
}
|
|
return noErr;
|
|
}
|
|
|
|
OSStatus AUInstrumentBase::HandlePitchWheel( UInt8 inChannel,
|
|
UInt8 inPitch1, // LSB
|
|
UInt8 inPitch2, // MSB
|
|
UInt32 inStartFrame)
|
|
{
|
|
SynthGroupElement *gp = GetElForGroupID(inChannel);
|
|
if (gp)
|
|
{
|
|
gp->ChannelMessage(kMidiMessage_PitchWheel, (inPitch2 << 7) | inPitch1);
|
|
return noErr;
|
|
}
|
|
else
|
|
return kAudioUnitErr_InvalidElement;
|
|
}
|
|
|
|
|
|
OSStatus AUInstrumentBase::HandleChannelPressure(UInt8 inChannel,
|
|
UInt8 inValue,
|
|
UInt32 inStartFrame)
|
|
{
|
|
SynthGroupElement *gp = GetElForGroupID(inChannel);
|
|
if (gp)
|
|
{
|
|
gp->ChannelMessage(kMidiMessage_ChannelPressure, inValue);
|
|
return noErr;
|
|
}
|
|
else
|
|
return kAudioUnitErr_InvalidElement;
|
|
}
|
|
|
|
|
|
OSStatus AUInstrumentBase::HandleProgramChange( UInt8 inChannel,
|
|
UInt8 inValue)
|
|
{
|
|
#if DEBUG_PRINT
|
|
printf("AUInstrumentBase::HandleProgramChange %u %u\n", inChannel, inValue);
|
|
#endif
|
|
SynthGroupElement *gp = GetElForGroupID(inChannel);
|
|
if (gp)
|
|
{
|
|
gp->ChannelMessage(kMidiMessage_ProgramChange, inValue);
|
|
return noErr;
|
|
}
|
|
else
|
|
return kAudioUnitErr_InvalidElement;
|
|
}
|
|
|
|
|
|
OSStatus AUInstrumentBase::HandlePolyPressure( UInt8 inChannel,
|
|
UInt8 inKey,
|
|
UInt8 inValue,
|
|
UInt32 inStartFrame)
|
|
{
|
|
SynthGroupElement *gp = GetElForGroupID(inChannel);
|
|
if (gp)
|
|
{
|
|
// Combine key and value into single argument. UGLY!
|
|
gp->ChannelMessage(kMidiMessage_PolyPressure, (inKey << 7) | inValue);
|
|
return noErr;
|
|
}
|
|
else
|
|
return kAudioUnitErr_InvalidElement;
|
|
}
|
|
|
|
|
|
OSStatus AUInstrumentBase::HandleResetAllControllers( UInt8 inChannel)
|
|
{
|
|
return SendPedalEvent (inChannel, SynthEvent::kEventType_ResetAllControllers, 0);
|
|
}
|
|
|
|
|
|
OSStatus AUInstrumentBase::HandleAllNotesOff( UInt8 inChannel)
|
|
{
|
|
return SendPedalEvent (inChannel, SynthEvent::kEventType_AllNotesOff, 0);
|
|
}
|
|
|
|
|
|
OSStatus AUInstrumentBase::HandleAllSoundOff( UInt8 inChannel)
|
|
{
|
|
return SendPedalEvent (inChannel, SynthEvent::kEventType_AllSoundOff, 0);
|
|
}
|
|
|
|
SynthNote* AUInstrumentBase::GetAFreeNote(UInt32 inFrame)
|
|
{
|
|
#if DEBUG_PRINT_NOTE
|
|
printf("AUInstrumentBase::GetAFreeNote: %lu available\n", mFreeNotes.Length());
|
|
#endif
|
|
SynthNote *note = mFreeNotes.mHead;
|
|
if (note)
|
|
{
|
|
mFreeNotes.RemoveNote(note);
|
|
return note;
|
|
}
|
|
|
|
return VoiceStealing(inFrame, true);
|
|
}
|
|
|
|
SynthNote* AUInstrumentBase::VoiceStealing(UInt32 inFrame, bool inKillIt)
|
|
{
|
|
|
|
#if DEBUG_PRINT_NOTE
|
|
printf("AUInstrumentBase::VoiceStealing\n");
|
|
#endif
|
|
// free list was empty so we need to kill a note.
|
|
UInt32 startState = inKillIt ? kNoteState_FastReleased : kNoteState_Released;
|
|
for (UInt32 i = startState; i <= startState; --i)
|
|
{
|
|
#if DEBUG_PRINT_NOTE
|
|
printf(" checking state %d...\n", i);
|
|
#endif
|
|
UInt32 numGroups = Groups().GetNumberOfElements();
|
|
for (UInt32 j = 0; j < numGroups; ++j)
|
|
{
|
|
SynthGroupElement *group = (SynthGroupElement*)Groups().GetElement(j);
|
|
#if DEBUG_PRINT_NOTE
|
|
printf("\tsteal group %d size %d\n", j, group->mNoteList[i].Length());
|
|
#endif
|
|
if (group->mNoteList[i].NotEmpty()) {
|
|
#if DEBUG_PRINT_NOTE
|
|
printf("\t-- not empty\n");
|
|
#endif
|
|
SynthNote *note = group->mNoteList[i].FindMostQuietNote();
|
|
if (inKillIt) {
|
|
#if DEBUG_PRINT_NOTE
|
|
printf("\t--=== KILL ===---\n");
|
|
#endif
|
|
note->Kill(inFrame);
|
|
group->mNoteList[i].RemoveNote(note);
|
|
if (i != kNoteState_FastReleased)
|
|
DecNumActiveNotes();
|
|
return note;
|
|
} else {
|
|
#if DEBUG_PRINT_NOTE
|
|
printf("\t--=== FAST RELEASE ===---\n");
|
|
#endif
|
|
group->mNoteList[i].RemoveNote(note);
|
|
note->FastRelease(inFrame);
|
|
group->mNoteList[kNoteState_FastReleased].AddNote(note);
|
|
DecNumActiveNotes(); // kNoteState_FastReleased counts as inactive for voice stealing purposes.
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if DEBUG_PRINT_NOTE
|
|
printf("no notes to steal????\n");
|
|
#endif
|
|
return NULL; // It should be impossible to get here. It means there were no notes to kill in any state.
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
AUMonotimbralInstrumentBase::AUMonotimbralInstrumentBase(
|
|
AudioComponentInstance inInstance,
|
|
UInt32 numInputs,
|
|
UInt32 numOutputs,
|
|
UInt32 numGroups,
|
|
UInt32 numParts)
|
|
: AUInstrumentBase(inInstance, numInputs, numOutputs, numGroups, numParts)
|
|
{
|
|
}
|
|
|
|
OSStatus AUMonotimbralInstrumentBase::RealTimeStartNote(
|
|
SynthGroupElement *inGroup,
|
|
NoteInstanceID inNoteInstanceID,
|
|
UInt32 inOffsetSampleFrame,
|
|
const MusicDeviceNoteParams &inParams)
|
|
{
|
|
#if DEBUG_PRINT_RENDER
|
|
printf("AUMonotimbralInstrumentBase::RealTimeStartNote %d\n", inNoteInstanceID);
|
|
#endif
|
|
|
|
if (NumActiveNotes() + 1 > MaxActiveNotes())
|
|
{
|
|
VoiceStealing(inOffsetSampleFrame, false);
|
|
}
|
|
SynthNote *note = GetAFreeNote(inOffsetSampleFrame);
|
|
if (!note) return -1;
|
|
|
|
SynthPartElement *part = GetPartElement (0); // Only one part for monotimbral
|
|
|
|
IncNumActiveNotes();
|
|
inGroup->NoteOn(note, part, inNoteInstanceID, inOffsetSampleFrame, inParams);
|
|
|
|
return noErr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
OSStatus AUMultitimbralInstrumentBase::GetPropertyInfo(AudioUnitPropertyID inID,
|
|
AudioUnitScope inScope,
|
|
AudioUnitElement inElement,
|
|
UInt32 & outDataSize,
|
|
Boolean & outWritable)
|
|
{
|
|
OSStatus result = noErr;
|
|
|
|
switch (inID)
|
|
{
|
|
#if !TARGET_OS_IPHONE
|
|
case kMusicDeviceProperty_PartGroup:
|
|
if (inScope != kAudioUnitScope_Part) return kAudioUnitErr_InvalidScope;
|
|
outDataSize = sizeof(UInt32);
|
|
outWritable = true;
|
|
break;
|
|
#endif
|
|
default:
|
|
result = AUInstrumentBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
OSStatus AUMultitimbralInstrumentBase::GetProperty( AudioUnitPropertyID inID,
|
|
AudioUnitScope inScope,
|
|
AudioUnitElement inElement,
|
|
void * outData)
|
|
{
|
|
OSStatus result = noErr;
|
|
|
|
switch (inID)
|
|
{
|
|
#if !TARGET_OS_IPHONE
|
|
case kMusicDeviceProperty_PartGroup:
|
|
if (inScope != kAudioUnitScope_Group) return kAudioUnitErr_InvalidScope;
|
|
// ??
|
|
return -1; //unimpl
|
|
break;
|
|
#endif
|
|
default:
|
|
result = AUInstrumentBase::GetProperty (inID, inScope, inElement, outData);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
OSStatus AUMultitimbralInstrumentBase::SetProperty( AudioUnitPropertyID inID,
|
|
AudioUnitScope inScope,
|
|
AudioUnitElement inElement,
|
|
const void * inData,
|
|
UInt32 inDataSize)
|
|
{
|
|
OSStatus result = noErr;
|
|
|
|
switch (inID)
|
|
{
|
|
#if !TARGET_OS_IPHONE
|
|
case kMusicDeviceProperty_PartGroup:
|
|
if (inScope != kAudioUnitScope_Group) return kAudioUnitErr_InvalidScope;
|
|
// ??
|
|
return -1; //unimpl
|
|
break;
|
|
#endif
|
|
default:
|
|
result = MusicDeviceBase::SetProperty (inID, inScope, inElement, inData, inDataSize);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|