From f43935b5c873676a632b23cbfcef45a4431b233d Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 8 Aug 2021 10:34:54 +0200 Subject: Add support for a single midi note that plays multiple instruments. --- plugin/drumgizmo_plugin.cc | 13 +++- src/audioinputenginemidi.cc | 51 +++++++------- src/midimapparser.cc | 3 +- src/midimapper.cc | 24 ++++--- src/midimapper.h | 16 +++-- test/Makefile.am | 17 ++++- test/dgreftest/midiinputengine.cc | 17 +++-- test/midimapparsertest.cc | 44 +++++++----- test/midimappertest.cc | 140 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 257 insertions(+), 68 deletions(-) create mode 100644 test/midimappertest.cc 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> midnam; - const auto& map = mmap.getMap(); + const auto& midimap = mmap.getMap(); + std::map 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 MidiMapper::lookup(int note_id) { - std::lock_guard guard(mutex); + std::vector instruments; - auto midimap_it = midimap.find(note); - if(midimap_it == midimap.end()) - { - return -1; - } + std::lock_guard 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 #include #include +#include -typedef std::map midimap_t; -typedef std::map instrmap_t; +struct MidimapEntry +{ + int note_id; + std::string instrument_name; +}; + +using midimap_t = std::vector; +using instrmap_t = std::map; 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 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& 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: " \n" \ " \n" \ " \n" \ + " \n" \ " \n" \ ""); 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: ""); 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 + +#include + +#include + +#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; -- cgit v1.2.3