PageRenderTime 41ms CodeModel.GetById 9ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

/gme/Vgm_Emu.cpp

http://game-music-emu.googlecode.com/
C++ | 416 lines | 319 code | 75 blank | 22 comment | 49 complexity | 3e815abaed6c5a4563dc08baccadc0a8 MD5 | raw file
  1// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
  2
  3#include "Vgm_Emu.h"
  4
  5#include "blargg_endian.h"
  6#include <string.h>
  7#include <math.h>
  8
  9/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
 10can redistribute it and/or modify it under the terms of the GNU Lesser
 11General Public License as published by the Free Software Foundation; either
 12version 2.1 of the License, or (at your option) any later version. This
 13module is distributed in the hope that it will be useful, but WITHOUT ANY
 14WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 15FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 16details. You should have received a copy of the GNU Lesser General Public
 17License along with this module; if not, write to the Free Software Foundation,
 18Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
 19
 20#include "blargg_source.h"
 21
 22double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
 23double const rolloff = 0.990;
 24double const oversample_factor = 1.5;
 25
 26Vgm_Emu::Vgm_Emu()
 27{
 28	disable_oversampling_ = false;
 29	psg_rate   = 0;
 30	set_type( gme_vgm_type );
 31	
 32	static int const types [8] = {
 33		wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0
 34	};
 35	set_voice_types( types );
 36	
 37	set_silence_lookahead( 1 ); // tracks should already be trimmed
 38	
 39	static equalizer_t const eq = { -14.0, 80 };
 40	set_equalizer( eq );
 41}
 42
 43Vgm_Emu::~Vgm_Emu() { }
 44
 45// Track info
 46
 47static byte const* skip_gd3_str( byte const* in, byte const* end )
 48{
 49	while ( end - in >= 2 )
 50	{
 51		in += 2;
 52		if ( !(in [-2] | in [-1]) )
 53			break;
 54	}
 55	return in;
 56}
 57
 58static byte const* get_gd3_str( byte const* in, byte const* end, char* field )
 59{
 60	byte const* mid = skip_gd3_str( in, end );
 61	int len = (mid - in) / 2 - 1;
 62	if ( len > 0 )
 63	{
 64		len = min( len, (int) Gme_File::max_field_ );
 65		field [len] = 0;
 66		for ( int i = 0; i < len; i++ )
 67			field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8
 68	}
 69	return mid;
 70}
 71
 72static byte const* get_gd3_pair( byte const* in, byte const* end, char* field )
 73{
 74	return skip_gd3_str( get_gd3_str( in, end, field ), end );
 75}
 76
 77static void parse_gd3( byte const* in, byte const* end, track_info_t* out )
 78{
 79	in = get_gd3_pair( in, end, out->song );
 80	in = get_gd3_pair( in, end, out->game );
 81	in = get_gd3_pair( in, end, out->system );
 82	in = get_gd3_pair( in, end, out->author );
 83	in = get_gd3_str ( in, end, out->copyright );
 84	in = get_gd3_pair( in, end, out->dumper );
 85	in = get_gd3_str ( in, end, out->comment );
 86}
 87
 88int const gd3_header_size = 12;
 89
 90static long check_gd3_header( byte const* h, long remain )
 91{
 92	if ( remain < gd3_header_size ) return 0;
 93	if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
 94	if ( get_le32( h + 4 ) >= 0x200 ) return 0;
 95	
 96	long gd3_size = get_le32( h + 8 );
 97	if ( gd3_size > remain - gd3_header_size ) return 0;
 98	
 99	return gd3_size;
100}
101
102byte const* Vgm_Emu::gd3_data( int* size ) const
103{
104	if ( size )
105		*size = 0;
106	
107	long gd3_offset = get_le32( header().gd3_offset ) - 0x2C;
108	if ( gd3_offset < 0 )
109		return 0;
110	
111	byte const* gd3 = data + header_size + gd3_offset;
112	long gd3_size = check_gd3_header( gd3, data_end - gd3 );
113	if ( !gd3_size )
114		return 0;
115	
116	if ( size )
117		*size = gd3_size + gd3_header_size;
118	
119	return gd3;
120}
121
122static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out )
123{
124	long length = get_le32( h.track_duration ) * 10 / 441;
125	if ( length > 0 )
126	{
127		long loop = get_le32( h.loop_duration );
128		if ( loop > 0 && get_le32( h.loop_offset ) )
129		{
130			out->loop_length = loop * 10 / 441;
131			out->intro_length = length - out->loop_length;
132		}
133		else
134		{
135			out->length = length; // 1000 / 44100 (VGM files used 44100 as timebase)
136			out->intro_length = length; // make it clear that track is no longer than length
137			out->loop_length = 0;
138		}
139	}
140}
141
142blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const
143{
144	get_vgm_length( header(), out );
145	
146	int size;
147	byte const* gd3 = gd3_data( &size );
148	if ( gd3 )
149		parse_gd3( gd3 + gd3_header_size, gd3 + size, out );
150	
151	return 0;
152}
153
154static blargg_err_t check_vgm_header( Vgm_Emu::header_t const& h )
155{
156	if ( memcmp( h.tag, "Vgm ", 4 ) )
157		return gme_wrong_file_type;
158	return 0;
159}
160
161struct Vgm_File : Gme_Info_
162{
163	Vgm_Emu::header_t h;
164	blargg_vector<byte> gd3;
165	
166	Vgm_File() { set_type( gme_vgm_type ); }
167	
168	blargg_err_t load_( Data_Reader& in )
169	{
170		long file_size = in.remain();
171		if ( file_size <= Vgm_Emu::header_size )
172			return gme_wrong_file_type;
173		
174		RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) );
175		RETURN_ERR( check_vgm_header( h ) );
176		
177		long gd3_offset = get_le32( h.gd3_offset ) - 0x2C;
178		long remain = file_size - Vgm_Emu::header_size - gd3_offset;
179		byte gd3_h [gd3_header_size];
180		if ( gd3_offset > 0 && remain >= gd3_header_size )
181		{
182			RETURN_ERR( in.skip( gd3_offset ) );
183			RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) );
184			long gd3_size = check_gd3_header( gd3_h, remain );
185			if ( gd3_size )
186			{
187				RETURN_ERR( gd3.resize( gd3_size ) );
188				RETURN_ERR( in.read( gd3.begin(), gd3.size() ) );
189			}
190		}
191		return 0;
192	}
193	
194	blargg_err_t track_info_( track_info_t* out, int ) const
195	{
196		get_vgm_length( h, out );
197		if ( gd3.size() )
198			parse_gd3( gd3.begin(), gd3.end(), out );
199		return 0;
200	}
201};
202
203static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; }
204static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; }
205
206static gme_type_t_ const gme_vgm_type_ = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 };
207gme_type_t const gme_vgm_type = &gme_vgm_type_;
208
209static gme_type_t_ const gme_vgz_type_ = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 };
210gme_type_t const gme_vgz_type = &gme_vgz_type_;
211
212
213// Setup
214
215void Vgm_Emu::set_tempo_( double t )
216{
217	if ( psg_rate )
218	{
219		vgm_rate = (long) (44100 * t + 0.5);
220		blip_time_factor = (long) floor( double (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 );
221		//debug_printf( "blip_time_factor: %ld\n", blip_time_factor );
222		//debug_printf( "vgm_rate: %ld\n", vgm_rate );
223		// TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only)
224		//blip_time_factor = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 );
225		//vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 );
226		
227		fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 );
228	}
229}
230
231blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate )
232{
233	RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) );
234	return Classic_Emu::set_sample_rate_( sample_rate );
235}
236
237void Vgm_Emu::update_eq( blip_eq_t const& eq )
238{
239	psg.treble_eq( eq );
240	dac_synth.treble_eq( eq );
241}
242
243void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
244{
245	if ( i < psg.osc_count )
246		psg.osc_output( i, c, l, r );
247}
248
249void Vgm_Emu::mute_voices_( int mask )
250{
251	Classic_Emu::mute_voices_( mask );
252	dac_synth.output( &blip_buf );
253	if ( uses_fm )
254	{
255		psg.output( (mask & 0x80) ? 0 : &blip_buf );
256		if ( ym2612.enabled() )
257		{
258			dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() );
259			ym2612.mute_voices( mask );
260		}
261		
262		if ( ym2413.enabled() )
263		{
264			int m = mask & 0x3F;
265			if ( mask & 0x20 )
266				m |= 0x01E0; // channels 5-8
267			if ( mask & 0x40 )
268				m |= 0x3E00;
269			ym2413.mute_voices( m );
270		}
271	}
272}
273
274blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size )
275{
276	assert( offsetof (header_t,unused2 [8]) == header_size );
277	
278	if ( new_size <= header_size )
279		return gme_wrong_file_type;
280	
281	header_t const& h = *(header_t const*) new_data;
282	
283	RETURN_ERR( check_vgm_header( h ) );
284	
285	check( get_le32( h.version ) <= 0x150 );
286	
287	// psg rate
288	psg_rate = get_le32( h.psg_rate );
289	if ( !psg_rate )
290		psg_rate = 3579545;
291	blip_buf.clock_rate( psg_rate );
292	
293	data     = new_data;
294	data_end = new_data + new_size;
295	
296	// get loop
297	loop_begin = data_end;
298	if ( get_le32( h.loop_offset ) )
299		loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)];
300	
301	set_voice_count( psg.osc_count );
302	
303	RETURN_ERR( setup_fm() );
304	
305	static const char* const fm_names [] = {
306		"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
307	};
308	static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
309	set_voice_names( uses_fm ? fm_names : psg_names );
310	
311	// do after FM in case output buffer is changed
312	return Classic_Emu::setup_buffer( psg_rate );
313}
314
315blargg_err_t Vgm_Emu::setup_fm()
316{
317	long ym2612_rate = get_le32( header().ym2612_rate );
318	long ym2413_rate = get_le32( header().ym2413_rate );
319	if ( ym2413_rate && get_le32( header().version ) < 0x110 )
320		update_fm_rates( &ym2413_rate, &ym2612_rate );
321	
322	uses_fm = false;
323	
324	fm_rate = blip_buf.sample_rate() * oversample_factor;
325	
326	if ( ym2612_rate )
327	{
328		uses_fm = true;
329		if ( disable_oversampling_ )
330			fm_rate = ym2612_rate / 144.0;
331		Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() );
332		RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) );
333		ym2612.enable( true );
334		set_voice_count( 8 );
335	}
336	
337	if ( !uses_fm && ym2413_rate )
338	{
339		uses_fm = true;
340		if ( disable_oversampling_ )
341			fm_rate = ym2413_rate / 72.0;
342		Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() );
343		int result = ym2413.set_rate( fm_rate, ym2413_rate );
344		if ( result == 2 )
345			return "YM2413 FM sound isn't supported";
346		CHECK_ALLOC( !result );
347		ym2413.enable( true );
348		set_voice_count( 8 );
349	}
350	
351	if ( uses_fm )
352	{
353		RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) );
354		psg.volume( 0.135 * fm_gain * gain() );
355	}
356	else
357	{
358		ym2612.enable( false );
359		ym2413.enable( false );
360		psg.volume( gain() );
361	}
362	
363	return 0;
364}
365
366// Emulation
367
368blargg_err_t Vgm_Emu::start_track_( int track )
369{
370	RETURN_ERR( Classic_Emu::start_track_( track ) );
371	psg.reset( get_le16( header().noise_feedback ), header().noise_width );
372	
373	dac_disabled = -1;
374	pos          = data + header_size;
375	pcm_data     = pos;
376	pcm_pos      = pos;
377	dac_amp      = -1;
378	vgm_time     = 0;
379	if ( get_le32( header().version ) >= 0x150 )
380	{
381		long data_offset = get_le32( header().data_offset );
382		check( data_offset );
383		if ( data_offset )
384			pos += data_offset + offsetof (header_t,data_offset) - 0x40;
385	}
386	
387	if ( uses_fm )
388	{
389		if ( ym2413.enabled() )
390			ym2413.reset();
391		
392		if ( ym2612.enabled() )
393			ym2612.reset();
394		
395		fm_time_offset = 0;
396		blip_buf.clear();
397		Dual_Resampler::clear();
398	}
399	return 0;
400}
401
402blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec )
403{
404	time_io = run_commands( msec * vgm_rate / 1000 );
405	psg.end_frame( time_io );
406	return 0;
407}
408
409blargg_err_t Vgm_Emu::play_( long count, sample_t* out )
410{
411	if ( !uses_fm )
412		return Classic_Emu::play_( count, out );
413		
414	Dual_Resampler::dual_play( count, out, blip_buf );
415	return 0;
416}