Bela
Real-time, ultra-low-latency audio and sensor processing system for BeagleBone Black
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
Midi.h
1 #pragma once
2 #include <Bela.h>
3 #include <vector>
4 #include <string>
5 #ifdef XENOMAI_SKIN_native
6 #include <native/pipe.h>
7 #endif
8 
9 typedef unsigned char midi_byte_t;
10 
11 typedef enum midiMessageType{
12  kmmNoteOff = 0,
13  kmmNoteOn,
14  kmmPolyphonicKeyPressure,
15  kmmControlChange,
16  kmmProgramChange,
17  kmmChannelPressure,
18  kmmPitchBend,
19  kmmSystem,
20  kmmNone,
21  kmmAny,
22 } MidiMessageType;
23 #define midiMessageStatusBytesLength 8+2 //2 being kmmNone and kmmAny
24 
25 extern midi_byte_t midiMessageStatusBytes[midiMessageStatusBytesLength];
26 extern unsigned int midiMessageNumDataBytes[midiMessageStatusBytesLength];
27 
29 public:
31  MidiChannelMessage(MidiMessageType type);
32  midi_byte_t getStatusByte() const {
33  return _statusByte;
34  }
35  virtual ~MidiChannelMessage();
36  MidiMessageType getType() const;
37  int getChannel() const;
38  const char* getTypeText() const {
39  return getTypeText(getType());
40  }
41  static const char* getTypeText(MidiMessageType type){
42  switch (type) {
43  case kmmNoteOff:
44  return "note off";
45  case kmmNoteOn:
46  return "note on";
47  case kmmPolyphonicKeyPressure:
48  return "polyphonic aftertouch";
49  case kmmControlChange:
50  return "control change";
51  case kmmProgramChange:
52  return "program change";
53  case kmmChannelPressure:
54  return "channel aftertouch";
55  case kmmPitchBend:
56  return "pitch bend";
57  case kmmSystem:
58  return "system";
59  case kmmAny:
60  return "any";
61  case kmmNone:
62  default:
63  return "none";
64  }
65  }
66 
67  unsigned int getNumDataBytes() const {
68  return midiMessageNumDataBytes[(unsigned int)_type];
69  }
70  void setDataByte(unsigned int dataByteIndex, midi_byte_t input){
71  _dataBytes[dataByteIndex] = input;
72  }
73  void setType(MidiMessageType type){
74  _type = type;
75  _statusByte = midiMessageStatusBytes[_type];
76  }
77  void setChannel(midi_byte_t channel){
78  _channel = channel;
79  }
80  midi_byte_t getDataByte(unsigned int index) const {
81  return _dataBytes[index];
82  }
83  void clear(){
84  for(int n = 0; n<maxDataBytes; n++){
85  _dataBytes[n] = 0;
86  }
87  _type = kmmNone;
88  _statusByte = 0;
89  }
90  void prettyPrint() const {
91  rt_printf("type: %s, ", this->getTypeText());
92  rt_printf("channel: %u, ", this->getChannel());
93  for(unsigned int n = 0; n < this->getNumDataBytes(); n++){
94  rt_printf("data%d: %d, ", n + 1, this->getDataByte(n));
95  }
96  rt_printf("\n");
97  }
98 private:
99  const static int maxDataBytes = 2;
100 protected:
101  midi_byte_t _statusByte;
102  midi_byte_t _dataBytes[maxDataBytes]; // where 2 is the maximum number of data bytes for a channel message
103  MidiMessageType _type;
104  midi_byte_t _channel;
105 };
106 /*
107 class MidiControlChangeMessage : public MidiChannelMessage{
108  int number;
109  int value;
110 public:
111  int getNumber();
112  int getNumDataBytes();
113  int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
114  int getValue();
115  int set(midi_byte_t* input);
116 };
117 
118 class MidiNoteMessage : public MidiChannelMessage{
119  int note;
120  int velocity;
121 public:
122  int getNote();
123  int getVelocity();
124  int getNumDataBytes();
125  int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
126 };
127 
128 class MidiProgramChangeMessage : public MidiChannelMessage{
129  midi_byte_t program;
130 public:
131  int getNumDataBytes();
132  int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
133  midi_byte_t getProgram();
134 };
135 */
136 
138 private:
139  std::vector<MidiChannelMessage> messages;
140  unsigned int writePointer;
141  unsigned int readPointer;
142  unsigned int elapsedDataBytes;
143  bool waitingForStatus;
144  bool receivingSysex;
145  void (*messageReadyCallback)(MidiChannelMessage,void*);
146  void* callbackArg;
147  void (*sysexCallback)(midi_byte_t,void*);
148  void* sysexCallbackArg;
149 public:
150  MidiParser(){
151  waitingForStatus = true;
152  receivingSysex = false;
153  elapsedDataBytes= 0;
154  messages.resize(100); // 100 is the number of messages that can be buffered
155  writePointer = 0;
156  readPointer = 0;
157  messageReadyCallback = nullptr;
158  callbackArg = nullptr;
159  sysexCallback = nullptr;
160  sysexCallbackArg = nullptr;
161  }
162 
171  int parse(midi_byte_t* input, unsigned int length);
172 
190  void setCallback(void (*newCallback)(MidiChannelMessage, void*), void* arg=NULL){
191  callbackArg = arg;
192  messageReadyCallback = newCallback;
193  };
194 
201  return messageReadyCallback != nullptr;
202  };
203 
220  void setSysexCallback(void (*newCallback)(midi_byte_t, void*), void* arg=nullptr){
221  sysexCallbackArg = arg;
222  sysexCallback = newCallback;
223  };
224 
231  return sysexCallback != nullptr;
232  };
233 
240  int num = (writePointer - readPointer + messages.size() ) % messages.size();
241  return num;
242  }
243 
253  MidiChannelMessage message;
254  message = messages[readPointer];
255  if(message.getType() == kmmNone){
256  message.clear();
257  }
258  messages[readPointer].setType(kmmNone); // do not use it again
259  readPointer++;
260  if(readPointer == messages.size()){
261  readPointer = 0;
262  }
263  return message;
264  };
265 
266 // MidiChannelMessage getNextChannelMessage(){
267 // getNextChannelMessage(kmmAny);
268 // }
269 // MidiControlChangeMessage* getNextControlChangeMessage(){
270 // return (MidiControlChangeMessage*)getNextChannelMessage(kmmControlChange);
271 // };
272 // MidiProgramChangeMessage* getNextProgramChangeMessage(){
273 // return (MidiProgramChangeMessage*)getNextChannelMessage(kmmProgramChange);
274 // };
275 // MidiNoteMessage* getNextNoteOnMessage(){
276 // return (MidiNoteMessage*)getNextChannelMessage(kmmNoteOn);
277 // };
278 };
279 
280 
281 typedef struct _snd_rawmidi snd_rawmidi_t;
282 class Midi {
283 public:
284  Midi();
285  void setup();
286  void cleanup();
295  void enableParser(bool enable);
296 
303 
313  void setParserCallback(void (*callback)(MidiChannelMessage, void*), void* arg=NULL){
314  // if callback is not NULL, also enable the parser
315  enableParser(callback != NULL); //this needs to be first, as it deletes the parser(if exists)
316  getParser()->setCallback(callback, arg);
317  }
318 
324  int readFrom(const char* port);
325 
331  int writeTo(const char* port);
332 
338  int getInput();
339 
345  int writeOutput(midi_byte_t byte);
346 
353  int writeOutput(midi_byte_t* bytes, unsigned int length);
354 
355 
356  static midi_byte_t makeStatusByte(midi_byte_t statusCode, midi_byte_t dataByte);
357  int writeMessage(midi_byte_t statusCode, midi_byte_t channel, midi_byte_t dataByte);
358  int writeMessage(midi_byte_t statusCode, midi_byte_t channel, midi_byte_t dataByte1, midi_byte_t dataByte2);
359  int writeMessage(const MidiChannelMessage& msg);
360  int writeNoteOff(midi_byte_t channel, midi_byte_t pitch, midi_byte_t velocity);
361  int writeNoteOn(midi_byte_t channel, midi_byte_t pitch, midi_byte_t velocity);
362  int writePolyphonicKeyPressure(midi_byte_t channel, midi_byte_t pitch, midi_byte_t pressure);
363  int writeControlChange(midi_byte_t channel, midi_byte_t controller, midi_byte_t value);
364  int writeProgramChange(midi_byte_t channel, midi_byte_t program);
365  int writeChannelPressure(midi_byte_t channel, midi_byte_t pressure);
366  int writePitchBend(midi_byte_t channel, uint16_t bend);
367 
374  virtual ~Midi();
375 
376  bool isInputEnabled();
377 
378  bool isOutputEnabled();
379 
384  static void createAllPorts(std::vector<Midi*>& ports, bool useParser = false);
385 
389  static void destroyPorts(std::vector<Midi*>& ports);
390 private:
391  char defaultPort[9];
392  std::string inPort;
393  std::string outPort;
394  int _getInput();
395  int attemptRecoveryRead();
396  static void readInputLoop(void* obj);
397  int attemptRecoveryWrite();
398  static void writeOutputLoop(void* obj);
399  snd_rawmidi_t *alsaIn,*alsaOut;
400  std::vector<midi_byte_t> inputBytes;
401  unsigned int inputBytesWritePointer;
402  unsigned int inputBytesReadPointer;
403  std::vector<midi_byte_t> outputBytes;
404  MidiParser* inputParser;
405  bool parserEnabled;
406  bool inputEnabled;
407  bool outputEnabled;
408  AuxiliaryTask midiInputTask;
409  AuxiliaryTask midiOutputTask;
410  char* inId;
411  char* outId;
412  char* outPipeName;
413 #ifdef XENOMAI_SKIN_native
414  RT_PIPE outPipe;
415 #endif
416 #ifdef XENOMAI_SKIN_posix
417  int sock;
418 #endif
419 };
static void createAllPorts(std::vector< Midi * > &ports, bool useParser=false)
Definition: Midi.cpp:366
MidiParser * getMidiParser()
Definition: Midi.h:137
int numAvailableMessages()
Definition: Midi.h:239
void setSysexCallback(void(*newCallback)(midi_byte_t, void *), void *arg=nullptr)
Definition: Midi.h:220
void enableParser(bool enable)
Definition: Midi.cpp:148
bool isCallbackEnabled()
Definition: Midi.h:200
MidiParser * getParser()
Definition: Midi.cpp:469
Definition: Midi.h:28
int writeTo(const char *port)
Definition: Midi.cpp:322
Definition: Midi.h:282
int parse(midi_byte_t *input, unsigned int length)
Definition: Midi.cpp:39
int writeOutput(midi_byte_t byte)
Definition: Midi.cpp:476
bool isSysexCallbackEnabled()
Definition: Midi.h:230
MidiChannelMessage getNextChannelMessage()
Definition: Midi.h:252
void setParserCallback(void(*callback)(MidiChannelMessage, void *), void *arg=NULL)
Definition: Midi.h:313
static void destroyPorts(std::vector< Midi * > &ports)
int readFrom(const char *port)
Definition: Midi.cpp:303
int getInput()
Definition: Midi.cpp:462
Main Bela public API.
void * AuxiliaryTask
Definition: Bela.h:557
void setCallback(void(*newCallback)(MidiChannelMessage, void *), void *arg=NULL)
Definition: Midi.h:190