/
HiseEventBuffer.h
772 lines (563 loc) · 25.1 KB
/
HiseEventBuffer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
/* ===========================================================================
*
* This file is part of HISE.
* Copyright 2016 Christoph Hart
*
* HISE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HISE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HISE. If not, see <http://www.gnu.org/licenses/>.
*
* Commercial licenses for using HISE in an closed source project are
* available on request. Please visit the project's website to get more
* information about commercial licensing:
*
* http://www.hise.audio/
*
* HISE is based on the JUCE library,
* which must be separately licensed for closed source applications:
*
* http://www.juce.com
*
* ===========================================================================
*/
#ifndef HISEEVENTBUFFER_H_INCLUDED
#define HISEEVENTBUFFER_H_INCLUDED
namespace hise { using namespace juce;
#define HISE_EVENT_ID_ARRAY_SIZE 16384
/** The event type of HISE.
@ingroup core
This is an enhancement of the MIDI Standard and is used for all internal
events in the audio path of HISE.
The MIDI standard (and its implementation of JUCE) have a few limitations
and misses some convenient data. Therefore, a new event type was introduced,
with the following additions:
- fixed size. The MIDI message has to have a dynamic size for handling
SysEx messages, which is not used in 99,9999% of all cases. The HiseEvent
simply ignores SysEx (there are better ways to communicate bigger chunks
of data anyways) and uses a fixed size of 128bit per message. This makes
copying / clearing the HiseEventBuffer trivially fast (just a memset / memcpy)
- note-on / note-off messages will be associated with a unique index (the
EventID), which can be used to alter all voices that are started by
the event.
- more types for internal actions like timer events, pitch / volume fades
- a timestamp that is baked into the message.
- 128 channels instead of 16 MIDI channels. 16 is a pretty low number and
if you use the channel information to route the signals to certain
Processors, you might hit this limitation pretty easily.
- a transpose amount that will be added on top of the actual note number.
This heavily simplifies any MIDI processing that changes the note number
because the note off event does not need to be transposed to match the
note on in order to prevent stuck notes
- a few flags that describe the state and origin of the note (whether the
message should be processed at all or if it was created internally).
Most of its methods aim to be fully compatible to the juce::MidiMessage class,
so if you're used to this class, you will find your way around this class
pretty quickly.
*/
class HiseEvent
{
public:
/** The type of the event. The most important MIDI types are there, but there are a few
more interesting types for internal HISE stuff. */
enum class Type : uint8
{
Empty = 0, ///< an empty event (as created by the default constructor)
NoteOn, ///< a note on event (which will get a unique EventID upon creation).
NoteOff, ///< a note-off event (with the same EventID as its corresponding note-on)
Controller, ///< a MIDI CC message
PitchBend, ///< a 14-bit pitch-bend message
Aftertouch, ///< an aftertouch message (both channel aftertouch and polyphonic aftertouch)
AllNotesOff, ///< an all notes off message.
SongPosition, ///< the position of the DAW transport
MidiStart, ///< indicated the start of the playback in the DAW
MidiStop, ///< indicates the stop of the DAW playback
VolumeFade, ///< a volume fade that is applied to all voices started with the given EventID
PitchFade, ///< a pitch fade that is applied to all voices started with the given EventID
TimerEvent, ///< this event will fire the onTimer callback of MIDI Processors.
ProgramChange, ///< the MIDI ProgramChange message.
numTypes
};
/** Creates an empty HiseEvent. */
HiseEvent() {};
/** Creates a HiseEvent from a MIDI message. */
HiseEvent(const MidiMessage& message);
/** Creates a HiseEvent with the given data. */
HiseEvent(Type type_, uint8 number_, uint8 value_, uint8 channel_ = 1);
/** Creates a bit-wise copy of another event. */
HiseEvent(const HiseEvent &other) noexcept;
/** Converts the HiseEvent back to a MidiMessage. This isn't lossless obviously. */
MidiMessage toMidiMesage() const;
/** Allows using the empty check in a scoped if-condition. */
explicit operator bool() const noexcept { return isEmpty(); }
/** checks whether the event is equal to another. This checks for
bit-equality. */
bool operator==(const HiseEvent &other) const;
/** Swaps the event with another. */
void swapWith(HiseEvent &other);
/** Returns the Type of the HiseEvent. */
Type getType() const noexcept { return type; }
/** Returns a String representation of the type. */
String getTypeAsString() const noexcept;
/** Changes the type. Don't use this unless you know why. */
void setType(Type t) noexcept { type = t; }
/** Checks if the message was marked as ignored (by a script). */
bool isIgnored() const noexcept;;
/** Ignores the event. Ignored events will not be processed, but remain in the buffer (they are not cleared). */
void ignoreEvent(bool shouldBeIgnored) noexcept;;
/** Returns the event ID of the message. The event IDs will be automatically created
by HISE when it is processing the incoming MIDI messages and associates sequentially
increasing IDS for each note-on and its corresponding note-off event.
Be aware the the event ID is stored as unsigned 16 bit integer, so it will wrap around
65536. It's highly unlikely that you will hit any collisions, but you can't expect that
older notes have a higher event ID.
*/
uint16 getEventId() const noexcept{ return eventId; };
/** Sets the event ID of the HiseEvent. Normally you don't need to do this because HISE
will automatically assign this to note-on / note-off messages, but for all the types
that alter an existing event (like volume-fades), this can be used for setting the
target event.
*/
void setEventId(uint16 newEventId) noexcept{ eventId = (uint16)newEventId; };
/** If the event was created artificially by a MIDI Processor, it will call this method.
You don't need to use this yourself. */
void setArtificial() noexcept;
/** Returns true if this method was created artificially.
Events that come in as MIDI message (no matter if their origin is in an actual key
press or if there was a previous MIDI processor (like an arpeggiator) that created
it, will be flagged as "non-artificial". Events that are created within HISE are
flagged as "artificial".
This information can be useful sometimes in order to prevent endless recursive loops.
Also, the HiseEventBuffer::Iterator class can be told to skip artificial events.
*/
bool isArtificial() const noexcept;;
/** Sets the transpose amount of the given event ID.
Unlike changing the note-number directly, this method will keep the original note
number so that you don't have to process the note-off number to match the note-on.
This is the recommended way of handling all note-number processing in HISE.
*/
void setTransposeAmount(int newTransposeValue) noexcept{ transposeValue = (int8)newTransposeValue; };
/** Returns the transpose amount. Be aware that you need to take this into account when
you need the actual note-number of an HiseEvent.
*/
int getTransposeAmount() const noexcept{ return (int)transposeValue; };
/** Sets the coarse detune amount in semitones. */
void setCoarseDetune(int semiToneDetune) noexcept{ semitones = (int8)semiToneDetune; };
/** Returns the coarse detune amount in semitones. */
int getCoarseDetune() const noexcept{ return (int)semitones; }
/** Sets the fine detune amount in cents. */
void setFineDetune(int newCents) noexcept{ cents = (int8)newCents; };
/** Returns the fine detune amount int cents. */
int getFineDetune() const noexcept{ return (int)cents; };
/** Returns a ready to use pitch factor (from 0.5 ... 2.0) */
double getPitchFactorForEvent() const;
/** Returns the frequency in hertz. Uses all event properties. */
double getFrequency() const;
/** Sets the gain in decibels for this note. */
void setGain(int decibels) noexcept{ gain = (int8)decibels; };
/** returns the gain in decibels. */
int getGain() const noexcept{ return gain; };
/** Returns the gain factor (from 0...1) for the given event. */
float getGainFactor() const noexcept { return Decibels::decibelsToGain((float)gain); };
/** Creates a volume fade.
@param eventId the event ID that this fade should be applied to.
@param fadeTimeMilliseconds the fade time (it will be a linear fade).
@targetValue the target gain in decibels.
*/
static HiseEvent createVolumeFade(uint16 eventId, int fadeTimeMilliseconds, int8 targetValue);
/** Creates a pitch fade.
@param eventID the ID of the event that will be changed.
@param fadeTimeMilliseconds the length of the fade.
@param coarseTune the target pitch in semitones
@param fineTune the target pitch detune in cent.
*/
static HiseEvent createPitchFade(uint16 eventId, int fadeTimeMilliseconds, int8 coarseTune, int8 fineTune);
/** Creates a timer event.
@param timerIndex There are 4 timer slots per sound generator and this will
contain the index (0-3).
@param offset the sample offset within the current buffer [0 - buffer size).
*/
static HiseEvent createTimerEvent(uint8 timerIndex, int offset);
/** Returns true if the event is a volume fade. */
bool isVolumeFade() const noexcept{ return type == Type::VolumeFade; };
/** Returns true if the event is a pitch fade. */
bool isPitchFade() const noexcept { return type == Type::PitchFade; };
/** Returns the fade time for both pitch and volume fades. */
int getFadeTime() const noexcept{ return getPitchWheelValue(); };
/** Returns true if the event is a timer event. */
bool isTimerEvent() const noexcept { return type == Type::TimerEvent; };
/** Returns the index of the timer slot. */
int getTimerIndex() const noexcept { return channel; }
// ========================================================================================================================== MIDI Message methods
/** Returns the timestamp of the message. The timestamp is the offset from
the current buffer start. If the timestamp is bigger than the current
buffer size, the message will be delayed until the buffer range contains
the time stamp.
*/
int getTimeStamp() const noexcept;;
/** Sets the timestamp to a sample offset in the future. */
void setTimeStamp(int newTimestamp) noexcept;
template <int Alignment> void alignToRaster(int maxTimestamp) noexcept
{
int thisTimeStamp = getTimeStamp();
const int odd = thisTimeStamp % (int)Alignment;
constexpr int half = static_cast<int>(Alignment) / 2;
int roundUpValue = (int)(static_cast<int>(odd > half) * static_cast<int>(Alignment));
int delta = roundUpValue - odd;
thisTimeStamp += delta;
int limitRoundDownValue = static_cast<int>(thisTimeStamp >= maxTimestamp) * static_cast<int>(Alignment);
thisTimeStamp -= limitRoundDownValue;
setTimeStamp(thisTimeStamp);
}
/** Adds the delta value to the timestamp. */
void addToTimeStamp(int delta) noexcept;
/** Returns the MIDI channel. */
int getChannel() const noexcept{ return (int)channel; };
/** Sets the MIDI channel. Note that in HISE you have 256 MIDI channels. */
void setChannel(int newChannelNumber) noexcept{ channel = (uint8)newChannelNumber; };
/** Copied from MidiMessage. */
bool isNoteOn(bool returnTrueForVelocity0 = false) const noexcept;;
/** Copied from MidiMessage. */
bool isNoteOff() const noexcept { return type == Type::NoteOff; }
/** Copied from MidiMessage. */
bool isNoteOnOrOff() const noexcept { return type == Type::NoteOn || type == Type::NoteOff; };
/** Copied from MidiMessage. */
int getNoteNumber() const noexcept{ return (int)number; };
/** Copied from MidiMessage. */
void setNoteNumber(int newNoteNumber) noexcept;;
/** Copied from MidiMessage. */
uint8 getVelocity() const noexcept{ return value; };
/** Copied from MidiMessage. */
float getFloatVelocity() const noexcept{ return (float)value / 127.0f; }
/** Copied from MidiMessage. */
void setVelocity(uint8 newVelocity) noexcept{ value = newVelocity; };
/** Copied from MidiMessage. */
bool isPitchWheel() const noexcept{ return type == Type::PitchBend; };
/** Copied from MidiMessage. */
int getPitchWheelValue() const noexcept;;
/** Copied from MidiMessage. */
void setPitchWheelValue(int position) noexcept;;
/** Sets the fade time for the event type. Only valid for VolumeFade and PitchFade types. */
void setFadeTime(int fadeTime) noexcept
{
setPitchWheelValue(fadeTime);
}
/** Adds a offset to the event. Unlike the timestamp, this will not delay the
event to the future, but tell the sound generator to skip the given amount
when the voice starts. This can be used for eg. skipping the attack phase of samples. */
void setStartOffset(uint16 startOffset) noexcept;
/** Returns the start offset of the event. */
uint16 getStartOffset() const noexcept;;
/** Copied from MidiMessage. */
bool isChannelPressure() const noexcept{ return type == Type::Aftertouch; };
/** Copied from MidiMessage. */
int getChannelPressureValue() const noexcept{ return value; };
/** Copied from MidiMessage. */
void setChannelPressureValue(int pressure) noexcept{ value = (uint8)pressure; };
/** Copied from MidiMessage. */
bool isAftertouch() const noexcept { return type == Type::Aftertouch; };
/** Copied from MidiMessage. */
int getAfterTouchValue() const noexcept { return (uint8)value; };
/** Copied from MidiMessage. */
void setAfterTouchValue(int noteNumber, int aftertouchAmount) noexcept{ number = (uint8)noteNumber; value = (uint8)aftertouchAmount; };
/** Copied from MidiMessage. */
bool isController() const noexcept{ return type == Type::Controller; }
/** Copied from MidiMessage. */
bool isControllerOfType(int controllerType) const noexcept{ return type == Type::Controller && controllerType == (int)number; };
/** Copied from MidiMessage. */
int getControllerNumber() const noexcept{ return number; };
/** Copied from MidiMessage. */
int getControllerValue() const noexcept{ return value; };
/** Copied from MidiMessage. */
void setControllerNumber(int controllerNumber) noexcept{ number = (uint8)controllerNumber; };
/** Copied from MidiMessage. */
void setControllerValue(int controllerValue) noexcept{ value = (uint8)controllerValue; };
/** Copied from MidiMessage. */
bool isProgramChange() const noexcept { return type == Type::ProgramChange; };
/** Copied from MidiMessage. */
int getProgramChangeNumber() const noexcept { return number; };
/** Returns true if the HiseEvent is empty. */
bool isEmpty() const noexcept{ return type == Type::Empty; };
/** Copied from MidiMessage. */
bool isAllNotesOff() const noexcept{ return type == Type::AllNotesOff; };
/** Copied from MidiMessage. */
bool isMidiStart() const noexcept{ return type == Type::MidiStart; };
/** Copied from MidiMessage. */
bool isMidiStop() const noexcept{ return type == Type::MidiStop; };
/** Copied from MidiMessage. */
bool isSongPositionPointer() const noexcept{ return type == Type::SongPosition; };
/** Copied from MidiMessage. */
int getSongPositionPointerMidiBeat() const noexcept{ return number | (value << 7); };
/** Copied from MidiMessage. */
void setSongPositionValue(int positionInMidiBeats);
/** This clears the events using the fast memset operation. */
static void clear(HiseEvent* eventToClear, int numEvents = 1)
{
memset(eventToClear, 0, sizeof(HiseEvent) * numEvents);
}
struct ChannelFilterData
{
ChannelFilterData():
enableAllChannels(true)
{
for (int i = 0; i < 16; i++) activeChannels[i] = false;
}
void restoreFromData(int data)
{
BigInteger d(data);
enableAllChannels = d[0];
for (int i = 0; i < 16; i++) activeChannels[i] = d[i + 1];
}
int exportData() const
{
BigInteger d;
d.setBit(0, enableAllChannels);
for (int i = 0; i < 16; i++) d.setBit(i + 1, activeChannels[i]);
return d.toInteger();
}
void setEnableAllChannels(bool shouldBeEnabled) noexcept { enableAllChannels = shouldBeEnabled; }
bool areAllChannelsEnabled() const noexcept { return enableAllChannels; }
void setEnableMidiChannel(int channelIndex, bool shouldBeEnabled) noexcept
{
activeChannels[channelIndex] = shouldBeEnabled;
}
bool isChannelEnabled(int channelIndex) const noexcept
{
return activeChannels[channelIndex];
}
bool activeChannels[16];
bool enableAllChannels;
};
private:
Type type = Type::Empty; // DWord 1
uint8 channel = 0;
uint8 number = 0;
uint8 value = 0;
int8 transposeValue = 0; // DWord 2
int8 gain = 0;
int8 semitones = 0;
int8 cents = 0;
uint16 eventId = 0; // DWord 3
uint16 startOffset = 0;
uint32 timestamp = 0;
};
#define HISE_EVENT_BUFFER_SIZE 256
/** The buffer type for the HiseEvent.
*/
class HiseEventBuffer
{
public:
/** A simple stack type with 16 slots. */
class EventStack
{
public:
EventStack()
{
clear();
}
/** Inserts an event. */
void push(const HiseEvent &newEvent)
{
size = jmin<int>(16, size + 1);
data[size-1] = HiseEvent(newEvent);
}
/** Removes and returns an event. */
HiseEvent pop()
{
if (size == 0) return HiseEvent();
HiseEvent returnEvent = data[size - 1];
data[size - 1] = HiseEvent();
size = jmax<int>(0, size-1);
return returnEvent;
}
bool peekNoteOnForEventId(uint16 eventId, HiseEvent& eventToFill)
{
for (int i = 0; i < size; i++)
{
if (data[i].getEventId() == eventId)
{
eventToFill = data[i];
return true;
}
}
return false;
}
bool popNoteOnForEventId(uint16 eventId, HiseEvent& eventToFill)
{
int thisIndex = -1;
for (int i = 0; i < size; i++)
{
if (data[i].getEventId() == eventId)
{
thisIndex = i;
break;
}
}
if (thisIndex == -1) return false;
eventToFill = data[thisIndex];
for (int i = thisIndex; i < size-1; i++)
{
data[i] = data[i + 1];
}
data[size-1] = HiseEvent();
size--;
return true;
}
void clear()
{
for (int i = 0; i < 16; i++)
data[i] = HiseEvent();
size = 0;
}
const HiseEvent* peek() const
{
if (size == 0) return nullptr;
return &data[size - 1];
}
HiseEvent* peek()
{
if (size == 0) return nullptr;
return &data[size - 1];
}
int getNumUsed() { return size; };
private:
HiseEvent data[16];
int size = 0;
};
HiseEventBuffer();
bool operator==(const HiseEventBuffer& other)
{
if (other.getNumUsed() != numUsed) return false;
const HiseEventBuffer::Iterator iter(other);
for (int i = 0; i < numUsed; i++)
{
const HiseEvent* e = iter.getNextConstEventPointer();
if (e == nullptr)
{
jassertfalse;
return false;
}
if (!(*e == buffer[i]))
return false;
}
return true;
}
/** Clears the buffer. */
void clear();
/** checks if the buffer is empty. */
bool isEmpty() const noexcept{ return numUsed == 0; };
/** Returns the number of events in this buffer. */
int getNumUsed() const { return numUsed; }
HiseEvent getEvent(int index) const;
HiseEvent popEvent(int index);
void subtractFromTimeStamps(int delta);
void moveEventsBelow(HiseEventBuffer& targetBuffer, int highestTimestamp);
void moveEventsAbove(HiseEventBuffer& targetBuffer, int lowestTimestamp);
void copyFrom(const HiseEventBuffer& otherBuffer);
void addEvent(const HiseEvent& hiseEvent);
void addEvent(const MidiMessage& midiMessage, int sampleNumber);
void addEvents(const MidiBuffer& otherBuffer);
void addEvents(const HiseEventBuffer &otherBuffer);
void sortTimestamps();
template <int Alignment> void alignEventsToRaster(int maxTimeStamp)
{
for (auto& e : *this)
e.alignToRaster<Alignment>(maxTimeStamp);
}
bool timeStampsAreSorted() const;
int getMinTimeStamp() const;
int getMaxTimeStamp() const;
struct CopyHelpers
{
static void copyEvents(HiseEvent* destination, const HiseEvent* source, int numElements)
{
memcpy(destination, source, sizeof(HiseEvent) * numElements);
}
static void copyEvents(HiseEventBuffer &destination, int offsetInDestination, const HiseEventBuffer& source, int offsetInSource, int numElements)
{
memcpy(destination.buffer + offsetInDestination, source.buffer + offsetInSource, sizeof(HiseEvent) * numElements);
}
};
/** A iterator type for the HiseEventBuffer. */
class Iterator
{
public:
/** Creates an iterator which allows access to the HiseEvents in the buffer. */
Iterator(const HiseEventBuffer& b);
/** Saves the next event into the given HiseEvent address.
@param e - the event adress. Remember this will copy the event. If you want to alter the event in the buffer,
use the other iterator methods which return a pointer to the element in the buffer.
@param samplePosition - the timestamp position. This will be sorted and compatible to the MidiBuffer::Iterator method.
@param skipIgnoredEvents - skips HiseEvents which are ignored.
@param skipArtificialNotes - skips artificial notes. Use this to avoid loops when processing HiseEventBuffers.
*/
bool getNextEvent(HiseEvent& e, int &samplePosition, bool skipIgnoredEvents=false, bool skipArtificialEvents=false) const;
/** Returns a read pointer to the event in the buffer. */
const HiseEvent* getNextConstEventPointer(bool skipIgnoredEvents=false, bool skipArtificialNotes = false) const;
/** Returns a write pointer to the event in the buffer. */
HiseEvent* getNextEventPointer(bool skipIgnoredEvents=false, bool skipArtificialNotes = false);
private:
HiseEventBuffer *buffer;
mutable int index;
};
/** compatibility for standard C++ type iterators. */
inline HiseEvent* begin() const noexcept
{
return const_cast<HiseEvent*>(buffer);
}
/** compatibility for standard C++ type iterators. */
inline HiseEvent* end() const noexcept
{
return const_cast<HiseEvent*>(buffer + numUsed);
}
private:
friend class Iterator;
void insertEventAtPosition(const HiseEvent& e, int positionInBuffer);
HiseEvent buffer[HISE_EVENT_BUFFER_SIZE];
int numUsed = 0;
};
/** This class will iterate over incoming MIDI messages, and transform them
* into HiseEvents with a succesive index for note-on / note-off messages.
*
* Normally, you won't use this class, but rather benefit from it in the MIDI
* processing world using Message.getEventId(), but there are a few methods
* that can access these things directly.
*/
class EventIdHandler
{
public:
// ===========================================================================================================
EventIdHandler(HiseEventBuffer& masterBuffer_);
~EventIdHandler();
// ===========================================================================================================
/** Fills note on / note off messages with the event id and returns the current value for external storage. */
void handleEventIds();
/** Removes the matching noteOn event for the given noteOff event. */
uint16 getEventIdForNoteOff(const HiseEvent ¬eOffEvent);
/** Returns the matching note on event for the given note off event (but doesn't remove it). */
HiseEvent peekNoteOn(const HiseEvent& noteOffEvent);
/** Adds the artificial event to the internal stack array. */
void pushArtificialNoteOn(HiseEvent& noteOnEvent) noexcept;
/** Searches all active note on events and returns the one with the given event id. */
HiseEvent popNoteOnFromEventId(uint16 eventId);
// ===========================================================================================================
private:
const HiseEventBuffer &masterBuffer;
HeapBlock<HiseEvent> artificialEvents;
uint16 lastArtificialEventIds[16][128];
HiseEvent realNoteOnEvents[16][128];
uint16 currentEventId;
UnorderedStack<HiseEvent, 256> overlappingNoteOns;
// ===========================================================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EventIdHandler)
};
} // namespace hise
#endif // HISEEVENTBUFFER_H_INCLUDED