summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog79
-rw-r--r--RELEASE-CHECKLIST2
-rwxr-xr-xautogen.sh1
-rw-r--r--drumgizmo/drumgizmoc.cc2
-rw-r--r--drumgizmo/input/jackmidi.cc2
-rw-r--r--drumgizmo/input/midifile.h2
-rw-r--r--drumgizmo/input/test.cc1
-rw-r--r--drumgizmo/input/test.h1
m---------plugin/plugingizmo0
-rw-r--r--plugingui/diskstreamingframecontent.cc4
-rw-r--r--plugingui/locale/da.po2
-rw-r--r--plugingui/locale/drumgizmo.pot2
-rw-r--r--plugingui/locale/fr.po2
-rw-r--r--src/Makefile.am5
-rw-r--r--src/audioinputengine.h2
-rw-r--r--src/drumgizmo.cc3
-rw-r--r--src/engineevent.h (renamed from src/event.h)2
-rw-r--r--src/events.h3
-rw-r--r--src/inputfilter.h2
-rw-r--r--src/inputprocessor.cc40
-rw-r--r--src/inputprocessor.h17
-rw-r--r--src/powerlist.cc1
-rw-r--r--test/Makefile.am12
-rw-r--r--test/dgreftest/midiinputengine.h8
-rw-r--r--version.h2
25 files changed, 144 insertions, 53 deletions
diff --git a/ChangeLog b/ChangeLog
index b2f40ea..a598f87 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/autogen.sh b/autogen.sh
index ae4952f..9975ca9 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -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/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/src/Makefile.am b/src/Makefile.am
index 85b10fa..ff02883 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,7 +33,8 @@ 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
@@ -106,7 +107,7 @@ EXTRA_DIST = \
drumgizmoconf.h \
drumkit.h \
drumkitloader.h \
- event.h \
+ engineevent.h \
events.h \
events_ds.h \
grid.h \
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/drumgizmo.cc b/src/drumgizmo.cc
index abe57be..b67e91a 100644
--- a/src/drumgizmo.cc
+++ b/src/drumgizmo.cc
@@ -32,7 +32,6 @@
#include <cstring>
#include <mutex>
-#include <event.h>
#include <audiotypes.h>
#include <config.h>
@@ -312,7 +311,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)
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/powerlist.cc b/src/powerlist.cc
index 23d9795..8dd0b24 100644
--- a/src/powerlist.cc
+++ b/src/powerlist.cc
@@ -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/test/Makefile.am b/test/Makefile.am
index 6441fd3..15ceb7d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -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 \
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/version.h b/version.h
index 311c449..a0ac531 100644
--- a/version.h
+++ b/version.h
@@ -1 +1 @@
-#define VERSION "0.9.19"
+#define VERSION "0.9.20"