/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_MidiStreamParser.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. 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. $Revision: 20981 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include // #define DEBUG_REPORT namespace nw { namespace snd { namespace internal { MidiStreamParser::MidiStreamParser() { m_RestBuffer.ptr = m_RestBuf; m_RestBuffer.len = 0; m_RestBuffer.readPos = 0; m_RestBuffer.donePos = 0; m_BackByte = 0; m_IsBackByteAvailable = false; } void MidiStreamParser::SetCallback( MidiCallback callback, void* arg ) { m_CallbackFunc = callback; m_pCallbackArg = arg; } void MidiStreamParser::Parse( const void* buffer, unsigned long size ) { // バッファを登録 SetMsgBuffer( buffer, size ); // メッセージ処理 // MIDIメッセージに対するコールバック呼び出し ParseBuffer(); // 未処理メッセージを次回に繰り越し RestBuffer(); } void MidiStreamParser::Reset() { m_IsReset = true; m_IsSysEx = false;; } void MidiStreamParser::ParseBuffer( void ) { u8 status; u8 data1; u8 data2; while( 1 ) { status = 0; data1 = 0; data2 = 0; // ステータスバイト待ち if ( m_IsReset ) { if ( ! SeekStatusByte() ) return; m_IsReset = false; } // システムエクスクルーシブ if ( m_IsSysEx ) { u8 b; do { if ( ! ReadByte( &b ) ) return; // 読み捨て EatByte(); } while( IsDataByte( b ) ); BackByte( b ); if ( b != 0xf7 ) { // eof sys Ex #ifdef DEBUG_REPORT NN_LOG("Not found EOF sysEx\n"); #endif Reset(); continue; } } // 1バイト読む if ( ! ReadByte( &status ) ) return; // ランニングステータス if ( IsDataByte( status ) ) { BackByte( status ); status = m_RunningStatus; } if ( ( status & 0xf0 ) == 0xf0 ) { // コモンメッセージ switch( status ) { case 0xf0: // sys Ex m_IsSysEx = true; break; case 0xf1: // time code quoter frame if ( ! ReadByte( &data1 ) ) return; break; case 0xf2: // song pos if ( ! ReadByte( &data1 ) ) return; if ( ! ReadByte( &data2 ) ) return; break; case 0xf3: // song select if ( ! ReadByte( &data1 ) ) return; break; case 0xf4: // undef case 0xf5: // undef case 0xf6: // tune request break; case 0xf7: // eof Ex m_IsSysEx = false; break; } } else { // チャンネルメッセージ u8 ch = (u8)( status & 0x0f ); if ( ! ReadByte( &data1 ) ) return; switch( status & 0xf0 ) { case 0x80: // noteoff case 0x90: // noteon case 0xa0: // polyphonic key pressure case 0xb0: // control change case 0xe0: // pitchbend if ( ! ReadByte( &data2 ) ) return; break; case 0xc0: // program change case 0xd0: // channel presure break; } } if ( ! IsDataByte( data1 ) || ! IsDataByte( data2 )) { #ifdef DEBUG_REPORT NN_LOG("Unexpected status byte\n"); #endif Reset(); continue; } #ifdef DEBUG_REPORT NN_LOG("MIDI: %02x %02x %02x\n", status,data1,data2); #endif m_CallbackFunc( status, data1, data2, m_pCallbackArg ); m_RunningStatus = status; EatByte(); } } void MidiStreamParser::SetMsgBuffer( const void* buffer, u32 len ) { m_MsgBuffer.ptr = (const u8* )buffer; m_MsgBuffer.len = len; m_MsgBuffer.readPos = 0; m_MsgBuffer.donePos = 0; #ifdef DEBUG_REPORT if ( len > 0 ) { const u8* p = (const u8*)buffer; for( int i = 0 ; i < len ; i++ ) { NN_LOG("%02x ", p[i]); } NN_LOG("\n"); } #endif } bool MidiStreamParser::ReadByte( u8* data ) { if ( m_IsBackByteAvailable ) { NW_ASSERT( ! IsRealtimeMesg( m_BackByte ) ); m_IsBackByteAvailable = false; *data = m_BackByte; return true; } while ( m_RestBuffer.readPos < m_RestBuffer.len ) { *data = m_RestBuffer.ptr[ m_RestBuffer.readPos++ ]; if ( IsRealtimeMesg( *data ) ) { #ifdef DEBUG_REPORT NN_LOG("MIDI: %02x %02x %02x\n", *data,0,0); #endif m_CallbackFunc( *data, 0, 0, m_pCallbackArg ); if ( IsSystemResetMesg(*data) ) m_IsReset = true; continue; } return true; } while ( m_MsgBuffer.readPos < m_MsgBuffer.len ) { *data = m_MsgBuffer.ptr[ m_MsgBuffer.readPos++ ]; if ( IsRealtimeMesg( *data ) ) { #ifdef DEBUG_REPORT NN_LOG("MIDI: %02x %02x %02x\n", *data,0,0); #endif m_CallbackFunc( *data, 0, 0, m_pCallbackArg ); if ( IsSystemResetMesg(*data) ) m_IsReset = true; continue; } return true; } return false; } bool MidiStreamParser::SeekStatusByte( void ) { u8 byte; if ( m_IsBackByteAvailable ) { NW_ASSERT( ! IsRealtimeMesg( m_BackByte ) ); if ( IsStatusByte( m_BackByte ) ) return true; m_IsBackByteAvailable = false; } while( m_RestBuffer.readPos < m_RestBuffer.len ) { byte = m_RestBuffer.ptr[ m_RestBuffer.readPos ]; if ( IsStatusByte( byte ) && ! IsRealtimeMesg( byte ) ) { EatByte(); return true; } m_RestBuffer.readPos++; } while ( m_MsgBuffer.readPos < m_MsgBuffer.len ) { byte = m_MsgBuffer.ptr[ m_MsgBuffer.readPos ]; if ( IsStatusByte( byte ) && ! IsRealtimeMesg( byte ) ) { EatByte(); return true; } m_MsgBuffer.readPos++; } EatByte(); return false; } void MidiStreamParser::BackByte( u8 byte ) { NW_ASSERT( ! m_IsBackByteAvailable ); NW_ASSERT( ! IsRealtimeMesg( byte ) ); m_BackByte = byte; m_IsBackByteAvailable = true; } void MidiStreamParser::EatByte( void ) { m_RestBuffer.donePos = m_RestBuffer.readPos; m_MsgBuffer.donePos = m_MsgBuffer.readPos; } void MidiStreamParser::RestBuffer( void ) { u8* rest = m_RestBuf; u32 len; u32 restLen; restLen = 0; NW_ASSERT( ! m_IsBackByteAvailable ); len = m_RestBuffer.len - m_RestBuffer.donePos; for( u32 i = 0; i < len ; i++ ) { *rest = m_RestBuffer.ptr[ m_RestBuffer.donePos + i ]; if ( ! IsRealtimeMesg( *rest ) ) { // リアルタイムメッセージは処理済み rest++; restLen++; } } len = m_MsgBuffer.len - m_MsgBuffer.donePos; for( u32 i = 0; i < len ; i++ ) { *rest = m_MsgBuffer.ptr[ m_MsgBuffer.donePos + i ]; if ( ! IsRealtimeMesg( *rest ) ) { // リアルタイムメッセージは処理済み rest++; restLen++; } } m_RestBuffer.readPos = 0; m_RestBuffer.donePos = 0; m_RestBuffer.len = restLen; NW_ASSERT( m_RestBuffer.len < sizeof(m_RestBuf)/sizeof(m_RestBuf[0]) ); } } // namespace nw::snd::internal } // namespace nw::snd } // namespace nw