Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] Supporting more midi messages in MidiDrop plugin #1399

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/plugins/score-plugin-midi/Midi/Commands/AddNote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void AddNotes::deserializeImpl(DataStreamOutput& s)

ReplaceNotes::ReplaceNotes(
const ProcessModel& model,
const std::vector<NoteData>& n,
const MidiTrackNotes& n,
int min,
int max,
TimeVal d)
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/score-plugin-midi/Midi/Commands/AddNote.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <Midi/Commands/CommandFactory.hpp>
#include <Midi/MidiNote.hpp>
#include "Midi/MidiDrop.hpp"
#include <Process/TimeValue.hpp>

#include <score/model/path/Path.hpp>
Expand Down Expand Up @@ -52,7 +54,7 @@ class SCORE_PLUGIN_MIDI_EXPORT ReplaceNotes final : public score::Command
public:
ReplaceNotes(
const ProcessModel& model,
const std::vector<NoteData>& note,
const MidiTrackNotes& note,
int min,
int max,
TimeVal dur);
Expand Down
38 changes: 17 additions & 21 deletions src/plugins/score-plugin-midi/Midi/MidiDrop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,11 @@ void DropHandler::dropData(
const double ratio = song_t / actualDuration.msec();
if (ratio != 1.)
{
for (auto& note : track.notes)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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));
}
Expand Down Expand Up @@ -276,11 +273,7 @@ static void parseEvent_format0(const libremidi::track_event& ev, std::vector<Mid
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
Expand All @@ -290,7 +283,7 @@ static void parseEvent_format0(const libremidi::track_event& ev, std::vector<Mid
{
NoteData note = it->second;
note.setDuration(delta * (tick / total - note.start()));
nv.notes.push_back(note);
nv.notes.append(note);
}
notes.erase(pitch);
}
Expand All @@ -308,7 +301,7 @@ static void parseEvent_format0(const libremidi::track_event& ev, std::vector<Mid
{
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;
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
Expand All @@ -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:
Expand Down Expand Up @@ -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;
Expand Down
106 changes: 104 additions & 2 deletions src/plugins/score-plugin-midi/Midi/MidiDrop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <Midi/MidiNote.hpp>
#include <Process/Drop/ProcessDropHandler.hpp>
#include <Process/TimeValue.hpp>
#include <mpark/variant.hpp>
namespace Midi
{

Expand All @@ -18,12 +19,113 @@ class DropHandler final : public Process::ProcessDropHandler
const score::DocumentContext& ctx) const noexcept override;
};

struct PitchbendData{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAybe all of these could go into a more general Midi.hpp ... or, I wonder if it wouldn't be best in libremidi, and then try to consume those directly ? not sure...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion, I'll start moving it soon then, please let me know what makes most sense.

I was going with MidiTrackEvent as type name (which is the core object along MidiNote which has "pointers" to optional? pair), and considering a "domain" folder:

/domain/MidiNote.hpp moving the current one
/domain/MidiTrackEvent.hpp the detailed header
/domain/Midi.hpp a logical header, including several others, for convenience in other modules.

Do you like this?

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){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these functions look a lot like the ones in libremidi, maybe it should go there ?

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<Midi::NoteOnData, Midi::NoteOffData, Midi::ControllerData,Midi::PitchbendData> m_message;
};

struct MidiTrackEvents {
void push_back(double delta, int tick, double total, Midi::ControllerData c){
smoothdeveloper marked this conversation as resolved.
Show resolved Hide resolved
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<Midi::MidiTrackEvent> trackEvents;
};

struct MidiTrackNotes {
using pitch = int;
std::vector<Midi::NoteData> 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<Midi::NoteData> notes;
int min{127}, max{0};
MidiTrackNotes notes;
MidiTrackEvents trackEvents;

struct MidiSong
{
Expand Down
9 changes: 3 additions & 6 deletions src/plugins/score-plugin-midi/Midi/MidiPresenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Midi::ReplaceNotes>(
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<Id<Note>> Presenter::selectedNotes() const
Expand Down