LCOV - code coverage report
Current view: top level - src/engraving/playback/renderers - arpeggiorenderer.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 88.9 % 45 40
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2025 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "arpeggiorenderer.h"
      24              : 
      25              : #include "dom/chord.h"
      26              : #include "dom/arpeggio.h"
      27              : 
      28              : #include "noterenderer.h"
      29              : 
      30              : using namespace mu::engraving;
      31              : using namespace muse;
      32              : using namespace muse::mpe;
      33              : 
      34           10 : static bool isDirectionUp(const mpe::ArticulationType type)
      35              : {
      36           10 :     switch (type) {
      37            2 :     case mpe::ArticulationType::ArpeggioDown:
      38              :     case mpe::ArticulationType::ArpeggioStraightDown:
      39            2 :         return false;
      40            8 :     case mpe::ArticulationType::Arpeggio:
      41              :     case mpe::ArticulationType::ArpeggioUp:
      42              :     case mpe::ArticulationType::ArpeggioStraightUp:
      43            8 :         return true;
      44            0 :     default:
      45            0 :         return false;
      46              :     }
      47              : }
      48              : 
      49           10 : static usecs_t timestampOffsetStep(const RenderingContext& ctx, int stepCount)
      50              : {
      51           10 :     constexpr int MAX_TIMESTAMP_OFFSET_STEP = 60000;
      52           10 :     const int offsetStep = ctx.nominalDuration / stepCount;
      53              : 
      54           10 :     return std::min(offsetStep, MAX_TIMESTAMP_OFFSET_STEP);
      55              : }
      56              : 
      57           10 : static std::map<int /*pitch*/, const Note*> arpeggioNotes(const Chord* chord)
      58              : {
      59           10 :     std::map<int, const Note*> result;
      60           35 :     for (const Note* note : chord->notes()) {
      61           25 :         result.emplace(note->pitch(), note);
      62              :     }
      63              : 
      64           10 :     return result;
      65            0 : }
      66              : 
      67           32 : const ArticulationTypeSet& ArpeggioRenderer::supportedTypes()
      68              : {
      69              :     static const mpe::ArticulationTypeSet types = {
      70              :         mpe::ArticulationType::Arpeggio, mpe::ArticulationType::ArpeggioDown,
      71              :         mpe::ArticulationType::ArpeggioStraightDown, mpe::ArticulationType::ArpeggioStraightUp,
      72              :         mpe::ArticulationType::ArpeggioUp
      73           34 :     };
      74              : 
      75           32 :     return types;
      76              : }
      77              : 
      78           10 : void ArpeggioRenderer::doRender(const EngravingItem* item, const mpe::ArticulationType preferredType,
      79              :                                 const RenderingContext& ctx, mpe::PlaybackEventList& result)
      80              : {
      81           10 :     const Chord* chord = toChord(item);
      82           10 :     IF_ASSERT_FAILED(chord) {
      83            0 :         return;
      84              :     }
      85              : 
      86           10 :     const Arpeggio* arpeggio = chord->arpeggio();
      87           10 :     IF_ASSERT_FAILED(arpeggio) {
      88            0 :         return;
      89              :     }
      90              : 
      91           10 :     const int stepsCount = static_cast<int>(chord->notes().size());
      92           10 :     const usecs_t offsetStep = timestampOffsetStep(ctx, stepsCount);
      93           10 :     const double stretch = arpeggio->Stretch();
      94              : 
      95           25 :     auto renderEvent = [&](const Note* note, const int stepNumber) {
      96           25 :         const timestamp_t offset = offsetStep * stepNumber * stretch;
      97              : 
      98           25 :         RenderingContext noteCtx(ctx);
      99           25 :         noteCtx.nominalTimestamp += offset;
     100           25 :         noteCtx.nominalDuration -= offset;
     101              : 
     102           25 :         NoteRenderer::render(note, noteCtx, result);
     103           25 :     };
     104              : 
     105           10 :     const std::map<int, const Note*> notes = arpeggioNotes(chord);
     106              : 
     107           10 :     if (isDirectionUp(preferredType)) {
     108           27 :         for (auto it = notes.begin(); it != notes.end(); ++it) {
     109           38 :             renderEvent(it->second, std::distance(notes.begin(), it));
     110              :         }
     111              :     } else {
     112            8 :         for (auto it = notes.rbegin(); it != notes.rend(); ++it) {
     113           12 :             renderEvent(it->second, std::distance(notes.rbegin(), it));
     114              :         }
     115              :     }
     116           10 : }
        

Generated by: LCOV version 2.0-1