diff options
| author | TheMarlboroMan <marlborometal@gmail.com> | 2020-11-15 16:50:27 +0100 | 
|---|---|---|
| committer | TheMarlboroMan <marlborometal@gmail.com> | 2020-11-15 17:04:21 +0100 | 
| commit | eb0a72576c71557c8bb64cfd319620f5ea7ba24c (patch) | |
| tree | df15c068870705d7c7847b8d98a760e058756f03 /src | |
| parent | b0fa70c97c9b4886fb6e063664dc4d10daf12c1c (diff) | |
Implementation of the voice limiting feature.
Diffstat (limited to 'src')
| -rw-r--r-- | src/inputprocessor.cc | 87 | ||||
| -rw-r--r-- | src/inputprocessor.h | 5 | ||||
| -rw-r--r-- | src/settings.h | 24 | 
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)  | 
