1 // Game_Music_Emu 0.6.0. http://www.slack.net/~ant/
2 
3 #include "Gym_Emu.h"
4 
5 #include "blargg_endian.h"
6 #include <string.h>
7 
8 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
9 can redistribute it and/or modify it under the terms of the GNU Lesser
10 General Public License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version. This
12 module is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 details. You should have received a copy of the GNU Lesser General Public
16 License along with this module; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18 
19 #include "blargg_source.h"
20 
21 double const min_tempo = 0.25;
22 double const oversample_factor = 5 / 3.0;
23 double const fm_gain = 3.0;
24 
25 const long base_clock = 53700300;
26 const long clock_rate = base_clock / 15;
27 
Gym_Emu()28 Gym_Emu::Gym_Emu()
29 {
30 	data = 0;
31 	pos  = 0;
32 	set_type( gme_gym_type );
33 
34 	static const char* const names [] = {
35 		"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
36 	};
37 	set_voice_names( names );
38 	set_silence_lookahead( 1 ); // tracks should already be trimmed
39 }
40 
~Gym_Emu()41 Gym_Emu::~Gym_Emu() { }
42 
43 // Track info
44 
get_gym_info(Gym_Emu::header_t const & h,long length,track_info_t * out)45 static void get_gym_info( Gym_Emu::header_t const& h, long length, track_info_t* out )
46 {
47 	if ( !memcmp( h.tag, "GYMX", 4 ) )
48 	{
49 		length = length * 50 / 3; // 1000 / 60
50 		long loop = get_le32( h.loop_start );
51 		if ( loop )
52 		{
53 			out->intro_length = loop * 50 / 3;
54 			out->loop_length  = length - out->intro_length;
55 		}
56 		else
57 		{
58 			out->length = length;
59 			out->intro_length = length; // make it clear that track is no longer than length
60 			out->loop_length = 0;
61 		}
62 
63 		// more stupidity where the field should have been left
64 		if ( strcmp( h.song, "Unknown Song" ) )
65 			GME_COPY_FIELD( h, out, song );
66 
67 		if ( strcmp( h.game, "Unknown Game" ) )
68 			GME_COPY_FIELD( h, out, game );
69 
70 		if ( strcmp( h.copyright, "Unknown Publisher" ) )
71 			GME_COPY_FIELD( h, out, copyright );
72 
73 		if ( strcmp( h.dumper, "Unknown Person" ) )
74 			GME_COPY_FIELD( h, out, dumper );
75 
76 		if ( strcmp( h.comment, "Header added by YMAMP" ) )
77 			GME_COPY_FIELD( h, out, comment );
78 	}
79 }
80 
track_info_(track_info_t * out,int) const81 blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const
82 {
83 	get_gym_info( header_, track_length(), out );
84 	return 0;
85 }
86 
gym_track_length(byte const * p,byte const * end)87 static long gym_track_length( byte const* p, byte const* end )
88 {
89 	long time = 0;
90 	while ( p < end )
91 	{
92 		switch ( *p++ )
93 		{
94 			case 0:
95 				time++;
96 				break;
97 
98 			case 1:
99 			case 2:
100 				p += 2;
101 				break;
102 
103 			case 3:
104 				p += 1;
105 				break;
106 		}
107 	}
108 	return time;
109 }
110 
track_length() const111 long Gym_Emu::track_length() const { return gym_track_length( data, data_end ); }
112 
check_header(byte const * in,long size,int * data_offset=0)113 static blargg_err_t check_header( byte const* in, long size, int* data_offset = 0 )
114 {
115 	if ( size < 4 )
116 		return gme_wrong_file_type;
117 
118 	if ( memcmp( in, "GYMX", 4 ) == 0 )
119 	{
120 		if ( size < Gym_Emu::header_size + 1 )
121 			return gme_wrong_file_type;
122 
123 		if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 )
124 			return "Packed GYM file not supported";
125 
126 		if ( data_offset )
127 			*data_offset = Gym_Emu::header_size;
128 	}
129 	else if ( *in > 3 )
130 	{
131 		return gme_wrong_file_type;
132 	}
133 
134 	return 0;
135 }
136 
137 struct Gym_File : Gme_Info_
138 {
139 	byte const* file_begin;
140 	byte const* file_end;
141 	int data_offset;
142 
Gym_FileGym_File143 	Gym_File() { set_type( gme_gym_type ); }
144 
load_mem_Gym_File145 	blargg_err_t load_mem_( byte const* in, long size )
146 	{
147 		file_begin = in;
148 		file_end   = in + size;
149 		data_offset = 0;
150 		return check_header( in, size, &data_offset );
151 	}
152 
track_info_Gym_File153 	blargg_err_t track_info_( track_info_t* out, int ) const
154 	{
155 		long length = gym_track_length( &file_begin [data_offset], file_end );
156 		get_gym_info( *(Gym_Emu::header_t const*) file_begin, length, out );
157 		return 0;
158 	}
159 };
160 
new_gym_emu()161 static Music_Emu* new_gym_emu () { return BLARGG_NEW Gym_Emu ; }
new_gym_file()162 static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; }
163 
164 static gme_type_t_ const gme_gym_type_ = { "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 };
165 gme_type_t const gme_gym_type = &gme_gym_type_;
166 
167 // Setup
168 
set_sample_rate_(long sample_rate)169 blargg_err_t Gym_Emu::set_sample_rate_( long sample_rate )
170 {
171 	blip_eq_t eq( -32, 8000, sample_rate );
172 	apu.treble_eq( eq );
173 	dac_synth.treble_eq( eq );
174 	apu.volume( 0.135 * fm_gain * gain() );
175 	dac_synth.volume( 0.125 / 256 * fm_gain * gain() );
176 	double factor = Dual_Resampler::setup( oversample_factor, 0.990, fm_gain * gain() );
177 	fm_sample_rate = sample_rate * factor;
178 
179 	RETURN_ERR( blip_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) );
180 	blip_buf.clock_rate( clock_rate );
181 
182 	RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) );
183 	RETURN_ERR( Dual_Resampler::reset( long (1.0 / 60 / min_tempo * sample_rate) ) );
184 
185 	return 0;
186 }
187 
set_tempo_(double t)188 void Gym_Emu::set_tempo_( double t )
189 {
190 	if ( t < min_tempo )
191 	{
192 		set_tempo( min_tempo );
193 		return;
194 	}
195 
196 	if ( blip_buf.sample_rate() )
197 	{
198 		clocks_per_frame = long (clock_rate / 60 / tempo());
199 		Dual_Resampler::resize( long (sample_rate() / (60.0 * tempo())) );
200 	}
201 }
202 
mute_voices_(int mask)203 void Gym_Emu::mute_voices_( int mask )
204 {
205 	Music_Emu::mute_voices_( mask );
206 	fm.mute_voices( mask );
207 	dac_muted = (mask & 0x40) != 0;
208 	apu.output( (mask & 0x80) ? 0 : &blip_buf );
209 }
210 
load_mem_(byte const * in,long size)211 blargg_err_t Gym_Emu::load_mem_( byte const* in, long size )
212 {
213 	assert( offsetof (header_t,packed [4]) == header_size );
214 	int offset = 0;
215 	RETURN_ERR( check_header( in, size, &offset ) );
216 	set_voice_count( 8 );
217 
218 	data     = in + offset;
219 	data_end = in + size;
220 	loop_begin = 0;
221 
222 	if ( offset )
223 		header_ = *(header_t const*) in;
224 	else
225 		memset( &header_, 0, sizeof header_ );
226 
227 	return 0;
228 }
229 
230 // Emulation
231 
start_track_(int track)232 blargg_err_t Gym_Emu::start_track_( int track )
233 {
234 	RETURN_ERR( Music_Emu::start_track_( track ) );
235 
236 	pos         = data;
237 	loop_remain = get_le32( header_.loop_start );
238 
239 	prev_dac_count = 0;
240 	dac_enabled    = false;
241 	dac_amp        = -1;
242 
243 	fm.reset();
244 	apu.reset();
245 	blip_buf.clear();
246 	Dual_Resampler::clear();
247 	return 0;
248 }
249 
run_dac(int dac_count)250 void Gym_Emu::run_dac( int dac_count )
251 {
252 	// Guess beginning and end of sample and adjust rate and buffer position accordingly.
253 
254 	// count dac samples in next frame
255 	int next_dac_count = 0;
256 	const byte* p = this->pos;
257 	int cmd;
258 	while ( (cmd = *p++) != 0 )
259 	{
260 		int data = *p++;
261 		if ( cmd <= 2 )
262 			++p;
263 		if ( cmd == 1 && data == 0x2A )
264 			next_dac_count++;
265 	}
266 
267 	// detect beginning and end of sample
268 	int rate_count = dac_count;
269 	int start = 0;
270 	if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count )
271 	{
272 		rate_count = next_dac_count;
273 		start = next_dac_count - dac_count;
274 	}
275 	else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count )
276 	{
277 		rate_count = prev_dac_count;
278 	}
279 
280 	// Evenly space samples within buffer section being used
281 	blip_resampled_time_t period = blip_buf.resampled_duration( clocks_per_frame ) / rate_count;
282 
283 	blip_resampled_time_t time = blip_buf.resampled_time( 0 ) +
284 			period * start + (period >> 1);
285 
286 	int dac_amp = this->dac_amp;
287 	if ( dac_amp < 0 )
288 		dac_amp = dac_buf [0];
289 
290 	for ( int i = 0; i < dac_count; i++ )
291 	{
292 		int delta = dac_buf [i] - dac_amp;
293 		dac_amp += delta;
294 		dac_synth.offset_resampled( time, delta, &blip_buf );
295 		time += period;
296 	}
297 	this->dac_amp = dac_amp;
298 }
299 
parse_frame()300 void Gym_Emu::parse_frame()
301 {
302 	int dac_count = 0;
303 	const byte* pos = this->pos;
304 
305 	if ( loop_remain && !--loop_remain )
306 		loop_begin = pos; // find loop on first time through sequence
307 
308 	int cmd;
309 	while ( (cmd = *pos++) != 0 )
310 	{
311 		int data = *pos++;
312 		if ( cmd == 1 )
313 		{
314 			int data2 = *pos++;
315 			if ( data != 0x2A )
316 			{
317 				if ( data == 0x2B )
318 					dac_enabled = (data2 & 0x80) != 0;
319 
320 				fm.write0( data, data2 );
321 			}
322 			else if ( dac_count < (int) sizeof dac_buf )
323 			{
324 				dac_buf [dac_count] = data2;
325 				dac_count += dac_enabled;
326 			}
327 		}
328 		else if ( cmd == 2 )
329 		{
330 			fm.write1( data, *pos++ );
331 		}
332 		else if ( cmd == 3 )
333 		{
334 			apu.write_data( 0, data );
335 		}
336 		else
337 		{
338 			// to do: many GYM streams are full of errors, and error count should
339 			// reflect cases where music is really having problems
340 			//log_error();
341 			--pos; // put data back
342 		}
343 	}
344 
345 	// loop
346 	if ( pos >= data_end )
347 	{
348 		check( pos == data_end );
349 
350 		if ( loop_begin )
351 			pos = loop_begin;
352 		else
353 			set_track_ended();
354 	}
355 	this->pos = pos;
356 
357 	// dac
358 	if ( dac_count && !dac_muted )
359 		run_dac( dac_count );
360 	prev_dac_count = dac_count;
361 }
362 
play_frame(blip_time_t blip_time,int sample_count,sample_t * buf)363 int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
364 {
365 	if ( !track_ended() )
366 		parse_frame();
367 
368 	apu.end_frame( blip_time );
369 
370 	memset( buf, 0, sample_count * sizeof *buf );
371 	fm.run( sample_count >> 1, buf );
372 
373 	return sample_count;
374 }
375 
play_(long count,sample_t * out)376 blargg_err_t Gym_Emu::play_( long count, sample_t* out )
377 {
378 	Dual_Resampler::dual_play( count, out, blip_buf );
379 	return 0;
380 }
381