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 // -----------------------------------------------------------------------
39 uint8_t data
[MAX_EVENT_DATA_SIZE
];
42 // -----------------------------------------------------------------------
44 class AbstractMidiPlayer
47 virtual ~AbstractMidiPlayer() {}
48 virtual void writeMidiEvent(const uint8_t port
, const double timePosFrame
, const RawMidiEvent
* const event
) = 0;
51 // -----------------------------------------------------------------------
56 MidiPattern(AbstractMidiPlayer
* const player
) noexcept
64 CARLA_SAFE_ASSERT(kPlayer
!= nullptr);
67 ~MidiPattern() noexcept
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
;
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
;
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 // -------------------------------------------------------------------
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
)
200 if (rawMidiEvent
->size
!= size
)
202 if (std::memcmp(rawMidiEvent
->data
, data
, size
) != 0)
206 const CarlaMutexLocker
cmlr(fReadMutex
);
214 carla_stderr("MidiPattern::removeRaw(%u, %p, %i) - unable to find event to remove", time
, data
, size
);
217 // -------------------------------------------------------------------
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);
231 // -------------------------------------------------------------------
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)
243 const CarlaMutexTryLocker
cmtl(fReadMutex
);
245 if (cmtl
.wasNotLocked())
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
)
260 if (ldtime
> timePosFrame
+ frames
)
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]))
270 kPlayer
->writeMidiEvent(fMidiPort
, ldtime
+ offset
- timePosFrame
, rawMidiEvent
);
276 // -------------------------------------------------------------------
279 void setMidiPort(const uint8_t port
) noexcept
284 void setStartTime(const uint32_t time
) noexcept
289 // -------------------------------------------------------------------
292 const CarlaMutex
& getWriteMutex() const noexcept
297 LinkedList
<const RawMidiEvent
*>::Itenerator
iteneratorBegin() const noexcept
299 return fData
.begin2();
302 // -------------------------------------------------------------------
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)
322 char* dataWrtn
= data
;
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);
334 wrtn
= std::snprintf(dataWrtn
, 5, "0x%02X", rawMidiEvent
->data
[0]);
335 CARLA_SAFE_ASSERT_BREAK(wrtn
> 0);
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);
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
;
360 RawMidiEvent midiEvent
;
366 const CarlaMutexLocker
cmlr(fReadMutex
);
367 const CarlaMutexLocker
cmlw(fWriteMutex
);
369 for (size_t dataPos
=0; dataPos
< dataLen
&& *dataRead
!= '\0';)
372 needle
= std::strchr(dataRead
, ':');
374 if (needle
== nullptr)
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;
391 const long long time
= std::atoll(tmpBuf
);
392 CARLA_SAFE_ASSERT_RETURN(time
>= 0,);
394 midiEvent
.time
= static_cast<uint32_t>(time
);
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;
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
);
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;
433 mdata
= std::strtol(tmpBuf
, nullptr, 16);
434 CARLA_SAFE_ASSERT_RETURN(mdata
>= 0x80 && mdata
<= 0xFF,);
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
);
454 // -------------------------------------------------------------------
457 AbstractMidiPlayer
* const kPlayer
;
462 CarlaMutex fReadMutex
;
463 CarlaMutex fWriteMutex
;
464 LinkedList
<const RawMidiEvent
*> fData
;
466 void appendSorted(const RawMidiEvent
* const event
)
468 const CarlaMutexLocker
cmlw(fWriteMutex
);
476 if (const RawMidiEvent
* const lastEvent
= fData
.getLast(nullptr))
478 if (event
->time
>= lastEvent
->time
)
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
)
493 fData
.insertAt(event
, it
);
500 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiPattern
)
503 // -----------------------------------------------------------------------
505 #endif // MIDI_BASE_HPP_INCLUDED