diff options
68 files changed, 1053 insertions, 614 deletions
@@ -1,3 +1,82 @@ +Version 0.9.20 +============== +Release date: September 8th 2022 +Contributors: chaot4, corrados, trebmuh, deva + +This release was originally intended as a bugfix release, but quite a few +features also managed to find their way in. + +Noteworthy bugs that has been fixed: + - Software regression error in cymbal choke code has been fixed. So now + choking of cymbals using both directed chokes and after-touch midi + events work again as expected. + - A compilation error due to an update in LV2 API (we used an + obsolete LV2 type) has been fixed, so now compilation should work for + everybody again. + - Code can now properly be compiled with NSL support disabled at + configure time. + - Compilation with the modern gcc-11 compiler has been fixed. + +On top of this a lot of crash-bugs has been fixed. + +Thanks to corrados, this version adds support for midi input through +the alsa seq driver for the command line version of drumgizmo. So now +drumgizmo can be run from the commandline entirely without the need +for jack. + +Compilation Bugs: + - Fix compilation with gcc-11 + - Fix compilation issue when compiling without nls enabled. + - Remove sndfile.h from audiofile header + - Fix missing backslash in libdg includepaths + - Fix compiler warning. + - Add missing include + - Make configure fail if nls is enabled but gettext tools not found. + +Crash Bugs: + - Fix crash when loading a kit with more channels than the engine + was compiled for. + - Fix crash when loading Crocell kit in ardour (stack allocation + issue) - see Ardour bug report. + - Get rid of big stack allocation during resampler (re-)configuration. + - Prevent processing jack clients that are being deleted. + - Fix crash when pressing play while loading a drumkit. + - Skip events whose audio-files has not yet been loaded. + - Fix crash when doing async-load in cli. + +Other Bugs: + - Fixed directed-chokes regression error. + - Fix after-touch chokes regression error. + - Fix wrong sample selection with instruments having only two samples. + - Make aftertouch choke if velocities > 0 instead of == 0 as this + seem to be the vdrum vendor concensus. + - Clear all active events (ie. stop playing samples) when loading a new kit. + - Fix ALSA output frame size change. + - added getBufferSize function for alsa out + +Features: + - Record and measure real cymbal choke-time and use this value + instead of the current 68ms. + - Adjust choke ramp-down time to better reflect the actual + dampening time of a hand-dampened cymbal. + - Add support for triggering multiple instruments with one midi note. + - Add voice-limit parameters to cli + - Add setting for controlling ALSA periods to output module. + - Add ALSA MIDI seq input support + +Other: + - Add unit-test for EventsDS::clear() method. + - Add clear member function to EventsDS. + - Add assert to break infinite loop in case of a bug. + - Add -Wextra to debug compilation flags. + - Make it possible to (only) build unit-test in parrallel. + - Add dggui namespace to libdggui components. + - Split UI code into application/plugin UI and UI library. + - Run and fix unit-tests on windows. + - Make rcgen compile and work on windows again through autotools. + - Improve cocoa/macOS rendering speed by removing redraw on all events. + - Reduce UI window height to fit on low-res displays. + Version 0.9.19 ============== Release date: November 22nd 2020 diff --git a/RELEASE-CHECKLIST b/RELEASE-CHECKLIST index eb51141..a46ffc7 100644 --- a/RELEASE-CHECKLIST +++ b/RELEASE-CHECKLIST @@ -4,7 +4,7 @@ Check list for releasing: [ ] Update man page - [ ] Bump version in version.h + [ ] Bump version in version.h and in man pages and po/pot files. [ ] Update ChangeLog @@ -16,6 +16,7 @@ mkdir -p actest cat << EOF > actest/configure.ac AC_INIT([actest], [1.0.0]) AC_PROG_OBJCXX +AC_OUTPUT([]) EOF [ -f acinclude.m4 ] && rm acinclude.m4 autoreconf -W error actest 2>/dev/null || echo "AC_DEFUN([AC_PROG_OBJCXX],[echo ' - ObjC++ hack - not support by this platform, but not needed either.'])" > acinclude.m4 diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc index 8eba4c9..bca55d2 100644 --- a/drumgizmo/drumgizmoc.cc +++ b/drumgizmo/drumgizmoc.cc @@ -46,8 +46,6 @@ #include "enginefactory.h" #include "bytesizeparser.h" -#include "event.h" - #include "nolocale.h" struct ParmToken diff --git a/drumgizmo/input/jackmidi.cc b/drumgizmo/input/jackmidi.cc index 445678b..7081bf1 100644 --- a/drumgizmo/input/jackmidi.cc +++ b/drumgizmo/input/jackmidi.cc @@ -122,6 +122,6 @@ void JackMidiInputEngine::process(jack_nframes_t num_frames) jack_midi_event_get(&event, buffer, i); processNote(event.buffer, event.size, event.time, events); } - jack_midi_clear_buffer(buffer); + pos += num_frames; } diff --git a/drumgizmo/input/midifile.h b/drumgizmo/input/midifile.h index 5756718..a8cf574 100644 --- a/drumgizmo/input/midifile.h +++ b/drumgizmo/input/midifile.h @@ -25,9 +25,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #pragma once + #include <string> -#include <event.h> #include <smf.h> #include "audioinputenginemidi.h" diff --git a/drumgizmo/input/test.cc b/drumgizmo/input/test.cc index 955d218..bc88c48 100644 --- a/drumgizmo/input/test.cc +++ b/drumgizmo/input/test.cc @@ -27,7 +27,6 @@ #include <stdlib.h> #include <string> -#include "event.h" #include "test.h" TestInputEngine::TestInputEngine() diff --git a/drumgizmo/input/test.h b/drumgizmo/input/test.h index baf7c95..f1e0b42 100644 --- a/drumgizmo/input/test.h +++ b/drumgizmo/input/test.h @@ -27,7 +27,6 @@ #pragma once #include <string> -#include "event.h" #include "audioinputengine.h" class TestInputEngine diff --git a/plugin/Makefile.mingw32.in b/plugin/Makefile.mingw32.in index ad47bcc..9cfcc9e 100644 --- a/plugin/Makefile.mingw32.in +++ b/plugin/Makefile.mingw32.in @@ -39,7 +39,7 @@ DG_SRC = \ @top_srcdir@/src/midimapper.cc \ @top_srcdir@/src/path.cc \ @top_srcdir@/src/powerlist.cc \ - @top_srcdir@/src/powermap.cc \ + @top_srcdir@/src/curvemap.cc \ @top_srcdir@/src/powermapfilter.cc \ @top_srcdir@/src/random.cc \ @top_srcdir@/src/sample.cc \ diff --git a/plugin/plugingizmo b/plugin/plugingizmo -Subproject be64ddf9da525cd5c6757464efc966052731ba7 +Subproject a88c76afd8fbfe31b76010bac34c1437b192724 diff --git a/plugingui/diskstreamingframecontent.cc b/plugingui/diskstreamingframecontent.cc index 4c63817..5420afd 100644 --- a/plugingui/diskstreamingframecontent.cc +++ b/plugingui/diskstreamingframecontent.cc @@ -95,7 +95,7 @@ void DiskstreamingframeContent::resize(std::size_t width, std::size_t height) void DiskstreamingframeContent::limitSettingsValueChanged(std::size_t value) { - float new_slider_value = (float)(value - min_limit)/(max_limit - min_limit); + float new_slider_value = (float)(value - min_limit)/(float)(max_limit - min_limit); slider.setValue(new_slider_value); if(new_slider_value < 0.99) @@ -116,7 +116,7 @@ void DiskstreamingframeContent::limitSettingsValueChanged(std::size_t value) void DiskstreamingframeContent::limitValueChanged(float value) { std::size_t new_limit = value < 0.99 ? - value * (max_limit - min_limit) + min_limit : + value * (float)(max_limit - min_limit) + min_limit : std::numeric_limits<std::size_t>::max(); settings.disk_cache_upper_limit.store(new_limit); diff --git a/plugingui/locale/da.po b/plugingui/locale/da.po index e25235f..e3904d0 100644 --- a/plugingui/locale/da.po +++ b/plugingui/locale/da.po @@ -6,7 +6,7 @@ # msgid "" msgstr "" -"Project-Id-Version: drumgizmo 0.9.17\n" +"Project-Id-Version: drumgizmo 0.9.20\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-09-13 21:07+0200\n" "PO-Revision-Date: 2019-09-13 19:42+0200\n" diff --git a/plugingui/locale/drumgizmo.pot b/plugingui/locale/drumgizmo.pot index 72e600e..e93e387 100644 --- a/plugingui/locale/drumgizmo.pot +++ b/plugingui/locale/drumgizmo.pot @@ -6,7 +6,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: drumgizmo 0.9.19\n" +"Project-Id-Version: drumgizmo 0.9.20\n" "Report-Msgid-Bugs-To: \n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" diff --git a/plugingui/locale/fr.po b/plugingui/locale/fr.po index b46174c..c2fbb63 100644 --- a/plugingui/locale/fr.po +++ b/plugingui/locale/fr.po @@ -7,7 +7,7 @@ # msgid "" msgstr "" -"Project-Id-Version: drumgizmo 0.9.18.1\n" +"Project-Id-Version: drumgizmo 0.9.20\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-09-13 21:07+0200\n" "PO-Revision-Date: 2020-11-21 16:49+0200\n" diff --git a/plugingui/powerwidget.cc b/plugingui/powerwidget.cc index 9be0c48..71ad74b 100644 --- a/plugingui/powerwidget.cc +++ b/plugingui/powerwidget.cc @@ -31,7 +31,6 @@ #include <notifier.h> #include <settings.h> -#include <powermap.h> #include <hugin.hpp> #include <cmath> @@ -120,7 +119,7 @@ PowerWidget::Canvas::Canvas(dggui::Widget* parent, void PowerWidget::Canvas::repaintEvent(dggui::RepaintEvent *repaintEvent) { - if(width() < 1 || height() < 1) + if(width() < 4 || height() < 4) { return; } diff --git a/plugingui/powerwidget.h b/plugingui/powerwidget.h index 3a7bb8e..3d09e6b 100644 --- a/plugingui/powerwidget.h +++ b/plugingui/powerwidget.h @@ -34,7 +34,7 @@ #include <dggui/label.h> #include <dggui/font.h> -#include <powermap.h> +#include <curvemap.h> struct Settings; class SettingsNotifier; @@ -75,7 +75,7 @@ private: virtual void mouseLeaveEvent() override; private: - Powermap power_map; + CurveMap power_map; void parameterChangedFloat(float); void parameterChangedBool(bool); diff --git a/plugingui/resamplingframecontent.cc b/plugingui/resamplingframecontent.cc index d92dc27..6e631c7 100644 --- a/plugingui/resamplingframecontent.cc +++ b/plugingui/resamplingframecontent.cc @@ -30,6 +30,8 @@ #include <translation.h> +#include <algorithm> + namespace GUI { @@ -70,9 +72,9 @@ ResamplingframeContent::ResamplingframeContent(dggui::Widget* parent, void ResamplingframeContent::resize(std::size_t width, std::size_t height) { Widget::resize(width, height); - text_field.resize(width - 50, height); - quality_knob.move(width - 36, 20); - quality_label.move(width - 40, 0); + text_field.resize(std::max(width, std::size_t{51}) - 50, height); + quality_knob.move(std::max(width, std::size_t{37}) - 36, 20); + quality_label.move(std::max(width, std::size_t{41}) - 40, 0); } void ResamplingframeContent::updateContent() diff --git a/src/Makefile.am b/src/Makefile.am index 85b10fa..e2a2583 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -33,49 +33,50 @@ libdg_la_CPPFLAGS = \ $(DEBUG_FLAGS) \ -I$(top_srcdir)/hugin -I$(top_srcdir)/pugixml/src \ $(SSEFLAGS) -I$(top_srcdir)/zita-resampler/libs \ - $(SNDFILE_CFLAGS) $(PTHREAD_CFLAGS) + $(SNDFILE_CFLAGS) $(PTHREAD_CFLAGS) \ + -Wno-deprecated-declarations libdg_la_LIBADD = \ $(SNDFILE_LIBS) $(PTHREAD_LIBS) libzr.la libpugi.la libdg_la_SOURCES = \ - audiocachefile.cc \ - audiocache.cc \ - audiocacheeventhandler.cc \ - audiocacheidmanager.cc \ - audioinputenginemidi.cc \ - audiofile.cc \ - bytesizeparser.cc \ - channel.cc \ - channelmixer.cc \ - configfile.cc \ - configparser.cc \ - directory.cc \ - domloader.cc \ - dgxmlparser.cc \ - drumgizmo.cc \ - drumgizmoconf.cc \ - drumkit.cc \ - drumkitloader.cc \ - events.cc \ - events_ds.cc \ - inputprocessor.cc \ - instrument.cc \ - latencyfilter.cc \ - midimapparser.cc \ - midimapper.cc \ - path.cc \ - powerlist.cc \ - powermap.cc \ - powermapfilter.cc \ - random.cc \ - sample.cc \ - sample_selection.cc \ - sem.cc \ - staminafilter.cc \ - thread.cc \ - velocityfilter.cc \ - versionstr.cc + $(top_srcdir)/src/audiocachefile.cc \ + $(top_srcdir)/src/audiocache.cc \ + $(top_srcdir)/src/audiocacheeventhandler.cc \ + $(top_srcdir)/src/audiocacheidmanager.cc \ + $(top_srcdir)/src/audioinputenginemidi.cc \ + $(top_srcdir)/src/audiofile.cc \ + $(top_srcdir)/src/bytesizeparser.cc \ + $(top_srcdir)/src/channel.cc \ + $(top_srcdir)/src/channelmixer.cc \ + $(top_srcdir)/src/configfile.cc \ + $(top_srcdir)/src/configparser.cc \ + $(top_srcdir)/src/directory.cc \ + $(top_srcdir)/src/domloader.cc \ + $(top_srcdir)/src/dgxmlparser.cc \ + $(top_srcdir)/src/drumgizmo.cc \ + $(top_srcdir)/src/drumgizmoconf.cc \ + $(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 \ + $(top_srcdir)/src/midimapparser.cc \ + $(top_srcdir)/src/midimapper.cc \ + $(top_srcdir)/src/path.cc \ + $(top_srcdir)/src/powerlist.cc \ + $(top_srcdir)/src/curvemap.cc \ + $(top_srcdir)/src/powermapfilter.cc \ + $(top_srcdir)/src/random.cc \ + $(top_srcdir)/src/sample.cc \ + $(top_srcdir)/src/sample_selection.cc \ + $(top_srcdir)/src/sem.cc \ + $(top_srcdir)/src/staminafilter.cc \ + $(top_srcdir)/src/thread.cc \ + $(top_srcdir)/src/velocityfilter.cc \ + $(top_srcdir)/src/versionstr.cc EXTRA_DIST = \ $(libzr_la_SOURCES) \ @@ -106,7 +107,7 @@ EXTRA_DIST = \ drumgizmoconf.h \ drumkit.h \ drumkitloader.h \ - event.h \ + engineevent.h \ events.h \ events_ds.h \ grid.h \ @@ -121,10 +122,11 @@ EXTRA_DIST = \ midimapper.h \ nolocale.h \ notifier.h \ + owner.h \ path.h \ platform.h \ powerlist.h \ - powermap.h \ + curvemap.h \ powermapfilter.h \ random.h \ range.h \ @@ -140,3 +142,10 @@ EXTRA_DIST = \ velocityfilter.h \ versionstr.h \ zrwrapper.h + +%.lint: %.cc + clang-tidy --quiet $< -- $(libdg_la_CPPFLAGS) $(CXXFLAGS) + +LINT_FILES = $(libdg_la_SOURCES:.cc=.lint) + +lint: $(LINT_FILES) diff --git a/src/atomic.h b/src/atomic.h index 7ca5d1e..a29eeee 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -69,7 +69,7 @@ public: : data{} , mutex{} { - std::lock_guard<std::mutex> lock{other.mutex}; + const std::lock_guard<std::mutex> lock{other.mutex}; data = other.data; } @@ -77,13 +77,13 @@ public: : data{} , mutex{} { - std::lock_guard<std::mutex> lock{other.mutex}; + const std::lock_guard<std::mutex> lock{other.mutex}; std::swap(data, other.data); } T operator=(T data) { - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; this->data = std::move(data); return this->data; } @@ -100,42 +100,42 @@ public: void store(T data) { - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; this->data = std::move(data); } T load() const { - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; return data; } T exchange(T data){ - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; std::swap(data, this->data); return data; } bool operator==(const T& other) const { - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; return other == data; } bool operator!=(const T& other) const { - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; return !(other == data); } bool operator==(const Atomic<T>& other) const { - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; return other.load() == data; } bool operator!=(const Atomic<T>& other) const { - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; return !(other.load() == data); } diff --git a/src/audio.h b/src/audio.h index d022147..289f662 100644 --- a/src/audio.h +++ b/src/audio.h @@ -26,4 +26,4 @@ */ #pragma once -#include <audiotypes.h> +#include "audiotypes.h" diff --git a/src/audiocache.cc b/src/audiocache.cc index a2d26d9..92d3d61 100644 --- a/src/audiocache.cc +++ b/src/audiocache.cc @@ -28,8 +28,8 @@ #include <mutex> -#include <stdio.h> -#include <assert.h> +#include <cstdio> +#include <cassert> #include <hugin.hpp> @@ -64,9 +64,9 @@ void AudioCache::deinit() } // Invariant: initial_samples_needed < preloaded audio data -sample_t* AudioCache::open(const AudioFile& file, - std::size_t initial_samples_needed, - int channel, cacheid_t& id) +gsl::owner<sample_t*> AudioCache::open(const AudioFile& file, + std::size_t initial_samples_needed, + int channel, cacheid_t& new_cacheid) { assert(chunk_size); // Assert updateChunkSize was called before processing. @@ -74,16 +74,16 @@ sample_t* AudioCache::open(const AudioFile& file, { settings.number_of_underruns.fetch_add(1); // File preload not yet ready - skip this sample. - id = CACHE_DUMMYID; + new_cacheid = CACHE_DUMMYID; assert(nodata); return nodata; } // Register a new id for this cache session. - id = id_manager.registerID({}); + new_cacheid = id_manager.registerID({}); // If we are out of available ids we get CACHE_DUMMYID - if(id == CACHE_DUMMYID) + if(new_cacheid == CACHE_DUMMYID) { settings.number_of_underruns.fetch_add(1); // Use nodata buffer instead. @@ -91,20 +91,20 @@ sample_t* AudioCache::open(const AudioFile& file, return nodata; } - // Get the cache_t connected with the registered id. - cache_t& c = id_manager.getCache(id); + // Get the CacheBuffer connected with the registered id. + CacheBuffer& cache = id_manager.getCache(new_cacheid); - c.afile = nullptr; // File is opened when needed. - c.channel = channel; + cache.afile = nullptr; // File is opened when needed. + cache.channel = channel; // Next call to 'next()' will read from this point. - c.localpos = initial_samples_needed; + cache.localpos = initial_samples_needed; - c.ready = false; - c.front = nullptr; // This is allocated when needed. - c.back = nullptr; // This is allocated when needed. + cache.ready = false; + cache.front = nullptr; // This is allocated when needed. + cache.back = nullptr; // This is allocated when needed. - std::size_t cropped_size; + std::size_t cropped_size{}; if(file.preloadedsize == file.size) { @@ -122,162 +122,162 @@ sample_t* AudioCache::open(const AudioFile& file, // \----------------------v-------------------/ // cropped_size - cropped_size = file.preloadedsize - c.localpos; + cropped_size = file.preloadedsize - cache.localpos; cropped_size -= cropped_size % framesize; cropped_size += initial_samples_needed; } - c.preloaded_samples = file.data; - c.preloaded_samples_size = cropped_size; + cache.preloaded_samples = file.data; + cache.preloaded_samples_size = cropped_size; // Next potential read from disk will read from this point. - c.pos = cropped_size; + cache.pos = cropped_size; // Only load next buffer if there is more data in the file to be loaded... - if(c.pos < file.size) + if(cache.pos < file.size) { - c.afile = &event_handler.openFile(file.filename); + cache.afile = &event_handler.openFile(file.filename); - if(c.back == nullptr) + if(cache.back == nullptr) { - c.back = new sample_t[chunk_size]; + cache.allocBack(chunk_size); } - event_handler.pushLoadNextEvent(c.afile, c.channel, c.pos, - c.back, &c.ready); + event_handler.pushLoadNextEvent(cache.afile, cache.channel, cache.pos, + cache.back, &cache.ready); } - return c.preloaded_samples; // return preloaded data + return cache.preloaded_samples; // return preloaded data } -sample_t* AudioCache::next(cacheid_t id, std::size_t& size) +gsl::owner<sample_t*> AudioCache::next(cacheid_t cacheid, std::size_t& size) { - if(id == CACHE_DUMMYID) + if(cacheid == CACHE_DUMMYID) { settings.number_of_underruns.fetch_add(1); assert(nodata); return nodata; } - cache_t& c = id_manager.getCache(id); + CacheBuffer& cache = id_manager.getCache(cacheid); - if(c.preloaded_samples) + if(cache.preloaded_samples != nullptr) { // We are playing from memory: - if(c.localpos < c.preloaded_samples_size) + if(cache.localpos < cache.preloaded_samples_size) { - sample_t* s = c.preloaded_samples + c.localpos; + sample_t* samples = cache.preloaded_samples + cache.localpos; // NOLINT: Fix with span? // If only a partial frame is returned. Reflect this in the size - size = std::min(size, c.preloaded_samples_size - c.localpos); + size = std::min(size, cache.preloaded_samples_size - cache.localpos); - c.localpos += size; - return s; + cache.localpos += size; + return samples; } - c.preloaded_samples = nullptr; // Start using samples from disk. + cache.preloaded_samples = nullptr; // Start using samples from disk. } else { // We are playing from cache: - if(c.localpos < chunk_size) + if(cache.localpos < chunk_size) { - if(c.front == nullptr) + if(cache.front == nullptr) { // Just return silence. settings.number_of_underruns.fetch_add(1); - c.localpos += size; // Skip these samples so we don't loose sync. + cache.localpos += size; // Skip these samples so we don't loose sync. assert(nodata); return nodata; } - sample_t* s = c.front + c.localpos; + sample_t* samples = cache.front + cache.localpos;// NOLINT: Fix with span? // If only a partial frame is returned. Reflect this in the size - size = std::min(size, chunk_size - c.localpos); - c.localpos += size; - return s; + size = std::min(size, chunk_size - cache.localpos); + cache.localpos += size; + return samples; } } // Check for buffer underrun - if(!c.ready) + if(!cache.ready) { // Just return silence. settings.number_of_underruns.fetch_add(1); - c.localpos += size; // Skip these samples so we don't loose sync. + cache.localpos += size; // Skip these samples so we don't loose sync. assert(nodata); return nodata; } // Swap buffers - std::swap(c.front, c.back); + cache.swap(); // Next time we go here we have already read the first frame. - c.localpos = size; + cache.localpos = size; - c.pos += chunk_size; + cache.pos += chunk_size; // Does the file have remaining unread samples? - assert(c.afile); // Assert that we have an audio file. - if(c.pos < c.afile->getSize()) + assert(cache.afile); // Assert that we have an audio file. + if(cache.pos < cache.afile->getSize()) { // Do we have a back buffer to read into? - if(c.back == nullptr) + if(cache.back == nullptr) { - c.back = new sample_t[chunk_size]; + cache.allocBack(chunk_size); } - event_handler.pushLoadNextEvent(c.afile, c.channel, c.pos, - c.back, &c.ready); + event_handler.pushLoadNextEvent(cache.afile, cache.channel, cache.pos, + cache.back, &cache.ready); } // We should always have a front buffer at this point. - assert(c.front); - return c.front; + assert(cache.front); + return cache.front; } -bool AudioCache::isReady(cacheid_t id) +bool AudioCache::isReady(cacheid_t cacheid) { - if(id == CACHE_DUMMYID) + if(cacheid == CACHE_DUMMYID) { return true; } - cache_t& cache = id_manager.getCache(id); + const CacheBuffer& cache = id_manager.getCache(cacheid); return cache.ready; } -void AudioCache::close(cacheid_t id) +void AudioCache::close(cacheid_t cacheid) { - if(id == CACHE_DUMMYID) + if(cacheid == CACHE_DUMMYID) { return; } - event_handler.pushCloseEvent(id); + event_handler.pushCloseEvent(cacheid); } void AudioCache::setFrameSize(std::size_t framesize) { // Make sure the event handler thread is stalled while we set the framesize // state. - std::lock_guard<AudioCacheEventHandler> event_handler_lock(event_handler); + const std::lock_guard<AudioCacheEventHandler> event_handler_lock(event_handler); // NOTE: Not threaded... //std::lock_guard<AudioCacheIDManager> id_manager_lock(id_manager); if(framesize > nodata_framesize) { - if(nodata) + if(nodata != nullptr) { - nodata_dirty.emplace_back(std::move(nodata)); // Store for later deletion. + nodata_dirty.emplace_back(nodata); // Store for later deletion. } nodata = new sample_t[framesize]; nodata_framesize = framesize; for(std::size_t i = 0; i < framesize; ++i) { - nodata[i] = 0.0f; + nodata[i] = 0.0f;// NOLINT: Fix with span? } } @@ -292,9 +292,11 @@ std::size_t AudioCache::getFrameSize() const void AudioCache::updateChunkSize(std::size_t output_channels) { // Make sure we won't get out-of-range chunk sizes. - std::size_t disk_cache_chunk_size = - std::max(settings.disk_cache_chunk_size.load(), std::size_t(512u * 1024u)); - output_channels = std::max(output_channels, std::size_t(1u)); + constexpr std::size_t max_cache_chunk_size{512ul * 1024ul}; + const auto disk_cache_chunk_size = + std::max(settings.disk_cache_chunk_size.load(), max_cache_chunk_size); + constexpr std::size_t min_output_channels{1}; + output_channels = std::max(output_channels, min_output_channels); // 1MB pr. chunk divided over 16 channels, 4 bytes pr. sample. const auto ideal_chunk_size = diff --git a/src/audiocache.h b/src/audiocache.h index 9c6fa53..632aae9 100644 --- a/src/audiocache.h +++ b/src/audiocache.h @@ -38,6 +38,7 @@ #include "audiocacheidmanager.h" #include "audiocacheeventhandler.h" #include "settings.h" +#include "owner.h" class AudioCache { @@ -68,8 +69,10 @@ public: //! \param [out] new_id The newly created cache id. //! \return A pointer to the first buffer containing the //! 'initial_samples_needed' number of samples. - sample_t* open(const AudioFile& file, std::size_t initial_samples_needed, int channel, - cacheid_t& new_id); + gsl::owner<sample_t*> open(const AudioFile& file, + std::size_t initial_samples_needed, + int channel, + cacheid_t& new_cacheid); //! Get next buffer. //! Returns the next buffer for reading based on cache id. @@ -78,9 +81,9 @@ public: //! \param id The cache id to read from. //! \param [out] size The size of the returned buffer. //! \return A pointer to the buffer. - sample_t* next(cacheid_t id, std::size_t &size); + gsl::owner<sample_t*> next(cacheid_t id, std::size_t &size); - //! Returns true iff the next chunk of the supplied id has been read from disk. + //! Returns true iff next chunk of the supplied id has been read from disk. bool isReady(cacheid_t id); //! Unregister cache entry. @@ -103,10 +106,10 @@ public: bool isAsyncMode() const; private: - std::size_t framesize{0}; - sample_t* nodata{nullptr}; - std::size_t nodata_framesize{0}; - std::size_t chunk_size{0}; + std::size_t framesize{}; + gsl::owner<sample_t*> nodata{}; + std::size_t nodata_framesize{}; + std::size_t chunk_size{}; std::list<std::unique_ptr<sample_t[]>> nodata_dirty; AudioCacheIDManager id_manager; diff --git a/src/audiocacheeventhandler.cc b/src/audiocacheeventhandler.cc index a0327b5..ffd183c 100644 --- a/src/audiocacheeventhandler.cc +++ b/src/audiocacheeventhandler.cc @@ -26,7 +26,7 @@ */ #include "audiocacheeventhandler.h" -#include <assert.h> +#include <cassert> #include <hugin.hpp> @@ -34,13 +34,14 @@ #include "audiocache.h" #include "audiocacheidmanager.h" -enum class EventType { +enum class EventType +{ LoadNext, Close, }; -class CacheEvent { -public: +struct CacheEvent +{ EventType event_type; // For close event: @@ -63,9 +64,9 @@ AudioCacheEventHandler::~AudioCacheEventHandler() clearEvents(); auto active_ids = id_manager.getActiveIDs(); - for(auto id : active_ids) + for(auto active_id : active_ids) { - handleCloseCache(id); + handleCloseCache(active_id); } } @@ -115,32 +116,32 @@ void AudioCacheEventHandler::unlock() } void AudioCacheEventHandler::pushLoadNextEvent(AudioCacheFile* afile, - size_t channel, + size_t channel_index, size_t pos, sample_t* buffer, volatile bool* ready) { - CacheEvent cache_event; + CacheEvent cache_event{}; cache_event.event_type = EventType::LoadNext; cache_event.pos = pos; cache_event.afile = afile; - CacheChannel c; - c.channel = channel; - c.samples = buffer; + CacheChannel cache_channel{}; + cache_channel.channel_index = channel_index; + cache_channel.samples = buffer; *ready = false; - c.ready = ready; + cache_channel.ready = ready; - cache_event.channels.insert(cache_event.channels.end(), c); + cache_event.channels.insert(cache_event.channels.end(), cache_channel); pushEvent(cache_event); } -void AudioCacheEventHandler::pushCloseEvent(cacheid_t id) +void AudioCacheEventHandler::pushCloseEvent(cacheid_t cacheid) { - CacheEvent cache_event; + CacheEvent cache_event{}; cache_event.event_type = EventType::Close; - cache_event.id = id; + cache_event.id = cacheid; pushEvent(cache_event); } @@ -179,7 +180,7 @@ size_t AudioCacheEventHandler::getChunkSize() const AudioCacheFile& AudioCacheEventHandler::openFile(const std::string& filename) { - std::lock_guard<std::mutex> lock(mutex); + const std::lock_guard<std::mutex> lock(mutex); return files.getFile(filename); } @@ -197,7 +198,7 @@ void AudioCacheEventHandler::clearEvents() eventqueue.clear(); } -void AudioCacheEventHandler::handleLoadNextEvent(CacheEvent& cache_event) +void AudioCacheEventHandler::handleLoadNextEvent(CacheEvent& cache_event) const { assert(cache_event.afile); // Assert that we have an audio file @@ -207,24 +208,25 @@ void AudioCacheEventHandler::handleLoadNextEvent(CacheEvent& cache_event) void AudioCacheEventHandler::handleCloseEvent(CacheEvent& cache_event) { - std::lock_guard<std::mutex> lock(mutex); + const std::lock_guard<std::mutex> lock(mutex); handleCloseCache(cache_event.id); } -void AudioCacheEventHandler::handleCloseCache(cacheid_t id) +void AudioCacheEventHandler::handleCloseCache(cacheid_t cacheid) { - auto& cache = id_manager.getCache(id); + auto& cache = id_manager.getCache(cacheid); // Only close the file if we have also opened it. - if(cache.afile) + if(cache.afile != nullptr) { files.releaseFile(cache.afile->getFilename()); } - delete[] cache.front; - delete[] cache.back; + //delete[] cache.front; + //delete[] cache.back; + cache.deleteChunks(); - id_manager.releaseID(id); + id_manager.releaseID(cacheid); } void AudioCacheEventHandler::handleEvent(CacheEvent& cache_event) @@ -272,7 +274,7 @@ void AudioCacheEventHandler::pushEvent(CacheEvent& cache_event) } { - std::lock_guard<std::mutex> lock(mutex); + const std::lock_guard<std::mutex> lock(mutex); bool found = false; diff --git a/src/audiocacheeventhandler.h b/src/audiocacheeventhandler.h index f80b4c8..e676f6e 100644 --- a/src/audiocacheeventhandler.h +++ b/src/audiocacheeventhandler.h @@ -36,7 +36,7 @@ #include "audiocachefile.h" #include "audiocacheidmanager.h" -class CacheEvent; +struct CacheEvent; class AudioCacheEventHandler : protected Thread @@ -68,10 +68,10 @@ public: //! This methods are supplied to make this class lockable by std::lock_guard void unlock(); - void pushLoadNextEvent(AudioCacheFile* afile, size_t channel, + void pushLoadNextEvent(AudioCacheFile* afile, size_t channel_index, size_t pos, sample_t* buffer, volatile bool* ready); - void pushCloseEvent(cacheid_t id); + void pushCloseEvent(cacheid_t cacheid); void setChunkSize(size_t chunksize); size_t getChunkSize() const; @@ -81,13 +81,13 @@ public: protected: void clearEvents(); - void handleLoadNextEvent(CacheEvent& cache_event); + void handleLoadNextEvent(CacheEvent& cache_event) const; //! Lock the mutex and calls handleCloseCache void handleCloseEvent(CacheEvent& cache_event); //! Close decrease the file ref and release the cache id. - void handleCloseCache(cacheid_t id); + void handleCloseCache(cacheid_t cacheid); void handleEvent(CacheEvent& cache_event); diff --git a/src/audiocachefile.cc b/src/audiocachefile.cc index 2c2888c..daf4fc1 100644 --- a/src/audiocachefile.cc +++ b/src/audiocachefile.cc @@ -26,23 +26,19 @@ */ #include "audiocachefile.h" -#include <assert.h> - -#include <hugin.hpp> - +#include <cassert> #include <cstring> +#include <functional> -#include "audiocache.h" +#include <hugin.hpp> AudioCacheFile::AudioCacheFile(const std::string& filename, std::vector<sample_t>& read_buffer) : filename(filename) , read_buffer(read_buffer) { - std::memset(&sf_info, 0, sizeof(SF_INFO)); - fh = sf_open(filename.c_str(), SFM_READ, &sf_info); - if(!fh) + if(fh == nullptr) { ERR(audiofile,"SNDFILE Error (%s): %s\n", filename.c_str(), sf_strerror(fh)); @@ -57,7 +53,7 @@ AudioCacheFile::AudioCacheFile(const std::string& filename, AudioCacheFile::~AudioCacheFile() { - if(fh) + if(fh != nullptr) { sf_close(fh); fh = nullptr; @@ -80,10 +76,10 @@ size_t AudioCacheFile::getChannelCount() const } void AudioCacheFile::readChunk(const CacheChannels& channels, - size_t pos, size_t num_samples) + std::size_t pos, std::size_t num_samples) { //assert(fh != nullptr); // File handle must never be nullptr - if(!fh) + if(fh == nullptr) { return; } @@ -95,9 +91,9 @@ void AudioCacheFile::readChunk(const CacheChannels& channels, return; } - sf_seek(fh, pos, SEEK_SET); + sf_seek(fh, static_cast<sf_count_t>(pos), SEEK_SET); - size_t size = sf_info.frames - pos; + auto size = sf_info.frames - pos; if(size > num_samples) { size = num_samples; @@ -108,38 +104,42 @@ void AudioCacheFile::readChunk(const CacheChannels& channels, read_buffer.resize(size * sf_info.channels); } - size_t read_size = sf_readf_float(fh, read_buffer.data(), size); - (void)read_size; + const size_t read_size = sf_readf_float(fh, read_buffer.data(), + static_cast<sf_count_t>(size)); + assert(read_size == size); - for(auto it = channels.begin(); it != channels.end(); ++it) + for(const auto& channel : channels) { - size_t channel = it->channel; - sample_t* data = it->samples; + auto channel_index = channel.channel_index; + auto* data = channel.samples; for (size_t i = 0; i < size; ++i) { - data[i] = read_buffer[(i * sf_info.channels) + channel]; + data[i] = read_buffer[(i * sf_info.channels) + channel_index]; // NOLINT: will be fixed with std::span } } - for(auto it = channels.begin(); it != channels.end(); ++it) + for(const auto & channel : channels) { - *(it->ready) = true; + *(channel.ready) = true; } } AudioCacheFile& AudioCacheFiles::getFile(const std::string& filename) { - std::lock_guard<std::mutex> lock(mutex); + const std::lock_guard<std::mutex> lock(mutex); - auto it = audiofiles.find(filename); - if(it == audiofiles.end()) + auto audiofile_it = audiofiles.find(filename); + if(audiofile_it == audiofiles.end()) { // Construct a new AudioCacheFile in place. The in place construction is relevant. - it = audiofiles.emplace(std::piecewise_construct, std::make_tuple(filename), - std::make_tuple(filename, std::ref(read_buffer))).first; + audiofile_it = + audiofiles.emplace(std::piecewise_construct, + std::make_tuple(filename), + std::make_tuple(filename, + std::ref(read_buffer))).first; // FIXME: This must be possible to do in a more easy-to-read way! } - auto& cache_audio_file = it->second; + auto& cache_audio_file = audiofile_it->second; // Increase ref count. ++cache_audio_file.ref; @@ -149,22 +149,22 @@ AudioCacheFile& AudioCacheFiles::getFile(const std::string& filename) void AudioCacheFiles::releaseFile(const std::string& filename) { - std::lock_guard<std::mutex> lock(mutex); + const std::lock_guard<std::mutex> lock(mutex); - auto it = audiofiles.find(filename); - if(it == audiofiles.end()) + auto audiofile_it = audiofiles.find(filename); + if(audiofile_it == audiofiles.end()) { assert(false); // This should never happen! return; // not open } - auto& audiofile = it->second; + auto& audiofile = audiofile_it->second; assert(audiofile.ref); // If ref is not > 0 it shouldn't be in the map. --audiofile.ref; if(audiofile.ref == 0) { - audiofiles.erase(it); + audiofiles.erase(audiofile_it); } } diff --git a/src/audiocachefile.h b/src/audiocachefile.h index 66a6529..63170cf 100644 --- a/src/audiocachefile.h +++ b/src/audiocachefile.h @@ -31,16 +31,17 @@ #include <map> #include <vector> #include <mutex> +#include <cstddef> #include <sndfile.h> -#include <audiotypes.h> +#include "audiotypes.h" //! Channel data container in the cache world. class CacheChannel { public: - size_t channel; //< Channel number + size_t channel_index; //< Channel number sample_t* samples; //< Sample buffer pointer. size_t num_samples; //< Number of samples in the sample buffer volatile bool* ready; //< Is set to true when the loading is done. @@ -72,12 +73,13 @@ public: size_t getChannelCount() const; //! Read audio data from the file into the supplied channel caches. - void readChunk(const CacheChannels& channels, size_t pos, size_t num_samples); + void readChunk(const CacheChannels& channels, std::size_t pos, + std::size_t num_samples); private: - int ref{0}; - SNDFILE* fh{nullptr}; - SF_INFO sf_info; + int ref{}; + SNDFILE* fh{}; + SF_INFO sf_info{}; std::string filename; std::vector<sample_t>& read_buffer; }; diff --git a/src/audiocacheidmanager.cc b/src/audiocacheidmanager.cc index efef17e..80b0f98 100644 --- a/src/audiocacheidmanager.cc +++ b/src/audiocacheidmanager.cc @@ -27,7 +27,23 @@ #include "audiocacheidmanager.h" #include <limits> -#include <assert.h> +#include <cassert> + +void CacheBuffer::allocBack(std::size_t chunk_size) +{ + back = new sample_t[chunk_size]; +} + +void CacheBuffer::deleteChunks() const +{ + delete[] front; + delete[] back; +} + +void CacheBuffer::swap() noexcept +{ + std::swap(front, back); +} AudioCacheIDManager::~AudioCacheIDManager() { @@ -36,34 +52,34 @@ AudioCacheIDManager::~AudioCacheIDManager() void AudioCacheIDManager::init(unsigned int capacity) { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); id2cache.resize(capacity); available_ids.resize(capacity); - for(size_t i = 0; i < capacity; ++i) + for(int i = 0; i < static_cast<int>(capacity); ++i) { available_ids[i] = i; } } -cache_t& AudioCacheIDManager::getCache(cacheid_t id) +CacheBuffer& AudioCacheIDManager::getCache(cacheid_t cacheid) { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); - assert(id != CACHE_NOID); - assert(id != CACHE_DUMMYID); - assert(id >= 0); - assert(id < (int)id2cache.size()); - assert(id2cache[id].id == id); + assert(cacheid != CACHE_NOID); + assert(cacheid != CACHE_DUMMYID); + assert(cacheid >= 0); + assert(cacheid < (int)id2cache.size()); + assert(id2cache[cacheid].id == cacheid); - return id2cache[id]; + return id2cache[cacheid]; } -cacheid_t AudioCacheIDManager::registerID(const cache_t& cache) +cacheid_t AudioCacheIDManager::registerID(const CacheBuffer& cache) { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); - cacheid_t id = CACHE_NOID; + cacheid_t cacheid = CACHE_NOID; if(available_ids.empty()) { @@ -71,32 +87,32 @@ cacheid_t AudioCacheIDManager::registerID(const cache_t& cache) } else { - id = available_ids.back(); + cacheid = available_ids.back(); available_ids.pop_back(); } - assert(id2cache[id].id == CACHE_NOID); // Make sure it is not already in use + assert(id2cache[cacheid].id == CACHE_NOID); // Make sure it is not already in use - id2cache[id] = cache; - id2cache[id].id = id; + id2cache[cacheid] = cache; + id2cache[cacheid].id = cacheid; - return id; + return cacheid; } -void AudioCacheIDManager::releaseID(cacheid_t id) +void AudioCacheIDManager::releaseID(cacheid_t cacheid) { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); - assert(id2cache[id].id != CACHE_NOID); // Test if it wasn't already released. + assert(id2cache[cacheid].id != CACHE_NOID); // Test if it wasn't already released. - id2cache[id].id = CACHE_NOID; + id2cache[cacheid].id = CACHE_NOID; - available_ids.push_back(id); + available_ids.push_back(cacheid); } void AudioCacheIDManager::disableActive() { - // Run through all active cache_ts and disable them. + // Run through all active CacheBuffers and disable them. for(auto& cache : id2cache) { if(cache.id != CACHE_NOID) diff --git a/src/audiocacheidmanager.h b/src/audiocacheidmanager.h index 0aa40e3..d440415 100644 --- a/src/audiocacheidmanager.h +++ b/src/audiocacheidmanager.h @@ -26,35 +26,40 @@ */ #pragma once -#include <stdlib.h> +#include <cstdlib> #include <vector> - -#include <audiotypes.h> - #include <mutex> +#include "audiotypes.h" +#include "owner.h" + class AudioCacheFile; -#define CACHE_DUMMYID -2 -#define CACHE_NOID -1 +using cacheid_t = int; -typedef int cacheid_t; +constexpr cacheid_t CACHE_DUMMYID{-2}; +constexpr cacheid_t CACHE_NOID{-1}; -struct cache_t +class CacheBuffer { - cacheid_t id{CACHE_NOID}; //< Current id of this cache_t. CACHE_NOID means not in use. +public: + void allocBack(std::size_t chunk_size); + void deleteChunks() const; + void swap() noexcept; + + cacheid_t id{CACHE_NOID}; //< Current id of this CacheBuffer. CACHE_NOID means not in use. - AudioCacheFile* afile{nullptr}; - size_t channel{0}; - size_t pos{0}; //< File position + AudioCacheFile* afile{}; + size_t channel{}; + size_t pos{}; //< File position volatile bool ready{false}; - sample_t* front{nullptr}; - sample_t* back{nullptr}; - size_t localpos{0}; //< Intra buffer (front) position. + gsl::owner<sample_t*> front{}; + gsl::owner<sample_t*> back{}; + size_t localpos{}; //< Intra buffer (front) position. - sample_t* preloaded_samples{nullptr}; // nullptr means preload buffer not active. - size_t preloaded_samples_size{0}; + sample_t* preloaded_samples{}; // nullptr means preload buffer not active. + size_t preloaded_samples_size{}; }; class AudioCacheIDManager @@ -71,16 +76,16 @@ public: //! Get the cache object connected with the specified cacheid. //! Note: The cacheid MUST be active. - cache_t& getCache(cacheid_t id); + CacheBuffer& getCache(cacheid_t cacheid); //! Reserve a new cache object and return its cacheid. //! The contents of the supplied cache object will be copied to the new //! cache object. - cacheid_t registerID(const cache_t& cache); + cacheid_t registerID(const CacheBuffer& cache); //! Release a cache object and its correseponding cacheid. //! After this call the cacheid can no longer be used. - void releaseID(cacheid_t id); + void releaseID(cacheid_t cacheid); protected: // For AudioCacheEventHandler @@ -89,6 +94,6 @@ protected: std::mutex mutex; - std::vector<cache_t> id2cache; + std::vector<CacheBuffer> id2cache; std::vector<cacheid_t> available_ids; }; diff --git a/src/audiofile.cc b/src/audiofile.cc index 2d61eb5..1228044 100644 --- a/src/audiofile.cc +++ b/src/audiofile.cc @@ -30,6 +30,7 @@ #include <cassert> #include <sndfile.h> +#include <array> #include <config.h> @@ -37,9 +38,9 @@ #include "channel.h" -AudioFile::AudioFile(const std::string& filename, std::size_t filechannel, +AudioFile::AudioFile(std::string filename, std::size_t filechannel, InstrumentChannel* instrument_channel) - : filename(filename) + : filename(std::move(filename)) , filechannel(filechannel) , magic{this} , instrument_channel(instrument_channel) @@ -61,9 +62,9 @@ bool AudioFile::isValid() const void AudioFile::unload() { // Make sure we don't unload the object while loading it... - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); - is_loaded = false; + is_loaded.store(false); preloadedsize = 0; size = 0; @@ -71,28 +72,28 @@ void AudioFile::unload() data = nullptr; } -#define BUFFER_SIZE 4096 +constexpr std::size_t buffer_size{4096}; -void AudioFile::load(LogFunction logger, std::size_t sample_limit) +void AudioFile::load(const LogFunction& logger, std::size_t sample_limit) { // Make sure we don't unload the object while loading it... - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); - if(this->data) // already loaded + if(this->data != nullptr) // already loaded { return; } SF_INFO sf_info{}; - SNDFILE *fh = sf_open(filename.c_str(), SFM_READ, &sf_info); - if(!fh) + SNDFILE *file_handle = sf_open(filename.c_str(), SFM_READ, &sf_info); + if(file_handle == nullptr) { ERR(audiofile,"SNDFILE Error (%s): %s\n", - filename.c_str(), sf_strerror(fh)); + filename.c_str(), sf_strerror(file_handle)); if(logger) { logger(LogLevel::Warning, "Could not load '" + filename + - "': " + sf_strerror(fh)); + "': " + sf_strerror(file_handle)); } return; } @@ -108,7 +109,7 @@ void AudioFile::load(LogFunction logger, std::size_t sample_limit) return; } - std::size_t size = sf_info.frames; + const std::size_t size = sf_info.frames; std::size_t preloadedsize = sf_info.frames; if(preloadedsize > sample_limit) @@ -116,10 +117,12 @@ void AudioFile::load(LogFunction logger, std::size_t sample_limit) preloadedsize = sample_limit; } - sample_t* data = new sample_t[preloadedsize]; + gsl::owner<sample_t*> data{}; + data = new sample_t[preloadedsize]; if(sf_info.channels == 1) { - preloadedsize = sf_read_float(fh, data, preloadedsize); + preloadedsize = + sf_read_float(file_handle, data, static_cast<sf_count_t>(preloadedsize)); } else { @@ -134,19 +137,22 @@ void AudioFile::load(LogFunction logger, std::size_t sample_limit) filechannel = sf_info.channels - 1; } - sample_t buffer[BUFFER_SIZE]; - std::size_t frame_count = BUFFER_SIZE / sf_info.channels; - std::size_t total_frames_read = 0; - int frames_read; + std::array<sample_t, buffer_size> buffer{}; + const sf_count_t frame_count{static_cast<sf_count_t>(buffer.size()) / + sf_info.channels}; + std::size_t total_frames_read{}; + std::size_t frames_read{}; do { - frames_read = sf_readf_float(fh, buffer, frame_count); - for(int i = 0; + frames_read = static_cast<std::size_t>( + sf_readf_float(file_handle, buffer.data(), frame_count)); + for(std::size_t i = 0; (i < frames_read) && (total_frames_read < sample_limit); ++i) { - data[total_frames_read++] = buffer[i * sf_info.channels + filechannel]; + data[total_frames_read++] = + buffer[i * sf_info.channels + filechannel]; // NOLINT - span } } while( (frames_read > 0) && @@ -157,17 +163,17 @@ void AudioFile::load(LogFunction logger, std::size_t sample_limit) preloadedsize = total_frames_read; } - sf_close(fh); + sf_close(file_handle); this->data = data; this->size = size; this->preloadedsize = preloadedsize; - is_loaded = true; + is_loaded.store(true); } bool AudioFile::isLoaded() const { - return is_loaded; + return is_loaded.load(); } main_state_t AudioFile::mainState() const diff --git a/src/audiofile.h b/src/audiofile.h index 504d0ae..cff4f3b 100644 --- a/src/audiofile.h +++ b/src/audiofile.h @@ -31,28 +31,31 @@ #include <vector> #include <limits> #include <mutex> +#include <atomic> #include "audio.h" #include "channel.h" #include "logger.h" +#include "owner.h" class InstrumentChannel; class AudioFile { public: - AudioFile(const std::string& filename, std::size_t filechannel, + AudioFile(std::string filename, std::size_t filechannel, InstrumentChannel* instrument_channel = nullptr); ~AudioFile(); - void load(LogFunction logger, std::size_t sample_limit = std::numeric_limits<std::size_t>::max()); + void load(const LogFunction& logger, + std::size_t sample_limit = std::numeric_limits<std::size_t>::max()); void unload(); bool isLoaded() const; volatile std::size_t size{0}; // Full size of the file volatile std::size_t preloadedsize{0}; // Number of samples preloaded (in data) - sample_t* data{nullptr}; + gsl::owner<sample_t*> data{nullptr}; std::string filename; @@ -70,7 +73,7 @@ private: friend class DOMLoaderTest; friend class InstrumentParserTest; - void* magic{nullptr}; - volatile bool is_loaded{false}; - InstrumentChannel* instrument_channel; + void* magic{}; + std::atomic<bool> is_loaded{false}; + InstrumentChannel* instrument_channel{}; }; diff --git a/src/audioinputengine.h b/src/audioinputengine.h index 71a86c2..55a06ae 100644 --- a/src/audioinputengine.h +++ b/src/audioinputengine.h @@ -29,7 +29,7 @@ #include <string> #include <vector> -#include <event.h> +#include "engineevent.h" #include "instrument.h" diff --git a/src/audioinputenginemidi.cc b/src/audioinputenginemidi.cc index 69aeeb6..2e794f4 100644 --- a/src/audioinputenginemidi.cc +++ b/src/audioinputenginemidi.cc @@ -28,26 +28,20 @@ #include "midimapparser.h" -#include "drumgizmo.h" +#include <cassert> #include <hugin.hpp> -AudioInputEngineMidi::AudioInputEngineMidi() - : refs(REFSFILE) -{ - is_valid = false; -} - -bool AudioInputEngineMidi::loadMidiMap(const std::string& file, +bool AudioInputEngineMidi::loadMidiMap(const std::string& midimap_file, const Instruments& instruments) { - std::string f = file; + std::string file = midimap_file; if(refs.load()) { if(file.size() > 1 && file[0] == '@') { - f = refs.getValue(file.substr(1)); + file = refs.getValue(file.substr(1)); } } else @@ -58,16 +52,16 @@ bool AudioInputEngineMidi::loadMidiMap(const std::string& file, midimap = ""; is_valid = false; - DEBUG(mmap, "loadMidiMap(%s, i.size() == %d)\n", f.c_str(), + DEBUG(mmap, "loadMidiMap(%s, i.size() == %d)\n", file.c_str(), (int)instruments.size()); - if(f == "") + if(file.empty()) { return false; } MidiMapParser midimap_parser; - if(!midimap_parser.parseFile(f)) + if(!midimap_parser.parseFile(file)) { return false; } @@ -75,7 +69,7 @@ bool AudioInputEngineMidi::loadMidiMap(const std::string& file, instrmap_t instrmap; for(size_t i = 0; i < instruments.size(); i++) { - instrmap[instruments[i]->getName()] = i; + instrmap[instruments[i]->getName()] = static_cast<int>(i); } mmap.swap(instrmap, midimap_parser.midimap); @@ -97,12 +91,12 @@ bool AudioInputEngineMidi::isValid() const } // Note types: -static const std::uint8_t NoteOff = 0x80; -static const std::uint8_t NoteOn = 0x90; -static const std::uint8_t NoteAftertouch = 0xA0; +constexpr std::uint8_t NoteOff{0x80}; +constexpr std::uint8_t NoteOn{0x90}; +constexpr std::uint8_t NoteAftertouch{0xA0}; // Note type mask: -static int const NoteMask = 0xF0; +constexpr std::uint8_t NoteMask{0xF0}; void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer, std::size_t midi_buffer_length, @@ -114,13 +108,13 @@ void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer, return; } - auto key = midi_buffer[1]; - auto velocity = midi_buffer[2]; + auto key = midi_buffer[1]; // NOLINT - span + auto velocity = midi_buffer[2]; // NOLINT - span auto instrument_idx = mmap.lookup(key); auto instruments = mmap.lookup(key); for(const auto& instrument_idx : instruments) { - switch(midi_buffer[0] & NoteMask) + switch(midi_buffer[0] & NoteMask) // NOLINT - span { case NoteOff: // Ignore for now @@ -129,8 +123,12 @@ void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer, case NoteOn: if(velocity != 0) { + constexpr float lower_offset{0.5f}; + constexpr float midi_velocity_max{127.0f}; // maps velocities to [.5/127, 126.5/127] - auto centered_velocity = (velocity-.5f)/127.0f; + assert(velocity <= 127); // MIDI only support up to 127 + auto centered_velocity = + (static_cast<float>(velocity) - lower_offset) / midi_velocity_max; events.push_back({EventType::OnSet, (std::size_t)instrument_idx, offset, centered_velocity}); } diff --git a/src/audioinputenginemidi.h b/src/audioinputenginemidi.h index 8da7bd2..386a055 100644 --- a/src/audioinputenginemidi.h +++ b/src/audioinputenginemidi.h @@ -37,7 +37,7 @@ class AudioInputEngineMidi : public AudioInputEngine { public: - AudioInputEngineMidi(); + AudioInputEngineMidi() = default; virtual ~AudioInputEngineMidi() = default; virtual bool init(const Instruments &instruments) = 0; @@ -51,14 +51,15 @@ public: virtual void run(size_t pos, size_t len, std::vector<event_t>& events) = 0; virtual void post() = 0; - virtual bool loadMidiMap(const std::string& file, const Instruments& i); + virtual bool loadMidiMap(const std::string& midimap_file, + const Instruments& i); std::string getMidimapFile() const; bool isValid() const; - void processNote(const std::uint8_t* note_data, - std::size_t note_data_size, + void processNote(const std::uint8_t* midi_buffer, + std::size_t midi_buffer_length, std::size_t offset, std::vector<event_t>& events); @@ -67,7 +68,7 @@ protected: private: std::string midimap; - bool is_valid; + bool is_valid{false}; - ConfigFile refs; + ConfigFile refs{REFSFILE}; }; diff --git a/src/audiooutputengine.h b/src/audiooutputengine.h index f69fd75..a69c56d 100644 --- a/src/audiooutputengine.h +++ b/src/audiooutputengine.h @@ -29,7 +29,7 @@ #include <cstddef> #include <string> -#include <audiotypes.h> +#include "audiotypes.h" #include "channel.h" diff --git a/src/bytesizeparser.cc b/src/bytesizeparser.cc index 08c7b5b..78a525b 100644 --- a/src/bytesizeparser.cc +++ b/src/bytesizeparser.cc @@ -25,23 +25,28 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "bytesizeparser.h" + #include <iostream> #include <stdexcept> - -static std::size_t suffixToSize(const char& suffix) +namespace { +std::size_t suffixToSize(const char& suffix) { - int size = 1; + constexpr std::size_t kilo{10}; + constexpr std::size_t mega{20}; + constexpr std::size_t giga{30}; + + std::size_t size{1}; switch(suffix) { case 'k': - size <<= 10; + size <<= kilo; break; case 'M': - size <<= 20; + size <<= mega; break; case 'G': - size <<= 30; + size <<= giga; break; default: size = 0; @@ -49,14 +54,14 @@ static std::size_t suffixToSize(const char& suffix) } return size; } - +} std::size_t byteSizeParser(const std::string& argument) { - std::string::size_type suffix_index; - std::size_t size; + std::string::size_type suffix_index{}; + std::size_t size{}; std::string suffix; - bool error = false; + bool error{false}; if(argument.find('-') != std::string::npos) { @@ -69,14 +74,15 @@ std::size_t byteSizeParser(const std::string& argument) } catch(std::invalid_argument&) { - std::cerr << "Invalid argument for diskstreamsize" << std::endl; + std::cerr << "Invalid argument for diskstreamsize\n"; error = true; } catch(std::out_of_range&) { - std::cerr << "Number too big. Try using bigger suffix for diskstreamsize" << std::endl; + std::cerr << "Number too big. Try using bigger suffix for diskstreamsize\n"; error = true; } + if(!error) { suffix = argument.substr(suffix_index); @@ -85,14 +91,17 @@ std::size_t byteSizeParser(const std::string& argument) error = true; } } - if(!error && suffix.size() > 0) + + if(!error && !suffix.empty()) { - std::size_t suffix_size = suffixToSize(suffix[0]); + const std::size_t suffix_size = suffixToSize(suffix[0]); size *= suffix_size; } + if(error) { return 0; } + return size; } diff --git a/src/bytesizeparser.h b/src/bytesizeparser.h index 3007454..e1ffeab 100644 --- a/src/bytesizeparser.h +++ b/src/bytesizeparser.h @@ -25,8 +25,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #pragma once -#include <string> +#include <string> //! Returns size in bytes //! \param argument The size in n{k,M,G} format diff --git a/src/configfile.h b/src/configfile.h index c17811b..49dbdee 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -30,6 +30,8 @@ #include <map> #include <fstream> +static const std::string REFSFILE{"refs.conf"}; + class ConfigFile { public: diff --git a/src/powermap.cc b/src/curvemap.cc index 2bb45b7..073dde4 100644 --- a/src/powermap.cc +++ b/src/curvemap.cc @@ -24,7 +24,7 @@ * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include "powermap.h" +#include "curvemap.h" #include <cassert> #include <cmath> @@ -32,31 +32,31 @@ namespace { -using Power = Powermap::Power; -using PowerPair = Powermap::PowerPair; +using CurveValue = CurveMap::CurveValue; +using CurveValuePair = CurveMap::CurveValuePair; -Power h00(Power x) +CurveValue h00(CurveValue x) { return (1 + 2 * x) * pow(1 - x, 2); } -Power h10(Power x) +CurveValue h10(CurveValue x) { return x * pow(1 - x, 2); } -Power h01(Power x) +CurveValue h01(CurveValue x) { return x * x * (3 - 2 * x); } -Power h11(Power x) +CurveValue h11(CurveValue x) { return x * x * (x - 1); } -Power computeValue(const Power x, const PowerPair& P0, const PowerPair& P1, - const Power m0, const Power m1) +CurveValue computeValue(const CurveValue x, const CurveValuePair& P0, const CurveValuePair& P1, + const CurveValue m0, const CurveValue m1) { const auto x0 = P0.in; const auto x1 = P1.in; @@ -74,21 +74,22 @@ Power computeValue(const Power x, const PowerPair& P0, const PowerPair& P1, } // end anonymous namespace -Powermap::Powermap() -{ - reset(); -} +constexpr std::array<CurveValuePair, 3> CurveMap::default_fixed; -Power Powermap::map(Power in) +CurveValue CurveMap::map(CurveValue in) { assert(in >= 0. && in <= 1.); + if (invert) + { + in = 1.0 - in; + } if (spline_needs_update) { updateSpline(); } - Power out; + CurveValue out; if (in < fixed[0].in) { out = shelf ? fixed[0].out @@ -113,84 +114,100 @@ Power Powermap::map(Power in) return out; } -void Powermap::reset() +void CurveMap::reset() { - setFixed0({eps, eps}); - setFixed1({.5, .5}); - setFixed2({1 - eps, 1 - eps}); - // FIXME: better false? - shelf = true; + *this = CurveMap{}; updateSpline(); } -void Powermap::setFixed0(PowerPair new_value) +void CurveMap::setFixed0(CurveValuePair new_value) { - if (fixed[0] != new_value) + auto prev = fixed[0]; + fixed[0].in = clamp(new_value.in, eps, fixed[1].in - eps); + fixed[0].out = clamp(new_value.out, eps, fixed[1].out - eps); + if (fixed[0] != prev) { spline_needs_update = true; - fixed[0].in = clamp(new_value.in, eps, fixed[1].in - eps); - fixed[0].out = clamp(new_value.out, eps, fixed[1].out - eps); } } -void Powermap::setFixed1(PowerPair new_value) +void CurveMap::setFixed1(CurveValuePair new_value) { - if (fixed[1] != new_value) + auto prev = fixed[1]; + fixed[1].in = clamp(new_value.in, fixed[0].in + eps, fixed[2].in - eps); + fixed[1].out = clamp(new_value.out, fixed[0].out + eps, fixed[2].out - eps); + if (fixed[1] != prev) { spline_needs_update = true; - fixed[1].in = clamp(new_value.in, fixed[0].in + eps, fixed[2].in - eps); - fixed[1].out = clamp(new_value.out, fixed[0].out + eps, fixed[2].out - eps); } } -void Powermap::setFixed2(PowerPair new_value) +void CurveMap::setFixed2(CurveValuePair new_value) { - if (fixed[2] != new_value) + auto prev = fixed[2]; + fixed[2].in = clamp(new_value.in, fixed[1].in + eps, 1 - eps); + fixed[2].out = clamp(new_value.out, fixed[1].out + eps, 1 - eps); + if (fixed[2] != prev) { spline_needs_update = true; - fixed[2].in = clamp(new_value.in, fixed[1].in + eps, 1 - eps); - fixed[2].out = clamp(new_value.out, fixed[1].out + eps, 1 - eps); } } -void Powermap::setShelf(bool enable) +void CurveMap::setInvert(bool enable) +{ + if (invert != enable) + { + spline_needs_update = true; + invert = enable; + } +} + +void CurveMap::setShelf(bool enable) { if (shelf != enable) { spline_needs_update = true; - this->shelf = enable; + shelf = enable; } } -PowerPair Powermap::getFixed0() const +CurveValuePair CurveMap::getFixed0() const { return fixed[0]; } -PowerPair Powermap::getFixed1() const +CurveValuePair CurveMap::getFixed1() const { return fixed[1]; } -PowerPair Powermap::getFixed2() const +CurveValuePair CurveMap::getFixed2() const { return fixed[2]; } +bool CurveMap::getInvert() const { + return invert; +} + +bool CurveMap::getShelf() const { + return shelf; +} + // This mostly followes the wikipedia article for monotone cubic splines: // https://en.wikipedia.org/wiki/Monotone_cubic_interpolation -void Powermap::updateSpline() +void CurveMap::updateSpline() { assert(0. <= fixed[0].in && fixed[0].in < fixed[1].in && fixed[1].in < fixed[2].in && fixed[2].in <= 1.); assert(0. <= fixed[0].out && fixed[0].out <= fixed[1].out && fixed[1].out <= fixed[2].out && fixed[2].out <= 1.); - Powers X = shelf ? Powers{fixed[0].in, fixed[1].in, fixed[2].in} - : Powers{0., fixed[0].in, fixed[1].in, fixed[2].in, 1.}; - Powers Y = shelf ? Powers{fixed[0].out, fixed[1].out, fixed[2].out} - : Powers{0., fixed[0].out, fixed[1].out, fixed[2].out, 1.}; + CurveValues X = shelf ? CurveValues{fixed[0].in, fixed[1].in, fixed[2].in} + : CurveValues{0., fixed[0].in, fixed[1].in, fixed[2].in, 1.}; + CurveValues Y = shelf ? CurveValues{fixed[0].out, fixed[1].out, fixed[2].out} + : CurveValues{0., fixed[0].out, fixed[1].out, fixed[2].out, 1.}; auto slopes = calcSlopes(X, Y); @@ -215,12 +232,12 @@ void Powermap::updateSpline() // This follows the monotone cubic spline algorithm of Steffen, from: // "A Simple Method for Monotonic Interpolation in One Dimension" -std::vector<float> Powermap::calcSlopes(const Powers& X, const Powers& Y) +std::vector<float> CurveMap::calcSlopes(const CurveValues& X, const CurveValues& Y) { - Powers m(X.size()); + CurveValues m(X.size()); - Powers d(X.size() - 1); - Powers h(X.size() - 1); + CurveValues d(X.size() - 1); + CurveValues h(X.size() - 1); for (std::size_t i = 0; i < d.size(); ++i) { h[i] = X[i + 1] - X[i]; @@ -245,7 +262,16 @@ std::vector<float> Powermap::calcSlopes(const Powers& X, const Powers& Y) return m; } -Power Powermap::clamp(Power in, Power min, Power max) const +CurveValue CurveMap::clamp(CurveValue in, CurveValue min, CurveValue max) const { return std::max(min, std::min(in, max)); } + +bool CurveMap::operator==(const CurveMap& other) const +{ + return getFixed0() == other.getFixed0() && + getFixed1() == other.getFixed1() && + getFixed2() == other.getFixed2() && + getShelf() == other.getShelf() && + getInvert() == other.getInvert(); +} diff --git a/src/curvemap.h b/src/curvemap.h new file mode 100644 index 0000000..926543d --- /dev/null +++ b/src/curvemap.h @@ -0,0 +1,94 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * curvemap.h + * + * Fri Apr 17 23:06:12 CEST 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 <array> +#include <vector> + +class CurveMap +{ +public: + using CurveValue = float; + using CurveValues = std::vector<CurveValue>; + + bool operator==(const CurveMap& other) const; + + struct CurveValuePair + { + CurveValue in; + CurveValue out; + + bool operator==(const CurveValuePair& other) + { + return in == other.in || out == other.out; + } + bool operator!=(const CurveValuePair& other) + { + return !(*this == other); + } + }; + + CurveValue map(CurveValue in); + void reset(); + + void setFixed0(CurveValuePair new_value); + void setFixed1(CurveValuePair new_value); + void setFixed2(CurveValuePair new_value); + void setShelf(bool enable); + + //! If enabled, inversion inverts (1 - x) the input value before mapping + //! it through the curve. + void setInvert(bool enable); + + CurveValuePair getFixed0() const; + CurveValuePair getFixed1() const; + CurveValuePair getFixed2() const; + bool getShelf() const; + bool getInvert() const; + +private: + static constexpr CurveValue eps = 1e-4; + static constexpr std::array<CurveValuePair, 3> default_fixed { + CurveValuePair {eps, eps}, + CurveValuePair {.5, .5}, + CurveValuePair {1 - eps, 1 - eps} + }; + + // input parameters (state of this class) + std::array<CurveValuePair, 3> fixed { default_fixed }; + bool shelf { true }; + bool invert { false }; + + // spline parameters (deterministically computed from the input parameters) + bool spline_needs_update { true }; + std::array<float, 5> m { 0, 0, 0, 0, 0 }; + + void updateSpline(); + std::vector<float> calcSlopes(const CurveValues& X, const CurveValues& P); + + CurveValue clamp(CurveValue in, CurveValue min, CurveValue max) const; +};
\ No newline at end of file diff --git a/src/directory.cc b/src/directory.cc index 596b045..27637a9 100644 --- a/src/directory.cc +++ b/src/directory.cc @@ -27,14 +27,14 @@ #include "directory.h" #include <dirent.h> -#include <stdio.h> +#include <cstdio> #include <string> #include <algorithm> #include <vector> -#include <string.h> +#include <cstring> #include <unistd.h> -#include <platform.h> +#include "platform.h" #if DG_PLATFORM == DG_PLATFORM_WINDOWS #include <direct.h> diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index abe57be..093b0bf 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -31,9 +31,10 @@ #include <cassert> #include <cstring> #include <mutex> +#include <thread> +#include <chrono> -#include <event.h> -#include <audiotypes.h> +#include "audiotypes.h" #include <config.h> #include <hugin.hpp> @@ -312,7 +313,7 @@ repeat: assert(t >= 0); assert(t < evt.buffer_size - evt.buffer_ptr); - if(evt.rampdownInProgress() && evt.rampdown_offset < (evt.t + t) && + if(evt.hasRampdown() && evt.rampdown_offset < (pos + t) && evt.rampdown_count > 0) { if(evt.ramp_length > 0) @@ -362,6 +363,11 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t* s, size_t sz) AudioFile& af = *sample_event.file; + while(freewheel && !af.isLoaded()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + if(!af.isLoaded() || !af.isValid() || (s == nullptr)) { // This event cannot be played - schedule for deletion and continue. @@ -392,7 +398,7 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t* s, size_t sz) { // TODO: We should make all audiofiles reference counted and get rid of this lock. - std::lock_guard<std::mutex> guard(af.mutex); + const std::lock_guard<std::mutex> guard(af.mutex); renderSampleEvent(sample_event, pos, s, sz); diff --git a/src/drumgizmo.h b/src/drumgizmo.h index 89c2960..a47898b 100644 --- a/src/drumgizmo.h +++ b/src/drumgizmo.h @@ -46,8 +46,6 @@ #include "inputprocessor.h" #include "zrwrapper.h" -#define REFSFILE "refs.conf" - class DrumGizmo { public: @@ -105,7 +103,7 @@ protected: Random rand; std::array<ZRWrapper, NUM_CHANNELS> zita; - std::array<std::unique_ptr<sample_t>, NUM_CHANNELS> resampler_input_buffer; + std::array<std::unique_ptr<sample_t[]>, NUM_CHANNELS> resampler_input_buffer; double ratio = 1.0; std::vector<sample_t> scratch_buffer; }; diff --git a/src/drumgizmoconf.h b/src/drumgizmoconf.h index f031730..ee14ec6 100644 --- a/src/drumgizmoconf.h +++ b/src/drumgizmoconf.h @@ -26,7 +26,7 @@ */ #pragma once -#include <configfile.h> +#include "configfile.h" class DrumgizmoConfig : public ConfigFile diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc index 9167201..b3a0e5b 100644 --- a/src/drumkitloader.cc +++ b/src/drumkitloader.cc @@ -38,8 +38,6 @@ #include "domloader.h" #include "directory.h" -#define REFSFILE "refs.conf" - DrumKitLoader::DrumKitLoader(Settings& settings, DrumKit& kit, AudioInputEngine& ie, Random& rand, @@ -93,7 +91,7 @@ void DrumKitLoader::deinit() framesize_semaphore.post(); { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); load_queue.clear(); } @@ -301,13 +299,13 @@ void DrumKitLoader::loadKitAudio(const DrumKit& kit) void DrumKitLoader::skip() { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); load_queue.clear(); } void DrumKitLoader::setFrameSize(std::size_t framesize) { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); this->framesize = framesize; framesize_semaphore.post(); // Signal that the framesize has been set. } @@ -324,7 +322,7 @@ void DrumKitLoader::thread_main() { std::size_t size; { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); size = load_queue.size(); } @@ -367,7 +365,7 @@ void DrumKitLoader::thread_main() std::string filename; { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); if(load_queue.size() == 0) { diff --git a/src/event.h b/src/engineevent.h index 737fb18..9c60a4a 100644 --- a/src/event.h +++ b/src/engineevent.h @@ -1,6 +1,6 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** - * event.h + * engineevent.h * * Fri Jun 3 12:10:50 CEST 2011 * Copyright 2011 Bent Bisballe Nyeng diff --git a/src/events.h b/src/events.h index 9e3cae1..538127b 100644 --- a/src/events.h +++ b/src/events.h @@ -84,11 +84,12 @@ public: { } - bool rampdownInProgress() const + bool hasRampdown() const { return rampdown_count != -1; } + cacheid_t cache_id; sample_t* buffer; std::size_t buffer_size; diff --git a/src/inputfilter.h b/src/inputfilter.h index 45dd01e..128dfb0 100644 --- a/src/inputfilter.h +++ b/src/inputfilter.h @@ -26,7 +26,7 @@ */ #pragma once -#include <event.h> +#include "engineevent.h" //! An abstract filter component for the InputProcessor class filter chain. class InputFilter diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc index 7d12bdd..c0c0e92 100644 --- a/src/inputprocessor.cc +++ b/src/inputprocessor.cc @@ -139,20 +139,18 @@ std::size_t InputProcessor::getLatency() const return latency; } -//! Applies choke with rampdown time in ms to event starting at offset. -static void applyChoke(Settings& settings, SampleEvent& event, - double length_ms, timepos_t offset) +void InputProcessor::applyChoke(Settings& settings, SampleEvent& event, + double length_ms, timepos_t offset, std::size_t pos) { std::size_t ramp_length = (length_ms / 1000.) * settings.samplerate.load(); event.rampdown_count = ramp_length; - event.rampdown_offset = offset; + event.rampdown_offset = offset + pos; event.ramp_length = ramp_length; } -//! Applies choke group actions to active events based on the input event -static void applyChokeGroup(Settings& settings, DrumKit& kit, - Instrument& instr, event_t& event, - EventsDS& events_ds) +void InputProcessor::applyChokeGroup(Settings& settings, DrumKit& kit, + Instrument& instr, event_t& event, + EventsDS& events_ds, std::size_t pos) { std::size_t instrument_id = event.instrument; if(instr.getGroup() == "") @@ -175,16 +173,15 @@ static void applyChokeGroup(Settings& settings, DrumKit& kit, 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); + applyChoke(settings, event_sample, 68, event.offset, pos); } } } } -//! Applies directed choke actions to active events based on the input event -static void applyDirectedChoke(Settings& settings, DrumKit& kit, - Instrument& instr, event_t& event, - EventsDS& events_ds) +void InputProcessor::applyDirectedChoke(Settings& settings, DrumKit& kit, + Instrument& instr, event_t& event, + EventsDS& events_ds, std::size_t pos) { for(const auto& choke : instr.getChokes()) { @@ -202,7 +199,7 @@ static void applyDirectedChoke(Settings& settings, DrumKit& kit, event_sample.rampdown_count == -1) // Only if not already ramping. { // choke.choketime is in ms - applyChoke(settings, event_sample, choke.choketime, event.offset); + applyChoke(settings, event_sample, choke.choketime, event.offset, pos); } } } @@ -245,10 +242,10 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos, } // Mute other instruments in the same instrument/choke group - applyChokeGroup(settings, kit, *instr, event, events_ds); + applyChokeGroup(settings, kit, *instr, event, events_ds, pos); // Apply directed chokes to mute other instruments if needed - applyDirectedChoke(settings, kit, *instr, event, events_ds); + applyDirectedChoke(settings, kit, *instr, event, events_ds, pos); auto const power_max = instr->getMaxPower(); auto const power_min = instr->getMinPower(); @@ -266,7 +263,7 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos, { limitVoices(instrument_id, settings.voice_limit_max.load(), - settings.voice_limit_rampdown.load()); + settings.voice_limit_rampdown.load(), pos); } //Given that audio files could be invalid, maybe we must add the new @@ -356,7 +353,7 @@ bool InputProcessor::processChoke(event_t& event, event_sample.rampdown_count == -1) // Only if not already ramping. { // Fixed group rampdown time of 68ms, independent of samplerate - applyChoke(settings, event_sample, 450, event.offset); + applyChoke(settings, event_sample, 450, event.offset, pos); } } } @@ -397,7 +394,7 @@ bool InputProcessor::processStop(event_t& event) void InputProcessor::limitVoices(std::size_t instrument_id, std::size_t max_voices, - float rampdown_time) + float rampdown_time, std::size_t pos) { const auto& group_ids=events_ds.getSampleEventGroupIDsOf(instrument_id); @@ -418,7 +415,7 @@ void InputProcessor::limitVoices(std::size_t instrument_id, } const auto& sample=events_ds.get<SampleEvent>(event_ids[0]); - return !sample.rampdownInProgress(); + return !sample.hasRampdown(); }; EventGroupIDs non_ramping; @@ -432,7 +429,6 @@ void InputProcessor::limitVoices(std::size_t instrument_id, } //Let us get the eldest... - //TODO: where is the playhead? Should we add it to the offset? auto compare_event_offsets = [this](EventGroupID a, EventGroupID b) { @@ -456,6 +452,6 @@ void InputProcessor::limitVoices(std::size_t instrument_id, for(const auto& event_id : event_ids) { auto& sample=events_ds.get<SampleEvent>(event_id); - applyChoke(settings, sample, rampdown_time, sample.offset); + applyChoke(settings, sample, rampdown_time, sample.offset, pos); } } diff --git a/src/inputprocessor.h b/src/inputprocessor.h index 971cc85..a8dc45b 100644 --- a/src/inputprocessor.h +++ b/src/inputprocessor.h @@ -30,13 +30,13 @@ #include <list> #include <memory> -#include <event.h> #include "drumkit.h" #include "events.h" #include "events_ds.h" #include "id.h" #include "inputfilter.h" +#include "engineevent.h" struct Settings; class Random; @@ -64,10 +64,23 @@ private: bool processChoke(event_t& event, std::size_t pos, double resample_ratio); bool processStop(event_t& event); + //! Applies choke with rampdown time in ms to event starting at offset. + void applyChoke(Settings& settings, SampleEvent& event, + double length_ms, timepos_t offset, std::size_t pos); + + //! Applies choke group actions to active events based on the input event + void applyChokeGroup(Settings& settings, DrumKit& kit, + Instrument& instr, event_t& event, + EventsDS& events_ds, std::size_t pos); + //! Applies directed choke actions to active events based on the input event + void applyDirectedChoke(Settings& settings, DrumKit& kit, + Instrument& instr, event_t& event, + EventsDS& events_ds, std::size_t pos); + //! Ramps down samples from events_ds is there are more groups playing than //! max_voices for a given instrument. void limitVoices(std::size_t instrument_id, std::size_t max_voices, - float rampdown_time); + float rampdown_time, std::size_t pos); std::vector<std::unique_ptr<InputFilter>> filters; diff --git a/src/midimapper.cc b/src/midimapper.cc index b9316c5..345ce2f 100644 --- a/src/midimapper.cc +++ b/src/midimapper.cc @@ -30,7 +30,7 @@ std::vector<int> MidiMapper::lookup(int note_id) { std::vector<int> instruments; - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); for(const auto& map_entry : midimap) { @@ -49,7 +49,7 @@ std::vector<int> MidiMapper::lookup(int note_id) void MidiMapper::swap(instrmap_t& instrmap, midimap_t& midimap) { - std::lock_guard<std::mutex> guard(mutex); + const std::lock_guard<std::mutex> guard(mutex); std::swap(this->instrmap, instrmap); std::swap(this->midimap, midimap); diff --git a/src/nolocale.h b/src/nolocale.h index 430c38c..7a10a9c 100644 --- a/src/nolocale.h +++ b/src/nolocale.h @@ -27,7 +27,7 @@ #pragma once #include <locale.h> -#include <stdarg.h> +#include <cstdarg> #include <cstdlib> static inline double atof_nol(const char* nptr) diff --git a/src/owner.h b/src/owner.h new file mode 100644 index 0000000..0c67ae5 --- /dev/null +++ b/src/owner.h @@ -0,0 +1,24 @@ +//! -*- c++ -*- +#pragma once + +//#include <type_traits> // for enable_if_t, is_convertible, is_assignable + +// Taken from the GSL Core Guidelines Support Library +namespace gsl +{ + +// +// owner +// +// `gsl::owner<T>` is designed as a safety mechanism for code that must deal directly with raw pointers that own memory. +// Ideally such code should be restricted to the implementation of low-level abstractions. `gsl::owner` can also be used +// as a stepping point in converting legacy code to use more modern RAII constructs, such as smart pointers. +// +// T must be a pointer type +// - disallow construction from any type other than pointer type +// +//template <class T, class = std::enable_if_t<std::is_pointer<T>::value>> +template<typename T> +using owner = T; + +}// gsl:: diff --git a/src/path.cc b/src/path.cc index 993f9a6..eaa1e04 100644 --- a/src/path.cc +++ b/src/path.cc @@ -30,8 +30,8 @@ #include <libgen.h> #endif/*__MINGW32__*/ -#include <string.h> -#include <stdlib.h> +#include <cstring> +#include <cstdlib> std::string getPath(const std::string& file) { diff --git a/src/powerlist.cc b/src/powerlist.cc index 23d9795..f852ac9 100644 --- a/src/powerlist.cc +++ b/src/powerlist.cc @@ -27,8 +27,8 @@ #include "powerlist.h" #include <algorithm> -#include <stdlib.h> -#include <string.h> +#include <cstdlib> +#include <cstring> #include <hugin.hpp> @@ -36,7 +36,7 @@ #ifdef __STRICT_ANSI__ #undef __STRICT_ANSI__ #endif -#include <math.h> +#include <cmath> #include "random.h" #include "settings.h" @@ -84,6 +84,7 @@ const Channel* PowerList::getMasterChannel() af->load(nullptr, LOAD_SIZE); float silence{0.f}; + (void)silence; std::size_t silence_length{4u}; for (auto s = af->size; s > 0 && s > af->size - silence_length; --s) { diff --git a/src/powermap.h b/src/powermap.h deleted file mode 100644 index 3a406cc..0000000 --- a/src/powermap.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- Mode: c++ -*- */ -/*************************************************************************** - * powermap.h - * - * Fri Apr 17 23:06:12 CEST 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 <array> -#include <vector> - -class Powermap -{ -public: - using Power = float; - using Powers = std::vector<Power>; - struct PowerPair - { - Power in; - Power out; - - bool operator!=(const PowerPair& other) - { - return in != other.in || out != other.out; - } - }; - - Powermap(); - - Power map(Power in); - void reset(); - - void setFixed0(PowerPair new_value); - void setFixed1(PowerPair new_value); - void setFixed2(PowerPair new_value); - void setShelf(bool enable); - - PowerPair getFixed0() const; - PowerPair getFixed1() const; - PowerPair getFixed2() const; - -private: - // input parameters (state of this class) - std::array<PowerPair, 3> fixed; - bool shelf; - - // spline parameters (deterministically computed from the input parameters) - bool spline_needs_update; - std::array<float, 5> m; - const Power eps = 1e-4; - - void updateSpline(); - std::vector<float> calcSlopes(const Powers& X, const Powers& P); - - Power clamp(Power in, Power min, Power max) const; -}; diff --git a/src/powermapfilter.h b/src/powermapfilter.h index 263f809..b9dfe5b 100644 --- a/src/powermapfilter.h +++ b/src/powermapfilter.h @@ -27,7 +27,7 @@ #pragma once #include "inputfilter.h" -#include "powermap.h" +#include "curvemap.h" struct Settings; @@ -43,5 +43,5 @@ public: private: Settings& settings; - Powermap powermap; + CurveMap powermap; }; @@ -28,8 +28,8 @@ #include <hugin.hpp> #include <limits> -#include <assert.h> -#include <string.h> +#include <cassert> +#include <cstring> #include <chrono> #include <thread> @@ -37,8 +37,8 @@ #if DG_PLATFORM != DG_PLATFORM_WINDOWS #include <semaphore.h> -#include <errno.h> -#include <stdio.h> +#include <cerrno> +#include <cstdio> #include <sys/time.h> #endif diff --git a/src/syncedsettings.h b/src/syncedsettings.h index 0fe5efd..d12277a 100644 --- a/src/syncedsettings.h +++ b/src/syncedsettings.h @@ -64,7 +64,7 @@ public: : mutex{} , data{} { - std::lock_guard<std::mutex> lock{other.mutex}; + const std::lock_guard<std::mutex> lock{other.mutex}; data = other.data; } @@ -72,7 +72,7 @@ public: : mutex{} , data{} { - std::lock_guard<std::mutex> lock{other.mutex}; + const std::lock_guard<std::mutex> lock{other.mutex}; std::swap(data, other.data); } @@ -80,19 +80,19 @@ public: { if (*this != &other) { - std::lock_guard<std::mutex> lock{mutex}; - std::lock_guard<std::mutex> lock2{other.mutex}; + const std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock2{other.mutex}; data = other.data; } return *this; } - + Group<T>& operator=(Group<T>&& other) { if (*this != &other) { - std::lock_guard<std::mutex> lock{mutex}; - std::lock_guard<std::mutex> lock2{other.mutex}; + const std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock2{other.mutex}; std::swap(data, other.data); } return *this; @@ -100,7 +100,7 @@ public: operator T() const { - std::lock_guard<std::mutex> lock{mutex}; + const std::lock_guard<std::mutex> lock{mutex}; return data; } }; diff --git a/src/translation.cc b/src/translation.cc index 18763e4..c71f6c0 100644 --- a/src/translation.cc +++ b/src/translation.cc @@ -62,13 +62,13 @@ bool comparator(const Text& a, const Text& b) Translation::Translation() { - std::lock_guard<std::mutex>(singleton.mutex); + const std::lock_guard<std::mutex> lock(singleton.mutex); ++singleton.refcnt; } Translation::~Translation() { - std::lock_guard<std::mutex>(singleton.mutex); + const std::lock_guard<std::mutex> lock(singleton.mutex); --singleton.refcnt; @@ -219,7 +219,7 @@ bool Translation::load(const char* catalog, std::size_t size) std::sort(texts.begin(), texts.end(), comparator); { - std::lock_guard<std::mutex>(singleton.mutex); + const std::lock_guard<std::mutex> lock(singleton.mutex); std::swap(singleton.texts, texts); } @@ -228,7 +228,7 @@ bool Translation::load(const char* catalog, std::size_t size) const char* Translation::gettext(std::uint64_t msgid, const char* original) { - std::lock_guard<std::mutex>(singleton.mutex); + const std::lock_guard<std::mutex> lock(singleton.mutex); if(singleton.refcnt == 0) { return original; diff --git a/src/versionstr.cc b/src/versionstr.cc index 8c6c66f..ac16e41 100644 --- a/src/versionstr.cc +++ b/src/versionstr.cc @@ -26,9 +26,8 @@ */ #include "versionstr.h" -#include <memory.h> -#include <stdlib.h> -#include <stdio.h> +#include <cstdlib> +#include <cstdio> #include <hugin.hpp> diff --git a/test/Makefile.am b/test/Makefile.am index 6441fd3..0827cae 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -8,7 +8,7 @@ TESTS = resource enginetest paintertest configfile audiocache \ randomtest atomictest syncedsettingstest imagecachetest \ semaphoretest drumkitcreatortest bytesizeparsertest notifiertest \ dgxmlparsertest domloadertest configparsertest midimapparsertest \ - eventsdstest powermaptest midimappertest + eventsdstest curvemaptest midimappertest if WITH_NLS TESTS += translationtest @@ -51,7 +51,8 @@ audiocache_CXXFLAGS = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/hugin -DDISABLE_HUGIN \ $(PTHREAD_CFLAGS) \ - $(SNDFILE_CFLAGS) + $(SNDFILE_CFLAGS) \ + -Wno-deprecated-declarations audiocache_LDFLAGS = $(PTHREAD_LIBS) $(SNDFILE_LIBS) audiocache_SOURCES = \ $(top_srcdir)/src/audiocache.cc \ @@ -72,7 +73,8 @@ audiocachefile_CXXFLAGS = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/hugin -DDISABLE_HUGIN \ $(PTHREAD_CFLAGS) \ - $(SNDFILE_CFLAGS) + $(SNDFILE_CFLAGS) \ + -Wno-deprecated-declarations audiocachefile_LDFLAGS = $(PTHREAD_LIBS) $(SNDFILE_LIBS) audiocachefile_SOURCES = \ $(top_srcdir)/src/audiocachefile.cc \ @@ -102,7 +104,8 @@ audiocacheeventhandler_CXXFLAGS = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/hugin -DDISABLE_HUGIN \ $(PTHREAD_CFLAGS) \ - $(SNDFILE_CFLAGS) + $(SNDFILE_CFLAGS) \ + -Wno-deprecated-declarations audiocacheeventhandler_LDFLAGS = $(PTHREAD_LIBS) $(SNDFILE_LIBS) audiocacheeventhandler_SOURCES = \ $(top_srcdir)/src/audiocacheeventhandler.cc \ @@ -218,7 +221,8 @@ semaphoretest_CXXFLAGS = \ $(DEBUG_FLAGS) \ -I$(top_srcdir)/src \ -I$(top_srcdir)/hugin \ - $(PTHREAD_CFLAGS) + $(PTHREAD_CFLAGS) \ + -Wno-deprecated-declarations semaphoretest_LDFLAGS = $(PTHREAD_LIBS) semaphoretest_SOURCES = \ $(top_srcdir)/hugin/hugin.c \ @@ -325,14 +329,14 @@ eventsdstest_SOURCES = \ eventsdstest.cc \ uunit/uunit.cc -powermaptest_CXXFLAGS = \ - -I$(top_srcdir)/test/uunit -DOUTPUT=\"powermaptest\" \ +curvemaptest_CXXFLAGS = \ + -I$(top_srcdir)/test/uunit -DOUTPUT=\"curvemaptest\" \ $(DEBUG_FLAGS) \ -I$(top_srcdir)/src -powermaptest_LDFLAGS = -powermaptest_SOURCES = \ - $(top_srcdir)/src/powermap.cc \ - powermaptest.cc \ +curvemaptest_LDFLAGS = +curvemaptest_SOURCES = \ + $(top_srcdir)/src/curvemap.cc \ + curvemaptest.cc \ uunit/uunit.cc midimappertest_CXXFLAGS = \ diff --git a/test/audiocacheidmanagertest.cc b/test/audiocacheidmanagertest.cc index c84926b..9d8be25 100644 --- a/test/audiocacheidmanagertest.cc +++ b/test/audiocacheidmanagertest.cc @@ -52,39 +52,39 @@ public: TestableAudioCacheIDManager manager; manager.init(2); - cache_t c1; c1.afile = (AudioCacheFile*)1; + CacheBuffer c1; c1.afile = (AudioCacheFile*)1; auto id1 = manager.registerID(c1); uUNIT_ASSERT(id1 != CACHE_DUMMYID); uUNIT_ASSERT(id1 != CACHE_NOID); uUNIT_ASSERT_EQUAL(1, manager.getAvailableIDs()); - cache_t c2; c2.afile = (AudioCacheFile*)2; + CacheBuffer c2; c2.afile = (AudioCacheFile*)2; auto id2 = manager.registerID(c2); uUNIT_ASSERT(id2 != CACHE_DUMMYID); uUNIT_ASSERT(id2 != CACHE_NOID); uUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs()); - cache_t c3; c3.afile = (AudioCacheFile*)3; + CacheBuffer c3; c3.afile = (AudioCacheFile*)3; auto id3 = manager.registerID(c3); uUNIT_ASSERT(id3 == CACHE_DUMMYID); uUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs()); - cache_t& tc1 = manager.getCache(id1); + CacheBuffer& tc1 = manager.getCache(id1); uUNIT_ASSERT_EQUAL(c1.afile, tc1.afile); - cache_t& tc2 = manager.getCache(id2); + CacheBuffer& tc2 = manager.getCache(id2); uUNIT_ASSERT_EQUAL(c2.afile, tc2.afile); manager.releaseID(id1); uUNIT_ASSERT_EQUAL(1, manager.getAvailableIDs()); - cache_t c4; c4.afile = (AudioCacheFile*)4; + CacheBuffer c4; c4.afile = (AudioCacheFile*)4; auto id4 = manager.registerID(c4); uUNIT_ASSERT(id4 != CACHE_DUMMYID); uUNIT_ASSERT(id4 != CACHE_NOID); uUNIT_ASSERT_EQUAL(0, manager.getAvailableIDs()); - cache_t& tc4 = manager.getCache(id4); + CacheBuffer& tc4 = manager.getCache(id4); uUNIT_ASSERT_EQUAL(c4.afile, tc4.afile); manager.releaseID(id2); diff --git a/test/curvemaptest.cc b/test/curvemaptest.cc new file mode 100644 index 0000000..cb90d1b --- /dev/null +++ b/test/curvemaptest.cc @@ -0,0 +1,267 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * curvemaptest.cc + * + * Fri Jul 26 19:43:32 CEST 2024 + * Copyright 2024 Sander Vocke + * sandervocke@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 <uunit.h> +#include <map> + +#include "../src/curvemap.h" +#include <iostream> +#include <iomanip> + +class test_curvemaptest + : public uUnit +{ +public: + test_curvemaptest() + { + uUNIT_TEST(test_curvemaptest::check_default); + uUNIT_TEST(test_curvemaptest::check_default_invert); + uUNIT_TEST(test_curvemaptest::check_default_disabled_shelf); + uUNIT_TEST(test_curvemaptest::check_default_disabled_shelf_invert); + uUNIT_TEST(test_curvemaptest::check_fixed1_075_025); + uUNIT_TEST(test_curvemaptest::check_fixed1_075_025_invert); + uUNIT_TEST(test_curvemaptest::check_shelf_060); + uUNIT_TEST(test_curvemaptest::check_shelf_060_invert); + uUNIT_TEST(test_curvemaptest::check_reset); + } + + const std::map<double, double> default_map = { + {0.0, 1e-4}, + {1e-4, 1e-4}, + {0.1, 0.1}, + {0.2, 0.2}, + {0.3, 0.3}, + {0.4, 0.4}, + {0.5, 0.5}, + {0.6, 0.6}, + {0.7, 0.7}, + {0.8, 0.8}, + {0.9, 0.9}, + {1.0 - 1e-4, 1.0 - 1e-4}, + {1.0, 1.0 - 1e-4} + }; + + const std::map<double, double> identity_map = { + {0.0, 0.0}, + {0.1, 0.1}, + {0.2, 0.2}, + {0.3, 0.3}, + {0.4, 0.4}, + {0.5, 0.5}, + {0.6, 0.6}, + {0.7, 0.7}, + {0.8, 0.8}, + {0.9, 0.9}, + {1.0, 1.0} + }; + + const std::map<double, double> fixed1_075_025_map = { + {0.0, 1e-4}, + {1e-4, 1e-4}, + {0, 0.0001}, + {0.1, 0.0295469705015421}, + {0.2, 0.0536915548145771}, + {0.3, 0.0760560110211372}, + {0.4, 0.100195862352848}, + {0.5, 0.129666686058044}, + {0.6, 0.168024003505707}, + {0.7, 0.218823373317719}, + {0.8, 0.325357049703598}, + {0.9, 0.64416378736496}, + {1.0 - 1e-4, 1.0 - 1e-4}, + {1.0, 1.0 - 1e-4} + }; + + const std::map<double, double> shelf_060_map = { + {0.0, 1e-4}, + {1e-4, 1e-4}, + {0.1, 0.1}, + {0.2, 0.2}, + {0.3, 0.3}, + {0.4, 0.4}, + {0.5, 0.5}, + {0.6, 0.6}, + {0.7, 0.6}, + {0.8, 0.6}, + {0.9, 0.6}, + {1.0, 0.6} + }; + + void check_default() + { + auto dataset = this->default_map; + + CurveMap map; + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_reset() + { + auto dataset = this->default_map; + + CurveMap map; + map.setFixed0({0.1, 0.2}); + map.setFixed1({0.2, 0.3}); + map.setFixed2({0.3, 0.4}); + map.setInvert(!map.getInvert()); + map.setShelf(!map.getShelf()); + + bool any_difference = false; + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + if (expect != map.map(in)) { + any_difference = true; + break; + } + } + + uASSERT(any_difference); + + map.reset(); + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_default_invert() + { + auto dataset = this->default_map; + + CurveMap map; + map.setInvert(true); + + for (auto& entry : dataset) { + auto in = 1.0 - entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_default_disabled_shelf() + { + auto dataset = this->identity_map; + + CurveMap map; + map.setShelf(false); + map.setFixed2({0.6, 0.6}); + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + // std::cout << "{" << in << ", " << std::setprecision (15) << map.map(in) << "}," << std::endl; // FIXME + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_default_disabled_shelf_invert() + { + auto dataset = this->identity_map; + + CurveMap map; + map.setShelf(false); + map.setFixed2({0.6, 0.6}); + map.setInvert(true); + + for (auto& entry : dataset) { + auto in = 1.0 - entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_fixed1_075_025() + { + auto dataset = this->fixed1_075_025_map; + + CurveMap map; + map.setFixed1({0.75, 0.25}); + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + // std::cout << "{" << in << ", " << std::setprecision (15) << map.map(in) << "}," << std::endl; // FIXME + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_fixed1_075_025_invert() + { + auto dataset = this->fixed1_075_025_map; + + CurveMap map; + map.setFixed1({0.75, 0.25}); + map.setInvert(true); + + for (auto& entry : dataset) { + auto in = 1.0 - entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_shelf_060() + { + auto dataset = this->shelf_060_map; + + CurveMap map; + map.setFixed2({0.6, 0.6}); + map.setShelf(true); + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_shelf_060_invert() + { + auto dataset = this->shelf_060_map; + + CurveMap map; + map.setFixed2({0.6, 0.6}); + map.setShelf(true); + map.setInvert(true); + + for (auto& entry : dataset) { + auto in = 1.0 - entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } +}; + +// Registers the fixture into the 'registry' +static test_curvemaptest test; diff --git a/test/dgreftest/midiinputengine.h b/test/dgreftest/midiinputengine.h index ffd22f8..e76a182 100644 --- a/test/dgreftest/midiinputengine.h +++ b/test/dgreftest/midiinputengine.h @@ -26,15 +26,15 @@ */ #pragma once -#include <audioinputenginemidi.h> -#include <midimapper.h> -#include <midimapparser.h> #include <string> #include <vector> -#include <event.h> #include <smf.h> +#include <audioinputenginemidi.h> +#include <midimapper.h> +#include <midimapparser.h> + class MidifileInputEngine : public AudioInputEngineMidi { diff --git a/test/paintertest.cc b/test/paintertest.cc index 5a1c44a..10b96dc 100644 --- a/test/paintertest.cc +++ b/test/paintertest.cc @@ -91,7 +91,7 @@ public: has_alpha = alpha; image_data.resize(_width * _height); - image_data_raw.resize(_width * _height); + image_data_raw.resize(4 * _width * _height); // Store x and y coordinates as red and green colour components for(std::uint8_t x = 0; x < _width; ++x) diff --git a/test/powermaptest.cc b/test/powermaptest.cc deleted file mode 100644 index bef5bdc..0000000 --- a/test/powermaptest.cc +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: c++ -*- */ -/*************************************************************************** - * powermaptest.cc - * - * Sun Apr 19 23:23:37 CEST 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 <uunit.h> - -#include "../src/powermap.h" - -class test_powermaptest - : public uUnit -{ -public: - test_powermaptest() - { - uUNIT_TEST(test_powermaptest::check_values); - } - - void check_values() - { - Powermap powermap; - - // TODO - // std::cout << powermap.map(.8) << std::endl; - // uUNIT_ASSERT_EQUAL(powermap.map(.8), .8); - } -}; - -// Registers the fixture into the 'registry' -static test_powermaptest test; diff --git a/tools/add_file b/tools/add_file index a704029..51d50b4 100755 --- a/tools/add_file +++ b/tools/add_file @@ -26,6 +26,10 @@ function allfile() { then NAME="Goran Mekić"; EMAIL="meka@tilda.center" fi + if [ "$USER" == "sander" ] + then + NAME="Sander Vocke"; EMAIL="sandervocke@gmail.com" + fi echo "/* -*- Mode: c++ -*- */" > $1; echo "/***************************************************************************" >> $1; @@ -1 +1 @@ -#define VERSION "0.9.19" +#define VERSION "0.9.20" |