diff src/console/Audacious_Driver.cxx @ 341:986f098da058 trunk

[svn] - merge in blargg's changes
author nenolod
date Thu, 07 Dec 2006 15:20:41 -0800
parents 626f9f4d79a8
children 92848bda520d
line wrap: on
line diff
--- a/src/console/Audacious_Driver.cxx	Wed Dec 06 07:57:05 2006 -0800
+++ b/src/console/Audacious_Driver.cxx	Thu Dec 07 15:20:41 2006 -0800
@@ -23,10 +23,11 @@
 #include "Audacious_Config.h"
 
 #include "Music_Emu.h"
-#include "Vfs_File.h"
+#include "Gzip_Reader.h"
 
 int const fade_threshold = 10 * 1000;
 int const fade_length    = 8 * 1000;
+int const path_max = 4096;
 
 AudaciousConsoleConfig audcfg = { 180, FALSE, 32000, TRUE, 0, 0, FALSE, 0 };
 static GThread* decode_thread;
@@ -35,7 +36,6 @@
 static volatile long pending_seek;
 extern InputPlugin console_ip;
 static Music_Emu* emu = 0;
-static int track_ended;
 
 static blargg_err_t log_err( blargg_err_t err )
 {
@@ -59,84 +59,126 @@
 	emu = NULL;
 }
 
-// Extracts track number from file path, also frees memory at end of block
-
-struct Url_Parser
-{
-	gchar* path; // path without track number specification
-	int track;   // track number (0 = first track)
-	bool track_specified; // false if no track number was specified in path
-	Url_Parser( gchar* path );
-	~Url_Parser() { g_free( path ); }
+// Handles URL parsing, file opening and identification, and file loading.
+// Keeps file header around when loading rest of file to avoid seeking
+// and re-reading.
+class File_Handler {
+public:
+	gchar* path;            // path without track number specification
+	int track;              // track number (0 = first track)
+	bool track_specified;   // false if no track number was specified in path
+	Music_Emu* emu;         // set to 0 to take ownership
+	gme_type_t type;
+	
+	// Parses path and identifies file type
+	File_Handler( const char* path, VFSFile* fd = 0 );
+	
+	// Creates emulator and returns 0. If this wasn't a music file or
+	// emulator couldn't be created, returns 1.
+	int load( long sample_rate );
+	
+	// Deletes owned emu and closes file
+	~File_Handler();
+private:
+	char header [4];
+	Vfs_File_Reader vfs_in;
+	Gzip_Reader in;
 };
 
-Url_Parser::Url_Parser( gchar* path_in )
+File_Handler::File_Handler( const char* path_in, VFSFile* fd )
 {
+	emu   = 0;
+	type  = 0;
 	track = 0;
 	track_specified = false;
 	
 	path = g_strdup( path_in );
-	if ( path )
+	if ( !path )
+		return; // out of memory
+	
+	// extract track number
+	gchar* args = strchr( path, '?' ); // TODO: use strrchr()?
+	if ( args )
 	{
-		gchar* args = strchr( path, '?' );
-		if ( args )
+		*args = '\0';
+		// TODO: use func with better error reporting, and perhaps don't
+		// truncate path if there is no number after ?
+		track = atoi( args + 1 );
+		track_specified = true;
+	}
+	
+	// open vfs
+	if ( fd )
+		vfs_in.reset( fd );
+	else if ( log_err( vfs_in.open( path ) ) )
+		return;
+	
+	// now open gzip_reader on top of vfs
+	if ( log_err( in.open( &vfs_in ) ) )
+		return;
+	
+	// read and identify header
+	if ( !log_err( in.read( header, sizeof header ) ) )
+	{
+		type = gme_identify_extension( gme_identify_header( header ) );
+		if ( !type )
 		{
-			*args = '\0';
-			track = atoi( args + 1 );
-			track_specified = true;
+			type = gme_identify_extension( path );
+			if ( type != gme_gym_type ) // only trust file extension for headerless .gym files
+				type = 0;
 		}
 	}
 }
 
-// Determine file type based on header contents. Returns 0 if unrecognized or path is NULL.
-static gme_type_t identify_file( gchar* path )
+File_Handler::~File_Handler()
 {
-	if ( path )
-	{
-		char header [4] = { };
-		GME_FILE_READER in;
-		if ( !log_err( in.open( path ) ) && !log_err( in.read( header, sizeof header ) ) )
-			return gme_identify_extension( gme_identify_header( header ), gme_type_list() );
-	}
-	return 0;
+	gme_delete( emu );
+	g_free( path );
 }
 
-// Load file into emulator/info reader and load m3u in same directory, if present.
-// If emu is NULL, returns out of memory error.
-static blargg_err_t load_in_emu( Music_Emu* emu, const char* path, VFSFile* fd = 0 )
+int File_Handler::load( long sample_rate )
 {
-	if ( !emu )
-		return "Out of memory";
-	
-	Vfs_File_Reader in;
-	blargg_err_t err = 0;
-	if ( fd )
-		in.reset( fd ); // use fd and let caller close it
-	else
-		err = in.open( path );
+	if ( !type )
+		return 1;
 	
-	if ( !err )
-		err = emu->load( in );
-	in.close();
-	
-	if ( !err )
+	emu = gme_new_emu( type, sample_rate );
+	if ( !emu )
 	{
-		log_warning( emu );
-		
-		// load .m3u in same directory
-		int const path_max = 4096;
-		char m3u_path [path_max + 5];
-		strncpy( m3u_path, path, path_max );
-		m3u_path [path_max] = 0;
-		char* p = strrchr( m3u_path, '.' );
-		if ( !p )
-			p = m3u_path + strlen( m3u_path );
-		strcpy( p, ".m3u" );
-		
-		if ( emu->load_m3u( m3u_path ) ) { } // TODO: log error if m3u file exists
+		log_err( "Out of memory" );
+		return 1;
+	}
+	
+	{
+		// combine header with remaining file data
+		Remaining_Reader reader( header, sizeof header, &in );
+		if ( log_err( emu->load( reader ) ) )
+			return 1;
 	}
 	
-	return err;
+	// files can be closed now
+	in.close();
+	vfs_in.close();
+	
+	log_warning( emu );
+	
+	// load .m3u from same directory( replace/add extension with ".m3u")
+	char m3u_path [path_max + 5];
+	strncpy( m3u_path, path, path_max );
+	m3u_path [path_max] = 0;
+	// TODO: use better path-building functions
+	char* p = strrchr( m3u_path, '.' );
+	if ( !p )
+		p = m3u_path + strlen( m3u_path );
+	strcpy( p, ".m3u" );
+	
+	Vfs_File_Reader m3u;
+	if ( !m3u.open( m3u_path ) )
+	{
+		if ( log_err( emu->load_m3u( m3u ) ) ) // TODO: fail if m3u can't be loaded?
+			log_warning( emu ); // this will log line number of first problem in m3u
+	}
+	
+	return 0;
 }
 
 // Get info
@@ -154,6 +196,7 @@
 		if ( info.track_count > 1 )
 			ti->track_number = track + 1;
 		ti->comment    = g_strdup( info.copyright );
+		ti->genre      = g_strconcat( "Console: ", info.system, NULL );
 		
 		int length = info.length;
 		if ( length <= 0 )
@@ -180,14 +223,13 @@
 static TitleInput *get_song_tuple( gchar *path )
 {
 	TitleInput* result = 0;
-	
-	Url_Parser url( path );
-	Music_Emu* emu = gme_new_info( identify_file( url.path ) );
-	track_info_t info;
-	if ( !log_err( load_in_emu( emu, url.path ) ) &&
-			!log_err( emu->track_info( &info, url.track ) ) )
-		result = get_track_ti( url.path, info, url.track );
-	delete emu;
+	File_Handler fh( path );
+	if ( !fh.load( gme_info_only ) )
+	{
+		track_info_t info;
+		if ( !log_err( fh.emu->track_info( &info, fh.track ) ) )
+			result = get_track_ti( fh.path, info, fh.track );
+	}
 	return result;
 }
 
@@ -207,11 +249,8 @@
 {
 	g_static_mutex_lock( &playback_mutex );
 	
-	while ( console_ip_is_going )
+	while ( console_ip_is_going && !emu->track_ended() )
 	{
-		int const buf_size = 1024;
-		Music_Emu::sample_t buf [buf_size];
-		
 		// handle pending seek
 		long s = pending_seek;
 		pending_seek = -1; // TODO: use atomic swap
@@ -220,21 +259,13 @@
 			console_ip.output->flush( s * 1000 );
 			emu->seek( s * 1000 );
 		}
-
-		// fill buffer
-		if ( track_ended )
-		{
-			// TODO: remove delay once host doesn't cut the end of track off
-			int const delay = 0; // seconds
-			if ( track_ended++ > delay * emu->sample_rate() / (buf_size / 2) )
-				console_ip_is_going = false;
-			memset( buf, 0, sizeof buf );
-		}
-		else
-		{
-			emu->play( buf_size, buf );
-			track_ended = emu->track_ended();
-		}
+		
+		// fill and play buffer of audio
+		// TODO: see if larger buffer helps efficiency
+		int const buf_size = 1024;
+		Music_Emu::sample_t buf [buf_size];
+		emu->play( buf_size, buf );
+		
 		produce_audio( console_ip.output->written_time(), 
 			FMT_S16_NE, 1, sizeof buf, buf, 
 			&console_ip_is_going );
@@ -255,29 +286,25 @@
 	unload_file();
 	
 	// identify file
-	Url_Parser url( path );
-	gme_type_t type = identify_file( url.path );
-	if ( !type ) return;
+	File_Handler fh( path );
+	if ( !fh.type )
+		return;
 	
-	// sample rate
+	// select sample rate
 	long sample_rate = 0;
-	if ( type == gme_spc_type )
+	if ( fh.type == gme_spc_type )
 		sample_rate = 32000;
 	if ( audcfg.resample )
 		sample_rate = audcfg.resample_rate;
 	if ( !sample_rate )
 		sample_rate = 44100;
 	
-	// create emulator and load
-	emu = gme_new_emu( type, sample_rate );
-	if ( load_in_emu( emu, url.path ) )
-	{
-		unload_file();
+	// create emulator and load file
+	if ( fh.load( sample_rate ) )
 		return;
-	}
 	
 	// stereo echo depth
-	gme_set_stereo_depth( emu, 1.0 / 100 * audcfg.echo );
+	gme_set_stereo_depth( fh.emu, 1.0 / 100 * audcfg.echo );
 	
 	// set equalizer
 	if ( audcfg.treble || audcfg.bass )
@@ -292,53 +319,54 @@
 		double treble = audcfg.treble / 100.0;
 		eq.treble = treble * (treble < 0 ? 50.0 : 5.0);
 		
-		emu->set_equalizer(eq);
+		fh.emu->set_equalizer(eq);
 	}
 	
 	// get info
 	int length = -1;
 	track_info_t info;
-	if ( !log_err( emu->track_info( &info, url.track ) ) )
+	if ( !log_err( fh.emu->track_info( &info, fh.track ) ) )
 	{
-		if ( type == gme_spc_type && audcfg.ignore_spc_length )
+		if ( fh.type == gme_spc_type && audcfg.ignore_spc_length )
 			info.length = -1;
-		TitleInput* ti = get_track_ti( url.path, info, url.track );
+		TitleInput* ti = get_track_ti( fh.path, info, fh.track );
 		if ( ti )
 		{
 			char* title = format_and_free_ti( ti, &length );
 			if ( title )
 			{
-				console_ip.set_info( title, length, emu->voice_count() * 1000, sample_rate, 2 );
+				console_ip.set_info( title, length, fh.emu->voice_count() * 1000, sample_rate, 2 );
 				g_free( title );
 			}
 		}
 	}
+	
+	// start track
+	if ( log_err( fh.emu->start_track( fh.track ) ) )
+		return;
+	log_warning( fh.emu );
+	if ( !console_ip.output->open_audio( FMT_S16_NE, sample_rate, 2 ) )
+		return;
+	
+	// set fade time
 	if ( length <= 0 )
 		length = audcfg.loop_length * 1000;
-	
-	if ( log_err( emu->start_track( url.track ) ) )
-	{
-		unload_file();
-		return;
-	}
-	log_warning( emu );
-	
-	// start track
-    if ( !console_ip.output->open_audio( FMT_S16_NE, sample_rate, 2 ) )
-		return;
-	pending_seek = -1;
-	track_ended = 0;
 	if ( length >= fade_threshold + fade_length )
 		length -= fade_length;
-	emu->set_fade( length, fade_length );
+	fh.emu->set_fade( length, fade_length );
+	
+	// take ownership of emu
+	emu = fh.emu;
+	fh.emu = 0;
+	
+	pending_seek = -1;
 	console_ip_is_going = 1;
 	decode_thread = g_thread_create( play_loop_track, NULL, TRUE, NULL );
 }
 
 static void seek( gint time )
 {
-	// TODO: be sure seek works at all
-	// TODO: disallow seek on slow formats (SPC, GYM, VGM using FM)?
+	// TODO: use thread-safe atomic set
 	pending_seek = time;
 }
 
@@ -364,59 +392,37 @@
 	return console_ip_is_going ? console_ip.output->output_time() : -1;
 }
 
-static gint is_our_file_from_vfs( gchar* filename, VFSFile* fd )
+static gint is_our_file_from_vfs( gchar* path, VFSFile* fd )
 {
-	Url_Parser url( filename );
-	if ( !url.path ) return false;
-
-	// open file if not already open
-	Vfs_File_Reader in;
-	if ( !fd )
+	gint result = 0;
+	File_Handler fh( path, fd );
+	if ( fh.type )
 	{
-		if ( log_err( in.open( url.path ) ) ) return false;
-		fd = in.file();
-		// in will be closed when function ends
-	}
-	
-	// read header and identify type
-	gchar header [4] = { };
-	vfs_fread( header, sizeof header, 1, fd );
-	gme_type_t type = gme_identify_extension( gme_identify_header( header ), gme_type_list() );
-	
-	gint result = 0;
-	if ( type )
-	{
-		if ( url.track_specified || type->track_count == 1 )
+		if ( fh.track_specified || fh.type->track_count == 1 )
 		{
 			// don't even need to read file if track is specified or
 			// that file format can't have more than one track per file
 			result = 1;
 		}
-		else
+		else if ( !fh.load( gme_info_only ) )
 		{
 			// format requires reading file info to get track count
-			Music_Emu* emu = gme_new_info( type );
-			vfs_rewind( fd );
-			if ( !log_err( load_in_emu( emu, url.path, fd ) ) )
+			if ( fh.emu->track_count() == 1 )
 			{
-				if ( emu->track_count() == 1 )
-				{
-					result = 1;
-				}
-				else
+				result = 1;
+			}
+			else
+			{
+				// for multi-track types, add each track to playlist
+				for (int i = 0; i < fh.emu->track_count(); i++)
 				{
-					// for multi-track types, add each track to playlist
-					for (int i = 0; i < emu->track_count(); i++)
-					{
-						gchar _buf[4096];
-						g_snprintf(_buf, 4096, "%s?%d", url.path, i);
+					gchar _buf[path_max];
+					g_snprintf(_buf, path_max, "%s?%d", fh.path, i);
 
-						playlist_add_url(_buf);
-					}
-					result = -1;
+					playlist_add_url(_buf);
 				}
+				result = -1;
 			}
-			delete emu;
 		}
 	}
 	return result;
@@ -436,7 +442,7 @@
 	if (!aboutbox)
 	{
 		aboutbox = xmms_show_message(_("About the Console Music Decoder"),
-						_("Console music decoder engine based on Game_Music_Emu 0.5.1.\n"
+						_("Console music decoder engine based on Game_Music_Emu 0.5.2.\n"
 						"Audacious implementation by: William Pitcock <nenolod@nenolod.net>, \n"
 						"        Shay Green <gblargg@gmail.com>"),
 						_("Ok"),