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) 2026 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 : #include "diveinfoconverter.h"
23 :
24 : #include <engraving/dom/note.h>
25 :
26 : #include <cmath>
27 :
28 : using namespace mu::engraving;
29 :
30 : namespace mu::iex::guitarpro {
31 92 : static std::vector<SegmentData> classifyDiveSegments(const PitchValues& pitchValues)
32 : {
33 : enum class Dir {
34 : NONE, UP, DOWN
35 : };
36 :
37 92 : std::vector<SegmentData> segments;
38 92 : Dir prevDir = Dir::NONE;
39 92 : size_t segStartIdx = 0;
40 :
41 276 : for (size_t i = 0; i + 1 < pitchValues.size(); ++i) {
42 184 : const int dp = pitchValues[i + 1].pitch - pitchValues[i].pitch;
43 184 : if (dp == 0) {
44 84 : continue;
45 : }
46 :
47 159 : const int qt = gpPitchToQuarterTones(dp);
48 159 : if (qt == 0) {
49 0 : continue;
50 : }
51 :
52 159 : Dir curDir = dp > 0 ? Dir::UP : Dir::DOWN;
53 :
54 159 : if (curDir == prevDir && !segments.empty()) {
55 59 : SegmentData& last = segments.back();
56 118 : last.quarterTones = gpPitchToQuarterTones(
57 59 : pitchValues[i + 1].pitch - pitchValues[segStartIdx].pitch);
58 59 : last.endFactor = static_cast<double>(pitchValues[i + 1].time) / PitchValue::MAX_TIME;
59 59 : continue;
60 59 : }
61 :
62 100 : segStartIdx = i;
63 100 : SegmentData seg;
64 100 : seg.quarterTones = qt;
65 100 : seg.startFactor = static_cast<double>(pitchValues[i].time) / PitchValue::MAX_TIME;
66 100 : seg.endFactor = static_cast<double>(pitchValues[i + 1].time) / PitchValue::MAX_TIME;
67 100 : segments.push_back(std::move(seg));
68 :
69 100 : prevDir = curDir;
70 : }
71 :
72 92 : return segments;
73 0 : }
74 :
75 353 : int gpPitchToQuarterTones(int gpPitch)
76 : {
77 353 : return static_cast<int>(std::lround(static_cast<float>(gpPitch) / GP_PITCH_PER_QUARTERTONE));
78 : }
79 :
80 92 : ImportedDiveInfo DiveInfoConverter::fromPitchValues(const PitchValues& pitchValues, bool isContinuedWhammy)
81 : {
82 92 : ImportedDiveInfo info;
83 :
84 92 : if (pitchValues.size() < 2) {
85 0 : return info;
86 : }
87 :
88 92 : const int originPitch = pitchValues.front().pitch;
89 92 : const int destPitch = pitchValues.back().pitch;
90 92 : const int originQuarterTones = gpPitchToQuarterTones(originPitch);
91 :
92 92 : std::vector<SegmentData> chain = classifyDiveSegments(pitchValues);
93 :
94 92 : if (isContinuedWhammy && destPitch == originPitch) {
95 1 : return info;
96 91 : } else if (!isContinuedWhammy && originPitch != 0 && destPitch == originPitch) {
97 8 : info.type = DiveType::PRE_DIVE;
98 8 : info.quarterTones = -gpPitchToQuarterTones(originPitch);
99 8 : info.endFactor = 1.0;
100 8 : return info;
101 83 : } else if (!isContinuedWhammy && originPitch != 0) {
102 34 : info.type = DiveType::PRE_DIVE;
103 34 : info.quarterTones = -originQuarterTones;
104 34 : info.endFactor = 1.0;
105 34 : info.graceDiveSegments = std::move(chain);
106 34 : return info;
107 49 : } else if (chain.empty()) {
108 0 : return info;
109 : }
110 :
111 49 : info.type = DiveType::DIVE;
112 49 : info.graceDiveSegments = std::move(chain);
113 :
114 49 : if (info.graceDiveSegments.size() == 1) {
115 35 : SegmentData& only = info.graceDiveSegments.front();
116 35 : only.quarterTones = gpPitchToQuarterTones(destPitch - originPitch);
117 35 : only.startFactor = static_cast<double>(pitchValues.front().time) / PitchValue::MAX_TIME;
118 35 : only.endFactor = static_cast<double>(pitchValues.back().time) / PitchValue::MAX_TIME;
119 : }
120 :
121 49 : return info;
122 92 : }
123 :
124 92 : ImportedDiveInfo DiveInfoConverter::fillDiveInfo(const Note* note, const PitchValues& pitchValues, bool isContinuedWhammy)
125 : {
126 92 : if (!note || pitchValues.size() < 2) {
127 0 : return {};
128 : }
129 :
130 92 : ImportedDiveInfo info = fromPitchValues(pitchValues, isContinuedWhammy);
131 :
132 92 : if (info.type == DiveType::NONE) {
133 1 : return info;
134 : }
135 :
136 91 : if (info.type == DiveType::DIVE && info.graceDiveSegments.empty()) {
137 0 : info.type = DiveType::NONE;
138 0 : return info;
139 : }
140 :
141 91 : info.note = note;
142 91 : return info;
143 92 : }
144 : } // namespace mu::iex::guitarpro
|