21 out <<
"[z" << (int)r.mAddress.mZone <<
" c" << (
int)r.mAddress.mChannel <<
" k" << (int)r.mAddress.mKey <<
" f" << (
int)r.mAddress.mFlags <<
"]" ;
22 out <<
"[action:" << r.mAction <<
" ctl:" << r.mControllerNumber <<
" val:" << r.mValue <<
" off:" << r.mSampleOffset <<
"]";
26VoiceAllocator::VoiceAllocator()
29 mKeyToPitchFn = [](
int k){
return (k - 69.f)/12.f;};
31 mSustainedNotes.reserve(128);
32 mHeldKeys.reserve(128);
35VoiceAllocator::~VoiceAllocator()
39void VoiceAllocator::Clear()
42 mSustainedNotes.clear();
46void VoiceAllocator::ClearVoiceInputs(
SynthVoice* pVoice)
48 for(
int i=0; i<kNumVoiceControlRamps; ++i)
50 pVoice->mInputs[i].Clear();
56 if(mVoicePtrs.size() + 1 < UCHAR_MAX)
58 mVoicePtrs.push_back(pVoice);
59 ClearVoiceInputs(pVoice);
64 mVoiceGlides.emplace_back(ControlRampProcessor::Create(pVoice->mInputs));
68 throw std::runtime_error{
"VoiceAllocator: max voices exceeded!"};
72VoiceAllocator::VoiceBitsArray VoiceAllocator::VoicesMatchingAddress(
VoiceAddress addr)
74 const int n =
static_cast<int>(mVoicePtrs.size());
78 for(
int i=0; i<n; ++i)
86 if(addr.mZone != kAllZones)
88 for(
int i=0; i<n; ++i)
90 v[i] = v[i] & (mVoicePtrs[i]->mZone == addr.mZone);
95 if(addr.mFlags & kVoicesAll)
return v;
98 if(addr.mChannel != kAllChannels)
100 for(
int i=0; i<n; ++i)
102 v[i] = v[i] & (mVoicePtrs[i]->mChannel == addr.mChannel);
107 if(addr.mKey != kAllKeys)
109 for(
int i=0; i<n; ++i)
111 v[i] = v[i] & (mVoicePtrs[i]->mKey == addr.mKey);
116 if(addr.mFlags & kVoicesBusy)
118 for(
int i=0; i<n; ++i)
120 v[i] = v[i] & mVoicePtrs[i]->GetBusy();
125 if(addr.mFlags & kVoicesMostRecent)
129 for(
int i=0; i<n; ++i)
133 int64_t vt = mVoicePtrs[i]->mLastTriggeredTime;
144 for(
int i=0; i<n; ++i)
157void VoiceAllocator::SendControlToVoiceInputs(VoiceBitsArray v,
int ctlIdx,
float val,
int glideSamples)
160 for(
int i=0; i<mVoicePtrs.size(); ++i)
164 mVoiceGlides[i]->at(ctlIdx).SetTarget(val, 0, glideSamples, mBlockSize);
169void VoiceAllocator::SendControlToVoicesDirect(VoiceBitsArray v,
int ctlIdx,
float val)
172 for(
int i=0; i<mVoicePtrs.size(); ++i)
176 mVoicePtrs[i]->SetControl(ctlIdx, val);
181void VoiceAllocator::SendProgramChangeToVoices(VoiceBitsArray v,
int pgm)
183 for(
int i=0; i<mVoicePtrs.size(); ++i)
187 mVoicePtrs[i]->SetProgramNumber(pgm);
197 mInputQueue.
Pop(event);
198 VoiceAllocator::VoiceBitsArray voices = VoicesMatchingAddress(event.mAddress);
200 switch(event.mAction)
204 NoteOn(event, sampleTime);
209 if(event.mAddress.mFlags == kVoicesAll)
215 NoteOff(event, sampleTime);
219 case kPitchBendAction:
221 SendControlToVoiceInputs(voices, kVoiceControlPitchBend, event.mValue, mControlGlideSamples);
224 case kPressureAction:
226 SendControlToVoiceInputs(voices, kVoiceControlPressure, event.mValue, mControlGlideSamples);
231 SendControlToVoiceInputs(voices, kVoiceControlTimbre, event.mValue, mControlGlideSamples);
236 mSustainPedalDown = (bool) (event.mValue >= 0.5);
237 if (!mSustainPedalDown)
240 if (!mSustainedNotes.empty())
242 for (
auto susNotesItr = mSustainedNotes.begin(); susNotesItr != mSustainedNotes.end();)
244 uint8_t key = *susNotesItr;
245 bool held = std::find(mHeldKeys.begin(), mHeldKeys.end(), key) != mHeldKeys.end();
248 StopVoices(VoicesMatchingAddress({
event.mAddress.mZone, kAllChannels, key, 0}), event.mSampleOffset);
249 susNotesItr = mSustainedNotes.erase(susNotesItr);
258 case kControllerAction:
261 SendControlToVoicesDirect(voices, event.mControllerNumber, event.mValue);
264 case kProgramChangeAction:
266 SendProgramChangeToVoices(voices, event.mControllerNumber);
278 for(
auto& glides : mVoiceGlides)
280 for(
int i=0; i<kNumVoiceControlRamps; ++i)
282 glides->at(i).Process(blockSize);
287void VoiceAllocator::CalcGlideTimesInSamples()
289 mNoteGlideSamples =
static_cast<int>(mNoteGlideTime * mSampleRate);
290 mControlGlideSamples =
static_cast<int>(mControlGlideTime * mSampleRate);
293int VoiceAllocator::FindFreeVoiceIndex(
int startIndex)
const
295 size_t voices = mVoicePtrs.size();
296 for(
int i=0; i<voices; ++i)
298 int j = (startIndex + i)%voices;
308int VoiceAllocator::FindVoiceIndexToSteal(int64_t sampleTime)
const
310 size_t voices = mVoicePtrs.size();
311 int64_t earliestTime = sampleTime;
312 int longestPlayingVoiceIdx = 0;
313 for(
int i=0; i<voices; ++i)
316 if(pv->mLastTriggeredTime < earliestTime)
318 earliestTime = pv->mLastTriggeredTime;
319 longestPlayingVoiceIdx = i;
322 return longestPlayingVoiceIdx;
326void VoiceAllocator::StartVoice(
int voiceIdx,
int channel,
int key,
float pitch,
float velocity,
int sampleOffset, int64_t sampleTime,
bool retrig)
331 mVoiceGlides[voiceIdx]->at(kVoiceControlGate).SetTarget(velocity, sampleOffset, 1, mBlockSize);
335 mVoiceGlides[voiceIdx]->at(kVoiceControlPitch).SetTarget(pitch, sampleOffset, mNoteGlideSamples, mBlockSize);
339 pVoice->mLastTriggeredTime = sampleTime;
340 pVoice->mChannel = channel;
345 pVoice->
Trigger(velocity, retrig);
349void VoiceAllocator::StartVoices(VoiceBitsArray vbits,
int channel,
int key,
float pitch,
float velocity,
int sampleOffset, int64_t sampleTime,
bool retrig)
351 for(
int i=0; i<mVoicePtrs.size(); ++i)
355 StartVoice(i, channel, key, pitch, velocity, sampleOffset, sampleTime, retrig);
360void VoiceAllocator::StopVoice(
int voiceIdx,
int sampleOffset)
362 mVoiceGlides[voiceIdx]->at(kVoiceControlGate).SetTarget(0.0, sampleOffset, 1, mBlockSize);
363 mVoicePtrs[voiceIdx]->mKey = -1;
364 mVoicePtrs[voiceIdx]->Release();
368void VoiceAllocator::StopVoices(VoiceBitsArray vbits,
int sampleOffset)
370 for(
int i=0; i<mVoicePtrs.size(); ++i)
374 StopVoice(i, sampleOffset);
382 mSustainedNotes.clear();
383 mSustainPedalDown =
false;
385 size_t voices = mVoicePtrs.size();
386 for (
int v = 0; v < voices; v++)
395 for (
int v = 0; v < mVoicePtrs.size(); v++)
397 mVoicePtrs[v]->mGain = 0.;
403 int channel = e.mAddress.mChannel;
404 int key = e.mAddress.mKey;
405 int offset = e.mSampleOffset;
406 float velocity = e.mValue;
407 float pitch = mKeyToPitchFn(key +
static_cast<int>(mPitchOffset));
417 StartVoices(VoicesMatchingAddress({e.mAddress.mZone, kAllChannels, kAllKeys, 0}), channel, key, pitch, velocity, offset, sampleTime, retrig);
420 mSustainedNotes.clear();
425 int i = FindFreeVoiceIndex(mVoiceRotateIndex);
428 i = FindVoiceIndexToSteal(sampleTime);
432 mVoiceRotateIndex = i + 1;
437 StartVoice(i, channel, key, pitch, velocity, offset, sampleTime, retrig);
447 if(std::find(mHeldKeys.begin(), mHeldKeys.end(), key) == mHeldKeys.end())
449 mHeldKeys.push_back(key);
450 mMinHeldVelocity = std::min(velocity, mMinHeldVelocity);
454 if(std::find(mSustainedNotes.begin(), mSustainedNotes.end(), key) == mSustainedNotes.end())
456 mSustainedNotes.push_back(key);
462 int channel = e.mAddress.mChannel;
463 int key = e.mAddress.mKey;
464 int offset = e.mSampleOffset;
467 mHeldKeys.erase(std::remove(mHeldKeys.begin(), mHeldKeys.end(), key), mHeldKeys.end());
468 if(mHeldKeys.empty())
470 mMinHeldVelocity = 1.0f;
473 if(mPolyMode == kPolyModeMono)
475 bool doPlayQueuedKey =
false;
479 if(!mHeldKeys.empty())
481 queuedKey = mHeldKeys.back();
482 if (queuedKey != mVoicePtrs[0]->mKey)
484 doPlayQueuedKey =
true;
485 if(mSustainPedalDown)
488 mSustainedNotes.clear();
489 mSustainedNotes.push_back(queuedKey);
493 else if(mSustainPedalDown)
495 if(!mSustainedNotes.empty())
497 queuedKey = mSustainedNotes.back();
498 if (queuedKey != mVoicePtrs[0]->mKey)
500 doPlayQueuedKey =
true;
507 StopVoices(VoicesMatchingAddress({e.mAddress.mZone, kAllChannels, kAllKeys, 0}), offset);
514 float pitch = mKeyToPitchFn(queuedKey +
static_cast<int>(mPitchOffset));
517 StartVoices(VoicesMatchingAddress({e.mAddress.mZone, kAllChannels, kAllKeys, 0}), channel, queuedKey, pitch, mMinHeldVelocity, offset, sampleTime, retrig);
522 if (!mSustainPedalDown)
524 StopVoices(VoicesMatchingAddress(e.mAddress), e.mSampleOffset);
525 mSustainedNotes.erase(std::remove(mSustainedNotes.begin(), mSustainedNotes.end(), key), mSustainedNotes.end());
530void VoiceAllocator::ProcessVoices(sample** inputs, sample** outputs,
int nInputs,
int nOutputs,
int startIndex,
int blockSize)
532 for(
auto pVoice : mVoicePtrs)
size_t ElementsAvailable() const
virtual void ProcessSamplesAccumulating(sample **inputs, sample **outputs, int nInputs, int nOutputs, int startIdx, int nFrames)
Process a block of audio data for the voice.
virtual void Trigger(double level, bool isRetrigger)
Trigger is called by the VoiceAllocator when a new voice should start, or if the voice limit has been...
virtual bool GetBusy() const =0
void HardKillAllVoices()
Stop all voices from making sound immdiately.
void SoftKillAllVoices()
Turn all voice gates off, allowing any voice envelopes to finish.
void ProcessEvents(int samples, int64_t sampleTime)
Process all input events and generate voice outputs.
void AddVoice(SynthVoice *pv, uint8_t zone)
Add a synth voice to the allocator.