2011-08-31 19:19:49 +00:00
|
|
|
// Multi_Buffer 0.4.1. http://www.slack.net/~ant/
|
2011-08-07 20:01:04 +00:00
|
|
|
|
|
|
|
#include "multi_buffer.h"
|
|
|
|
|
|
|
|
/* Copyright (C) 2003-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.h"
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
// Tracked_Blip_Buffer
|
|
|
|
|
|
|
|
int const blip_buffer_extra = 32; // TODO: explain why this value
|
|
|
|
|
|
|
|
void Tracked_init( struct Tracked_Blip_Buffer* this )
|
|
|
|
{
|
|
|
|
Blip_init( &this->blip );
|
|
|
|
this->last_non_silence = 0;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
void Tracked_clear( struct Tracked_Blip_Buffer* this )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
this->last_non_silence = 0;
|
|
|
|
Blip_clear( &this->blip );
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
void Tracked_end_frame( struct Tracked_Blip_Buffer* this, blip_time_t t )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
Blip_end_frame( &this->blip, t );
|
|
|
|
if ( this->blip.modified )
|
|
|
|
{
|
|
|
|
this->blip.modified = false;
|
|
|
|
this->last_non_silence = Blip_samples_avail( &this->blip ) + blip_buffer_extra;
|
|
|
|
}
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
unsigned Tracked_non_silent( struct Tracked_Blip_Buffer* this )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
return this->last_non_silence | unsettled( &this->blip );
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
static inline void remove_( struct Tracked_Blip_Buffer* this, int n )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
if ( (this->last_non_silence -= n) < 0 )
|
|
|
|
this->last_non_silence = 0;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
void Tracked_remove_silence( struct Tracked_Blip_Buffer* this, int n )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
remove_( this, n );
|
|
|
|
Blip_remove_silence( &this->blip, n );
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
void Tracked_remove_samples( struct Tracked_Blip_Buffer* this, int n )
|
|
|
|
{
|
|
|
|
remove_( this, n );
|
|
|
|
Blip_remove_samples( &this->blip, n );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tracked_remove_all_samples( struct Tracked_Blip_Buffer* this )
|
|
|
|
{
|
|
|
|
int avail = Blip_samples_avail( &this->blip );
|
|
|
|
if ( !Tracked_non_silent( this ) )
|
|
|
|
Tracked_remove_silence( this, avail );
|
|
|
|
else
|
|
|
|
Tracked_remove_samples( this, avail );
|
|
|
|
}
|
|
|
|
|
|
|
|
int Tracked_read_samples( struct Tracked_Blip_Buffer* this, blip_sample_t out [], int count )
|
|
|
|
{
|
|
|
|
count = Blip_read_samples( &this->blip, out, count, false );
|
|
|
|
remove_( this, count );
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stereo_Mixer
|
|
|
|
|
|
|
|
// mixers use a single index value to improve performance on register-challenged processors
|
|
|
|
// offset goes from negative to zero
|
|
|
|
|
|
|
|
void Mixer_init( struct Stereo_Mixer* this )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
this->samples_read = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mix_mono( struct Stereo_Mixer* this, blip_sample_t out_ [], int count )
|
|
|
|
{
|
|
|
|
int const bass = this->bufs [2]->blip.bass_shift_;
|
|
|
|
delta_t const* center = this->bufs [2]->blip.buffer_ + this->samples_read;
|
|
|
|
int center_sum = this->bufs [2]->blip.reader_accum_;
|
|
|
|
|
|
|
|
typedef blip_sample_t stereo_blip_sample_t [stereo];
|
|
|
|
stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_ + count;
|
|
|
|
int offset = -count;
|
|
|
|
do
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
int s = center_sum >> delta_bits;
|
|
|
|
|
|
|
|
center_sum -= center_sum >> bass;
|
|
|
|
center_sum += center [offset];
|
|
|
|
|
|
|
|
BLIP_CLAMP( s, s );
|
|
|
|
|
|
|
|
out [offset] [0] = (blip_sample_t) s;
|
|
|
|
out [offset] [1] = (blip_sample_t) s;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
2011-08-31 19:19:49 +00:00
|
|
|
while ( ++offset );
|
|
|
|
|
|
|
|
this->bufs [2]->blip.reader_accum_ = center_sum;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
static void mix_stereo( struct Stereo_Mixer* this, blip_sample_t out_ [], int count )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
blip_sample_t* BLARGG_RESTRICT out = out_ + count * stereo;
|
|
|
|
// do left + center and right + center separately to reduce register load
|
|
|
|
struct Tracked_Blip_Buffer* const* buf = &this->bufs [2];
|
|
|
|
while ( true ) // loop runs twice
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
--buf;
|
|
|
|
--out;
|
|
|
|
|
|
|
|
int const bass = this->bufs [2]->blip.bass_shift_;
|
|
|
|
delta_t const* side = (*buf)->blip.buffer_ + this->samples_read;
|
|
|
|
delta_t const* center = this->bufs [2]->blip.buffer_ + this->samples_read;
|
|
|
|
|
|
|
|
int side_sum = (*buf)->blip.reader_accum_;
|
|
|
|
int center_sum = this->bufs [2]->blip.reader_accum_;
|
|
|
|
|
|
|
|
int offset = -count;
|
|
|
|
do
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
int s = (center_sum + side_sum) >> delta_bits;
|
|
|
|
|
|
|
|
side_sum -= side_sum >> bass;
|
|
|
|
center_sum -= center_sum >> bass;
|
|
|
|
|
|
|
|
side_sum += side [offset];
|
|
|
|
center_sum += center [offset];
|
|
|
|
|
|
|
|
BLIP_CLAMP( s, s );
|
|
|
|
|
|
|
|
++offset; // before write since out is decremented to slightly before end
|
|
|
|
out [offset * stereo] = (blip_sample_t) s;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
2011-08-31 19:19:49 +00:00
|
|
|
while ( offset );
|
|
|
|
|
|
|
|
(*buf)->blip.reader_accum_ = side_sum;
|
|
|
|
|
|
|
|
if ( buf != this->bufs )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// only end center once
|
|
|
|
this->bufs [2]->blip.reader_accum_ = center_sum;
|
|
|
|
break;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
void Mixer_read_pairs( struct Stereo_Mixer* this, blip_sample_t out [], int count )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
// TODO: if caller never marks buffers as modified, uses mono
|
|
|
|
// except that buffer isn't cleared, so caller can encounter
|
|
|
|
// subtle problems and not realize the cause.
|
|
|
|
this->samples_read += count;
|
|
|
|
if ( Tracked_non_silent( this->bufs [0] ) | Tracked_non_silent( this->bufs [1] ) )
|
|
|
|
mix_stereo( this, out, count );
|
|
|
|
else
|
|
|
|
mix_mono( this, out, count );
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
// Multi_Buffer
|
|
|
|
|
|
|
|
void Buffer_init( struct Multi_Buffer* this )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
int const spf = 2;
|
|
|
|
|
|
|
|
Tracked_init( &this->bufs [0] );
|
|
|
|
Tracked_init( &this->bufs [1] );
|
|
|
|
Tracked_init( &this->bufs [2] );
|
|
|
|
|
|
|
|
Mixer_init( &this->mixer );
|
|
|
|
|
|
|
|
this->length_ = 0;
|
|
|
|
this->sample_rate_ = 0;
|
|
|
|
this->channels_changed_count_ = 1;
|
|
|
|
this->channel_types_ = NULL;
|
|
|
|
this->channel_count_ = 0;
|
|
|
|
this->samples_per_frame_ = spf;
|
|
|
|
this->immediate_removal_ = true;
|
|
|
|
|
|
|
|
this->mixer.bufs [2] = &this->bufs [2];
|
|
|
|
this->mixer.bufs [0] = &this->bufs [0];
|
|
|
|
this->mixer.bufs [1] = &this->bufs [1];
|
|
|
|
|
|
|
|
this->chan.center = &this->bufs [2].blip;
|
|
|
|
this->chan.left = &this->bufs [0].blip;
|
|
|
|
this->chan.right = &this->bufs [1].blip;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
blargg_err_t Buffer_set_sample_rate( struct Multi_Buffer* this, int rate, int msec )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
int i;
|
|
|
|
for ( i = bufs_size; --i >= 0; )
|
|
|
|
RETURN_ERR( Blip_set_sample_rate( &this->bufs [i].blip, rate, msec ) );
|
|
|
|
|
|
|
|
this->sample_rate_ = Blip_sample_rate( &this->bufs [0].blip );
|
|
|
|
this->length_ = Blip_length( &this->bufs [0].blip );
|
|
|
|
return 0;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
void Buffer_clock_rate( struct Multi_Buffer* this, int rate )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
int i;
|
|
|
|
for ( i = bufs_size; --i >= 0; )
|
|
|
|
Blip_set_clock_rate( &this->bufs [i].blip, rate );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer_bass_freq( struct Multi_Buffer* this, int bass )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for ( i = bufs_size; --i >= 0; )
|
|
|
|
Blip_bass_freq( &this->bufs [i].blip, bass );
|
|
|
|
}
|
|
|
|
|
|
|
|
blargg_err_t Buffer_set_channel_count( struct Multi_Buffer* this, int n, int const* types )
|
|
|
|
{
|
|
|
|
this->channel_count_ = n;
|
|
|
|
this->channel_types_ = types;
|
|
|
|
return 0;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 19:19:49 +00:00
|
|
|
struct channel_t Buffer_channel( struct Multi_Buffer* this, int i )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
(void) i;
|
|
|
|
return this->chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer_clear( struct Multi_Buffer* this )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
this->mixer.samples_read = 0;
|
|
|
|
for ( i = bufs_size; --i >= 0; )
|
|
|
|
Tracked_clear( &this->bufs [i] );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer_end_frame( struct Multi_Buffer* this, blip_time_t clock_count )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for ( i = bufs_size; --i >= 0; )
|
|
|
|
Tracked_end_frame( &this->bufs [i], clock_count );
|
|
|
|
}
|
|
|
|
|
|
|
|
int Buffer_read_samples( struct Multi_Buffer* this, blip_sample_t out [], int out_size )
|
|
|
|
{
|
|
|
|
require( (out_size & 1) == 0 ); // must read an even number of samples
|
|
|
|
out_size = min( out_size, Buffer_samples_avail( this ) );
|
|
|
|
|
|
|
|
int pair_count = (int) (out_size >> 1);
|
|
|
|
if ( pair_count )
|
2011-08-07 20:01:04 +00:00
|
|
|
{
|
2011-08-31 19:19:49 +00:00
|
|
|
Mixer_read_pairs( &this->mixer, out, pair_count );
|
|
|
|
|
|
|
|
if ( Buffer_samples_avail( this ) <= 0 || this->immediate_removal_ )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for ( i = bufs_size; --i >= 0; )
|
|
|
|
{
|
|
|
|
buf_t* b = &this->bufs [i];
|
|
|
|
// TODO: might miss non-silence settling since it checks END of last read
|
|
|
|
if ( !Tracked_non_silent( b ) )
|
|
|
|
Tracked_remove_silence( b, this->mixer.samples_read );
|
|
|
|
else
|
|
|
|
Tracked_remove_samples( b, this->mixer.samples_read );
|
|
|
|
}
|
|
|
|
this->mixer.samples_read = 0;
|
|
|
|
}
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|
2011-08-31 19:19:49 +00:00
|
|
|
|
|
|
|
return out_size;
|
2011-08-07 20:01:04 +00:00
|
|
|
}
|