diff options
author | André Nusser <andre.nusser@googlemail.com> | 2020-01-12 15:41:39 +0100 |
---|---|---|
committer | André Nusser <andre.nusser@googlemail.com> | 2020-02-16 19:57:13 +0100 |
commit | fb992677cf62b6aa982476538faae43e5bbbd87f (patch) | |
tree | 8973b63653e2acbecb2b273444b4dc67dc623fd8 /src | |
parent | 8070e5578aa9d185f07534778dc0ebf596fece13 (diff) |
Introduce EventsDS to handle all the events and enable new features.
Also:
* Added an id.h class to make IDs with type
* Added a range class to easily use range based for loops
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; +}; |