summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/inputprocessor.cc87
-rw-r--r--src/inputprocessor.h5
-rw-r--r--src/settings.h24
3 files changed, 113 insertions, 3 deletions
diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc
index 2da5dbc..fd6e5b9 100644
--- a/src/inputprocessor.cc
+++ b/src/inputprocessor.cc
@@ -252,7 +252,16 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,
return false;
}
- events_ds.startAddingNewGroup(instrument_id);
+ if(settings.enable_voice_limit.load())
+ {
+ limitVoices(instrument_id,
+ settings.voice_limit_max.load(),
+ settings.voice_limit_rampdown.load());
+ }
+
+ //Given that audio files could be invalid, maybe we must add the new
+ //group just before adding the first new sample...
+ bool new_group_added = false;
for(Channel& ch: kit.channels)
{
const auto af = sample->getAudioFile(ch);
@@ -263,8 +272,15 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,
else
{
//DEBUG(inputprocessor, "Adding event %d.\n", event.offset);
- auto& event_sample = events_ds.emplace<SampleEvent>(ch.num, ch.num, 1.0, af,
- instr->getGroup(), instrument_id);
+ if(!new_group_added)
+ {
+ new_group_added=true;
+ events_ds.startAddingNewGroup(instrument_id);
+ }
+
+ auto& event_sample =
+ events_ds.emplace<SampleEvent>(ch.num, ch.num, 1.0, af,
+ instr->getGroup(), instrument_id);
event_sample.offset = (event.offset + pos) * resample_ratio;
if(settings.normalized_samples.load() && sample->getNormalized())
@@ -353,3 +369,68 @@ bool InputProcessor::processStop(event_t& event)
return true;
}
+
+void InputProcessor::limitVoices(std::size_t instrument_id,
+ std::size_t max_voices,
+ float rampdown_time)
+{
+ const auto& group_ids=events_ds.getSampleEventGroupIDsOf(instrument_id);
+
+ if(group_ids.size() <= max_voices)
+ {
+ return;
+ }
+
+ //Filter out ramping events...
+ auto filter_ramping_predicate =
+ [this](EventGroupID group_id) -> bool
+ {
+ const auto& event_ids=events_ds.getEventIDsOf(group_id);
+ //TODO: This should not happen.
+ if(!event_ids.size())
+ {
+ return false;
+ }
+
+ const auto& sample=events_ds.get<SampleEvent>(event_ids[0]);
+ return !sample.rampdownInProgress();
+ };
+
+ EventGroupIDs non_ramping;
+ std::copy_if(std::begin(group_ids),
+ std::end(group_ids),
+ std::back_inserter(non_ramping), filter_ramping_predicate);
+
+ if(!non_ramping.size())
+ {
+ return;
+ }
+
+ //Let us get the eldest...
+ //TODO: where is the playhead? Should we add it to the offset?
+ auto compare_event_offsets =
+ [this](EventGroupID a, EventGroupID b)
+ {
+ const auto& event_ids_a=events_ds.getEventIDsOf(a);
+ const auto& event_ids_b=events_ds.getEventIDsOf(b);
+
+ const auto& sample_a=events_ds.get<SampleEvent>(event_ids_a[0]);
+ const auto& sample_b=events_ds.get<SampleEvent>(event_ids_b[0]);
+ return sample_a.offset < sample_b.offset;
+ };
+
+ auto it = std::min_element(std::begin(non_ramping),
+ std::end(non_ramping),
+ compare_event_offsets);
+ if(it == std::end(non_ramping))
+ {
+ return;
+ }
+
+ const auto& event_ids = events_ds.getEventIDsOf(*it);
+ for(const auto& event_id : event_ids)
+ {
+ auto& sample=events_ds.get<SampleEvent>(event_id);
+ applyChoke(settings, sample, rampdown_time, sample.offset);
+ }
+}
diff --git a/src/inputprocessor.h b/src/inputprocessor.h
index 3c2cd5a..971cc85 100644
--- a/src/inputprocessor.h
+++ b/src/inputprocessor.h
@@ -64,6 +64,11 @@ private:
bool processChoke(event_t& event, std::size_t pos, double resample_ratio);
bool processStop(event_t& event);
+ //! Ramps down samples from events_ds is there are more groups playing than
+ //! max_voices for a given instrument.
+ void limitVoices(std::size_t instrument_id, std::size_t max_voices,
+ float rampdown_time);
+
std::vector<std::unique_ptr<InputFilter>> filters;
Settings& settings;
diff --git a/src/settings.h b/src/settings.h
index 7749adf..7507827 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -165,6 +165,15 @@ struct Settings
// Notify UI about load errors
Atomic<std::string> load_status_text;
+
+ // Enables the ramping down of old samples once X groups of the same instrument are playing.
+ Atomic<bool> enable_voice_limit{false};
+ // Max number of voices before old samples are ramped down.
+ static std::size_t constexpr voice_limit_max_default = 15;
+ Atomic<std::size_t> voice_limit_max{voice_limit_max_default};
+ // Time it takes for an old sample to completely fall silent.
+ static float constexpr voice_limit_rampdown_default = 0.5f;
+ Atomic<float> voice_limit_rampdown{voice_limit_rampdown_default};
};
//! Settings getter class.
@@ -243,6 +252,10 @@ struct SettingsGetter
SettingRef<std::string> load_status_text;
+ SettingRef<bool> enable_voice_limit;
+ SettingRef<std::size_t> voice_limit_max;
+ SettingRef<float> voice_limit_rampdown;
+
SettingsGetter(Settings& settings)
: drumkit_file(settings.drumkit_file)
, drumkit_load_status(settings.drumkit_load_status)
@@ -300,6 +313,9 @@ struct SettingsGetter
, audition_instrument{settings.audition_instrument}
, audition_velocity{settings.audition_velocity}
, load_status_text{settings.load_status_text}
+ , enable_voice_limit{settings.enable_voice_limit}
+ , voice_limit_max{settings.voice_limit_max}
+ , voice_limit_rampdown{settings.voice_limit_rampdown}
{
}
};
@@ -379,6 +395,10 @@ public:
Notifier<std::string> load_status_text;
+ Notifier<bool> enable_voice_limit;
+ Notifier<std::size_t> voice_limit_max;
+ Notifier<float> voice_limit_rampdown;
+
void evaluate()
{
#define EVAL(x) if(settings.x.hasChanged()) { x(settings.x.getValue()); }
@@ -453,6 +473,10 @@ public:
EVAL(audition_velocity);
EVAL(load_status_text);
+
+ EVAL(enable_voice_limit);
+ EVAL(voice_limit_max);
+ EVAL(voice_limit_rampdown);
}
SettingsNotifier(Settings& settings)