Line data Source code
1 : #include "pitchwheelrenderer.h"
2 :
3 : #include <algorithm>
4 :
5 : #include "log.h"
6 :
7 : using namespace mu::engraving;
8 :
9 88 : PitchWheelRenderer::PitchWheelRenderer(PitchWheelSpecs wheelSpec)
10 88 : : _wheelSpec(wheelSpec)
11 88 : {}
12 :
13 107 : void PitchWheelRenderer::addPitchWheelFunction(const PitchWheelFunction& function, uint32_t channel, staff_idx_t staffIdx,
14 : MidiInstrumentEffect effect)
15 : {
16 107 : PitchWheelFunctions& functions = _functions[channel];
17 107 : _effectByChannel[channel] = effect;
18 107 : _staffIdxByChannel[channel] = staffIdx;
19 :
20 107 : if (function.mStartTick < functions.startTick) {
21 30 : functions.startTick = function.mStartTick;
22 : }
23 107 : if (function.mEndTick > functions.endTick) {
24 104 : functions.endTick = function.mEndTick;
25 : }
26 :
27 107 : functions.functions.push_back(function);
28 107 : }
29 :
30 88 : EventsHolder PitchWheelRenderer::renderPitchWheel() const noexcept
31 : {
32 88 : EventsHolder pitchWheelEvents;
33 :
34 118 : for (const auto& function : _functions) {
35 30 : renderChannelPitchWheel(pitchWheelEvents, function.second, function.first);
36 : }
37 :
38 88 : return pitchWheelEvents;
39 : }
40 :
41 : //! MARK: PRIVATE METHODS
42 :
43 30 : void PitchWheelRenderer::renderChannelPitchWheel(EventsHolder& pitchWheelEvents,
44 : const PitchWheelFunctions& functions,
45 : uint32_t channel) const noexcept
46 : {
47 30 : if (functions.endTick < functions.startTick) {
48 0 : return;
49 : }
50 :
51 30 : MidiInstrumentEffect effect = MidiInstrumentEffect::NONE;
52 30 : if (_effectByChannel.find(channel) != _effectByChannel.end()) {
53 30 : effect = _effectByChannel.at(channel);
54 : }
55 :
56 30 : bool staffInfoValid = false;
57 30 : staff_idx_t staffIdx = 0;
58 :
59 30 : if (_staffIdxByChannel.find(channel) != _staffIdxByChannel.end()) {
60 30 : staffInfoValid = true;
61 30 : staffIdx = _staffIdxByChannel.at(channel);
62 : }
63 :
64 30 : std::map<int, int, std::greater<> > ranges;
65 30 : generateRanges(functions.functions, ranges);
66 :
67 79 : for (auto rit = ranges.crbegin(); rit != ranges.crend(); ++rit) {
68 49 : int32_t start = rit->first;
69 49 : int32_t end = rit->second;
70 49 : int32_t tick = start;
71 :
72 49 : std::vector<PitchWheelFunction> functionsToProcess;
73 49 : functionsToProcess.reserve(functions.functions.size());
74 261 : for (const auto& func : functions.functions) {
75 212 : if (func.mEndTick <= end) {
76 158 : functionsToProcess.push_back(func);
77 : }
78 : }
79 49 : std::vector<int> pitches;
80 49 : pitches.reserve(functionsToProcess.size());
81 207 : for (size_t i = 0; i < functionsToProcess.size(); ++i) {
82 158 : pitches.push_back(0);
83 : }
84 49 : int prevPitch = _wheelSpec.mLimit;
85 49 : bool forceUpdate = false;
86 1475 : while (tick < end) {
87 1426 : auto funcIt = functionsToProcess.begin();
88 7014 : for (size_t i = 0; i < functionsToProcess.size(); ++i) {
89 5588 : if (tick >= funcIt->mEndTick) {
90 : // Function exceeds its max range
91 : // don't need its value anymore
92 2772 : pitches.at(i) = 0;
93 2772 : ++funcIt;
94 2772 : forceUpdate = true;
95 2772 : continue;
96 : }
97 2816 : if (tick < funcIt->mStartTick) {
98 1278 : ++funcIt;
99 1278 : continue;
100 : }
101 1538 : int pitch = funcIt->func(tick);
102 1538 : pitches.at(i) = pitch;
103 1538 : ++funcIt;
104 : }
105 1426 : int finalPitch = _wheelSpec.mLimit;
106 7014 : for (const auto& pitch : pitches) {
107 5588 : finalPitch += pitch;
108 : }
109 1426 : finalPitch = std::clamp(finalPitch, 0, 2 * _wheelSpec.mLimit - 1);
110 1426 : if (forceUpdate || finalPitch != prevPitch || tick == start) {
111 1345 : NPlayEvent evb(ME_PITCHBEND, channel, finalPitch % 128, finalPitch / 128);
112 1345 : evb.setEffect(effect);
113 1345 : if (staffInfoValid) {
114 1345 : evb.setOriginatingStaff(staffIdx);
115 : }
116 1345 : pitchWheelEvents[channel].emplace_hint(pitchWheelEvents[channel].end(), std::make_pair(tick, evb));
117 1345 : forceUpdate = false;
118 : }
119 :
120 1426 : prevPitch = finalPitch;
121 1426 : tick += _wheelSpec.mStep;
122 : }
123 49 : }
124 30 : }
125 :
126 0 : int32_t PitchWheelRenderer::findNextStartTick(const std::vector<PitchWheelFunction>& functions) const noexcept
127 : {
128 0 : int32_t tick = std::numeric_limits<int32_t>::max();
129 0 : for (const auto& func : functions) {
130 0 : tick = std::min(tick, func.mStartTick);
131 : }
132 :
133 0 : return tick;
134 : }
135 :
136 0 : int32_t PitchWheelRenderer::calculatePitchBend(const std::vector<PitchWheelFunction>& functions, int32_t tick) const noexcept
137 : {
138 0 : int pitchValue = _wheelSpec.mLimit;
139 :
140 0 : for (const auto& func : functions) {
141 0 : if (tick < func.mStartTick || tick >= func.mEndTick) {
142 0 : continue;
143 : }
144 :
145 0 : pitchValue += func.func(tick);
146 : }
147 :
148 0 : return pitchValue;
149 : }
150 :
151 : // 1 |------|
152 : // 2 |-----------| handleStartTick();
153 : // result |--------------|
154 : // 3 |--------| handleEndTick();
155 : // result |-------------------|
156 35 : void PitchWheelRenderer::generateRanges(const std::vector<PitchWheelFunction>& functions, std::map<int, int, std::greater<> >& ranges)
157 : {
158 : // !NOTE ranges map is reversed. Use reverse iterators
159 32 : auto handleEndTick = [&](const PitchWheelFunction& func) {
160 32 : auto lowerRange = ranges.upper_bound(func.mEndTick);
161 32 : if (lowerRange == ranges.end()) {
162 31 : return false;
163 : }
164 1 : ranges.insert({ func.mStartTick, lowerRange->second });
165 1 : ranges.erase(lowerRange);
166 1 : return true;
167 35 : };
168 :
169 110 : auto handleStartTick = [&](const PitchWheelFunction& func) {
170 110 : auto lowerRange = ranges.upper_bound(func.mStartTick);
171 110 : if (lowerRange == ranges.end()) {
172 : // We are getting PW events sorted by startTick
173 : // So in ideal world handleEndTick always return false
174 32 : return handleEndTick(func);
175 : }
176 78 : if (lowerRange->second >= func.mStartTick) {
177 58 : lowerRange->second = std::max(lowerRange->second, func.mEndTick);
178 58 : return true;
179 : }
180 20 : return false;
181 35 : };
182 :
183 147 : for (const auto& func : functions) {
184 112 : if (auto key = ranges.find(func.mStartTick); key != ranges.end()) {
185 2 : key->second = std::max(key->second, func.mEndTick);
186 2 : continue;
187 : }
188 110 : if (!handleStartTick(func)) {
189 51 : ranges.insert({ func.mStartTick, func.mEndTick });
190 : }
191 : }
192 35 : }
|