1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     snd_MidiStreamParser.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 20981 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/snd/snd_MidiStreamParser.h>
19 
20 // #define DEBUG_REPORT
21 
22 namespace nw {
23 namespace snd {
24 namespace internal {
25 
MidiStreamParser()26 MidiStreamParser::MidiStreamParser()
27 {
28     m_RestBuffer.ptr = m_RestBuf;
29     m_RestBuffer.len = 0;
30     m_RestBuffer.readPos = 0;
31     m_RestBuffer.donePos = 0;
32 
33     m_BackByte = 0;
34     m_IsBackByteAvailable = false;
35 }
36 
SetCallback(MidiCallback callback,void * arg)37 void MidiStreamParser::SetCallback( MidiCallback callback, void* arg )
38 {
39     m_CallbackFunc = callback;
40     m_pCallbackArg = arg;
41 }
42 
Parse(const void * buffer,unsigned long size)43 void MidiStreamParser::Parse( const void* buffer, unsigned long size )
44 {
45     // バッファを登録
46     SetMsgBuffer( buffer, size );
47 
48     // メッセージ処理
49     // MIDIメッセージに対するコールバック呼び出し
50     ParseBuffer();
51 
52     // 未処理メッセージを次回に繰り越し
53     RestBuffer();
54 }
55 
Reset()56 void MidiStreamParser::Reset()
57 {
58     m_IsReset = true;
59     m_IsSysEx = false;;
60 }
61 
ParseBuffer(void)62 void MidiStreamParser::ParseBuffer( void )
63 {
64     u8 status;
65     u8 data1;
66     u8 data2;
67 
68     while( 1 )
69     {
70         status = 0;
71         data1 = 0;
72         data2 = 0;
73 
74         // ステータスバイト待ち
75         if ( m_IsReset )
76         {
77             if ( ! SeekStatusByte() ) return;
78             m_IsReset = false;
79         }
80 
81         // システムエクスクルーシブ
82         if ( m_IsSysEx ) {
83             u8 b;
84             do {
85                 if ( ! ReadByte( &b ) ) return;
86 
87                 // 読み捨て
88                 EatByte();
89 
90             } while( IsDataByte( b ) );
91 
92             BackByte( b );
93 
94             if ( b != 0xf7 ) { // eof sys Ex
95 #ifdef DEBUG_REPORT
96                 NN_LOG("Not found EOF sysEx\n");
97 #endif
98                 Reset();
99                 continue;
100             }
101         }
102 
103         // 1バイト読む
104         if ( ! ReadByte( &status ) ) return;
105 
106         // ランニングステータス
107         if ( IsDataByte( status ) ) {
108             BackByte( status );
109             status = m_RunningStatus;
110         }
111 
112         if ( ( status & 0xf0 ) == 0xf0 ) {
113             // コモンメッセージ
114             switch( status ) {
115             case 0xf0: // sys Ex
116                 m_IsSysEx = true;
117                 break;
118 
119             case 0xf1: // time code quoter frame
120                 if ( ! ReadByte( &data1 ) ) return;
121                 break;
122 
123             case 0xf2: // song pos
124                 if ( ! ReadByte( &data1 ) ) return;
125                 if ( ! ReadByte( &data2 ) ) return;
126                 break;
127 
128             case 0xf3: // song select
129                 if ( ! ReadByte( &data1 ) ) return;
130                 break;
131 
132             case 0xf4: // undef
133             case 0xf5: // undef
134             case 0xf6: // tune request
135                 break;
136 
137             case 0xf7: // eof Ex
138                 m_IsSysEx = false;
139                 break;
140             }
141         }
142         else {
143             // チャンネルメッセージ
144             u8 ch = (u8)( status & 0x0f );
145 
146             if ( ! ReadByte( &data1 ) ) return;
147 
148             switch( status & 0xf0 ) {
149             case 0x80: // noteoff
150             case 0x90: // noteon
151             case 0xa0: // polyphonic key pressure
152             case 0xb0: // control change
153             case 0xe0: // pitchbend
154                 if ( ! ReadByte( &data2 ) ) return;
155                 break;
156             case 0xc0: // program change
157             case 0xd0: // channel presure
158                 break;
159             }
160         }
161 
162         if ( ! IsDataByte( data1 ) || ! IsDataByte( data2 )) {
163 #ifdef DEBUG_REPORT
164             NN_LOG("Unexpected status byte\n");
165 #endif
166             Reset();
167             continue;
168         }
169 
170 #ifdef DEBUG_REPORT
171         NN_LOG("MIDI: %02x %02x %02x\n", status,data1,data2);
172 #endif
173         m_CallbackFunc( status, data1, data2, m_pCallbackArg );
174 
175         m_RunningStatus = status;
176         EatByte();
177     }
178 }
179 
SetMsgBuffer(const void * buffer,u32 len)180 void MidiStreamParser::SetMsgBuffer( const void* buffer, u32 len )
181 {
182     m_MsgBuffer.ptr = (const u8* )buffer;
183     m_MsgBuffer.len = len;
184     m_MsgBuffer.readPos = 0;
185     m_MsgBuffer.donePos = 0;
186 
187 #ifdef DEBUG_REPORT
188     if ( len > 0 )
189     {
190         const u8* p = (const u8*)buffer;
191         for( int i = 0 ; i < len ; i++ ) {
192             NN_LOG("%02x ", p[i]);
193         }
194         NN_LOG("\n");
195     }
196 #endif
197 }
198 
ReadByte(u8 * data)199 bool MidiStreamParser::ReadByte( u8* data )
200 {
201     if ( m_IsBackByteAvailable ) {
202         NW_ASSERT( ! IsRealtimeMesg( m_BackByte ) );
203         m_IsBackByteAvailable = false;
204         *data = m_BackByte;
205         return true;
206     }
207 
208     while ( m_RestBuffer.readPos < m_RestBuffer.len ) {
209         *data = m_RestBuffer.ptr[ m_RestBuffer.readPos++ ];
210         if ( IsRealtimeMesg( *data ) ) {
211 #ifdef DEBUG_REPORT
212             NN_LOG("MIDI: %02x %02x %02x\n", *data,0,0);
213 #endif
214             m_CallbackFunc( *data, 0, 0, m_pCallbackArg );
215             if ( IsSystemResetMesg(*data) ) m_IsReset = true;
216             continue;
217         }
218         return true;
219     }
220 
221     while ( m_MsgBuffer.readPos < m_MsgBuffer.len ) {
222         *data = m_MsgBuffer.ptr[ m_MsgBuffer.readPos++ ];
223         if ( IsRealtimeMesg( *data ) ) {
224 #ifdef DEBUG_REPORT
225             NN_LOG("MIDI: %02x %02x %02x\n", *data,0,0);
226 #endif
227             m_CallbackFunc( *data, 0, 0, m_pCallbackArg );
228             if ( IsSystemResetMesg(*data) ) m_IsReset = true;
229             continue;
230         }
231         return true;
232     }
233 
234     return false;
235 }
236 
SeekStatusByte(void)237 bool MidiStreamParser::SeekStatusByte( void )
238 {
239     u8 byte;
240 
241     if ( m_IsBackByteAvailable ) {
242         NW_ASSERT( ! IsRealtimeMesg( m_BackByte ) );
243         if ( IsStatusByte( m_BackByte ) ) return true;
244         m_IsBackByteAvailable = false;
245     }
246 
247     while( m_RestBuffer.readPos < m_RestBuffer.len ) {
248         byte = m_RestBuffer.ptr[ m_RestBuffer.readPos ];
249         if ( IsStatusByte( byte ) && ! IsRealtimeMesg( byte ) ) {
250             EatByte();
251             return true;
252         }
253         m_RestBuffer.readPos++;
254     }
255 
256     while ( m_MsgBuffer.readPos < m_MsgBuffer.len ) {
257         byte = m_MsgBuffer.ptr[ m_MsgBuffer.readPos ];
258         if ( IsStatusByte( byte ) && ! IsRealtimeMesg( byte ) ) {
259             EatByte();
260             return true;
261         }
262         m_MsgBuffer.readPos++;
263     }
264 
265     EatByte();
266     return false;
267 }
268 
BackByte(u8 byte)269 void MidiStreamParser::BackByte( u8 byte )
270 {
271     NW_ASSERT( ! m_IsBackByteAvailable );
272     NW_ASSERT( ! IsRealtimeMesg( byte ) );
273 
274     m_BackByte = byte;
275     m_IsBackByteAvailable = true;
276 }
277 
EatByte(void)278 void MidiStreamParser::EatByte( void )
279 {
280     m_RestBuffer.donePos = m_RestBuffer.readPos;
281     m_MsgBuffer.donePos = m_MsgBuffer.readPos;
282 }
283 
RestBuffer(void)284 void MidiStreamParser::RestBuffer( void )
285 {
286     u8* rest = m_RestBuf;
287 	u32 len;
288     u32 restLen;
289 
290 	restLen = 0;
291 
292     NW_ASSERT( ! m_IsBackByteAvailable );
293 
294     len = m_RestBuffer.len - m_RestBuffer.donePos;
295 	for( u32 i = 0; i < len ; i++ ) {
296 		*rest = m_RestBuffer.ptr[ m_RestBuffer.donePos + i ];
297         if ( ! IsRealtimeMesg( *rest ) ) { // リアルタイムメッセージは処理済み
298             rest++;
299             restLen++;
300         }
301 	}
302 
303 	len = m_MsgBuffer.len - m_MsgBuffer.donePos;
304 	for( u32 i = 0; i < len ; i++ ) {
305         *rest = m_MsgBuffer.ptr[ m_MsgBuffer.donePos + i ];
306         if ( ! IsRealtimeMesg( *rest ) ) { // リアルタイムメッセージは処理済み
307             rest++;
308             restLen++;
309         }
310 	}
311 
312     m_RestBuffer.readPos = 0;
313     m_RestBuffer.donePos = 0;
314     m_RestBuffer.len = restLen;
315 
316     NW_ASSERT( m_RestBuffer.len < sizeof(m_RestBuf)/sizeof(m_RestBuf[0]) );
317 }
318 
319 } // namespace nw::snd::internal
320 } // namespace nw::snd
321 } // namespace nw
322 
323