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

tremolo_tied_notes_fix #24774

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

RomanPudashkin
Copy link
Contributor

Resolves: #23168

@@ -32,6 +32,38 @@ using namespace mu::engraving;
using namespace muse;
using namespace muse::mpe;

static const Note* findFirstTremoloNote(const Note* note)
Copy link
Contributor

Choose a reason for hiding this comment

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

The methods Note::first/lastTiedNote contain some additional complications, one seemingly to prevent infinite loops. It might be good to investigate why these complications are necessary there, and if they are necessary here too.

const Note* firstTremoloNote = findFirstTremoloNote(note);
if (firstTremoloNote) {
if (firstTremoloNote != note) {
tnd.timestamp = timestampFromTicks(score, firstTremoloNote->tick().ticks());
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you need to do something with ctx.positionTickOffset here

Suggested change
tnd.timestamp = timestampFromTicks(score, firstTremoloNote->tick().ticks());
tnd.timestamp = timestampFromTicks(score, firstTremoloNote->tick().ticks() + ctx.positionTickOffset);

Comment on lines +233 to +238
const TimestampAndDuration lastNoteTnD = timestampAndDurationFromStartAndDurationTicks(score,
lastTremoloNote->tick().ticks(),
lastTremoloNote->chord()->actualTicks().ticks(),
ctx.positionTickOffset);

tnd.duration = lastNoteTnD.timestamp + lastNoteTnD.duration - tnd.timestamp;
Copy link
Contributor

Choose a reason for hiding this comment

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

Simpler:

Suggested change
const TimestampAndDuration lastNoteTnD = timestampAndDurationFromStartAndDurationTicks(score,
lastTremoloNote->tick().ticks(),
lastTremoloNote->chord()->actualTicks().ticks(),
ctx.positionTickOffset);
tnd.duration = lastNoteTnD.timestamp + lastNoteTnD.duration - tnd.timestamp;
const timestamp_t endTimestamp = timestampFromTicks(
score,
lastTremoloNote->tick().ticks() + lastTremoloNote->chord()->actualTicks().ticks() + ctx.positionTickOffset
);
tnd.duration = endTimestamp - tnd.timestamp;

I.e. instead of doing the addition in lastNoteTnD.timestamp + lastNoteTnD.duration, do the addition still in the ticks world and convert to timestamp world only once

Comment on lines +224 to +226
const Note* firstTremoloNote = findFirstTremoloNote(note);
if (firstTremoloNote) {
if (firstTremoloNote != note) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Either

Suggested change
const Note* firstTremoloNote = findFirstTremoloNote(note);
if (firstTremoloNote) {
if (firstTremoloNote != note) {
if (const Note* firstTremoloNote = findFirstTremoloNote(note)) {
if (firstTremoloNote != note) {

or

Suggested change
const Note* firstTremoloNote = findFirstTremoloNote(note);
if (firstTremoloNote) {
if (firstTremoloNote != note) {
const Note* firstTremoloNote = findFirstTremoloNote(note);
if (firstTremoloNote && firstTremoloNote != note) {

}

const Note* lastTremoloNote = findLastTremoloNote(note);
if (lastTremoloNote) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to check lastTremoloNote != note analogously to above?

Comment on lines +198 to 206

const TimestampAndDuration& tremoloTnD = tremoloTimeAndDuration(note, context, tremoloCache);
muse::mpe::ArticulationAppliedData& articulationData = noteCtx.chordCtx.commonArticulations.at(type);
articulationData.meta.timestamp = tremoloTnD.timestamp;
articulationData.meta.overallDuration = tremoloTnD.duration;

updateArticulationBoundaries(type, noteCtx.timestamp, noteCtx.duration, noteCtx.chordCtx.commonArticulations);

result.emplace_back(buildNoteEvent(std::move(noteCtx)));
Copy link
Contributor

Choose a reason for hiding this comment

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

How does it work exactly, is the entire tremolo rendered for the first tied note? And are subsequent tied notes encountered here too, or are they filtered out already? If we encounter them here, won't we render the tremolo multiple times? I don't fully understand why we need to cache something because I suppose we see each note only once, don't we?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're rendering all tied notes (not just the 1st one) that have the tremolo articulation. For each tied note we're creating stepCount sub-events (see TremoloRenderer::doRender; there is a for loop there). Each sub-event has ArticulationMeta that stores the global start position and duration of the tremolo, which are then used to trigger a single continuous sound (if the instrument supports it, otherwise we will just play multiple note on/off events). The cache is used to avoid recalculation the start position and duration for sub-events

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[TIED NOTES] Tremolo samples are being re-triggered by ties
2 participants