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