
// Blip_Buffer 0.3.0. http://www.slack.net/~ant/nes-emu/

#include "Stereo_Buffer.h"
#include <istream>
#include <ostream>

/* Library Copyright (C) 2004 Shay Green. Blip_Buffer is free software;
you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
Stereo_Buffer 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 General Public License
for more details. You should have received a copy of the GNU General
Public License along with Stereo_Buffer; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */

Stereo_Buffer::Stereo_Buffer() {
	stereo_added = false;
	was_stereo = false;
}

Stereo_Buffer::~Stereo_Buffer() {
}

bool Stereo_Buffer::set_sample_rate( long rate, int msec )
{
	for ( int i = 0; i < buf_count; i++ ) {
		if ( bufs [i].set_sample_rate( rate, msec ) )
		{
			return false;
		}
	}
	
	return true;
}

void Stereo_Buffer::clock_rate( long rate )
{
	for ( int i = 0; i < buf_count; i++ )
		bufs [i].clock_rate( rate );
}

void Stereo_Buffer::bass_freq( int bass )
{
	for ( unsigned i = 0; i < buf_count; i++ )
		bufs [i].bass_freq( bass );
}

void Stereo_Buffer::clear()
{
	stereo_added = false;
	was_stereo = false;
	for ( int i = 0; i < buf_count; i++ )
		bufs [i].clear();
}

void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo )
{
	for ( unsigned i = 0; i < buf_count; i++ )
	{
		bufs [i].end_frame( clock_count );
	}
	stereo_added |= stereo;
}



long Stereo_Buffer::read_samples( blip_sample_t* out, long max_samples )
{
	long count = bufs [0].samples_avail();
	if ( count > max_samples / 2 )
		count = max_samples / 2;
	if ( count )
	{
		if ( stereo_added || was_stereo )
		{
			mix_stereo( out, count );
			
			bufs [0].remove_samples( count );
			bufs [1].remove_samples( count );
			bufs [2].remove_samples( count );
		}
		else
		{
			mix_mono( out, count );
			
			bufs [0].remove_samples( count );
			
			bufs [1].remove_silence( count );
			bufs [2].remove_silence( count );
		}
		
		// to do: this might miss opportunities for optimization
		if ( !bufs [0].samples_avail() ) {
			was_stereo = stereo_added;
			stereo_added = false;
		}
	}
	
	return count * 2;
}

void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count )
{
	Blip_Reader l_left; 
	Blip_Reader l_right; 
	Blip_Reader l_center;
	
	l_left.begin( bufs [1] );
	l_right.begin( bufs [2] );
	int bass = l_center.begin( bufs [0] );
	
	while ( count-- )
	{
		int c = l_center.read();
		out [0] = (blip_sample_t) (c + l_left.read());
		out [1] = (blip_sample_t) (c + l_right.read());
		out += 2;
		
		l_center.next( bass );
		l_left.next( bass );
		l_right.next( bass );
	}
	
	l_center.end( bufs [0] );
	l_right.end( bufs [2] );
	l_left.end( bufs [1] );
}

void Stereo_Buffer::mix_mono( blip_sample_t* out, long count )
{
	Blip_Reader in;
	int bass = in.begin( bufs [0] );
	
	while ( count-- )
	{
		int sample = in.read();
		out [0] = sample;
		out [1] = sample;
		out += 2;
		in.next( bass );
	}
	
	in.end( bufs [0] );
}

void Stereo_Buffer::SaveState( std::ostream& stream )
{
	for ( int i = 0; i < buf_count; i++ )
	{
		blip_buffer_state_t state;
		bufs [i].save_state( &state );
		stream.write( reinterpret_cast<const char*>( &state ), sizeof( state ) );
	}

	stream.write( reinterpret_cast<const char*>( &stereo_added ), sizeof( stereo_added ) );
	stream.write( reinterpret_cast<const char*>( &was_stereo ), sizeof( was_stereo ) );
}

void Stereo_Buffer::LoadState( std::istream& stream )
{
	for ( int i = 0; i < buf_count; i++ )
	{
		blip_buffer_state_t state;
		stream.read( reinterpret_cast<char*>( &state ), sizeof( state ) );
		bufs [i].load_state( state );
	}

	stream.read( reinterpret_cast<char*>( &stereo_added ), sizeof( stereo_added ) );
	stream.read( reinterpret_cast<char*>( &was_stereo ), sizeof( was_stereo ) );
}
