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..2deff28be9 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp +++ b/src/plugins/score-plugin-midi/Midi/MidiDrop.cpp @@ -65,14 +65,11 @@ 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}); + midi, track.notes, track.notes.minimum_pitch_noticed(), track.notes.maximum_pitch_noticed(), actualDuration}); }; vec.push_back(std::move(p)); } @@ -276,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 @@ -290,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); } @@ -308,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; @@ -359,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 @@ -373,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); } @@ -386,9 +375,16 @@ 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]); + + 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 +483,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..85d93e2142 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 { @@ -18,12 +19,113 @@ 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; } + mpark::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 { + using pitch = int; + std::vector notes; + 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(); } + 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; - int min{127}, max{0}; + MidiTrackNotes notes; + MidiTrackEvents trackEvents; struct MidiSong { diff --git a/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp b/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp index 9a91133710..21a07ae17b 100644 --- a/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp +++ b/src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp @@ -372,13 +372,10 @@ 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()); + model(), track.notes, track.notes.minimum_pitch_noticed(), track.notes.maximum_pitch_noticed(), model().duration()); } std::vector> Presenter::selectedNotes() const