tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_MPEStartTrimmer.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
23{
28 static void reconstructExpression (juce::Array<juce::MidiMessage>& mpeMessagesToAddAtStart,
29 const juce::MidiMessageSequence& data,
30 int trimIndex, int channel)
31 {
32 jassert (trimIndex < data.getNumEvents());
33
34 const int lastNoteOnIndex = searchBackForNoteOn (data, trimIndex, channel);
35
36 if (! wasFound (lastNoteOnIndex))
37 return;
38
39 const auto& noteOn = data.getEventPointer (lastNoteOnIndex)->message;
40 const auto initial = searchBackForExpression (data, lastNoteOnIndex, channel, MessageToStopAt::noteOff);
41 const auto mostRecent = searchBackForExpression (data, trimIndex, channel, MessageToStopAt::noteOn);
42
43 const int centrePitchbend = juce::MidiMessage::pitchbendToPitchwheelPos (0.0f, 12.f);
44
45 mpeMessagesToAddAtStart.add (juce::MidiMessage::controllerEvent (channel, 74, wasFound (initial.timbre) ? initial.timbre : 64));
46 mpeMessagesToAddAtStart.add (juce::MidiMessage::channelPressureChange (channel, wasFound (initial.pressure) ? initial.pressure : 0));
47 mpeMessagesToAddAtStart.add (juce::MidiMessage::pitchWheel (channel, wasFound (initial.pitchBend) ? initial.pitchBend : centrePitchbend));
48 mpeMessagesToAddAtStart.add (juce::MidiMessage::noteOn (channel, noteOn.getNoteNumber(), noteOn.getVelocity()));
49
50 if (wasFound (mostRecent.timbre))
51 mpeMessagesToAddAtStart.add (juce::MidiMessage::controllerEvent (channel, 74, mostRecent.timbre));
52
53 if (wasFound (mostRecent.pressure))
54 mpeMessagesToAddAtStart.add (juce::MidiMessage::channelPressureChange (channel, mostRecent.pressure));
55
56 if (wasFound (mostRecent.pitchBend))
57 mpeMessagesToAddAtStart.add (juce::MidiMessage::pitchWheel (channel, mostRecent.pitchBend));
58 }
59
64 static void reconstructExpression (juce::Array<juce::MidiMessage>& mpeMessagesToAddAtStart,
65 const choc::midi::Sequence& data,
66 size_t trimIndex, int channel1to16)
67 {
68 jassert (trimIndex < data.events.size());
69
70 const int lastNoteOnIndex = searchBackForNoteOn (data, (int) trimIndex, (uint8_t) channel1to16);
71
72 if (! wasFound (lastNoteOnIndex))
73 return;
74
75 const auto& noteOn = data.events[(size_t) lastNoteOnIndex].message;
76 const auto initial = searchBackForExpression (data, lastNoteOnIndex, (uint8_t) channel1to16, MessageToStopAt::noteOff);
77 const auto mostRecent = searchBackForExpression (data, (int) trimIndex, (uint8_t) channel1to16, MessageToStopAt::noteOn);
78
79 const int centrePitchbend = juce::MidiMessage::pitchbendToPitchwheelPos (0.0f, 12.f);
80
81 mpeMessagesToAddAtStart.add (juce::MidiMessage::controllerEvent (channel1to16, 74, wasFound (initial.timbre) ? initial.timbre : 64));
82 mpeMessagesToAddAtStart.add (juce::MidiMessage::channelPressureChange (channel1to16, wasFound (initial.pressure) ? initial.pressure : 0));
83 mpeMessagesToAddAtStart.add (juce::MidiMessage::pitchWheel (channel1to16, wasFound (initial.pitchBend) ? initial.pitchBend : centrePitchbend));
84 mpeMessagesToAddAtStart.add (juce::MidiMessage::noteOn (channel1to16, noteOn.getNoteNumber(), noteOn.getVelocity()));
85
86 if (wasFound (mostRecent.timbre))
87 mpeMessagesToAddAtStart.add (juce::MidiMessage::controllerEvent (channel1to16, 74, mostRecent.timbre));
88
89 if (wasFound (mostRecent.pressure))
90 mpeMessagesToAddAtStart.add (juce::MidiMessage::channelPressureChange (channel1to16, mostRecent.pressure));
91
92 if (wasFound (mostRecent.pitchBend))
93 mpeMessagesToAddAtStart.add (juce::MidiMessage::pitchWheel (channel1to16, mostRecent.pitchBend));
94 }
95
96private:
97 static int searchBackForNoteOn (const juce::MidiMessageSequence& data, int startIndex, int channel)
98 {
99 while (--startIndex >= 0)
100 {
101 const auto& m = data.getEventPointer (startIndex)->message;
102
103 if (m.getChannel() == channel)
104 {
105 if (m.isNoteOn (true))
106 return startIndex;
107
108 if (m.isNoteOff (true))
109 return notFound;
110 }
111 }
112
113 return notFound;
114 }
115
116 static int searchBackForNoteOn (const choc::midi::Sequence& data, int startIndex, uint8_t channel1to16)
117 {
118 while (--startIndex >= 0)
119 {
120 const auto& e = data.events[(size_t) startIndex];
121
122 const auto& m = e.message;
123
124 if (! m.isShortMessage())
125 continue;
126
127 if (m.getChannel1to16() == channel1to16)
128 {
129 if (m.isNoteOn())
130 return startIndex;
131
132 if (m.isNoteOff())
133 return notFound;
134 }
135 }
136
137 return notFound;
138 }
139
140 struct ExpressionData
141 {
142 int timbre;
143 int pressure;
144 int pitchBend;
145 };
146
147 enum MessageToStopAt
148 {
149 noteOn,
150 noteOff
151 };
152
153 static ExpressionData searchBackForExpression (const juce::MidiMessageSequence& data,
154 int startIndex, int channel, MessageToStopAt stopAt)
155 {
156 int timbre = notFound;
157 int pressure = notFound;
158 int pitchBend = notFound;
159
160 for (int i = startIndex; --i >= 0;) // Find initial note-on timbre value
161 {
162 const auto& m = data.getEventPointer (i)->message;
163
164 if (m.getChannel() != channel)
165 continue;
166
167 if ((stopAt == noteOn ? m.isNoteOn (true) : m.isNoteOff (true))
168 || (wasFound (timbre) && wasFound (pressure) && wasFound (pitchBend)))
169 break;
170
171 if (m.isController() && m.getControllerNumber() == 74 && ! wasFound (timbre))
172 timbre = m.getControllerValue();
173
174 else if (m.isChannelPressure() && ! wasFound (pressure))
175 pressure = m.getChannelPressureValue();
176
177 else if (m.isPitchWheel() && ! wasFound (pitchBend))
178 pitchBend = m.getPitchWheelValue();
179 }
180
181 return { timbre, pressure, pitchBend };
182 }
183
184 static ExpressionData searchBackForExpression (const choc::midi::Sequence& data,
185 int startIndex, uint8_t channel, MessageToStopAt stopAt)
186 {
187 int timbre = notFound;
188 int pressure = notFound;
189 int pitchBend = notFound;
190
191 for (int i = startIndex; --i >= 0;) // Find initial note-on timbre value
192 {
193 const auto& m = data.events[(size_t) startIndex].message;
194
195 if (! m.isShortMessage())
196 continue;
197
198 if (m.getChannel1to16() != channel)
199 continue;
200
201 if ((stopAt == noteOn ? m.isNoteOn() : m.isNoteOff())
202 || (wasFound (timbre) && wasFound (pressure) && wasFound (pitchBend)))
203 break;
204
205 if (m.isController() && m.getControllerNumber() == 74 && ! wasFound (timbre))
206 timbre = m.getControllerValue();
207 else if (m.isChannelPressure() && ! wasFound (pressure))
208 pressure = m.getChannelPressureValue();
209 else if (m.isPitchWheel() && ! wasFound (pitchBend))
210 pitchBend = (int) m.getPitchWheelValue();
211 }
212
213 return { timbre, pressure, pitchBend };
214 }
215
216 static constexpr int notFound = -1;
217 static bool wasFound (int v) { return v != notFound; }
218};
219
220}} // namespace tracktion { inline namespace engine
void add(const ElementType &newElement)
static MidiMessage pitchWheel(int channel, int position) noexcept
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
static uint16 pitchbendToPitchwheelPos(float pitchbendInSemitones, float pitchbendRangeInSemitones) noexcept
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
static MidiMessage channelPressureChange(int channel, int pressure) noexcept
T data(T... args)
#define jassert(expression)
typedef int
typedef uint8_t
If you need to play back MPE data from a point after the data starts, it's important to reconstruct t...
static void reconstructExpression(juce::Array< juce::MidiMessage > &mpeMessagesToAddAtStart, const choc::midi::Sequence &data, size_t trimIndex, int channel1to16)
Reconstruct note expression for a particular channel.
static void reconstructExpression(juce::Array< juce::MidiMessage > &mpeMessagesToAddAtStart, const juce::MidiMessageSequence &data, int trimIndex, int channel)
Reconstruct note expression for a particular channel.
typedef size_t