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 "swing.h"
24 :
25 : #include "realfn.h"
26 :
27 : #include "../editing/navigation.h"
28 :
29 : #include "chord.h"
30 : #include "measure.h"
31 : #include "staff.h"
32 :
33 : using namespace mu::engraving;
34 :
35 0 : bool Swing::ChordDurationAdjustment::isNull() const
36 : {
37 0 : return muse::RealIsNull(remainingDurationMultiplier) && muse::RealIsNull(durationMultiplier);
38 : }
39 :
40 : //---------------------------------------------------------
41 : // isSubdivided
42 : // Check for subdivided beat
43 : //---------------------------------------------------------
44 :
45 0 : static bool isSubdivided(const ChordRest* chord, int swingUnit)
46 : {
47 0 : if (!chord) {
48 0 : return false;
49 : }
50 :
51 0 : const ChordRest* prev = Navigation::prevChordRest(chord);
52 0 : if (chord->actualTicks().ticks() < swingUnit || (prev && prev->actualTicks().ticks() < swingUnit)) {
53 0 : return true;
54 : }
55 :
56 0 : return false;
57 : }
58 :
59 0 : void Swing::swingAdjustParams(const Chord* chord, const SwingParameters& params, int& onTime, int& gateTime)
60 : {
61 0 : Fraction tick = chord->rtick() + chord->measure()->anacrusisOffset();
62 :
63 0 : int swingBeat = params.swingUnit * 2;
64 0 : double ticksDuration = (double)chord->actualTicks().ticks();
65 0 : double swingTickAdjust = ((double)swingBeat) * (((double)(params.swingRatio - 50)) / 100.0);
66 0 : double swingActualAdjust = (swingTickAdjust / ticksDuration) * 1000.0;
67 0 : int startTick = tick.ticks();
68 :
69 : //Check the position of the chord to apply changes accordingly
70 0 : if (startTick % swingBeat == params.swingUnit) {
71 0 : if (!isSubdivided(chord, params.swingUnit)) {
72 0 : onTime = onTime + swingActualAdjust;
73 0 : gateTime = (200 - (gateTime + (swingActualAdjust / 10)));
74 : }
75 : }
76 :
77 0 : int endTick = startTick + ticksDuration;
78 0 : if (endTick % swingBeat == params.swingUnit) {
79 0 : const ChordRest* ncr = Navigation::nextChordRest(chord);
80 :
81 0 : if (!isSubdivided(ncr, params.swingUnit)) {
82 0 : gateTime = gateTime + (swingActualAdjust / 10);
83 : }
84 : }
85 0 : }
86 :
87 0 : Swing::ChordDurationAdjustment Swing::applySwing(const Chord* chord, const SwingParameters& params)
88 : {
89 0 : int onTime = 0;
90 0 : int gateTime = 100;
91 :
92 0 : swingAdjustParams(chord, params, onTime, gateTime);
93 :
94 0 : if (gateTime <= 0) {
95 0 : gateTime = 100;
96 : }
97 :
98 0 : ChordDurationAdjustment result;
99 0 : result.remainingDurationMultiplier = static_cast<double>(onTime) / 1000.;
100 0 : result.durationMultiplier = static_cast<double>(gateTime) / 100.;
101 :
102 0 : return result;
103 : }
|