summaryrefslogtreecommitdiff
path: root/apps/codecs/libgme/multi_buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/libgme/multi_buffer.c')
-rw-r--r--apps/codecs/libgme/multi_buffer.c390
1 files changed, 225 insertions, 165 deletions
diff --git a/apps/codecs/libgme/multi_buffer.c b/apps/codecs/libgme/multi_buffer.c
index 26cb8cd..554778c 100644
--- a/apps/codecs/libgme/multi_buffer.c
+++ b/apps/codecs/libgme/multi_buffer.c
@@ -1,4 +1,4 @@
-// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
+// Multi_Buffer 0.4.1. http://www.slack.net/~ant/
#include "multi_buffer.h"
@@ -15,212 +15,272 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
-#ifdef BLARGG_ENABLE_OPTIMIZER
- #include BLARGG_ENABLE_OPTIMIZER
-#endif
-
-// Stereo_Buffer
-
-void Buffer_init( struct Stereo_Buffer* this )
-{
- Blip_init( &this->bufs [0] );
- Blip_init( &this->bufs [1] );
- Blip_init( &this->bufs [2] );
-
- this->chan.center = &this->bufs [0];
- this->chan.left = &this->bufs [1];
- this->chan.right = &this->bufs [2];
-
- this->length_ = 0;
- this->sample_rate_ = 0;
- this->channels_changed_count_ = 1;
- this->samples_per_frame_ = 2;
+// 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;
}
-blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long rate, int msec )
+void Tracked_clear( struct Tracked_Blip_Buffer* this )
{
- int i;
- for ( i = 0; i < buf_count; i++ )
- RETURN_ERR( Blip_set_sample_rate( &this->bufs[i], rate, msec ) );
-
- this->sample_rate_ = Blip_sample_rate( &this->bufs [0] );
- this->length_ = Blip_length( &this->bufs [0] );
- return 0;
+ this->last_non_silence = 0;
+ Blip_clear( &this->blip );
}
-void Buffer_clock_rate( struct Stereo_Buffer* this, long rate )
+void Tracked_end_frame( struct Tracked_Blip_Buffer* this, blip_time_t t )
{
- int i;
- for ( i = 0; i < buf_count; i++ )
- Blip_set_clock_rate( &this->bufs [i], rate );
+ 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;
+ }
}
-void Buffer_bass_freq( struct Stereo_Buffer* this, int bass )
+unsigned Tracked_non_silent( struct Tracked_Blip_Buffer* this )
{
- unsigned i;
- for ( i = 0; i < buf_count; i++ )
- Blip_bass_freq( &this->bufs [i], bass );
+ return this->last_non_silence | unsettled( &this->blip );
}
-struct channel_t Buffer_channel( struct Stereo_Buffer* this )
+static inline void remove_( struct Tracked_Blip_Buffer* this, int n )
{
- return this->chan;
+ if ( (this->last_non_silence -= n) < 0 )
+ this->last_non_silence = 0;
}
-void Buffer_clear( struct Stereo_Buffer* this )
+void Tracked_remove_silence( struct Tracked_Blip_Buffer* this, int n )
{
- this->stereo_added = 0;
- this->was_stereo = false;
- int i;
- for ( i = 0; i < buf_count; i++ )
- Blip_clear( &this->bufs [i], 1 );
+ remove_( this, n );
+ Blip_remove_silence( &this->blip, n );
}
-void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t clock_count )
+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 )
{
- this->stereo_added = 0;
- unsigned i;
- for ( i = 0; i < buf_count; i++ )
+ 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
{
- this->stereo_added |= Blip_clear_modified( &this->bufs [i] ) << i;
- Blip_end_frame( &this->bufs [i], clock_count );
+ 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;
}
+ while ( ++offset );
+
+ this->bufs [2]->blip.reader_accum_ = center_sum;
}
-long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t* out, long count )
+static void mix_stereo( struct Stereo_Mixer* this, blip_sample_t out_ [], int count )
{
- require( !(count & 1) ); // count must be even
- count = (unsigned) count / 2;
-
- long avail = Blip_samples_avail( &this->bufs [0] );
- if ( count > avail )
- count = avail;
- if ( count )
+ 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
{
- int bufs_used = this->stereo_added | this->was_stereo;
- //dprintf( "%X\n", bufs_used );
- if ( bufs_used <= 1 )
- {
- Buffer_mix_mono( this, out, count );
- Blip_remove_samples( &this->bufs [0], count );
- Blip_remove_silence( &this->bufs [1], count );
- Blip_remove_silence( &this->bufs [2], count );
- }
- else if ( bufs_used & 1 )
- {
- Buffer_mix_stereo( this, out, count );
- Blip_remove_samples( &this->bufs [0], count );
- Blip_remove_samples( &this->bufs [1], count );
- Blip_remove_samples( &this->bufs [2], count );
- }
- else
- {
- Buffer_mix_stereo_no_center( this, out, count );
- Blip_remove_silence( &this->bufs [0], count );
- Blip_remove_samples( &this->bufs [1], count );
- Blip_remove_samples( &this->bufs [2], count );
- }
-
- // to do: this might miss opportunities for optimization
- if ( !Blip_samples_avail( &this->bufs [0] ) )
+ --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
{
- this->was_stereo = this->stereo_added;
- this->stereo_added = 0;
+ 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;
}
+ 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;
}
-
- return count * 2;
}
-unsigned Buffer_channels_changed_count( struct Stereo_Buffer* this )
+void Mixer_read_pairs( struct Stereo_Mixer* this, blip_sample_t out [], int count )
{
- return this->channels_changed_count_;
+ // 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 );
}
-void Buffer_channels_changed( struct Stereo_Buffer* this )
+// Multi_Buffer
+
+void Buffer_init( struct Multi_Buffer* this )
{
- this->channels_changed_count_++;
+ 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;
}
-void Buffer_mix_stereo( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+blargg_err_t Buffer_set_sample_rate( struct Multi_Buffer* this, int rate, int msec )
{
- blip_sample_t* BLIP_RESTRICT out = out_;
- int const bass = BLIP_READER_BASS( this->bufs [1] );
- BLIP_READER_BEGIN( left, this->bufs [1] );
- BLIP_READER_BEGIN( right, this->bufs [2] );
- BLIP_READER_BEGIN( center, this->bufs [0] );
-
- for ( ; count; --count )
- {
- int c = BLIP_READER_READ( center );
- blargg_long l = c + BLIP_READER_READ( left );
- blargg_long r = c + BLIP_READER_READ( right );
- if ( (int16_t) l != l )
- l = 0x7FFF - (l >> 24);
-
- BLIP_READER_NEXT( center, bass );
- if ( (int16_t) r != r )
- r = 0x7FFF - (r >> 24);
-
- BLIP_READER_NEXT( left, bass );
- BLIP_READER_NEXT( right, bass );
-
- out [0] = l;
- out [1] = r;
- out += 2;
- }
-
- BLIP_READER_END( center, this->bufs [0] );
- BLIP_READER_END( right, this->bufs [2] );
- BLIP_READER_END( left, this->bufs [1] );
+ 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;
}
-void Buffer_mix_stereo_no_center( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+void Buffer_clock_rate( struct Multi_Buffer* this, int rate )
{
- blip_sample_t* BLIP_RESTRICT out = out_;
- int const bass = BLIP_READER_BASS( this->bufs [1] );
- BLIP_READER_BEGIN( left, this->bufs [1] );
- BLIP_READER_BEGIN( right, this->bufs [2] );
-
- for ( ; count; --count )
- {
- blargg_long l = BLIP_READER_READ( left );
- if ( (int16_t) l != l )
- l = 0x7FFF - (l >> 24);
-
- blargg_long r = BLIP_READER_READ( right );
- if ( (int16_t) r != r )
- r = 0x7FFF - (r >> 24);
-
- BLIP_READER_NEXT( left, bass );
- BLIP_READER_NEXT( right, bass );
-
- out [0] = l;
- out [1] = r;
- out += 2;
- }
-
- BLIP_READER_END( right, this->bufs [2] );
- BLIP_READER_END( left, this->bufs [1] );
+ 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;
}
-void Buffer_mix_mono( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+struct channel_t Buffer_channel( struct Multi_Buffer* this, int i )
{
- blip_sample_t* BLIP_RESTRICT out = out_;
- int const bass = BLIP_READER_BASS( this->bufs [0] );
- BLIP_READER_BEGIN( center, this->bufs [0] );
-
- for ( ; count; --count )
+ (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 )
{
- blargg_long s = BLIP_READER_READ( center );
- if ( (int16_t) s != s )
- s = 0x7FFF - (s >> 24);
-
- BLIP_READER_NEXT( center, bass );
- out [0] = s;
- out [1] = s;
- out += 2;
+ 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;
+ }
}
-
- BLIP_READER_END( center, this->bufs [0] );
+
+ return out_size;
}