Skip to content

Commit b50b002

Browse files
authored
Merge pull request #28558 from alexpavlov96/grace_bend_midi
midi-render: implemented playback for bends to "grace note after"
2 parents f0ed402 + 861f443 commit b50b002

File tree

6 files changed

+692
-18
lines changed

6 files changed

+692
-18
lines changed

src/engraving/compat/midi/compatmidirender.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,18 @@ void CompatMidiRender::createGraceNotesPlayEvents(const Score* score, const Frac
610610

611611
on += graceDuration;
612612
}
613-
if (na) {
613+
614+
bool graceBendAfter = std::any_of(gna.begin(), gna.end(), [](const Chord* graceChord) {
615+
for (Note* note : graceChord->notes()) {
616+
if (note->bendBack()) {
617+
return true;
618+
}
619+
}
620+
621+
return false;
622+
});
623+
624+
if (!graceBendAfter && na) {
614625
if (chord->dots() == 1) {
615626
trailtime = floor(667 * weighta);
616627
} else if (chord->dots() == 2) {

src/engraving/compat/midi/compatmidirenderinternal.cpp

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -344,12 +344,12 @@ static bool shouldProceedBend(const Note* note)
344344
return baseNote->lastTiedNote(false) == note;
345345
}
346346

347-
static BendPlaybackInfo getBendPlaybackInfo(const GuitarBend* bend, int bendStart, int bendDuration)
347+
static BendPlaybackInfo getBendPlaybackInfo(const GuitarBend* bend, int bendStart, int bendDuration, bool graceBeforeBend)
348348
{
349349
BendPlaybackInfo bendInfo;
350350

351-
// currently ignoring diagram for grace bends
352-
if (bend->type() != GuitarBendType::GRACE_NOTE_BEND) {
351+
// currently ignoring diagram for "grace before" bends
352+
if (!graceBeforeBend) {
353353
bendInfo.startTimeFactor = bend->startTimeFactor();
354354
bendInfo.endTimeFactor = bend->endTimeFactor();
355355
}
@@ -360,6 +360,71 @@ static BendPlaybackInfo getBendPlaybackInfo(const GuitarBend* bend, int bendStar
360360
return bendInfo;
361361
}
362362

363+
static void fillBendDurations(const Note* bendStartNote, const std::unordered_set<const Note*>& currentNotes,
364+
std::unordered_map<const Note*, size_t>& durations, bool tiedToNext)
365+
{
366+
if (!bendStartNote || currentNotes.empty()) {
367+
return;
368+
}
369+
370+
size_t bendsAmount = tiedToNext ? currentNotes.size() + 1 : currentNotes.size();
371+
size_t eachBendDuration = bendStartNote->chord()->actualTicks().ticks() / bendsAmount;
372+
373+
for (const Note* note : currentNotes) {
374+
durations.insert({ note, eachBendDuration });
375+
}
376+
}
377+
378+
static std::unordered_map<const Note*, size_t> getGraceNoteBendDurations(const Note* note)
379+
{
380+
std::unordered_map<const Note*, size_t> durations;
381+
const Note* bendStartNote = nullptr;
382+
std::unordered_set<const Note*> currentNotes;
383+
384+
while (note->tieFor()) {
385+
const Tie* tieFor = note->tieFor();
386+
IF_ASSERT_FAILED(tieFor->endNote()) {
387+
LOGE() << "cannot find tied note for note on track " << note->track() << ", tick " << note->tick().ticks();
388+
return {};
389+
}
390+
note = tieFor->endNote();
391+
}
392+
393+
while (note->bendFor()) {
394+
const GuitarBend* bendFor = note->bendFor();
395+
const Note* endNote = bendFor->endNote();
396+
if (!endNote || note == endNote) {
397+
LOGE() << "cannot find end bend note for note on track " << note->track() << ", tick " << note->tick().ticks();
398+
return {};
399+
}
400+
401+
if (endNote->chord()->isGraceAfter()) {
402+
if (currentNotes.empty()) {
403+
IF_ASSERT_FAILED(note->chord() == endNote->chord()->explicitParent()) {
404+
LOGE() << "error in filling bends midi data for note on track " << note->track() << ", tick " << note->tick().ticks();
405+
return {};
406+
}
407+
bendStartNote = note;
408+
currentNotes.insert(bendStartNote);
409+
}
410+
411+
if (endNote->bendFor()) {
412+
currentNotes.insert(endNote);
413+
}
414+
} else {
415+
fillBendDurations(bendStartNote, currentNotes, durations, true);
416+
bendStartNote = nullptr;
417+
currentNotes.clear();
418+
}
419+
420+
note = bendFor->endNote();
421+
}
422+
423+
fillBendDurations(bendStartNote, currentNotes, durations, false);
424+
425+
return durations;
426+
}
427+
363428
/*
364429
* All consecutive tie and bend combinations are processed in a single pass, adding pitch bends where needed to ensure continuity between notes.
365430
*
@@ -378,8 +443,9 @@ static void collectGuitarBend(const Note* note,
378443
return;
379444
}
380445

446+
const auto& graceNoteBendDurations = getGraceNoteBendDurations(note);
447+
381448
int curPitchBendSegmentStart = onTime;
382-
int curPitchBendSegmentEnd = 0;
383449

384450
int quarterOffsetFromStartNote = 0;
385451
int currentQuarterTones = 0;
@@ -392,22 +458,26 @@ static void collectGuitarBend(const Note* note,
392458

393459
while (note->bendFor() || note->tieFor()) {
394460
const GuitarBend* bendFor = note->bendFor();
395-
int duration = 0;
396-
if (bendFor && bendFor->type() == GuitarBendType::GRACE_NOTE_BEND) {
397-
duration = (previousChordTicks == -1) ? GRACE_BEND_DURATION : std::min(previousChordTicks / 2, GRACE_BEND_DURATION);
398-
} else {
399-
duration = note->chord()->actualTicks().ticks();
400-
}
401-
curPitchBendSegmentEnd = curPitchBendSegmentStart + duration;
402-
461+
int duration = note->chord()->actualTicks().ticks();
403462
if (bendFor) {
404463
const Note* endNote = bendFor->endNote();
405464

406465
if (!endNote) {
407466
return;
408467
}
409468

410-
BendPlaybackInfo bendPlaybackInfo = getBendPlaybackInfo(bendFor, curPitchBendSegmentStart, duration);
469+
bool graceBeforeBend = false;
470+
if (note->chord()->isGraceBefore() && bendFor) {
471+
Note* endNote = bendFor->endNote();
472+
if (endNote && endNote->noteType() == NoteType::NORMAL) {
473+
duration = (previousChordTicks == -1) ? GRACE_BEND_DURATION : std::min(previousChordTicks / 2, GRACE_BEND_DURATION);
474+
graceBeforeBend = true;
475+
}
476+
} else if (muse::contains(graceNoteBendDurations, note)) {
477+
duration = graceNoteBendDurations.at(note);
478+
}
479+
480+
BendPlaybackInfo bendPlaybackInfo = getBendPlaybackInfo(bendFor, curPitchBendSegmentStart, duration, graceBeforeBend);
411481
double initialPitchBendValue = quarterOffsetFromStartNote / 2.0;
412482

413483
if (bendPlaybackInfo.startTick > curPitchBendSegmentStart) {
@@ -435,7 +505,7 @@ static void collectGuitarBend(const Note* note,
435505
pitchWheelRenderer.addPitchWheelFunction(pitchWheelSquareFunc, channel, note->staffIdx(), effect);
436506
quarterOffsetFromStartNote += currentQuarterTones;
437507

438-
if (bendPlaybackInfo.endTick < curPitchBendSegmentEnd) {
508+
if (bendPlaybackInfo.endTick < curPitchBendSegmentStart + duration) {
439509
addConstPitchWheel(bendPlaybackInfo.endTick, quarterOffsetFromStartNote / 2.0, pitchWheelRenderer, channel,
440510
note->staffIdx(),
441511
effect);
@@ -447,7 +517,7 @@ static void collectGuitarBend(const Note* note,
447517

448518
note = endNote;
449519
} else {
450-
if (note->bendBack()) {
520+
if (!note->isGrace() && note->bendBack()) {
451521
addConstPitchWheel(note->tick().ticks(), quarterOffsetFromStartNote / 2.0, pitchWheelRenderer, channel,
452522
note->staffIdx(), effect);
453523
}
@@ -459,10 +529,10 @@ static void collectGuitarBend(const Note* note,
459529
}
460530
}
461531

462-
curPitchBendSegmentStart = curPitchBendSegmentEnd;
532+
curPitchBendSegmentStart += duration;
463533
}
464534

465-
if (note->bendBack()) {
535+
if (!note->isGrace() && note->bendBack()) {
466536
addConstPitchWheel(note->tick().ticks(), quarterOffsetFromStartNote / 2.0, pitchWheelRenderer, channel, note->staffIdx(), effect);
467537
}
468538
}

0 commit comments

Comments
 (0)