diff src/Input/console/Track_Emu.cxx @ 0:13389e613d67 trunk

[svn] - initial import of audacious-plugins tree (lots to do)
author nenolod
date Mon, 18 Sep 2006 01:11:49 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Input/console/Track_Emu.cxx	Mon Sep 18 01:11:49 2006 -0700
@@ -0,0 +1,186 @@
+
+// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
+
+#include "Track_Emu.h"
+
+#include <string.h>
+#include <math.h>
+
+/* Copyright (C) 2005-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details. You should have received a copy of the GNU Lesser General
+Public License along with this module; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include BLARGG_SOURCE_BEGIN
+
+int const stereo = 2; // channels for a stereo signal
+int const fade_block_size = 512;
+int const fade_length = 8000; // msec
+int const silence_max = 6; // seconds
+int const silence_threshold = 0x10;
+
+long Track_Emu::msec_to_samples( long msec ) const
+{
+	long rate = emu->sample_rate() * stereo;
+	return (msec / 1000L) * rate + (msec % 1000L) * rate / 1000;
+}
+
+void Track_Emu::sync( long time )
+{
+	buf_count = 0;
+	silence_count = 0;
+	emu_time = time;
+	out_time = time;
+	silence_time = time;
+	track_ended = time > fade_time + fade_length * stereo * emu->sample_rate();
+}
+
+void Track_Emu::restart_track()
+{
+	emu->start_track( track );
+	sync ( 0 );
+	
+	// skip initial silence
+	for ( int n = 40 * stereo * emu->sample_rate() / buf_size; n--; )
+	{
+		fill_buf( true );
+		if ( buf_count || track_ended )
+			break;
+	}
+	sync( 0 );
+}
+
+void Track_Emu::seek( long time )
+{
+	long pos = msec_to_samples( time ) & ~1;
+	if ( pos < out_time )
+		restart_track();
+	emu->skip( pos - emu_time );
+	sync( pos );
+}
+
+long Track_Emu::tell() const
+{
+	long rate = emu->sample_rate() * stereo;
+	return (out_time / rate * 1000) + (out_time % rate * 1000 / rate);
+}
+
+void Track_Emu::start_track( Music_Emu* e, int t, long length, bool ds )
+{
+// to do: remove
+//length = 50 * 1000;
+//ds = true;
+//t = 23;
+
+	emu = e;
+	track = t;
+	detect_silence = ds;
+	fade_factor = pow( 0.005, 1.0 / msec_to_samples( fade_length ) );
+	fade_time = msec_to_samples( length );
+	restart_track();
+}
+
+static bool is_silence( const Music_Emu::sample_t* p, int count )
+{
+	while ( count-- )
+	{
+		if ( (unsigned) (*p++ + silence_threshold / 2) > (unsigned) silence_threshold )
+			return false;
+	}
+	return true;
+}
+
+void Track_Emu::fill_buf( bool check_silence )
+{
+	emu->play( buf_size, buf );
+	emu_time += buf_size;
+	if ( (check_silence || emu_time > fade_time) && is_silence( buf, buf_size ) )
+	{
+		silence_count += buf_size;
+	}
+	else
+	{
+		silence_time = emu_time;
+		buf_count = buf_size;
+	}
+	if ( emu->track_ended() || emu->error_count() )
+		track_ended = true;
+}
+
+inline void Track_Emu::end_track()
+{
+	silence_count = 0;
+	buf_count = 0;
+	track_ended = true;
+}
+
+bool Track_Emu::play( int out_count, Music_Emu::sample_t* out )
+{
+	assert( out_count % 2 == 0 );
+	assert( emu );
+	
+	int pos = 0;
+	while ( pos < out_count )
+	{
+		// fill with any remaining silence
+		int count = min( silence_count, out_count - pos );
+		if ( count )
+		{
+			silence_count -= count;
+			memset( &out [pos], 0, count * sizeof *out );
+		}
+		else
+		{
+			// empty internal buffer
+			count = min( buf_count, out_count - pos );
+			if ( !count && track_ended )
+			{
+				memset( &out [pos], 0, (out_count - pos) * sizeof *out );
+				return true;
+			}
+			
+			memcpy( &out [pos], &buf [buf_size - buf_count], count * sizeof *out );
+			buf_count -= count;
+		}
+		pos += count;
+		
+		// keep internal buffer full and possibly run ahead
+		for ( int n = 6; n--; )
+		{
+			if ( buf_count || track_ended ||
+					emu_time - out_time > silence_max * stereo * emu->sample_rate() )
+				break;
+			fill_buf( detect_silence );
+		}
+	}
+	out_time += out_count;
+	
+	if ( detect_silence && 
+		( emu_time - silence_time > silence_max * stereo * emu->sample_rate() && silence_time ) )
+		end_track();
+	
+	// fade if track is ending
+	if ( out_time > fade_time )
+	{
+		for ( int i = 0; i < out_count; i += fade_block_size )
+		{
+			double gain = pow( fade_factor, (double) (out_time + i - fade_time) );
+			if ( gain < 0.005 )
+				end_track();
+			
+			int count = min( fade_block_size, out_count - i );
+			int igain = (unsigned int)((double)gain * (1 << 15));;
+			for ( int j = 0; j < count; j++ )
+				out [i + j] = (out [i + j] * igain) >> 15;
+		}
+	}
+	
+	return !silence_count && !buf_count && track_ended;
+}
+