From 634877978f0468c8e8b1f48080fa3802c08a7d29 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Fri, 10 Jan 2025 18:00:53 +0100 Subject: [PATCH 01/13] Add InitTimemapNotesFunctor --- include/vrv/midifunctor.h | 44 +++++++++++++++++++++++++++++++++++++++ src/doc.cpp | 4 ++++ src/midifunctor.cpp | 21 +++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index 7569eaf4f2..c77ad4cc74 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -177,6 +177,50 @@ class InitTimemapTiesFunctor : public Functor { // }; +//---------------------------------------------------------------------------- +// InitTimemapNotesFunctor +//---------------------------------------------------------------------------- + +/** + * This class adjusts note duration for grace notes and arpeggios. + */ +class InitTimemapNotesFunctor : public Functor { +public: + /** + * @name Constructors, destructors + */ + ///@{ + InitTimemapNotesFunctor(); + virtual ~InitTimemapNotesFunctor() = default; + ///@} + + /* + * Abstract base implementation + */ + bool ImplementsEndInterface() const override { return true; } + + /* + * Functor interface + */ + ///@{ + FunctorCode VisitChord(Chord *chord) override; + FunctorCode VisitGraceGrpEnd(GraceGrp *graceGrp) override; + FunctorCode VisitNote(Note *note) override; + ///@} + +protected: + // +private: + // +public: + // +private: + // Grace note sequence + std::list m_graceNotes; + // Indicates whether the last grace note/chord was accented + bool m_accentedGraceNote; +}; + //---------------------------------------------------------------------------- // InitMIDIFunctor //---------------------------------------------------------------------------- diff --git a/src/doc.cpp b/src/doc.cpp index fa1b7bfe2c..ba48144123 100644 --- a/src/doc.cpp +++ b/src/doc.cpp @@ -404,6 +404,10 @@ void Doc::CalculateTimemap() initTimemapTies.SetDirection(BACKWARD); this->Process(initTimemapTies); + // Adjust the duration of notes (grace notes and arpeggios) + InitTimemapNotesFunctor initTimemapNotes; + this->Process(initTimemapNotes); + m_timemapTempo = m_options->m_midiTempoAdjustment.GetValue(); } diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index 731c5a5158..b1799792ba 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -313,6 +313,27 @@ FunctorCode InitTimemapTiesFunctor::VisitTie(Tie *tie) return FUNCTOR_SIBLINGS; } +//---------------------------------------------------------------------------- +// InitTimemapNotesFunctor +//---------------------------------------------------------------------------- + +InitTimemapNotesFunctor::InitTimemapNotesFunctor() : Functor() {} + +FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) +{ + return FUNCTOR_CONTINUE; +} + +FunctorCode InitTimemapNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) +{ + return FUNCTOR_SIBLINGS; +} + +FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) +{ + return FUNCTOR_SIBLINGS; +} + //---------------------------------------------------------------------------- // InitMidiFunctor //---------------------------------------------------------------------------- From ed5158e6a82660c18dc14f9ee014a23ccce7e038 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Wed, 29 Jan 2025 08:38:16 +0100 Subject: [PATCH 02/13] Implementation of the InitTimemapNotesFunctor (WIP) --- include/vrv/midifunctor.h | 21 ++++++++++-- src/doc.cpp | 1 + src/midifunctor.cpp | 72 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index c77ad4cc74..0efda7f5a1 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -177,6 +177,14 @@ class InitTimemapTiesFunctor : public Functor { // }; +/** + * Helper struct to store grace note/chord sequences + */ +struct GraceChord { + std::list pitches; + data_DURATION duration; +}; + //---------------------------------------------------------------------------- // InitTimemapNotesFunctor //---------------------------------------------------------------------------- @@ -199,6 +207,13 @@ class InitTimemapNotesFunctor : public Functor { */ bool ImplementsEndInterface() const override { return true; } + /* + * Setter for various properties + */ + ///@{ + void SetNoCue(bool noCue) { m_noCue = noCue; } + ///@} + /* * Functor interface */ @@ -215,8 +230,10 @@ class InitTimemapNotesFunctor : public Functor { public: // private: - // Grace note sequence - std::list m_graceNotes; + // Indicates whether cue notes should be included + bool m_noCue; + // Grace note/chord sequence + std::list m_graceNotes; // Indicates whether the last grace note/chord was accented bool m_accentedGraceNote; }; diff --git a/src/doc.cpp b/src/doc.cpp index ba48144123..2ee8597de1 100644 --- a/src/doc.cpp +++ b/src/doc.cpp @@ -406,6 +406,7 @@ void Doc::CalculateTimemap() // Adjust the duration of notes (grace notes and arpeggios) InitTimemapNotesFunctor initTimemapNotes; + initTimemapNotes.SetNoCue(this->GetOptions()->m_midiNoCue.GetValue()); this->Process(initTimemapNotes); m_timemapTempo = m_options->m_midiTempoAdjustment.GetValue(); diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index b1799792ba..5738c30a7d 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -317,10 +317,32 @@ FunctorCode InitTimemapTiesFunctor::VisitTie(Tie *tie) // InitTimemapNotesFunctor //---------------------------------------------------------------------------- -InitTimemapNotesFunctor::InitTimemapNotesFunctor() : Functor() {} +InitTimemapNotesFunctor::InitTimemapNotesFunctor() : Functor() +{ + m_noCue = false; +} FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) { + if (chord->IsGraceNote()) { + std::list pitches; + const ListOfObjects ¬es = chord->GetList(); + for (Object *obj : notes) { + Note *note = vrv_cast(obj); + assert(note); + pitches.push_back(note); + } + + m_graceNotes.push_back({ pitches, chord->GetActualDur() }); + + bool accented = (chord->GetGrace() == GRACE_acc); + const GraceGrp *graceGrp = vrv_cast(chord->GetFirstAncestor(GRACEGRP)); + if (graceGrp && (graceGrp->GetGrace() == GRACE_acc)) accented = true; + m_accentedGraceNote = accented; + + return FUNCTOR_SIBLINGS; + } + return FUNCTOR_CONTINUE; } @@ -331,6 +353,54 @@ FunctorCode InitTimemapNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) { + // Skip linked notes + if (note->HasSameasLink()) { + return FUNCTOR_SIBLINGS; + } + + // Skip cue notes when midiNoCue is activated + if ((note->GetCue() == BOOLEAN_true) && m_noCue) { + return FUNCTOR_SIBLINGS; + } + + // If the note is a secondary tied note, then ignore it + if (note->GetScoreTimeTiedDuration() < 0) { + return FUNCTOR_SIBLINGS; + } + + // Handle grace notes + if (note->IsGraceNote()) { + m_graceNotes.push_back({ { note }, note->GetDur() }); + + bool accented = (note->GetGrace() == GRACE_acc); + const GraceGrp *graceGrp = vrv_cast(note->GetFirstAncestor(GRACEGRP)); + if (graceGrp && (graceGrp->GetGrace() == GRACE_acc)) accented = true; + m_accentedGraceNote = accented; + + return FUNCTOR_SIBLINGS; + } + + const int channel = 0; + int velocity = MIDI_VELOCITY; + if (note->HasVel()) velocity = note->GetVel(); + + const int tpq = m_midiFile->getTPQ(); + + // Check if some grace notes must be performed + if (!m_graceNotes.empty()) { + this->GenerateGraceNoteMIDI(note, startTime, tpq, channel, velocity); + m_graceNotes.clear(); + } + + // Check if note is deferred + if (m_deferredNotes.find(note) != m_deferredNotes.end()) { + startTime += m_deferredNotes.at(note); + m_deferredNotes.erase(note); + } + + // Store reference, i.e. for Nachschlag + m_lastNote = note; + return FUNCTOR_SIBLINGS; } From 4313494777e3afe68699c0501206d9e0d02eb351 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Fri, 14 Feb 2025 16:53:04 +0100 Subject: [PATCH 03/13] Use timemap for grace notes --- include/vrv/midifunctor.h | 33 ++---- include/vrv/vrvdef.h | 1 + src/midifunctor.cpp | 215 +++++++++++++++----------------------- 3 files changed, 96 insertions(+), 153 deletions(-) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index 0efda7f5a1..8aba15aec0 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -213,20 +213,25 @@ class InitTimemapNotesFunctor : public Functor { ///@{ void SetNoCue(bool noCue) { m_noCue = noCue; } ///@} - + /* * Functor interface */ ///@{ FunctorCode VisitChord(Chord *chord) override; FunctorCode VisitGraceGrpEnd(GraceGrp *graceGrp) override; + FunctorCode VisitMeasure(Measure *measure) override; FunctorCode VisitNote(Note *note) override; ///@} protected: // private: - // + /** + * Creates the MIDI output of the grace note sequence + */ + void GenerateGraceNoteMIDI(Note *refNote); + public: // private: @@ -236,6 +241,10 @@ class InitTimemapNotesFunctor : public Functor { std::list m_graceNotes; // Indicates whether the last grace note/chord was accented bool m_accentedGraceNote; + // The current tempo + double m_currentTempo; + // The last (non grace) note + Note *m_lastNote; }; //---------------------------------------------------------------------------- @@ -328,16 +337,6 @@ struct MIDIHeldNote { double m_stopTime = 0; }; -/** - * Helper struct to store chord sequences in MIDI output due to grace notes - */ -struct MIDIChord { - std::set pitches; - double duration; -}; - -using MIDIChordSequence = std::list; - /** * This class performs the export to a MidiFile. */ @@ -388,7 +387,6 @@ class GenerateMIDIFunctor : public ConstFunctor { FunctorCode VisitBTrem(const BTrem *bTrem) override; FunctorCode VisitChord(const Chord *chord) override; FunctorCode VisitFTrem(const FTrem *fTrem) override; - FunctorCode VisitGraceGrpEnd(const GraceGrp *graceGrp) override; FunctorCode VisitHalfmRpt(const HalfmRpt *halfmRpt) override; FunctorCode VisitLayer(const Layer *layer) override; FunctorCode VisitLayerEnd(const Layer *layer) override; @@ -412,11 +410,6 @@ class GenerateMIDIFunctor : public ConstFunctor { */ void DeferMIDINote(const Note *refNote, double shift, bool includeChordSiblings); - /** - * Creates the MIDI output of the grace note sequence - */ - void GenerateGraceNoteMIDI(const Note *refNote, double startTime, int tpq, int channel, int velocity); - /** * Change the octave shift at the begin/end of octaves */ @@ -459,10 +452,6 @@ class GenerateMIDIFunctor : public ConstFunctor { std::map m_deferredNotes; // Octave info which is used to determine the octave shift std::list m_octaves; - // Grace note sequence - MIDIChordSequence m_graceNotes; - // Indicates whether the last grace note/chord was accented - bool m_accentedGraceNote; // Indicates whether cue notes should be included bool m_noCue; // Tablature held notes indexed by (course - 1) diff --git a/include/vrv/vrvdef.h b/include/vrv/vrvdef.h index 9db819881f..af70654806 100644 --- a/include/vrv/vrvdef.h +++ b/include/vrv/vrvdef.h @@ -73,6 +73,7 @@ namespace vrv { #define MIDI_TEMPO 120 #define UNACC_GRACENOTE_DUR 27 // in milliseconds +#define UNACC_GRANENOTE_FRACTION Fraction(1, 64) //---------------------------------------------------------------------------- // Object defines diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index 5738c30a7d..1ff937a002 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -320,6 +320,8 @@ FunctorCode InitTimemapTiesFunctor::VisitTie(Tie *tie) InitTimemapNotesFunctor::InitTimemapNotesFunctor() : Functor() { m_noCue = false; + m_lastNote = NULL; + m_currentTempo = MIDI_TEMPO; } FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) @@ -342,13 +344,47 @@ FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) return FUNCTOR_SIBLINGS; } - + return FUNCTOR_CONTINUE; } FunctorCode InitTimemapNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) { - return FUNCTOR_SIBLINGS; + // Handling of Nachschlag + if (!m_graceNotes.empty() && (graceGrp->GetAttach() == graceGrpLog_ATTACH_pre) && !m_accentedGraceNote + && m_lastNote) { + Fraction startTime = m_lastNote->GetScoreTimeOffset(); + const Fraction graceNoteDur = UNACC_GRANENOTE_FRACTION * (int)m_currentTempo / 120; + const Fraction totalDur = graceNoteDur * (int)m_graceNotes.size(); + startTime = startTime - totalDur; + startTime = (startTime < 0) ? 0 : startTime; + + for (const auto &chord : m_graceNotes) { + const Fraction stopTime = startTime + graceNoteDur; + for (const auto ¬e : chord.pitches) { + // Set the start (onset) and end (offset) of the grace note + note->SetScoreTimeOnset(startTime); + note->SetScoreTimeOffset(stopTime); + double startRealTimeSeconds = startTime.ToDouble() * 60.0 / m_currentTempo; + double stopRealTimeSeconds = stopTime.ToDouble() * 60.0 / m_currentTempo; + note->SetRealTimeOnsetSeconds(startRealTimeSeconds); + note->SetRealTimeOffsetSeconds(stopRealTimeSeconds); + } + startTime = stopTime; + } + + m_graceNotes.clear(); + } + + return FUNCTOR_CONTINUE; +} + +FunctorCode InitTimemapNotesFunctor::VisitMeasure(Measure *measure) +{ + // Update the current tempo + m_currentTempo = measure->GetCurrentTempo(); + + return FUNCTOR_CONTINUE; } FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) @@ -367,7 +403,7 @@ FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) if (note->GetScoreTimeTiedDuration() < 0) { return FUNCTOR_SIBLINGS; } - + // Handle grace notes if (note->IsGraceNote()) { m_graceNotes.push_back({ { note }, note->GetDur() }); @@ -380,30 +416,60 @@ FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) return FUNCTOR_SIBLINGS; } - const int channel = 0; - int velocity = MIDI_VELOCITY; - if (note->HasVel()) velocity = note->GetVel(); - - const int tpq = m_midiFile->getTPQ(); - // Check if some grace notes must be performed if (!m_graceNotes.empty()) { - this->GenerateGraceNoteMIDI(note, startTime, tpq, channel, velocity); + this->GenerateGraceNoteMIDI(note); m_graceNotes.clear(); } - // Check if note is deferred - if (m_deferredNotes.find(note) != m_deferredNotes.end()) { - startTime += m_deferredNotes.at(note); - m_deferredNotes.erase(note); - } - // Store reference, i.e. for Nachschlag m_lastNote = note; - + return FUNCTOR_SIBLINGS; } +void InitTimemapNotesFunctor::GenerateGraceNoteMIDI(Note *refNote) +{ + Fraction startTime = refNote->GetScoreTimeOnset(); + + Fraction graceNoteDur = 0; + if (m_accentedGraceNote && !m_graceNotes.empty()) { + const Fraction totalDur = refNote->GetScoreTimeDuration() / 2; + // Adjust the start of the main note + refNote->SetScoreTimeOnset(startTime + totalDur); + double startRealTime = (startTime + totalDur).ToDouble() * 60.0 / m_currentTempo; + refNote->SetRealTimeOnsetSeconds(startRealTime); + graceNoteDur = totalDur / (int)m_graceNotes.size(); + } + else { + graceNoteDur = UNACC_GRANENOTE_FRACTION * (int)m_currentTempo / 120; + const Fraction totalDur = graceNoteDur * (int)m_graceNotes.size(); + if (startTime >= totalDur) { + startTime = startTime - totalDur; + } + else { + // Adjust the start of the main note + refNote->SetScoreTimeOnset(startTime + totalDur); + double startRealTime = (startTime + totalDur).ToDouble() * 60.0 / m_currentTempo; + refNote->SetRealTimeOnsetSeconds(startRealTime); + } + } + + for (const auto &chord : m_graceNotes) { + const Fraction stopTime = startTime + graceNoteDur; + for (const auto ¬e : chord.pitches) { + // Set the start (onset) and end (offset) of the grace note + note->SetScoreTimeOnset(startTime); + note->SetScoreTimeOffset(stopTime); + double startRealTimeSeconds = startTime.ToDouble() * 60.0 / m_currentTempo; + double stopRealTimeSeconds = stopTime.ToDouble() * 60.0 / m_currentTempo; + note->SetRealTimeOnsetSeconds(startRealTimeSeconds); + note->SetRealTimeOffsetSeconds(stopRealTimeSeconds); + } + startTime = stopTime; + } +} + //---------------------------------------------------------------------------- // InitMidiFunctor //---------------------------------------------------------------------------- @@ -484,7 +550,6 @@ GenerateMIDIFunctor::GenerateMIDIFunctor(smf::MidiFile *midiFile) : ConstFunctor m_octaveShift = 0; m_currentTempo = MIDI_TEMPO; m_lastNote = NULL; - m_accentedGraceNote = false; m_noCue = false; m_controlEvents = false; } @@ -580,32 +645,6 @@ FunctorCode GenerateMIDIFunctor::VisitChord(const Chord *chord) { this->HandleOctave(chord); - // Handle grace chords - if (chord->IsGraceNote()) { - std::set pitches; - const ListOfConstObjects ¬es = chord->GetList(); - for (const Object *obj : notes) { - const Note *note = vrv_cast(obj); - assert(note); - pitches.insert(this->GetMIDIPitch(note)); - } - - double quarterDuration = 0.0; - const data_DURATION dur = chord->GetDur(); - if ((dur >= DURATION_long) && (dur <= DURATION_1024)) { - quarterDuration = pow(2.0, (DURATION_4 - dur)); - } - - m_graceNotes.push_back({ pitches, quarterDuration }); - - bool accented = (chord->GetGrace() == GRACE_acc); - const GraceGrp *graceGrp = vrv_cast(chord->GetFirstAncestor(GRACEGRP)); - if (graceGrp && (graceGrp->GetGrace() == GRACE_acc)) accented = true; - m_accentedGraceNote = accented; - - return FUNCTOR_SIBLINGS; - } - return FUNCTOR_CONTINUE; } @@ -620,36 +659,6 @@ FunctorCode GenerateMIDIFunctor::VisitFTrem(const FTrem *fTrem) return FUNCTOR_CONTINUE; } -FunctorCode GenerateMIDIFunctor::VisitGraceGrpEnd(const GraceGrp *graceGrp) -{ - // Handling of Nachschlag - if (!m_graceNotes.empty() && (graceGrp->GetAttach() == graceGrpLog_ATTACH_pre) && !m_accentedGraceNote - && m_lastNote) { - double startTime = m_totalTime + m_lastNote->GetScoreTimeOffset().ToDouble(); - const double graceNoteDur = UNACC_GRACENOTE_DUR * m_currentTempo / 60000.0; - const double totalDur = graceNoteDur * m_graceNotes.size(); - startTime -= totalDur; - startTime = std::max(startTime, 0.0); - - int velocity = MIDI_VELOCITY; - if (m_lastNote->HasVel()) velocity = m_lastNote->GetVel(); - const int tpq = m_midiFile->getTPQ(); - - for (const MIDIChord &chord : m_graceNotes) { - const double stopTime = startTime + graceNoteDur; - for (int pitch : chord.pitches) { - m_midiFile->addNoteOn(m_midiTrack, startTime * tpq, m_midiChannel, pitch, velocity); - m_midiFile->addNoteOff(m_midiTrack, stopTime * tpq, m_midiChannel, pitch); - } - startTime = stopTime; - } - - m_graceNotes.clear(); - } - - return FUNCTOR_CONTINUE; -} - FunctorCode GenerateMIDIFunctor::VisitHalfmRpt(const HalfmRpt *halfmRpt) { LogWarning("HalfmRpt produces empty MIDI output"); @@ -736,26 +745,6 @@ FunctorCode GenerateMIDIFunctor::VisitNote(const Note *note) return FUNCTOR_SIBLINGS; } - // Handle grace notes - if (note->IsGraceNote()) { - const int pitch = this->GetMIDIPitch(note); - - double quarterDuration = 0.0; - const data_DURATION dur = note->GetDur(); - if ((dur >= DURATION_long) && (dur <= DURATION_1024)) { - quarterDuration = pow(2.0, (DURATION_4 - dur)); - } - - m_graceNotes.push_back({ { pitch }, quarterDuration }); - - bool accented = (note->GetGrace() == GRACE_acc); - const GraceGrp *graceGrp = vrv_cast(note->GetFirstAncestor(GRACEGRP)); - if (graceGrp && (graceGrp->GetGrace() == GRACE_acc)) accented = true; - m_accentedGraceNote = accented; - - return FUNCTOR_SIBLINGS; - } - const int channel = m_midiChannel; int velocity = MIDI_VELOCITY; if (note->HasVel()) velocity = note->GetVel(); @@ -763,12 +752,6 @@ FunctorCode GenerateMIDIFunctor::VisitNote(const Note *note) double startTime = m_totalTime + note->GetScoreTimeOnset().ToDouble(); const int tpq = m_midiFile->getTPQ(); - // Check if some grace notes must be performed - if (!m_graceNotes.empty()) { - this->GenerateGraceNoteMIDI(note, startTime, tpq, channel, velocity); - m_graceNotes.clear(); - } - // Check if note is deferred if (m_deferredNotes.find(note) != m_deferredNotes.end()) { startTime += m_deferredNotes.at(note); @@ -1003,36 +986,6 @@ void GenerateMIDIFunctor::DeferMIDINote(const Note *refNote, double shift, bool } } -void GenerateMIDIFunctor::GenerateGraceNoteMIDI( - const Note *refNote, double startTime, int tpq, int channel, int velocity) -{ - double graceNoteDur = 0.0; - if (m_accentedGraceNote && !m_graceNotes.empty()) { - const double totalDur = refNote->GetScoreTimeDuration().ToDouble() / 2.0; - this->DeferMIDINote(refNote, totalDur, true); - graceNoteDur = totalDur / m_graceNotes.size(); - } - else { - graceNoteDur = UNACC_GRACENOTE_DUR * m_currentTempo / 60000.0; - const double totalDur = graceNoteDur * m_graceNotes.size(); - if (startTime >= totalDur) { - startTime -= totalDur; - } - else { - this->DeferMIDINote(refNote, totalDur, true); - } - } - - for (const MIDIChord &chord : m_graceNotes) { - const double stopTime = startTime + graceNoteDur; - for (int pitch : chord.pitches) { - m_midiFile->addNoteOn(m_midiTrack, startTime * tpq, channel, pitch, velocity); - m_midiFile->addNoteOff(m_midiTrack, stopTime * tpq, channel, pitch); - } - startTime = stopTime; - } -} - void GenerateMIDIFunctor::HandleOctave(const LayerElement *layerElement) { // Handle octave end @@ -1107,7 +1060,7 @@ FunctorCode GenerateTimemapFunctor::VisitMeasure(const Measure *measure) FunctorCode GenerateTimemapFunctor::VisitNote(const Note *note) { - if (note->HasGrace()) return FUNCTOR_SIBLINGS; + // if (note->HasGrace()) return FUNCTOR_SIBLINGS; // Skip cue notes when midiNoCue is activated if ((note->GetCue() == BOOLEAN_true) && m_noCue) { From 0e5e024eb42eb6a98f787a367420ba45ce8b0455 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Sun, 16 Feb 2025 11:34:57 +0100 Subject: [PATCH 04/13] Rename members and make structure private --- include/vrv/midifunctor.h | 21 ++++++++++---------- src/midifunctor.cpp | 41 ++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index 8aba15aec0..3c4037ee14 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -176,15 +176,6 @@ class InitTimemapTiesFunctor : public Functor { private: // }; - -/** - * Helper struct to store grace note/chord sequences - */ -struct GraceChord { - std::list pitches; - data_DURATION duration; -}; - //---------------------------------------------------------------------------- // InitTimemapNotesFunctor //---------------------------------------------------------------------------- @@ -227,10 +218,18 @@ class InitTimemapNotesFunctor : public Functor { protected: // private: + /** + * Helper struct to store grace note/chord sequences + */ + struct Grace { + std::list notes; + data_DURATION duration; + }; + /** * Creates the MIDI output of the grace note sequence */ - void GenerateGraceNoteMIDI(Note *refNote); + void AddGraceNotesFor(Note *refNote); public: // @@ -238,7 +237,7 @@ class InitTimemapNotesFunctor : public Functor { // Indicates whether cue notes should be included bool m_noCue; // Grace note/chord sequence - std::list m_graceNotes; + std::list m_graces; // Indicates whether the last grace note/chord was accented bool m_accentedGraceNote; // The current tempo diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index 1ff937a002..0f1db104a6 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -327,15 +327,15 @@ InitTimemapNotesFunctor::InitTimemapNotesFunctor() : Functor() FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) { if (chord->IsGraceNote()) { - std::list pitches; - const ListOfObjects ¬es = chord->GetList(); + std::list notes; + const ListOfObjects &chordNotes = chord->GetList(); for (Object *obj : notes) { Note *note = vrv_cast(obj); assert(note); - pitches.push_back(note); + notes.push_back(note); } - m_graceNotes.push_back({ pitches, chord->GetActualDur() }); + m_graces.push_back({ notes, chord->GetActualDur() }); bool accented = (chord->GetGrace() == GRACE_acc); const GraceGrp *graceGrp = vrv_cast(chord->GetFirstAncestor(GRACEGRP)); @@ -351,17 +351,16 @@ FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) FunctorCode InitTimemapNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) { // Handling of Nachschlag - if (!m_graceNotes.empty() && (graceGrp->GetAttach() == graceGrpLog_ATTACH_pre) && !m_accentedGraceNote - && m_lastNote) { + if (!m_graces.empty() && (graceGrp->GetAttach() == graceGrpLog_ATTACH_pre) && !m_accentedGraceNote && m_lastNote) { Fraction startTime = m_lastNote->GetScoreTimeOffset(); const Fraction graceNoteDur = UNACC_GRANENOTE_FRACTION * (int)m_currentTempo / 120; - const Fraction totalDur = graceNoteDur * (int)m_graceNotes.size(); + const Fraction totalDur = graceNoteDur * (int)m_graces.size(); startTime = startTime - totalDur; startTime = (startTime < 0) ? 0 : startTime; - for (const auto &chord : m_graceNotes) { + for (const auto &grace : m_graces) { const Fraction stopTime = startTime + graceNoteDur; - for (const auto ¬e : chord.pitches) { + for (const auto ¬e : grace.notes) { // Set the start (onset) and end (offset) of the grace note note->SetScoreTimeOnset(startTime); note->SetScoreTimeOffset(stopTime); @@ -373,7 +372,7 @@ FunctorCode InitTimemapNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) startTime = stopTime; } - m_graceNotes.clear(); + m_graces.clear(); } return FUNCTOR_CONTINUE; @@ -406,7 +405,7 @@ FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) // Handle grace notes if (note->IsGraceNote()) { - m_graceNotes.push_back({ { note }, note->GetDur() }); + m_graces.push_back({ { note }, note->GetDur() }); bool accented = (note->GetGrace() == GRACE_acc); const GraceGrp *graceGrp = vrv_cast(note->GetFirstAncestor(GRACEGRP)); @@ -417,9 +416,9 @@ FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) } // Check if some grace notes must be performed - if (!m_graceNotes.empty()) { - this->GenerateGraceNoteMIDI(note); - m_graceNotes.clear(); + if (!m_graces.empty()) { + this->AddGraceNotesFor(note); + m_graces.clear(); } // Store reference, i.e. for Nachschlag @@ -428,22 +427,22 @@ FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) return FUNCTOR_SIBLINGS; } -void InitTimemapNotesFunctor::GenerateGraceNoteMIDI(Note *refNote) +void InitTimemapNotesFunctor::AddGraceNotesFor(Note *refNote) { Fraction startTime = refNote->GetScoreTimeOnset(); Fraction graceNoteDur = 0; - if (m_accentedGraceNote && !m_graceNotes.empty()) { + if (m_accentedGraceNote && !m_graces.empty()) { const Fraction totalDur = refNote->GetScoreTimeDuration() / 2; // Adjust the start of the main note refNote->SetScoreTimeOnset(startTime + totalDur); double startRealTime = (startTime + totalDur).ToDouble() * 60.0 / m_currentTempo; refNote->SetRealTimeOnsetSeconds(startRealTime); - graceNoteDur = totalDur / (int)m_graceNotes.size(); + graceNoteDur = totalDur / (int)m_graces.size(); } else { graceNoteDur = UNACC_GRANENOTE_FRACTION * (int)m_currentTempo / 120; - const Fraction totalDur = graceNoteDur * (int)m_graceNotes.size(); + const Fraction totalDur = graceNoteDur * (int)m_graces.size(); if (startTime >= totalDur) { startTime = startTime - totalDur; } @@ -455,9 +454,9 @@ void InitTimemapNotesFunctor::GenerateGraceNoteMIDI(Note *refNote) } } - for (const auto &chord : m_graceNotes) { + for (const auto &grace : m_graces) { const Fraction stopTime = startTime + graceNoteDur; - for (const auto ¬e : chord.pitches) { + for (const auto ¬e : grace.notes) { // Set the start (onset) and end (offset) of the grace note note->SetScoreTimeOnset(startTime); note->SetScoreTimeOffset(stopTime); @@ -1060,8 +1059,6 @@ FunctorCode GenerateTimemapFunctor::VisitMeasure(const Measure *measure) FunctorCode GenerateTimemapFunctor::VisitNote(const Note *note) { - // if (note->HasGrace()) return FUNCTOR_SIBLINGS; - // Skip cue notes when midiNoCue is activated if ((note->GetCue() == BOOLEAN_true) && m_noCue) { return FUNCTOR_SIBLINGS; From e53c379ce9df0aaaee75343a45aa0eb5bfa486d7 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Sun, 16 Feb 2025 20:44:17 +0100 Subject: [PATCH 05/13] Add support for `@grace.time` for `acc` grace notes --- include/vrv/midifunctor.h | 1 + src/midifunctor.cpp | 34 +++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index 3c4037ee14..7de97bb962 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -224,6 +224,7 @@ class InitTimemapNotesFunctor : public Functor { struct Grace { std::list notes; data_DURATION duration; + data_PERCENT time; }; /** diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index 0f1db104a6..6e5a5f9e0f 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -9,6 +9,10 @@ //---------------------------------------------------------------------------- +#include + +//---------------------------------------------------------------------------- + #include "arpeg.h" #include "beatrpt.h" #include "btrem.h" @@ -335,12 +339,14 @@ FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) notes.push_back(note); } - m_graces.push_back({ notes, chord->GetActualDur() }); - - bool accented = (chord->GetGrace() == GRACE_acc); + m_accentedGraceNote = (chord->GetGrace() == GRACE_acc); + double time = chord->HasGraceTime() ? chord->GetGraceTime() : 50.0; const GraceGrp *graceGrp = vrv_cast(chord->GetFirstAncestor(GRACEGRP)); - if (graceGrp && (graceGrp->GetGrace() == GRACE_acc)) accented = true; - m_accentedGraceNote = accented; + if (graceGrp) { + if (graceGrp->GetGrace() == GRACE_acc) m_accentedGraceNote = true; + time = (graceGrp->HasGraceTime()) ? graceGrp->GetGraceTime() : 50.0; + } + m_graces.push_back({ notes, chord->GetActualDur(), time }); return FUNCTOR_SIBLINGS; } @@ -405,12 +411,14 @@ FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) // Handle grace notes if (note->IsGraceNote()) { - m_graces.push_back({ { note }, note->GetDur() }); - - bool accented = (note->GetGrace() == GRACE_acc); + m_accentedGraceNote = (note->GetGrace() == GRACE_acc); + double time = note->HasGraceTime() ? note->GetGraceTime() : 50.0; const GraceGrp *graceGrp = vrv_cast(note->GetFirstAncestor(GRACEGRP)); - if (graceGrp && (graceGrp->GetGrace() == GRACE_acc)) accented = true; - m_accentedGraceNote = accented; + if (graceGrp) { + if (graceGrp->GetGrace() == GRACE_acc) m_accentedGraceNote = true; + time = (graceGrp->HasGraceTime()) ? graceGrp->GetGraceTime() : 50.0; + } + m_graces.push_back({ { note }, note->GetDur(), time }); return FUNCTOR_SIBLINGS; } @@ -433,7 +441,11 @@ void InitTimemapNotesFunctor::AddGraceNotesFor(Note *refNote) Fraction graceNoteDur = 0; if (m_accentedGraceNote && !m_graces.empty()) { - const Fraction totalDur = refNote->GetScoreTimeDuration() / 2; + // Arbitraritly looks at the first note, not sure what to do if we have condradictory values + double percent = m_graces.front().time; + // Arbitraritly constraint the time between 5% and 95% + percent = std::min(95.0, std::max(5.0, percent)); + const Fraction totalDur = refNote->GetScoreTimeDuration() * (int)percent / 100; // Adjust the start of the main note refNote->SetScoreTimeOnset(startTime + totalDur); double startRealTime = (startTime + totalDur).ToDouble() * 60.0 / m_currentTempo; From 77f65318b94966a56085c2a01fe6ee20fac928c5 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Mon, 17 Feb 2025 09:12:32 +0100 Subject: [PATCH 06/13] Rename functor --- include/vrv/midifunctor.h | 6 +++--- src/doc.cpp | 6 +++--- src/midifunctor.cpp | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index 7de97bb962..02bd56f071 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -183,14 +183,14 @@ class InitTimemapTiesFunctor : public Functor { /** * This class adjusts note duration for grace notes and arpeggios. */ -class InitTimemapNotesFunctor : public Functor { +class InitTimemapAdjustNotesFunctor : public Functor { public: /** * @name Constructors, destructors */ ///@{ - InitTimemapNotesFunctor(); - virtual ~InitTimemapNotesFunctor() = default; + InitTimemapAdjustNotesFunctor(); + virtual ~InitTimemapAdjustNotesFunctor() = default; ///@} /* diff --git a/src/doc.cpp b/src/doc.cpp index 2ee8597de1..13fa0044f9 100644 --- a/src/doc.cpp +++ b/src/doc.cpp @@ -405,9 +405,9 @@ void Doc::CalculateTimemap() this->Process(initTimemapTies); // Adjust the duration of notes (grace notes and arpeggios) - InitTimemapNotesFunctor initTimemapNotes; - initTimemapNotes.SetNoCue(this->GetOptions()->m_midiNoCue.GetValue()); - this->Process(initTimemapNotes); + InitTimemapAdjustNotesFunctor initTimemapAdjustNotes; + initTimemapAdjustNotes.SetNoCue(this->GetOptions()->m_midiNoCue.GetValue()); + this->Process(initTimemapAdjustNotes); m_timemapTempo = m_options->m_midiTempoAdjustment.GetValue(); } diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index 6e5a5f9e0f..72cbd7be24 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -321,14 +321,14 @@ FunctorCode InitTimemapTiesFunctor::VisitTie(Tie *tie) // InitTimemapNotesFunctor //---------------------------------------------------------------------------- -InitTimemapNotesFunctor::InitTimemapNotesFunctor() : Functor() +InitTimemapAdjustNotesFunctor::InitTimemapAdjustNotesFunctor() : Functor() { m_noCue = false; m_lastNote = NULL; m_currentTempo = MIDI_TEMPO; } -FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) +FunctorCode InitTimemapAdjustNotesFunctor::VisitChord(Chord *chord) { if (chord->IsGraceNote()) { std::list notes; @@ -354,7 +354,7 @@ FunctorCode InitTimemapNotesFunctor::VisitChord(Chord *chord) return FUNCTOR_CONTINUE; } -FunctorCode InitTimemapNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) +FunctorCode InitTimemapAdjustNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) { // Handling of Nachschlag if (!m_graces.empty() && (graceGrp->GetAttach() == graceGrpLog_ATTACH_pre) && !m_accentedGraceNote && m_lastNote) { @@ -384,7 +384,7 @@ FunctorCode InitTimemapNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) return FUNCTOR_CONTINUE; } -FunctorCode InitTimemapNotesFunctor::VisitMeasure(Measure *measure) +FunctorCode InitTimemapAdjustNotesFunctor::VisitMeasure(Measure *measure) { // Update the current tempo m_currentTempo = measure->GetCurrentTempo(); @@ -392,7 +392,7 @@ FunctorCode InitTimemapNotesFunctor::VisitMeasure(Measure *measure) return FUNCTOR_CONTINUE; } -FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) +FunctorCode InitTimemapAdjustNotesFunctor::VisitNote(Note *note) { // Skip linked notes if (note->HasSameasLink()) { @@ -435,7 +435,7 @@ FunctorCode InitTimemapNotesFunctor::VisitNote(Note *note) return FUNCTOR_SIBLINGS; } -void InitTimemapNotesFunctor::AddGraceNotesFor(Note *refNote) +void InitTimemapAdjustNotesFunctor::AddGraceNotesFor(Note *refNote) { Fraction startTime = refNote->GetScoreTimeOnset(); From 75845cf9cc9a59814ad99d7dd121a0a7b163ede2 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Mon, 17 Feb 2025 09:14:36 +0100 Subject: [PATCH 07/13] Correct unacc grace note duration and fix typo --- include/vrv/vrvdef.h | 2 +- src/midifunctor.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/vrv/vrvdef.h b/include/vrv/vrvdef.h index af70654806..5c86e0a601 100644 --- a/include/vrv/vrvdef.h +++ b/include/vrv/vrvdef.h @@ -73,7 +73,7 @@ namespace vrv { #define MIDI_TEMPO 120 #define UNACC_GRACENOTE_DUR 27 // in milliseconds -#define UNACC_GRANENOTE_FRACTION Fraction(1, 64) +#define UNACC_GRACENOTE_FRACTION Fraction(1, 2024) //---------------------------------------------------------------------------- // Object defines diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index 72cbd7be24..6334174e00 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -359,7 +359,7 @@ FunctorCode InitTimemapAdjustNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) // Handling of Nachschlag if (!m_graces.empty() && (graceGrp->GetAttach() == graceGrpLog_ATTACH_pre) && !m_accentedGraceNote && m_lastNote) { Fraction startTime = m_lastNote->GetScoreTimeOffset(); - const Fraction graceNoteDur = UNACC_GRANENOTE_FRACTION * (int)m_currentTempo / 120; + const Fraction graceNoteDur = UNACC_GRACENOTE_FRACTION * (int)m_currentTempo; const Fraction totalDur = graceNoteDur * (int)m_graces.size(); startTime = startTime - totalDur; startTime = (startTime < 0) ? 0 : startTime; @@ -453,7 +453,7 @@ void InitTimemapAdjustNotesFunctor::AddGraceNotesFor(Note *refNote) graceNoteDur = totalDur / (int)m_graces.size(); } else { - graceNoteDur = UNACC_GRANENOTE_FRACTION * (int)m_currentTempo / 120; + graceNoteDur = UNACC_GRACENOTE_FRACTION * (int)m_currentTempo; const Fraction totalDur = graceNoteDur * (int)m_graces.size(); if (startTime >= totalDur) { startTime = startTime - totalDur; From 8d62c0e6b4c3b9ad13bc22773aa3935db2a29eae Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Mon, 17 Feb 2025 09:16:42 +0100 Subject: [PATCH 08/13] Improve the method name and correct comments --- include/vrv/midifunctor.h | 4 ++-- src/midifunctor.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index 02bd56f071..21f96d925d 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -228,9 +228,9 @@ class InitTimemapAdjustNotesFunctor : public Functor { }; /** - * Creates the MIDI output of the grace note sequence + * Set the grace note onset and offset times for the reference note */ - void AddGraceNotesFor(Note *refNote); + void SetGraceNotesFor(Note *refNote); public: // diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index 6334174e00..46885815b1 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -425,7 +425,7 @@ FunctorCode InitTimemapAdjustNotesFunctor::VisitNote(Note *note) // Check if some grace notes must be performed if (!m_graces.empty()) { - this->AddGraceNotesFor(note); + this->SetGraceNotesFor(note); m_graces.clear(); } @@ -435,15 +435,15 @@ FunctorCode InitTimemapAdjustNotesFunctor::VisitNote(Note *note) return FUNCTOR_SIBLINGS; } -void InitTimemapAdjustNotesFunctor::AddGraceNotesFor(Note *refNote) +void InitTimemapAdjustNotesFunctor::SetGraceNotesFor(Note *refNote) { Fraction startTime = refNote->GetScoreTimeOnset(); Fraction graceNoteDur = 0; if (m_accentedGraceNote && !m_graces.empty()) { - // Arbitraritly looks at the first note, not sure what to do if we have condradictory values + // Arbitrarily looks at the first note, not sure what to do if we have contradictory values double percent = m_graces.front().time; - // Arbitraritly constraint the time between 5% and 95% + // Arbitrarily constraint the time between 5% and 95% percent = std::min(95.0, std::max(5.0, percent)); const Fraction totalDur = refNote->GetScoreTimeDuration() * (int)percent / 100; // Adjust the start of the main note From 4110892d0948eaebce2aa28aa0c003e7f58fb874 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Mon, 17 Feb 2025 11:00:30 +0100 Subject: [PATCH 09/13] Move arpeggios deferring to InitTimemapAdjustNotesFunctor --- include/vrv/midifunctor.h | 11 +++-- src/doc.cpp | 1 - src/midifunctor.cpp | 88 ++++++++++++++++++++++----------------- 3 files changed, 56 insertions(+), 44 deletions(-) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index 21f96d925d..d86f07841a 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -209,6 +209,7 @@ class InitTimemapAdjustNotesFunctor : public Functor { * Functor interface */ ///@{ + FunctorCode VisitArpeg(Arpeg *arpeg) override; FunctorCode VisitChord(Chord *chord) override; FunctorCode VisitGraceGrpEnd(GraceGrp *graceGrp) override; FunctorCode VisitMeasure(Measure *measure) override; @@ -232,6 +233,12 @@ class InitTimemapAdjustNotesFunctor : public Functor { */ void SetGraceNotesFor(Note *refNote); + /** + * Set the start (and stop) time for a note (score and real times) + */ + void SetNoteStartStop(Note *note, const Fraction &startTime, const Fraction &stopTime); + void SetNoteStart(Note *note, const Fraction &startTime); + public: // private: @@ -287,7 +294,6 @@ class InitMIDIFunctor : public ConstFunctor { */ ///@{ void SetCurrentTempo(double tempo) { m_currentTempo = tempo; } - const std::map &GetDeferredNotes() const { return m_deferredNotes; } const std::list &GetOctaves() const { return m_octaves; } ///@} @@ -295,7 +301,6 @@ class InitMIDIFunctor : public ConstFunctor { * Functor interface */ ///@{ - FunctorCode VisitArpeg(const Arpeg *arpeg) override; FunctorCode VisitMeasure(const Measure *measure) override; FunctorCode VisitOctave(const Octave *octave) override; ///@} @@ -309,8 +314,6 @@ class InitMIDIFunctor : public ConstFunctor { private: // The current tempo double m_currentTempo; - // Deferred notes which start slightly later - std::map m_deferredNotes; // Octave info which is collected std::list m_octaves; }; diff --git a/src/doc.cpp b/src/doc.cpp index 13fa0044f9..d00c10e3c9 100644 --- a/src/doc.cpp +++ b/src/doc.cpp @@ -551,7 +551,6 @@ void Doc::ExportMIDI(smf::MidiFile *midiFile) generateMIDI.SetTempoEventTicks(tempoEventTicks); generateMIDI.SetTransSemi(transSemi); generateMIDI.SetCurrentTempo(tempo); - generateMIDI.SetDeferredNotes(initMIDI.GetDeferredNotes()); generateMIDI.SetOctaves(initMIDI.GetOctaves()); generateMIDI.SetNoCue(this->GetOptions()->m_midiNoCue.GetValue()); generateMIDI.SetControlEvents(controlEvents); diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index 46885815b1..c8f7fd11e1 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -328,6 +328,33 @@ InitTimemapAdjustNotesFunctor::InitTimemapAdjustNotesFunctor() : Functor() m_currentTempo = MIDI_TEMPO; } +FunctorCode InitTimemapAdjustNotesFunctor::VisitArpeg(Arpeg *arpeg) +{ + // Sort the involved notes by playing order + const bool playTopDown = (arpeg->GetOrder() == arpegLog_ORDER_down); + std::set notes = arpeg->GetNotes(); + if (notes.empty()) return FUNCTOR_CONTINUE; + + std::vector sortedNotes; + std::copy(notes.begin(), notes.end(), std::back_inserter(sortedNotes)); + std::sort(sortedNotes.begin(), sortedNotes.end(), [playTopDown](const Note *note1, const Note *note2) { + const int pitch1 = note1->GetMIDIPitch(); + const int pitch2 = note2->GetMIDIPitch(); + return playTopDown ? (pitch1 > pitch2) : (pitch1 < pitch2); + }); + + // Defer the notes in playing order + Fraction shift = 0; + Fraction startTime = sortedNotes.front()->GetScoreTimeOffset(); + const Fraction increment = UNACC_GRACENOTE_FRACTION * (int)m_currentTempo; + for (Note *note : sortedNotes) { + if (shift != 0) this->SetNoteStart(note, startTime + shift); + shift = shift + increment; + } + + return FUNCTOR_CONTINUE; +} + FunctorCode InitTimemapAdjustNotesFunctor::VisitChord(Chord *chord) { if (chord->IsGraceNote()) { @@ -368,12 +395,7 @@ FunctorCode InitTimemapAdjustNotesFunctor::VisitGraceGrpEnd(GraceGrp *graceGrp) const Fraction stopTime = startTime + graceNoteDur; for (const auto ¬e : grace.notes) { // Set the start (onset) and end (offset) of the grace note - note->SetScoreTimeOnset(startTime); - note->SetScoreTimeOffset(stopTime); - double startRealTimeSeconds = startTime.ToDouble() * 60.0 / m_currentTempo; - double stopRealTimeSeconds = stopTime.ToDouble() * 60.0 / m_currentTempo; - note->SetRealTimeOnsetSeconds(startRealTimeSeconds); - note->SetRealTimeOffsetSeconds(stopRealTimeSeconds); + this->SetNoteStartStop(note, startTime, stopTime); } startTime = stopTime; } @@ -447,9 +469,7 @@ void InitTimemapAdjustNotesFunctor::SetGraceNotesFor(Note *refNote) percent = std::min(95.0, std::max(5.0, percent)); const Fraction totalDur = refNote->GetScoreTimeDuration() * (int)percent / 100; // Adjust the start of the main note - refNote->SetScoreTimeOnset(startTime + totalDur); - double startRealTime = (startTime + totalDur).ToDouble() * 60.0 / m_currentTempo; - refNote->SetRealTimeOnsetSeconds(startRealTime); + this->SetNoteStart(refNote, startTime + totalDur); graceNoteDur = totalDur / (int)m_graces.size(); } else { @@ -470,17 +490,31 @@ void InitTimemapAdjustNotesFunctor::SetGraceNotesFor(Note *refNote) const Fraction stopTime = startTime + graceNoteDur; for (const auto ¬e : grace.notes) { // Set the start (onset) and end (offset) of the grace note - note->SetScoreTimeOnset(startTime); - note->SetScoreTimeOffset(stopTime); - double startRealTimeSeconds = startTime.ToDouble() * 60.0 / m_currentTempo; - double stopRealTimeSeconds = stopTime.ToDouble() * 60.0 / m_currentTempo; - note->SetRealTimeOnsetSeconds(startRealTimeSeconds); - note->SetRealTimeOffsetSeconds(stopRealTimeSeconds); + this->SetNoteStartStop(note, startTime, stopTime); } startTime = stopTime; } } +void InitTimemapAdjustNotesFunctor::SetNoteStartStop(Note *note, const Fraction &startTime, const Fraction &stopTime) +{ + assert(note); + + this->SetNoteStart(note, startTime); + note->SetScoreTimeOffset(stopTime); + double stopRealTimeSeconds = stopTime.ToDouble() * 60.0 / m_currentTempo; + note->SetRealTimeOffsetSeconds(stopRealTimeSeconds); +} + +void InitTimemapAdjustNotesFunctor::SetNoteStart(Note *note, const Fraction &startTime) +{ + assert(note); + + note->SetScoreTimeOnset(startTime); + double startRealTimeSeconds = startTime.ToDouble() * 60.0 / m_currentTempo; + note->SetRealTimeOnsetSeconds(startRealTimeSeconds); +} + //---------------------------------------------------------------------------- // InitMidiFunctor //---------------------------------------------------------------------------- @@ -490,30 +524,6 @@ InitMIDIFunctor::InitMIDIFunctor() : ConstFunctor() m_currentTempo = MIDI_TEMPO; } -FunctorCode InitMIDIFunctor::VisitArpeg(const Arpeg *arpeg) -{ - // Sort the involved notes by playing order - const bool playTopDown = (arpeg->GetOrder() == arpegLog_ORDER_down); - std::set notes = arpeg->GetNotes(); - std::vector sortedNotes; - std::copy(notes.begin(), notes.end(), std::back_inserter(sortedNotes)); - std::sort(sortedNotes.begin(), sortedNotes.end(), [playTopDown](const Note *note1, const Note *note2) { - const int pitch1 = note1->GetMIDIPitch(); - const int pitch2 = note2->GetMIDIPitch(); - return playTopDown ? (pitch1 > pitch2) : (pitch1 < pitch2); - }); - - // Defer the notes in playing order - double shift = 0.0; - const double increment = UNACC_GRACENOTE_DUR * m_currentTempo / 60000.0; - for (const Note *note : sortedNotes) { - if (shift > 0.0) m_deferredNotes[note] = shift; - shift += increment; - } - - return FUNCTOR_CONTINUE; -} - FunctorCode InitMIDIFunctor::VisitMeasure(const Measure *measure) { m_currentTempo = measure->GetCurrentTempo(); From 4f35b5045e3a9aa850fb5574038105ce780730cd Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Tue, 18 Feb 2025 09:11:36 +0100 Subject: [PATCH 10/13] Update include/vrv/midifunctor.h --- include/vrv/midifunctor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/vrv/midifunctor.h b/include/vrv/midifunctor.h index d86f07841a..84dc512cf4 100644 --- a/include/vrv/midifunctor.h +++ b/include/vrv/midifunctor.h @@ -177,7 +177,7 @@ class InitTimemapTiesFunctor : public Functor { // }; //---------------------------------------------------------------------------- -// InitTimemapNotesFunctor +// InitTimemapAdjustNotesFunctor //---------------------------------------------------------------------------- /** From 1e2c8160dae870678461732ab92047f412d2a0a5 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Tue, 18 Feb 2025 09:11:54 +0100 Subject: [PATCH 11/13] Update src/midifunctor.cpp --- src/midifunctor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index c8f7fd11e1..eae5d8c6a0 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -318,7 +318,7 @@ FunctorCode InitTimemapTiesFunctor::VisitTie(Tie *tie) } //---------------------------------------------------------------------------- -// InitTimemapNotesFunctor +// InitTimemapAdjustNotesFunctor //---------------------------------------------------------------------------- InitTimemapAdjustNotesFunctor::InitTimemapAdjustNotesFunctor() : Functor() From dd6811f77c2bb020e732a6d3569fc933d21ff5b7 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Tue, 18 Feb 2025 09:13:44 +0100 Subject: [PATCH 12/13] Fix arpeggios using offset instead of onset --- src/midifunctor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/midifunctor.cpp b/src/midifunctor.cpp index eae5d8c6a0..0eb70daa2b 100644 --- a/src/midifunctor.cpp +++ b/src/midifunctor.cpp @@ -345,7 +345,7 @@ FunctorCode InitTimemapAdjustNotesFunctor::VisitArpeg(Arpeg *arpeg) // Defer the notes in playing order Fraction shift = 0; - Fraction startTime = sortedNotes.front()->GetScoreTimeOffset(); + Fraction startTime = sortedNotes.front()->GetScoreTimeOnset(); const Fraction increment = UNACC_GRACENOTE_FRACTION * (int)m_currentTempo; for (Note *note : sortedNotes) { if (shift != 0) this->SetNoteStart(note, startTime + shift); From cf1ef686a64775625cd51f00e68cf216821fc7a4 Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Tue, 18 Feb 2025 13:40:14 +0100 Subject: [PATCH 13/13] Fix unacc duration --- include/vrv/vrvdef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/vrv/vrvdef.h b/include/vrv/vrvdef.h index 5c86e0a601..2338db375c 100644 --- a/include/vrv/vrvdef.h +++ b/include/vrv/vrvdef.h @@ -73,7 +73,7 @@ namespace vrv { #define MIDI_TEMPO 120 #define UNACC_GRACENOTE_DUR 27 // in milliseconds -#define UNACC_GRACENOTE_FRACTION Fraction(1, 2024) +#define UNACC_GRACENOTE_FRACTION Fraction(1, 2048) //---------------------------------------------------------------------------- // Object defines