long Stereo_Buffer::read_samples( blip_sample_t* out, long out_size )
{
	require( (out_size & 1) == 0 ); // must read an even number of samples
	out_size = min( out_size, samples_avail() );

	int pair_count = int (out_size >> 1);
	if ( pair_count )
	{
		mixer.read_pairs( out, pair_count );

		if ( samples_avail() <= 0 || immediate_removal() )
		{
			for ( int i = bufs_size; --i >= 0; )
			{
				buf_t& b = bufs [i];
				// TODO: might miss non-silence settling since it checks END of last read
				if ( !b.non_silent() )
					b.remove_silence( mixer.samples_read );
				else
					b.remove_samples( mixer.samples_read );
			}
			mixer.samples_read = 0;
		}
	}
	return out_size;
}
long Effects_Buffer::read_samples( blip_sample_t* out, long out_size )
{
    out_size = min( out_size, samples_avail() );

    int pair_count = int (out_size >> 1);
    require( pair_count * stereo == out_size ); // must read an even number of samples
    if ( pair_count )
    {
        if ( no_effects )
        {
            mixer.read_pairs( out, pair_count );
        }
        else
        {
            int pairs_remain = pair_count;
            do
            {
                // mix at most max_read pairs at a time
                int count = max_read;
                if ( count > pairs_remain )
                    count = pairs_remain;

                if ( no_echo )
                {
                    // optimization: clear echo here to keep mix_effects() a leaf function
                    echo_pos = 0;
                    memset( echo.begin(), 0, count * stereo * sizeof echo [0] );
                }
                mix_effects( out, count );

                blargg_long new_echo_pos = echo_pos + count * stereo;
                if ( new_echo_pos >= echo_size )
                    new_echo_pos -= echo_size;
                echo_pos = new_echo_pos;
                assert( echo_pos < echo_size );

                out += count * stereo;
                mixer.samples_read += count;
                pairs_remain -= count;
            }
            while ( pairs_remain );
        }

        if ( samples_avail() <= 0 || immediate_removal() )
        {
            for ( int i = bufs_size; --i >= 0; )
            {
                buf_t& b = bufs [i];
                // TODO: might miss non-silence settling since it checks END of last read
                if ( b.non_silent() )
                    b.remove_samples( mixer.samples_read );
                else
                    b.remove_silence( mixer.samples_read );
            }
            mixer.samples_read = 0;
        }
    }
    return out_size;
}