LCOV - code coverage report
Current view: top level - src/engraving/playback/metaparsers/internal - spannersmetaparser.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 78.1 % 73 57
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) 2021 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 "spannersmetaparser.h"
      24              : 
      25              : #include "dom/glissando.h"
      26              : #include "dom/note.h"
      27              : #include "dom/spanner.h"
      28              : #include "dom/trill.h"
      29              : #include "dom/pedal.h"
      30              : 
      31              : #include "playback/utils/pitchutils.h"
      32              : #include "playback/filters/spannerfilter.h"
      33              : 
      34              : using namespace mu::engraving;
      35              : using namespace muse;
      36              : 
      37           67 : bool SpannersMetaParser::isAbleToParse(const EngravingItem* spannerItem)
      38              : {
      39              :     static const std::unordered_set<ElementType> SUPPORTED_TYPES {
      40              :         ElementType::SLUR,
      41              :         ElementType::HAMMER_ON_PULL_OFF,
      42              :         ElementType::PEDAL,
      43              :         ElementType::LET_RING,
      44              :         ElementType::PALM_MUTE,
      45              :         ElementType::TRILL,
      46              :         ElementType::GLISSANDO,
      47              :         ElementType::GUITAR_BEND,
      48              :         ElementType::VIBRATO,
      49           69 :     };
      50              : 
      51           67 :     return muse::contains(SUPPORTED_TYPES, spannerItem->type());
      52              : }
      53              : 
      54           46 : void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingContext& spannerCtx, mpe::ArticulationMap& result)
      55              : {
      56           46 :     IF_ASSERT_FAILED(item->isSpanner()) {
      57            1 :         return;
      58              :     }
      59              : 
      60           46 :     const Spanner* spanner = toSpanner(item);
      61           46 :     if (!spanner->playSpanner()) {
      62            1 :         return;
      63              :     }
      64              : 
      65           45 :     const int overallDurationTicks = SpannerFilter::spannerActualDurationTicks(spanner, spannerCtx.nominalDurationTicks);
      66              : 
      67           45 :     mpe::ArticulationType type = mpe::ArticulationType::Undefined;
      68           45 :     mpe::pitch_level_t overallPitchRange = 0;
      69           45 :     mpe::dynamic_level_t overallDynamicRange = 0;
      70              : 
      71           45 :     switch (spanner->type()) {
      72            2 :     case ElementType::SLUR:
      73              :     case ElementType::HAMMER_ON_PULL_OFF:
      74            2 :         type = mpe::ArticulationType::Legato;
      75            2 :         break;
      76           12 :     case ElementType::PEDAL:
      77              :     case ElementType::LET_RING:
      78           12 :         type = mpe::ArticulationType::Pedal;
      79           12 :         break;
      80            0 :     case ElementType::PALM_MUTE:
      81            0 :         type = mpe::ArticulationType::PalmMute;
      82            0 :         break;
      83           18 :     case ElementType::GUITAR_BEND:
      84           18 :         type = mpe::ArticulationType::Multibend;
      85           18 :         break;
      86            0 :     case ElementType::VIBRATO:
      87            0 :         type = mpe::ArticulationType::Vibrato;
      88            0 :         break;
      89            5 :     case ElementType::TRILL: {
      90            5 :         const Trill* trill = toTrill(spanner);
      91              : 
      92            5 :         if (trill->trillType() == TrillType::TRILL_LINE) {
      93            5 :             type = mpe::ArticulationType::Trill;
      94            0 :         } else if (trill->trillType() == TrillType::UPPRALL_LINE) {
      95            0 :             type = mpe::ArticulationType::UpPrall;
      96            0 :         } else if (trill->trillType() == TrillType::DOWNPRALL_LINE) {
      97            0 :             type = mpe::ArticulationType::PrallDown;
      98            0 :         } else if (trill->trillType() == TrillType::PRALLPRALL_LINE) {
      99            0 :             type = mpe::ArticulationType::LinePrall;
     100              :         }
     101            5 :         break;
     102              :     }
     103            8 :     case ElementType::GLISSANDO: {
     104            8 :         const Glissando* glissando = toGlissando(spanner);
     105            8 :         const Note* startNote = toNote(glissando->startElement());
     106            8 :         const Note* endNote = toNote(glissando->endElement());
     107              : 
     108            8 :         if (!startNote || !endNote) {
     109              :             break;
     110              :         }
     111              : 
     112            8 :         if (glissando->glissandoStyle() == GlissandoStyle::PORTAMENTO) {
     113            3 :             type = mpe::ArticulationType::ContinuousGlissando;
     114              :         } else {
     115            5 :             type = mpe::ArticulationType::DiscreteGlissando;
     116              :         }
     117              : 
     118            8 :         int startNoteTpc = startNote->playingTpc();
     119            8 :         int endNoteTpc = endNote->playingTpc();
     120              : 
     121            8 :         mpe::PitchClass startNotePitchClass = pitchClassFromTpc(startNoteTpc);
     122            8 :         mpe::PitchClass endNotePitchClass = pitchClassFromTpc(endNoteTpc);
     123              : 
     124            8 :         mpe::octave_t startNoteOctave = actualOctave(startNote->playingOctave(), startNotePitchClass, tpc2alter(startNoteTpc));
     125            8 :         mpe::octave_t endNoteOctave = actualOctave(endNote->playingOctave(), endNotePitchClass, tpc2alter(endNoteTpc));
     126              : 
     127            8 :         overallPitchRange = mpe::pitchLevelDiff(endNotePitchClass, endNoteOctave, startNotePitchClass, startNoteOctave);
     128              : 
     129            8 :         break;
     130              :     }
     131            0 :     default:
     132            0 :         break;
     133              :     }
     134              : 
     135           45 :     if (type == mpe::ArticulationType::Undefined) {
     136            0 :         return;
     137              :     }
     138              : 
     139           45 :     const mpe::ArticulationPattern& pattern = spannerCtx.profile->pattern(type);
     140           45 :     if (pattern.empty()) {
     141            0 :         return;
     142              :     }
     143              : 
     144           90 :     mpe::ArticulationMeta articulationMeta;
     145           45 :     articulationMeta.type = type;
     146           45 :     articulationMeta.pattern = pattern;
     147           45 :     articulationMeta.timestamp = spannerCtx.nominalTimestamp;
     148           45 :     articulationMeta.overallPitchChangesRange = overallPitchRange;
     149           45 :     articulationMeta.overallDynamicChangesRange = overallDynamicRange;
     150           90 :     articulationMeta.overallDuration = durationFromStartAndTicks(spannerCtx.score,
     151           45 :                                                                  spannerCtx.nominalPositionStartTick,
     152              :                                                                  overallDurationTicks,
     153           45 :                                                                  spannerCtx.positionTickOffset);
     154              : 
     155           45 :     appendArticulationData(std::move(articulationMeta), result);
     156              : }
        

Generated by: LCOV version 2.0-1