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
599IMidiQueueBase is a template adapted by Alex Harker from the following source
600It has been adapted to allow different types (e.g. IMidiMsg or ISysEx)
601It is then mapped to IMidiQueue as an alias
602
603 (c) Theo Niessink 2009-2011
604<http://www.taletn.com/>
605
606
607This software is provided 'as-is', without any express or implied
608warranty. In no event will the authors be held liable for any damages
609arising from the use of this software.
610
611Permission is granted to anyone to use this software for any purpose,
612including commercial applications, and to alter it and redistribute it
613freely, subject to the following restrictions:
614
6151. The origin of this software must not be misrepresented; you must not
616 claim that you wrote the original software. If you use this software in a
617 product, an acknowledgment in the product documentation would be
618 appreciated but is not required.
6192. Altered source versions must be plainly marked as such, and must not be
620 misrepresented as being the original software.
6213. This notice may not be removed or altered from any source distribution.
622
623
624IMidiQueue is a fast, lean & mean MIDI queue for IPlug instruments or
625effects. Here are a few code snippets showing how to implement IMidiQueue in
626an IPlug project:
627
628
629MyPlug.h:
630
631#include "WDL/IPlug/IMidiQueue.h"
632
633class MyPlug: public IPlug
634{
635protected:
636 IMidiQueue mMidiQueue;
637}
638
639
640MyPlug.cpp:
641
642void MyPlug::OnReset()
643{
644 mMidiQueue.Resize(GetBlockSize());
645}
646
647void MyPlug::ProcessMidiMsg(IMidiMsg* pMsg)
648{
649 mMidiQueue.Add(pMsg);
650}
651
652void MyPlug::ProcessBlock(double** inputs, double** outputs, int nFrames)
653{
654 for (int offset = 0; offset < nFrames; ++offset)
655 {
656 while (!mMidiQueue.Empty())
657 {
658 IMidiMsg* pMsg = mMidiQueue.Peek();
659 if (msg.mOffset > offset) break;
660
661 // To-do: Handle the MIDI message
662
663 mMidiQueue.Remove();
664 }
665
666 // To-do: Process audio
667
668 }
669 mMidiQueue.Flush(nFrames);
670}
671
672*/
673
674#ifndef DEFAULT_BLOCK_SIZE
675 #define DEFAULT_BLOCK_SIZE 512
676#endif
677
680template <class T>
682{
683public:
684 IMidiQueueBase(int size = DEFAULT_BLOCK_SIZE)
685 : mBuf(NULL), mSize(0), mGrow(Granulize(size)), mFront(0), mBack(0)
686 {
687 Expand();
688 }
689
691 {
692 free(mBuf);
693 }
694
695 // Adds a MIDI message at the back of the queue. If the queue is full,
696 // it will automatically expand itself.
697 void Add(const T& msg)
698 {
699 if (mBack >= mSize)
700 {
701 if (mFront > 0)
702 Compact();
703 else if (!Expand()) return;
704 }
705
706#ifndef DONT_SORT_IMIDIQUEUE
707 // Insert the MIDI message at the right offset.
708 if (mBack > mFront && msg.mOffset < mBuf[mBack - 1].mOffset)
709 {
710 int i = mBack - 2;
711 while (i >= mFront && msg.mOffset < mBuf[i].mOffset) --i;
712 i++;
713 memmove(&mBuf[i + 1], &mBuf[i], (mBack - i) * sizeof(T));
714 mBuf[i] = msg;
715 }
716 else
717#endif
718 mBuf[mBack] = msg;
719 ++mBack;
720 }
721
722 // Removes a MIDI message from the front of the queue (but does *not*
723 // free up its space until Compact() is called).
724 inline void Remove() { ++mFront; }
725
726 // Returns true if the queue is empty.
727 inline bool Empty() const { return mFront == mBack; }
728
729 // Returns the number of MIDI messages in the queue.
730 inline int ToDo() const { return mBack - mFront; }
731
732 // Returns the number of MIDI messages for which memory has already been
733 // allocated.
734 inline int GetSize() const { return mSize; }
735
736 // Returns the "next" MIDI message (all the way in the front of the
737 // queue), but does *not* remove it from the queue.
738 inline T& Peek() const { return mBuf[mFront]; }
739
740 // Moves back MIDI messages all the way to the front of the queue, thus
741 // freeing up space at the back, and updates the sample offset of the
742 // remaining MIDI messages by substracting nFrames.
743 inline void Flush(int nFrames)
744 {
745 // Move everything all the way to the front.
746 if (mFront > 0) Compact();
747
748 // Update the sample offset.
749 for (int i = 0; i < mBack; ++i) mBuf[i].mOffset -= nFrames;
750 }
751
752 // Clears the queue.
753 inline void Clear() { mFront = mBack = 0; }
754
755 // Resizes (grows or shrinks) the queue, returns the new size.
756 int Resize(int size)
757 {
758 if (mFront > 0) Compact();
759 mGrow = size = Granulize(size);
760 // Don't shrink below the number of currently queued MIDI messages.
761 if (size < mBack) size = Granulize(mBack);
762 if (size == mSize) return mSize;
763
764 void* buf = realloc(mBuf, size * sizeof(T));
765 if (!buf) return mSize;
766
767 mBuf = (T*)buf;
768 mSize = size;
769 return size;
770 }
771
772protected:
773 // Automatically expands the queue.
774 bool Expand()
775 {
776 if (!mGrow) return false;
777 int size = (mSize / mGrow + 1) * mGrow;
778
779 void* buf = realloc(mBuf, size * sizeof(T));
780 if (!buf) return false;
781
782 mBuf = (T*)buf;
783 mSize = size;
784 return true;
785 }
786
787 // Moves everything all the way to the front.
788 inline void Compact()
789 {
790 mBack -= mFront;
791 if (mBack > 0) memmove(&mBuf[0], &mBuf[mFront], mBack * sizeof(T));
792 mFront = 0;
793 }
794
795 // Rounds the MIDI queue size up to the next 4 kB memory page size.
796 inline int Granulize(int size) const
797 {
798 int bytes = size * sizeof(T);
799 int rest = bytes % 4096;
800 if (rest) size = (bytes - rest + 4096) / sizeof(T);
801 return size;
802 }
803
804 T* mBuf;
805
806 int mSize, mGrow;
807 int mFront, mBack;
808};
809
811
812END_IPLUG_NAMESPACE
IPlug logging a.k.a tracing functionality.
A class to help with queuing timestamped MIDI messages.
Definition: IPlugMidi.h:682
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