1 // Game_Music_Emu 0.6.0. http://www.slack.net/~ant/
2 
3 #include "Vgm_Emu.h"
4 
5 #include <math.h>
6 #include <string.h>
7 #include "blargg_endian.h"
8 
9 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
10 can redistribute it and/or modify it under the terms of the GNU Lesser
11 General Public License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version. This
13 module is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16 details. You should have received a copy of the GNU Lesser General Public
17 License along with this module; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19 
20 #include "blargg_source.h"
21 
22 enum {
23 	cmd_gg_stereo       = 0x4F,
24 	cmd_psg             = 0x50,
25 	cmd_ym2413          = 0x51,
26 	cmd_ym2612_port0    = 0x52,
27 	cmd_ym2612_port1    = 0x53,
28 	cmd_ym2151          = 0x54,
29 	cmd_delay           = 0x61,
30 	cmd_delay_735       = 0x62,
31 	cmd_delay_882       = 0x63,
32 	cmd_byte_delay      = 0x64,
33 	cmd_end             = 0x66,
34 	cmd_data_block      = 0x67,
35 	cmd_short_delay     = 0x70,
36 	cmd_pcm_delay       = 0x80,
37 	cmd_pcm_seek        = 0xE0,
38 
39 	pcm_block_type      = 0x00,
40 	ym2612_dac_port     = 0x2A
41 };
42 
command_len(int command)43 inline int command_len( int command )
44 {
45 	switch ( command >> 4 )
46 	{
47 		case 0x03:
48 		case 0x04:
49 			return 2;
50 
51 		case 0x05:
52 		case 0x0A:
53 		case 0x0B:
54 			return 3;
55 
56 		case 0x0C:
57 		case 0x0D:
58 			return 4;
59 
60 		case 0x0E:
61 		case 0x0F:
62 			return 5;
63 	}
64 
65 	check( false );
66 	return 1;
67 }
68 
69 template<class Emu>
begin_frame(short * p)70 inline void Ym_Emu<Emu>::begin_frame( short* p )
71 {
72 	require( enabled() );
73 	out = p;
74 	last_time = 0;
75 }
76 
77 template<class Emu>
run_until(int time)78 inline int Ym_Emu<Emu>::run_until( int time )
79 {
80 	int count = time - last_time;
81 	if ( count > 0 )
82 	{
83 		if ( last_time < 0 )
84 			return false;
85 		last_time = time;
86 		short* p = out;
87 		out += count * Emu::out_chan_count;
88 		Emu::run( count, p );
89 	}
90 	return true;
91 }
92 
to_fm_time(vgm_time_t t) const93 inline Vgm_Emu_Impl::fm_time_t Vgm_Emu_Impl::to_fm_time( vgm_time_t t ) const
94 {
95 	return (t * fm_time_factor + fm_time_offset) >> fm_time_bits;
96 }
97 
to_blip_time(vgm_time_t t) const98 inline blip_time_t Vgm_Emu_Impl::to_blip_time( vgm_time_t t ) const
99 {
100 	return (t * blip_time_factor) >> blip_time_bits;
101 }
102 
write_pcm(vgm_time_t vgm_time,int amp)103 void Vgm_Emu_Impl::write_pcm( vgm_time_t vgm_time, int amp )
104 {
105 	blip_time_t blip_time = to_blip_time( vgm_time );
106 	int old = dac_amp;
107 	int delta = amp - old;
108 	dac_amp = amp;
109 	if ( old >= 0 )
110 		dac_synth.offset_inline( blip_time, delta, &blip_buf );
111 	else
112 		dac_amp |= dac_disabled;
113 }
114 
run_commands(vgm_time_t end_time)115 blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
116 {
117 	vgm_time_t vgm_time = this->vgm_time;
118 	byte const* pos = this->pos;
119 	if ( pos >= data_end )
120 	{
121 		set_track_ended();
122 		if ( pos > data_end )
123 			set_warning( "Stream lacked end event" );
124 	}
125 
126 	while ( vgm_time < end_time && pos < data_end )
127 	{
128 		// TODO: be sure there are enough bytes left in stream for particular command
129 		// so we don't read past end
130 		switch ( *pos++ )
131 		{
132 		case cmd_end:
133 			pos = loop_begin; // if not looped, loop_begin == data_end
134 			break;
135 
136 		case cmd_delay_735:
137 			vgm_time += 735;
138 			break;
139 
140 		case cmd_delay_882:
141 			vgm_time += 882;
142 			break;
143 
144 		case cmd_gg_stereo:
145 			psg.write_ggstereo( to_blip_time( vgm_time ), *pos++ );
146 			break;
147 
148 		case cmd_psg:
149 			psg.write_data( to_blip_time( vgm_time ), *pos++ );
150 			break;
151 
152 		case cmd_delay:
153 			vgm_time += pos [1] * 0x100L + pos [0];
154 			pos += 2;
155 			break;
156 
157 		case cmd_byte_delay:
158 			vgm_time += *pos++;
159 			break;
160 
161 		case cmd_ym2413:
162 			if ( ym2413.run_until( to_fm_time( vgm_time ) ) )
163 				ym2413.write( pos [0], pos [1] );
164 			pos += 2;
165 			break;
166 
167 		case cmd_ym2612_port0:
168 			if ( pos [0] == ym2612_dac_port )
169 			{
170 				write_pcm( vgm_time, pos [1] );
171 			}
172 			else if ( ym2612.run_until( to_fm_time( vgm_time ) ) )
173 			{
174 				if ( pos [0] == 0x2B )
175 				{
176 					dac_disabled = (pos [1] >> 7 & 1) - 1;
177 					dac_amp |= dac_disabled;
178 				}
179 				ym2612.write0( pos [0], pos [1] );
180 			}
181 			pos += 2;
182 			break;
183 
184 		case cmd_ym2612_port1:
185 			if ( ym2612.run_until( to_fm_time( vgm_time ) ) )
186 				ym2612.write1( pos [0], pos [1] );
187 			pos += 2;
188 			break;
189 
190 		case cmd_data_block: {
191 			check( *pos == cmd_end );
192 			int type = pos [1];
193 			long size = get_le32( pos + 2 );
194 			pos += 6;
195 			if ( type == pcm_block_type )
196 				pcm_data = pos;
197 			pos += size;
198 			break;
199 		}
200 
201 		case cmd_pcm_seek:
202 			pcm_pos = pcm_data + pos [3] * 0x1000000L + pos [2] * 0x10000L +
203 					pos [1] * 0x100L + pos [0];
204 			pos += 4;
205 			break;
206 
207 		default:
208 			int cmd = pos [-1];
209 			switch ( cmd & 0xF0 )
210 			{
211 				case cmd_pcm_delay:
212 					write_pcm( vgm_time, *pcm_pos++ );
213 					vgm_time += cmd & 0x0F;
214 					break;
215 
216 				case cmd_short_delay:
217 					vgm_time += (cmd & 0x0F) + 1;
218 					break;
219 
220 				case 0x50:
221 					pos += 2;
222 					break;
223 
224 				default:
225 					pos += command_len( cmd ) - 1;
226 					set_warning( "Unknown stream event" );
227 			}
228 		}
229 	}
230 	vgm_time -= end_time;
231 	this->pos = pos;
232 	this->vgm_time = vgm_time;
233 
234 	return to_blip_time( end_time );
235 }
236 
play_frame(blip_time_t blip_time,int sample_count,sample_t * buf)237 int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
238 {
239 	// to do: timing is working mostly by luck
240 
241 	int min_pairs = sample_count >> 1;
242 	int vgm_time = ((long) min_pairs << fm_time_bits) / fm_time_factor - 1;
243 	assert( to_fm_time( vgm_time ) <= min_pairs );
244 	int pairs = min_pairs;
245 	while ( (pairs = to_fm_time( vgm_time )) < min_pairs )
246 		vgm_time++;
247 	//debug_printf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs );
248 
249 	if ( ym2612.enabled() )
250 	{
251 		ym2612.begin_frame( buf );
252 		memset( buf, 0, pairs * stereo * sizeof *buf );
253 	}
254 	else if ( ym2413.enabled() )
255 	{
256 		ym2413.begin_frame( buf );
257 	}
258 
259 	run_commands( vgm_time );
260 	ym2612.run_until( pairs );
261 	ym2413.run_until( pairs );
262 
263 	fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) -
264 			((long) pairs << fm_time_bits);
265 
266 	psg.end_frame( blip_time );
267 
268 	return pairs * stereo;
269 }
270 
271 // Update pre-1.10 header FM rates by scanning commands
update_fm_rates(long * ym2413_rate,long * ym2612_rate) const272 void Vgm_Emu_Impl::update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const
273 {
274 	byte const* p = data + 0x40;
275 	while ( p < data_end )
276 	{
277 		switch ( *p )
278 		{
279 		case cmd_end:
280 			return;
281 
282 		case cmd_psg:
283 		case cmd_byte_delay:
284 			p += 2;
285 			break;
286 
287 		case cmd_delay:
288 			p += 3;
289 			break;
290 
291 		case cmd_data_block:
292 			p += 7 + get_le32( p + 3 );
293 			break;
294 
295 		case cmd_ym2413:
296 			*ym2612_rate = 0;
297 			return;
298 
299 		case cmd_ym2612_port0:
300 		case cmd_ym2612_port1:
301 			*ym2612_rate = *ym2413_rate;
302 			*ym2413_rate = 0;
303 			return;
304 
305 		case cmd_ym2151:
306 			*ym2413_rate = 0;
307 			*ym2612_rate = 0;
308 			return;
309 
310 		default:
311 			p += command_len( *p );
312 		}
313 	}
314 }
315