1 /*---------------------------------------------------------------------------*
2   Project:      MIDI adaptor sample demo
3   File:         midiqueue.c
4   Programmer:   HIRATSU Daisuke
5 
6   Copyright (C)2007 Nintendo  All rights reserved.
7 
8   These coded instructions, statements, and computer programs contain
9   proprietary information of Nintendo of America Inc. and/or Nintendo
10   Company Ltd., and are protected by Federal copyright law.  They may
11   not be disclosed to third parties or copied or duplicated in any form,
12   in whole or in part, without the prior written consent of Nintendo.
13 
14   $Log: midiqueue.c,v $
15   Revision 1.2  2007/04/17 08:24:54  hiratsu
16   Refactoring.
17 
18   Revision 1.1  2007/04/13 08:39:53  hiratsu
19   Initial check-in.
20 
21  *---------------------------------------------------------------------------*/
22 
23 
24 #include "midiqueue.h"
25 
26 #include <revolution/midi.h>
27 #include <demo.h>
28 
29 #define RINGBUF_SIZE 128
30 
31 // Queue related operations.
32 static int  getHeadIndex(void);
33 static int  getTailIndex(void);
34 static int  getLength(void);
35 static int  proceedIndex(int oldIndex);
36 static void proceedHeadIndex(void);
37 static void proceedTailIndex(void);
38 
39 // MIDI related operations.
40 static BOOL isStatusByte(u8 ch);
41 static BOOL isDataByte  (u8 ch);
42 static BOOL isSupportedMidiMessage(u8 ch);
43 static BOOL isSupportedMidiControl(u8 ch);
44 static BOOL isNoteOnMessage(u8 ch);          // 3 bytes
45 static BOOL isNoteOffMessage(u8 ch);         // 3 bytes
46 static BOOL isControlChangeMessage(u8 ch);   // 3 bytes
47 static BOOL isProgramChangeMessage(u8 ch);   // 2 bytes
48 static BOOL isPitchbendChangeMessage(u8 ch); // 3 bytes
49 static BOOL isExclusiveMessage(u8 ch);       // Flexible length
50 static BOOL isEndOfExclusiveMessage(u8 ch);
51 static BOOL isActiveSensingMessage(u8 ch);
52 static BOOL isSystemMessage(u8 ch);
53 static int  getExpectedDataNum(u8 statusByte);
54 
55 
56 static u8  s_ringBuffer[RINGBUF_SIZE];
57 static int s_headIndex = 0;
58 static int s_tailIndex = 0;
59 static u8  s_lastStatusByte = 0x00;
60 static int s_expectedDataNum = 0;
61 
62 
isStatusByte(u8 ch)63 static BOOL isStatusByte(u8 ch)
64 {
65     return (ch & 0x80) ? TRUE : FALSE;
66 }
67 
68 
isDataByte(u8 ch)69 static BOOL isDataByte(u8 ch)
70 {
71     return isStatusByte(ch) ? FALSE : TRUE;
72 }
73 
74 
isSupportedMidiMessage(u8 ch)75 static BOOL isSupportedMidiMessage(u8 ch)
76 {
77     if(isNoteOnMessage(ch)         ||
78        isNoteOffMessage(ch)        ||
79        isControlChangeMessage(ch)  ||
80        isProgramChangeMessage(ch)  ||
81        isPitchbendChangeMessage(ch) )
82     {
83         return TRUE;
84     }
85     else
86     {
87         return FALSE;
88     }
89 }
90 
91 
isSupportedMidiControl(u8 ch)92 static BOOL isSupportedMidiControl(u8 ch)
93 {
94     const u8 LIST[] = {
95         0x01, // Modulation wheel
96         0x06, 0x26, // Data entry (for pitch wheel range setting)
97         0x07, // Volume
98         0x0a, // Pan
99         0x0b, // Expression
100         0x40, // Hold pedal
101         0x5b, // Effects (AuxA)
102         0x5c, // Effects (AuxB)
103         0x62, 0x63, // NRPN
104         0x64, 0x65, // RPN
105         0x78, // All sound off
106         0x79, // Reset all controllers
107         0x7b, 0x7c, 0x7d, 0x7e, 0x7f // All note off
108       };
109     int i = 0;
110 
111     for(i = 0; i<sizeof(LIST); ++i)
112     {
113         if(ch==LIST[i])
114         {
115             return TRUE;
116         }
117     }
118 
119     return FALSE;  // Not found
120 }
121 
122 
123 
isNoteOnMessage(u8 ch)124 static BOOL isNoteOnMessage(u8 ch)
125 {
126     return ((ch & 0xf0)==0x90) ? TRUE : FALSE;
127 }
128 
129 
isNoteOffMessage(u8 ch)130 static BOOL isNoteOffMessage(u8 ch)
131 {
132     return ((ch & 0xf0)==0x80) ? TRUE : FALSE;
133 }
134 
135 
isControlChangeMessage(u8 ch)136 static BOOL isControlChangeMessage(u8 ch)
137 {
138     return ((ch & 0xf0)==0xb0) ? TRUE : FALSE;
139 }
140 
141 
isProgramChangeMessage(u8 ch)142 static BOOL isProgramChangeMessage(u8 ch)
143 {
144     return ((ch & 0xf0)==0xc0) ? TRUE : FALSE;
145 }
146 
147 
isPitchbendChangeMessage(u8 ch)148 static BOOL isPitchbendChangeMessage(u8 ch)
149 {
150     return ((ch & 0xf0)==0xe0) ? TRUE : FALSE;
151 }
152 
153 
isExclusiveMessage(u8 ch)154 static BOOL isExclusiveMessage(u8 ch)
155 {
156     return (ch==0xf0) ? TRUE : FALSE;
157 }
158 
159 
isEndOfExclusiveMessage(u8 ch)160 static BOOL isEndOfExclusiveMessage(u8 ch)
161 {
162     return (ch==0xf7) ? TRUE : FALSE;
163 }
164 
165 
isActiveSensingMessage(u8 ch)166 static BOOL isActiveSensingMessage(u8 ch)
167 {
168     return (ch==0xfe) ? TRUE : FALSE;
169 }
170 
171 
isSystemMessage(u8 ch)172 static BOOL isSystemMessage(u8 ch)
173 {
174     return ((ch & 0xf0) == 0xf0) ? TRUE : FALSE;
175 }
176 
177 
getExpectedDataNum(u8 statusByte)178 static int getExpectedDataNum(u8 statusByte)
179 {
180     if(isNoteOnMessage(statusByte)         ||
181        isNoteOffMessage(statusByte)        ||
182        isControlChangeMessage(statusByte)  ||
183        isPitchbendChangeMessage(statusByte) )
184     {
185         return 2;
186     }
187     else if(isProgramChangeMessage(statusByte))
188     {
189         return 1;
190     }
191     else if(isEndOfExclusiveMessage(statusByte))
192     {
193         return 0;
194     }
195     else
196     {
197         OSHalt("Caution!  Unexpected status byte.");
198         return 0;  // Never reach here.
199     }
200 }
201 
202 
getHeadIndex(void)203 static int getHeadIndex(void)
204 {
205     return s_headIndex;
206 }
207 
208 
getTailIndex(void)209 static int getTailIndex(void)
210 {
211     return s_tailIndex;
212 }
213 
214 
getLength(void)215 static int getLength(void)
216 {
217     int ret = 0;
218     BOOL enabled = OSDisableInterrupts();
219     int tail = getTailIndex();
220     int head = getHeadIndex();
221 
222     if(tail >= head)
223     {
224         ret = tail - head;
225     }
226     else
227     {
228         ret = RINGBUF_SIZE+tail - head;
229     }
230     OSRestoreInterrupts(enabled);
231 
232     return ret;
233 }
234 
235 
proceedIndex(int oldIndex)236 static int proceedIndex(int oldIndex)
237 {
238     if((0<=oldIndex) && (oldIndex < RINGBUF_SIZE-1))
239     {
240         return oldIndex+1;
241     }
242     else if(oldIndex == RINGBUF_SIZE-1)
243     {
244         return 0;
245     }
246     else
247     {
248         OSReport("Invalid index (out of range): %d", oldIndex);
249         OSHalt("");
250         return -1;  // Never reach here.
251     }
252 }
253 
254 
proceedHeadIndex(void)255 static void proceedHeadIndex(void)
256 {
257     s_headIndex = proceedIndex(s_headIndex);
258 }
259 
260 
proceedTailIndex(void)261 static void proceedTailIndex(void)
262 {
263     s_tailIndex = proceedIndex(s_tailIndex);
264 }
265 
266 
pushImpl(u8 a)267 static void pushImpl(u8 a)
268 {
269     BOOL enabled = OSDisableInterrupts();
270 
271     s_ringBuffer[getTailIndex()] = a;
272     proceedTailIndex();
273     if(getHeadIndex()==getTailIndex()){
274         OSReport("Caution!  Queue is full.\n");
275     }
276 
277     OSRestoreInterrupts(enabled);
278 }
279 
280 
push(u8 a)281 void push(u8 a)
282 {
283     if(isExclusiveMessage(s_lastStatusByte) && !isEndOfExclusiveMessage(a))
284     {
285         return;  // Ignore exclusive message.
286     }
287 
288     if(isActiveSensingMessage(a))
289     {
290         return;  // Ignore active sensing.
291     }
292 
293     if(isStatusByte(a))
294     {
295         s_lastStatusByte = a;
296         s_expectedDataNum = getExpectedDataNum(a);
297         if(!isEndOfExclusiveMessage(a))
298         {
299             pushImpl(a);
300         }
301     }
302     else
303     {
304         if(s_expectedDataNum > 0)
305         {
306             --s_expectedDataNum;
307             pushImpl(a);
308         }
309         else if(s_expectedDataNum == 0)
310         {
311             pushImpl(s_lastStatusByte);  // Running status process.
312             pushImpl(a);
313 
314             s_expectedDataNum = getExpectedDataNum(s_lastStatusByte)-1;
315         }
316         else
317         {
318             OSHalt("Caution!  Unexpected sequence.");
319         }
320     }
321 }
322 
323 
pop(void)324 void pop(void)
325 {
326     BOOL enabled = OSDisableInterrupts();
327     if(isReady())
328     {
329         int i = 0;
330         int n = getExpectedDataNum(s_ringBuffer[getHeadIndex()]);
331         for(i=0; i<=n; ++i)
332         {
333             proceedHeadIndex();
334         }
335     }
336     else
337     {
338         OSReport("Caution!  You're going to pop from empty queue.\n");
339     }
340     OSRestoreInterrupts(enabled);
341 }
342 
343 
front(u8 msg[3])344 BOOL front(u8 msg[3])
345 {
346     BOOL ret = FALSE;
347     BOOL enabled = OSDisableInterrupts();
348     if(isReady())
349     {
350         int head = getHeadIndex();
351         switch(getExpectedDataNum(s_ringBuffer[head]))
352         {
353         case 2:
354             msg[2] = s_ringBuffer[(head+2)%RINGBUF_SIZE];
355         case 1:
356             msg[1] = s_ringBuffer[(head+1)%RINGBUF_SIZE];
357         case 0:
358             msg[0] = s_ringBuffer[head];
359             break;
360         default:
361             OSHalt("Illegal sequence.");
362             break;
363         }
364         ret = TRUE;
365     }
366     else
367     {
368         ret = FALSE;
369     }
370     OSRestoreInterrupts(enabled);
371 
372     return ret;
373 }
374 
375 
376 // Can one or more MIDI messages be retrieved?
isReady(void)377 BOOL isReady(void)
378 {
379     BOOL ret = FALSE;
380     BOOL enabled = OSDisableInterrupts();
381     int len = getLength();
382     if(len==0)
383     {
384         ret = FALSE;
385     }
386     else
387     {
388         int expect = getExpectedDataNum(s_ringBuffer[getHeadIndex()]);
389         if(expect < len)
390         {
391             ret = TRUE;
392         }
393         else
394         {
395             ret = FALSE;
396         }
397     }
398     OSRestoreInterrupts(enabled);
399 
400     return ret;
401 }
402 
403 
isSupportedCommand(const u8 cmd[3])404 BOOL isSupportedCommand(const u8 cmd[3])
405 {
406     if(isSupportedMidiMessage(cmd[0]))
407     {
408         if(!isControlChangeMessage(cmd[0]))
409         {
410             return TRUE;
411         }
412         else
413         {
414             if(isSupportedMidiControl(cmd[1]))
415             {
416                 return TRUE;
417             }
418             else
419             {
420                 return FALSE;
421             }
422         }
423     }
424     else
425     {
426         return FALSE;
427     }
428 }
429