/*---------------------------------------------------------------------------* Project: MIDI adaptor sample demo File: midiqueue.c Programmer: HIRATSU Daisuke Copyright (C)2007 Nintendo All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Log: midiqueue.c,v $ Revision 1.2 2007/04/17 08:24:54 hiratsu Refactoring. Revision 1.1 2007/04/13 08:39:53 hiratsu Initial check-in. *---------------------------------------------------------------------------*/ #include "midiqueue.h" #include #include #define RINGBUF_SIZE 128 // Queue related operations. static int getHeadIndex(void); static int getTailIndex(void); static int getLength(void); static int proceedIndex(int oldIndex); static void proceedHeadIndex(void); static void proceedTailIndex(void); // MIDI related operations. static BOOL isStatusByte(u8 ch); static BOOL isDataByte (u8 ch); static BOOL isSupportedMidiMessage(u8 ch); static BOOL isSupportedMidiControl(u8 ch); static BOOL isNoteOnMessage(u8 ch); // 3 bytes static BOOL isNoteOffMessage(u8 ch); // 3 bytes static BOOL isControlChangeMessage(u8 ch); // 3 bytes static BOOL isProgramChangeMessage(u8 ch); // 2 bytes static BOOL isPitchbendChangeMessage(u8 ch); // 3 bytes static BOOL isExclusiveMessage(u8 ch); // Flexible length static BOOL isEndOfExclusiveMessage(u8 ch); static BOOL isActiveSensingMessage(u8 ch); static BOOL isSystemMessage(u8 ch); static int getExpectedDataNum(u8 statusByte); static u8 s_ringBuffer[RINGBUF_SIZE]; static int s_headIndex = 0; static int s_tailIndex = 0; static u8 s_lastStatusByte = 0x00; static int s_expectedDataNum = 0; static BOOL isStatusByte(u8 ch) { return (ch & 0x80) ? TRUE : FALSE; } static BOOL isDataByte(u8 ch) { return isStatusByte(ch) ? FALSE : TRUE; } static BOOL isSupportedMidiMessage(u8 ch) { if(isNoteOnMessage(ch) || isNoteOffMessage(ch) || isControlChangeMessage(ch) || isProgramChangeMessage(ch) || isPitchbendChangeMessage(ch) ) { return TRUE; } else { return FALSE; } } static BOOL isSupportedMidiControl(u8 ch) { const u8 LIST[] = { 0x01, // Modulation wheel 0x06, 0x26, // Data entry (for pitch wheel range setting) 0x07, // Volume 0x0a, // Pan 0x0b, // Expression 0x40, // Hold pedal 0x5b, // Effects (AuxA) 0x5c, // Effects (AuxB) 0x62, 0x63, // NRPN 0x64, 0x65, // RPN 0x78, // All sound off 0x79, // Reset all controllers 0x7b, 0x7c, 0x7d, 0x7e, 0x7f // All note off }; int i = 0; for(i = 0; i= head) { ret = tail - head; } else { ret = RINGBUF_SIZE+tail - head; } OSRestoreInterrupts(enabled); return ret; } static int proceedIndex(int oldIndex) { if((0<=oldIndex) && (oldIndex < RINGBUF_SIZE-1)) { return oldIndex+1; } else if(oldIndex == RINGBUF_SIZE-1) { return 0; } else { OSReport("Invalid index (out of range): %d", oldIndex); OSHalt(""); return -1; // Never reach here. } } static void proceedHeadIndex(void) { s_headIndex = proceedIndex(s_headIndex); } static void proceedTailIndex(void) { s_tailIndex = proceedIndex(s_tailIndex); } static void pushImpl(u8 a) { BOOL enabled = OSDisableInterrupts(); s_ringBuffer[getTailIndex()] = a; proceedTailIndex(); if(getHeadIndex()==getTailIndex()){ OSReport("Caution! Queue is full.\n"); } OSRestoreInterrupts(enabled); } void push(u8 a) { if(isExclusiveMessage(s_lastStatusByte) && !isEndOfExclusiveMessage(a)) { return; // Ignore exclusive message. } if(isActiveSensingMessage(a)) { return; // Ignore active sensing. } if(isStatusByte(a)) { s_lastStatusByte = a; s_expectedDataNum = getExpectedDataNum(a); if(!isEndOfExclusiveMessage(a)) { pushImpl(a); } } else { if(s_expectedDataNum > 0) { --s_expectedDataNum; pushImpl(a); } else if(s_expectedDataNum == 0) { pushImpl(s_lastStatusByte); // Running status process. pushImpl(a); s_expectedDataNum = getExpectedDataNum(s_lastStatusByte)-1; } else { OSHalt("Caution! Unexpected sequence."); } } } void pop(void) { BOOL enabled = OSDisableInterrupts(); if(isReady()) { int i = 0; int n = getExpectedDataNum(s_ringBuffer[getHeadIndex()]); for(i=0; i<=n; ++i) { proceedHeadIndex(); } } else { OSReport("Caution! You're going to pop from empty queue.\n"); } OSRestoreInterrupts(enabled); } BOOL front(u8 msg[3]) { BOOL ret = FALSE; BOOL enabled = OSDisableInterrupts(); if(isReady()) { int head = getHeadIndex(); switch(getExpectedDataNum(s_ringBuffer[head])) { case 2: msg[2] = s_ringBuffer[(head+2)%RINGBUF_SIZE]; case 1: msg[1] = s_ringBuffer[(head+1)%RINGBUF_SIZE]; case 0: msg[0] = s_ringBuffer[head]; break; default: OSHalt("Illegal sequence."); break; } ret = TRUE; } else { ret = FALSE; } OSRestoreInterrupts(enabled); return ret; } // Can one or more MIDI messages be retrieved? BOOL isReady(void) { BOOL ret = FALSE; BOOL enabled = OSDisableInterrupts(); int len = getLength(); if(len==0) { ret = FALSE; } else { int expect = getExpectedDataNum(s_ringBuffer[getHeadIndex()]); if(expect < len) { ret = TRUE; } else { ret = FALSE; } } OSRestoreInterrupts(enabled); return ret; } BOOL isSupportedCommand(const u8 cmd[3]) { if(isSupportedMidiMessage(cmd[0])) { if(!isControlChangeMessage(cmd[0])) { return TRUE; } else { if(isSupportedMidiControl(cmd[1])) { return TRUE; } else { return FALSE; } } } else { return FALSE; } }