summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2021-08-08 10:34:54 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2021-08-08 10:34:54 +0200
commitf43935b5c873676a632b23cbfcef45a4431b233d (patch)
treead1473fa34f3da5292e3344a99ed447261d99584
parenta2483a839264369482fff135d33f007ded266d3c (diff)
Add support for a single midi note that plays multiple instruments.
-rw-r--r--plugin/drumgizmo_plugin.cc13
-rw-r--r--src/audioinputenginemidi.cc51
-rw-r--r--src/midimapparser.cc3
-rw-r--r--src/midimapper.cc24
-rw-r--r--src/midimapper.h16
-rw-r--r--test/Makefile.am17
-rw-r--r--test/dgreftest/midiinputengine.cc17
-rw-r--r--test/midimapparsertest.cc44
-rw-r--r--test/midimappertest.cc140
9 files changed, 257 insertions, 68 deletions
diff --git a/plugin/drumgizmo_plugin.cc b/plugin/drumgizmo_plugin.cc
index 82c0ee9..7960763 100644
--- a/plugin/drumgizmo_plugin.cc
+++ b/plugin/drumgizmo_plugin.cc
@@ -410,7 +410,18 @@ bool DrumGizmoPlugin::Input::loadMidiMap(const std::string& file,
bool result = AudioInputEngineMidi::loadMidiMap(file, i);
std::vector<std::pair<int, std::string>> midnam;
- const auto& map = mmap.getMap();
+ const auto& midimap = mmap.getMap();
+ std::map<int, std::string> map;
+ for(const auto& entry : midimap)
+ {
+ // in case of multiple instruments mapped to one note, use '/' as separator
+ if(!map[entry.note_id].empty())
+ {
+ map[entry.note_id] += "/";
+ }
+ map[entry.note_id] += entry.instrument_name;
+ }
+
midnam.reserve(map.size());
for(const auto& m : map)
{
diff --git a/src/audioinputenginemidi.cc b/src/audioinputenginemidi.cc
index 8732d8d..69aeeb6 100644
--- a/src/audioinputenginemidi.cc
+++ b/src/audioinputenginemidi.cc
@@ -117,32 +117,35 @@ void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer,
auto key = midi_buffer[1];
auto velocity = midi_buffer[2];
auto instrument_idx = mmap.lookup(key);
-
- switch(midi_buffer[0] & NoteMask)
+ auto instruments = mmap.lookup(key);
+ for(const auto& instrument_idx : instruments)
{
- case NoteOff:
- // Ignore for now
- break;
-
- case NoteOn:
- if(velocity != 0 && instrument_idx != -1)
- {
- // maps velocities to [.5/127, 126.5/127]
- auto centered_velocity = (velocity-.5f)/127.0f;
- events.push_back({EventType::OnSet, (std::size_t)instrument_idx,
- offset, centered_velocity});
- }
- break;
-
- case NoteAftertouch:
- if(velocity > 0 && instrument_idx != -1)
+ switch(midi_buffer[0] & NoteMask)
{
- events.push_back({EventType::Choke, (std::size_t)instrument_idx,
- offset, .0f});
+ case NoteOff:
+ // Ignore for now
+ break;
+
+ case NoteOn:
+ if(velocity != 0)
+ {
+ // maps velocities to [.5/127, 126.5/127]
+ auto centered_velocity = (velocity-.5f)/127.0f;
+ events.push_back({EventType::OnSet, (std::size_t)instrument_idx,
+ offset, centered_velocity});
+ }
+ break;
+
+ case NoteAftertouch:
+ if(velocity > 0)
+ {
+ events.push_back({EventType::Choke, (std::size_t)instrument_idx,
+ offset, .0f});
+ }
+ break;
+
+ default:
+ break;
}
- break;
-
- default:
- break;
}
}
diff --git a/src/midimapparser.cc b/src/midimapparser.cc
index 059dfec..363e1d5 100644
--- a/src/midimapparser.cc
+++ b/src/midimapparser.cc
@@ -50,7 +50,8 @@ bool MidiMapParser::parseFile(const std::string& filename)
continue;
}
- midimap[note] = instr;
+ MidimapEntry entry{note, instr};
+ midimap.push_back(entry);
}
return true;
diff --git a/src/midimapper.cc b/src/midimapper.cc
index 9593aae..b9316c5 100644
--- a/src/midimapper.cc
+++ b/src/midimapper.cc
@@ -26,23 +26,25 @@
*/
#include "midimapper.h"
-int MidiMapper::lookup(int note)
+std::vector<int> MidiMapper::lookup(int note_id)
{
- std::lock_guard<std::mutex> guard(mutex);
+ std::vector<int> instruments;
- auto midimap_it = midimap.find(note);
- if(midimap_it == midimap.end())
- {
- return -1;
- }
+ std::lock_guard<std::mutex> guard(mutex);
- auto instrmap_it = instrmap.find(midimap_it->second);
- if(instrmap_it == instrmap.end())
+ for(const auto& map_entry : midimap)
{
- return -1;
+ if(map_entry.note_id == note_id)
+ {
+ auto instrmap_it = instrmap.find(map_entry.instrument_name);
+ if(instrmap_it != instrmap.end())
+ {
+ instruments.push_back(instrmap_it->second);
+ }
+ }
}
- return instrmap_it->second;
+ return instruments;
}
void MidiMapper::swap(instrmap_t& instrmap, midimap_t& midimap)
diff --git a/src/midimapper.h b/src/midimapper.h
index 4673e33..94781d4 100644
--- a/src/midimapper.h
+++ b/src/midimapper.h
@@ -29,16 +29,22 @@
#include <map>
#include <string>
#include <mutex>
+#include <vector>
-typedef std::map<int, std::string> midimap_t;
-typedef std::map<std::string, int> instrmap_t;
+struct MidimapEntry
+{
+ int note_id;
+ std::string instrument_name;
+};
+
+using midimap_t = std::vector<MidimapEntry>;
+using instrmap_t = std::map<std::string, int>;
class MidiMapper
{
public:
- //! Lookup note in map and return its index.
- //! \returns -1 if not found or the note index.
- int lookup(int note);
+ //! Lookup note in map and returns the corresponding instrument index list.
+ std::vector<int> lookup(int note_id);
//! Set new map sets.
void swap(instrmap_t& instrmap, midimap_t& midimap);
diff --git a/test/Makefile.am b/test/Makefile.am
index 6d62314..6441fd3 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
+ eventsdstest powermaptest midimappertest
if WITH_NLS
TESTS += translationtest
@@ -335,6 +335,21 @@ powermaptest_SOURCES = \
powermaptest.cc \
uunit/uunit.cc
+midimappertest_CXXFLAGS = \
+ -I$(top_srcdir)/test/uunit -DOUTPUT=\"midimappertest\" \
+ $(DEBUG_FLAGS) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/hugin \
+ -I$(top_srcdir)/pugixml/src
+midimappertest_LDFLAGS =
+midimappertest_SOURCES = \
+ $(top_srcdir)/hugin/hugin.c \
+ $(top_srcdir)/src/midimapper.cc \
+ $(top_srcdir)/pugixml/src/pugixml.cpp \
+ scopedfile.cc \
+ midimappertest.cc \
+ uunit/uunit.cc
+
RES = \
$(top_srcdir)/test/locale/da.mo
diff --git a/test/dgreftest/midiinputengine.cc b/test/dgreftest/midiinputengine.cc
index ca223da..dbffec9 100644
--- a/test/dgreftest/midiinputengine.cc
+++ b/test/dgreftest/midiinputengine.cc
@@ -155,16 +155,15 @@ void MidifileInputEngine::run(size_t pos, size_t len, std::vector<event_t>& even
int key = current_event->midi_buffer[1];
int velocity = current_event->midi_buffer[2];
- events.emplace_back();
- auto& event = events.back();
- event.type = EventType::OnSet;
- size_t evpos = current_event->time_seconds * (samplerate / speed);
- event.offset = evpos - pos;
-
- int i = mmap.lookup(key);
- if(i != -1)
+ auto instruments = mmap.lookup(key);
+ for(const auto& instrument_idx : instruments)
{
- event.instrument = i;
+ events.emplace_back();
+ auto& event = events.back();
+ event.type = EventType::OnSet;
+ size_t evpos = current_event->time_seconds * (samplerate / speed);
+ event.offset = evpos - pos;
+ event.instrument = instrument_idx;
event.velocity = velocity / 127.0;
}
}
diff --git a/test/midimapparsertest.cc b/test/midimapparsertest.cc
index eb4d6d5..3e77c44 100644
--- a/test/midimapparsertest.cc
+++ b/test/midimapparsertest.cc
@@ -36,8 +36,8 @@ class MidimapParserTest
public:
MidimapParserTest()
{
- uUNIT_TEST(MidimapParserTest::test);
- uUNIT_TEST(MidimapParserTest::invalid);
+ uTEST(MidimapParserTest::test);
+ uTEST(MidimapParserTest::invalid);
}
void test()
@@ -49,23 +49,35 @@ public:
" <map note=\"60\" instr=\"Crash_left_whisker\"/>\n" \
" <map note=\"55\" instr=\"Crash_right_tip\"/>\n" \
" <map note=\"62\" instr=\"Crash_right_whisker\"/>\n" \
+ " <map note=\"62\" instr=\"Hihat_closed\"/>\n" \
" <map note=\"56\" instr=\"Hihat_closed\"/>\n" \
"</midimap>");
MidiMapParser parser;
- uUNIT_ASSERT(parser.parseFile(scoped_file.filename()));
-
- uUNIT_ASSERT(parser.midimap.find(54) != parser.midimap.end());
- uUNIT_ASSERT(parser.midimap.find(60) != parser.midimap.end());
- uUNIT_ASSERT(parser.midimap.find(55) != parser.midimap.end());
- uUNIT_ASSERT(parser.midimap.find(62) != parser.midimap.end());
- uUNIT_ASSERT(parser.midimap.find(56) != parser.midimap.end());
-
- uUNIT_ASSERT_EQUAL(std::string("Crash_left_tip"), parser.midimap[54]);
- uUNIT_ASSERT_EQUAL(std::string("Crash_left_whisker"), parser.midimap[60]);
- uUNIT_ASSERT_EQUAL(std::string("Crash_right_tip"), parser.midimap[55]);
- uUNIT_ASSERT_EQUAL(std::string("Crash_right_whisker"), parser.midimap[62]);
- uUNIT_ASSERT_EQUAL(std::string("Hihat_closed"), parser.midimap[56]);
+ uASSERT(parser.parseFile(scoped_file.filename()));
+
+ const auto& midimap = parser.midimap;
+ uASSERT_EQUAL(6u, midimap.size());
+
+ uASSERT_EQUAL(54, midimap[0].note_id);
+ uASSERT_EQUAL(std::string("Crash_left_tip"), midimap[0].instrument_name);
+
+ uASSERT_EQUAL(60, midimap[1].note_id);
+ uASSERT_EQUAL(std::string("Crash_left_whisker"), midimap[1].instrument_name);
+
+ uASSERT_EQUAL(55, midimap[2].note_id);
+ uASSERT_EQUAL(std::string("Crash_right_tip"), midimap[2].instrument_name);
+
+ // These next two note numbers are intentionally the same and trigger two
+ // different instruments:
+ uASSERT_EQUAL(62, midimap[3].note_id);
+ uASSERT_EQUAL(std::string("Crash_right_whisker"), midimap[3].instrument_name);
+
+ uASSERT_EQUAL(62, midimap[4].note_id);
+ uASSERT_EQUAL(std::string("Hihat_closed"), midimap[4].instrument_name);
+
+ uASSERT_EQUAL(56, midimap[5].note_id);
+ uASSERT_EQUAL(std::string("Hihat_closed"), midimap[5].instrument_name);
}
void invalid()
@@ -81,7 +93,7 @@ public:
"</midimap>");
MidiMapParser parser;
- uUNIT_ASSERT(!parser.parseFile(scoped_file.filename()));
+ uASSERT(!parser.parseFile(scoped_file.filename()));
}
};
diff --git a/test/midimappertest.cc b/test/midimappertest.cc
new file mode 100644
index 0000000..703c646
--- /dev/null
+++ b/test/midimappertest.cc
@@ -0,0 +1,140 @@
+/* -*- Mode: c++ -*- */
+/***************************************************************************
+ * midimappertest.cc
+ *
+ * Sun Aug 8 09:55:13 CEST 2021
+ * Copyright 2021 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * 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 <algorithm>
+
+#include <midimapper.h>
+
+#include "scopedfile.h"
+
+class MidiMapperTest
+ : public uUnit
+{
+public:
+ MidiMapperTest()
+ {
+ uTEST(MidiMapperTest::test);
+ uTEST(MidiMapperTest::exceptional);
+ }
+
+ void test()
+ {
+ midimap_t midimap
+ {
+ { 54, "Crash_left_tip" },
+ { 60, "Crash_left_whisker" },
+ { 55, "Crash_right_tip" },
+ { 62, "Crash_right_whisker" },
+ { 62, "Hihat_closed" },
+ { 56, "Hihat_closed" },
+ };
+
+ instrmap_t instrmap
+ {
+ { "Crash_left_tip", 0 },
+ { "Crash_left_whisker", 1 },
+ { "Crash_right_tip", 2 },
+ { "Crash_right_whisker", 3 },
+ { "Hihat_closed", 4 },
+ };
+
+ MidiMapper mapper;
+ mapper.swap(instrmap, midimap);
+
+ {
+ auto is = mapper.lookup(54);
+ uASSERT_EQUAL(1u, is.size());
+ uASSERT_EQUAL(0, is[0]);
+ }
+
+ {
+ auto is = mapper.lookup(60);
+ uASSERT_EQUAL(1u, is.size());
+ uASSERT_EQUAL(1, is[0]);
+ }
+
+ {
+ auto is = mapper.lookup(55);
+ uASSERT_EQUAL(1u, is.size());
+ uASSERT_EQUAL(2, is[0]);
+ }
+
+ {
+ auto is = mapper.lookup(62);
+ uASSERT_EQUAL(2u, is.size());
+ // We don't care about the order, so just count the instances
+ uASSERT_EQUAL(1u, std::count(is.begin(), is.end(), 3));
+ uASSERT_EQUAL(1u, std::count(is.begin(), is.end(), 4));
+ }
+
+ {
+ auto is = mapper.lookup(56);
+ uASSERT_EQUAL(1u, is.size());
+ uASSERT_EQUAL(4, is[0]);
+ }
+ }
+
+ void exceptional()
+ {
+ midimap_t midimap
+ {
+ { 54, "Crash_left_tip" },
+ { 60, "Crash_left_whisker_MISSING" },
+ { 55, "Crash_right_tip" },
+ { 62, "Crash_right_whisker" },
+ { 62, "Hihat_closed" },
+ { 56, "Hihat_closed" },
+ };
+
+ instrmap_t instrmap
+ {
+ { "Crash_left_tip", 0 },
+ { "Crash_left_whisker", 1 },
+ { "Crash_right_tip", 2 },
+ { "Crash_right_whisker", 3 },
+ { "Hihat_closed", 4 },
+ };
+
+ MidiMapper mapper;
+ mapper.swap(instrmap, midimap);
+
+ // no such note id
+ {
+ auto is = mapper.lookup(42);
+ uASSERT_EQUAL(0u, is.size());
+ }
+
+ // no such instrument
+ {
+ auto is = mapper.lookup(60);
+ uASSERT_EQUAL(0u, is.size());
+ }
+ }
+};
+
+// Registers the fixture into the 'registry'
+static MidiMapperTest test;