iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugMidi.h
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#pragma once
12
19#include <cassert>
20#include <cstdint>
21#include <cstdio>
22#include <algorithm>
23
24#include "IPlugLogger.h"
25
26BEGIN_IPLUG_NAMESPACE
27
31{
32 int mOffset;
33 uint8_t mStatus, mData1, mData2;
34
37 {
38 kNone = 0,
39 kNoteOff = 8,
40 kNoteOn = 9,
41 kPolyAftertouch = 10,
42 kControlChange = 11,
43 kProgramChange = 12,
44 kChannelAftertouch = 13,
45 kPitchWheel = 14
46 };
47
50 {
51 kNoCC = -1,
52 kModWheel = 1,
53 kBreathController = 2,
54 kUndefined003 = 3,
55 kFootController = 4,
56 kPortamentoTime = 5,
57 kChannelVolume = 7,
58 kBalance = 8,
59 kUndefined009 = 9,
60 kPan = 10,
61 kExpressionController = 11,
62 kEffectControl1 = 12,
63 kEffectControl2 = 13,
64 kUndefined014 = 14,
65 kUndefined015 = 15,
66 kGeneralPurposeController1 = 16,
67 kGeneralPurposeController2 = 17,
68 kGeneralPurposeController3 = 18,
69 kGeneralPurposeController4 = 19,
70 kUndefined020 = 20,
71 kUndefined021 = 21,
72 kUndefined022 = 22,
73 kUndefined023 = 23,
74 kUndefined024 = 24,
75 kUndefined025 = 25,
76 kUndefined026 = 26,
77 kUndefined027 = 27,
78 kUndefined028 = 28,
79 kUndefined029 = 29,
80 kUndefined030 = 30,
81 kUndefined031 = 31,
82 kSustainOnOff = 64,
83 kPortamentoOnOff = 65,
84 kSustenutoOnOff = 66,
85 kSoftPedalOnOff = 67,
86 kLegatoOnOff = 68,
87 kHold2OnOff = 69,
88 kSoundVariation = 70,
89 kResonance = 71,
90 kReleaseTime = 72,
91 kAttackTime = 73,
92 kCutoffFrequency = 74,
93 kDecayTime = 75,
94 kVibratoRate = 76,
95 kVibratoDepth = 77,
96 kVibratoDelay = 78,
97 kSoundControllerUndefined = 79,
98 kUndefined085 = 85,
99 kUndefined086 = 86,
100 kUndefined087 = 87,
101 kUndefined088 = 88,
102 kUndefined089 = 89,
103 kUndefined090 = 90,
104 kTremoloDepth = 92,
105 kChorusDepth = 93,
106 kPhaserDepth = 95,
107 kUndefined102 = 102,
108 kUndefined103 = 103,
109 kUndefined104 = 104,
110 kUndefined105 = 105,
111 kUndefined106 = 106,
112 kUndefined107 = 107,
113 kUndefined108 = 108,
114 kUndefined109 = 109,
115 kUndefined110 = 110,
116 kUndefined111 = 111,
117 kUndefined112 = 112,
118 kUndefined113 = 113,
119 kUndefined114 = 114,
120 kUndefined115 = 115,
121 kUndefined116 = 116,
122 kUndefined117 = 117,
123 kUndefined118 = 118,
124 kUndefined119 = 119,
125 kAllNotesOff = 123
126 };
127
133 IMidiMsg(int offset = 0, uint8_t status = 0, uint8_t data1 = 0, uint8_t data2 = 0)
134 : mOffset(offset)
135 , mStatus(status)
136 , mData1(data1)
137 , mData2(data2)
138 {}
139
145 void MakeNoteOnMsg(int noteNumber, int velocity, int offset, int channel = 0)
146 {
147 Clear();
148 mStatus = channel | (kNoteOn << 4) ;
149 mData1 = noteNumber;
150 mData2 = velocity;
151 mOffset = offset;
152 }
153
158 void MakeNoteOffMsg(int noteNumber, int offset, int channel = 0)
159 {
160 Clear();
161 mStatus = channel | (kNoteOff << 4);
162 mData1 = noteNumber;
163 mOffset = offset;
164 }
165
170 void MakePitchWheelMsg(double value, int channel = 0, int offset = 0)
171 {
172 Clear();
173 mStatus = channel | (kPitchWheel << 4);
174 int i = 8192 + (int) (value * 8192.0);
175 i = std::min(std::max(i, 0), 16383);
176 mData2 = i>>7;
177 mData1 = i&0x7F;
178 mOffset = offset;
179 }
180
186 void MakeControlChangeMsg(EControlChangeMsg idx, double value, int channel = 0, int offset = 0)
187 {
188 Clear();
189 mStatus = channel | (kControlChange << 4);
190 mData1 = idx;
191 mData2 = (int) (value * 127.0);
192 mOffset = offset;
193 }
194
199 void MakeProgramChange(int program, int channel = 0, int offset = 0)
200 {
201 Clear();
202 mStatus = channel | (kProgramChange << 4);
203 mData1 = program;
204 mOffset = offset;
205 }
206
211 void MakeChannelATMsg(int pressure, int offset, int channel)
212 {
213 Clear();
214 mStatus = channel | (kChannelAftertouch << 4);
215 mData1 = pressure;
216 mData2 = 0;
217 mOffset = offset;
218 }
219
225 void MakePolyATMsg(int noteNumber, int pressure, int offset, int channel)
226 {
227 Clear();
228 mStatus = channel | (kPolyAftertouch << 4);
229 mData1 = noteNumber;
230 mData2 = pressure;
231 mOffset = offset;
232 }
233
236 int Channel() const
237 {
238 return mStatus & 0x0F;
239 }
240
244 {
245 unsigned int e = mStatus >> 4;
246 if (e < kNoteOff || e > kPitchWheel)
247 {
248 return kNone;
249 }
250 return (EStatusMsg) e;
251 }
252
255 int NoteNumber() const
256 {
257 switch (StatusMsg())
258 {
259 case kNoteOn:
260 case kNoteOff:
261 case kPolyAftertouch:
262 return mData1;
263 default:
264 return -1;
265 }
266 }
267
270 int Velocity() const
271 {
272 switch (StatusMsg())
273 {
274 case kNoteOn:
275 case kNoteOff:
276 return mData2;
277 default:
278 return -1;
279 }
280 }
281
284 int PolyAfterTouch() const
285 {
286 switch (StatusMsg())
287 {
288 case kPolyAftertouch:
289 return mData2;
290 default:
291 return -1;
292 }
293 }
294
298 {
299 switch (StatusMsg())
300 {
301 case kChannelAftertouch:
302 return mData1;
303 default:
304 return -1;
305 }
306 }
307
310 int Program() const
311 {
312 if (StatusMsg() == kProgramChange)
313 {
314 return mData1;
315 }
316 return -1;
317 }
318
321 double PitchWheel() const
322 {
323 if (StatusMsg() == kPitchWheel)
324 {
325 int iVal = (mData2 << 7) + mData1;
326 return static_cast<double>(iVal - 8192) / 8192.0;
327 }
328 return 0.0;
329 }
330
334 {
335 return (EControlChangeMsg) mData1;
336 }
337
341 {
342 if (StatusMsg() == kControlChange && ControlChangeIdx() == idx)
343 {
344 return (double) mData2 / 127.0;
345 }
346 return -1.0;
347 }
348
352 static bool ControlChangeOnOff(double msgValue)
353 {
354 return (msgValue >= 0.5);
355 }
356
358 void Clear()
359 {
360 mOffset = 0;
361 mStatus = mData1 = mData2 = 0;
362 }
363
367 static const char* StatusMsgStr(EStatusMsg msg)
368 {
369 switch (msg)
370 {
371 case kNone: return "none";
372 case kNoteOff: return "noteoff";
373 case kNoteOn: return "noteon";
374 case kPolyAftertouch: return "aftertouch";
375 case kControlChange: return "controlchange";
376 case kProgramChange: return "programchange";
377 case kChannelAftertouch: return "channelaftertouch";
378 case kPitchWheel: return "pitchwheel";
379 default: return "unknown";
380 };
381 }
382
386 static const char* CCNameStr(int idx)
387 {
388 static const char* ccNameStrs[128] =
389 {
390 "BankSel.MSB",
391 "Modulation",
392 "BreathCtrl",
393 "Contr. 3",
394 "Foot Ctrl",
395 "Porta.Time",
396 "DataEntMSB",
397 "MainVolume",
398 "Balance",
399 "Contr. 9",
400 "Pan",
401 "Expression",
402 "FXControl1",
403 "FXControl2",
404 "Contr. 14",
405 "Contr. 15",
406 "Gen.Purp.1",
407 "Gen.Purp.2",
408 "Gen.Purp.3",
409 "Gen.Purp.4",
410 "Contr. 20",
411 "Contr. 21",
412 "Contr. 22",
413 "Contr. 23",
414 "Contr. 24",
415 "Contr. 25",
416 "Contr. 26",
417 "Contr. 27",
418 "Contr. 28",
419 "Contr. 29",
420 "Contr. 30",
421 "Contr. 31",
422 "BankSel.LSB",
423 "Modul. LSB",
424 "BrthCt LSB",
425 "Contr. 35",
426 "FootCt LSB",
427 "Port.T LSB",
428 "DataEntLSB",
429 "MainVolLSB",
430 "BalanceLSB",
431 "Contr. 41",
432 "Pan LSB",
433 "Expr. LSB",
434 "Contr. 44",
435 "Contr. 45",
436 "Contr. 46",
437 "Contr. 47",
438 "Gen.P.1LSB",
439 "Gen.P.2LSB",
440 "Gen.P.3LSB",
441 "Gen.P.4LSB",
442 "Contr. 52",
443 "Contr. 53",
444 "Contr. 54",
445 "Contr. 55",
446 "Contr. 56",
447 "Contr. 57",
448 "Contr. 58",
449 "Contr. 59",
450 "Contr. 60",
451 "Contr. 61",
452 "Contr. 62",
453 "Contr. 63",
454 "Damper Ped",
455 "Porta. Ped",
456 "Sostenuto ",
457 "Soft Pedal",
458 "Legato Sw",
459 "Hold 2",
460 "SoundCont 1",
461 "SoundCont 2",
462 "SoundCont 3",
463 "SoundCont 4",
464 "SoundCont 5",
465 "SoundCont 6",
466 "SoundCont 7",
467 "SoundCont 8",
468 "SoundCont 9",
469 "SoundCont 10",
470 "Gen.Purp.5",
471 "Gen.Purp.6",
472 "Gen.Purp.7",
473 "Gen.Purp.8",
474 "Portamento",
475 "Contr. 85",
476 "Contr. 86",
477 "Contr. 87",
478 "Contr. 88",
479 "Contr. 89",
480 "Contr. 90",
481 "FX 1 Depth",
482 "FX 2 Depth",
483 "FX 3 Depth",
484 "FX 4 Depth",
485 "FX 5 Depth",
486 "Data Incr",
487 "Data Decr",
488 "Non-RegLSB",
489 "Non-RegMSB",
490 "Reg LSB",
491 "Reg MSB",
492 "Contr. 102",
493 "Contr. 103",
494 "Contr. 104",
495 "Contr. 105",
496 "Contr. 106",
497 "Contr. 107",
498 "Contr. 108",
499 "Contr. 109",
500 "Contr. 110",
501 "Contr. 111",
502 "Contr. 112",
503 "Contr. 113",
504 "Contr. 114",
505 "Contr. 115",
506 "Contr. 116",
507 "Contr. 117",
508 "Contr. 118",
509 "Contr. 119",
510 "Contr. 120",
511 "Reset Ctrl",
512 "Local Ctrl",
513 "AllNoteOff",
514 "OmniModOff",
515 "OmniModeOn",
516 "MonoModeOn",
517 "PolyModeOn"
518 };
519
520 return ccNameStrs[idx];
521 }
522
524 void LogMsg()
525 {
526 Trace(TRACELOC, "midi:(%s:%d:%d:%d)", StatusMsgStr(StatusMsg()), Channel(), mData1, mData2);
527 }
528
530 void PrintMsg() const
531 {
532 DBGMSG("midi: offset %i, (%s:%d:%d:%d)\n", mOffset, StatusMsgStr(StatusMsg()), Channel(), mData1, mData2);
533 }
534};
535
538struct ISysEx
539{
540 int mOffset, mSize;
541 const uint8_t* mData;
542
547 ISysEx(int offset = 0, const uint8_t* pData = nullptr, int size = 0)
548 : mOffset(offset)
549 , mSize(size)
550 , mData(pData)
551 {}
552
554 void Clear()
555 {
556 mOffset = mSize = 0;
557 mData = NULL;
558 }
559
566 char* SysExStr(char* str, int maxLen, const uint8_t* pData, int size)
567 {
568 assert(str != NULL && maxLen >= 3);
569
570 if (!pData || !size) {
571 *str = '\0';
572 return str;
573 }
574
575 char* pStr = str;
576 int n = maxLen / 3;
577 if (n > size) n = size;
578 for (int i = 0; i < n; ++i, ++pData) {
579 snprintf(pStr, maxLen, "%02X", (int)*pData);
580 pStr += 2;
581 *pStr++ = ' ';
582 }
583 *--pStr = '\0';
584
585 return str;
586 }
587
589 void LogMsg()
590 {
591 char str[96];
592 Trace(TRACELOC, "sysex:(%d:%s)", mSize, SysExStr(str, sizeof(str), mData, mSize));
593 }
594
595};
596
597/*
598
599IMidiQueue
600(c) Theo Niessink 2009-2011
601<http://www.taletn.com/>
602
603
604This software is provided 'as-is', without any express or implied
605warranty. In no event will the authors be held liable for any damages
606arising from the use of this software.
607
608Permission is granted to anyone to use this software for any purpose,
609including commercial applications, and to alter it and redistribute it
610freely, subject to the following restrictions:
611
6121. The origin of this software must not be misrepresented; you must not
613 claim that you wrote the original software. If you use this software in a
614 product, an acknowledgment in the product documentation would be
615 appreciated but is not required.
6162. Altered source versions must be plainly marked as such, and must not be
617 misrepresented as being the original software.
6183. This notice may not be removed or altered from any source distribution.
619
620
621IMidiQueue is a fast, lean & mean MIDI queue for IPlug instruments or
622effects. Here are a few code snippets showing how to implement IMidiQueue in
623an IPlug project:
624
625
626MyPlug.h:
627
628#include "WDL/IPlug/IMidiQueue.h"
629
630class MyPlug: public IPlug
631{
632protected:
633 IMidiQueue mMidiQueue;
634}
635
636
637MyPlug.cpp:
638
639void MyPlug::OnReset()
640{
641 mMidiQueue.Resize(GetBlockSize());
642}
643
644void MyPlug::ProcessMidiMsg(IMidiMsg* pMsg)
645{
646 mMidiQueue.Add(pMsg);
647}
648
649void MyPlug::ProcessBlock(double** inputs, double** outputs, int nFrames)
650{
651 for (int offset = 0; offset < nFrames; ++offset)
652 {
653 while (!mMidiQueue.Empty())
654 {
655 IMidiMsg* pMsg = mMidiQueue.Peek();
656 if (msg.mOffset > offset) break;
657
658 // To-do: Handle the MIDI message
659
660 mMidiQueue.Remove();
661 }
662
663 // To-do: Process audio
664
665 }
666 mMidiQueue.Flush(nFrames);
667}
668
669*/
670
671#ifndef DEFAULT_BLOCK_SIZE
672 #define DEFAULT_BLOCK_SIZE 512
673#endif
674
678{
679public:
680 IMidiQueue(int size = DEFAULT_BLOCK_SIZE)
681 : mBuf(NULL), mSize(0), mGrow(Granulize(size)), mFront(0), mBack(0)
682 {
683 Expand();
684 }
685
687 {
688 free(mBuf);
689 }
690
691 // Adds a MIDI message at the back of the queue. If the queue is full,
692 // it will automatically expand itself.
693 void Add(const IMidiMsg& msg)
694 {
695 if (mBack >= mSize)
696 {
697 if (mFront > 0)
698 Compact();
699 else if (!Expand()) return;
700 }
701
702#ifndef DONT_SORT_IMIDIQUEUE
703 // Insert the MIDI message at the right offset.
704 if (mBack > mFront && msg.mOffset < mBuf[mBack - 1].mOffset)
705 {
706 int i = mBack - 2;
707 while (i >= mFront && msg.mOffset < mBuf[i].mOffset) --i;
708 i++;
709 memmove(&mBuf[i + 1], &mBuf[i], (mBack - i) * sizeof(IMidiMsg));
710 mBuf[i] = msg;
711 }
712 else
713#endif
714 mBuf[mBack] = msg;
715 ++mBack;
716 }
717
718 // Removes a MIDI message from the front of the queue (but does *not*
719 // free up its space until Compact() is called).
720 inline void Remove() { ++mFront; }
721
722 // Returns true if the queue is empty.
723 inline bool Empty() const { return mFront == mBack; }
724
725 // Returns the number of MIDI messages in the queue.
726 inline int ToDo() const { return mBack - mFront; }
727
728 // Returns the number of MIDI messages for which memory has already been
729 // allocated.
730 inline int GetSize() const { return mSize; }
731
732 // Returns the "next" MIDI message (all the way in the front of the
733 // queue), but does *not* remove it from the queue.
734 inline IMidiMsg& Peek() const { return mBuf[mFront]; }
735
736 // Moves back MIDI messages all the way to the front of the queue, thus
737 // freeing up space at the back, and updates the sample offset of the
738 // remaining MIDI messages by substracting nFrames.
739 inline void Flush(int nFrames)
740 {
741 // Move everything all the way to the front.
742 if (mFront > 0) Compact();
743
744 // Update the sample offset.
745 for (int i = 0; i < mBack; ++i) mBuf[i].mOffset -= nFrames;
746 }
747
748 // Clears the queue.
749 inline void Clear() { mFront = mBack = 0; }
750
751 // Resizes (grows or shrinks) the queue, returns the new size.
752 int Resize(int size)
753 {
754 if (mFront > 0) Compact();
755 mGrow = size = Granulize(size);
756 // Don't shrink below the number of currently queued MIDI messages.
757 if (size < mBack) size = Granulize(mBack);
758 if (size == mSize) return mSize;
759
760 void* buf = realloc(mBuf, size * sizeof(IMidiMsg));
761 if (!buf) return mSize;
762
763 mBuf = (IMidiMsg*)buf;
764 mSize = size;
765 return size;
766 }
767
768protected:
769 // Automatically expands the queue.
770 bool Expand()
771 {
772 if (!mGrow) return false;
773 int size = (mSize / mGrow + 1) * mGrow;
774
775 void* buf = realloc(mBuf, size * sizeof(IMidiMsg));
776 if (!buf) return false;
777
778 mBuf = (IMidiMsg*)buf;
779 mSize = size;
780 return true;
781 }
782
783 // Moves everything all the way to the front.
784 inline void Compact()
785 {
786 mBack -= mFront;
787 if (mBack > 0) memmove(&mBuf[0], &mBuf[mFront], mBack * sizeof(IMidiMsg));
788 mFront = 0;
789 }
790
791 // Rounds the MIDI queue size up to the next 4 kB memory page size.
792 inline int Granulize(int size) const
793 {
794 int bytes = size * sizeof(IMidiMsg);
795 int rest = bytes % 4096;
796 if (rest) size = (bytes - rest + 4096) / sizeof(IMidiMsg);
797 return size;
798 }
799
800 IMidiMsg* mBuf;
801
802 int mSize, mGrow;
803 int mFront, mBack;
804};
805
806END_IPLUG_NAMESPACE
IPlug logging a.k.a tracing functionality.
A class to help with queuing timestamped MIDI messages.
Definition: IPlugMidi.h:678
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31
void MakeNoteOnMsg(int noteNumber, int velocity, int offset, int channel=0)
Make a Note On message.
Definition: IPlugMidi.h:145
static const char * StatusMsgStr(EStatusMsg msg)
Get the Status Message as a CString.
Definition: IPlugMidi.h:367
double ControlChange(EControlChangeMsg idx) const
Get the value of a CC message.
Definition: IPlugMidi.h:340
int Channel() const
Gets the channel of a MIDI message.
Definition: IPlugMidi.h:236
IMidiMsg(int offset=0, uint8_t status=0, uint8_t data1=0, uint8_t data2=0)
Create an IMidiMsg, an abstraction for a MIDI message.
Definition: IPlugMidi.h:133
void MakeNoteOffMsg(int noteNumber, int offset, int channel=0)
Make a Note Off message.
Definition: IPlugMidi.h:158
void MakePitchWheelMsg(double value, int channel=0, int offset=0)
Create a pitch wheel/bend message.
Definition: IPlugMidi.h:170
int Velocity() const
Get the velocity value of a NoteOn/NoteOff message.
Definition: IPlugMidi.h:270
void LogMsg()
Log a message (TRACER BUILDS)
Definition: IPlugMidi.h:524
void MakeControlChangeMsg(EControlChangeMsg idx, double value, int channel=0, int offset=0)
Create a CC message.
Definition: IPlugMidi.h:186
int Program() const
Get the program index from a Program Change message.
Definition: IPlugMidi.h:310
EControlChangeMsg
Constants for MIDI CC messages.
Definition: IPlugMidi.h:50
static const char * CCNameStr(int idx)
Get the CC name as a CString.
Definition: IPlugMidi.h:386
void MakeProgramChange(int program, int channel=0, int offset=0)
Create a Program Change message.
Definition: IPlugMidi.h:199
void Clear()
Clear the message.
Definition: IPlugMidi.h:358
EStatusMsg
Constants for the status byte of a MIDI message.
Definition: IPlugMidi.h:37
void MakeChannelATMsg(int pressure, int offset, int channel)
Create a Channel AfterTouch message.
Definition: IPlugMidi.h:211
void MakePolyATMsg(int noteNumber, int pressure, int offset, int channel)
Create a Poly AfterTouch message.
Definition: IPlugMidi.h:225
int ChannelAfterTouch() const
Get the Pressure value from an AfterTouch message.
Definition: IPlugMidi.h:297
EControlChangeMsg ControlChangeIdx() const
Gets the controller index of a CC message.
Definition: IPlugMidi.h:333
int PolyAfterTouch() const
Get the Pressure value from a PolyAfterTouch message.
Definition: IPlugMidi.h:284
double PitchWheel() const
Get the value from a Pitchwheel message.
Definition: IPlugMidi.h:321
void PrintMsg() const
Print a message (DEBUG BUILDS)
Definition: IPlugMidi.h:530
int NoteNumber() const
Gets the MIDI note number.
Definition: IPlugMidi.h:255
static bool ControlChangeOnOff(double msgValue)
Helper to get a boolean value from a CC messages.
Definition: IPlugMidi.h:352
EStatusMsg StatusMsg() const
Gets the MIDI Status message.
Definition: IPlugMidi.h:243
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:539
ISysEx(int offset=0, const uint8_t *pData=nullptr, int size=0)
Create an ISysex.
Definition: IPlugMidi.h:547
void Clear()
Clear the data pointer and size (does not modify the external data!)
Definition: IPlugMidi.h:554
char * SysExStr(char *str, int maxLen, const uint8_t *pData, int size)
Get the bytes of a sysex message as a CString.
Definition: IPlugMidi.h:566
void LogMsg()
Log a message (TRACER BUILDS)
Definition: IPlugMidi.h:589