Merge pull request #16261 from alexpavlov96/midi_slides_tied
This commit is contained in:
commit
15c5ca2270
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue