Subversion Repositories svn.mios32

Rev

Rev 69 | Rev 80 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
32 tk 1
// $Id: mios32_midi.c 78 2008-10-12 22:09:23Z tk $
2
/*
3
 * MIDI layer functions for MIOS32
4
 *
33 tk 5
 * the mios32_midi_package_t format complies with USB MIDI spec (details see there)
6
 * and is used for transfers between other MIDI ports as well.
7
 *
32 tk 8
 * ==========================================================================
9
 *
10
 *  Copyright (C) 2008 Thorsten Klose (tk@midibox.org)
11
 *  Licensed for personal non-commercial use only.
12
 *  All other rights reserved.
13
 *
14
 * ==========================================================================
15
 */
16
 
17
/////////////////////////////////////////////////////////////////////////////
18
// Include files
19
/////////////////////////////////////////////////////////////////////////////
20
 
21
#include <mios32.h>
22
 
23
// this module can be optionally disabled in a local mios32_config.h file (included from mios32.h)
24
#if !defined(MIOS32_DONT_USE_MIDI)
25
 
26
 
27
/////////////////////////////////////////////////////////////////////////////
28
// Global variables
29
/////////////////////////////////////////////////////////////////////////////
30
 
31
 
32
/////////////////////////////////////////////////////////////////////////////
33
// Local variables
34
/////////////////////////////////////////////////////////////////////////////
35
 
36
 
37
/////////////////////////////////////////////////////////////////////////////
38
// Initializes MIDI layer
34 tk 39
// IN: <mode>: 0: MIOS32_MIDI_Send* works in blocking mode - function will
40
//                (shortly) stall if the output buffer is full
41
//             1: MIOS32_MIDI_Send* works in non-blocking mode - function will
42
//                return -2 if buffer is full, the caller has to loop if this
43
//                value is returned until the transfer was successful
44
//                A common method is to release the RTOS task for 1 mS
45
//                so that other tasks can be executed until the sender can
46
//                continue
32 tk 47
// OUT: returns < 0 if initialisation failed
48
/////////////////////////////////////////////////////////////////////////////
49
s32 MIOS32_MIDI_Init(u32 mode)
50
{
51
  s32 ret = 0;
52
 
34 tk 53
  // currently only mode 0 and 1 (blocking/non-blocking) supported
54
  if( mode != 0 && mode != 1 )
32 tk 55
    return -1; // unsupported mode
56
 
57
#if !defined(MIOS32_DONT_USE_USB)
78 tk 58
  if( MIOS32_USB_MIDI_Init(mode) < 0 )
32 tk 59
    ret |= (1 << 0);
60
#endif
61
 
78 tk 62
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
63
  if( MIOS32_IIC_MIDI_Init(mode) < 0 )
64
    ret |= (1 << 1);
65
#endif
66
 
32 tk 67
  return -ret;
68
}
69
 
70
 
71
/////////////////////////////////////////////////////////////////////////////
78 tk 72
// This function checks the availability of a MIDI port
73
// IN: <port>: MIDI port 
74
//             DEFAULT, USB0..USB7, UART0..UART1, IIC0..IIC7
75
// OUT: 1: port available
76
//      0: port not available
77
/////////////////////////////////////////////////////////////////////////////
78
s32 MIOS32_MIDI_CheckAvailable(mios32_midi_port_t port)
79
{
80
  // if default port: select mapped port
81
  if( !(port & 0xf0) ) {
82
    port = MIOS32_MIDI_DEFAULT_PORT;
83
  }
84
 
85
  // branch depending on selected port
86
  switch( port >> 4 ) {
87
    case 1:
88
#if !defined(MIOS32_DONT_USE_USB) && !defined(MIOS32_DONT_USE_USB_MIDI)
89
      return MIOS32_USB_MIDI_CheckAvailable();
90
#else
91
      return 0; // USB has been disabled
92
#endif
93
 
94
    case 2:
95
      return 0; // USART not implemented yet
96
 
97
    case 3:
98
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
99
      return MIOS32_IIC_MIDI_CheckAvailable(port & 0xf);
100
#else
101
      return 0; // IIC_MIDI has been disabled
102
#endif
103
 
104
    case 4:
105
      return 0; // Ethernet not implemented yet
106
 
107
    default:
108
      // invalid port
109
      return 0;
110
  }
111
}
112
 
113
 
114
/////////////////////////////////////////////////////////////////////////////
32 tk 115
// Sends a package over given port
33 tk 116
// This is a low level function - use the remaining MIOS32_MIDI_Send* functions
117
// to send specific MIDI events
32 tk 118
// IN: <port>: MIDI port 
78 tk 119
//             DEFAULT, USB0..USB7, UART0..UART1, IIC0..IIC7
32 tk 120
//     <package>: MIDI package (see definition in mios32_midi.h)
121
// OUT: returns -1 if port not available
34 tk 122
//      returns -2 if non-blocking mode activated: buffer is full
123
//                 caller should retry until buffer is free again
32 tk 124
//      returns 0 on success
125
/////////////////////////////////////////////////////////////////////////////
69 tk 126
s32 MIOS32_MIDI_SendPackage(mios32_midi_port_t port, mios32_midi_package_t package)
32 tk 127
{
128
  // insert subport number into package
33 tk 129
  package.type = (package.type&0x0f) | (port << 4);
32 tk 130
 
131
  // branch depending on selected port
132
  switch( port >> 4 ) {
133
    case 0:
78 tk 134
#if !defined(MIOS32_DONT_USE_USB) && !defined(MIOS32_DONT_USE_USB_MIDI)
135
      return MIOS32_USB_MIDI_MIDIPackageSend(package);
32 tk 136
#else
137
      return -1; // USB has been disabled
138
#endif
139
 
140
    case 1:
141
      return -1; // USART not implemented yet
142
 
143
    case 2:
78 tk 144
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
145
      return MIOS32_IIC_MIDI_PackageSend(package.cable, package);
146
#else
147
      return -1; // IIC_MIDI has been disabled
148
#endif
32 tk 149
 
150
    case 3:
151
      return -1; // Ethernet not implemented yet
152
 
153
    default:
154
      // invalid port
155
      return -1;
156
  }
157
}
158
 
159
 
160
/////////////////////////////////////////////////////////////////////////////
33 tk 161
// Sends a MIDI Event
162
// This function is provided for a more comfortable use model
163
//    o MIOS32_MIDI_SendNoteOff(port, chn, note, vel)
164
//    o MIOS32_MIDI_SendNoteOn(port, chn, note, vel)
165
//    o MIOS32_MIDI_SendPolyAftertouch(port, chn, note, val)
166
//    o MIOS32_MIDI_SendCC(port, chn, cc, val)
167
//    o MIOS32_MIDI_SendProgramChange(port, chn, prg)
168
//    o MIOS32_MIDI_ChannelAftertouch(port, chn, val)
169
//    o MIOS32_MIDI_PitchBend(port, chn, val)
170
//
171
// IN: <port>: MIDI port 
172
//     <evnt0> <evnt1> <evnt2> up to 3 bytes
173
// OUT: returns -1 if port not available
34 tk 174
//      returns -2 if non-blocking mode activated: buffer is full
175
//                 caller should retry until buffer is free again
33 tk 176
//      returns 0 on success
32 tk 177
/////////////////////////////////////////////////////////////////////////////
69 tk 178
s32 MIOS32_MIDI_SendEvent(mios32_midi_port_t port, u8 evnt0, u8 evnt1, u8 evnt2)
32 tk 179
{
33 tk 180
  mios32_midi_package_t package;
181
 
182
  // MEMO: don't optimize this function by calling MIOS32_MIDI_SendSpecialEvent
183
  // from here, because the 4 * u8 parameter list of this function leads
184
  // to best compile results (4*u8 combined to a single u32)
185
 
186
  package.type  = evnt0 >> 4;
187
  package.evnt0 = evnt0;
188
  package.evnt1 = evnt1;
189
  package.evnt2 = evnt2;
190
  return MIOS32_MIDI_SendPackage(port, package);
32 tk 191
}
192
 
69 tk 193
s32 MIOS32_MIDI_SendNoteOff(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 note, u8 vel)
194
{ MIOS32_MIDI_SendEvent(port, 0x80 | chn, note, vel); }
32 tk 195
 
69 tk 196
s32 MIOS32_MIDI_SendNoteOn(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 note, u8 vel)
197
{ MIOS32_MIDI_SendEvent(port, 0x90 | chn, note, vel); }
198
 
199
s32 MIOS32_MIDI_SendPolyPressure(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 note, u8 val)
200
{ MIOS32_MIDI_SendEvent(port, 0xa0 | chn, note, val); }
201
 
202
s32 MIOS32_MIDI_SendCC(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 cc, u8 val)
203
{ MIOS32_MIDI_SendEvent(port, 0xb0 | chn, cc,   val); }
204
 
205
s32 MIOS32_MIDI_SendProgramChange(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 prg)
206
{ MIOS32_MIDI_SendEvent(port, 0xc0 | chn, prg,  0x00); }
207
 
208
s32 MIOS32_MIDI_SendAftertouch(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 val)
209
{ MIOS32_MIDI_SendEvent(port, 0xd0 | chn, val,  0x00); }
210
 
211
s32 MIOS32_MIDI_SendPitchBend(mios32_midi_port_t port, mios32_midi_chn_t chn, u16 val)
212
{ MIOS32_MIDI_SendEvent(port, 0xe0 | chn, val & 0x7f, val >> 7); }
213
 
214
 
33 tk 215
/////////////////////////////////////////////////////////////////////////////
216
// Sends a special type MIDI Event
217
// This function is provided for a more comfortable use model
69 tk 218
// It is aliased to following functions
33 tk 219
//    o MIOS32_MIDI_SendMTC(port, val)
220
//    o MIOS32_MIDI_SendSongPosition(port, val)
221
//    o MIOS32_MIDI_SendSongSelect(port, val)
222
//    o MIOS32_MIDI_SendTuneRequest()
223
//    o MIOS32_MIDI_SendClock()
224
//    o MIOS32_MIDI_SendTick()
225
//    o MIOS32_MIDI_SendStart()
226
//    o MIOS32_MIDI_SendStop()
227
//    o MIOS32_MIDI_SendContinue()
228
//    o MIOS32_MIDI_SendActiveSense()
229
//    o MIOS32_MIDI_SendReset()
230
//
231
// IN: <port>: MIDI port 
232
//     <type>: the event type
233
//     <evnt0> <evnt1> <evnt2> up to 3 bytes
234
// OUT: returns -1 if port not available
34 tk 235
//      returns -2 if non-blocking mode activated: buffer is full
236
//                 caller should retry until buffer is free again
33 tk 237
//      returns 0 on success
238
/////////////////////////////////////////////////////////////////////////////
69 tk 239
s32 MIOS32_MIDI_SendSpecialEvent(mios32_midi_port_t port, u8 type, u8 evnt0, u8 evnt1, u8 evnt2)
33 tk 240
{
241
  mios32_midi_package_t package;
242
 
243
  package.type  = type;
244
  package.evnt0 = evnt0;
245
  package.evnt1 = evnt1;
246
  package.evnt2 = evnt2;
247
  return MIOS32_MIDI_SendPackage(port, package);
248
}
249
 
250
 
69 tk 251
s32 MIOS32_MIDI_SendMTC(mios32_midi_port_t port, u8 val)
252
{ MIOS32_MIDI_SendSpecialEvent(port, 0x2, 0xf1, val, 0x00); }
253
 
254
s32 MIOS32_MIDI_SendSongPosition(mios32_midi_port_t port, u16 val)
255
{ MIOS32_MIDI_SendSpecialEvent(port, 0x3, 0xf2, val & 0x7f, val >> 7); }
256
 
257
s32 MIOS32_MIDI_SendSongSelect(mios32_midi_port_t port, u8 val)
258
{ MIOS32_MIDI_SendSpecialEvent(port, 0x2, 0xf3, val, 0x00); }
259
 
260
s32 MIOS32_MIDI_SendTuneRequest(mios32_midi_port_t port)
261
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xf6, 0x00, 0x00); }
262
 
263
s32 MIOS32_MIDI_SendClock(mios32_midi_port_t port)
264
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xf8, 0x00, 0x00); }
265
 
266
s32 MIOS32_MIDI_SendTick(mios32_midi_port_t port)
267
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xf9, 0x00, 0x00); }
268
 
269
s32 MIOS32_MIDI_SendStart(mios32_midi_port_t port)
270
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xfa, 0x00, 0x00); }
271
 
272
s32 MIOS32_MIDI_SendStop(mios32_midi_port_t port)
273
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xfb, 0x00, 0x00); }
274
 
275
s32 MIOS32_MIDI_SendContinue(mios32_midi_port_t port)
276
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xfc, 0x00, 0x00); }
277
 
278
s32 MIOS32_MIDI_SendActiveSense(mios32_midi_port_t port)
279
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xfe, 0x00, 0x00); }
280
 
281
s32 MIOS32_MIDI_SendReset(mios32_midi_port_t port)
282
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xff, 0x00, 0x00); }
283
 
284
 
33 tk 285
/////////////////////////////////////////////////////////////////////////////
286
// Sends a SysEx Stream
287
// This function is provided for a more comfortable use model
288
// IN: <port>: MIDI port 
289
//     <stream>: pointer to SysEx stream
290
//     <count>: number of bytes
291
// OUT: returns -1 if port not available
34 tk 292
//      returns -2 if non-blocking mode activated: buffer is full
293
//                 caller should retry until buffer is free again
33 tk 294
//      returns 0 on success
295
/////////////////////////////////////////////////////////////////////////////
69 tk 296
s32 MIOS32_MIDI_SendSysEx(mios32_midi_port_t port, u8 *stream, u32 count)
33 tk 297
{
298
  s32 res;
299
  u32 offset;
300
  mios32_midi_package_t package;
301
 
302
  // MEMO: have a look into the project.lss file - gcc optimizes this code pretty well :)
303
 
304
  for(offset=0; offset<count;) {
305
    // package type depends on number of remaining bytes
306
    switch( count-offset ) {
307
      case 1:
308
    package.type = 0x5; // SysEx ends with following single byte. 
309
    package.evnt0 = stream[offset++];
310
    package.evnt1 = 0x00;
311
    package.evnt2 = 0x00;
312
    break;
313
      case 2:
314
    package.type = 0x6; // SysEx ends with following two bytes.
315
    package.evnt0 = stream[offset++];
316
    package.evnt1 = stream[offset++];
317
    package.evnt2 = 0x00;
318
    break;
319
      case 3:
320
    package.type = 0x7; // SysEx ends with following three bytes. 
321
    package.evnt0 = stream[offset++];
322
    package.evnt1 = stream[offset++];
323
    package.evnt2 = stream[offset++];
324
    break;
325
      default:
326
    package.type = 0x4; // SysEx starts or continues
327
    package.evnt0 = stream[offset++];
328
    package.evnt1 = stream[offset++];
329
    package.evnt2 = stream[offset++];
330
    }
331
 
332
    while( (res=MIOS32_MIDI_SendPackage(port, package)) == -2 ) {
34 tk 333
      // TODO: SysEx always sent in blocking mode to avoid inconsistent stream!
334
      // We poll until buffer is free again.
33 tk 335
      // Are there better ways?
336
    }
337
 
338
    // other expection? (e.g., port not available)
339
    if( res < 0 )
340
      return res;
341
  }
342
 
343
  return 0;
344
}
345
 
346
 
347
/////////////////////////////////////////////////////////////////////////////
348
// Checks for incoming MIDI messages, calls either the callback_event or
349
// callback_sysex function with following parameters:
69 tk 350
//    callback_event(mios32_midi_port_t port, mios32_midi_package_t midi_package)
351
//    callback_sysex(mios32_midi_port_t port, u8 sysex_byte)
33 tk 352
// OUT: returns < 0 on errors
353
/////////////////////////////////////////////////////////////////////////////
354
s32 MIOS32_MIDI_Receive_Handler(void *_callback_event, void *_callback_sysex)
355
{
356
  u8 port;
357
  mios32_midi_package_t package;
358
 
69 tk 359
  void (*callback_event)(mios32_midi_port_t port, mios32_midi_package_t midi_package) = _callback_event;
360
  void (*callback_sysex)(mios32_midi_port_t port, u8 sysex_byte) = _callback_sysex;
33 tk 361
 
78 tk 362
  u8 intf = 0; // interface to be checked
363
  u8 total_packages_forwarded = 0; // number of forwards - stop after 10 forwards to yield some CPU time for other tasks
364
  u8 packages_forwarded = 0;
365
  u8 again = 1;
33 tk 366
  do {
78 tk 367
    // Round Robin
368
    // TODO: maybe a list based approach would be better
369
    // it would allow to add/remove interfaces dynamically
370
    // this would also allow to give certain ports a higher priority (to add them multiple times to the list)
371
    // it would also improve this spagetthi code ;)
372
    s32 error = -1;
373
    switch( intf++ ) {
374
#if !defined(MIOS32_DONT_USE_USB) && !defined(MIOS32_DONT_USE_USB_MIDI)
375
      case 0: error = MIOS32_USB_MIDI_MIDIPackageReceive(&package); port = package.cable; break;
376
#else
377
      case 0: error = -1; break;
378
#endif
379
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
380
      case 1: error = MIOS32_IIC_MIDI_PackageReceive(0, &package); port = IIC0; break;
381
#else
382
      case 1: error = -1; break;
383
#endif
384
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
385
      case 2: error = MIOS32_IIC_MIDI_PackageReceive(1, &package); port = IIC1; break;
386
#else
387
      case 2: error = -1; break;
388
#endif
389
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
390
      case 3: error = MIOS32_IIC_MIDI_PackageReceive(2, &package); port = IIC2; break;
391
#else
392
      case 3: error = -1; break;
393
#endif
394
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
395
      case 4: error = MIOS32_IIC_MIDI_PackageReceive(3, &package); port = IIC3; break;
396
#else
397
      case 4: error = -1; break;
398
#endif
399
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
400
      case 5: error = MIOS32_IIC_MIDI_PackageReceive(4, &package); port = IIC4; break;
401
#else
402
      case 5: error = -1; break;
403
#endif
404
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
405
      case 6: error = MIOS32_IIC_MIDI_PackageReceive(5, &package); port = IIC5; break;
406
#else
407
      case 6: error = -1; break;
408
#endif
409
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
410
      case 7: error = MIOS32_IIC_MIDI_PackageReceive(6, &package); port = IIC6; break;
411
#else
412
      case 7: error = -1; break;
413
#endif
414
#if !defined(MIOS32_DONT_USE_IIC) && !defined(MIOS32_DONT_USE_IIC_MIDI)
415
      case 8: error = MIOS32_IIC_MIDI_PackageReceive(7, &package); port = IIC7; break;
416
#else
417
      case 8: error = -1; break;
418
#endif
419
      default:
420
    // allow 10 forwards maximum to yield some CPU time for other tasks
421
    if( packages_forwarded && total_packages_forwarded < 10 ) {
422
      intf = 0; // restart with USB
423
      packages_forwarded = 0; // for checking, if packages still have been forwarded in next round
424
    } else {
425
      again = 0; // no more interfaces to be processed
426
    }
427
    error = -1; // empty round - no message
428
    }
33 tk 429
 
78 tk 430
    // message received?
431
    if( error >= 0 ) {
432
      // notify that a package has been forwarded
433
      ++packages_forwarded;
434
      ++total_packages_forwarded;
33 tk 435
 
78 tk 436
      // remove cable number from package (MIOS32_MIDI passes it's own port number)
69 tk 437
      package.cable = 0;
33 tk 438
 
78 tk 439
      // branch depending on package type
33 tk 440
      if( package.type >= 0x8 ) {
441
    callback_event(port, package);
442
      } else {
443
    switch( package.type ) {
444
    case 0x0: // reserved, ignore
445
    case 0x1: // cable events, ignore
446
      break;
447
 
448
    case 0x2: // Two-byte System Common messages like MTC, SongSelect, etc. 
449
    case 0x3: // Three-byte System Common messages like SPP, etc. 
450
      callback_event(port, package); // -> forwarded as event
451
      break;
452
    case 0x4: // SysEx starts or continues (3 bytes)
453
      callback_sysex(port, package.evnt0); // -> forwarded as SysEx
454
      callback_sysex(port, package.evnt1); // -> forwarded as SysEx
455
      callback_sysex(port, package.evnt2); // -> forwarded as SysEx
456
      break;
457
    case 0x5: // Single-byte System Common Message or SysEx ends with following single byte. 
458
      if( package.evnt0 >= 0xf8 )
459
        callback_event(port, package); // -> forwarded as event
460
      else
461
        callback_sysex(port, package.evnt0); // -> forwarded as SysEx
462
      break;
463
    case 0x6: // SysEx ends with following two bytes.
464
      callback_sysex(port, package.evnt0); // -> forwarded as SysEx
465
      callback_sysex(port, package.evnt1); // -> forwarded as SysEx
466
      break;
467
    case 0x7: // SysEx ends with following three bytes.
468
      callback_sysex(port, package.evnt0); // -> forwarded as SysEx
469
      callback_sysex(port, package.evnt1); // -> forwarded as SysEx
470
      callback_sysex(port, package.evnt2); // -> forwarded as SysEx
471
      break;
472
    }
473
      }
474
    }
475
  } while( again );
476
 
477
  return 0;
478
}
479
 
480
 
32 tk 481
#endif /* MIOS32_DONT_USE_MIDI */