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@224
|
118 void (*messageReadyCallback)(MidiChannelMessage);
|
giuliomoro@224
|
119 bool callbackEnabled;
|
giuliomoro@197
|
120 public:
|
giuliomoro@197
|
121 MidiParser(){
|
giuliomoro@197
|
122 waitingForStatus = true;
|
giuliomoro@197
|
123 elapsedDataBytes= 0;
|
giuliomoro@197
|
124 messages.resize(100); // 100 is the number of messages that can be buffered
|
giuliomoro@197
|
125 writePointer = 0;
|
giuliomoro@197
|
126 readPointer = 0;
|
giuliomoro@224
|
127 callbackEnabled = false;
|
giuliomoro@224
|
128 messageReadyCallback = NULL;
|
giuliomoro@197
|
129 }
|
giuliomoro@197
|
130
|
giuliomoro@197
|
131 /**
|
giuliomoro@197
|
132 * Parses some midi messages.
|
giuliomoro@197
|
133 *
|
giuliomoro@197
|
134 * @param input the array to read from
|
giuliomoro@197
|
135 * @param length the maximum number of values available at the array
|
giuliomoro@197
|
136 *
|
giuliomoro@197
|
137 * @return the number of bytes parsed
|
giuliomoro@197
|
138 */
|
giuliomoro@197
|
139 int parse(midi_byte_t* input, unsigned int length);
|
giuliomoro@224
|
140
|
giuliomoro@225
|
141 /**
|
giuliomoro@225
|
142 * Sets the callback to call when a new MidiChannelMessage is available
|
giuliomoro@225
|
143 * from the input port.
|
giuliomoro@225
|
144 *
|
giuliomoro@225
|
145 * In order to deactivate the callback, call this method with NULL as an
|
giuliomoro@225
|
146 * argument.
|
giuliomoro@225
|
147 * While the callback is enabled, calling numAvailableMessages() and
|
giuliomoro@225
|
148 * getNextChannelMessage() is still possible, but it will probably always
|
giuliomoro@225
|
149 * return 0 as the callback is called as soon as a new message is available.
|
giuliomoro@225
|
150 *
|
giuliomoro@225
|
151 * @param newCallback the callback function. This function
|
giuliomoro@225
|
152 * should takes one argument: the MidiChannelMessage .
|
giuliomoro@225
|
153 *
|
giuliomoro@225
|
154 */
|
giuliomoro@224
|
155 void setCallback(void (*newCallback)(MidiChannelMessage)){
|
giuliomoro@224
|
156 messageReadyCallback = newCallback;
|
giuliomoro@224
|
157 if(newCallback != NULL){
|
giuliomoro@224
|
158 callbackEnabled = true;
|
giuliomoro@224
|
159 } else {
|
giuliomoro@224
|
160 callbackEnabled = false;
|
giuliomoro@224
|
161 }
|
giuliomoro@197
|
162 };
|
giuliomoro@224
|
163
|
giuliomoro@225
|
164 /**
|
giuliomoro@225
|
165 * Checks whether there is a callback currently set to be called
|
giuliomoro@225
|
166 * every time a new input MidiChannelMessage is available from the
|
giuliomoro@225
|
167 * input port.
|
giuliomoro@225
|
168 */
|
giuliomoro@224
|
169 bool isCallbackEnabled(){
|
giuliomoro@224
|
170 return callbackEnabled;
|
giuliomoro@224
|
171 };
|
giuliomoro@224
|
172
|
giuliomoro@225
|
173 /**
|
giuliomoro@225
|
174 * Returns the number of unread MidiChannelMessage available from the
|
giuliomoro@225
|
175 * input port.
|
giuliomoro@225
|
176 *
|
giuliomoro@225
|
177 */
|
giuliomoro@225
|
178
|
giuliomoro@197
|
179 int numAvailableMessages(){
|
giuliomoro@197
|
180 int num = (writePointer - readPointer + messages.size() ) % messages.size();
|
giuliomoro@197
|
181 if(num > 0){
|
giuliomoro@197
|
182 int a = a +1;
|
giuliomoro@224
|
183 a = readPointer;
|
giuliomoro@197
|
184 }
|
giuliomoro@197
|
185 return num;
|
giuliomoro@197
|
186 }
|
giuliomoro@225
|
187
|
giuliomoro@197
|
188 /**
|
giuliomoro@197
|
189 * Get the oldest channel message in the buffer.
|
giuliomoro@197
|
190 *
|
giuliomoro@199
|
191 * If this method is called when numAvailableMessages()==0, then
|
giuliomoro@199
|
192 * a message with all fields set to zero is returned.
|
giuliomoro@225
|
193 *
|
giuliomoro@197
|
194 * @param type the type of the message to retrieve
|
giuliomoro@225
|
195 *
|
giuliomoro@197
|
196 * @return a copy of the oldest message of the give type in the buffer
|
giuliomoro@197
|
197 */
|
giuliomoro@197
|
198 MidiChannelMessage getNextChannelMessage(/*MidiMessageType type*/){
|
giuliomoro@197
|
199 MidiChannelMessage message;
|
giuliomoro@197
|
200 message = messages[readPointer];
|
giuliomoro@197
|
201 if(message.getType() == kmmNone){
|
giuliomoro@197
|
202 message.clear();
|
giuliomoro@197
|
203 }
|
giuliomoro@197
|
204 messages[readPointer].setType(kmmNone); // do not use it again
|
giuliomoro@197
|
205 readPointer++;
|
giuliomoro@197
|
206 if(readPointer == messages.size()){
|
giuliomoro@197
|
207 readPointer = 0;
|
giuliomoro@197
|
208 }
|
giuliomoro@197
|
209 return message;
|
giuliomoro@197
|
210 };
|
giuliomoro@197
|
211
|
giuliomoro@197
|
212 // MidiChannelMessage getNextChannelMessage(){
|
giuliomoro@197
|
213 // getNextChannelMessage(kmmAny);
|
giuliomoro@197
|
214 // }
|
giuliomoro@197
|
215 // MidiControlChangeMessage* getNextControlChangeMessage(){
|
giuliomoro@197
|
216 // return (MidiControlChangeMessage*)getNextChannelMessage(kmmControlChange);
|
giuliomoro@197
|
217 // };
|
giuliomoro@197
|
218 // MidiProgramChangeMessage* getNextProgramChangeMessage(){
|
giuliomoro@197
|
219 // return (MidiProgramChangeMessage*)getNextChannelMessage(kmmProgramChange);
|
giuliomoro@197
|
220 // };
|
giuliomoro@197
|
221 // MidiNoteMessage* getNextNoteOnMessage(){
|
giuliomoro@197
|
222 // return (MidiNoteMessage*)getNextChannelMessage(kmmNoteOn);
|
giuliomoro@197
|
223 // };
|
giuliomoro@197
|
224 };
|
giuliomoro@197
|
225
|
giuliomoro@197
|
226
|
giuliomoro@181
|
227 class Midi {
|
giuliomoro@181
|
228 public:
|
giuliomoro@181
|
229 Midi();
|
giuliomoro@197
|
230
|
giuliomoro@197
|
231 /**
|
giuliomoro@197
|
232 * Enable the input MidiParser.
|
giuliomoro@197
|
233 *
|
giuliomoro@199
|
234 * If the parser is enabled, getInput() will return an error code.
|
giuliomoro@197
|
235 * Midi messages should instead be retrieved via, e.g.: getMidiParser()->getNextChannelMessage();
|
giuliomoro@197
|
236 *
|
giuliomoro@197
|
237 * @param enable true to enable the input MidiParser, false to disable it.
|
giuliomoro@197
|
238 */
|
giuliomoro@197
|
239 void enableParser(bool enable);
|
giuliomoro@197
|
240
|
giuliomoro@197
|
241 /**
|
giuliomoro@197
|
242 * Get access to the input parser in use, if any.
|
giuliomoro@197
|
243 *
|
giuliomoro@197
|
244 * @return a pointer to the instance of MidiParser, if currently enabled, zero otherwise.
|
giuliomoro@197
|
245 */
|
giuliomoro@197
|
246 MidiParser* getParser();
|
giuliomoro@197
|
247
|
giuliomoro@225
|
248 /**
|
giuliomoro@225
|
249 * Sets the callback to call when a new MidiChannelMessage is available
|
giuliomoro@225
|
250 * from the input port.
|
giuliomoro@225
|
251 *
|
giuliomoro@225
|
252 * Internally, it calls enableParser() and the MidiParser::setCallback();
|
giuliomoro@225
|
253 *
|
giuliomoro@225
|
254 * @param newCallback the callback function. This function
|
giuliomoro@225
|
255 * should takes one argument: the MidiChannelMessage .
|
giuliomoro@225
|
256 */
|
giuliomoro@224
|
257 void setParserCallback(void (*callback)(MidiChannelMessage)){
|
giuliomoro@224
|
258 // if callback is not NULL, also enable the parser
|
giuliomoro@224
|
259 enableParser(callback != NULL); //this needs to be first, as it deletes the parser(if exists)
|
giuliomoro@224
|
260 getParser()->setCallback(callback);
|
giuliomoro@224
|
261 }
|
giuliomoro@224
|
262
|
giuliomoro@181
|
263 /**
|
giuliomoro@181
|
264 * Open the specified input Midi port and start reading from it.
|
giuliomoro@181
|
265 * @param port Midi port to open
|
giuliomoro@181
|
266 * @return 1 on success, -1 on failure
|
giuliomoro@181
|
267 */
|
giuliomoro@181
|
268 int readFrom(int port);
|
giuliomoro@181
|
269 /**
|
giuliomoro@181
|
270 * Open the specified output Midi port and prepares to write to 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 writeTo(int port);
|
giuliomoro@181
|
275
|
giuliomoro@181
|
276 /**
|
giuliomoro@191
|
277 * Get received midi bytes, one at a time.
|
giuliomoro@181
|
278 * @return -1 if no new byte is available, -2 on error,
|
giuliomoro@181
|
279 * the oldest not yet retrieved midi byte otherwise
|
giuliomoro@181
|
280 */
|
giuliomoro@181
|
281 int getInput();
|
giuliomoro@181
|
282
|
giuliomoro@181
|
283 /**
|
giuliomoro@181
|
284 * Writes a Midi byte to the output port
|
giuliomoro@181
|
285 * @param byte the Midi byte to write
|
giuliomoro@181
|
286 * @return 1 on success, -1 on error
|
giuliomoro@181
|
287 */
|
giuliomoro@181
|
288 int writeOutput(midi_byte_t byte);
|
giuliomoro@181
|
289
|
giuliomoro@181
|
290 /**
|
giuliomoro@181
|
291 * Writes Midi bytes to the output port
|
giuliomoro@181
|
292 * @param bytes an array of bytes to be written
|
giuliomoro@181
|
293 * @param length number of bytes to write
|
giuliomoro@181
|
294 * @return 1 on success, -1 on error
|
giuliomoro@181
|
295 */
|
giuliomoro@191
|
296 int writeOutput(midi_byte_t* bytes, unsigned int length);
|
giuliomoro@197
|
297 /**
|
giuliomoro@197
|
298 * Gives access to the midi parser, if it has been activated.
|
giuliomoro@197
|
299 *
|
giuliomoro@197
|
300 * @return a pointer to the midi parser if active, zero otherwise
|
giuliomoro@197
|
301 */
|
giuliomoro@197
|
302 MidiParser* getMidiParser();
|
giuliomoro@181
|
303 virtual ~Midi();
|
giuliomoro@181
|
304 static void midiInputLoop();
|
giuliomoro@191
|
305 static void midiOutputLoop();
|
giuliomoro@181
|
306 static bool staticConstructed;
|
giuliomoro@181
|
307 static void staticConstructor();
|
giuliomoro@181
|
308 private:
|
giuliomoro@199
|
309 int _getInput();
|
giuliomoro@181
|
310 void readInputLoop();
|
giuliomoro@191
|
311 void writeOutputLoop();
|
giuliomoro@181
|
312 int outputPort;
|
giuliomoro@181
|
313 int inputPort;
|
giuliomoro@181
|
314 std::vector<midi_byte_t> inputBytes;
|
giuliomoro@181
|
315 unsigned int inputBytesWritePointer;
|
giuliomoro@181
|
316 unsigned int inputBytesReadPointer;
|
giuliomoro@181
|
317 std::vector<midi_byte_t> outputBytes;
|
giuliomoro@191
|
318 unsigned int outputBytesWritePointer;
|
giuliomoro@191
|
319 unsigned int outputBytesReadPointer;
|
giuliomoro@197
|
320 MidiParser* inputParser;
|
giuliomoro@197
|
321 bool parserEnabled;
|
giuliomoro@191
|
322 static std::vector<Midi*> objAddrs[2];
|
giuliomoro@181
|
323 static AuxiliaryTask midiInputTask;
|
giuliomoro@181
|
324 static AuxiliaryTask midiOutputTask;
|
giuliomoro@181
|
325 };
|
giuliomoro@181
|
326
|
giuliomoro@181
|
327
|
giuliomoro@181
|
328 #endif /* MIDI_H_ */
|