Merge pull request #16261 from alexpavlov96/midi_slides_tied

This commit is contained in:
Alexander Pavlov 2023-02-07 19:59:32 +01:00 committed by GitHub
commit 15c5ca2270
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 59 deletions

View file

@ -289,35 +289,26 @@ static void collectNote(EventMap* events, int channel, const Note* note, double
if (!note->play() || note->hidden()) { // do not play overlapping notes
return;
}
Chord* chord = note->chord();
int staffIdx = static_cast<int>(staff->idx());
int ticks;
int tieLen = 0;
if (chord->isGrace()) {
assert(!graceNotesMerged(chord)); // this function should not be called on a grace note if grace notes are merged
chord = toChord(chord->explicitParent());
}
ticks = chord->actualTicks().ticks(); // ticks of the actual note
int ticks = chord->actualTicks().ticks(); // ticks of the actual note
// calculate additional length due to ties forward
// taking NoteEvent length adjustments into account
// but stopping at any note with multiple NoteEvents
// and processing those notes recursively
if (note->tieFor()) {
Note* n = note->tieFor()->endNote();
const Note* n = note->tieFor()->endNote();
while (n) {
NoteEventList nel = n->playEvents();
if (nel.size() == 1 && !isGlissandoFor(n)) {
// add value of this note to main note
// if we wish to suppress first note of ornament,
// then do this regardless of number of NoteEvents
if (!nel.empty()) {
tieLen += (n->chord()->actualTicks().ticks() * (nel[0].len())) / 1000;
} else {
// recurse
collectNote(events, channel, n, velocityMultiplier, tickOffset, staff, pitchWheelRenderer);
break;
}
if (n->tieFor() && n != n->tieFor()->endNote()) {
n = n->tieFor()->endNote();
} else {
@ -342,12 +333,8 @@ static void collectNote(EventMap* events, int channel, const Note* note, double
if (tieBack && nels == 1 && !isGlissandoFor(note)) {
break;
}
int p = pitch + e.pitch();
if (p < 0) {
p = 0;
} else if (p > 127) {
p = 127;
}
int p = std::clamp(pitch + e.pitch(), 0, 127);
int on = tick1 + (ticks * e.ontime()) / 1000;
int off = on + (ticks * e.len()) / 1000 - 1;
if (tieFor && i == static_cast<int>(nels) - 1) {
@ -356,15 +343,13 @@ static void collectNote(EventMap* events, int channel, const Note* note, double
// Get the velocity used for this note from the staff
// This allows correct playback of tremolos even without SND enabled.
int velo;
Fraction nonUnwoundTick = Fraction::fromTicks(on - tickOffset);
velo = staff->velocities().val(nonUnwoundTick);
velo *= velocityMultiplier;
velo *= e.velocityMultiplier();
playNote(events, note, channel, p, std::clamp(velo, 1, 127), std::max(0, on - graceOffsetOn), std::max(0,
off - graceOffsetOff),
staffIdx, pitchWheelRenderer);
int velo = staff->velocities().val(nonUnwoundTick) * velocityMultiplier * e.velocityMultiplier();
if (e.play()) {
playNote(events, note, channel, p, std::clamp(velo, 1, 127), std::max(0, on - graceOffsetOn), std::max(0,
off - graceOffsetOff),
static_cast<int>(staff->idx()), pitchWheelRenderer);
}
}
// Bends

View file

@ -36,11 +36,11 @@ void NoteEvent::read(XmlReader& e)
while (e.readNextStartElement()) {
const AsciiStringView tag(e.name());
if (tag == "pitch") {
_pitch = e.readInt();
m_pitch = e.readInt();
} else if (tag == "ontime") {
_ontime = e.readInt();
m_ontime = e.readInt();
} else if (tag == "len") {
_len = e.readInt();
m_len = e.readInt();
} else {
e.unknown();
}
@ -54,9 +54,9 @@ void NoteEvent::read(XmlReader& e)
void NoteEvent::write(XmlWriter& xml) const
{
xml.startElement("Event");
xml.tag("pitch", _pitch, 0);
xml.tag("ontime", _ontime, 0);
xml.tag("len", _len, NOTE_LENGTH);
xml.tag("pitch", m_pitch, 0);
xml.tag("ontime", m_ontime, 0);
xml.tag("len", m_len, NOTE_LENGTH);
xml.endElement();
}
@ -75,6 +75,6 @@ NoteEventList::NoteEventList()
bool NoteEvent::operator==(const NoteEvent& e) const
{
return (e._pitch == _pitch) && (e._ontime == _ontime) && (e._len == _len);
return (e.m_pitch == m_pitch) && (e.m_ontime == m_ontime) && (e.m_len == m_len);
}
}

View file

@ -38,34 +38,38 @@ class XmlReader;
class NoteEvent
{
int _pitch; // relative pitch to note pitch
int _ontime; // one unit is 1/1000 of nominal note len
int _len; // one unit is 1/1000 of nominal note len
double _velocityMultiplier = 1.0;
public:
constexpr static int NOTE_LENGTH = 1000;
constexpr static double GLISSANDO_VELOCITY_MULTIPLIER = 0.7;
constexpr static double DEFAULT_VELOCITY_MULTIPLIER = 1.0;
NoteEvent()
: _pitch(0), _ontime(0), _len(NOTE_LENGTH), _velocityMultiplier(1.0) {}
NoteEvent(int a, int b, int c, double d = 1.0)
: _pitch(a), _ontime(b), _len(c), _velocityMultiplier(d) {}
NoteEvent() {}
NoteEvent(int a, int b, int c, double d = 1.0, double play = true)
: m_pitch(a), m_ontime(b), m_len(c), m_velocityMultiplier(d), m_play(play) {}
void read(XmlReader&);
void write(XmlWriter&) const;
int pitch() const { return _pitch; }
int ontime() const { return _ontime; }
int offtime() const { return _ontime + _len; }
int len() const { return _len; }
double velocityMultiplier() const { return _velocityMultiplier; }
void setPitch(int v) { _pitch = v; }
void setOntime(int v) { _ontime = v; }
void setLen(int v) { _len = v; }
void setVelocityMultiplier(double velocityMultiplier) { _velocityMultiplier = velocityMultiplier; }
int pitch() const { return m_pitch; }
int ontime() const { return m_ontime; }
int offtime() const { return m_ontime + m_len; }
int len() const { return m_len; }
double velocityMultiplier() const { return m_velocityMultiplier; }
bool play() const { return m_play; }
void setPitch(int v) { m_pitch = v; }
void setOntime(int v) { m_ontime = v; }
void setLen(int v) { m_len = v; }
void setVelocityMultiplier(double velocityMultiplier) { m_velocityMultiplier = velocityMultiplier; }
void setPlay(bool play) { m_play = play; }
bool operator==(const NoteEvent&) const;
private:
int m_pitch = 0; // relative pitch to note pitch
int m_ontime = 0; // one unit is 1/1000 of nominal note len
int m_len = NOTE_LENGTH; // one unit is 1/1000 of nominal note len
double m_velocityMultiplier = DEFAULT_VELOCITY_MULTIPLIER; // can be used to lower sound (0-1)
bool m_play = true; // when 'false', note event is used only for length calculation
};
//---------------------------------------------------------

View file

@ -702,10 +702,12 @@ bool renderNoteArticulation(NoteEventList* events, Note* note, bool chromatic, i
// append a NoteEvent either by calculating an articulationExcursion or by
// the given chromatic relative pitch.
// RETURNS the new ontime value. The caller is expected to assign this value.
auto makeEvent = [note, chord, chromatic, events](int pitch, int ontime, int duration, double velocityMultiplier = 1.0) {
auto makeEvent
= [note, chord, chromatic, events](int pitch, int ontime, int duration, double velocityMultiplier = 1.0, bool play = true) {
events->push_back(NoteEvent(chromatic ? pitch : chromaticPitchSteps(note, note, pitch),
ontime / chord->actualTicks().ticks(),
duration / chord->actualTicks().ticks(), velocityMultiplier));
duration / chord->actualTicks().ticks(), velocityMultiplier, play));
return ontime + duration;
};
@ -775,9 +777,14 @@ bool renderNoteArticulation(NoteEventList* events, Note* note, bool chromatic, i
const double glissandoVelocityMultiplier = NoteEvent::GLISSANDO_VELOCITY_MULTIPLIER;
// render the body, i.e. the glissando
for (int j = 0; j < b - 1; j++) {
if (onTimes.size() > 1) {
makeEvent(body[0], onTimes[0], onTimes[1] - onTimes[0],
defaultVelocityMultiplier, !note->tieBack());
}
for (int j = 1; j < b - 1; j++) {
makeEvent(body[j], onTimes[j], onTimes[j + 1] - onTimes[j],
j == 0 ? defaultVelocityMultiplier : glissandoVelocityMultiplier);
glissandoVelocityMultiplier);
}
makeEvent(body[b - 1], onTimes[b - 1], (millespernote * b - onTimes[b - 1]) + sustain, glissandoVelocityMultiplier);
} else {
@ -1092,7 +1099,7 @@ static void createSlideOutNotePlayEvents(Note* note, NoteEventList* el, int& onT
const int slideDuration = 45;
trailtime += slideNotes * slideDuration;
int slideOn = 1000 - trailtime;
el->push_back(NoteEvent(0, onTime, slideOn));
el->push_back(NoteEvent(0, onTime, slideOn, NoteEvent::DEFAULT_VELOCITY_MULTIPLIER, !note->tieBack()));
int pitch = 0;
int pitchOffset = note->slide().is(Note::SlideType::Doit) ? 1 : -1;