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@181
|
11 #include <BeagleRT.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 if(num > 0){
|
giuliomoro@197
|
188 int a = a +1;
|
giuliomoro@224
|
189 a = readPointer;
|
giuliomoro@197
|
190 }
|
giuliomoro@197
|
191 return num;
|
giuliomoro@197
|
192 }
|
giuliomoro@225
|
193
|
giuliomoro@197
|
194 /**
|
giuliomoro@197
|
195 * Get the oldest channel message in the buffer.
|
giuliomoro@197
|
196 *
|
giuliomoro@199
|
197 * If this method is called when numAvailableMessages()==0, then
|
giuliomoro@199
|
198 * a message with all fields set to zero is returned.
|
giuliomoro@225
|
199 *
|
giuliomoro@197
|
200 * @param type the type of the message to retrieve
|
giuliomoro@225
|
201 *
|
giuliomoro@197
|
202 * @return a copy of the oldest message of the give type in the buffer
|
giuliomoro@197
|
203 */
|
giuliomoro@197
|
204 MidiChannelMessage getNextChannelMessage(/*MidiMessageType type*/){
|
giuliomoro@197
|
205 MidiChannelMessage message;
|
giuliomoro@197
|
206 message = messages[readPointer];
|
giuliomoro@197
|
207 if(message.getType() == kmmNone){
|
giuliomoro@197
|
208 message.clear();
|
giuliomoro@197
|
209 }
|
giuliomoro@197
|
210 messages[readPointer].setType(kmmNone); // do not use it again
|
giuliomoro@197
|
211 readPointer++;
|
giuliomoro@197
|
212 if(readPointer == messages.size()){
|
giuliomoro@197
|
213 readPointer = 0;
|
giuliomoro@197
|
214 }
|
giuliomoro@197
|
215 return message;
|
giuliomoro@197
|
216 };
|
giuliomoro@197
|
217
|
giuliomoro@197
|
218 // MidiChannelMessage getNextChannelMessage(){
|
giuliomoro@197
|
219 // getNextChannelMessage(kmmAny);
|
giuliomoro@197
|
220 // }
|
giuliomoro@197
|
221 // MidiControlChangeMessage* getNextControlChangeMessage(){
|
giuliomoro@197
|
222 // return (MidiControlChangeMessage*)getNextChannelMessage(kmmControlChange);
|
giuliomoro@197
|
223 // };
|
giuliomoro@197
|
224 // MidiProgramChangeMessage* getNextProgramChangeMessage(){
|
giuliomoro@197
|
225 // return (MidiProgramChangeMessage*)getNextChannelMessage(kmmProgramChange);
|
giuliomoro@197
|
226 // };
|
giuliomoro@197
|
227 // MidiNoteMessage* getNextNoteOnMessage(){
|
giuliomoro@197
|
228 // return (MidiNoteMessage*)getNextChannelMessage(kmmNoteOn);
|
giuliomoro@197
|
229 // };
|
giuliomoro@197
|
230 };
|
giuliomoro@197
|
231
|
giuliomoro@197
|
232
|
giuliomoro@181
|
233 class Midi {
|
giuliomoro@181
|
234 public:
|
giuliomoro@181
|
235 Midi();
|
giuliomoro@197
|
236
|
giuliomoro@197
|
237 /**
|
giuliomoro@197
|
238 * Enable the input MidiParser.
|
giuliomoro@197
|
239 *
|
giuliomoro@199
|
240 * If the parser is enabled, getInput() will return an error code.
|
giuliomoro@197
|
241 * Midi messages should instead be retrieved via, e.g.: getMidiParser()->getNextChannelMessage();
|
giuliomoro@197
|
242 *
|
giuliomoro@197
|
243 * @param enable true to enable the input MidiParser, false to disable it.
|
giuliomoro@197
|
244 */
|
giuliomoro@197
|
245 void enableParser(bool enable);
|
giuliomoro@197
|
246
|
giuliomoro@197
|
247 /**
|
giuliomoro@197
|
248 * Get access to the input parser in use, if any.
|
giuliomoro@197
|
249 *
|
giuliomoro@197
|
250 * @return a pointer to the instance of MidiParser, if currently enabled, zero otherwise.
|
giuliomoro@197
|
251 */
|
giuliomoro@197
|
252 MidiParser* getParser();
|
giuliomoro@197
|
253
|
giuliomoro@225
|
254 /**
|
giuliomoro@225
|
255 * Sets the callback to call when a new MidiChannelMessage is available
|
giuliomoro@225
|
256 * from the input port.
|
giuliomoro@225
|
257 *
|
giuliomoro@225
|
258 * Internally, it calls enableParser() and the MidiParser::setCallback();
|
giuliomoro@225
|
259 *
|
giuliomoro@226
|
260 * @param newCallback the callback function.
|
giuliomoro@226
|
261 * @param arg the second argument to be passed to the callback function.
|
giuliomoro@225
|
262 */
|
giuliomoro@226
|
263 void setParserCallback(void (*callback)(MidiChannelMessage, void*), void* arg=NULL){
|
giuliomoro@224
|
264 // if callback is not NULL, also enable the parser
|
giuliomoro@224
|
265 enableParser(callback != NULL); //this needs to be first, as it deletes the parser(if exists)
|
giuliomoro@226
|
266 getParser()->setCallback(callback, arg);
|
giuliomoro@224
|
267 }
|
giuliomoro@224
|
268
|
giuliomoro@181
|
269 /**
|
giuliomoro@181
|
270 * Open the specified input Midi port and start reading from it.
|
giuliomoro@181
|
271 * @param port Midi port to open
|
giuliomoro@181
|
272 * @return 1 on success, -1 on failure
|
giuliomoro@181
|
273 */
|
giuliomoro@181
|
274 int readFrom(int port);
|
giuliomoro@181
|
275 /**
|
giuliomoro@181
|
276 * Open the specified output Midi port and prepares to write to it.
|
giuliomoro@181
|
277 * @param port Midi port to open
|
giuliomoro@181
|
278 * @return 1 on success, -1 on failure
|
giuliomoro@181
|
279 */
|
giuliomoro@181
|
280 int writeTo(int port);
|
giuliomoro@181
|
281
|
giuliomoro@181
|
282 /**
|
giuliomoro@191
|
283 * Get received midi bytes, one at a time.
|
giuliomoro@181
|
284 * @return -1 if no new byte is available, -2 on error,
|
giuliomoro@181
|
285 * the oldest not yet retrieved midi byte otherwise
|
giuliomoro@181
|
286 */
|
giuliomoro@181
|
287 int getInput();
|
giuliomoro@181
|
288
|
giuliomoro@181
|
289 /**
|
giuliomoro@181
|
290 * Writes a Midi byte to the output port
|
giuliomoro@181
|
291 * @param byte the Midi byte to write
|
giuliomoro@181
|
292 * @return 1 on success, -1 on error
|
giuliomoro@181
|
293 */
|
giuliomoro@181
|
294 int writeOutput(midi_byte_t byte);
|
giuliomoro@181
|
295
|
giuliomoro@181
|
296 /**
|
giuliomoro@181
|
297 * Writes Midi bytes to the output port
|
giuliomoro@181
|
298 * @param bytes an array of bytes to be written
|
giuliomoro@181
|
299 * @param length number of bytes to write
|
giuliomoro@181
|
300 * @return 1 on success, -1 on error
|
giuliomoro@181
|
301 */
|
giuliomoro@191
|
302 int writeOutput(midi_byte_t* bytes, unsigned int length);
|
giuliomoro@197
|
303 /**
|
giuliomoro@197
|
304 * Gives access to the midi parser, if it has been activated.
|
giuliomoro@197
|
305 *
|
giuliomoro@197
|
306 * @return a pointer to the midi parser if active, zero otherwise
|
giuliomoro@197
|
307 */
|
giuliomoro@197
|
308 MidiParser* getMidiParser();
|
giuliomoro@181
|
309 virtual ~Midi();
|
giuliomoro@181
|
310 static void midiInputLoop();
|
giuliomoro@191
|
311 static void midiOutputLoop();
|
giuliomoro@181
|
312 static bool staticConstructed;
|
giuliomoro@181
|
313 static void staticConstructor();
|
giuliomoro@181
|
314 private:
|
giuliomoro@199
|
315 int _getInput();
|
giuliomoro@181
|
316 void readInputLoop();
|
giuliomoro@191
|
317 void writeOutputLoop();
|
giuliomoro@181
|
318 int outputPort;
|
giuliomoro@181
|
319 int inputPort;
|
giuliomoro@181
|
320 std::vector<midi_byte_t> inputBytes;
|
giuliomoro@181
|
321 unsigned int inputBytesWritePointer;
|
giuliomoro@181
|
322 unsigned int inputBytesReadPointer;
|
giuliomoro@181
|
323 std::vector<midi_byte_t> outputBytes;
|
giuliomoro@191
|
324 unsigned int outputBytesWritePointer;
|
giuliomoro@191
|
325 unsigned int outputBytesReadPointer;
|
giuliomoro@197
|
326 MidiParser* inputParser;
|
giuliomoro@197
|
327 bool parserEnabled;
|
giuliomoro@191
|
328 static std::vector<Midi*> objAddrs[2];
|
giuliomoro@181
|
329 static AuxiliaryTask midiInputTask;
|
giuliomoro@181
|
330 static AuxiliaryTask midiOutputTask;
|
giuliomoro@181
|
331 };
|
giuliomoro@181
|
332
|
giuliomoro@181
|
333
|
giuliomoro@181
|
334 #endif /* MIDI_H_ */
|