Fix potential wrong-over-optimization in math utilities
[carla.git] / source / native-plugins / midi-base.hpp
blob382f5a7411de72027f1c0734965b8ce173046962
1 /*
2 * Carla Native Plugins
3 * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #ifndef MIDI_BASE_HPP_INCLUDED
19 #define MIDI_BASE_HPP_INCLUDED
21 #include "CarlaMIDI.h"
22 #include "CarlaMutex.hpp"
23 #include "LinkedList.hpp"
25 #include "CarlaJuceUtils.hpp"
26 #include "CarlaMathUtils.hpp"
28 // -----------------------------------------------------------------------
30 #define MAX_EVENT_DATA_SIZE 4
31 #define MIN_PREALLOCATED_EVENT_COUNT 100
32 #define MAX_PREALLOCATED_EVENT_COUNT 1000
34 // -----------------------------------------------------------------------
36 struct RawMidiEvent {
37 uint32_t time;
38 uint8_t size;
39 uint8_t data[MAX_EVENT_DATA_SIZE];
42 // -----------------------------------------------------------------------
44 class AbstractMidiPlayer
46 public:
47 virtual ~AbstractMidiPlayer() {}
48 virtual void writeMidiEvent(const uint8_t port, const double timePosFrame, const RawMidiEvent* const event) = 0;
51 // -----------------------------------------------------------------------
53 class MidiPattern
55 public:
56 MidiPattern(AbstractMidiPlayer* const player) noexcept
57 : kPlayer(player),
58 fMidiPort(0),
59 fStartTime(0),
60 fReadMutex(),
61 fWriteMutex(),
62 fData()
64 CARLA_SAFE_ASSERT(kPlayer != nullptr);
67 ~MidiPattern() noexcept
69 clear();
72 // -------------------------------------------------------------------
73 // add data, time always counts from 0
75 void addControl(const uint32_t time, const uint8_t channel, const uint8_t control, const uint8_t value)
77 RawMidiEvent* const ctrlEvent(new RawMidiEvent());
78 ctrlEvent->time = time;
79 ctrlEvent->size = 3;
80 ctrlEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT));
81 ctrlEvent->data[1] = control;
82 ctrlEvent->data[2] = value;
84 appendSorted(ctrlEvent);
87 void addChannelPressure(const uint32_t time, const uint8_t channel, const uint8_t pressure)
89 RawMidiEvent* const pressureEvent(new RawMidiEvent());
90 pressureEvent->time = time;
91 pressureEvent->size = 2;
92 pressureEvent->data[0] = uint8_t(MIDI_STATUS_CHANNEL_PRESSURE | (channel & MIDI_CHANNEL_BIT));
93 pressureEvent->data[1] = pressure;
95 appendSorted(pressureEvent);
98 void addNote(const uint32_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity, const uint32_t duration)
100 addNoteOn(time, channel, pitch, velocity);
101 addNoteOff(time+duration, channel, pitch, velocity);
104 void addNoteOn(const uint32_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity)
106 RawMidiEvent* const noteOnEvent(new RawMidiEvent());
107 noteOnEvent->time = time;
108 noteOnEvent->size = 3;
109 noteOnEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_ON | (channel & MIDI_CHANNEL_BIT));
110 noteOnEvent->data[1] = pitch;
111 noteOnEvent->data[2] = velocity;
113 appendSorted(noteOnEvent);
116 void addNoteOff(const uint32_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity = 0)
118 RawMidiEvent* const noteOffEvent(new RawMidiEvent());
119 noteOffEvent->time = time;
120 noteOffEvent->size = 3;
121 noteOffEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_OFF | (channel & MIDI_CHANNEL_BIT));
122 noteOffEvent->data[1] = pitch;
123 noteOffEvent->data[2] = velocity;
125 appendSorted(noteOffEvent);
128 void addNoteAftertouch(const uint32_t time, const uint8_t channel, const uint8_t pitch, const uint8_t pressure)
130 RawMidiEvent* const noteAfterEvent(new RawMidiEvent());
131 noteAfterEvent->time = time;
132 noteAfterEvent->size = 3;
133 noteAfterEvent->data[0] = uint8_t(MIDI_STATUS_POLYPHONIC_AFTERTOUCH | (channel & MIDI_CHANNEL_BIT));
134 noteAfterEvent->data[1] = pitch;
135 noteAfterEvent->data[2] = pressure;
137 appendSorted(noteAfterEvent);
140 void addProgram(const uint32_t time, const uint8_t channel, const uint8_t bank, const uint8_t program)
142 RawMidiEvent* const bankEvent(new RawMidiEvent());
143 bankEvent->time = time;
144 bankEvent->size = 3;
145 bankEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT));
146 bankEvent->data[1] = MIDI_CONTROL_BANK_SELECT;
147 bankEvent->data[2] = bank;
149 RawMidiEvent* const programEvent(new RawMidiEvent());
150 programEvent->time = time;
151 programEvent->size = 2;
152 programEvent->data[0] = uint8_t(MIDI_STATUS_PROGRAM_CHANGE | (channel & MIDI_CHANNEL_BIT));
153 programEvent->data[1] = program;
155 appendSorted(bankEvent);
156 appendSorted(programEvent);
159 void addPitchbend(const uint32_t time, const uint8_t channel, const uint8_t lsb, const uint8_t msb)
161 RawMidiEvent* const pressureEvent(new RawMidiEvent());
162 pressureEvent->time = time;
163 pressureEvent->size = 3;
164 pressureEvent->data[0] = uint8_t(MIDI_STATUS_PITCH_WHEEL_CONTROL | (channel & MIDI_CHANNEL_BIT));
165 pressureEvent->data[1] = lsb;
166 pressureEvent->data[2] = msb;
168 appendSorted(pressureEvent);
171 void addRaw(const uint32_t time, const uint8_t* const data, const uint8_t size)
173 RawMidiEvent* const rawEvent(new RawMidiEvent());
174 rawEvent->time = time;
175 rawEvent->size = size;
177 carla_copy<uint8_t>(rawEvent->data, data, size);
179 // Fix zero-velocity note-ons
180 if (MIDI_IS_STATUS_NOTE_ON(data[0]) && data[2] == 0)
181 rawEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_OFF | (data[0] & MIDI_CHANNEL_BIT));
183 appendSorted(rawEvent);
186 // -------------------------------------------------------------------
187 // remove data
189 void removeRaw(const uint32_t time, const uint8_t* const data, const uint8_t size)
191 const CarlaMutexLocker cmlw(fWriteMutex);
193 for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
195 const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
196 CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
198 if (rawMidiEvent->time != time)
199 continue;
200 if (rawMidiEvent->size != size)
201 continue;
202 if (std::memcmp(rawMidiEvent->data, data, size) != 0)
203 continue;
206 const CarlaMutexLocker cmlr(fReadMutex);
207 fData.remove(it);
210 delete rawMidiEvent;
211 return;
214 carla_stderr("MidiPattern::removeRaw(%u, %p, %i) - unable to find event to remove", time, data, size);
217 // -------------------------------------------------------------------
218 // clear
220 void clear() noexcept
222 const CarlaMutexLocker cmlr(fReadMutex);
223 const CarlaMutexLocker cmlw(fWriteMutex);
225 for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
226 delete it.getValue(nullptr);
228 fData.clear();
231 // -------------------------------------------------------------------
232 // play on time
234 bool play(const uint32_t timePosFrame, const uint32_t frames)
236 return play(static_cast<double>(timePosFrame), static_cast<double>(frames));
239 bool play(double timePosFrame, const double frames, const double offset = 0.0)
241 double ldtime;
243 const CarlaMutexTryLocker cmtl(fReadMutex);
245 if (cmtl.wasNotLocked())
246 return false;
248 if (fStartTime != 0)
249 timePosFrame += static_cast<double>(fStartTime);
251 for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
253 const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
254 CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
256 ldtime = static_cast<double>(rawMidiEvent->time);
258 if (ldtime < timePosFrame)
259 continue;
260 if (ldtime > timePosFrame + frames)
261 break;
263 if (carla_isEqual(ldtime, timePosFrame + frames))
265 // only allow a few events to pass through in this special case
266 if (! MIDI_IS_STATUS_NOTE_OFF(rawMidiEvent->data[0]))
267 continue;
270 kPlayer->writeMidiEvent(fMidiPort, ldtime + offset - timePosFrame, rawMidiEvent);
273 return true;
276 // -------------------------------------------------------------------
277 // configure
279 void setMidiPort(const uint8_t port) noexcept
281 fMidiPort = port;
284 void setStartTime(const uint32_t time) noexcept
286 fStartTime = time;
289 // -------------------------------------------------------------------
290 // special
292 const CarlaMutex& getWriteMutex() const noexcept
294 return fWriteMutex;
297 LinkedList<const RawMidiEvent*>::Itenerator iteneratorBegin() const noexcept
299 return fData.begin2();
302 // -------------------------------------------------------------------
303 // state
305 char* getState() const
307 static const std::size_t maxTimeSize = 20; // std::strlen("18446744073709551615");
308 static const std::size_t maxDataSize = 4 + 4*MAX_EVENT_DATA_SIZE; // std::strlen("0xFF:127:127:127");
309 static const std::size_t maxMsgSize = maxTimeSize + 3 /* sep + size + sep */ + maxDataSize + 1 /* newline */;
311 const CarlaMutexLocker cmlw(fWriteMutex);
313 char* const data((char*)std::calloc(1, fData.count() * maxMsgSize + 1));
314 CARLA_SAFE_ASSERT_RETURN(data != nullptr, nullptr);
316 if (fData.count() == 0)
318 *data = '\0';
319 return data;
322 char* dataWrtn = data;
323 int wrtn;
325 for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
327 const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr));
328 CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr);
330 wrtn = std::snprintf(dataWrtn, maxTimeSize+6, "%u:%u:", rawMidiEvent->time, rawMidiEvent->size);
331 CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
332 dataWrtn += wrtn;
334 wrtn = std::snprintf(dataWrtn, 5, "0x%02X", rawMidiEvent->data[0]);
335 CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
336 dataWrtn += wrtn;
338 for (uint8_t i=1, size=rawMidiEvent->size; i<size; ++i)
340 wrtn = std::snprintf(dataWrtn, 5, ":%03u", rawMidiEvent->data[i]);
341 CARLA_SAFE_ASSERT_BREAK(wrtn > 0);
342 dataWrtn += wrtn;
345 *dataWrtn++ = '\n';
348 *dataWrtn = '\0';
350 return data;
353 void setState(const char* const data)
355 CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
357 const size_t dataLen = std::strlen(data);
358 const char* dataRead = data;
359 const char* needle;
360 RawMidiEvent midiEvent;
361 char tmpBuf[24];
362 ssize_t tmpSize;
364 clear();
366 const CarlaMutexLocker cmlr(fReadMutex);
367 const CarlaMutexLocker cmlw(fWriteMutex);
369 for (size_t dataPos=0; dataPos < dataLen && *dataRead != '\0';)
371 // get time
372 needle = std::strchr(dataRead, ':');
374 if (needle == nullptr)
375 return;
377 carla_zeroStruct(midiEvent);
379 tmpSize = needle - dataRead;
380 CARLA_SAFE_ASSERT_RETURN(tmpSize > 0,);
381 CARLA_SAFE_ASSERT_RETURN(tmpSize < 24,);
384 const size_t uSize = static_cast<size_t>(tmpSize);
385 std::strncpy(tmpBuf, dataRead, uSize);
386 tmpBuf[tmpSize] = '\0';
387 dataRead += uSize+1U;
388 dataPos += uSize+1U;
391 const long long time = std::atoll(tmpBuf);
392 CARLA_SAFE_ASSERT_RETURN(time >= 0,);
394 midiEvent.time = static_cast<uint32_t>(time);
396 // get size
397 needle = std::strchr(dataRead, ':');
398 CARLA_SAFE_ASSERT_RETURN(needle != nullptr,);
400 tmpSize = needle - dataRead;
401 CARLA_SAFE_ASSERT_RETURN(tmpSize > 0 && tmpSize < 24,);
404 const size_t uSize = static_cast<size_t>(tmpSize);
405 std::strncpy(tmpBuf, dataRead, uSize);
406 tmpBuf[tmpSize] = '\0';
407 dataRead += uSize+1U;
408 dataPos += uSize+1U;
411 const int midiDataSize = std::atoi(tmpBuf);
412 CARLA_SAFE_ASSERT_RETURN(midiDataSize > 0 && midiDataSize <= MAX_EVENT_DATA_SIZE,);
414 midiEvent.size = static_cast<uint8_t>(midiDataSize);
416 // get events
417 for (int i=0; i<midiDataSize; ++i)
419 CARLA_SAFE_ASSERT_RETURN(dataRead-data >= 4,);
421 tmpSize = i==0 ? 4 : 3;
423 const size_t uSize = static_cast<size_t>(tmpSize);
424 std::strncpy(tmpBuf, dataRead, uSize);
425 tmpBuf[tmpSize] = '\0';
426 dataRead += uSize+1U;
427 dataPos += uSize+1U;
429 long mdata;
431 if (i == 0)
433 mdata = std::strtol(tmpBuf, nullptr, 16);
434 CARLA_SAFE_ASSERT_RETURN(mdata >= 0x80 && mdata <= 0xFF,);
436 else
438 mdata = std::atoi(tmpBuf);
439 CARLA_SAFE_ASSERT_RETURN(mdata >= 0 && mdata < MAX_MIDI_VALUE,);
442 midiEvent.data[i] = static_cast<uint8_t>(mdata);
445 for (int i=midiDataSize; i<MAX_EVENT_DATA_SIZE; ++i)
446 midiEvent.data[i] = 0;
448 RawMidiEvent* const event(new RawMidiEvent());
449 carla_copyStruct(*event, midiEvent);
450 fData.append(event);
454 // -------------------------------------------------------------------
456 private:
457 AbstractMidiPlayer* const kPlayer;
459 uint8_t fMidiPort;
460 uint32_t fStartTime;
462 CarlaMutex fReadMutex;
463 CarlaMutex fWriteMutex;
464 LinkedList<const RawMidiEvent*> fData;
466 void appendSorted(const RawMidiEvent* const event)
468 const CarlaMutexLocker cmlw(fWriteMutex);
470 if (fData.isEmpty())
472 fData.append(event);
473 return;
476 if (const RawMidiEvent* const lastEvent = fData.getLast(nullptr))
478 if (event->time >= lastEvent->time)
480 fData.append(event);
481 return;
485 for (LinkedList<const RawMidiEvent*>::Itenerator it = fData.begin2(); it.valid(); it.next())
487 const RawMidiEvent* const oldEvent(it.getValue(nullptr));
488 CARLA_SAFE_ASSERT_CONTINUE(oldEvent != nullptr);
490 if (event->time >= oldEvent->time)
491 continue;
493 fData.insertAt(event, it);
494 return;
497 fData.append(event);
500 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiPattern)
503 // -----------------------------------------------------------------------
505 #endif // MIDI_BASE_HPP_INCLUDED