diff options
Diffstat (limited to 'libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase')
11 files changed, 2815 insertions, 0 deletions
diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp new file mode 100644 index 0000000000..1ce8b308b4 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp @@ -0,0 +1,843 @@ +/* + 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; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h new file mode 100644 index 0000000000..3ad7e03a66 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h @@ -0,0 +1,269 @@ +/* + File: AUInstrumentBase.h + Abstract: Part of CoreAudio Utility Classes + 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. + +*/ +#ifndef __AUInstrumentBase__ +#define __AUInstrumentBase__ + +#include <vector> +#include <stdexcept> +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.h> +#include <libkern/OSAtomic.h> +#include "MusicDeviceBase.h" +#include "LockFreeFIFO.h" +#include "SynthEvent.h" +#include "SynthNote.h" +#include "SynthElement.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef LockFreeFIFOWithFree<SynthEvent> SynthEventQueue; + +class AUInstrumentBase : public MusicDeviceBase +{ +public: + AUInstrumentBase( + AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups = 16, + UInt32 numParts = 1); + virtual ~AUInstrumentBase(); + + virtual OSStatus Initialize(); + + /*! @method Parts */ + AUScope & Parts() { return mPartScope; } + + /*! @method GetPart */ + AUElement * GetPart( AudioUnitElement inElement) + { + return mPartScope.SafeGetElement(inElement); + } + + virtual AUScope * GetScopeExtended (AudioUnitScope inScope); + + virtual AUElement * CreateElement( AudioUnitScope inScope, + AudioUnitElement inElement); + + virtual void CreateExtendedElements(); + + virtual void Cleanup(); + + virtual OSStatus Reset( AudioUnitScope inScope, + AudioUnitElement inElement); + + virtual bool ValidFormat( AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inNewFormat); + + virtual bool StreamFormatWritable( AudioUnitScope scope, + AudioUnitElement element); + + virtual bool CanScheduleParameters() const { return false; } + + virtual OSStatus Render( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames); + + virtual OSStatus StartNote( MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID * outNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams); + + virtual OSStatus StopNote( MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame); + + virtual OSStatus RealTimeStartNote( SynthGroupElement *inGroup, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams); + + virtual OSStatus RealTimeStopNote( MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame); + + virtual OSStatus HandleControlChange( UInt8 inChannel, + UInt8 inController, + UInt8 inValue, + UInt32 inStartFrame); + + virtual OSStatus HandlePitchWheel( UInt8 inChannel, + UInt8 inPitch1, + UInt8 inPitch2, + UInt32 inStartFrame); + + virtual OSStatus HandleChannelPressure( UInt8 inChannel, + UInt8 inValue, + UInt32 inStartFrame); + + virtual OSStatus HandleProgramChange( UInt8 inChannel, + UInt8 inValue); + + virtual OSStatus HandlePolyPressure( UInt8 inChannel, + UInt8 inKey, + UInt8 inValue, + UInt32 inStartFrame); + + virtual OSStatus HandleResetAllControllers( UInt8 inChannel); + + virtual OSStatus HandleAllNotesOff( UInt8 inChannel); + + virtual OSStatus HandleAllSoundOff( UInt8 inChannel); + + SynthNote* GetNote(UInt32 inIndex) + { + if (!mNotes) + throw std::runtime_error("no notes"); + return (SynthNote*)((char*)mNotes + inIndex * mNoteSize); + } + + SynthNote* GetAFreeNote(UInt32 inFrame); + void AddFreeNote(SynthNote* inNote); + + friend class SynthGroupElement; +protected: + + UInt32 NextNoteID() { return OSAtomicIncrement32((int32_t *)&mNoteIDCounter); } + + + // call SetNotes in your Initialize() method to give the base class your note structures and to set the maximum + // number of active notes. inNoteData should be an array of size inMaxActiveNotes. + void SetNotes(UInt32 inNumNotes, UInt32 inMaxActiveNotes, SynthNote* inNotes, UInt32 inNoteSize); + + void PerformEvents( const AudioTimeStamp & inTimeStamp); + OSStatus SendPedalEvent(MusicDeviceGroupID inGroupID, UInt32 inEventType, UInt32 inOffsetSampleFrame); + virtual SynthNote* VoiceStealing(UInt32 inFrame, bool inKillIt); + UInt32 MaxActiveNotes() const { return mMaxActiveNotes; } + UInt32 NumActiveNotes() const { return mNumActiveNotes; } + void IncNumActiveNotes() { ++mNumActiveNotes; } + void DecNumActiveNotes() { --mNumActiveNotes; } + UInt32 CountActiveNotes(); + + SynthPartElement * GetPartElement (AudioUnitElement inPartElement); + + // this call throws if there's no assigned element for the group ID + virtual SynthGroupElement * GetElForGroupID (MusicDeviceGroupID inGroupID); + virtual SynthGroupElement * GetElForNoteID (NoteInstanceID inNoteID); + + SInt64 mAbsoluteSampleFrame; + + +private: + + SInt32 mNoteIDCounter; + + SynthEventQueue mEventQueue; + + UInt32 mNumNotes; + UInt32 mNumActiveNotes; + UInt32 mMaxActiveNotes; + SynthNote* mNotes; + SynthNoteList mFreeNotes; + UInt32 mNoteSize; + + AUScope mPartScope; + const UInt32 mInitNumPartEls; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class AUMonotimbralInstrumentBase : public AUInstrumentBase +{ +public: + AUMonotimbralInstrumentBase( + AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups = 16, + UInt32 numParts = 1); + + virtual OSStatus RealTimeStartNote( SynthGroupElement *inGroup, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// this is a work in progress! The mono-timbral one is finished though! +class AUMultitimbralInstrumentBase : public AUInstrumentBase +{ +public: + AUMultitimbralInstrumentBase( + AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups, + UInt32 numParts); + + virtual OSStatus GetPropertyInfo( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable); + + virtual OSStatus GetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData); + + virtual OSStatus SetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize); + +private: + +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h new file mode 100644 index 0000000000..ea6c4c26e1 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h @@ -0,0 +1,168 @@ +/* + File: LockFreeFIFO.h + Abstract: Part of CoreAudio Utility Classes + 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 <libkern/OSAtomic.h> + +template <class ITEM> +class LockFreeFIFOWithFree +{ + LockFreeFIFOWithFree(); // private, unimplemented. +public: + LockFreeFIFOWithFree(UInt32 inMaxSize) + : mReadIndex(0), mWriteIndex(0), mFreeIndex(0) + { + //assert(IsPowerOfTwo(inMaxSize)); + mItems = new ITEM[inMaxSize]; + mMask = inMaxSize - 1; + } + + ~LockFreeFIFOWithFree() + { + delete [] mItems; + } + + + void Reset() + { + FreeItems(); + mReadIndex = 0; + mWriteIndex = 0; + mFreeIndex = 0; + } + + ITEM* WriteItem() + { + //printf("WriteItem %d %d\n", mReadIndex, mWriteIndex); + FreeItems(); // free items on the write thread. + int32_t nextWriteIndex = (mWriteIndex + 1) & mMask; + if (nextWriteIndex == mFreeIndex) return NULL; + return &mItems[mWriteIndex]; + } + + ITEM* ReadItem() + { + //printf("ReadItem %d %d\n", mReadIndex, mWriteIndex); + if (mReadIndex == mWriteIndex) return NULL; + return &mItems[mReadIndex]; + } + void AdvanceWritePtr() { OSAtomicCompareAndSwap32(mWriteIndex, (mWriteIndex + 1) & mMask, &mWriteIndex); } + void AdvanceReadPtr() { OSAtomicCompareAndSwap32(mReadIndex, (mReadIndex + 1) & mMask, &mReadIndex); } +private: + ITEM* FreeItem() + { + if (mFreeIndex == mReadIndex) return NULL; + return &mItems[mFreeIndex]; + } + void AdvanceFreePtr() { OSAtomicCompareAndSwap32(mFreeIndex, (mFreeIndex + 1) & mMask, &mFreeIndex); } + + void FreeItems() + { + ITEM* item; + while ((item = FreeItem()) != NULL) + { + item->Free(); + AdvanceFreePtr(); + } + } + + volatile int32_t mReadIndex, mWriteIndex, mFreeIndex; + int32_t mMask; + ITEM *mItems; +}; + + + +// Same as above but no free. + +template <class ITEM> +class LockFreeFIFO +{ + LockFreeFIFO(); // private, unimplemented. +public: + LockFreeFIFO(UInt32 inMaxSize) + : mReadIndex(0), mWriteIndex(0) + { + //assert(IsPowerOfTwo(inMaxSize)); + mItems = new ITEM[inMaxSize]; + mMask = inMaxSize - 1; + } + + ~LockFreeFIFO() + { + delete [] mItems; + } + + void Reset() + { + mReadIndex = 0; + mWriteIndex = 0; + } + + ITEM* WriteItem() + { + int32_t nextWriteIndex = (mWriteIndex + 1) & mMask; + if (nextWriteIndex == mReadIndex) return NULL; + return &mItems[mWriteIndex]; + } + + ITEM* ReadItem() + { + if (mReadIndex == mWriteIndex) return NULL; + return &mItems[mReadIndex]; + } + + // the CompareAndSwap will always succeed. We use CompareAndSwap because it calls the PowerPC sync instruction, + // plus any processor bug workarounds for various CPUs. + void AdvanceWritePtr() { OSAtomicCompareAndSwap32(mWriteIndex, (mWriteIndex + 1) & mMask, &mWriteIndex); } + void AdvanceReadPtr() { OSAtomicCompareAndSwap32(mReadIndex, (mReadIndex + 1) & mMask, &mReadIndex); } + +private: + + volatile int32_t mReadIndex, mWriteIndex; + int32_t mMask; + ITEM *mItems; +}; + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/MIDIControlHandler.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/MIDIControlHandler.h new file mode 100644 index 0000000000..0f8003fcd5 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/MIDIControlHandler.h @@ -0,0 +1,92 @@ +/* + File: MIDIControlHandler.h + Abstract: Part of CoreAudio Utility Classes + 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. + +*/ +#ifndef __MIDICONTROLHANDLER_H__ +#define __MIDICONTROLHANDLER_H__ + +#include <CoreAudio/CoreAudio.h> + +/*! Abstract interface base class for classes which handle all incoming MIDI data */ + +class MIDIControlHandler +{ +public: + virtual ~MIDIControlHandler() {} + virtual void Reset() = 0; //! Restore all state to defaults + virtual bool SetProgramChange(UInt16 inProgram) = 0; + virtual bool SetPitchWheel(UInt16 inValue) = 0; + virtual bool SetChannelPressure(UInt8 inValue) = 0; + virtual bool SetPolyPressure(UInt8 inKey, UInt8 inValue) = 0; + virtual bool SetController(UInt8 inControllerNumber, UInt8 inValue) = 0; + virtual bool SetSysex(void *inSysexMsg) = 0; + + virtual float GetPitchBend() const = 0; + + /*! Default controller values. These represent MSB values unless indicated in the name */ + + enum + { + kDefault_Midpoint = 0x40, //! Used for all center-null-point controllers + kDefault_Volume = 100, + kDefault_Pan = kDefault_Midpoint, + kDefault_ModWheel = 0, + kDefault_Pitch = kDefault_Midpoint, + kDefault_Expression = 0x7f, + kDefault_ChannelPressure = 0, + kDefault_ReverbSend = 40, + kDefault_ChorusSend = 0, + + kDefault_RPN_LSB = 0x7f, + kDefault_RPN_MSB = 0x7f, + kDefault_PitchBendRange = 2, + kDefault_FineTuning = kDefault_Midpoint, + kDefault_CoarseTuning = kDefault_Midpoint, + kDefault_ModDepthRange = 0, + kDefault_ModDepthRangeLSB = kDefault_Midpoint + }; +}; + +#endif // __MIDICONTROLHANDLER_H__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp new file mode 100644 index 0000000000..fd329f84e8 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp @@ -0,0 +1,419 @@ +/* + File: SynthElement.cpp + Abstract: SynthElement.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 "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; +} + + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h new file mode 100644 index 0000000000..4ca3643106 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h @@ -0,0 +1,227 @@ +/* + File: SynthElement.h + Abstract: Part of CoreAudio Utility Classes + 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. + +*/ +#ifndef __SynthElement__ +#define __SynthElement__ + +#include <AudioUnit/AudioUnit.h> +#include "MusicDeviceBase.h" +#include "SynthNoteList.h" +#include "MIDIControlHandler.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +class AUInstrumentBase; + +class SynthElement : public AUElement +{ +public: + SynthElement(AUInstrumentBase *audioUnit, UInt32 inElement); + virtual ~SynthElement(); + + UInt32 GetIndex() const { return mIndex; } + + AUInstrumentBase* GetAUInstrument() { return (AUInstrumentBase*)GetAudioUnit(); } + +private: + UInt32 mIndex; +}; + +class MidiControls : public MIDIControlHandler +{ + enum { kMaxControls = 128 }; +public: + MidiControls(); + virtual ~MidiControls() {} + virtual void Reset(); + virtual bool SetProgramChange(UInt16 inProgram) { mProgramChange = inProgram; return true; } + virtual bool SetPitchWheel(UInt16 inValue) { + mPitchBend = inValue; + mFPitchBend = (float)(((SInt16)mPitchBend - 8192) / 8192.); + return true; + } + virtual bool SetChannelPressure(UInt8 inValue) { mMonoPressure = inValue; return true; } + virtual bool SetPolyPressure(UInt8 inKey, UInt8 inValue) { + mPolyPressure[inKey] = inValue; + return true; + } + virtual bool SetController(UInt8 inControllerNumber, UInt8 inValue) { + if (inControllerNumber < kMaxControls) { + mControls[inControllerNumber] = inValue; + return true; + } + return false; + } + virtual bool SetSysex(void *inSysexMsg) { return false; } + + virtual float GetPitchBend() const { return mFPitchBend * mFPitchBendDepth; } + + SInt16 GetHiResControl(UInt32 inIndex) const + { + return ((mControls[inIndex] & 127) << 7) | (mControls[inIndex + 32] & 127); + } + + float GetControl(UInt32 inIndex) const + { + if (inIndex < 32) { + return (float)(mControls[inIndex] + (mControls[inIndex + 32] / 127.)); + } else { + return (float)mControls[inIndex]; + } + } + + +private: + + UInt8 mControls[128]; + UInt8 mPolyPressure[128]; + UInt8 mMonoPressure; + UInt8 mProgramChange; + UInt16 mPitchBend; + UInt16 mActiveRPN; + UInt16 mActiveNRPN; + UInt16 mActiveRPValue; + UInt16 mActiveNRPValue; + + UInt16 mPitchBendDepth; + float mFPitchBendDepth; + float mFPitchBend; + + void SetHiResControl(UInt32 inIndex, UInt8 inMSB, UInt8 inLSB) + { + mControls[inIndex] = inMSB; + mControls[inIndex + 32] = inLSB; + } + +}; + + +class SynthGroupElement : public SynthElement +{ +public: + enum { + kUnassignedGroup = 0xFFFFFFFF + }; + + SynthGroupElement(AUInstrumentBase *audioUnit, UInt32 inElement, MIDIControlHandler *inHandler); + virtual ~SynthGroupElement(); + + virtual void NoteOn(SynthNote *note, SynthPartElement *part, NoteInstanceID inNoteID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams &inParams); + virtual void NoteOff(NoteInstanceID inNoteID, UInt32 inOffsetSampleFrame); + void SustainOn(UInt32 inFrame); + void SustainOff(UInt32 inFrame); + void SostenutoOn(UInt32 inFrame); + void SostenutoOff(UInt32 inFrame); + + void NoteEnded(SynthNote *inNote, UInt32 inFrame); + void NoteFastReleased(SynthNote *inNote); + + virtual bool ChannelMessage(UInt16 controlID, UInt16 controlValue); + virtual void AllNotesOff(UInt32 inFrame); + virtual void AllSoundOff(UInt32 inFrame); + void ResetAllControllers(UInt32 inFrame); + + SynthNote * GetNote(NoteInstanceID inNoteID, bool unreleasedOnly=false, UInt32 *outNoteState=NULL); + + void Reset(); + + virtual OSStatus Render(SInt64 inAbsoluteSampleFrame, UInt32 inNumberFrames, AUScope &outputs); + + float GetPitchBend() const { return mMidiControlHandler->GetPitchBend(); } + SInt64 GetCurrentAbsoluteFrame() const { return mCurrentAbsoluteFrame; } + + MusicDeviceGroupID GroupID () const { return mGroupID; } + virtual void SetGroupID (MusicDeviceGroupID inGroup); + + MIDIControlHandler * GetMIDIControlHandler() const { return mMidiControlHandler; } + +protected: + SInt64 mCurrentAbsoluteFrame; + SynthNoteList mNoteList[kNumberOfSoundingNoteStates]; + MIDIControlHandler *mMidiControlHandler; + +private: + friend class AUInstrumentBase; + friend class AUMonotimbralInstrumentBase; + friend class AUMultitimbralInstrumentBase; + + bool mSustainIsOn; + bool mSostenutoIsOn; + UInt32 mOutputBus; + MusicDeviceGroupID mGroupID; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct SynthKeyZone +{ + UInt8 mLoNote; + UInt8 mHiNote; + UInt8 mLoVelocity; + UInt8 mHiVelocity; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const UInt32 kUnlimitedPolyphony = 0xFFFFFFFF; + +class SynthPartElement : public SynthElement +{ +public: + SynthPartElement(AUInstrumentBase *audioUnit, UInt32 inElement); + + UInt32 GetGroupIndex() const { return mGroupIndex; } + bool InRange(Float32 inNote, Float32 inVelocity); + + UInt32 GetMaxPolyphony() const { return mMaxPolyphony; } + void SetMaxPolyphony(UInt32 inMaxPolyphony) { mMaxPolyphony = inMaxPolyphony; } + +private: + UInt32 mGroupIndex; + UInt32 mPatchIndex; + UInt32 mMaxPolyphony; + SynthKeyZone mKeyZone; +}; + +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h new file mode 100644 index 0000000000..9c27aee04f --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h @@ -0,0 +1,145 @@ +/* + File: SynthEvent.h + Abstract: Part of CoreAudio Utility Classes + 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. + +*/ +/* You can either fill in code here or remove this and create or add new files. */ + +#ifndef __SynthEvent__ +#define __SynthEvent__ + +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.h> +#include "MusicDeviceBase.h" +#include <stdexcept> + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +class SynthEvent +{ +public: + enum { + kEventType_NoteOn = 1, + kEventType_NoteOff = 2, + kEventType_SustainOn = 3, + kEventType_SustainOff = 4, + kEventType_SostenutoOn = 5, + kEventType_SostenutoOff = 6, + kEventType_AllNotesOff = 7, + kEventType_AllSoundOff = 8, + kEventType_ResetAllControllers = 9 + }; + + + SynthEvent() {} + ~SynthEvent() {} + + void Set( + UInt32 inEventType, + MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams* inNoteParams + ) + { + mEventType = inEventType; + mGroupID = inGroupID; + mNoteID = inNoteID; + mOffsetSampleFrame = inOffsetSampleFrame; + + if (inNoteParams) + { + UInt32 paramSize = offsetof(MusicDeviceNoteParams, mControls) + (inNoteParams->argCount-2)*sizeof(NoteParamsControlValue); + mNoteParams = inNoteParams->argCount > 3 + ? (MusicDeviceNoteParams*)malloc(paramSize) + : &mSmallNoteParams; + memcpy(mNoteParams, inNoteParams, paramSize); + } + else + mNoteParams = NULL; + } + + + void Free() + { + if (mNoteParams) + { + if (mNoteParams->argCount > 3) + free(mNoteParams); + mNoteParams = NULL; + } + } + + UInt32 GetEventType() const { return mEventType; } + MusicDeviceGroupID GetGroupID() const { return mGroupID; } + NoteInstanceID GetNoteID() const { return mNoteID; } + UInt32 GetOffsetSampleFrame() const { return mOffsetSampleFrame; } + + MusicDeviceNoteParams* GetParams() const { return mNoteParams; } + + UInt32 GetArgCount() const { return mNoteParams->argCount; } + UInt32 NumberParameters() const { return mNoteParams->argCount - 2; } + + Float32 GetNote() const { return mNoteParams->mPitch; } + Float32 GetVelocity() const { return mNoteParams->mVelocity; } + + NoteParamsControlValue GetParameter(UInt32 inIndex) const + { + if (inIndex >= NumberParameters()) + throw std::runtime_error("index out of range"); + return mNoteParams->mControls[inIndex]; + } + +private: + UInt32 mEventType; + MusicDeviceGroupID mGroupID; + NoteInstanceID mNoteID; + UInt32 mOffsetSampleFrame; + MusicDeviceNoteParams* mNoteParams; + MusicDeviceNoteParams mSmallNoteParams; // inline a small one to eliminate malloc for the simple case. +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp new file mode 100644 index 0000000000..96a24d33c2 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp @@ -0,0 +1,140 @@ +/* + File: SynthNote.cpp + Abstract: SynthNote.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 "SynthNote.h" +#include "SynthElement.h" +#include "AUInstrumentBase.h" + +bool SynthNote::AttackNote( + SynthPartElement * inPart, + SynthGroupElement * inGroup, + NoteInstanceID inNoteID, + UInt64 inAbsoluteSampleFrame, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) +{ +#if DEBUG_PRINT + printf("SynthNote::AttackNote %lu %lu abs frame %llu rel frame %lu\n", (UInt32)inGroup->GroupID(), (UInt32)inNoteID, inAbsoluteSampleFrame, inOffsetSampleFrame); +#endif + mPart = inPart; + mGroup = inGroup; + mNoteID = inNoteID; + + mAbsoluteStartFrame = inAbsoluteSampleFrame; + mRelativeStartFrame = inOffsetSampleFrame; + mRelativeReleaseFrame = -1; + mRelativeKillFrame = -1; + + mPitch = inParams.mPitch; + mVelocity = inParams.mVelocity; + + + return Attack(inParams); +} + + +void SynthNote::Reset() +{ + mPart = 0; + mGroup = 0; + mAbsoluteStartFrame = 0; + mRelativeStartFrame = 0; + mRelativeReleaseFrame = 0; + mRelativeKillFrame = 0; +} + +void SynthNote::Kill(UInt32 inFrame) +{ + mRelativeKillFrame = inFrame; +} + +void SynthNote::Release(UInt32 inFrame) +{ + mRelativeReleaseFrame = inFrame; +} + +void SynthNote::FastRelease(UInt32 inFrame) +{ + mRelativeReleaseFrame = inFrame; +} + +double SynthNote::TuningA() const +{ + return 440.0; +} + +double SynthNote::Frequency() +{ + return TuningA() * pow(2., (mPitch - 69. + GetPitchBend()) / 12.); +} + +double SynthNote::SampleRate() +{ + return GetAudioUnit()->GetOutput(0)->GetStreamFormat().mSampleRate; +} + +AUInstrumentBase* SynthNote::GetAudioUnit() const +{ + return (AUInstrumentBase*)mGroup->GetAudioUnit(); +} + +Float32 SynthNote::GetGlobalParameter(AudioUnitParameterID inParamID) const +{ + return mGroup->GetAudioUnit()->Globals()->GetParameter(inParamID); +} + +void SynthNote::NoteEnded(UInt32 inFrame) +{ + mGroup->NoteEnded(this, inFrame); + mNoteID = 0xFFFFFFFF; +} + +float SynthNote::GetPitchBend() const +{ + return mGroup->GetPitchBend(); +} + + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h new file mode 100644 index 0000000000..cec6377217 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h @@ -0,0 +1,187 @@ +/* + File: SynthNote.h + Abstract: Part of CoreAudio Utility Classes + 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. + +*/ +#ifndef __SynthNote__ +#define __SynthNote__ + +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.h> +#include "MusicDeviceBase.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +enum SynthNoteState { + kNoteState_Attacked = 0, + kNoteState_Sostenutoed = 1, + kNoteState_ReleasedButSostenutoed = 2, + kNoteState_ReleasedButSustained = 3, + kNoteState_Released = 4, + kNoteState_FastReleased = 5, + kNoteState_Free = 6, + kNumberOfActiveNoteStates = 5, + kNumberOfSoundingNoteStates = 6, + kNumberOfNoteStates = 7, + kNoteState_Unset = kNumberOfNoteStates +}; + +/* + This table describes the state transitions for SynthNotes + + EVENT CURRENT STATE NEW STATE + note on free attacked + note off attacked (and sustain on) released but sustained + note off attacked released + note off sostenutoed released but sostenutoed + sustain on -- no changes -- + sustain off released but sustained released + sostenuto on attacked sostenutoed + sostenuto off sostenutoed attacked + sostenuto off released but sostenutoed (and sustain on) released but sustained + sostenuto off released but sostenutoed released + end of note any state free + soft voice stealing any state fast released + hard voice stealing any state free + + soft voice stealing happens when there is a note on event and NumActiveNotes > MaxActiveNotes + hard voice stealing happens when there is a note on event and NumActiveNotes == NumNotes (no free notes) + voice stealing removes the quietest note in the highest numbered state that has sounding notes. +*/ + +class SynthGroupElement; +class SynthPartElement; +class AUInstrumentBase; + +struct SynthNote +{ + SynthNote() : + mPrev(0), mNext(0), mPart(0), mGroup(0), + mNoteID(0xffffffff), + mState(kNoteState_Unset), + mAbsoluteStartFrame(0), + mRelativeStartFrame(0), + mRelativeReleaseFrame(-1), + mRelativeKillFrame(-1), + mPitch(0.0f), + mVelocity(0.0f) + { + } + + virtual ~SynthNote() {} + + virtual void Reset(); + //! Returns true if active note resulted from this call, otherwise false + virtual bool AttackNote( + SynthPartElement * inPart, + SynthGroupElement * inGroup, + NoteInstanceID inNoteID, + UInt64 inAbsoluteSampleFrame, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams + ); + + virtual OSStatus Render(UInt64 inAbsoluteSampleFrame, UInt32 inNumFrames, AudioBufferList** inBufferList, UInt32 inOutBusCount) = 0; + //! Returns true if active note resulted from this call, otherwise false + virtual bool Attack(const MusicDeviceNoteParams &inParams) = 0; + virtual void Kill(UInt32 inFrame); // voice is being stolen. + virtual void Release(UInt32 inFrame); + virtual void FastRelease(UInt32 inFrame); + virtual Float32 Amplitude() = 0; // used for finding quietest note for voice stealing. + + virtual void NoteEnded(UInt32 inFrame); + + SynthGroupElement* GetGroup() const { return mGroup; } + SynthPartElement* GetPart() const { return mPart; } + + AUInstrumentBase* GetAudioUnit() const; + + Float32 GetGlobalParameter(AudioUnitParameterID inParamID) const; + + NoteInstanceID GetNoteID() const { return mNoteID; } + SynthNoteState GetState() const { return mState; } + UInt8 GetMidiKey() const { return (UInt8) mPitch; } + UInt8 GetMidiVelocity() const { return (UInt8) mVelocity; } + + Boolean IsSounding() const { return mState < kNumberOfSoundingNoteStates; } + Boolean IsActive() const { return mState < kNumberOfActiveNoteStates; } + UInt64 GetAbsoluteStartFrame() const { return mAbsoluteStartFrame; } + SInt32 GetRelativeStartFrame() const { return mRelativeStartFrame; } + SInt32 GetRelativeReleaseFrame() const { return mRelativeReleaseFrame; } + SInt32 GetRelativeKillFrame() const { return mRelativeKillFrame; } + + void ListRemove() { mPrev = mNext = 0; } // only use when lists will be reset. + + float GetPitchBend() const; + double TuningA() const; + + Float32 GetPitch() const { return mPitch; } // returns raw pitch from MusicDeviceNoteParams + virtual double Frequency(); // returns the frequency of note + pitch bend. + virtual double SampleRate(); + + // linked list pointers + SynthNote *mPrev; + SynthNote *mNext; + + friend class SynthGroupElement; + friend struct SynthNoteList; +protected: + void SetState(SynthNoteState inState) { mState = inState; } +private: + SynthPartElement* mPart; + SynthGroupElement* mGroup; + + NoteInstanceID mNoteID; + SynthNoteState mState; + UInt64 mAbsoluteStartFrame; + SInt32 mRelativeStartFrame; + SInt32 mRelativeReleaseFrame; + SInt32 mRelativeKillFrame; + + Float32 mPitch; + Float32 mVelocity; +}; + +#endif + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp new file mode 100644 index 0000000000..849abd266b --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp @@ -0,0 +1,93 @@ +/* + File: SynthNoteList.cpp + Abstract: SynthNoteList.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 "SynthNoteList.h" +#include <stdexcept> + +void SynthNoteList::SanityCheck() const +{ + if (mState >= kNoteState_Unset) { + throw std::runtime_error("SanityCheck: mState is bad"); + } + + if (mHead == NULL) { + if (mTail != NULL) + throw std::runtime_error("SanityCheck: mHead is NULL but not mTail"); + return; + } + if (mTail == NULL) { + throw std::runtime_error("SanityCheck: mTail is NULL but not mHead"); + } + + if (mHead->mPrev) { + throw std::runtime_error("SanityCheck: mHead has a mPrev"); + } + if (mTail->mNext) { + throw std::runtime_error("SanityCheck: mTail has a mNext"); + } + + SynthNote *note = mHead; + while (note) + { + if (note->mState != mState) + throw std::runtime_error("SanityCheck: note in wrong state"); + if (note->mNext) { + if (note->mNext->mPrev != note) + throw std::runtime_error("SanityCheck: bad link 1"); + } else { + if (mTail != note) + throw std::runtime_error("SanityCheck: note->mNext is nil, but mTail != note"); + } + if (note->mPrev) { + if (note->mPrev->mNext != note) + throw std::runtime_error("SanityCheck: bad link 2"); + } else { + if (mHead != note) + throw std::runtime_error("SanityCheck: note->mPrev is nil, but mHead != note"); + } + note = note->mNext; + } +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h new file mode 100644 index 0000000000..47a3593018 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h @@ -0,0 +1,232 @@ +/* + File: SynthNoteList.h + Abstract: Part of CoreAudio Utility Classes + 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. + +*/ +#ifndef __SynthNoteList__ +#define __SynthNoteList__ + +#include "SynthNote.h" + +#if DEBUG +#ifndef DEBUG_PRINT + #define DEBUG_PRINT 0 +#endif + #define USE_SANITY_CHECK 0 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct SynthNoteList +{ + SynthNoteList() : mState(kNoteState_Unset), mHead(0), mTail(0) {} + + bool NotEmpty() const { return mHead != NULL; } + bool IsEmpty() const { return mHead == NULL; } + void Empty() { +#if USE_SANITY_CHECK + SanityCheck(); +#endif + mHead = mTail = NULL; + } + + UInt32 Length() const { +#if USE_SANITY_CHECK + SanityCheck(); +#endif + UInt32 length = 0; + for (SynthNote* note = mHead; note; note = note->mNext) + length++; + return length; + }; + + void AddNote(SynthNote *inNote) + { +#if DEBUG_PRINT + printf("AddNote(inNote=%p) to state: %lu\n", inNote, mState); +#endif +#if USE_SANITY_CHECK + SanityCheck(); +#endif + inNote->SetState(mState); + inNote->mNext = mHead; + inNote->mPrev = NULL; + + if (mHead) { mHead->mPrev = inNote; mHead = inNote; } + else mHead = mTail = inNote; +#if USE_SANITY_CHECK + SanityCheck(); +#endif + } + + void RemoveNote(SynthNote *inNote) + { +#if DEBUG_PRINT + printf("RemoveNote(inNote=%p) from state: %lu\n", inNote, mState); +#endif +#if USE_SANITY_CHECK + SanityCheck(); +#endif + if (inNote->mPrev) inNote->mPrev->mNext = inNote->mNext; + else mHead = inNote->mNext; + + if (inNote->mNext) inNote->mNext->mPrev = inNote->mPrev; + else mTail = inNote->mPrev; + + inNote->mPrev = 0; + inNote->mNext = 0; +#if USE_SANITY_CHECK + SanityCheck(); +#endif + } + + void TransferAllFrom(SynthNoteList *inNoteList, UInt32 inFrame) + { +#if DEBUG_PRINT + printf("TransferAllFrom: from state %lu into state %lu\n", inNoteList->mState, mState); +#endif +#if USE_SANITY_CHECK + SanityCheck(); + inNoteList->SanityCheck(); +#endif + if (!inNoteList->mTail) return; + + if (mState == kNoteState_Released) + { + for (SynthNote* note = inNoteList->mHead; note; note = note->mNext) + { +#if DEBUG_PRINT + printf("TransferAllFrom: releasing note %p\n", note); +#endif + note->Release(inFrame); + note->SetState(mState); + } + } + else + { + for (SynthNote* note = inNoteList->mHead; note; note = note->mNext) + { + note->SetState(mState); + } + } + + inNoteList->mTail->mNext = mHead; + + if (mHead) mHead->mPrev = inNoteList->mTail; + else mTail = inNoteList->mTail; + + mHead = inNoteList->mHead; + + inNoteList->mHead = NULL; + inNoteList->mTail = NULL; +#if USE_SANITY_CHECK + SanityCheck(); + inNoteList->SanityCheck(); +#endif + } + + SynthNote* FindOldestNote() + { +#if DEBUG_PRINT + printf("FindOldestNote\n"); +#endif +#if USE_SANITY_CHECK + SanityCheck(); +#endif + UInt64 minStartFrame = -1; + SynthNote* oldestNote = NULL; + for (SynthNote* note = mHead; note; note = note->mNext) + { + if (note->mAbsoluteStartFrame < minStartFrame) + { + oldestNote = note; + minStartFrame = note->mAbsoluteStartFrame; + } + } + return oldestNote; + } + + SynthNote* FindMostQuietNote() + { +#if DEBUG_PRINT + printf("FindMostQuietNote\n"); +#endif + Float32 minAmplitude = 1e9f; + UInt64 minStartFrame = -1; + SynthNote* mostQuietNote = NULL; + for (SynthNote* note = mHead; note; note = note->mNext) + { + Float32 amp = note->Amplitude(); +#if DEBUG_PRINT + printf(" amp %g minAmplitude %g\n", amp, minAmplitude); +#endif + if (amp < minAmplitude) + { + mostQuietNote = note; + minAmplitude = amp; + minStartFrame = note->mAbsoluteStartFrame; + } + else if (amp == minAmplitude && note->mAbsoluteStartFrame < minStartFrame) + { + // use earliest start time as a tie breaker + mostQuietNote = note; + minStartFrame = note->mAbsoluteStartFrame; + } + } +#if USE_SANITY_CHECK + SanityCheck(); +#endif + return mostQuietNote; + } + + void SanityCheck() const; + + SynthNoteState mState; + SynthNote * mHead; + SynthNote * mTail; +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif |