Subversion Repositories svn.mios32

Rev

Rev 34 | Rev 78 | 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 69 2008-10-06 22:24:22Z 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)
58
  if( MIOS32_USB_Init(mode) < 0 )
59
    ret |= (1 << 0);
60
#endif
61
 
62
  return -ret;
63
}
64
 
65
 
66
/////////////////////////////////////////////////////////////////////////////
67
// Sends a package over given port
33 tk 68
// This is a low level function - use the remaining MIOS32_MIDI_Send* functions
69
// to send specific MIDI events
32 tk 70
// IN: <port>: MIDI port 
69 tk 71
//             USB0..USB7, UART0..UART1, IIC0..IIC3
32 tk 72
//     <package>: MIDI package (see definition in mios32_midi.h)
73
// OUT: returns -1 if port not available
34 tk 74
//      returns -2 if non-blocking mode activated: buffer is full
75
//                 caller should retry until buffer is free again
32 tk 76
//      returns 0 on success
77
/////////////////////////////////////////////////////////////////////////////
69 tk 78
s32 MIOS32_MIDI_SendPackage(mios32_midi_port_t port, mios32_midi_package_t package)
32 tk 79
{
80
  // insert subport number into package
33 tk 81
  package.type = (package.type&0x0f) | (port << 4);
32 tk 82
 
83
  // branch depending on selected port
84
  switch( port >> 4 ) {
85
    case 0:
86
#if !defined(MIOS32_DONT_USE_USB)
69 tk 87
      return MIOS32_USB_MIDIPackageSend(package);
32 tk 88
#else
89
      return -1; // USB has been disabled
90
#endif
91
 
92
    case 1:
93
      return -1; // USART not implemented yet
94
 
95
    case 2:
96
      return -1; // IIC not implemented yet
97
 
98
    case 3:
99
      return -1; // Ethernet not implemented yet
100
 
101
    default:
102
      // invalid port
103
      return -1;
104
  }
105
}
106
 
107
 
108
/////////////////////////////////////////////////////////////////////////////
33 tk 109
// Sends a MIDI Event
110
// This function is provided for a more comfortable use model
111
//    o MIOS32_MIDI_SendNoteOff(port, chn, note, vel)
112
//    o MIOS32_MIDI_SendNoteOn(port, chn, note, vel)
113
//    o MIOS32_MIDI_SendPolyAftertouch(port, chn, note, val)
114
//    o MIOS32_MIDI_SendCC(port, chn, cc, val)
115
//    o MIOS32_MIDI_SendProgramChange(port, chn, prg)
116
//    o MIOS32_MIDI_ChannelAftertouch(port, chn, val)
117
//    o MIOS32_MIDI_PitchBend(port, chn, val)
118
//
119
// IN: <port>: MIDI port 
120
//     <evnt0> <evnt1> <evnt2> up to 3 bytes
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
33 tk 124
//      returns 0 on success
32 tk 125
/////////////////////////////////////////////////////////////////////////////
69 tk 126
s32 MIOS32_MIDI_SendEvent(mios32_midi_port_t port, u8 evnt0, u8 evnt1, u8 evnt2)
32 tk 127
{
33 tk 128
  mios32_midi_package_t package;
129
 
130
  // MEMO: don't optimize this function by calling MIOS32_MIDI_SendSpecialEvent
131
  // from here, because the 4 * u8 parameter list of this function leads
132
  // to best compile results (4*u8 combined to a single u32)
133
 
134
  package.type  = evnt0 >> 4;
135
  package.evnt0 = evnt0;
136
  package.evnt1 = evnt1;
137
  package.evnt2 = evnt2;
138
  return MIOS32_MIDI_SendPackage(port, package);
32 tk 139
}
140
 
69 tk 141
s32 MIOS32_MIDI_SendNoteOff(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 note, u8 vel)
142
{ MIOS32_MIDI_SendEvent(port, 0x80 | chn, note, vel); }
32 tk 143
 
69 tk 144
s32 MIOS32_MIDI_SendNoteOn(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 note, u8 vel)
145
{ MIOS32_MIDI_SendEvent(port, 0x90 | chn, note, vel); }
146
 
147
s32 MIOS32_MIDI_SendPolyPressure(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 note, u8 val)
148
{ MIOS32_MIDI_SendEvent(port, 0xa0 | chn, note, val); }
149
 
150
s32 MIOS32_MIDI_SendCC(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 cc, u8 val)
151
{ MIOS32_MIDI_SendEvent(port, 0xb0 | chn, cc,   val); }
152
 
153
s32 MIOS32_MIDI_SendProgramChange(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 prg)
154
{ MIOS32_MIDI_SendEvent(port, 0xc0 | chn, prg,  0x00); }
155
 
156
s32 MIOS32_MIDI_SendAftertouch(mios32_midi_port_t port, mios32_midi_chn_t chn, u8 val)
157
{ MIOS32_MIDI_SendEvent(port, 0xd0 | chn, val,  0x00); }
158
 
159
s32 MIOS32_MIDI_SendPitchBend(mios32_midi_port_t port, mios32_midi_chn_t chn, u16 val)
160
{ MIOS32_MIDI_SendEvent(port, 0xe0 | chn, val & 0x7f, val >> 7); }
161
 
162
 
33 tk 163
/////////////////////////////////////////////////////////////////////////////
164
// Sends a special type MIDI Event
165
// This function is provided for a more comfortable use model
69 tk 166
// It is aliased to following functions
33 tk 167
//    o MIOS32_MIDI_SendMTC(port, val)
168
//    o MIOS32_MIDI_SendSongPosition(port, val)
169
//    o MIOS32_MIDI_SendSongSelect(port, val)
170
//    o MIOS32_MIDI_SendTuneRequest()
171
//    o MIOS32_MIDI_SendClock()
172
//    o MIOS32_MIDI_SendTick()
173
//    o MIOS32_MIDI_SendStart()
174
//    o MIOS32_MIDI_SendStop()
175
//    o MIOS32_MIDI_SendContinue()
176
//    o MIOS32_MIDI_SendActiveSense()
177
//    o MIOS32_MIDI_SendReset()
178
//
179
// IN: <port>: MIDI port 
180
//     <type>: the event type
181
//     <evnt0> <evnt1> <evnt2> up to 3 bytes
182
// OUT: returns -1 if port not available
34 tk 183
//      returns -2 if non-blocking mode activated: buffer is full
184
//                 caller should retry until buffer is free again
33 tk 185
//      returns 0 on success
186
/////////////////////////////////////////////////////////////////////////////
69 tk 187
s32 MIOS32_MIDI_SendSpecialEvent(mios32_midi_port_t port, u8 type, u8 evnt0, u8 evnt1, u8 evnt2)
33 tk 188
{
189
  mios32_midi_package_t package;
190
 
191
  package.type  = type;
192
  package.evnt0 = evnt0;
193
  package.evnt1 = evnt1;
194
  package.evnt2 = evnt2;
195
  return MIOS32_MIDI_SendPackage(port, package);
196
}
197
 
198
 
69 tk 199
s32 MIOS32_MIDI_SendMTC(mios32_midi_port_t port, u8 val)
200
{ MIOS32_MIDI_SendSpecialEvent(port, 0x2, 0xf1, val, 0x00); }
201
 
202
s32 MIOS32_MIDI_SendSongPosition(mios32_midi_port_t port, u16 val)
203
{ MIOS32_MIDI_SendSpecialEvent(port, 0x3, 0xf2, val & 0x7f, val >> 7); }
204
 
205
s32 MIOS32_MIDI_SendSongSelect(mios32_midi_port_t port, u8 val)
206
{ MIOS32_MIDI_SendSpecialEvent(port, 0x2, 0xf3, val, 0x00); }
207
 
208
s32 MIOS32_MIDI_SendTuneRequest(mios32_midi_port_t port)
209
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xf6, 0x00, 0x00); }
210
 
211
s32 MIOS32_MIDI_SendClock(mios32_midi_port_t port)
212
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xf8, 0x00, 0x00); }
213
 
214
s32 MIOS32_MIDI_SendTick(mios32_midi_port_t port)
215
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xf9, 0x00, 0x00); }
216
 
217
s32 MIOS32_MIDI_SendStart(mios32_midi_port_t port)
218
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xfa, 0x00, 0x00); }
219
 
220
s32 MIOS32_MIDI_SendStop(mios32_midi_port_t port)
221
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xfb, 0x00, 0x00); }
222
 
223
s32 MIOS32_MIDI_SendContinue(mios32_midi_port_t port)
224
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xfc, 0x00, 0x00); }
225
 
226
s32 MIOS32_MIDI_SendActiveSense(mios32_midi_port_t port)
227
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xfe, 0x00, 0x00); }
228
 
229
s32 MIOS32_MIDI_SendReset(mios32_midi_port_t port)
230
{ MIOS32_MIDI_SendSpecialEvent(port, 0x5, 0xff, 0x00, 0x00); }
231
 
232
 
33 tk 233
/////////////////////////////////////////////////////////////////////////////
234
// Sends a SysEx Stream
235
// This function is provided for a more comfortable use model
236
// IN: <port>: MIDI port 
237
//     <stream>: pointer to SysEx stream
238
//     <count>: number of bytes
239
// OUT: returns -1 if port not available
34 tk 240
//      returns -2 if non-blocking mode activated: buffer is full
241
//                 caller should retry until buffer is free again
33 tk 242
//      returns 0 on success
243
/////////////////////////////////////////////////////////////////////////////
69 tk 244
s32 MIOS32_MIDI_SendSysEx(mios32_midi_port_t port, u8 *stream, u32 count)
33 tk 245
{
246
  s32 res;
247
  u32 offset;
248
  mios32_midi_package_t package;
249
 
250
  // MEMO: have a look into the project.lss file - gcc optimizes this code pretty well :)
251
 
252
  for(offset=0; offset<count;) {
253
    // package type depends on number of remaining bytes
254
    switch( count-offset ) {
255
      case 1:
256
    package.type = 0x5; // SysEx ends with following single byte. 
257
    package.evnt0 = stream[offset++];
258
    package.evnt1 = 0x00;
259
    package.evnt2 = 0x00;
260
    break;
261
      case 2:
262
    package.type = 0x6; // SysEx ends with following two bytes.
263
    package.evnt0 = stream[offset++];
264
    package.evnt1 = stream[offset++];
265
    package.evnt2 = 0x00;
266
    break;
267
      case 3:
268
    package.type = 0x7; // SysEx ends with following three bytes. 
269
    package.evnt0 = stream[offset++];
270
    package.evnt1 = stream[offset++];
271
    package.evnt2 = stream[offset++];
272
    break;
273
      default:
274
    package.type = 0x4; // SysEx starts or continues
275
    package.evnt0 = stream[offset++];
276
    package.evnt1 = stream[offset++];
277
    package.evnt2 = stream[offset++];
278
    }
279
 
280
    while( (res=MIOS32_MIDI_SendPackage(port, package)) == -2 ) {
34 tk 281
      // TODO: SysEx always sent in blocking mode to avoid inconsistent stream!
282
      // We poll until buffer is free again.
33 tk 283
      // Are there better ways?
284
    }
285
 
286
    // other expection? (e.g., port not available)
287
    if( res < 0 )
288
      return res;
289
  }
290
 
291
  return 0;
292
}
293
 
294
 
295
/////////////////////////////////////////////////////////////////////////////
296
// Checks for incoming MIDI messages, calls either the callback_event or
297
// callback_sysex function with following parameters:
69 tk 298
//    callback_event(mios32_midi_port_t port, mios32_midi_package_t midi_package)
299
//    callback_sysex(mios32_midi_port_t port, u8 sysex_byte)
33 tk 300
// OUT: returns < 0 on errors
301
/////////////////////////////////////////////////////////////////////////////
302
s32 MIOS32_MIDI_Receive_Handler(void *_callback_event, void *_callback_sysex)
303
{
304
  u8 port;
305
  mios32_midi_package_t package;
306
  u8 again;
307
 
69 tk 308
  void (*callback_event)(mios32_midi_port_t port, mios32_midi_package_t midi_package) = _callback_event;
309
  void (*callback_sysex)(mios32_midi_port_t port, u8 sysex_byte) = _callback_sysex;
33 tk 310
 
311
  do {
312
    // notifies, that there are more messages to be processed
313
    again = 0;
314
 
315
    // TODO: more generic handler used by all port types (USB, USART, IIC, etc..)
316
 
317
    // check for USB messages
318
    if( MIOS32_USB_MIDIPackageReceive(&package) ) {
69 tk 319
      // get port and mask out cable number
320
      port = package.cable;
321
      package.cable = 0;
33 tk 322
 
323
      if( package.type >= 0x8 ) {
324
    callback_event(port, package);
325
      } else {
326
    switch( package.type ) {
327
    case 0x0: // reserved, ignore
328
    case 0x1: // cable events, ignore
329
      break;
330
 
331
    case 0x2: // Two-byte System Common messages like MTC, SongSelect, etc. 
332
    case 0x3: // Three-byte System Common messages like SPP, etc. 
333
      callback_event(port, package); // -> forwarded as event
334
      break;
335
    case 0x4: // SysEx starts or continues (3 bytes)
336
      callback_sysex(port, package.evnt0); // -> forwarded as SysEx
337
      callback_sysex(port, package.evnt1); // -> forwarded as SysEx
338
      callback_sysex(port, package.evnt2); // -> forwarded as SysEx
339
      break;
340
    case 0x5: // Single-byte System Common Message or SysEx ends with following single byte. 
341
      if( package.evnt0 >= 0xf8 )
342
        callback_event(port, package); // -> forwarded as event
343
      else
344
        callback_sysex(port, package.evnt0); // -> forwarded as SysEx
345
      break;
346
    case 0x6: // SysEx ends with following two bytes.
347
      callback_sysex(port, package.evnt0); // -> forwarded as SysEx
348
      callback_sysex(port, package.evnt1); // -> forwarded as SysEx
349
      break;
350
    case 0x7: // SysEx ends with following three bytes.
351
      callback_sysex(port, package.evnt0); // -> forwarded as SysEx
352
      callback_sysex(port, package.evnt1); // -> forwarded as SysEx
353
      callback_sysex(port, package.evnt2); // -> forwarded as SysEx
354
      break;
355
    }
356
      }
357
    }
358
  } while( again );
359
 
360
  return 0;
361
}
362
 
363
 
32 tk 364
#endif /* MIOS32_DONT_USE_MIDI */