giuliomoro@181
|
1 /*
|
giuliomoro@181
|
2 * Midi.h
|
giuliomoro@181
|
3 *
|
giuliomoro@181
|
4 * Created on: 15 Jan 2016
|
giuliomoro@181
|
5 * Author: giulio
|
giuliomoro@181
|
6 */
|
giuliomoro@181
|
7
|
giuliomoro@181
|
8 #ifndef MIDI_H_
|
giuliomoro@181
|
9 #define MIDI_H_
|
giuliomoro@181
|
10
|
giuliomoro@301
|
11 #include <Bela.h>
|
giuliomoro@181
|
12 #include <vector>
|
giuliomoro@181
|
13
|
giuliomoro@181
|
14 typedef unsigned char midi_byte_t;
|
giuliomoro@181
|
15
|
giuliomoro@197
|
16
|
giuliomoro@197
|
17 typedef enum midiMessageType{
|
giuliomoro@197
|
18 kmmNoteOff = 0,
|
giuliomoro@197
|
19 kmmNoteOn,
|
giuliomoro@197
|
20 kmmPolyphonicKeyPressure,
|
giuliomoro@197
|
21 kmmControlChange,
|
giuliomoro@197
|
22 kmmProgramChange,
|
giuliomoro@197
|
23 kmmChannelPressure,
|
giuliomoro@197
|
24 kmmPitchBend,
|
giuliomoro@197
|
25 kmmNone,
|
giuliomoro@197
|
26 kmmAny,
|
giuliomoro@197
|
27 } MidiMessageType;
|
giuliomoro@197
|
28 #define midiMessageStatusBytesLength 7+2 //2 being kmmNone and kmmAny
|
giuliomoro@197
|
29
|
giuliomoro@197
|
30 extern midi_byte_t midiMessageStatusBytes[midiMessageStatusBytesLength];
|
giuliomoro@197
|
31 extern unsigned int midiMessageNumDataBytes[midiMessageStatusBytesLength];
|
giuliomoro@197
|
32
|
giuliomoro@197
|
33 class MidiChannelMessage{
|
giuliomoro@197
|
34 public:
|
giuliomoro@197
|
35 MidiChannelMessage();
|
giuliomoro@197
|
36 MidiChannelMessage(MidiMessageType type);
|
giuliomoro@197
|
37 virtual ~MidiChannelMessage();
|
giuliomoro@197
|
38 MidiMessageType getType();
|
giuliomoro@197
|
39 int getChannel();
|
giuliomoro@197
|
40 unsigned int getNumDataBytes(){
|
giuliomoro@197
|
41 return midiMessageNumDataBytes[(unsigned int)_type];
|
giuliomoro@197
|
42 }
|
giuliomoro@197
|
43 void setDataByte(unsigned int dataByteIndex, midi_byte_t input){
|
giuliomoro@197
|
44 _dataBytes[dataByteIndex] = input;
|
giuliomoro@197
|
45 }
|
giuliomoro@197
|
46 void setType(MidiMessageType type){
|
giuliomoro@197
|
47 _type = type;
|
giuliomoro@197
|
48 _statusByte = midiMessageStatusBytes[_type];
|
giuliomoro@197
|
49 }
|
giuliomoro@197
|
50 void setChannel(midi_byte_t channel){
|
giuliomoro@197
|
51 _channel = channel;
|
giuliomoro@197
|
52 }
|
giuliomoro@197
|
53 midi_byte_t getDataByte(unsigned int index){
|
giuliomoro@197
|
54 return _dataBytes[index];
|
giuliomoro@197
|
55 }
|
giuliomoro@197
|
56 void clear(){
|
giuliomoro@197
|
57 for(int n = 0; n<maxDataBytes; n++){
|
giuliomoro@197
|
58 _dataBytes[n] = 0;
|
giuliomoro@197
|
59 }
|
giuliomoro@197
|
60 _type = kmmNone;
|
giuliomoro@197
|
61 _statusByte = 0;
|
giuliomoro@197
|
62 }
|
giuliomoro@197
|
63 void prettyPrint(){
|
giuliomoro@197
|
64 rt_printf("MessageType: %x, ", this->getType());
|
giuliomoro@197
|
65 rt_printf("channel: %u, ", this->getChannel());
|
giuliomoro@216
|
66 for(unsigned int n = 0; n < this->getNumDataBytes(); n++){
|
giuliomoro@197
|
67 rt_printf("data%d: %d, ", n + 1, this->getDataByte(n));
|
giuliomoro@197
|
68 }
|
giuliomoro@197
|
69 rt_printf("\n");
|
giuliomoro@197
|
70 }
|
giuliomoro@197
|
71
|
giuliomoro@197
|
72 private:
|
giuliomoro@197
|
73 const static int maxDataBytes = 2;
|
giuliomoro@197
|
74 protected:
|
giuliomoro@197
|
75 midi_byte_t _statusByte;
|
giuliomoro@197
|
76 midi_byte_t _dataBytes[maxDataBytes]; // where 2 is the maximum number of data bytes for a channel message
|
giuliomoro@197
|
77 MidiMessageType _type;
|
giuliomoro@197
|
78 midi_byte_t _channel;
|
giuliomoro@197
|
79 };
|
giuliomoro@197
|
80 /*
|
giuliomoro@197
|
81 class MidiControlChangeMessage : public MidiChannelMessage{
|
giuliomoro@197
|
82 int number;
|
giuliomoro@197
|
83 int value;
|
giuliomoro@197
|
84 public:
|
giuliomoro@197
|
85 int getNumber();
|
giuliomoro@197
|
86 int getNumDataBytes();
|
giuliomoro@197
|
87 int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
|
giuliomoro@197
|
88 int getValue();
|
giuliomoro@197
|
89 int set(midi_byte_t* input);
|
giuliomoro@197
|
90 };
|
giuliomoro@197
|
91
|
giuliomoro@197
|
92 class MidiNoteMessage : public MidiChannelMessage{
|
giuliomoro@197
|
93 int note;
|
giuliomoro@197
|
94 int velocity;
|
giuliomoro@197
|
95 public:
|
giuliomoro@197
|
96 int getNote();
|
giuliomoro@197
|
97 int getVelocity();
|
giuliomoro@197
|
98 int getNumDataBytes();
|
giuliomoro@197
|
99 int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
|
giuliomoro@197
|
100 };
|
giuliomoro@197
|
101
|
giuliomoro@197
|
102 class MidiProgramChangeMessage : public MidiChannelMessage{
|
giuliomoro@197
|
103 midi_byte_t program;
|
giuliomoro@197
|
104 public:
|
giuliomoro@197
|
105 int getNumDataBytes();
|
giuliomoro@197
|
106 int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
|
giuliomoro@197
|
107 midi_byte_t getProgram();
|
giuliomoro@197
|
108 };
|
giuliomoro@197
|
109 */
|
giuliomoro@197
|
110
|
giuliomoro@197
|
111 class MidiParser{
|
giuliomoro@197
|
112 private:
|
giuliomoro@197
|
113 std::vector<MidiChannelMessage> messages;
|
giuliomoro@197
|
114 unsigned int writePointer;
|
giuliomoro@197
|
115 unsigned int readPointer;
|
giuliomoro@197
|
116 unsigned int elapsedDataBytes;
|
giuliomoro@197
|
117 bool waitingForStatus;
|
giuliomoro@226
|
118 void (*messageReadyCallback)(MidiChannelMessage,void*);
|
giuliomoro@224
|
119 bool callbackEnabled;
|
giuliomoro@226
|
120 void* callbackArg;
|
giuliomoro@197
|
121 public:
|
giuliomoro@197
|
122 MidiParser(){
|
giuliomoro@197
|
123 waitingForStatus = true;
|
giuliomoro@197
|
124 elapsedDataBytes= 0;
|
giuliomoro@197
|
125 messages.resize(100); // 100 is the number of messages that can be buffered
|
giuliomoro@197
|
126 writePointer = 0;
|
giuliomoro@197
|
127 readPointer = 0;
|
giuliomoro@224
|
128 callbackEnabled = false;
|
giuliomoro@224
|
129 messageReadyCallback = NULL;
|
giuliomoro@226
|
130 callbackArg = NULL;
|
giuliomoro@197
|
131 }
|
giuliomoro@197
|
132
|
giuliomoro@197
|
133 /**
|
giuliomoro@197
|
134 * Parses some midi messages.
|
giuliomoro@197
|
135 *
|
giuliomoro@197
|
136 * @param input the array to read from
|
giuliomoro@197
|
137 * @param length the maximum number of values available at the array
|
giuliomoro@197
|
138 *
|
giuliomoro@197
|
139 * @return the number of bytes parsed
|
giuliomoro@197
|
140 */
|
giuliomoro@197
|
141 int parse(midi_byte_t* input, unsigned int length);
|
giuliomoro@224
|
142
|
giuliomoro@225
|
143 /**
|
giuliomoro@225
|
144 * Sets the callback to call when a new MidiChannelMessage is available
|
giuliomoro@225
|
145 * from the input port.
|
giuliomoro@225
|
146 *
|
giuliomoro@226
|
147 * The callback will be called with two arguments:
|
giuliomoro@226
|
148 * callback(MidiChannelMessage newMessage, void* arg)
|
giuliomoro@226
|
149 *
|
giuliomoro@226
|
150 * In order to deactivate the callback, call this method with NULL as the
|
giuliomoro@226
|
151 * first argument.
|
giuliomoro@225
|
152 * While the callback is enabled, calling numAvailableMessages() and
|
giuliomoro@225
|
153 * getNextChannelMessage() is still possible, but it will probably always
|
giuliomoro@225
|
154 * return 0 as the callback is called as soon as a new message is available.
|
giuliomoro@225
|
155 *
|
giuliomoro@226
|
156 * @param newCallback the callback function.
|
giuliomoro@226
|
157 * @param arg the second argument to be passed to the callback function.
|
giuliomoro@225
|
158 *
|
giuliomoro@225
|
159 */
|
giuliomoro@226
|
160 void setCallback(void (*newCallback)(MidiChannelMessage, void*), void* arg=NULL){
|
giuliomoro@226
|
161 callbackArg = arg;
|
giuliomoro@224
|
162 messageReadyCallback = newCallback;
|
giuliomoro@224
|
163 if(newCallback != NULL){
|
giuliomoro@224
|
164 callbackEnabled = true;
|
giuliomoro@224
|
165 } else {
|
giuliomoro@224
|
166 callbackEnabled = false;
|
giuliomoro@224
|
167 }
|
giuliomoro@197
|
168 };
|
giuliomoro@224
|
169
|
giuliomoro@225
|
170 /**
|
giuliomoro@225
|
171 * Checks whether there is a callback currently set to be called
|
giuliomoro@225
|
172 * every time a new input MidiChannelMessage is available from the
|
giuliomoro@225
|
173 * input port.
|
giuliomoro@225
|
174 */
|
giuliomoro@224
|
175 bool isCallbackEnabled(){
|
giuliomoro@224
|
176 return callbackEnabled;
|
giuliomoro@224
|
177 };
|
giuliomoro@224
|
178
|
giuliomoro@225
|
179 /**
|
giuliomoro@225
|
180 * Returns the number of unread MidiChannelMessage available from the
|
giuliomoro@225
|
181 * input port.
|
giuliomoro@225
|
182 *
|
giuliomoro@225
|
183 */
|
giuliomoro@225
|
184
|
giuliomoro@197
|
185 int numAvailableMessages(){
|
giuliomoro@197
|
186 int num = (writePointer - readPointer + messages.size() ) % messages.size();
|
giuliomoro@197
|
187 return num;
|
giuliomoro@197
|
188 }
|
giuliomoro@225
|
189
|
giuliomoro@197
|
190 /**
|
giuliomoro@197
|
191 * Get the oldest channel message in the buffer.
|
giuliomoro@197
|
192 *
|
giuliomoro@199
|
193 * If this method is called when numAvailableMessages()==0, then
|
giuliomoro@199
|
194 * a message with all fields set to zero is returned.
|
giuliomoro@225
|
195 *
|
giuliomoro@197
|
196 * @param type the type of the message to retrieve
|
giuliomoro@225
|
197 *
|
giuliomoro@197
|
198 * @return a copy of the oldest message of the give type in the buffer
|
giuliomoro@197
|
199 */
|
giuliomoro@197
|
200 MidiChannelMessage getNextChannelMessage(/*MidiMessageType type*/){
|
giuliomoro@197
|
201 MidiChannelMessage message;
|
giuliomoro@197
|
202 message = messages[readPointer];
|
giuliomoro@197
|
203 if(message.getType() == kmmNone){
|
giuliomoro@197
|
204 message.clear();
|
giuliomoro@197
|
205 }
|
giuliomoro@197
|
206 messages[readPointer].setType(kmmNone); // do not use it again
|
giuliomoro@197
|
207 readPointer++;
|
giuliomoro@197
|
208 if(readPointer == messages.size()){
|
giuliomoro@197
|
209 readPointer = 0;
|
giuliomoro@197
|
210 }
|
giuliomoro@197
|
211 return message;
|
giuliomoro@197
|
212 };
|
giuliomoro@197
|
213
|
giuliomoro@197
|
214 // MidiChannelMessage getNextChannelMessage(){
|
giuliomoro@197
|
215 // getNextChannelMessage(kmmAny);
|
giuliomoro@197
|
216 // }
|
giuliomoro@197
|
217 // MidiControlChangeMessage* getNextControlChangeMessage(){
|
giuliomoro@197
|
218 // return (MidiControlChangeMessage*)getNextChannelMessage(kmmControlChange);
|
giuliomoro@197
|
219 // };
|
giuliomoro@197
|
220 // MidiProgramChangeMessage* getNextProgramChangeMessage(){
|
giuliomoro@197
|
221 // return (MidiProgramChangeMessage*)getNextChannelMessage(kmmProgramChange);
|
giuliomoro@197
|
222 // };
|
giuliomoro@197
|
223 // MidiNoteMessage* getNextNoteOnMessage(){
|
giuliomoro@197
|
224 // return (MidiNoteMessage*)getNextChannelMessage(kmmNoteOn);
|
giuliomoro@197
|
225 // };
|
giuliomoro@197
|
226 };
|
giuliomoro@197
|
227
|
giuliomoro@197
|
228
|
giuliomoro@181
|
229 class Midi {
|
giuliomoro@181
|
230 public:
|
giuliomoro@181
|
231 Midi();
|
giuliomoro@197
|
232
|
giuliomoro@197
|
233 /**
|
giuliomoro@197
|
234 * Enable the input MidiParser.
|
giuliomoro@197
|
235 *
|
giuliomoro@199
|
236 * If the parser is enabled, getInput() will return an error code.
|
giuliomoro@197
|
237 * Midi messages should instead be retrieved via, e.g.: getMidiParser()->getNextChannelMessage();
|
giuliomoro@197
|
238 *
|
giuliomoro@197
|
239 * @param enable true to enable the input MidiParser, false to disable it.
|
giuliomoro@197
|
240 */
|
giuliomoro@197
|
241 void enableParser(bool enable);
|
giuliomoro@197
|
242
|
giuliomoro@197
|
243 /**
|
giuliomoro@197
|
244 * Get access to the input parser in use, if any.
|
giuliomoro@197
|
245 *
|
giuliomoro@197
|
246 * @return a pointer to the instance of MidiParser, if currently enabled, zero otherwise.
|
giuliomoro@197
|
247 */
|
giuliomoro@197
|
248 MidiParser* getParser();
|
giuliomoro@197
|
249
|
giuliomoro@225
|
250 /**
|
giuliomoro@225
|
251 * Sets the callback to call when a new MidiChannelMessage is available
|
giuliomoro@225
|
252 * from the input port.
|
giuliomoro@225
|
253 *
|
giuliomoro@225
|
254 * Internally, it calls enableParser() and the MidiParser::setCallback();
|
giuliomoro@225
|
255 *
|
giuliomoro@226
|
256 * @param newCallback the callback function.
|
giuliomoro@226
|
257 * @param arg the second argument to be passed to the callback function.
|
giuliomoro@225
|
258 */
|
giuliomoro@226
|
259 void setParserCallback(void (*callback)(MidiChannelMessage, void*), void* arg=NULL){
|
giuliomoro@224
|
260 // if callback is not NULL, also enable the parser
|
giuliomoro@224
|
261 enableParser(callback != NULL); //this needs to be first, as it deletes the parser(if exists)
|
giuliomoro@226
|
262 getParser()->setCallback(callback, arg);
|
giuliomoro@224
|
263 }
|
giuliomoro@224
|
264
|
giuliomoro@181
|
265 /**
|
giuliomoro@181
|
266 * Open the specified input Midi port and start reading from it.
|
giuliomoro@181
|
267 * @param port Midi port to open
|
giuliomoro@181
|
268 * @return 1 on success, -1 on failure
|
giuliomoro@181
|
269 */
|
giuliomoro@181
|
270 int readFrom(int port);
|
giuliomoro@181
|
271 /**
|
giuliomoro@181
|
272 * Open the specified output Midi port and prepares to write to it.
|
giuliomoro@181
|
273 * @param port Midi port to open
|
giuliomoro@181
|
274 * @return 1 on success, -1 on failure
|
giuliomoro@181
|
275 */
|
giuliomoro@181
|
276 int writeTo(int port);
|
giuliomoro@181
|
277
|
giuliomoro@181
|
278 /**
|
giuliomoro@191
|
279 * Get received midi bytes, one at a time.
|
giuliomoro@181
|
280 * @return -1 if no new byte is available, -2 on error,
|
giuliomoro@181
|
281 * the oldest not yet retrieved midi byte otherwise
|
giuliomoro@181
|
282 */
|
giuliomoro@181
|
283 int getInput();
|
giuliomoro@181
|
284
|
giuliomoro@181
|
285 /**
|
giuliomoro@181
|
286 * Writes a Midi byte to the output port
|
giuliomoro@181
|
287 * @param byte the Midi byte to write
|
giuliomoro@181
|
288 * @return 1 on success, -1 on error
|
giuliomoro@181
|
289 */
|
giuliomoro@181
|
290 int writeOutput(midi_byte_t byte);
|
giuliomoro@181
|
291
|
giuliomoro@181
|
292 /**
|
giuliomoro@181
|
293 * Writes Midi bytes to the output port
|
giuliomoro@181
|
294 * @param bytes an array of bytes to be written
|
giuliomoro@181
|
295 * @param length number of bytes to write
|
giuliomoro@181
|
296 * @return 1 on success, -1 on error
|
giuliomoro@181
|
297 */
|
giuliomoro@191
|
298 int writeOutput(midi_byte_t* bytes, unsigned int length);
|
giuliomoro@197
|
299 /**
|
giuliomoro@197
|
300 * Gives access to the midi parser, if it has been activated.
|
giuliomoro@197
|
301 *
|
giuliomoro@197
|
302 * @return a pointer to the midi parser if active, zero otherwise
|
giuliomoro@197
|
303 */
|
giuliomoro@197
|
304 MidiParser* getMidiParser();
|
giuliomoro@181
|
305 virtual ~Midi();
|
giuliomoro@181
|
306 static void midiInputLoop();
|
giuliomoro@191
|
307 static void midiOutputLoop();
|
giuliomoro@181
|
308 static bool staticConstructed;
|
giuliomoro@181
|
309 static void staticConstructor();
|
giuliomoro@181
|
310 private:
|
giuliomoro@199
|
311 int _getInput();
|
giuliomoro@181
|
312 void readInputLoop();
|
giuliomoro@191
|
313 void writeOutputLoop();
|
giuliomoro@181
|
314 int outputPort;
|
giuliomoro@181
|
315 int inputPort;
|
giuliomoro@181
|
316 std::vector<midi_byte_t> inputBytes;
|
giuliomoro@181
|
317 unsigned int inputBytesWritePointer;
|
giuliomoro@181
|
318 unsigned int inputBytesReadPointer;
|
giuliomoro@181
|
319 std::vector<midi_byte_t> outputBytes;
|
giuliomoro@191
|
320 unsigned int outputBytesWritePointer;
|
giuliomoro@191
|
321 unsigned int outputBytesReadPointer;
|
giuliomoro@197
|
322 MidiParser* inputParser;
|
giuliomoro@197
|
323 bool parserEnabled;
|
giuliomoro@191
|
324 static std::vector<Midi*> objAddrs[2];
|
giuliomoro@181
|
325 static AuxiliaryTask midiInputTask;
|
giuliomoro@181
|
326 static AuxiliaryTask midiOutputTask;
|
giuliomoro@181
|
327 };
|
giuliomoro@181
|
328
|
giuliomoro@181
|
329
|
giuliomoro@181
|
330 #endif /* MIDI_H_ */
|