summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xautogen.sh1
-rw-r--r--drumgizmo/input/jackmidi.cc8
-rw-r--r--src/DGDOM.h1
-rw-r--r--src/audioinputenginemidi.cc58
-rw-r--r--src/audioinputenginemidi.h1
-rw-r--r--src/dgxmlparser.cc2
-rw-r--r--src/domloader.cc1
-rw-r--r--src/drumkit.cc5
-rw-r--r--src/drumkit.h2
-rw-r--r--src/drumkitloader.cc2
-rw-r--r--src/inputprocessor.cc31
-rw-r--r--src/midimapparser.cc6
-rw-r--r--src/midimapparser.h1
-rw-r--r--src/midimapper.cc42
-rw-r--r--src/midimapper.h15
-rw-r--r--src/settings.h3
16 files changed, 122 insertions, 57 deletions
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/input/jackmidi.cc b/drumgizmo/input/jackmidi.cc
index 445678b..39aebf8 100644
--- a/drumgizmo/input/jackmidi.cc
+++ b/drumgizmo/input/jackmidi.cc
@@ -122,6 +122,12 @@ 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);
+
+ // Disable clear buffer on input port because for Edrumulus usage we need that
+ // two clients use the some output buffer and if we clear it here, the other
+ // application will not get any MIDI input signal. Usually, the output port
+ // is responsible for clearing the buffer, not the input port.
+ //jack_midi_clear_buffer(buffer);
+
pos += num_frames;
}
diff --git a/src/DGDOM.h b/src/DGDOM.h
index a03f0ef..e39f445 100644
--- a/src/DGDOM.h
+++ b/src/DGDOM.h
@@ -139,6 +139,7 @@ struct DrumkitDOM
{
std::string version;
double samplerate;
+ bool islogpower;
MetadataDOM metadata;
diff --git a/src/audioinputenginemidi.cc b/src/audioinputenginemidi.cc
index 4f8ff1f..564a5d6 100644
--- a/src/audioinputenginemidi.cc
+++ b/src/audioinputenginemidi.cc
@@ -78,7 +78,7 @@ bool AudioInputEngineMidi::loadMidiMap(const std::string& file,
instrmap[instruments[i]->getName()] = i;
}
- mmap.swap(instrmap, midimap_parser.midimap);
+ mmap.swap(instrmap, midimap_parser.midimap, midimap_parser.controlthreshmap);
midimap = file;
is_valid = true;
@@ -108,11 +108,6 @@ static const std::uint8_t TypeMask = 0xF0;
// See:
// https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message
-
-// TODO better implementation: use member variable for controller value, set MIDI key on command line
-int hihat_controller = 0; // open hi-hat
-int hihat_midi_key = 26;
-
void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer,
std::size_t midi_buffer_length,
std::size_t offset,
@@ -123,24 +118,6 @@ void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer,
return;
}
-const bool use_edrumulus_debugging_output = false;
-if(use_edrumulus_debugging_output)
-{
- auto type = midi_buffer[0] & TypeMask;
- auto key = midi_buffer[1];
- auto velocity = midi_buffer[2];
- if((type == ControlChange) && (key == 16))
- {
- std::string bar = "--------------------";
- bar[static_cast<int>(static_cast<float>(velocity) / 128 * 20)] = '*';
- printf(std::string(" " + bar + "\n").c_str());
- }
- else
- {
- printf("key: %d, velocity: %d\n", key, velocity);
- }
-}
-
switch(midi_buffer[0] & TypeMask)
{
case NoteOff:
@@ -151,7 +128,7 @@ if(use_edrumulus_debugging_output)
{
auto key = midi_buffer[1];
auto velocity = midi_buffer[2];
- auto instrument_idx = mmap.lookup(key);
+ auto instrument_idx = mmap.lookup(key, controller4_value);
if(velocity != 0 && instrument_idx != -1)
{
// maps velocities to [.5/127, 126.5/127]
@@ -159,16 +136,21 @@ if(use_edrumulus_debugging_output)
events.push_back({ EventType::OnSet, (std::size_t)instrument_idx,
offset, centered_velocity, positional_information });
+
+/*
+// TODO better implementation: use member variable for controller value, set MIDI key on command line
+int hihat_midi_key = 26;
// quick hack, add 1000000 to offset to transport hi-hat controller value
auto instrument_idx1 = mmap.lookup(hihat_midi_key);
if(instrument_idx == instrument_idx1)
{
- if(hihat_controller < 100) // quick hack: hard-coded value
+ if(controller4_value < 100) // quick hack: hard-coded value
{
events.push_back({ EventType::Choke, (std::size_t)instrument_idx,
- 1000000 + hihat_controller, .0f, .0f });
+ 1000000 + controller4_value, .0f, .0f });
}
}
+*/
}
}
@@ -200,18 +182,20 @@ if(instrument_idx == instrument_idx1)
return;
}
- if(controller_number == 4) // hi-hat pedal
+ if(controller_number == 4) // usually, controller 4 is the hi-hat controller
{
+ // in case the hi-hat was just closed, choke current hi-hat samples
+ if(controller4_value < mmap.getMaxControlthresh() && value >= mmap.getMaxControlthresh())
+ {
+ for(auto instrument_idx : mmap.getInstWithControlthresh())
+ {
+ events.push_back({ EventType::Choke, (std::size_t)instrument_idx,
+ offset, .0f, .0f });
+ }
+ }
-// quick hack: if hi-hat control pedal is down, choke hi-hat instrument
-auto instrument_idx = mmap.lookup(hihat_midi_key);
-hihat_controller = value;
-if(value > 100 && instrument_idx != -1) // quick hack: hard-coded value
-{
- events.push_back({ EventType::Choke, (std::size_t)instrument_idx,
- offset, .0f, .0f });
-}
-
+ // Store value for use in next NoteOn event.
+ controller4_value = value;
}
}
break;
diff --git a/src/audioinputenginemidi.h b/src/audioinputenginemidi.h
index 12efd66..7656f8d 100644
--- a/src/audioinputenginemidi.h
+++ b/src/audioinputenginemidi.h
@@ -72,4 +72,5 @@ private:
ConfigFile refs;
float positional_information{0.0f};
+ int controller4_value{0};
};
diff --git a/src/dgxmlparser.cc b/src/dgxmlparser.cc
index bd9af66..b3aba75 100644
--- a/src/dgxmlparser.cc
+++ b/src/dgxmlparser.cc
@@ -209,6 +209,8 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom, LogFunction
res &= attrcpy(dom.version, drumkit, "version", logger, filename, true);
dom.samplerate = 44100.0;
res &= attrcpy(dom.samplerate, drumkit, "samplerate", logger, filename, true);
+ dom.islogpower = false;
+ res &= attrcpy(dom.islogpower, drumkit, "islogpower", logger, filename, true);
// Use the old name and description attributes on the drumkit node as fallback
res &= attrcpy(dom.metadata.title, drumkit, "name", logger, filename, true);
diff --git a/src/domloader.cc b/src/domloader.cc
index 5d411bd..d422c9a 100644
--- a/src/domloader.cc
+++ b/src/domloader.cc
@@ -60,6 +60,7 @@ bool DOMLoader::loadDom(const std::string& basepath,
drumkit.metadata._version = dom.version;
drumkit.metadata._description = dom.metadata.description;
drumkit.metadata._samplerate = dom.samplerate;
+ drumkit.metadata._islogpower = dom.islogpower;
for(const auto& channel: dom.channels)
{
diff --git a/src/drumkit.cc b/src/drumkit.cc
index 9df9a33..2fc16db 100644
--- a/src/drumkit.cc
+++ b/src/drumkit.cc
@@ -55,6 +55,11 @@ bool DrumKit::isValid() const
return this == magic;
}
+bool DrumKit::isLogPower() const
+{
+ return metadata._islogpower;
+}
+
std::string DrumKit::getFile() const
{
return _file;
diff --git a/src/drumkit.h b/src/drumkit.h
index aee5d19..3f2677f 100644
--- a/src/drumkit.h
+++ b/src/drumkit.h
@@ -51,6 +51,7 @@ public:
void clear();
bool isValid() const;
+ bool isLogPower() const;
float getSamplerate() const;
@@ -74,5 +75,6 @@ private:
std::string _description;
float _samplerate{44100.0f};
VersionStr _version;
+ bool _islogpower{false};
} metadata;
};
diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc
index 9167201..91a235f 100644
--- a/src/drumkitloader.cc
+++ b/src/drumkitloader.cc
@@ -136,6 +136,7 @@ bool DrumKitLoader::loadkit(const std::string& file)
settings.drumkit_description.store("");
settings.drumkit_version.store("");
settings.drumkit_samplerate.store(44100);
+ settings.drumkit_is_log_power.store(false);
settings.load_status_text.store("");
settings.drumkit_load_status.store(LoadStatus::Parsing);
@@ -219,6 +220,7 @@ bool DrumKitLoader::loadkit(const std::string& file)
settings.drumkit_description = kit.getDescription();
settings.drumkit_version = kit.getVersion();
settings.drumkit_samplerate = kit.getSamplerate();
+ settings.drumkit_is_log_power = kit.isLogPower();
// only load the default midi map if there is one and no midimap is selected yet
if (drumkitdom.metadata.default_midimap_file != "" && settings.midimap_file == "")
{
diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc
index 4b0a1bb..6d50c68 100644
--- a/src/inputprocessor.cc
+++ b/src/inputprocessor.cc
@@ -210,6 +210,16 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,
return false;
}
+ // In case log-power and dynamic expander is used, apply a special velocity curve
+ if(settings.drumkit_is_log_power)
+ {
+ auto a = 0.981; // compatible to Roland sound modules
+ event.velocity = (126.0f / (pow(a, 126.0f) - 1.0f) * (pow(a, event.velocity * 127.0f - 1.0f) - 1.0f) + 1.0f) / 127.0f;
+ }
+
+// TODO velocity 1 does not select thehighest amplitude samples... -> maybe bug in sample selection
+//event.velocity=1.0;
+
std::size_t instrument_id = event.instrument;
Instrument* instr = nullptr;
@@ -291,19 +301,12 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,
event_sample.scale *= event.velocity;
}
-// TEST dynamic expander
-//printf("event_sample.scale: %f, event.velocity: %f, sample->getPower(): %f, instr->getMinPower(): %f, instr->getMaxPower(): %f\n", event_sample.scale, event.velocity, sample->getPower(), instr->getMinPower(), instr->getMaxPower());
-
-auto target_max_power_db = 10 * log10(instr->getMaxPower());
-auto sel_power_db = 10 * log10(sample->getPower());
-auto target_dynamic_db = 40.0; // dB
-
-auto target_power_db = sqrt(event.velocity) * target_dynamic_db + target_max_power_db - target_dynamic_db;
-auto diff_power_db = target_power_db - sel_power_db;
-event_sample.scale = pow(10.0, diff_power_db / 20);
-
-//printf("event_sample.scale: %f, diff_power_db: %f, target_power_db: %f, sel_power_db: %f\n", event_sample.scale, diff_power_db, target_power_db, sel_power_db);
-
+ // Dynamic expander (only enabled if log-power is configured)
+ if(settings.drumkit_is_log_power)
+ {
+ auto target_dynamic_db = 50.0; // dB, defined maximum dynamic after expander
+ event_sample.scale = pow(10.0, (event.velocity * target_dynamic_db + instr->getMaxPower() - target_dynamic_db - sample->getPower()) / 20);
+ }
}
}
@@ -358,7 +361,7 @@ bool InputProcessor::processChoke(event_t& event,
if(event.offset >= 1000000) // quick hack: added 1000000 to offset to transport hi-hat controller value
{
int hihat_controller = event.offset - 1000000;
- int rampdown_time = static_cast<int>(std::max(100.0, pow(10.0, (127.0 - hihat_controller) / 38.0))); // TODO optimize
+ int rampdown_time = static_cast<int>(std::max(180.0, pow(10.0, (127.0 - hihat_controller) / 38.0))); // TODO optimize
applyChoke(settings, event_sample, rampdown_time, 0);
}
else
diff --git a/src/midimapparser.cc b/src/midimapparser.cc
index 059dfec..2361199 100644
--- a/src/midimapparser.cc
+++ b/src/midimapparser.cc
@@ -45,11 +45,17 @@ bool MidiMapParser::parseFile(const std::string& filename)
constexpr int bad_value = 10000;
auto note = map_node.attribute("note").as_int(bad_value);
auto instr = map_node.attribute("instr").as_string();
+ auto controlthresh = map_node.attribute("controlthresh").as_int(bad_value);
if(std::string(instr) == "" || note == bad_value)
{
continue;
}
+ if(controlthresh != bad_value)
+ {
+ controlthreshmap[note][instr] = controlthresh;
+ }
+
midimap[note] = instr;
}
diff --git a/src/midimapparser.h b/src/midimapparser.h
index d2f2ddd..d13b4f5 100644
--- a/src/midimapparser.h
+++ b/src/midimapparser.h
@@ -35,4 +35,5 @@ public:
bool parseFile(const std::string& filename);
midimap_t midimap;
+ controlthreshmap_t controlthreshmap;
};
diff --git a/src/midimapper.cc b/src/midimapper.cc
index 9593aae..1f75461 100644
--- a/src/midimapper.cc
+++ b/src/midimapper.cc
@@ -26,7 +26,7 @@
*/
#include "midimapper.h"
-int MidiMapper::lookup(int note)
+int MidiMapper::lookup(int note, int controller)
{
std::lock_guard<std::mutex> guard(mutex);
@@ -42,15 +42,53 @@ int MidiMapper::lookup(int note)
return -1;
}
+ if(controller >= 0 && !controlthreshmap[note].empty())
+ {
+ // find instrument where controller is above threshold with smallest distance to threshold
+ int diff = 10000;
+ std::string instr = controlthreshmap[note].begin()->first;
+ for(auto& control_thresh : controlthreshmap[note])
+ {
+ int cur_diff = controller - control_thresh.second;
+ if(cur_diff >= 0 && cur_diff < diff)
+ {
+ diff = cur_diff;
+ instr = control_thresh.first;
+ }
+ }
+ instrmap_it = instrmap.find(instr);
+ }
+
return instrmap_it->second;
}
-void MidiMapper::swap(instrmap_t& instrmap, midimap_t& midimap)
+void MidiMapper::swap(instrmap_t& instrmap, midimap_t& midimap, controlthreshmap_t& controlthreshmap)
{
std::lock_guard<std::mutex> guard(mutex);
std::swap(this->instrmap, instrmap);
std::swap(this->midimap, midimap);
+ std::swap(this->controlthreshmap, controlthreshmap);
+
+ // find instruments which define a control threshold and store it
+ for(auto& control_thresh : this->controlthreshmap)
+ {
+ for(auto& instr : control_thresh.second)
+ {
+ auto instrmap_it = this->instrmap.find(instr.first);
+ if(instrmap_it != this->instrmap.end())
+ {
+ instwithcontrolthresh.push_back(instrmap_it->second);
+ }
+ maxcontrolthresh = std::max(maxcontrolthresh, instr.second);
+ }
+ }
+
+ // in case no controller threshold is defined, use fix definition
+ if(maxcontrolthresh == 0)
+ {
+ maxcontrolthresh = 100;
+ }
}
const midimap_t& MidiMapper::getMap()
diff --git a/src/midimapper.h b/src/midimapper.h
index 4673e33..adb5777 100644
--- a/src/midimapper.h
+++ b/src/midimapper.h
@@ -29,25 +29,36 @@
#include <map>
#include <string>
#include <mutex>
+#include <vector>
typedef std::map<int, std::string> midimap_t;
typedef std::map<std::string, int> instrmap_t;
+typedef std::map<int, std::map<std::string, int> > controlthreshmap_t;
class MidiMapper
{
public:
//! Lookup note in map and return its index.
//! \returns -1 if not found or the note index.
- int lookup(int note);
+ int lookup(int note, int controller = -1);
+
+ //! Get all instruments with controller thresholds defined.
+ std::vector<int>& getInstWithControlthresh() { return instwithcontrolthresh; }
+
+ //! Get the maximum configured control threshold
+ int getMaxControlthresh() { return maxcontrolthresh; }
//! Set new map sets.
- void swap(instrmap_t& instrmap, midimap_t& midimap);
+ void swap(instrmap_t& instrmap, midimap_t& midimap, controlthreshmap_t& controlthreshmap);
const midimap_t& getMap();
private:
instrmap_t instrmap;
midimap_t midimap;
+ controlthreshmap_t controlthreshmap;
+ std::vector<int> instwithcontrolthresh;
+ int maxcontrolthresh{0};
std::mutex mutex;
};
diff --git a/src/settings.h b/src/settings.h
index 5c2e4ee..f2d656e 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -3,7 +3,7 @@
* settings.h
*
* Tue Mar 22 10:59:46 CET 2016
- * Copyright 2016 Christian Glöckner
+ * Copyright 2016 Christian Gl�ckner
* cgloeckner@freenet.de
****************************************************************************/
@@ -53,6 +53,7 @@ struct Settings
Atomic<std::string> drumkit_description{""};
Atomic<std::string> drumkit_version{""};
Atomic<std::size_t> drumkit_samplerate{44100};
+ Atomic<bool> drumkit_is_log_power{false};
//! The maximum amount of memory in bytes that the AudioCache
//! is allowed to use for preloading. Default is 1GB.