diff options
Diffstat (limited to 'distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase')
10 files changed, 2437 insertions, 0 deletions
diff --git a/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp new file mode 100755 index 00000000..f8bfe39e --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp @@ -0,0 +1,760 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#include "AUInstrumentBase.h" + +#if DEBUG + #define DEBUG_PRINT 0 + #define DEBUG_PRINT_RENDER 0 +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const UInt32 kEventQueueSize = 1024; + +AUInstrumentBase::AUInstrumentBase( + ComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups, + UInt32 numParts) + : MusicDeviceBase(inInstance, numInputs, numOutputs, numGroups, numParts), + mAbsoluteSampleFrame(0), + mEventQueue(kEventQueueSize), + mNumNotes(0), + mNumActiveNotes(0), + mMaxActiveNotes(0), + mNotes(0), + mNoteSize(0) +{ +#if DEBUG_PRINT + printf("new AUInstrumentBase\n"); +#endif + mFreeNotes.mState = kNoteState_Free; +} + + +AUInstrumentBase::~AUInstrumentBase() +{ +#if DEBUG_PRINT + printf("delete AUInstrumentBase\n"); +#endif +} + +AUElement* AUInstrumentBase::CreateElement( AudioUnitScope scope, + AudioUnitElement element) +{ +#if DEBUG_PRINT + printf("AUInstrumentBase::CreateElement %d %d\n", scope, element); +#endif + switch (scope) + { + case kAudioUnitScope_Group : + return new SynthGroupElement(this, element); + case kAudioUnitScope_Part : + return new SynthPartElement(this, element); + default : + return AUBase::CreateElement(scope, element); + } +} + + +void AUInstrumentBase::SetNotes(UInt32 inNumNotes, UInt32 inMaxActiveNotes, SynthNote* inNotes, UInt32 inNoteDataSize) +{ +#if DEBUG_PRINT + printf("AUInstrumentBase::SetNotes %d %d %08X %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->mState <= kNoteState_Released) + sum++; + } + return sum; +} + +void AUInstrumentBase::AddFreeNote(SynthNote* inNote) +{ + if (inNote->mState != kNoteState_FastReleased) + DecNumActiveNotes(); +#if DEBUG_PRINT + printf("AUInstrumentBase::AddFreeNote mNumActiveNotes %lu\n", 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() +{ +} + + +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); + + UInt32 numOutputs = Outputs().GetNumberOfElements(); + for (UInt32 j = 0; j < numOutputs; ++j) + { + 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(inNumberFrames); + 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; +} + +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 %d %d\n", inGroupID, inNoteInstanceID); +#endif + + SynthGroupElement *gp = (inGroupID == kMusicNoteEvent_Unused + ? GetElForNoteID (inNoteInstanceID) + : GetElForGroupID(inGroupID)); + + gp->NoteOff (inNoteInstanceID, inOffsetSampleFrame); + + return noErr; +} + +SynthGroupElement * AUInstrumentBase::GetElForNoteID (NoteInstanceID inNoteID) +{ +#if DEBUG_PRINT + printf("GetElForNoteID id %d\n", (int)inNoteID); +#endif + if (!mNotes) throw std::runtime_error("no notes"); + + for (unsigned int i = 0; i < mNumNotes; ++i) { + if (inNoteID == mNotes[i].GetNoteID()) { + return mNotes[i].GetGroup(); + } + } + throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement); +} + +OSStatus AUInstrumentBase::StartNote( MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID * outNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) +{ +#if DEBUG_PRINT + printf("AUInstrumentBase::StartNote %d\n", inGroupID); +#endif + OSStatus err = noErr; + + NoteInstanceID noteID; + if (outNoteInstanceID) { + noteID = NextNoteID(); + *outNoteInstanceID = noteID; + } else + noteID = (UInt32)inParams.mPitch; + + 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 %d %d\n", inGroupID, inNoteInstanceID); +#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); + 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) +{ + GetControls(inChannel)->mControls[inController] = inValue; + 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; + } + return noErr; +} + +OSStatus AUInstrumentBase::HandlePitchWheel( UInt8 inChannel, + UInt8 inPitch1, + UInt8 inPitch2, + UInt32 inStartFrame) +{ + MidiControls* controls = GetControls(inChannel); + controls->mPitchBend = (inPitch2 << 7) | inPitch1; + controls->mFPitchBend = (float)((SInt16)controls->mPitchBend - 8192) / 8192.; + return noErr; +} + + +OSStatus AUInstrumentBase::HandleChannelPressure(UInt8 inChannel, + UInt8 inValue, + UInt32 inStartFrame) +{ + GetControls(inChannel)->mMonoPressure = inValue; + return noErr; +} + + +OSStatus AUInstrumentBase::HandleProgramChange( UInt8 inChannel, + UInt8 inValue) +{ + GetControls(inChannel)->mMonoPressure = inValue; + return noErr; +} + + +OSStatus AUInstrumentBase::HandlePolyPressure( UInt8 inChannel, + UInt8 inKey, + UInt8 inValue, + UInt32 inStartFrame) +{ + GetControls(inChannel)->mPolyPressure[inKey] = inValue; + return noErr; +} + + +OSStatus AUInstrumentBase::HandleResetAllControllers( UInt8 inChannel) +{ + SendPedalEvent (inChannel, SynthEvent::kEventType_ResetAllControllers, 0); + return noErr; +} + + +OSStatus AUInstrumentBase::HandleAllNotesOff( UInt8 inChannel) +{ + SendPedalEvent (inChannel, SynthEvent::kEventType_AllNotesOff, 0); + return noErr; +} + + +OSStatus AUInstrumentBase::HandleAllSoundOff( UInt8 inChannel) +{ + SendPedalEvent (inChannel, SynthEvent::kEventType_AllSoundOff, 0); + return noErr; +} + +SynthNote* AUInstrumentBase::GetAFreeNote(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("GetAFreeNote size %d\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 + printf("enter voice stealing\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 + printf(" steal 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 + printf(" steal group %d size %d\n", j, group->mNoteList[i].Length()); +#endif + if (group->mNoteList[i].NotEmpty()) { +#if DEBUG_PRINT + printf("not empty %d %d\n", i, j); +#endif + SynthNote *note = group->mNoteList[i].FindMostQuietNote(); + if (inKillIt) { +#if DEBUG_PRINT + printf("--=== KILL ===---\n"); +#endif + note->mRelativeKillFrame = inFrame; + note->Kill(inFrame); + group->mNoteList[i].RemoveNote(note); + if (i != kNoteState_FastReleased) + DecNumActiveNotes(); + return note; + } else { +#if DEBUG_PRINT + printf("--=== 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 + 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( + ComponentInstance 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; + + IncNumActiveNotes(); + note->AttackNote(NULL, inGroup, inNoteInstanceID, + mAbsoluteSampleFrame + inOffsetSampleFrame, inOffsetSampleFrame, inParams); + + inGroup->mNoteList[kNoteState_Attacked].AddNote(note); + return noErr; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +OSStatus AUMultitimbralInstrumentBase::GetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + OSStatus result = noErr; + + switch (inID) + { + case kMusicDeviceProperty_PartGroup: + if (inScope != kAudioUnitScope_Part) return kAudioUnitErr_InvalidScope; + outDataSize = sizeof(UInt32); + outWritable = true; + break; + + 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) + { + case kMusicDeviceProperty_PartGroup: + if (inScope != kAudioUnitScope_Group) return kAudioUnitErr_InvalidScope; + // ?? + return -1; //unimpl + break; + + 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) + { + case kMusicDeviceProperty_PartGroup: + if (inScope != kAudioUnitScope_Group) return kAudioUnitErr_InvalidScope; + // ?? + return -1; //unimpl + break; + + default: + result = MusicDeviceBase::SetProperty (inID, inScope, inElement, inData, inDataSize); + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h new file mode 100755 index 00000000..8f779cbf --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h @@ -0,0 +1,246 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#ifndef __AUInstrumentBase__ +#define __AUInstrumentBase__ + +#include <vector> +#include <stdexcept> +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.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( + ComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups = 32, + UInt32 numParts = 0); + virtual ~AUInstrumentBase(); + + virtual OSStatus Initialize(); + + virtual void Cleanup(); + + virtual AUElement* CreateElement( AudioUnitScope scope, + AudioUnitElement element); + + 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 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); + + MidiControls* GetControls( MusicDeviceGroupID inChannel) + { + SynthGroupElement *group = GetElForGroupID(inChannel); + return &(group->mMidiControls); + } + + +protected: + + UInt32 NextNoteID() { return IncrementAtomic(&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(); + // this call throws if there's no assigned element for the group ID + SynthGroupElement * GetElForGroupID (MusicDeviceGroupID inGroupID); + SynthGroupElement * GetElForNoteID (NoteInstanceID inNoteID); + + SInt64 mAbsoluteSampleFrame; + + +private: + + SInt32 mNoteIDCounter; + + SynthEventQueue mEventQueue; + + UInt32 mNumNotes; + UInt32 mNumActiveNotes; + UInt32 mMaxActiveNotes; + SynthNote* mNotes; + SynthNoteList mFreeNotes; + UInt32 mNoteSize; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class AUMonotimbralInstrumentBase : public AUInstrumentBase +{ +public: + AUMonotimbralInstrumentBase( + ComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups = 32, + UInt32 numParts = 0); + + 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( + ComponentInstance 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/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h new file mode 100755 index 00000000..50f97771 --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h @@ -0,0 +1,167 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#include <Carbon/Carbon.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. + UInt32 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]; + } + + // 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() { CompareAndSwap(mWriteIndex, (mWriteIndex + 1) & mMask, (UInt32*)&mWriteIndex); } + void AdvanceReadPtr() { CompareAndSwap(mReadIndex, (mReadIndex + 1) & mMask, (UInt32*)&mReadIndex); } + +private: + ITEM* FreeItem() + { + if (mFreeIndex == mReadIndex) return NULL; + return &mItems[mFreeIndex]; + } + void AdvanceFreePtr() { CompareAndSwap(mFreeIndex, (mFreeIndex + 1) & mMask, (UInt32*)&mFreeIndex); } + + void FreeItems() + { + ITEM* item; + while ((item = FreeItem()) != NULL) + { + item->Free(); + AdvanceFreePtr(); + } + } + + volatile UInt32 mReadIndex, mWriteIndex, mFreeIndex; + UInt32 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() + { + UInt32 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() { CompareAndSwap(mWriteIndex, (mWriteIndex + 1) & mMask, (UInt32*)&mWriteIndex); } + void AdvanceReadPtr() { CompareAndSwap(mReadIndex, (mReadIndex + 1) & mMask, (UInt32*)&mReadIndex); } + +private: + + volatile UInt32 mReadIndex, mWriteIndex; + UInt32 mMask; + ITEM *mItems; +}; + diff --git a/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp new file mode 100755 index 00000000..d16f8e87 --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp @@ -0,0 +1,266 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#include "SynthElement.h" +#include "AUInstrumentBase.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +MidiControls::MidiControls() +{ + Reset(); +} + +void MidiControls::Reset() +{ + memset(this, 0, sizeof(MidiControls)); + mControls[kMidiController_Pan] = 64; + mControls[kMidiController_Expression] = 127; + mPitchBendDepth = 2 << 7; + mFPitchBendDepth = 2.; +} + + +SynthElement::SynthElement(AUInstrumentBase *audioUnit, UInt32 inElement) + : AUElement(audioUnit), mName(0), mIndex(inElement) +{ +} + +SynthElement::~SynthElement() +{ + if (mName) CFRelease(mName); +} + +SynthGroupElement::SynthGroupElement(AUInstrumentBase *audioUnit, UInt32 inElement) + : SynthElement(audioUnit, inElement), mSustainIsOn(false), mSostenutoIsOn(false), mOutputBus(0), mGroupID(kUnassignedGroup) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::SynthGroupElement %d\n", inElement); +#endif + for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i) + mNoteList[i].mState = i; +} + +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 + mMidiControls.Reset(); + for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i) + mNoteList[i].Empty(); +} + +SynthPartElement::SynthPartElement(AUInstrumentBase *audioUnit, UInt32 inElement) + : SynthElement(audioUnit, inElement) +{ +} + +void SynthGroupElement::NoteOff(NoteInstanceID inNoteID, UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::NoteOff %d\n", inNoteID); +#endif + SynthNote *note = mNoteList[kNoteState_Attacked].mHead; + // see if this note is attacked. + while (note && note->mNoteID != inNoteID) + { +#if DEBUG_PRINT + printf(" ? %08X %d\n", note, note->mNoteID); +#endif + note = note->mNext; + } + +#if DEBUG_PRINT + printf(" found %08X\n", note); +#endif + if (note) + { +#if DEBUG_PRINT + printf(" old state %d\n", note->mState); +#endif + mNoteList[kNoteState_Attacked].RemoveNote(note); + note->Release(inFrame); + if (mSustainIsOn) { + mNoteList[kNoteState_ReleasedButSustained].AddNote(note); + } else { + mNoteList[kNoteState_Released].AddNote(note); + } +#if DEBUG_PRINT + printf(" new state %d\n", note->mState); +#endif + } + else if (mSostenutoIsOn) + { + // see if this note is sostenutoed. + note = mNoteList[kNoteState_Sostenutoed].mHead; + while (note && note->mNoteID != inNoteID) + note = note->mNext; + if (note) + { + mNoteList[kNoteState_Sostenutoed].RemoveNote(note); + mNoteList[kNoteState_ReleasedButSostenutoed].AddNote(note); + } + } +} + +void SynthGroupElement::NoteEnded(SynthNote *inNote, UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::NoteEnded %d %d\n", inNote->mNoteID, inNote->mState); +#endif + SynthNoteList *list = mNoteList + inNote->mState; + list->RemoveNote(inNote); + + GetAUInstrument()->AddFreeNote(inNote); +} + +void SynthGroupElement::SostenutoOn(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::SostenutoOn\n"); +#endif + mSostenutoIsOn = true; + mNoteList[kNoteState_Sostenutoed].TransferAllFrom(&mNoteList[kNoteState_Attacked], inFrame); +} + +void SynthGroupElement::SostenutoOff(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::SostenutoOff\n"); +#endif + 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 + mSustainIsOn = true; +} + +void SynthGroupElement::SustainOff(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::SustainOff\n"); +#endif + 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<kNumberOfActiveNoteStates; ++i) + { + note = mNoteList[i].mHead; + while (note) + { + SynthNote *nextNote = note->mNext; + + mNoteList[i].RemoveNote(note); + note->FastRelease(inFrame); + mNoteList[kNoteState_FastReleased].AddNote(note); + + note = nextNote; + } + } +} + +void SynthGroupElement::AllSoundOff(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::AllSoundOff\n"); +#endif + AllNotesOff(inFrame); +} + +void SynthGroupElement::ResetAllControllers(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::ResetAllControllers\n"); +#endif + mMidiControls.Reset(); +} + +OSStatus SynthGroupElement::Render(UInt32 inNumberFrames) +{ + SynthNote *note; + AudioBufferList& bufferList = GetAudioUnit()->GetOutput(mOutputBus)->GetBufferList(); + + for (UInt32 i=0 ; i<kNumberOfSoundingNoteStates; ++i) + { + note = mNoteList[i].mHead; + while (note) + { +#if DEBUG_PRINT + printf(" note %d %08X %d\n", i, note, inNumberFrames); +#endif + SynthNote *nextNote = note->mNext; + + OSStatus err = note->Render(inNumberFrames, bufferList); + if (err) return err; + + note = nextNote; + } + } + + return noErr; +} + + diff --git a/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h new file mode 100755 index 00000000..a1170730 --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h @@ -0,0 +1,262 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#ifndef __SynthElement__ +#define __SynthElement__ + +#include <Carbon/Carbon.h> +#include <AudioUnit/AudioUnit.h> +#include "MusicDeviceBase.h" +#include "SynthNoteList.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(); } + + CFStringRef GetName() const { return mName; } + void SetName(CFStringRef inName) + { + CFStringRef oldName = mName; + mName = inName; + CFRetain(mName); + if (oldName) CFRelease(oldName); + } +private: + CFStringRef mName; + UInt32 mIndex; +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +enum { + kMidiController_BankSelect = 0, + kMidiController_ModWheel = 1, + kMidiController_Breath = 2, + kMidiController_Foot = 4, + kMidiController_PortamentoTime = 5, + kMidiController_DataEntry = 6, + kMidiController_Volume = 7, + kMidiController_Balance = 8, + kMidiController_Pan = 10, + kMidiController_Expression = 11, + + // these controls have a (0-63) == off, (64-127) == on + kMidiController_Sustain = 64, //hold1 + kMidiController_Portamento = 65, + kMidiController_Sostenuto = 66, + kMidiController_Soft = 67, + kMidiController_LegatoPedal = 68, + kMidiController_Hold2Pedal = 69, + kMidiController_FilterResonance = 71, + kMidiController_ReleaseTime = 72, + kMidiController_AttackTime = 73, + kMidiController_Brightness = 74, + kMidiController_DecayTime = 75, + kMidiController_VibratoRate = 76, + kMidiController_VibratoDepth = 77, + kMidiController_VibratoDelay = 78, + + // these controls have a 0-127 range and in MIDI they have no LSB (so fractional values are lost in MIDI) + kMidiController_ReverbLevel = 91, + kMidiController_ChorusLevel = 93, + + kMidiController_AllSoundOff = 120, + kMidiController_ResetAllControllers = 121, + kMidiController_AllNotesOff = 123 +}; + +struct MidiControls +{ + MidiControls(); + void Reset(); + + 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; + + SInt16 GetHiResControl(UInt32 inIndex) const + { + return ((mControls[inIndex] & 127) << 7) | (mControls[inIndex + 32] & 127); + } + + void SetHiResControl(UInt32 inIndex, UInt8 inMSB, UInt8 inLSB) + { + mControls[inIndex] = inMSB; + mControls[inIndex + 32] = inLSB; + } + + float GetControl(UInt32 inIndex) const + { + if (inIndex < 32) { + return (float)mControls[inIndex] + (float)mControls[inIndex + 32] / 127.; + } else { + return (float)mControls[inIndex]; + } + } + + float PitchBend() const { return mFPitchBend * mFPitchBendDepth; } + +}; + + +class SynthGroupElement : public SynthElement +{ +public: + enum { + kUnassignedGroup = 0xFFFFFFFF + }; + + SynthGroupElement(AUInstrumentBase *audioUnit, UInt32 inElement); + + void NoteOff(NoteInstanceID inNoteID, UInt32 inFrame); + void SustainOn(UInt32 inFrame); + void SustainOff(UInt32 inFrame); + void SostenutoOn(UInt32 inFrame); + void SostenutoOff(UInt32 inFrame); + + void NoteEnded(SynthNote *inNote, UInt32 inFrame); + + void AllNotesOff(UInt32 inFrame); + void AllSoundOff(UInt32 inFrame); + void ResetAllControllers(UInt32 inFrame); + + UInt32 GetOutputBus() const { return mOutputBus; } + void SetOutputBus(UInt32 inBus) { mOutputBus = inBus; } + + void Reset(); + + virtual OSStatus Render(UInt32 inNumberFrames); + + float GetControl(UInt32 inIndex) const { return mMidiControls.GetControl(inIndex); } + float PitchBend() const { return mMidiControls.PitchBend(); } + + MusicDeviceGroupID GroupID () const { return mGroupID; } + void SetGroupID (MusicDeviceGroupID inGroup); + +private: + friend class AUInstrumentBase; + friend class AUMonotimbralInstrumentBase; + friend class AUMultitimbralInstrumentBase; + + MidiControls mMidiControls; + + bool mSustainIsOn; + bool mSostenutoIsOn; + UInt32 mOutputBus; + MusicDeviceGroupID mGroupID; + + + SynthNoteList mNoteList[kNumberOfSoundingNoteStates]; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +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; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline AUInstrumentBase* SynthNote::GetAudioUnit() const + { + return (AUInstrumentBase*)mGroup->GetAudioUnit(); + } + +inline Float32 SynthNote::GetGlobalParameter(AudioUnitParameterID inParamID) const + { + return mGroup->GetAudioUnit()->Globals()->GetParameter(inParamID); + } + +inline void SynthNote::NoteEnded(UInt32 inFrame) + { + mGroup->NoteEnded(this, inFrame); + mNoteID = 0xFFFFFFFF; + } + +inline float SynthNote::PitchBend() const + { + return mGroup->PitchBend(); + } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h new file mode 100755 index 00000000..86a439ad --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h @@ -0,0 +1,140 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +/* You can either fill in code here or remove this and create or add new files. */ + +#ifndef __SynthEvent__ +#define __SynthEvent__ + +#include <Carbon/Carbon.h> +#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/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp new file mode 100755 index 00000000..a3c838ca --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp @@ -0,0 +1,113 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#include "SynthNote.h" +#include "SynthElement.h" +#include "AUInstrumentBase.h" + +void SynthNote::AttackNote( + SynthPartElement * inPart, + SynthGroupElement * inGroup, + NoteInstanceID inNoteID, + SInt64 inAbsoluteSampleFrame, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) +{ +#if DEBUG_PRINT + printf("SynthNote::AttackNote %d %d %d\n", inPart, inGroup->GroupID(), inNoteID); +#endif + mPart = inPart; + mGroup = inGroup; + mNoteID = inNoteID; + + mAbsoluteStartFrame = inAbsoluteSampleFrame; + mRelativeStartFrame = inOffsetSampleFrame; + mRelativeReleaseFrame = -1; + mRelativeKillFrame = -1; + + mPitch = inParams.mPitch; + mVelocity = inParams.mVelocity; + + + 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. + PitchBend()) / 12.); +} + +double SynthNote::SampleRate() +{ + return GetAudioUnit()->GetOutput(0)->GetStreamFormat().mSampleRate; +} + + diff --git a/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h new file mode 100755 index 00000000..afb1730d --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h @@ -0,0 +1,170 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#ifndef __SynthNote__ +#define __SynthNote__ + +#include <Carbon/Carbon.h> +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.h> +#include "MusicDeviceBase.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +enum { + 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 +}; + +/* + 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), mState(kNoteState_Free), + mRelativeStartFrame(0), + mRelativeReleaseFrame(-1), + mRelativeKillFrame(-1) + { + } + + virtual ~SynthNote() {} + + virtual void Reset(); + virtual void AttackNote( + SynthPartElement * inPart, + SynthGroupElement * inGroup, + NoteInstanceID inNoteID, + SInt64 inAbsoluteSampleFrame, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams + ); + + virtual OSStatus Render(UInt32 inNumFrames, AudioBufferList& inBufferList)=0; + virtual void 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; } + UInt32 GetState() const { return mState; } + Boolean IsSounding() const { return mState < kNumberOfSoundingNoteStates; } + Boolean IsActive() const { return mState < kNumberOfActiveNoteStates; } + SInt64 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 PitchBend() const; + double TuningA() const; + + virtual double Frequency(); // returns the frequency of note + pitch bend. + double SampleRate(); + +//private: +// friend class NoteList; + + // linked list pointers + SynthNote *mPrev; + SynthNote *mNext; + + SynthPartElement* mPart; + SynthGroupElement* mGroup; + + NoteInstanceID mNoteID; + UInt32 mState; + SInt64 mAbsoluteStartFrame; + SInt32 mRelativeStartFrame; + SInt32 mRelativeReleaseFrame; + SInt32 mRelativeKillFrame; + + Float32 mPitch; + Float32 mVelocity; +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp new file mode 100755 index 00000000..a5ee505b --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp @@ -0,0 +1,87 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#include "SynthNoteList.h" +#include <stdexcept> + +void SynthNoteList::SanityCheck() const +{ + if (mState >= kNumberOfNoteStates) { + throw std::runtime_error("mState is bad"); + } + + if (mHead == NULL) { + if (mTail != NULL) + throw std::runtime_error("mHead is NULL but not mTail"); + return; + } + if (mTail == NULL) { + throw std::runtime_error("mTail is NULL but not mHead"); + } + + if (mHead->mPrev) { + throw std::runtime_error("mHead has a mPrev"); + } + if (mTail->mNext) { + throw std::runtime_error("mTail has a mNext"); + } + + SynthNote *note = mHead; + while (note) + { + if (note->mState != mState) + throw std::runtime_error("note in wrong state"); + if (note->mNext) { + if (note->mNext->mPrev != note) + throw std::runtime_error("bad link 1"); + } else { + if (mTail != note) + throw std::runtime_error("note->mNext is nil, but mTail != note"); + } + if (note->mPrev) { + if (note->mPrev->mNext != note) + throw std::runtime_error("bad link 2"); + } else { + if (mHead != note) + throw std::runtime_error("note->mPrev is nil, but mHead != note"); + } + note = note->mNext; + } +} diff --git a/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h new file mode 100755 index 00000000..5ab7ffa0 --- /dev/null +++ b/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h @@ -0,0 +1,226 @@ +/* Copyright © 2007 Apple Inc. All Rights Reserved. + + 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. +*/ +#ifndef __SynthNoteList__ +#define __SynthNoteList__ + +#include "SynthNote.h" + +#if DEBUG +#ifndef DEBUG_PRINT + #define DEBUG_PRINT 0 +#endif + #define SANITY_CHECK 0 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct SynthNoteList +{ + SynthNoteList() : mState(0xFFFFFFFF), mHead(0), mTail(0) {} + + bool NotEmpty() const { return mHead != NULL; } + bool IsEmpty() const { return mHead == NULL; } + void Empty() { +#if SANITY_CHECK + SanityCheck(); +#endif + mHead = mTail = NULL; + } + + UInt32 Length() const { +#if 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 %d %08X\n", mState, inNote); +#endif +#if SANITY_CHECK + SanityCheck(); +#endif + inNote->mState = mState; + inNote->mNext = mHead; + inNote->mPrev = NULL; + + if (mHead) { mHead->mPrev = inNote; mHead = inNote; } + else mHead = mTail = inNote; +#if SANITY_CHECK + SanityCheck(); +#endif + } + + void RemoveNote(SynthNote *inNote) + { +#if DEBUG_PRINT + printf("RemoveNote\n"); +#endif +#if 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 SANITY_CHECK + SanityCheck(); +#endif + } + + void TransferAllFrom(SynthNoteList *inNoteList, UInt32 inFrame) + { +#if DEBUG_PRINT + printf("TransferAllFrom\n"); +#endif +#if 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 release %08X\n", note); +#endif + note->mState = mState; + note->Release(inFrame); + } + } + else + { + for (SynthNote* note = inNoteList->mHead; note; note = note->mNext) + { + note->mState = 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 SANITY_CHECK + SanityCheck(); + inNoteList->SanityCheck(); +#endif + } + + SynthNote* FindOldestNote() + { +#if DEBUG_PRINT + printf("FindOldestNote\n"); +#endif +#if SANITY_CHECK + SanityCheck(); +#endif + SInt64 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 = 1e9; + SInt64 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 SANITY_CHECK + SanityCheck(); +#endif + return mostQuietNote; + } + + void SanityCheck() const; + + UInt32 mState; + SynthNote *mHead; + SynthNote *mTail; +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif |