16 constexpr int kParallelVoices = poly_float::kSize / 2;
17 constexpr int kChannelShift = 8;
18 constexpr int kNoteMask = (1 << kChannelShift) - 1;
21 force_inline int combineNoteChannel(
int note,
int channel) {
22 return (channel << kChannelShift) + note;
27 return value >> kChannelShift;
32 return value & kNoteMask;
36 force_inline int voiceCompareNewestFirst(Voice* left, Voice* right) {
37 int left_count = left->state().note_count;
38 int right_count = right->state().note_count;
39 return left_count - right_count;
43 force_inline int voiceCompareLowestFirst(Voice* left, Voice* right) {
44 return right->state().midi_note - left->state().midi_note;
48 force_inline int voiceCompareHighestFirst(Voice* left, Voice* right) {
49 return left->state().midi_note - right->state().midi_note;
53 force_inline int pressedCompareLowestFirst(
int left,
int right) {
54 return getNote(right) - getNote(left);
58 force_inline int pressedCompareHighestFirst(
int left,
int right) {
59 return getNote(left) - getNote(right);
69 aftertouch_sample_(-1),
82 last_key_state_ =
kDead;
91 voice_killer_(nullptr),
93 last_played_note_(-1.0f),
97 pitch_wheel_values_(),
98 zoned_pitch_wheel_values_(),
102 voice_priority_(kRoundRobin),
103 voice_override_(kKill),
110 all_aggregate_voices_.reserve(
kMaxPolyphony / kParallelVoices + kParallelVoices);
111 active_aggregate_voices_.reserve(
kMaxPolyphony / kParallelVoices + kParallelVoices);
114 voice_midi_ = ¬e_;
117 voice_event_.
owner = &voice_router_;
118 retrigger_.
owner = &voice_router_;
119 reset_.
owner = &voice_router_;
120 note_.
owner = &voice_router_;
121 last_note_.
owner = &voice_router_;
122 note_pressed_.
owner = &voice_router_;
123 note_count_.
owner = &voice_router_;
124 note_in_octave_.
owner = &voice_router_;
125 channel_.
owner = &voice_router_;
126 velocity_.
owner = &voice_router_;
127 lift_.
owner = &voice_router_;
128 aftertouch_.
owner = &voice_router_;
129 slide_.
owner = &voice_router_;
130 active_mask_.
owner = &voice_router_;
131 mod_wheel_.
owner = &voice_router_;
132 pitch_wheel_.
owner = &voice_router_;
133 pitch_wheel_percent_.
owner = &voice_router_;
134 local_pitch_bend_.
owner = &voice_router_;
137 voice_router_.
router(
this);
138 global_router_.
router(
this);
143 void VoiceHandler::prepareVoiceTriggers(
AggregateVoice* aggregate_voice,
int num_samples) {
160 if (num_samples <= offset)
191 if (num_samples <= aftertouch_sample)
202 if (num_samples <= slide_sample)
213 void VoiceHandler::prepareVoiceValues(AggregateVoice* aggregate_voice) {
215 for (Voice* voice : aggregate_voice->voices) {
217 int channel = voice->state().channel;
231 mono_float lift_val = (voice->released() ? voice->state().lift : 0.0f);
240 poly_float active_value = dead ? 0.0f : 1.0f;
250 mono_float pitch_wheel_percent_val = pitch_wheel_val * 0.5f + 0.5f;
252 pitch_wheel_percent_val, mask);
260 void VoiceHandler::processVoice(AggregateVoice* aggregate_voice,
int num_samples) {
262 aggregate_voice->processor->process(num_samples);
265 void VoiceHandler::clearAccumulatedOutputs() {
267 for (
auto&
output : accumulated_outputs_)
271 void VoiceHandler::clearNonaccumulatedOutputs() {
273 for (
auto& outputs : nonaccumulated_outputs_)
277 void VoiceHandler::accumulateOutputs(
int num_samples) {
279 for (
auto&
output : accumulated_outputs_) {
284 for (
int i = 0; i < buffer_size; ++i)
285 dest[i] += source[i];
289 void VoiceHandler::combineAccumulatedOutputs(
int num_samples) {
291 for (
auto&
output : accumulated_outputs_) {
295 for (
int i = 0; i < buffer_size; ++i)
300 void VoiceHandler::writeNonaccumulatedOutputs(
poly_mask voice_mask,
int num_samples) {
302 for (
auto& outputs : nonaccumulated_outputs_) {
303 int buffer_size = std::min(num_samples, outputs.second->buffer_size);
304 poly_float* dest = outputs.second->buffer;
305 const poly_float* source = outputs.first->buffer;
309 for (
int i = 0; i < buffer_size; ++i) {
310 poly_float masked = source[i] & voice_mask;
323 global_router_.
process(num_samples);
325 int num_voices = active_voices_.size();
326 if (num_voices == 0) {
328 if (last_num_voices_)
329 clearAccumulatedOutputs();
331 last_num_voices_ = num_voices;
345 voice_override_ =
static_cast<VoiceOverride>(voice_override);
347 clearAccumulatedOutputs();
350 active_aggregate_voices_.clear();
352 int last_aggregate_index = 0;
353 for (
Voice* active_voice : active_voices_) {
354 if (active_aggregate_voices_.count(active_voice->parent()) == 0)
355 active_aggregate_voices_.push_back(active_voice->parent());
356 last_aggregate_voice = active_voice->parent();
357 last_aggregate_index = active_voice->voice_index();
361 if (last_aggregate_voice) {
362 active_aggregate_voices_.remove(last_aggregate_voice);
363 active_aggregate_voices_.push_back(last_aggregate_voice);
367 for (
AggregateVoice* aggregate_voice : active_aggregate_voices_) {
368 prepareVoiceTriggers(aggregate_voice, num_samples);
369 prepareVoiceValues(aggregate_voice);
370 processVoice(aggregate_voice, num_samples);
371 accumulateOutputs(num_samples);
376 alive_mask =
~utils::getSilentMask(voice_killer_->
buffer, num_samples);
379 for (
Voice* single_voice : aggregate_voice->voices) {
381 bool alive = (single_voice->
voice_mask() & alive_mask).sum();
382 bool active = active_voices_.count(single_voice);
383 if (released && !alive && active) {
384 active_voices_.remove(single_voice);
385 free_voices_.push_back(single_voice);
391 combineAccumulatedOutputs(num_samples);
394 if (active_voices_.size()) {
396 if (last_aggregate_index)
397 voice_mask = ~voice_mask;
399 writeNonaccumulatedOutputs(voice_mask, num_samples);
406 last_num_voices_ = num_voices;
411 voice_router_.
init();
412 global_router_.
init();
421 for (
auto& aggregate_voice : all_aggregate_voices_)
422 aggregate_voice->processor->setSampleRate(sample_rate);
426 return active_voices_.size();
430 for (
Voice* voice : active_voices_) {
431 if (voice->state().event !=
kVoiceKill && voice->state().midi_note ==
note)
438 for (
Voice* voice : active_voices_) {
439 if (voice->state().event !=
kVoiceKill && voice->state().midi_note ==
note && voice->state().channel ==
channel)
451 for (
Voice* voice : active_voices_) {
452 if (voice->sustained() && !voice->sostenuto() && voice->state().channel ==
channel)
453 voice->deactivate(sample);
459 for (
Voice* voice : active_voices_) {
460 if (voice->state().channel ==
channel)
461 voice->setSostenuto(
true);
467 for (
Voice* voice : active_voices_) {
468 if (voice->state().channel ==
channel) {
469 voice->setSostenuto(
false);
471 if (voice->sustained() && !sustain_[
channel])
472 voice->deactivate(sample);
478 for (
int i = from_channel; i <= to_channel; ++i)
483 for (
int i = from_channel; i <= to_channel; ++i)
486 for (
Voice* voice : active_voices_) {
487 int channel = voice->state().channel;
488 if (voice->sustained() && !voice->sostenuto() &&
channel >= from_channel &&
channel <= to_channel)
489 voice->deactivate(sample);
494 for (
int i = from_channel; i <= to_channel; ++i)
495 sostenuto_[i] =
true;
496 for (
Voice* voice : active_voices_) {
497 int channel = voice->state().channel;
499 voice->setSostenuto(
true);
504 for (
int i = from_channel; i <= to_channel; ++i)
505 sostenuto_[i] =
false;
507 for (
Voice* voice : active_voices_) {
508 int channel = voice->state().channel;
510 voice->setSostenuto(
false);
511 if (voice->sustained() && !sustain_[
channel])
512 voice->deactivate(sample);
519 pressed_notes_.
clear();
521 for (
Voice* voice : active_voices_) {
524 free_voices_.push_back(voice);
527 active_voices_.clear();
532 pressed_notes_.
clear();
533 for (
Voice* voice : active_voices_)
534 voice->deactivate(sample);
539 pressed_notes_.
clear();
540 for (
Voice* voice : active_voices_) {
541 if (voice->state().channel ==
channel)
542 voice->deactivate(sample);
548 pressed_notes_.
clear();
549 for (
Voice* voice : active_voices_) {
550 int channel = voice->state().channel;
552 voice->deactivate(sample);
558 if (active_voices_.size()) {
559 if (active_voices_.back()->voice_index())
566 Voice* VoiceHandler::grabVoice() {
570 if (active_voices_.size() <
polyphony() || (voice_override_ ==
kKill && !legato_)) {
571 Voice* parallel_voice = grabFreeParallelVoice();
573 parallel_voice = grabFreeVoice();
575 return parallel_voice;
598 Voice* VoiceHandler::grabFreeVoice() {
600 Voice* voice =
nullptr;
601 if (free_voices_.size()) {
602 voice = free_voices_.front();
603 free_voices_.pop_front();
608 Voice* VoiceHandler::grabFreeParallelVoice() {
610 for (
auto& aggregate_voice : all_aggregate_voices_) {
611 Voice* dead_voice =
nullptr;
612 bool has_active_voice =
false;
614 for (Voice* single_voice : aggregate_voice->voices) {
616 dead_voice = single_voice;
618 has_active_voice =
true;
622 if (has_active_voice && dead_voice) {
624 free_voices_.remove(dead_voice);
633 for (
auto iter = active_voices_.begin(); iter != active_voices_.end(); ++iter) {
634 Voice* voice = *iter;
635 if (voice->key_state() == key_state) {
636 active_voices_.erase(iter);
643 Voice* VoiceHandler::getVoiceToKill(
int max_voices) {
645 int excess_voices = active_voices_.size() - max_voices;
646 Voice* released =
nullptr;
647 Voice* sustained =
nullptr;
648 Voice* held =
nullptr;
650 for (Voice* voice : active_voices_) {
661 if (excess_voices <= 0)
674 int VoiceHandler::grabNextUnplayedPressedNote() {
677 auto iter = pressed_notes_.
begin();
679 if (voice_priority_ ==
kNewest) {
680 iter = pressed_notes_.
end();
681 while (iter != pressed_notes_.
begin()) {
688 for (; iter != pressed_notes_.
end(); ++iter) {
694 int old_note_value = *iter;
696 pressed_notes_.
erase(iter);
697 pressed_notes_.
push_back(old_note_value);
699 return old_note_value;
702 void VoiceHandler::sortVoicePriority() {
704 switch (voice_priority_) {
706 active_voices_.sort<voiceCompareLowestFirst>();
707 pressed_notes_.
sort<pressedCompareHighestFirst>();
710 active_voices_.sort<voiceCompareHighestFirst>();
711 pressed_notes_.
sort<pressedCompareLowestFirst>();
714 active_voices_.sort<voiceCompareNewestFirst>();
724 Voice* voice = grabVoice();
733 if (last_played_note_[0] >= 0.0f)
734 last_note_val = last_played_note_;
735 last_played_note_ = tuned_note;
737 int note_value = combineNoteChannel(
note,
channel);
738 pressed_notes_.
remove(note_value);
743 pressed_notes_.
size(), total_notes_, sample,
channel);
747 active_voices_.push_back(voice);
756 for (
Voice* voice : active_voices_) {
757 if (voice->state().midi_note ==
note && voice->state().channel ==
channel) {
760 voice->setLiftVelocity(
lift);
764 if (polyphony_ <= pressed_notes_.
size() && voice->state().event !=
kVoiceKill) {
765 Voice* new_voice = voice;
766 if (voice_override_ ==
kKill) {
768 new_voice = grabVoice();
771 active_voices_.remove(voice);
773 if (voice_priority_ ==
kNewest)
774 active_voices_.push_front(new_voice);
776 active_voices_.push_back(new_voice);
778 int old_note_value = grabNextUnplayedPressedNote();
780 mono_float old_note = getNote(old_note_value);
781 int old_channel = getChannel(old_note_value);
787 new_voice->
activate(old_note, tuned_note, voice->state().velocity,
788 last_played_note_, pressed_notes_.
size() + 1,
789 total_notes_, sample, old_channel);
790 voice->setLocalPitchBend(pitch_wheel_values_[
channel]);
791 voice->setAftertouch(pressure_values_[
channel]);
792 voice->setSlide(slide_values_[
channel]);
795 voice->deactivate(sample);
796 voice->setLiftVelocity(
lift);
806 for (
Voice* voice : active_voices_) {
807 if (voice->state().midi_note ==
note && voice->state().channel ==
channel)
815 for (
Voice* voice : active_voices_) {
816 if (voice->state().channel ==
channel && voice->held())
823 for (
Voice* voice : active_voices_) {
824 int ch = voice->state().channel;
825 if (ch >= from_channel && ch <= to_channel)
832 for (
Voice* voice : active_voices_) {
833 if (voice->state().channel ==
channel && voice->held())
834 voice->setSlide(
slide, sample);
840 for (
Voice* voice : active_voices_) {
841 int ch = voice->state().channel;
842 if (ch >= from_channel && ch <= to_channel)
843 voice->setSlide(
slide, sample);
849 while (all_voices_.size() <
static_cast<size_t>(
polyphony))
853 int num_voices_to_kill = active_voices_.size() -
polyphony;
854 for (
int i = 0; i < num_voices_to_kill; ++i) {
865 if (active_voices_.size())
866 return active_voices_.back()->state().tuned_note;
901 new_output->
owner =
this;
906 accumulated_outputs_[
output] = std::unique_ptr<Output>(new_output);
908 last_voice_outputs_[
output] = std::unique_ptr<Output>(new_output);
909 nonaccumulated_outputs_.ensureCapacity(
static_cast<int>(last_voice_outputs_.size()));
920 new_output->
owner =
this;
923 last_voice_outputs_[
output] = std::unique_ptr<Output>(new_output);
924 nonaccumulated_outputs_.ensureCapacity(
static_cast<int>(last_voice_outputs_.size()));
926 nonaccumulated_outputs_.push_back({
output, new_output });
938 return (processor == &voice_router_);
942 if (last_voice_outputs_.count(
output) == 0)
944 std::pair<Output*, Output*> pair(
output, last_voice_outputs_[
output].get());
945 if (!nonaccumulated_outputs_.contains(pair))
946 nonaccumulated_outputs_.push_back(pair);
950 if (last_voice_outputs_.count(
output) == 0)
952 std::pair<Output*, Output*> pair(
output, last_voice_outputs_[
output].get());
954 nonaccumulated_outputs_.remove(pair);
957 void VoiceHandler::addParallelVoices() {
961 for (
int i = 0; i < kParallelVoices; ++i) {
962 voice_value.
set(2 * i, i);
963 voice_value.
set(2 * i + 1, i);
966 std::unique_ptr<AggregateVoice> aggregate_voice = std::make_unique<AggregateVoice>();
967 aggregate_voice->processor = std::unique_ptr<Processor>(voice_router_.
clone());
968 aggregate_voice->processor->process(1);
970 aggregate_voice->voices.reserve(kParallelVoices);
971 for (
int i = 0; i < kParallelVoices; ++i) {
972 std::unique_ptr<Voice> single_voice = std::make_unique<Voice>(aggregate_voice.get());
975 aggregate_voice->voices.push_back(single_voice.get());
976 free_voices_.push_back(single_voice.get());
977 all_voices_.push_back(std::move(single_voice));
980 all_aggregate_voices_.push_back(std::move(aggregate_voice));
vital::mono_float convertMidiNote(int note) const
Converts a MIDI note number to a pitch offset based on the current tuning.
Definition tuning.cpp:370
void removeAll(T entry)
Removes all occurrences of a given element.
Definition circular_queue.h:297
force_inline void remove(T entry)
Removes the first occurrence of an element if found.
Definition circular_queue.h:283
force_inline int size() const
Returns the current number of elements in the queue.
Definition circular_queue.h:410
void reserve(int capacity)
Ensures that the queue has at least the given capacity.
Definition circular_queue.h:159
force_inline iterator end() const
Returns an iterator to the past-the-end element of the queue.
Definition circular_queue.h:432
force_inline iterator begin() const
Returns an iterator to the first element of the queue.
Definition circular_queue.h:425
force_inline void clear()
Clears all elements in the queue.
Definition circular_queue.h:388
void sort()
Sorts the elements according to a compare function.
Definition circular_queue.h:312
force_inline void push_back(T entry)
Pushes an element to the back of the queue.
Definition circular_queue.h:220
force_inline iterator erase(iterator &iter)
Erases the element at the iterator position and returns an iterator to the next element.
Definition circular_queue.h:350
Base class for all signal-processing units in Vital.
Definition processor.h:212
virtual Output * registerOutput(Output *output, int index)
Registers a new Output in the output list at a specified index.
Definition processor.cpp:223
virtual bool isPolyphonic() const
Checks if this Processor is polyphonic by querying its ProcessorRouter.
Definition processor.cpp:73
force_inline Input * input(unsigned int index=0) const
Retrieves the Input pointer at a given index.
Definition processor.h:587
force_inline int getSampleRate() const
Retrieves the current (effective) sample rate.
Definition processor.h:326
force_inline bool isControlRate() const
Checks if this Processor is running at control rate (buffer_size == 1).
Definition processor.h:342
force_inline int getOversampleAmount() const
Retrieves the current oversampling factor.
Definition processor.h:334
force_inline Output * output(unsigned int index=0) const
Retrieves the Output pointer at a given index.
Definition processor.h:616
virtual void setSampleRate(int sample_rate)
Updates the sample rate of this Processor (scaled by oversampling).
Definition processor.h:285
force_inline void router(ProcessorRouter *router)
Sets the ProcessorRouter that owns or manages this Processor.
Definition processor.h:507
virtual Processor * clone() const override
Creates a copy of this ProcessorRouter.
Definition processor_router.h:59
virtual void removeProcessor(Processor *processor)
Removes a Processor from this router.
Definition processor_router.cpp:151
virtual void init() override
Initializes the ProcessorRouter and all its Processors.
Definition processor_router.cpp:84
virtual void setSampleRate(int sample_rate) override
Sets the sample rate for all Processors in this router.
Definition processor_router.cpp:90
virtual void addIdleProcessor(Processor *processor)
Adds a Processor that should remain idle (not processed) in the router.
Definition processor_router.cpp:146
virtual void resetFeedbacks(poly_mask reset_mask)
Resets all Feedback nodes within this router using a reset mask.
Definition processor_router.cpp:273
virtual void process(int num_samples) override
Processes audio through all Processors managed by this router.
Definition processor_router.cpp:57
virtual void addProcessor(Processor *processor)
Adds a Processor to be managed by this router.
Definition processor_router.cpp:121
A ProcessorRouter that encapsulates a cohesive unit of functionality in the synthesizer.
Definition synth_module.h:129
force_inline Output * velocity()
Returns a pointer to velocity, the note-on velocity.
Definition voice_handler.h:634
void removeGlobalProcessor(Processor *processor)
Removes a Processor from the global router.
Definition voice_handler.cpp:888
void allNotesOffRange(int sample, int from_channel, int to_channel)
Definition voice_handler.cpp:546
force_inline Output * note()
Returns a pointer to the note Output, giving the current tuned note frequency or pitch.
Definition voice_handler.h:616
void setPolyphony(int polyphony)
Sets the polyphony to a new value, allocating or freeing voices as needed.
Definition voice_handler.cpp:847
virtual void noteOn(int note, mono_float velocity, int sample, int channel) override
Handles a MIDI note-on event, starting a note with a specified velocity and timing.
Definition voice_handler.cpp:721
void resetFeedbacks(poly_mask reset_mask) override
Resets any feedback paths in the poly router, applying the given mask.
Definition voice_handler.cpp:892
void setChannelAftertouch(int channel, mono_float aftertouch, int sample)
Sets channel-wide aftertouch (applies to all voices on that channel).
Definition voice_handler.cpp:812
@ kVoicePriority
Priority scheme for stealing or reassigning voices.
Definition voice_handler.h:391
@ kVoiceOverride
Behavior when exceeding polyphony: kill or steal.
Definition voice_handler.h:392
@ kPolyphony
Desired polyphony setting (1..kMaxActivePolyphony).
Definition voice_handler.h:390
force_inline Output * channel()
Returns a pointer to channel, indicating the MIDI channel of the voice.
Definition voice_handler.h:631
virtual void init() override
Initializes the voice and global routers, then calls SynthModule::init().
Definition voice_handler.cpp:409
void setAftertouch(int note, mono_float aftertouch, int sample, int channel)
Handles per-note aftertouch for a specific note and channel.
Definition voice_handler.cpp:804
force_inline Output * slide()
Returns a pointer to slide, the MPE "slide" expression value.
Definition voice_handler.h:643
void sostenutoOn(int channel)
Turns on sostenuto for a single channel.
Definition voice_handler.cpp:457
int getNumActiveVoices()
Returns the number of currently active voices (not dead).
Definition voice_handler.cpp:425
force_inline int polyphony()
Returns the current maximum polyphony (number of active voices allowed).
Definition voice_handler.h:670
virtual ~VoiceHandler()
Virtual destructor.
Definition voice_handler.cpp:141
bool isNotePlaying(int note)
Checks if a given MIDI note is playing.
Definition voice_handler.cpp:429
void setActiveNonaccumulatedOutput(Output *output)
Marks an Output as "active" for non-accumulated usage (e.g., for the last active voice only).
Definition voice_handler.cpp:941
void removeProcessor(Processor *processor) override
Removes a Processor from this router.
Definition voice_handler.cpp:880
virtual void process(int num_samples) override
Processes audio for a block of samples. For each active voice, triggers events, updates parameters,...
Definition voice_handler.cpp:321
VoiceOverride
Behavior for assigning a new note when at max polyphony.
Definition voice_handler.h:400
@ kKill
Immediately kill an existing voice to free one.
Definition voice_handler.h:401
virtual void noteOff(int note, mono_float velocity, int sample, int channel) override
Handles a MIDI note-off event, releasing a currently active note.
Definition voice_handler.cpp:752
void sostenutoOffRange(int sample, int from_channel, int to_channel)
Turns off sostenuto for a range of channels, prompting release if not sustained.
Definition voice_handler.cpp:503
void addGlobalProcessor(Processor *processor)
Adds a Processor to the "global" (monophonic) router, e.g. for final mixing or master effects.
Definition voice_handler.cpp:884
force_inline Output * aftertouch()
Returns a pointer to aftertouch, storing per-voice or channel-based aftertouch.
Definition voice_handler.h:640
void allNotesOff(int sample) override
Turns off all currently active notes, optionally specifying a sample index for timing.
Definition voice_handler.cpp:530
VoicePriority
Determines the voice stealing strategy (oldest, newest, highest, etc.).
Definition voice_handler.h:410
@ kRoundRobin
Definition voice_handler.h:415
@ kHighest
Definition voice_handler.h:413
@ kOldest
Definition voice_handler.h:412
@ kNewest
Definition voice_handler.h:411
@ kLowest
Definition voice_handler.h:414
void sustainOff(int sample, int channel)
Turns off sustain for a single channel, prompting voices to release.
Definition voice_handler.cpp:449
void addProcessor(Processor *processor) override
Adds a Processor to be managed by this router.
Definition voice_handler.cpp:870
static constexpr mono_float kLocalPitchBendRange
Range of local pitch bend in semitones for each voice.
Definition voice_handler.h:383
void addIdleProcessor(Processor *processor) override
Adds a Processor that should remain idle (not processed) in the router.
Definition voice_handler.cpp:875
VoiceHandler()=delete
Disabled default constructor; must specify polyphony and output count.
void allSoundsOff() override
Immediately turns off all sounding notes and stops all sound production.
Definition voice_handler.cpp:517
void setInactiveNonaccumulatedOutput(Output *output)
Marks an Output as "inactive" for non-accumulated usage, effectively disabling it.
Definition voice_handler.cpp:949
mono_float getLastActiveNote() const
Gets the last active note's tuned frequency (or 0 if none).
Definition voice_handler.cpp:863
void sostenutoOff(int sample, int channel)
Turns off sostenuto for a single channel, prompting release if not sustained.
Definition voice_handler.cpp:465
void sustainOffRange(int sample, int from_channel, int to_channel)
Turns off sustain for a range of channels, prompting voices to release.
Definition voice_handler.cpp:482
Output * registerOutput(Output *output) override
Registers an Output with this VoiceHandler, returning a pointer to a new accumulated or single-lane O...
Definition voice_handler.cpp:896
virtual void setSampleRate(int sample_rate) override
Sets the sample rate for both mono/global and voice (poly) routers.
Definition voice_handler.cpp:416
force_inline Output * lift()
Returns a pointer to lift, the note-off velocity or release velocity.
Definition voice_handler.h:637
Output * registerControlRateOutput(Output *output, bool active)
Registers a control-rate Output with the VoiceHandler.
Definition voice_handler.cpp:914
virtual bool shouldAccumulate(Output *output)
Determines whether an Output should be summed across voices (accumulated) or handled individually.
Definition voice_handler.cpp:316
void sustainOnRange(int from_channel, int to_channel)
Turns on sustain for a range of channels.
Definition voice_handler.cpp:477
void sustainOn(int channel)
Turns on sustain for a single channel.
Definition voice_handler.cpp:445
void setChannelRangeAftertouch(int from_channel, int to_channel, mono_float aftertouch, int sample)
Sets channel-wide aftertouch for a range of channels.
Definition voice_handler.cpp:821
poly_mask getCurrentVoiceMask()
Returns a mask for the last active voice, used for writing to output buffers.
Definition voice_handler.cpp:556
void setChannelSlide(int channel, mono_float aftertouch, int sample)
Sets channel-wide MPE "slide" for a single channel.
Definition voice_handler.cpp:830
void setChannelRangeSlide(int from_channel, int to_channel, mono_float aftertouch, int sample)
Sets channel-wide MPE "slide" for a range of channels.
Definition voice_handler.cpp:838
void sostenutoOnRange(int from_channel, int to_channel)
Turns on sostenuto for a range of channels.
Definition voice_handler.cpp:493
Represents a single playing note/voice, including voice-state and event handling.
Definition voice_handler.h:54
force_inline void shiftAftertouchEvent(int num_samples)
Shifts the aftertouch event sample index by num_samples.
Definition voice_handler.h:288
force_inline void clearSlideEvent()
Clears the unprocessed slide event, if any.
Definition voice_handler.h:307
force_inline mono_float slide()
Returns the current slide (MPE expression) value for this voice.
Definition voice_handler.h:117
force_inline void kill(int sample=0)
Immediately kills this voice (disregarding release).
Definition voice_handler.h:221
KeyState
Describes the lifecycle stage of a voice: kTriggering -> kHeld -> kReleased -> kDead,...
Definition voice_handler.h:64
@ kDead
The voice is no longer active.
Definition voice_handler.h:69
@ kReleased
The note has ended (off event) and is releasing.
Definition voice_handler.h:68
@ kSustained
The note has ended, but sustain pedal is holding it on.
Definition voice_handler.h:67
@ kTriggering
Note-on occurred, but hasn't processed yet.
Definition voice_handler.h:65
@ kHeld
The note is actively held down.
Definition voice_handler.h:66
force_inline mono_float aftertouch()
Returns the current aftertouch value for this voice.
Definition voice_handler.h:111
force_inline void setLocalPitchBend(mono_float bend)
Sets the local pitch bend (used for legato transitions or channel pitch bend).
Definition voice_handler.h:198
force_inline int event_sample()
Returns the sample index at which the latest event (on/off) was triggered.
Definition voice_handler.h:102
force_inline poly_mask voice_mask()
Returns the SIMD mask representing this voice's active lanes.
Definition voice_handler.h:108
force_inline mono_float aftertouch_sample()
Returns the sample index at which the latest aftertouch event occurred.
Definition voice_handler.h:114
force_inline void setAftertouch(mono_float aftertouch, int sample=0)
Sets the aftertouch (pressure) value for the voice.
Definition voice_handler.h:241
force_inline const KeyState last_key_state()
Returns the previous key state (before the most recent update).
Definition voice_handler.h:96
force_inline void markDead()
Marks this voice as kDead, meaning it's completely inactive.
Definition voice_handler.h:227
Voice()=delete
Disabled default constructor: Voice must belong to an AggregateVoice.
force_inline void shiftVoiceEvent(int num_samples)
Shifts the event sample index by num_samples (e.g., for partial block processing).
Definition voice_handler.h:280
force_inline const VoiceState & state()
Returns a const reference to the VoiceState struct that holds all relevant data.
Definition voice_handler.h:93
force_inline bool hasNewEvent()
Checks if there is a new (non-processed) on/off event for this voice.
Definition voice_handler.h:232
force_inline void shiftSlideEvent(int num_samples)
Shifts the slide event sample index by num_samples.
Definition voice_handler.h:296
force_inline bool hasNewSlide()
Returns true if there's a new slide event not yet processed.
Definition voice_handler.h:262
force_inline void setSlide(mono_float slide, int sample=0)
Sets the MPE "slide" value for the voice (often CC#74).
Definition voice_handler.h:251
force_inline void completeVoiceEvent()
Completes (consumes) the voice event, marking it as processed. If the voice was kTriggering,...
Definition voice_handler.h:270
force_inline void clearAftertouchEvent()
Clears the unprocessed aftertouch event, if any.
Definition voice_handler.h:302
force_inline bool hasNewAftertouch()
Returns true if there's a new aftertouch event not yet processed.
Definition voice_handler.h:257
force_inline void activate(int midi_note, mono_float tuned_note, mono_float velocity, poly_float last_note, int note_pressed, int note_count, int sample, int channel)
Activates (starts) the voice with the given note parameters.
Definition voice_handler.h:133
force_inline mono_float slide_sample()
Returns the sample index at which the latest slide event occurred.
Definition voice_handler.h:120
#define VITAL_ASSERT(x)
Definition common.h:11
#define force_inline
Definition common.h:23
Contains a collection of utility functions and classes used throughout Vital.
const poly_mask kFullMask
A mask covering all lanes of a poly_float vector.
Definition synth_constants.h:257
const poly_mask kFirstMask
A mask identifying the first voice slots in a polyphonic vector.
Definition synth_constants.h:266
force_inline int iclamp(int value, int min_val, int max_val)
Clamps an integer between [min_val, max_val].
Definition utils.h:250
force_inline poly_float mod(poly_float value)
Returns the fractional part of each lane by subtracting the floored value.
Definition poly_utils.h:814
force_inline void zeroBuffer(mono_float *buffer, int size)
Zeros a mono buffer (standard array).
Definition poly_utils.h:570
force_inline poly_int roundToInt(poly_float value)
Rounds each lane to the nearest integer.
Definition poly_utils.h:792
force_inline poly_float swapVoices(poly_float value)
Swaps the first half of the lanes with the second half.
Definition poly_utils.h:437
force_inline poly_float maskLoad(poly_float zero_value, poly_float one_value, poly_mask reset_mask)
Selects between two values (zero_value or one_value) based on a mask in each lane.
Definition poly_utils.h:351
Contains classes and functions used within the Vital synthesizer framework.
@ kVoiceOn
Definition common.h:77
@ kVoiceKill
Definition common.h:81
@ kVoiceOff
Definition common.h:80
constexpr int kNumMidiChannels
MIDI channels available per device.
Definition common.h:57
constexpr int kNotesPerOctave
Number of semitones per octave.
Definition common.h:51
constexpr int kMaxActivePolyphony
The maximum number of active voices Vital uses simultaneously.
Definition synth_constants.h:43
constexpr int kMidiSize
MIDI note count (0-127).
Definition common.h:44
poly_int poly_mask
Alias for clarity; used as a mask type in poly_float.
Definition poly_values.h:590
constexpr int kMaxPolyphony
The maximum number of voices allocated for polyphony (includes an extra for handling transitions).
Definition synth_constants.h:40
float mono_float
Definition common.h:33
An aggregate grouping that pairs multiple (parallel) voices with a shared Processor instance.
Definition voice_handler.h:365
CircularQueue< Voice * > voices
Collection of active Voice pointers.
Definition voice_handler.h:366
Holds and manages a buffer of samples (poly_float) for a Processor's output.
Definition processor.h:35
force_inline void trigger(poly_mask mask, poly_float value, poly_int offset)
Sets trigger values (mask, trigger value, and offset).
Definition processor.h:63
poly_float * buffer
Pointer to the output buffer.
Definition processor.h:110
Processor * owner
Owning processor.
Definition processor.h:112
int buffer_size
Current buffer size in samples.
Definition processor.h:114
force_inline void clearTrigger()
Clears the trigger mask, value, and offset.
Definition processor.h:72
poly_float trigger_value
Trigger values for voices.
Definition processor.h:116
int channel
Which MIDI channel this voice is associated with.
Definition voice_handler.h:41
int midi_note
MIDI note (0-127 usually).
Definition voice_handler.h:33
VoiceEvent event
The most recent voice event (on/off/kill).
Definition voice_handler.h:32
int note_pressed
Pressed note count (e.g., for note priority logic).
Definition voice_handler.h:39
mono_float velocity
Velocity of the note-on event.
Definition voice_handler.h:36
mono_float lift
Velocity of the note-off (a.k.a. release velocity).
Definition voice_handler.h:37
mono_float tuned_note
Possibly adjusted by a Tuning object.
Definition voice_handler.h:34
poly_float last_note
Holds the last note played for this voice.
Definition voice_handler.h:35
A specialized Output that always runs at control rate (buffer_size = 1).
Definition processor.h:189
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
static force_inline mask_simd_type vector_call equal(simd_type one, simd_type two)
Compares two SIMD float registers for equality, element-wise.
Definition poly_values.h:954
force_inline void vector_call set(size_t index, float new_value) noexcept
Sets a specific element in the SIMD register.
Definition poly_values.h:1182
Represents a vector of integer values using SIMD instructions.
Definition poly_values.h:56
Provides various utility functions, classes, and constants for audio, math, and general-purpose opera...
Declares classes and data structures to handle polyphonic voices in Vital, including voice assignment...