midi renderer [step 1] (#19816)

* midi renderer [step 1]
This commit is contained in:
mikekirin 2023-10-25 22:50:22 +02:00 committed by GitHub
parent 5dc9152f14
commit d26589e7ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 602 additions and 87 deletions

View File

@ -80,6 +80,7 @@ option(MUE_BUILD_CLOUD_MODULE "Build cloud module" ON)
option(MUE_BUILD_CONVERTER_MODULE "Build converter module" ON)
option(MUE_BUILD_DIAGNOSTICS_MODULE "Build diagnostic code" ON)
option(MUE_BUILD_IMPORTEXPORT_MODULE "Build importexport module" ON)
option(MUE_ENABLE_MIDI_IMPORTEXPORT "Enable midi import and export" ON)
option(MUE_BUILD_VIDEOEXPORT_MODULE "Build videoexport module" OFF)
option(MUE_BUILD_IMAGESEXPORT_MODULE "Build imagesexport module" ON)
option(MUE_BUILD_INSPECTOR_MODULE "Build inspector module" ON)

View File

@ -251,6 +251,7 @@ def_opt(MUE_BUILD_ACCESSIBILITY_MODULE ${MUE_BUILD_ACCESSIBILITY_MODULE})
def_opt(MUE_BUILD_AUDIO_MODULE ${MUE_BUILD_AUDIO_MODULE})
def_opt(MUE_ENABLE_AUDIO_EXPORT ${MUE_ENABLE_AUDIO_EXPORT})
def_opt(MUE_BUILD_MIDI_MODULE ${MUE_BUILD_MIDI_MODULE})
def_opt(MUE_ENABLE_MIDI_IMPORTEXPORT ${MUE_ENABLE_MIDI_IMPORTEXPORT})
def_opt(MUE_BUILD_MPE_MODULE ${MUE_BUILD_MPE_MODULE})
def_opt(MUE_BUILD_MUSESAMPLER_MODULE ${MUE_BUILD_MUSESAMPLER_MODULE})
def_opt(MUE_BUILD_NETWORK_MODULE ${MUE_BUILD_NETWORK_MODULE})

View File

@ -5,7 +5,7 @@ void CompatMidiRender::renderScore(Score* score, EventsHolder& events, const Com
{
score->masterScore()->setExpandRepeats(expandRepeats);
CompatMidiRendererInternal internal{ score };
internal.renderScore(events, ctx, expandRepeats);
internal.renderScore(events, ctx);
}
int CompatMidiRender::getControllerForSnd(Score* score, int globalSndController)

View File

@ -1170,10 +1170,10 @@ void CompatMidiRendererInternal::renderMetronome(EventsHolder& events, Measure c
}
}
void CompatMidiRendererInternal::renderScore(EventsHolder& events, const Context& ctx, bool expandRepeats)
void CompatMidiRendererInternal::renderScore(EventsHolder& events, const Context& ctx)
{
UNUSED(expandRepeats);
_context = ctx;
_context.instrumentsHaveEffects = true;
PitchWheelRenderer pitchWheelRender(wheelSpec);
score->updateSwing();

View File

@ -151,7 +151,7 @@ public:
explicit CompatMidiRendererInternal(Score* s);
void renderScore(EventsHolder& events, const Context& ctx, bool expandRepeats);
void renderScore(EventsHolder& events, const Context& ctx);
static const int ARTICULATION_CONV_FACTOR { 100000 };
static bool graceNotesMerged(Chord* chord);

View File

@ -24,6 +24,7 @@
#include "utils/scorerw.h"
#include "engraving/compat/midi/compatmidirender.h"
#include "engraving/compat/midi/compatmidirenderinternal.h"
#include "engraving/infrastructure/localfileinfoprovider.h"
#include "engraving/rw/mscloader.h"
#include "engraving/dom/noteevent.h"

View File

@ -20,29 +20,40 @@
set(MODULE iex_midi)
include(${CMAKE_CURRENT_LIST_DIR}/internal/midishared/midishared.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/internal/midiimport/midiimport.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/internal/midiexport/midiexport.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/internal/midirender/midirender.cmake)
set(MODULE_SRC
${MIDISHARED_SRC}
${MIDIIMPORT_SRC}
${MIDIEXPORT_SRC}
${CMAKE_CURRENT_LIST_DIR}/midimodule.cpp
${CMAKE_CURRENT_LIST_DIR}/midimodule.h
${CMAKE_CURRENT_LIST_DIR}/imidiconfiguration.h
${CMAKE_CURRENT_LIST_DIR}/internal/midiconfiguration.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/midiconfiguration.h
${CMAKE_CURRENT_LIST_DIR}/internal/notationmidireader.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/notationmidireader.h
${CMAKE_CURRENT_LIST_DIR}/internal/notationmidiwriter.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/notationmidiwriter.h
${MIDIRENDER_SRC}
)
if (MUE_ENABLE_MIDI_IMPORTEXPORT)
include(${CMAKE_CURRENT_LIST_DIR}/internal/midishared/midishared.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/internal/midiimport/midiimport.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/internal/midiexport/midiexport.cmake)
set(MODULE_SRC
${MODULE_SRC}
${MIDISHARED_SRC}
${MIDIIMPORT_SRC}
${MIDIEXPORT_SRC}
${CMAKE_CURRENT_LIST_DIR}/midimodule.cpp
${CMAKE_CURRENT_LIST_DIR}/midimodule.h
${CMAKE_CURRENT_LIST_DIR}/imidiconfiguration.h
${CMAKE_CURRENT_LIST_DIR}/internal/midiconfiguration.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/midiconfiguration.h
${CMAKE_CURRENT_LIST_DIR}/internal/notationmidireader.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/notationmidireader.h
${CMAKE_CURRENT_LIST_DIR}/internal/notationmidiwriter.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/notationmidiwriter.h
)
add_subdirectory(${THIRDPARTY_DIR}/beatroot beatroot)
set(MODULE_LINK
beatroot
)
endif ()
set(MODULE_LINK
${MODULE_LINK}
engraving
)

View File

@ -0,0 +1,49 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_IMPORTEXPORT_IMIDICONVERTER_H
#define MU_IMPORTEXPORT_IMIDICONVERTER_H
#include "modularity/imoduleinterface.h"
namespace mu::engraving {
class Score;
}
namespace mu::iex::midi {
class RenderStrategy;
class RenderContext;
struct MidiRenderOutData;
class IMidiRender : MODULE_EXPORT_INTERFACE {
INTERFACE_ID(IMidiConverter)
public:
~IMidiRender() override = default;
virtual void
render(mu::engraving::Score *, RenderContext &ctx, MidiRenderOutData &outData, RenderStrategy &strategy) = 0;
};
}
#endif // MU_IMPORTEXPORT_IMIDICONVERTER_H

View File

@ -51,7 +51,7 @@ private:
//---------------------------------------------------
// PauseMap
// MIDI files cannot contain pauses so need to insert
// extra ticks extra ticks and tempo changes instead.
// extra ticks and tempo changes instead.
//---------------------------------------------------
class PauseMap : std::map<int, int>
{

View File

@ -94,16 +94,16 @@ double findChordSalience2(
return velocity;
}
::EventList prepareChordEvents(
BeatTracker::EventList prepareChordEvents(
const std::multimap<ReducedFraction, MidiChord>& chords,
const std::function<double(const std::pair<const ReducedFraction, MidiChord>&,
double)>& findChordSalience,
double ticksPerSec)
{
::EventList events;
BeatTracker::EventList events;
double minSalience = std::numeric_limits<double>::max();
for (const auto& chord: chords) {
::Event e;
BeatTracker::Event e;
e.time = chord.first.ticks() / ticksPerSec;
e.salience = findChordSalience(chord, ticksPerSec);
if (e.salience < minSalience) {
@ -217,7 +217,7 @@ MidiOperations::HumanBeatData prepareHumanBeatData(
}
double findMatchRank(const std::set<ReducedFraction>& beatSet,
const ::EventList& events,
const BeatTracker::EventList& events,
const std::vector<int>& levels,
int beatsInBar,
double ticksPerSec)

View File

@ -0,0 +1,8 @@
set (MIDIRENDER_SRC
${CMAKE_CURRENT_LIST_DIR}/midirender.cpp
${CMAKE_CURRENT_LIST_DIR}/midirender.h
${CMAKE_CURRENT_LIST_DIR}/renderstrategy.cpp
${CMAKE_CURRENT_LIST_DIR}/renderstrategy.h
${CMAKE_CURRENT_LIST_DIR}/midirendertypes.h
)

View File

@ -0,0 +1,137 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "midirender.h"
#include "renderstrategy.h"
#include "engraving/dom/staff.h"
#include "engraving/dom/part.h"
#include "engraving/dom/score.h"
#include "engraving/dom/chord.h"
#include "engraving/dom/note.h"
#include "engraving/dom/spanner.h"
#include "engraving/dom/navigate.h"
#include "log.h"
namespace mu::iex::midi {
void MidiRender::render(mu::engraving::Score* score, RenderContext& ctx, MidiRenderOutData& outData,
RenderStrategy& strategy)
{
m_ctx = ctx;
m_score = score;
for (const auto p: m_score->parts()) {
// TODO: Figure out what the diff between idx and id and why the differ
m_ctx.parts().insert({ p->id().toUint64(), {} });
}
collectNotes();
strategy.render(m_ctx, outData, m_renderMeta);
}
void MidiRender::collectNotes()
{
track_idx_t numTracks = m_score->staves().size() * VOICES;
for (auto m = m_score->firstMeasure(); m; m = m->nextMeasure()) {
for (auto s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
for (track_idx_t track = 0; track < numTracks; ++track) {
Staff* staff = m_score->staff(track / VOICES);
if (!staff->isPrimaryStaff()) {
track += VOICES - 1;
continue;
}
auto cr = s->element(track);
if (!cr || !cr->isChord()) [[likely]] {
continue;
}
Chord* chord = toChord(cr);
const uint64_t partID = chord->part()->id().toUint64();
auto graceBefore = chord->graceNotesBefore();
auto graceAfter = chord->graceNotesBefore();
auto startTick = chord->tick().ticks();
if (!graceBefore.empty()) [[unlikely]] {
for (const Chord* ch: graceBefore) {
auto prevChord = prevChordRest(chord, true, true);
collectGraceNotes(true, ch, startTick, partID,
prevChord->isChord() ? toChord(prevChord) : nullptr);
}
}
if (!graceAfter.empty()) [[unlikely]] {
for (const Chord* ch: graceAfter) {
collectGraceNotes(false, ch, startTick, partID, chord);
}
}
for (const auto n: chord->notes()) {
// TODO: This code for collecting spanners should be refactored.
// Need to figure out why cant use n->isPalmMute;
for (auto it: m_score->spannerMap().findOverlapping(startTick + 1, startTick + 2)) {
Spanner* spanner = it.value;
if (spanner->track() != chord->track()) [[likely]] {
continue;
}
if (spanner->isLetRing()) {
// LetRing* letRing = toLetRing(spanner);
} else if (spanner->isPalmMute()) {
m_ctx.part(partID).palmMuteNotes.insert({ startTick, n });
Meta meta;
meta.isPalmMute = true;
m_renderMeta.insert({ toEngravingItem(spanner), meta });
}
}
if (n->isPalmMute()) {
m_ctx.part(partID).palmMuteNotes.insert({ startTick, n });
} else if (n->isHammerOn()) {
LOGI() << "Hammer on";
} else [[likely]] {
m_renderMeta.insert({ toEngravingItem(n), {} });
m_ctx.part(partID).regularNotes.insert({ startTick, n });
}
}
}
}
}
}
void
MidiRender::collectGraceNotes(bool isBefore, const mu::engraving::Chord* chord, int mainChordStartTick,
uint64_t partID,
mu::engraving::Chord* dependentItem)
{
// Collect grace notes
// Give them the same start tick as root note has
// We will handle that during events generating (merging)
for (const auto n: chord->notes()) {
if (isBefore) [[likely]] {
m_ctx.part(partID).graceNotesBefore.insert({ mainChordStartTick, n });
} else {
m_ctx.part(partID).graceNotesAfter.insert({ mainChordStartTick, n });
}
Meta meta;
meta.isGraceBefore = isBefore;
meta.isGraceAfter = !isBefore;
if (dependentItem) {
for (const auto dependentNote: dependentItem->notes()) {
meta.dependentItems.push_back(toEngravingItem(dependentNote));
}
}
m_renderMeta.insert({ toEngravingItem(n), meta });
}
}
}

View File

@ -0,0 +1,53 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_IMPORTEXPORT_MIDIRENDER_HPP
#define MU_IMPORTEXPORT_MIDIRENDER_HPP
#include "importexport/midi/imidirender.h"
#include "midirendertypes.h"
namespace mu::iex::midi {
class RenderStrategy;
class MidiRender : public IMidiRender
{
public:
~MidiRender() override = default;
void render(mu::engraving::Score* score, RenderContext& ctx, MidiRenderOutData& outData, RenderStrategy& strategy) override;
private:
RenderContext m_ctx;
mu::engraving::Score* m_score;
std::unordered_map<mu::engraving::EngravingItem*, Meta> m_renderMeta;
void collectNotes();
void
collectGraceNotes(bool isBefore, const mu::engraving::Chord* chord, int mainChordStartTick, uint64_t partID,
mu::engraving::Chord* dependentItem);
};
}
#endif //MU_IMPORTEXPORT_MIDIRENDER_HPP

View File

@ -0,0 +1,95 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_IMPORTEXPORT_MIDIRENDERTYPES_H
#define MU_IMPORTEXPORT_MIDIRENDERTYPES_H
#include <unordered_map>
#include <map>
#include "log.h"
namespace mu::engraving {
class Chord;
class EngravingItem;
class Note;
}
namespace mu::iex::midi {
struct MidiRenderEvent {
uint8_t type = 0;
uint8_t channel = 0; // ???
uint8_t a = 0;
uint8_t b = 0;
};
using midirender_channels_t = std::array<std::multimap<size_t, MidiRenderEvent>, 16>; // midi can handle only 16 channels
struct MidiRenderOutData {
std::unordered_map<uint64_t, midirender_channels_t> tracks{};
};
using notes_mmap_t = std::multimap<int, mu::engraving::Note*>;
struct MRPart {
notes_mmap_t regularNotes;
notes_mmap_t palmMuteNotes;
notes_mmap_t graceNotesBefore;
notes_mmap_t graceNotesAfter;
};
struct Meta {
std::vector<mu::engraving::EngravingItem*> dependentItems;
int pitch = 0;
int noteOn = 0;
int noteOff = 0;
bool isGraceBefore = false;
bool isGraceAfter = false;
bool isPalmMute = false;
};
class RenderContext
{
std::unordered_map<uint64_t, MRPart> m_parts;
public:
virtual ~RenderContext() = default;
std::unordered_map<uint64_t, MRPart>& parts()
{
return m_parts;
}
[[nodiscard]] const std::unordered_map<uint64_t, MRPart>& parts() const
{
return m_parts;
}
MRPart& part(uint64_t id)
{
IF_ASSERT_FAILED(parts().find(id) != parts().end()) {
m_parts.insert({ 1, {} });
return parts().at(1);
}
return parts().at(id);
}
};
}
#endif //MU_IMPORTEXPORT_MIDIRENDERTYPES_H

View File

@ -0,0 +1,90 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "renderstrategy.h"
#include "midirendertypes.h"
#include "engraving/dom/note.h"
namespace mu::iex::midi {
void RenderStrategy::render(RenderContext& ctx, MidiRenderOutData& outData,
std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta)
{
renderRegularNotes(ctx, renderMeta);
renderGraceNotes(ctx, renderMeta);
renderPalmMute(ctx, renderMeta);
mergeEventsBefore(ctx, outData, renderMeta);
// mergeEvents(ctx, outData, renderMeta);
// mergeEventsAfter(ctx, outData, renderMeta);
}
void RenderStrategy::renderRegularNotes(RenderContext& ctx,
std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta)
{
for (const auto&[id, part]: ctx.parts()) {
for (const auto&[tick, note]: part.regularNotes) {
int on = tick;
int off = on + note->playTicks();
auto pitch = static_cast<uint8_t>(note->pitch());
auto& meta = renderMeta.at(toEngravingItem(note));
meta.noteOn = on;
meta.noteOff = off;
meta.pitch = pitch;
}
}
}
void RenderStrategy::renderGraceNotes(RenderContext& ctx,
std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta)
{
for (const auto&[id, part]: ctx.parts()) {
for (const auto&[tick, note]: part.graceNotesBefore) {
int on = tick - note->playTicks();
int off = on + note->playTicks();
auto pitch = static_cast<uint8_t>(note->pitch());
auto& meta = renderMeta.at(toEngravingItem(note));
meta.noteOn = on;
meta.noteOff = off;
meta.pitch = pitch;
}
}
}
void RenderStrategy::renderPalmMute(RenderContext& ctx,
std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta)
{
// TODO: setup program change
}
void RenderStrategy::mergeEventsBefore(mu::iex::midi::RenderContext& ctx, mu::iex::midi::MidiRenderOutData& outData,
std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta)
{
for (const auto&[id, part]: ctx.parts()) {
for (const auto&[tick, note]: part.graceNotesBefore) {
const auto& meta = renderMeta.at(toEngravingItem(note));
for (auto i: meta.dependentItems) {
auto& noteMeta = renderMeta.at(i);
noteMeta.noteOff -= note->playTicks();
}
}
}
}
}

View File

@ -0,0 +1,50 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_IMPORTEXPORT_RENDERSTRATEGY_H
#define MU_IMPORTEXPORT_RENDERSTRATEGY_H
namespace mu::iex::midi {
class RenderContext;
struct MidiRenderOutData;
class RenderStrategy
{
virtual void
renderRegularNotes(RenderContext& ctx, std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta);
virtual void
renderGraceNotes(RenderContext& ctx, std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta);
virtual void
renderPalmMute(RenderContext& ctx, std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta);
virtual void mergeEventsBefore(RenderContext& ctx, MidiRenderOutData& outData, std::unordered_map<mu::engraving::EngravingItem*,
Meta>& renderMeta);
public:
void render(RenderContext& ctx, MidiRenderOutData& outData, std::unordered_map<mu::engraving::EngravingItem*, Meta>& renderMeta);
};
}
#endif //MU_IMPORTEXPORT_RENDERSTRATEGY_H

View File

@ -28,6 +28,7 @@
#include "modularity/ioc.h"
#include "notation/inotationconfiguration.h"
#include "imidiconfiguration.h"
#include "imidirender.h"
namespace mu::iex::midi {
class NotationMidiWriter : public project::INotationWriter

View File

@ -23,48 +23,58 @@
#include "modularity/ioc.h"
#ifdef MUE_ENABLE_MIDI_IMPORTEXPORT
#include "project/inotationreadersregister.h"
#include "internal/notationmidireader.h"
#include "project/inotationwritersregister.h"
#include "internal/notationmidireader.h"
#include "internal/notationmidiwriter.h"
#include "internal/midiconfiguration.h"
using namespace mu::project;
#endif
#include "internal/midirender/midirender.h"
#include "log.h"
using namespace mu::iex::midi;
using namespace mu::project;
std::string MidiModule::moduleName() const
namespace mu::iex::midi
{
return "iex_midi";
}
void MidiModule::registerExports()
{
m_configuration = std::make_shared<MidiConfiguration>();
modularity::ioc()->registerExport<IMidiImportExportConfiguration>(moduleName(), m_configuration);
}
void MidiModule::resolveImports()
{
auto readers = modularity::ioc()->resolve<INotationReadersRegister>(moduleName());
if (readers) {
readers->reg({ "mid", "midi", "kar" }, std::make_shared<NotationMidiReader>());
std::string MidiModule::moduleName() const
{
return "iex_midi";
}
auto writers = modularity::ioc()->resolve<INotationWritersRegister>(moduleName());
if (writers) {
writers->reg({ "mid", "midi", "kar" }, std::make_shared<NotationMidiWriter>());
}
}
void MidiModule::registerExports()
{
#ifdef MUE_ENABLE_MIDI_IMPORTEXPORT
m_configuration = std::make_shared<MidiConfiguration>();
void MidiModule::onInit(const framework::IApplication::RunMode& mode)
{
if (mode == framework::IApplication::RunMode::AudioPluginRegistration) {
return;
modularity::ioc()->registerExport<IMidiImportExportConfiguration>(moduleName(), m_configuration);
// modularity::ioc()->registerExport<IMidiRenderStrategy>(moduleName());
#endif
modularity::ioc()->registerExport<IMidiRender>(moduleName(), new MidiRender());
}
m_configuration->init();
#ifdef MUE_ENABLE_MIDI_IMPORTEXPORT
void MidiModule::resolveImports()
{
auto readers = modularity::ioc()->resolve<INotationReadersRegister>(moduleName());
if (readers) {
readers->reg({ "mid", "midi", "kar" }, std::make_shared<NotationMidiReader>());
}
auto writers = modularity::ioc()->resolve<INotationWritersRegister>(moduleName());
if (writers) {
writers->reg({ "mid", "midi", "kar" }, std::make_shared<NotationMidiWriter>());
}
}
void MidiModule::onInit(const framework::IApplication::RunMode& mode)
{
if (mode == framework::IApplication::RunMode::AudioPluginRegistration) {
return;
}
m_configuration->init();
}
#endif
}

View File

@ -34,11 +34,14 @@ public:
std::string moduleName() const override;
void registerExports() override;
#ifdef MUE_ENABLE_MIDI_IMPORTEXPORT
void resolveImports() override;
void onInit(const framework::IApplication::RunMode& mode) override;
private:
std::shared_ptr<MidiConfiguration> m_configuration;
#endif
};
}

View File

@ -55,7 +55,7 @@ Agent *Agent::clone() const
return a;
}
void Agent::accept(const Event &e, double err, int beats)
void Agent::accept(const BeatTracker::Event &e, double err, int beats)
{
beatTime = e.time;
events.push_back(e);
@ -69,14 +69,14 @@ void Agent::accept(const Event &e, double err, int beats)
phaseScore += conFactor * e.salience;
}
bool Agent::considerAsBeat(const Event &e, AgentList &a)
bool Agent::considerAsBeat(const BeatTracker::Event &e, AgentList &a)
{
if (beatTime < 0) { // first event
accept(e, 0, 1);
return true;
}
else { // subsequent events
EventList::iterator last = events.end();
BeatTracker::EventList::iterator last = events.end();
--last;
if (e.time - last->time > expiryTime) {
phaseScore = -1.0; // flag agent to be deleted
@ -99,7 +99,7 @@ bool Agent::considerAsBeat(const Event &e, AgentList &a)
void Agent::fillBeats(double start)
{
EventList::iterator it = events.begin();
BeatTracker::EventList::iterator it = events.begin();
if (it == events.end())
return;
double prevBeat = it->time;
@ -109,7 +109,7 @@ void Agent::fillBeats(double start)
double currentInterval = (nextBeat - prevBeat) / beats;
for ( ; (nextBeat > start) && (beats > 1.5); --beats) {
prevBeat += currentInterval;
events.insert(it, Event(prevBeat, 0));
events.insert(it, BeatTracker::Event(prevBeat, 0));
}
prevBeat = nextBeat;
}

View File

@ -20,7 +20,10 @@
class AgentList;
namespace BeatTracker
{
struct Event;
}
class AgentParameters
{
@ -103,7 +106,7 @@ class Agent
/** The list of Events (onsets) accepted by this Agent as beats,
* plus interpolated beats. */
EventList events;
BeatTracker::EventList events;
/** Constructor: the work is performed by init()
* @param ibi The beat period (inter-beat interval)
@ -118,7 +121,7 @@ class Agent
* @param err The difference between the predicted and actual beat times.
* @param beats The number of beats since the last beat that matched an Event.
*/
void accept(const Event &e, double err, int beats);
void accept(const BeatTracker::Event &e, double err, int beats);
/** The given Event is tested for a possible beat time.
* The following situations can occur:
@ -136,7 +139,7 @@ class Agent
* @param a The list of all agents, which is updated if a new agent is created.
* @return Indicate whether the given Event was accepted as a beat by this Agent.
*/
bool considerAsBeat(const Event &e, AgentList &a);
bool considerAsBeat(const BeatTracker::Event &e, AgentList &a);
/** Interpolates missing beats in the Agent's beat track,
* starting from the beginning of the piece.

View File

@ -86,14 +86,14 @@ void AgentList::removeDuplicates()
}
void AgentList::beatTrack(const EventList &el,
void AgentList::beatTrack(const BeatTracker::EventList &el,
const AgentParameters &params,
double stop)
{
EventList::const_iterator ei = el.begin();
BeatTracker::EventList::const_iterator ei = el.begin();
bool phaseGiven = !empty() && ((*begin())->beatTime >= 0); // if given for one, assume given for others
while (ei != el.end()) {
Event ev = *ei;
BeatTracker::Event ev = *ei;
++ei;
if ((stop > 0) && (ev.time > stop))
break;

View File

@ -72,7 +72,7 @@ class AgentList
/** Perform beat tracking on a list of events (onsets).
* @param el The list of onsets (or events or peaks) to beat track
*/
void beatTrack(const EventList &el, const AgentParameters &params)
void beatTrack(const BeatTracker::EventList &el, const AgentParameters &params)
{
beatTrack(el, params, -1.0);
}
@ -80,7 +80,7 @@ class AgentList
* @param el The list of onsets (or events or peaks) to beat track.
* @param stop Do not find beats after <code>stop</code> seconds.
*/
void beatTrack(const EventList &el, const AgentParameters &params, double stop);
void beatTrack(const BeatTracker::EventList &el, const AgentParameters &params, double stop);
/** Finds the Agent with the highest score in the list,
* or NULL if beat tracking has failed.

View File

@ -18,26 +18,28 @@
#include <list>
namespace BeatTracker
{
struct Event
{
double time;
double salience;
{
double time;
double salience;
Event()
Event()
: time(0), salience(0)
{}
Event(double t, double s)
{}
Event(double t, double s)
: time(t), salience(s)
{}
{}
bool operator!=(const Event &e)
{
return time != e.time || salience != e.salience;
}
};
bool operator!=(const Event &e)
{
return time != e.time || salience != e.salience;
}
};
typedef std::list<Event> EventList;
}
#endif

View File

@ -30,7 +30,7 @@ int Induction::topN = 10;
AgentList Induction::beatInduction(const AgentParameters &params,
const EventList &events)
const BeatTracker::EventList &events)
{
int i, j, b, bestCount;
bool submult;
@ -48,8 +48,8 @@ AgentList Induction::beatInduction(const AgentParameters &params,
std::vector<int> clusterScore;
clusterScore.resize(maxClusterCount);
EventList::const_iterator ptr1, ptr2;
Event e1, e2;
BeatTracker::EventList::const_iterator ptr1, ptr2;
BeatTracker::Event e1, e2;
ptr1 = events.begin();
while (ptr1 != events.end()) {

View File

@ -64,7 +64,7 @@ class Induction
* of the top tempo hypotheses but no beats
*/
static AgentList beatInduction(const AgentParameters &params,
const EventList &events);
const BeatTracker::EventList &events);
private:
/** For variable cluster widths in newInduction().