summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndré Nusser <andre.nusser@googlemail.com>2020-01-12 15:41:39 +0100
committerAndré Nusser <andre.nusser@googlemail.com>2020-02-16 19:57:13 +0100
commitfb992677cf62b6aa982476538faae43e5bbbd87f (patch)
tree8973b63653e2acbecb2b273444b4dc67dc623fd8
parent8070e5578aa9d185f07534778dc0ebf596fece13 (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
-rw-r--r--plugin/Makefile.mingw32.in1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/drumgizmo.cc113
-rw-r--r--src/drumgizmo.h5
-rw-r--r--src/events.cc26
-rw-r--r--src/events.h68
-rw-r--r--src/events_ds.cc118
-rw-r--r--src/events_ds.h135
-rw-r--r--src/id.h80
-rw-r--r--src/inputprocessor.cc72
-rw-r--r--src/inputprocessor.h6
-rw-r--r--src/instrument.h21
-rw-r--r--src/memory_heap.h113
-rw-r--r--src/range.h43
14 files changed, 621 insertions, 182 deletions
diff --git a/plugin/Makefile.mingw32.in b/plugin/Makefile.mingw32.in
index 24975b1..570ecd7 100644
--- a/plugin/Makefile.mingw32.in
+++ b/plugin/Makefile.mingw32.in
@@ -29,6 +29,7 @@ DG_SRC = \
@top_srcdir@/src/drumkit.cc \
@top_srcdir@/src/drumkitloader.cc \
@top_srcdir@/src/events.cc \
+ @top_srcdir@/src/events_ds.cc \
@top_srcdir@/src/inputprocessor.cc \
@top_srcdir@/src/instrument.cc \
@top_srcdir@/src/latencyfilter.cc \
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;
+};