diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/drumgizmo.cc | 113 | ||||
| -rw-r--r-- | src/drumgizmo.h | 5 | ||||
| -rw-r--r-- | src/events.cc | 26 | ||||
| -rw-r--r-- | src/events.h | 68 | ||||
| -rw-r--r-- | src/events_ds.cc | 118 | ||||
| -rw-r--r-- | src/events_ds.h | 135 | ||||
| -rw-r--r-- | src/id.h | 80 | ||||
| -rw-r--r-- | src/inputprocessor.cc | 72 | ||||
| -rw-r--r-- | src/inputprocessor.h | 6 | ||||
| -rw-r--r-- | src/instrument.h | 21 | ||||
| -rw-r--r-- | src/memory_heap.h | 113 | ||||
| -rw-r--r-- | src/range.h | 43 | 
13 files changed, 620 insertions, 182 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index aad6cbf..0961f43 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,6 +50,7 @@ libdg_la_SOURCES = \  	drumkit.cc \  	drumkitloader.cc \  	events.cc \ +	events_ds.cc \  	inputprocessor.cc \  	instrument.cc \  	latencyfilter.cc \ @@ -96,6 +97,7 @@ EXTRA_DIST = \  	drumkitloader.h \  	event.h \  	events.h \ +	events_ds.h \  	grid.h \  	inputfilter.h \  	inputprocessor.h \ diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 65af878..6b01be2 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -46,7 +46,7 @@ DrumGizmo::DrumGizmo(Settings& settings,  	, oe(o)  	, ie(i)  	, audio_cache(settings) -	, input_processor(settings, kit, activeevents, rand) +	, input_processor(settings, kit, events_ds, rand)  	, settings(settings)  	, settings_getter(settings)  { @@ -244,7 +244,7 @@ bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples)  	return true;  } -void DrumGizmo::renderSampleEvent(EventSample& evt, int pos, sample_t *s, std::size_t sz) +void DrumGizmo::renderSampleEvent(SampleEvent& evt, int pos, sample_t *s, std::size_t sz)  {  	size_t n = 0; // default start point is 0. @@ -322,85 +322,72 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t* s, size_t sz)  	const auto enable_bleed_control = settings.enable_bleed_control.load();  	const auto master_bleed = settings.master_bleed.load(); -	std::vector< Event* > erase_list; -	std::list< Event* >::iterator i = activeevents[ch].begin(); -	for(; i != activeevents[ch].end(); ++i) +	EventIDs to_remove; +	for(auto& sample_event : events_ds.iterateOver<SampleEvent>(ch))  	{  		bool removeevent = false; -		Event* event = *i; -		Event::type_t type = event->getType(); -		switch(type) +		AudioFile& af = *sample_event.file; + +		if(!af.isLoaded() || !af.isValid() || (s == nullptr)) +		{ +			removeevent = true; +			break; +		} + +		if(sample_event.offset > (pos + sz))  		{ -		case Event::sample: +			// Don't handle event now. It is scheduled for a future iteration. +			continue; +		} + +		if(sample_event.cache_id == CACHE_NOID) +		{ +			size_t initial_chunksize = (pos + sz) - sample_event.offset; +			sample_event.buffer = audio_cache.open(af, initial_chunksize, +										  af.filechannel, sample_event.cache_id); +			if((af.mainState() == main_state_t::is_not_main) && +			   enable_bleed_control)  			{ -				EventSample& evt = *static_cast<EventSample*>(event); -				AudioFile& af = *evt.file; +				sample_event.scale *= master_bleed; +			} -				if(!af.isLoaded() || !af.isValid() || (s == nullptr)) -				{ -					removeevent = true; -					break; -				} +			sample_event.buffer_size = initial_chunksize; +			sample_event.sample_size = af.size; +		} -				if(evt.offset > (pos + sz)) -				{ -					// Don't handle event now. It is scheduled for a future iteration. -					continue; -				} +		{ +			std::lock_guard<std::mutex> guard(af.mutex); -				if(evt.cache_id == CACHE_NOID) -				{ -					size_t initial_chunksize = (pos + sz) - evt.offset; -					evt.buffer = audio_cache.open(af, initial_chunksize, -					                              af.filechannel, evt.cache_id); -					if((af.mainState() == main_state_t::is_not_main) && -					   enable_bleed_control) -					{ -						evt.scale *= master_bleed; -					} - -					evt.buffer_size = initial_chunksize; -					evt.sample_size = af.size; -				} +			renderSampleEvent(sample_event, pos, s, sz); -				{ -					std::lock_guard<std::mutex> guard(af.mutex); - -					renderSampleEvent(evt, pos, s, sz); - -					if((evt.t >= evt.sample_size) || (evt.rampdown_count == 0)) -					{ -						removeevent = true; -					} - -					if(evt.buffer_ptr >= evt.buffer_size && removeevent == false) -					{ -						evt.buffer_size = sz; -						evt.buffer = audio_cache.next(evt.cache_id, evt.buffer_size); -						evt.buffer_ptr = 0; -					} - -					if(removeevent) -					{ -						audio_cache.close(evt.cache_id); -					} -				} +			if((sample_event.t >= sample_event.sample_size) || (sample_event.rampdown_count == 0)) +			{ +				removeevent = true; +			} + +			if(sample_event.buffer_ptr >= sample_event.buffer_size && !removeevent) +			{ +				sample_event.buffer_size = sz; +				sample_event.buffer = audio_cache.next(sample_event.cache_id, sample_event.buffer_size); +				sample_event.buffer_ptr = 0; +			} + +			if(removeevent) +			{ +				audio_cache.close(sample_event.cache_id);  			} -			break;  		}  		if(removeevent)  		{ -			erase_list.push_back(event); // don't delete until we are out of the loop. -			continue; +			to_remove.push_back(sample_event.id); // don't delete until we are out of the loop.  		}  	} -	for(auto& event : erase_list) +	for(auto event_id : to_remove)  	{ -		activeevents[ch].remove(event); -		delete event; +		events_ds.remove(event_id);  	}  } diff --git a/src/drumgizmo.h b/src/drumgizmo.h index 8a423d1..0aeab07 100644 --- a/src/drumgizmo.h +++ b/src/drumgizmo.h @@ -36,6 +36,7 @@  #include "audiooutputengine.h"  #include "audioinputengine.h"  #include "events.h" +#include "events_ds.h"  #include "audiofile.h"  #include "drumkit.h"  #include "drumkitloader.h" @@ -59,7 +60,7 @@ public:  	bool run(size_t pos, sample_t *samples, size_t nsamples);  	void stop(); -	void renderSampleEvent(EventSample& evt, int pos, sample_t *s, std::size_t sz); +	void renderSampleEvent(SampleEvent& evt, int pos, sample_t *s, std::size_t sz);  	void getSamples(int ch, int pos, sample_t *s, size_t sz);  	//! Get the current engine latency in samples. @@ -83,7 +84,7 @@ protected:  	AudioOutputEngine& oe;  	AudioInputEngine& ie; -	std::list< Event* > activeevents[NUM_CHANNELS]; +	EventsDS events_ds;  	bool enable_resampling{true}; diff --git a/src/events.cc b/src/events.cc index 1acbc11..d8ca658 100644 --- a/src/events.cc +++ b/src/events.cc @@ -1,6 +1,6 @@  /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */  /*************************************************************************** - *            event.cc + *            events.cc   *   *  Sat Sep 18 22:02:16 CEST 2010   *  Copyright 2010 Bent Bisballe Nyeng @@ -26,26 +26,4 @@   */  #include "events.h" -void EventQueue::post(Event* event, timepos_t time) -{ -	std::lock_guard<std::mutex> guard(mutex); -	event->offset = time; -	queue.insert(std::pair<timepos_t, Event*>(time, event)); -} - -Event* EventQueue::take(timepos_t time) -{ -	std::lock_guard<std::mutex> guard(mutex); -	std::multimap<timepos_t, Event*>::iterator i = queue.find(time); -	if(i == queue.end()) -		return NULL; -	Event* event = i->second; -	queue.erase(i); -	return event; -} - -bool EventQueue::hasEvent(timepos_t time) -{ -	std::lock_guard<std::mutex> guard(mutex); -	return queue.find(time) != queue.end(); -} +// TODO: remove this file if it isn't needed anymore diff --git a/src/events.h b/src/events.h index 18f9af3..1b3c376 100644 --- a/src/events.h +++ b/src/events.h @@ -1,6 +1,6 @@  /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */  /*************************************************************************** - *            event.h + *            events.h   *   *  Sat Sep 18 22:02:16 CEST 2010   *  Copyright 2010 Bent Bisballe Nyeng @@ -34,39 +34,48 @@  #include "audiofile.h"  #include "audio.h"  #include "audiocache.h" +#include "id.h" -typedef unsigned int timepos_t; +using timepos_t = unsigned int; + +class InputEvent; // just used as template argument for the ID +using InputEventID = ID<InputEvent>; +using InputEventIDs = std::vector<InputEventID>; + +class EventGroup; // just used as template argument for the ID +using EventGroupID = ID<EventGroup>; +using EventGroupIDs = std::vector<EventGroupID>; + +class Event; +using EventID = ID<Event>; +using EventIDs = std::vector<EventID>;  class Event  {  public: -	Event(channel_t channel, timepos_t offset = 0) -		: channel(channel), offset(offset) -	{ -	} +	enum class Type { +		SampleEvent, +	}; -	virtual ~Event() -	{ -	} +	Event(Type type, channel_t channel, timepos_t offset = 0) +		: type(type), channel(channel), offset(offset) {} -	typedef enum -	{ -		sample -	} type_t; - -	virtual type_t getType() const = 0; +	virtual ~Event() {} +	EventID id; +	EventGroupID group_id; +	Type type;  	channel_t channel;  	timepos_t offset; //< Global position (ie. not relative to buffer)  }; -class EventSample +class SampleEvent  	: public Event  {  public: -	EventSample(channel_t c, float g, AudioFile* af, -	            const std::string& grp, std::size_t instrument_id) -		: Event(c) +	SampleEvent(channel_t ch, float g, AudioFile* af, const std::string& grp, +	            std::size_t instrument_id) +		: Event(Event::Type::SampleEvent, ch)  		, cache_id(CACHE_NOID)  		, gain(g)  		, t(0) @@ -78,11 +87,6 @@ public:  	{  	} -	Event::type_t getType() const -	{ -		return Event::sample; -	} -  	bool rampdownInProgress() const  	{  		return rampdown_count != -1; @@ -104,19 +108,3 @@ public:  	float scale{1.0f};  	std::size_t instrument_id;  }; - -class EventQueue -{ -public: -	void post(Event* event, timepos_t time); -	Event* take(timepos_t time); -	bool hasEvent(timepos_t time); -	size_t getSize() const -	{ -		return queue.size(); -	} - -private: -	std::multimap<timepos_t, Event*> queue; -	std::mutex mutex; -}; diff --git a/src/events_ds.cc b/src/events_ds.cc new file mode 100644 index 0000000..988cb21 --- /dev/null +++ b/src/events_ds.cc @@ -0,0 +1,118 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + *            events_ds.cc + * + *  Sun Jan 12 00:17:31 CET 2020 + *  Copyright 2020 André Nusser + *  andre.nusser@googlemail.com + ****************************************************************************/ + +/* + *  This file is part of DrumGizmo. + * + *  DrumGizmo is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  DrumGizmo is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with DrumGizmo; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ +#include "events_ds.h" + +#include <type_traits> + +// TODO: we assume that we add a group in one batch without deleting anything in-between!!! + +void EventsDS::remove(EventID event_id) +{ +	auto const& event_info = id_to_info.get(event_id); + +	if (event_info.type == Event::Type::SampleEvent) +	{ +		auto& sample_events = channel_data_array[event_info.ch].sample_events; +		auto& event = sample_events[event_info.channel_event_index]; + +		// remove from id_to_group_data and delete the group if empty +		auto& ids = id_to_group_data.get(event.group_id).event_ids; +		ids.erase(std::remove(ids.begin(), ids.end(), event_id), ids.end()); +		if (id_to_group_data.get(event.group_id).event_ids.empty()) +		{ +			removeGroup(event.group_id, event.instrument_id); +		} + +		// remove from channel_data_array +		auto& swap_element = sample_events.back(); +		id_to_info.get(swap_element.id).channel_event_index = event_info.channel_event_index; +		event = swap_element; +		sample_events.pop_back(); +	} + +	// remove from id_to_info +	id_to_info.remove(event_id); +} + +std::size_t EventsDS::numberOfEvents(int ch) const +{ +	auto& channel_data = channel_data_array[ch]; +	return channel_data.sample_events.size(); +} + +EventGroupIDs const& EventsDS::getSampleEventGroupIDsOf(InstrumentID instrument_id) const +{ +	return instruments_sample_event_group_ids[instrument_id]; +} + +EventIDs const& EventsDS::getEventIDsOf(EventGroupID event_group_id) const +{ +	return id_to_group_data.get(event_group_id).event_ids; +} + +void EventsDS::startAddingNewGroup(InstrumentID instrument_id) +{ +	// if nothing got added for the last group, delete it +	if (current_group_id.valid() && id_to_group_data.get(current_group_id).event_ids.empty()) +	{ +			removeGroup(current_group_id, current_groups_instrument_id); +	} + +	current_group_id = id_to_group_data.emplace(); +	if (instrument_id.valid()) +	{ +		current_groups_instrument_id = instrument_id; +		auto& group_ids = instruments_sample_event_group_ids[instrument_id]; +		group_ids.push_back(current_group_id); +		id_to_group_data.get(current_group_id).instrument_index = group_ids.size() - 1; +	} +	else { +		current_groups_instrument_id.invalidate(); +	} +} + +void EventsDS::removeGroup(EventGroupID group_id, InstrumentID instrument_id) +{ +	// if we remove the current group, then invalidate it +	if (group_id == current_group_id) +	{ +		current_group_id.invalidate(); +		current_groups_instrument_id.invalidate(); +	} + +	if (instrument_id.valid()) +	{ +		auto index = id_to_group_data.get(group_id).instrument_index; +		auto& ids = instruments_sample_event_group_ids[instrument_id]; + +		id_to_group_data.get(ids.back()).instrument_index = index; +		ids[index] = ids.back(); +		ids.pop_back(); +	} + +	id_to_group_data.remove(group_id); +} diff --git a/src/events_ds.h b/src/events_ds.h new file mode 100644 index 0000000..4a31f47 --- /dev/null +++ b/src/events_ds.h @@ -0,0 +1,135 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + *            events_ds.h + * + *  Sun Jan 12 00:17:31 CET 2020 + *  Copyright 2020 André Nusser + *  andre.nusser@googlemail.com + ****************************************************************************/ + +/* + *  This file is part of DrumGizmo. + * + *  DrumGizmo is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  DrumGizmo is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with DrumGizmo; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ +#pragma once + +#include <config.h> + +#include <algorithm> +#include <array> +#include <vector> + +#include "events.h" +#include "id.h" +#include "memory_heap.h" +#include "range.h" +#include "instrument.h" + +// TODO: make it possible to choose sizes + +struct EventsDS +{ +	// +	// member functions +	// +	EventsDS() = default; + +	template <typename T, typename... Args> +	T& emplace(int ch, Args&&... args); + +	void remove(EventID event_id); + +	std::size_t numberOfEvents(int ch) const; + +	template <typename T> +	ContainerRange<std::vector<T>> iterateOver(int ch); + +	EventGroupIDs const& getSampleEventGroupIDsOf(InstrumentID instrument_id) const; +	EventIDs const& getEventIDsOf(EventGroupID event_group_id) const; + +	void startAddingNewGroup(InstrumentID instrument_id = InstrumentID()); + +private: +	struct ChannelData { +		std::vector<SampleEvent> sample_events; +	}; + +	using ChannelEventIndex = std::size_t; + +	struct EventInfo { +		Event::Type type; +		int ch; +		ChannelEventIndex channel_event_index; + +		EventInfo(Event::Type type, int ch, ChannelEventIndex channel_event_index) +			: type(type), ch(ch), channel_event_index(channel_event_index) {} +	}; +	struct GroupData { +		EventIDs event_ids; +		Event::Type type; + +		// SampleEvent specific data +		std::size_t instrument_index; +	}; + +	// general data +	std::array<ChannelData, NUM_CHANNELS> channel_data_array; +	MemoryHeap<EventInfo> id_to_info; +	MemoryHeap<GroupData> id_to_group_data; + +	// SampleEvent specific data +	// TODO: maybe use something that actually has the size of the number of instruments +	// Also, can we guarantee that there are at most 128 instrument ids? +	std::array<EventGroupIDs, 128> instruments_sample_event_group_ids; + +	EventGroupID current_group_id; +	InstrumentID current_groups_instrument_id; + +	void removeGroup(EventGroupID group_id, InstrumentID instrument_id = InstrumentID()); +}; + +template <typename T, typename... Args> +T& EventsDS::emplace(int ch, Args&&... args) +{ +	if (std::is_same<T, SampleEvent>::value) +	{ +		auto& sample_events = channel_data_array[ch].sample_events; +		auto channel_event_index = sample_events.size(); +		sample_events.emplace_back(std::forward<Args>(args)...); + +		auto event_id = id_to_info.emplace(Event::Type::SampleEvent, ch, channel_event_index); +		id_to_group_data.get(current_group_id).event_ids.push_back(event_id); + +		auto& sample_event = sample_events.back(); +		sample_event.id = event_id; +		sample_event.group_id = current_group_id; +		assert(sample_event.instrument_id == current_groups_instrument_id); + +		return sample_event; +	} + +	assert(false); +} + +template <typename T> +ContainerRange<std::vector<T>> EventsDS::iterateOver(int ch) +{ +	if (std::is_same<T, SampleEvent>::value) +	{ +		auto& sample_events = channel_data_array[ch].sample_events; +		return ContainerRange<std::vector<T>>(sample_events.begin(), sample_events.end()); +	} +} diff --git a/src/id.h b/src/id.h new file mode 100644 index 0000000..384a3cc --- /dev/null +++ b/src/id.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + *            id.h + * + *  Sun 16 Feb 2020 04:17:19 PM CET + *  Copyright 2020 André Nusser + *  andre.nusser@googlemail.com + ****************************************************************************/ + +/* + *  This file is part of DrumGizmo. + * + *  DrumGizmo is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  DrumGizmo is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with DrumGizmo; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ +#pragma once + +#include <cstdint> +#include <functional> +#include <limits> + +// Typesafe ID class such that there are compiler errors if different IDs are +// mixed. The template parameter T is just there to assure this behavior. +// Additionally, we have a member function which can check for validity. +template <typename T> +struct ID +{ +public: +	using IDType = uint32_t; +	static constexpr IDType invalid_value = std::numeric_limits<IDType>::max(); + +	ID(IDType id = invalid_value) : id(id) {} + +	operator IDType() const { return id; } +	IDType operator+(ID<T> other) const { return id + other.id; } +	IDType operator+(int offset) const { return id + offset; } +	IDType operator+(size_t offset) const { return id + offset; } +	IDType operator-(ID<T> other) const { return id - other.id; } +	IDType operator-(int offset) const { return id - offset; } +	IDType operator/(int div) const { return id/div; } +	IDType operator+=(ID<T> other) { return id += other.id; } +	IDType operator-=(ID<T> other) { return id -= other.id; } +	IDType operator=(ID<T> other) { return id = other.id; } +	IDType operator++() { return ++id; } +	IDType operator--() { return --id; } +	bool operator!=(ID<T> other) const { return id != other.id; } + +	bool valid() const { return id != invalid_value; } +	void invalidate() { id = invalid_value; } + +private: +	IDType id; +}; + +// define custom hash function to be able to use IDs with maps/sets +namespace std +{ + +template <typename T> +struct hash<ID<T>> +{ +	using IDType = typename ID<T>::IDType; +	std::size_t operator()(ID<T> const& id) const noexcept +	{ +		return std::hash<IDType>()(id); +	} +}; + +} // std diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc index 3530128..a29ec77 100644 --- a/src/inputprocessor.cc +++ b/src/inputprocessor.cc @@ -40,10 +40,10 @@  InputProcessor::InputProcessor(Settings& settings,                                 DrumKit& kit, -                               std::list<Event*>* activeevents, +                               EventsDS& events_ds,                                 Random& random)  	: kit(kit) -	, activeevents(activeevents) +	, events_ds(events_ds)  	, is_stopping(false)  	, settings(settings)  { @@ -97,7 +97,7 @@ std::size_t InputProcessor::getLatency() const  }  //! Applies choke with rampdown time in ms to event starting at offset. -static void applyChoke(Settings& settings, EventSample& event, +static void applyChoke(Settings& settings, SampleEvent& event,                         double length_ms, timepos_t offset)  {  	std::size_t ramp_length = (length_ms / 1000.) * settings.samplerate.load(); @@ -109,7 +109,7 @@ static void applyChoke(Settings& settings, EventSample& event,  //! Applies choke group actions to active events based on the input event  static void applyChokeGroup(Settings& settings, DrumKit& kit,                              Instrument& instr, event_t& event, -                            std::list<Event*>* activeevents) +                            EventsDS& events_ds)  {  	std::size_t instrument_id = event.instrument;  	if(instr.getGroup() == "") @@ -120,18 +120,14 @@ static void applyChokeGroup(Settings& settings, DrumKit& kit,  	// Add event to ramp down all existing events with the same groupname.  	for(const auto& ch : kit.channels)  	{ -		for(auto active_event : activeevents[ch.num]) +		for(auto& event_sample : events_ds.iterateOver<SampleEvent>(ch.num))  		{ -			if(active_event->getType() == Event::sample) +			if(event_sample.group == instr.getGroup() && +			   event_sample.instrument_id != instrument_id && +			   event_sample.rampdown_count == -1) // Only if not already ramping.  			{ -				auto& event_sample = *static_cast<EventSample*>(active_event); -				if(event_sample.group == instr.getGroup() && -				   event_sample.instrument_id != instrument_id && -				   event_sample.rampdown_count == -1) // Only if not already ramping. -				{ -					// Fixed group rampdown time of 68ms, independent of samplerate -					applyChoke(settings, event_sample, 68, event.offset); -				} +				// Fixed group rampdown time of 68ms, independent of samplerate +				applyChoke(settings, event_sample, 68, event.offset);  			}  		}  	} @@ -140,24 +136,20 @@ static void applyChokeGroup(Settings& settings, DrumKit& kit,  //! Applies directed choke actions to active events based on the input event  static void applyDirectedChoke(Settings& settings, DrumKit& kit,                                 Instrument& instr, event_t& event, -                               std::list<Event*>* activeevents) +                               EventsDS& events_ds)  {  	for(const auto& choke : instr.getChokes())  	{  		// Add event to ramp down all existing events with the same groupname.  		for(const auto& ch : kit.channels)  		{ -			for(auto active_event : activeevents[ch.num]) +			for(auto& event_sample : events_ds.iterateOver<SampleEvent>(ch.num))  			{ -				if(active_event->getType() == Event::sample) +				if(choke.instrument_id == event_sample.instrument_id && +				   event_sample.rampdown_count == -1) // Only if not already ramping.  				{ -					auto& event_sample = *static_cast<EventSample*>(active_event); -					if(choke.instrument_id == event_sample.instrument_id && -					   event_sample.rampdown_count == -1) // Only if not already ramping. -					{ -						// choke.choketime is in ms -						applyChoke(settings, event_sample, choke.choketime, event.offset); -					} +					// choke.choketime is in ms +					applyChoke(settings, event_sample, choke.choketime, event_sample.offset);  				}  			}  		} @@ -200,10 +192,10 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,  	}  	// Mute other instruments in the same group -	applyChokeGroup(settings, kit, *instr, event, activeevents); +	applyChokeGroup(settings, kit, *instr, event, events_ds);  	// Apply directed chokes to mute other instruments if needed -	applyDirectedChoke(settings, kit, *instr, event, activeevents); +	applyDirectedChoke(settings, kit, *instr, event, events_ds);  	auto const power_max = instr->getMaxPower();  	auto const power_min = instr->getMinPower(); @@ -220,6 +212,7 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,  	auto const selected_level = (sample->getPower() - power_min)/power_span;  	settings.velocity_modifier_current.store(selected_level/original_level); +	events_ds.startAddingNewGroup(instrument_id);  	for(Channel& ch: kit.channels)  	{  		const auto af = sample->getAudioFile(ch); @@ -230,15 +223,14 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,  		else  		{  			//DEBUG(inputprocessor, "Adding event %d.\n", event.offset); -			auto evt = new EventSample(ch.num, 1.0, af, instr->getGroup(), -			                           instrument_id); -			evt->offset = (event.offset + pos) * resample_ratio; +			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())  			{ -				evt->scale *= event.velocity; +				event_sample.scale *= event.velocity;  			} - -			activeevents[ch.num].push_back(evt);  		}  	} @@ -282,17 +274,13 @@ bool InputProcessor::processChoke(event_t& event,  	// Add event to ramp down all existing events with the same groupname.  	for(const auto& ch : kit.channels)  	{ -		for(auto active_event : activeevents[ch.num]) +		for(auto& event_sample : events_ds.iterateOver<SampleEvent>(ch.num))  		{ -			if(active_event->getType() == Event::sample) +			if(event_sample.instrument_id == instrument_id && +			   event_sample.rampdown_count == -1) // Only if not already ramping.  			{ -				auto& event_sample = *static_cast<EventSample*>(active_event); -				if(event_sample.instrument_id == instrument_id && -				   event_sample.rampdown_count == -1) // Only if not already ramping. -				{ -					// Fixed group rampdown time of 68ms, independent of samplerate -					applyChoke(settings, event_sample, 68, event.offset); -				} +				// Fixed group rampdown time of 68ms, independent of samplerate +				applyChoke(settings, event_sample, 68, event_sample.offset);  			}  		}  	} @@ -313,7 +301,7 @@ bool InputProcessor::processStop(event_t& event)  		int num_active_events = 0;  		for(auto& ch: kit.channels)  		{ -			num_active_events += activeevents[ch.num].size(); +			num_active_events += events_ds.numberOfEvents(ch.num);  		}  		if(num_active_events == 0) diff --git a/src/inputprocessor.h b/src/inputprocessor.h index 2101a25..98623d5 100644 --- a/src/inputprocessor.h +++ b/src/inputprocessor.h @@ -34,6 +34,8 @@  #include "drumkit.h"  #include "events.h" +#include "events_ds.h" +#include "id.h"  #include "inputfilter.h"  struct Settings; @@ -44,7 +46,7 @@ class InputProcessor  public:  	InputProcessor(Settings& settings,  	               DrumKit& kit, -	               std::list<Event*>* activeevents, +	               EventsDS& events_ds,  	               Random& random);  	bool process(std::vector<event_t>& events, @@ -55,7 +57,7 @@ public:  private:  	DrumKit& kit; -	std::list<Event*>* activeevents; +	EventsDS& events_ds;  	bool is_stopping; ///< Is set to true when a EventType::Stop event has been seen.  	bool processOnset(event_t& event, std::size_t pos, double resample_ratio); diff --git a/src/instrument.h b/src/instrument.h index 2cb0813..c06ccdc 100644 --- a/src/instrument.h +++ b/src/instrument.h @@ -31,21 +31,17 @@  #include <memory>  #include <deque> -#include "rangemap.h" // for v1.0 kits +#include "id.h"  #include "powerlist.h" +#include "rangemap.h" // for v1.0 kits +#include "random.h"  #include "sample_selection.h" -  #include "sample.h"  #include "versionstr.h" -#include "random.h"  #include "settings.h" -struct Choke -{ -	std::size_t instrument_id; -	double choketime; -}; +struct Choke;  class Instrument  { @@ -109,5 +105,12 @@ private:  	SampleSelection sample_selection;  }; -// typedef std::map< std::string, Instrument > Instruments;  using Instruments = std::vector<std::unique_ptr<Instrument>>; +using InstrumentID = ID<Instrument>; +using InstrumentIDs = std::vector<InstrumentID>; + +struct Choke +{ +	std::size_t instrument_id; +	double choketime; +}; diff --git a/src/memory_heap.h b/src/memory_heap.h new file mode 100644 index 0000000..e2901b8 --- /dev/null +++ b/src/memory_heap.h @@ -0,0 +1,113 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + *            memory_heap.h + * + *  Sun Jan 19 13:49:56 CET 2020 + *  Copyright 2020 André Nusser + *  andre.nusser@googlemail.com + ****************************************************************************/ + +/* + *  This file is part of DrumGizmo. + * + *  DrumGizmo is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  DrumGizmo is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with DrumGizmo; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ +#pragma once + +#include <vector> + +// The purpose of this class is to have a heap for objects of a certain type. +// We want to avoid using the general "new"/"delete" memory allocation as this +// is problematic for real-time as well as slow. The underlying container still +// is a vector to handle the case when for some reason we need more memory +// than initially anticipated. Instead of throwing errors and giving up, we can +// then at least allocate new memory and just hope for the best. +template <typename T> +class MemoryHeap +{ +public: +	using Index = std::size_t; +	using Indices = std::vector<Index>; + +	MemoryHeap() = default; +	MemoryHeap(std::size_t size) +	{ +		memory.reserve(size); +		free_indices.reserve(size); +	} + +	template <typename... Args> +	Index emplace(Args&&... args); + +	Index add(T const& element); +	T& get(Index index); +	T const& get(Index index) const; +	void remove(Index index); + +private: +	std::vector<T> memory; +	Indices free_indices; +}; + +template <typename T> +auto MemoryHeap<T>::add(T const& element) -> Index +{ +	if (free_indices.empty()) +	{ +		memory.push_back(element); +		return memory.size()-1; +	} + +	auto free_index = free_indices.back(); +	free_indices.pop_back(); +	memory[free_index] = element; +	return free_index; +} + +template <typename T> +template <typename... Args> +auto MemoryHeap<T>::emplace(Args&&... args) -> Index +{ +	if (free_indices.empty()) +	{ +		memory.emplace_back(std::forward<Args>(args)...); +		return memory.size()-1; +	} + +	auto free_index = free_indices.back(); +	free_indices.pop_back(); +	memory[free_index] = T(std::forward<Args>(args)...); +	return free_index; +} + +template <typename T> +T& MemoryHeap<T>::get(Index index) +{ +	assert(index < memory.size()); +	return memory[index]; +} + +template <typename T> +T const& MemoryHeap<T>::get(Index index) const +{ +	assert(index < memory.size()); +	return memory[index]; +} + +template <typename T> +void MemoryHeap<T>::remove(Index index) +{ +	free_indices.push_back(index); +} diff --git a/src/range.h b/src/range.h new file mode 100644 index 0000000..0527577 --- /dev/null +++ b/src/range.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + *            range.h + * + *  Sun 16 Feb 2020 04:17:19 PM CET + *  Copyright 2020 André Nusser + *  andre.nusser@googlemail.com + ****************************************************************************/ + +/* + *  This file is part of DrumGizmo. + * + *  DrumGizmo is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  DrumGizmo is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with DrumGizmo; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ +#pragma once + +// Having the iterator as template parameter enables us to also +// define ContainerRanges with const_iterators. +template <typename T, typename iterator = typename T::iterator> +struct ContainerRange +{ +	ContainerRange(iterator begin, iterator end) +		: _begin(begin), _end(end) {}; + +	iterator begin() const { return _begin; }; +	iterator end() const { return _end; }; + +private: +	iterator const _begin; +	iterator const _end; +}; | 
