From c536c8c7e4ecb70b24b770736b043d13018b1386 Mon Sep 17 00:00:00 2001 From: Gauthier Segay Date: Tue, 10 May 2022 17:19:49 +0200 Subject: [PATCH 1/3] initialize data structures for pitchbend and CC support in MidiDrop, nothing is handled in the UI yet, but it parses the MidiTrackEvent objects. --- .../Midi/Commands/AddNote.cpp | 2 +- .../Midi/Commands/AddNote.hpp | 4 +- .../score-plugin-midi/Midi/MidiDrop.cpp | 16 ++-- .../score-plugin-midi/Midi/MidiDrop.hpp | 92 ++++++++++++++++++- .../score-plugin-midi/Midi/MidiPresenter.cpp | 7 +- 5 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/plugins/score-plugin-midi/Midi/Commands/AddNote.cpp b/src/plugins/score-plugin-midi/Midi/Commands/AddNote.cpp index dcd594892b..8da7b56ad1 100644 --- a/src/plugins/score-plugin-midi/Midi/Commands/AddNote.cpp +++ b/src/plugins/score-plugin-midi/Midi/Commands/AddNote.cpp @@ -69,7 +69,7 @@ void AddNotes::deserializeImpl(DataStreamOutput& s) ReplaceNotes::ReplaceNotes( const ProcessModel& model, - const std::vector& n, + const MidiTrackNotes& n, int min, int max, TimeVal d) diff --git a/src/plugins/score-plugin-midi/Midi/Commands/AddNote.hpp b/src/plugins/score-plugin-midi/Midi/Commands/AddNote.hpp index 75fc3419ed..5f418ef3dc 100644 --- a/src/plugins/score-plugin-midi/Midi/Commands/AddNote.hpp +++ b/src/plugins/score-plugin-midi/Midi/Commands/AddNote.hpp @@ -1,6 +1,8 @@ #pragma once + #include #include +#include "Midi/MidiDrop.hpp" #include #include @@ -52,7 +54,7 @@ class SCORE_PLUGIN_MIDI_EXPORT ReplaceNotes final : public score::Command public: ReplaceNotes( const ProcessModel& model, - const std::vector& note, + const MidiTrackNotes& note, int min, int max, TimeVal dur); diff --git a/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp b/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp index b320873f3b..339f7c434b 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp +++ b/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp @@ -65,11 +65,8 @@ void DropHandler::dropData( const double ratio = song_t / actualDuration.msec(); if (ratio != 1.) { - for (auto& note : track.notes) - { - note.setStart(ratio * note.start()); - note.setDuration(ratio * note.duration()); - } + track.trackEvents.apply_scale_ratio(ratio); + track.notes.apply_scale_ratio(ratio); } disp.submit(new Midi::ReplaceNotes{ midi, track.notes, track.min, track.max, actualDuration}); @@ -389,6 +386,13 @@ void parseEvent(const libremidi::track_event& ev, MidiTrack& nv, midi_note_map& nv.notes.push_back(note); } notes.erase(ev.m.bytes[1]); + + break; + } + case libremidi::message_type::CONTROL_CHANGE: + { + const auto& cc = ControllerData::make_cc(ev.m.get_channel() - 1, ev.m.bytes[1], ev.m.bytes[2]); + nv.trackEvents.push_back(delta, tick, total, cc); break; } default: @@ -487,7 +491,7 @@ MidiTrack::parse(const QByteArray& dat, const score::DocumentContext& ctx) tick += ev.tick; parseEvent(ev, nv, notes, delta, tick, total); } - if (nv.notes.size() > 0) + if (nv.notes.size() > 0 || nv.trackEvents.size() > 0) m.tracks.push_back(std::move(nv)); } break; diff --git a/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp b/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp index e3b37dc095..cec9c1473e 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp +++ b/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp @@ -18,11 +18,101 @@ class DropHandler final : public Process::ProcessDropHandler const score::DocumentContext& ctx) const noexcept override; }; +struct PitchbendData{ + int16_t bend; +}; +struct ControllerData { + midi_size_t channel; + midi_size_t number; + midi_size_t value; + static ControllerData make_cc(const midi_size_t channel, const midi_size_t controller, const midi_size_t value) + { + SCORE_ASSERT(channel >= 0 && channel < 16); + return ControllerData{channel, controller, value}; + } +}; + +struct NoteOnData { + midi_size_t channel; + midi_size_t note; + midi_size_t velocity; +}; +struct NoteOffData { + midi_size_t channel; + midi_size_t note; + midi_size_t velocity; +}; +struct MidiTrackEvent { + static MidiTrackEvent make_note_off(const double start, const midi_size_t ch, const midi_size_t n, const midi_size_t v){ + return MidiTrackEvent{m_start: start, m_message: Midi::NoteOffData{channel: ch, note: n, velocity: v}}; + } + static MidiTrackEvent make_note_on(const double start, const midi_size_t ch, const midi_size_t n, const midi_size_t v){ + return MidiTrackEvent{m_start: start, m_message: Midi::NoteOnData{channel: ch, note: n, velocity: v}}; + } + double m_start{}; + + void setStart(const double start){ m_start = start; } + const double start(){return m_start; } + std::variant m_message; +}; + +struct MidiTrackEvents { + void push_back(double delta, int tick, double total, Midi::ControllerData c){ + const double start = delta * (tick / total); + trackEvents.push_back(MidiTrackEvent{m_start:start, m_message: c}); + } + + void push_back(double delta, int tick, double total, Midi::NoteOnData n){ + const double start = delta * (tick / total); + trackEvents.push_back(MidiTrackEvent{m_start:start, m_message: n}); + } + + void push_back(double delta, int tick, double total, Midi::NoteOffData n){ + const double start = delta * (tick / total); + trackEvents.push_back(MidiTrackEvent{m_start:start, m_message: n}); + } + + void apply_scale_ratio(const double ratio){ + for (auto& event : trackEvents) + { + event.setStart(ratio * event.start()); + } + } + auto size() const { return trackEvents.size(); } + std::vector trackEvents; +}; + +struct MidiTrackNotes { + std::vector notes; + void push_back(Midi::NoteData note){ + notes.push_back(note); + } + + auto begin() { return notes.begin(); } + auto end() { return notes.end(); } + auto cbegin() const { return notes.begin(); } + auto cend() const { return notes.end(); } + auto begin() const { return notes.begin(); } + auto end() const { return notes.end(); } + + auto size() const { return notes.size(); } + auto empty() const { return notes.empty(); } + void apply_scale_ratio(const double ratio){ + for (auto& note : notes) + { + note.setStart(ratio * note.start()); + note.setDuration(ratio * note.duration()); + } + } +}; + + struct MidiTrack { QString name; - std::vector notes; + MidiTrackNotes notes; + MidiTrackEvents trackEvents; int min{127}, max{0}; struct MidiSong diff --git a/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp b/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp index 9a91133710..baeea1bc1d 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp +++ b/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp @@ -372,11 +372,8 @@ void Presenter::on_drop(const QPointF& pos, const QMimeData& md) // Scale notes so that the durations are relative to the ratio of the song // duration & constraint duration const double ratio = song.durationInMs / model().duration().msec(); - for (auto& note : track.notes) - { - note.setStart(ratio * note.start()); - note.setDuration(ratio * note.duration()); - } + track.notes.apply_scale_ratio(ratio); + track.trackEvents.apply_scale_ratio(ratio); disp.submit( model(), track.notes, track.min, track.max, model().duration()); } From f471c9839b2a9f0f74379c57f61c890a1b018a55 Mon Sep 17 00:00:00 2001 From: Gauthier Segay Date: Sat, 14 May 2022 15:08:47 +0200 Subject: [PATCH 2/3] use mpark/variant as MidiTrackEvent inner representation. --- src/plugins/score-plugin-midi/Midi/MidiDrop.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp b/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp index cec9c1473e..af1cd816f5 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp +++ b/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace Midi { @@ -53,7 +54,7 @@ struct MidiTrackEvent { void setStart(const double start){ m_start = start; } const double start(){return m_start; } - std::variant m_message; + mpark::variant m_message; }; struct MidiTrackEvents { From ba903981e20abb990bc0e3d17212a498bd20d6e0 Mon Sep 17 00:00:00 2001 From: Gauthier Segay Date: Tue, 17 May 2022 04:52:58 +0200 Subject: [PATCH 3/3] encapsulate min/max note pitch values into MidiTrackNotes --- .../score-plugin-midi/Midi/MidiDrop.cpp | 22 ++++++---------- .../score-plugin-midi/Midi/MidiDrop.hpp | 25 +++++++++++++------ .../score-plugin-midi/Midi/MidiPresenter.cpp | 2 +- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp b/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp index 339f7c434b..2deff28be9 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp +++ b/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp @@ -69,7 +69,7 @@ void DropHandler::dropData( track.notes.apply_scale_ratio(ratio); } disp.submit(new Midi::ReplaceNotes{ - midi, track.notes, track.min, track.max, actualDuration}); + midi, track.notes, track.notes.minimum_pitch_noticed(), track.notes.maximum_pitch_noticed(), actualDuration}); }; vec.push_back(std::move(p)); } @@ -273,11 +273,7 @@ static void parseEvent_format0(const libremidi::track_event& ev, std::vector nv.max) - nv.max = note.pitch(); - + nv.notes.notice_pitch(note.pitch()); notes.insert({note.pitch(), note}); } else @@ -287,7 +283,7 @@ static void parseEvent_format0(const libremidi::track_event& ev, std::vectorsecond; note.setDuration(delta * (tick / total - note.start())); - nv.notes.push_back(note); + nv.notes.append(note); } notes.erase(pitch); } @@ -305,7 +301,7 @@ static void parseEvent_format0(const libremidi::track_event& ev, std::vectorsecond; note.setDuration(delta * (tick / total - note.start())); - nv.notes.push_back(note); + nv.notes.append(note); } notes.erase(ev.m.bytes[1]); break; @@ -356,11 +352,7 @@ void parseEvent(const libremidi::track_event& ev, MidiTrack& nv, midi_note_map& note.setStart(delta * (tick / total)); note.setPitch(pitch); note.setVelocity(vel); - if (note.pitch() < nv.min) - nv.min = note.pitch(); - else if (note.pitch() > nv.max) - nv.max = note.pitch(); - + nv.notes.notice_pitch(note.pitch()); notes.insert({note.pitch(), note}); } else @@ -370,7 +362,7 @@ void parseEvent(const libremidi::track_event& ev, MidiTrack& nv, midi_note_map& { NoteData note = it->second; note.setDuration(delta * (tick / total - note.start())); - nv.notes.push_back(note); + nv.notes.append(note); } notes.erase(pitch); } @@ -383,7 +375,7 @@ void parseEvent(const libremidi::track_event& ev, MidiTrack& nv, midi_note_map& { NoteData note = it->second; note.setDuration(delta * (tick / total - note.start())); - nv.notes.push_back(note); + nv.notes.append(note); } notes.erase(ev.m.bytes[1]); diff --git a/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp b/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp index af1cd816f5..85d93e2142 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp +++ b/src/plugins/score-plugin-midi/Midi/MidiDrop.hpp @@ -45,10 +45,10 @@ struct NoteOffData { }; struct MidiTrackEvent { static MidiTrackEvent make_note_off(const double start, const midi_size_t ch, const midi_size_t n, const midi_size_t v){ - return MidiTrackEvent{m_start: start, m_message: Midi::NoteOffData{channel: ch, note: n, velocity: v}}; + return MidiTrackEvent{.m_start = start, .m_message = Midi::NoteOffData{.channel = ch, .note = n, .velocity = v}}; } static MidiTrackEvent make_note_on(const double start, const midi_size_t ch, const midi_size_t n, const midi_size_t v){ - return MidiTrackEvent{m_start: start, m_message: Midi::NoteOnData{channel: ch, note: n, velocity: v}}; + return MidiTrackEvent{.m_start = start, .m_message = Midi::NoteOnData{.channel = ch, .note = n, .velocity = v}}; } double m_start{}; @@ -60,17 +60,17 @@ struct MidiTrackEvent { struct MidiTrackEvents { void push_back(double delta, int tick, double total, Midi::ControllerData c){ const double start = delta * (tick / total); - trackEvents.push_back(MidiTrackEvent{m_start:start, m_message: c}); + trackEvents.push_back(MidiTrackEvent{.m_start = start, .m_message = c}); } void push_back(double delta, int tick, double total, Midi::NoteOnData n){ const double start = delta * (tick / total); - trackEvents.push_back(MidiTrackEvent{m_start:start, m_message: n}); + trackEvents.push_back(MidiTrackEvent{.m_start = start, .m_message = n}); } void push_back(double delta, int tick, double total, Midi::NoteOffData n){ const double start = delta * (tick / total); - trackEvents.push_back(MidiTrackEvent{m_start:start, m_message: n}); + trackEvents.push_back(MidiTrackEvent{.m_start = start, .m_message = n}); } void apply_scale_ratio(const double ratio){ @@ -84,11 +84,23 @@ struct MidiTrackEvents { }; struct MidiTrackNotes { + using pitch = int; std::vector notes; - void push_back(Midi::NoteData note){ + pitch min{127}, max{0}; + pitch minimum_pitch_noticed() const { return min; } + pitch maximum_pitch_noticed() const { return max; } + void append(Midi::NoteData note){ notes.push_back(note); } + void notice_pitch(int pitch){ + if (pitch < min) { + min = pitch; + } + else if (pitch > max) { + max = pitch; + } + } auto begin() { return notes.begin(); } auto end() { return notes.end(); } auto cbegin() const { return notes.begin(); } @@ -114,7 +126,6 @@ struct MidiTrack MidiTrackNotes notes; MidiTrackEvents trackEvents; - int min{127}, max{0}; struct MidiSong { diff --git a/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp b/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp index baeea1bc1d..21a07ae17b 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp +++ b/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp @@ -375,7 +375,7 @@ void Presenter::on_drop(const QPointF& pos, const QMimeData& md) track.notes.apply_scale_ratio(ratio); track.trackEvents.apply_scale_ratio(ratio); disp.submit( - model(), track.notes, track.min, track.max, model().duration()); + model(), track.notes, track.notes.minimum_pitch_noticed(), track.notes.maximum_pitch_noticed(), model().duration()); } std::vector> Presenter::selectedNotes() const