From fde5c6661d296a79d6d6a8a1bcd888d2182deb8d Mon Sep 17 00:00:00 2001 From: TheMarlboroMan Date: Mon, 27 Jan 2020 18:37:01 +0100 Subject: Small changes to source, added disposable logger, basic functionality, untested --- src/Makefile.am | 2 + src/drumgizmo.cc | 1 + src/events.h | 9 +- src/inputprocessor.cc | 236 ++++++++++++++++++++++++++++++++++++-------------- src/inputprocessor.h | 6 +- src/tracer.cc | 37 ++++++++ src/tracer.h | 84 ++++++++++++++++++ 7 files changed, 305 insertions(+), 70 deletions(-) create mode 100644 src/tracer.cc create mode 100644 src/tracer.h diff --git a/src/Makefile.am b/src/Makefile.am index aad6cbf..44500e7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -63,6 +63,7 @@ libdg_la_SOURCES = \ sem.cc \ staminafilter.cc \ thread.cc \ + tracer.cc \ velocityfilter.cc \ versionstr.cc @@ -118,6 +119,7 @@ EXTRA_DIST = \ staminafilter.h \ syncedsettings.h \ thread.h \ + tracer.h \ velocityfilter.h \ versionstr.h \ zrwrapper.h diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 65af878..591fcda 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/src/events.h b/src/events.h index 18f9af3..f005566 100644 --- a/src/events.h +++ b/src/events.h @@ -65,7 +65,10 @@ class EventSample { public: EventSample(channel_t c, float g, AudioFile* af, - const std::string& grp, std::size_t instrument_id) + const std::string& grp, + std::size_t instrument_id, + std::size_t insert_group_id +) : Event(c) , cache_id(CACHE_NOID) , gain(g) @@ -75,6 +78,7 @@ public: , rampdown_count(-1) , ramp_length(0) , instrument_id(instrument_id) + , insert_group_id(insert_group_id) { } @@ -92,7 +96,7 @@ public: sample_t* buffer; std::size_t buffer_size; std::size_t buffer_ptr{0}; //< Internal pointer into the current buffer - std::size_t sample_size{0}; //< Total number of audio samples in this sample. + std::size_t sample_size{0}; //< Total number of audio samples in this sample. float gain; unsigned int t; //< Internal sample position. @@ -103,6 +107,7 @@ public: std::size_t rampdown_offset{0}; float scale{1.0f}; std::size_t instrument_id; + std::size_t insert_group_id; //< All EventSample instances created from the same event_t will share this value. }; class EventQueue diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc index 3530128..4514f38 100644 --- a/src/inputprocessor.cc +++ b/src/inputprocessor.cc @@ -3,7 +3,7 @@ * inputprocessor.cc * * Sat Apr 23 20:39:30 CEST 2016 - * Copyright 2016 André Nusser + * Copyright 2016 Andr� Nusser * andre.nusser@googlemail.com ****************************************************************************/ @@ -29,6 +29,8 @@ #include #include +#include +#include #include "instrument.h" @@ -38,63 +40,12 @@ #include "cpp11fix.h" -InputProcessor::InputProcessor(Settings& settings, - DrumKit& kit, - std::list* activeevents, - Random& random) - : kit(kit) - , activeevents(activeevents) - , is_stopping(false) - , settings(settings) -{ - // Build filter list - filters.emplace_back(std::make_unique(settings)); - filters.emplace_back(std::make_unique(settings, random)); - filters.emplace_back(std::make_unique(settings, random)); -} - -bool InputProcessor::process(std::vector& events, - std::size_t pos, - double resample_ratio) -{ - for(auto& event: events) - { - if(event.type == EventType::OnSet) - { - if(!processOnset(event, pos, resample_ratio)) - { - continue; - } - } - - if(event.type == EventType::Choke) - { - if(!processChoke(event, pos, resample_ratio)) - { - continue; - } - } - - if(!processStop(event)) - { - return false; - } - } - - return true; -} +//TODO: This is just for development purposes. Remove when done. +#include "tracer.h" -std::size_t InputProcessor::getLatency() const +//Anonymous namespace for local functions. +namespace { - std::size_t latency = 0; - - for(const auto& filter : filters) - { - latency += filter->getLatency(); - } - - return latency; -} //! Applies choke with rampdown time in ms to event starting at offset. static void applyChoke(Settings& settings, EventSample& event, @@ -164,10 +115,74 @@ static void applyDirectedChoke(Settings& settings, DrumKit& kit, } } +} //End of anonymous namespace. + +InputProcessor::InputProcessor(Settings& settings, + DrumKit& kit, + std::list* activeevents, + Random& random) + : kit(kit) + , activeevents(activeevents) + , is_stopping(false) + , settings(settings) + , insert_group_id(0) +{ + tracer::trace("building InputProcessor", '\n'); + + // Build filter list + filters.emplace_back(std::make_unique(settings)); + filters.emplace_back(std::make_unique(settings, random)); + filters.emplace_back(std::make_unique(settings, random)); +} + +bool InputProcessor::process(std::vector& events, + std::size_t pos, + double resample_ratio) +{ + for(auto& event: events) + { + if(event.type == EventType::OnSet) + { + if(!processOnset(event, pos, resample_ratio)) + { + continue; + } + } + + if(event.type == EventType::Choke) + { + if(!processChoke(event, pos, resample_ratio)) + { + continue; + } + } + + if(!processStop(event)) + { + return false; + } + } + + return true; +} + +std::size_t InputProcessor::getLatency() const +{ + std::size_t latency = 0; + + for(const auto& filter : filters) + { + latency += filter->getLatency(); + } + + return latency; +} bool InputProcessor::processOnset(event_t& event, std::size_t pos, double resample_ratio) { + tracer::trace("processOnset was called", '\n'); + if(!kit.isValid()) { return false; @@ -198,6 +213,11 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos, return false; // Skip event completely } } + + //TODO: A lookup map would be much better... + const size_t max_voices=getMaxVoicesForInstrument(instrument_id); + //TODO: Somehow this seems like a very specific case of a group. + applyVoiceLimit(event, max_voices); // Mute other instruments in the same group applyChokeGroup(settings, kit, *instr, event, activeevents); @@ -220,26 +240,29 @@ 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); + ++insert_group_id; //Insert group id 0 is not allowed. for(Channel& ch: kit.channels) { const auto af = sample->getAudioFile(ch); if(af == nullptr || !af->isValid()) { //DEBUG(inputprocessor, "Missing AudioFile.\n"); + continue; } - else + + //DEBUG(inputprocessor, "Adding event %d.\n", event.offset); + auto evt = new EventSample(ch.num, 1.0, af, instr->getGroup(), + instrument_id, + insert_group_id); + evt->offset = (event.offset + pos) * resample_ratio; + if(settings.normalized_samples.load() && sample->getNormalized()) { - //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; - if(settings.normalized_samples.load() && sample->getNormalized()) - { - evt->scale *= event.velocity; - } - - activeevents[ch.num].push_back(evt); + evt->scale *= event.velocity; } + + tracer::trace("event added to channel ", ch.num, " for instrument id ", instrument_id, '\n'); + + activeevents[ch.num].push_back(evt); } return true; @@ -325,3 +348,82 @@ bool InputProcessor::processStop(event_t& event) return true; } + +//TODO: Document. +void InputProcessor::applyVoiceLimit(const event_t& event, size_t max_voices) +{ + //Find out how many voices for the instrument we are currently playing... + const auto instrument_id = event.instrument; + size_t current_count{0}; + + tracer::trace("applyVoiceLimit will count for instrument id ", instrument_id, '\n'); + + for(const auto& ch : kit.channels) + { + current_count+=std::count_if(std::begin(activeevents[ch.num]), + std::end(activeevents[ch.num]), + [instrument_id](Event const * active_event) + { + return active_event->getType() == Event::sample + && static_cast(active_event)->instrument_id == instrument_id; + }); + } + + //Early exit if the max number of voices has yet to be reached... + tracer::trace("found ", current_count, " for instrument id ", instrument_id, '\n'); + if(current_count <= max_voices) + { + return; + } + + //Locate the earliest sample group for this instrument that is not ramping. + tracer::trace("locating earliest sample group\n"); + std::size_t earliest_group{0}; + + for(const auto& ch : kit.channels) + { + for(const auto active_event : activeevents[ch.num]) + { + if(active_event->getType() == Event::sample) + { + auto& event_sample = *static_cast(active_event); + + //Same instrument, not ramping, with a lesser group. + if(event_sample.instrument_id != instrument_id + && -1 == event_sample.rampdown_count + //TODO: Not proud of this, not proud of it at all. + && (event_sample.insert_group_id < earliest_group + || 0 == earliest_group)) + { + earliest_group=event_sample.insert_group_id; + } + } + } + } + + tracer::trace("earliest sample group was ", earliest_group, '\n'); + + //Ramp down everyone belonging to that group... + for(const auto& ch : kit.channels) + { + for(auto active_event : activeevents[ch.num]) + { + if(active_event->getType() == Event::sample) + { + auto& event_sample = *static_cast(active_event); + if(event_sample.insert_group_id == earliest_group) + { + tracer::trace("found and ramping in channel ", ch.num, '\n'); + applyChoke(settings, event_sample, 68, event.offset); + } + } + } + } +} + +//TODO: Document. +size_t InputProcessor::getMaxVoicesForInstrument(size_t instrument_id) const +{ + //TODO: This would depend on the configuration set on the GUI. + return 16; +} \ No newline at end of file diff --git a/src/inputprocessor.h b/src/inputprocessor.h index 2101a25..5516146 100644 --- a/src/inputprocessor.h +++ b/src/inputprocessor.h @@ -3,7 +3,7 @@ * inputprocessor.h * * Sat Apr 23 20:39:30 CEST 2016 - * Copyright 2016 André Nusser + * Copyright 2016 Andr� Nusser * andre.nusser@googlemail.com ****************************************************************************/ @@ -61,8 +61,12 @@ private: bool processOnset(event_t& event, std::size_t pos, double resample_ratio); bool processChoke(event_t& event, std::size_t pos, double resample_ratio); bool processStop(event_t& event); + void applyVoiceLimit(const event_t& event, size_t max_voices); + size_t getMaxVoicesForInstrument(size_t instrument_id) const; std::vector> filters; Settings& settings; + + size_t insert_group_id; ///< Identifier for all Events added in all different channels from the same event_t. }; diff --git a/src/tracer.cc b/src/tracer.cc new file mode 100644 index 0000000..adc621f --- /dev/null +++ b/src/tracer.cc @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * tracer.cc + * + * Sun Dec 29 18:04:28 CET 2019 + * Copyright 2019 Daniel Pastor + * marlborometal@gmail.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 "tracer.h" + +//Instantiation of the static file stream. +std::unique_ptr tracer::tracer_manager::outfile=nullptr; + +void tracer::trace() +{ + std::flush(tracer_manager::get()); + return; +} \ No newline at end of file diff --git a/src/tracer.h b/src/tracer.h new file mode 100644 index 0000000..e42c29e --- /dev/null +++ b/src/tracer.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * tracer.h + * + * Sun Dec 29 18:04:28 CET 2019 + * Copyright 2019 Daniel Pastor + * marlborometal@gmail.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 +#include +#include + +//! This file contains the trace tool for developmnent purposes. The trace tool +//! just prints messages to a given file. +namespace tracer +{ + +//! This class takes care of the output stream where trace messages will be +//! printed. Its methods are not supposed to be called by anyone else than the +//! "trace" functions. +class tracer_manager +{ + public: + + //! Returns a reference to the file stream. + static std::ofstream& get() + { + if(nullptr==outfile.get()) + { +//TODO: This should not be fixed, but either depend on the OS or be able to +//specified somehow. + outfile.reset(new std::ofstream("/tmp/drumgizmo.trace", + std::ios::app)); + } + + if(nullptr==outfile.get()) + { + throw std::runtime_error("the tracer file could not be initialized"); + } + + return *(outfile.get()); + } + + private: + + //! Single output file. Will be initialized to nullptr when the program + //! starts and freed when the program ends. + static std::unique_ptr outfile; +}; + +//! Trace function without parameters, used on the last call to the variadic +//! templace "trace". +void trace(); + +//! Variadic trace function. Obtains a reference to the file stream and prints +//! its arguments there. +template +void trace(T val, Args... args) +{ + tracer_manager::get()<