Index: apps/codecs/kss.c =================================================================== --- apps/codecs/kss.c (Revision 30492) +++ apps/codecs/kss.c (Arbeitskopie) @@ -60,7 +60,7 @@ } codec_set_replaygain(ci->id3); - + /* Read the entire file */ DEBUGF("KSS: request file\n"); ci->seek_buffer(0); @@ -69,7 +69,7 @@ DEBUGF("KSS: file load failed\n"); return CODEC_ERROR; } - + if ((err = Kss_load_mem(&kss_emu, buf, ci->filesize))) { DEBUGF("KSS: Kss_load failed (%s)\n", err); return CODEC_ERROR; @@ -79,6 +79,9 @@ if (kss_emu.m3u.size > 0) kss_emu.track_count = kss_emu.m3u.size; + /* Add some stereo enhancement */ + Sound_set_stereo_depth(&kss_emu, (int)(0.40 * FP_ONE_EFFECT)); + next_track: set_codec_track(track); Index: apps/codecs/gbs.c =================================================================== --- apps/codecs/gbs.c (Revision 30492) +++ apps/codecs/gbs.c (Arbeitskopie) @@ -76,6 +76,9 @@ if (gbs_emu.m3u.size > 0) gbs_emu.track_count = gbs_emu.m3u.size; + /* Add some stereo enhancement */ + Sound_set_stereo_depth(&gbs_emu, (int)(0.40 * FP_ONE_EFFECT)); + next_track: set_codec_track(track); Index: apps/codecs/libgme/sgc_emu.h =================================================================== --- apps/codecs/libgme/sgc_emu.h (Revision 30492) +++ apps/codecs/libgme/sgc_emu.h (Arbeitskopie) @@ -179,6 +179,36 @@ this->gain = g; } +static inline void Sound_set_stereo_depth( struct Sgc_Emu* this, int depth ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = (depth > 0); + c->echo = depth; + c->stereo = depth; + c->surround = true; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) depth; +#endif +} + +static inline void Sound_set_effects( struct Sgc_Emu* this, struct gme_effects_t const* in ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = in->enabled; + c->echo = in->echo; + c->stereo = in->stereo; + c->surround = in->surround; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) in; +#endif +} + // True if Master System or Game Gear static inline bool sega_mapping( struct Sgc_Emu* this ) { Index: apps/codecs/libgme/vgm_emu.h =================================================================== --- apps/codecs/libgme/vgm_emu.h (Revision 30492) +++ apps/codecs/libgme/vgm_emu.h (Arbeitskopie) @@ -203,4 +203,34 @@ this->gain = g; } +static inline void Sound_set_stereo_depth( struct Vgm_Emu* this, int depth ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = (depth > 0); + c->echo = depth; + c->stereo = depth; + c->surround = true; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) depth; #endif +} + +static inline void Sound_set_effects( struct Vgm_Emu* this, struct gme_effects_t const* in ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = in->enabled; + c->echo = in->echo; + c->stereo = in->stereo; + c->surround = in->surround; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) in; +#endif +} + +#endif Index: apps/codecs/libgme/kss_emu.h =================================================================== --- apps/codecs/libgme/kss_emu.h (Revision 30492) +++ apps/codecs/libgme/kss_emu.h (Arbeitskopie) @@ -204,6 +204,36 @@ this->gain = g; } +static inline void Sound_set_stereo_depth( struct Kss_Emu* this, int depth ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = (depth > 0); + c->echo = depth; + c->stereo = depth; + c->surround = true; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) depth; +#endif +} + +static inline void Sound_set_effects( struct Kss_Emu* this, struct gme_effects_t const* in ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = in->enabled; + c->echo = in->echo; + c->stereo = in->stereo; + c->surround = in->surround; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) in; +#endif +} + // Emulation (You shouldn't touch these void cpu_write( struct Kss_Emu* this, kss_addr_t, int ); int cpu_in( struct Kss_Emu* this, kss_time_t, kss_addr_t ); Index: apps/codecs/libgme/blargg_common.h =================================================================== --- apps/codecs/libgme/blargg_common.h (Revision 30492) +++ apps/codecs/libgme/blargg_common.h (Arbeitskopie) @@ -24,6 +24,7 @@ #define FP_ONE_TEMPO (1LL << 24) #define FP_ONE_GAIN (1LL << 24) #define FP_ONE_VOLUME FP_ONE_GAIN +#define FP_ONE_EFFECT (1LL << 8) // IRAM configuration #if (CONFIG_CPU == MCF5250) Index: apps/codecs/libgme/multi_buffer.c =================================================================== --- apps/codecs/libgme/multi_buffer.c (Revision 30492) +++ apps/codecs/libgme/multi_buffer.c (Arbeitskopie) @@ -176,13 +176,32 @@ // Multi_Buffer +#ifndef GME_DISABLE_STEREO_DEPTH +int const fixed_shift = 12; +#define TO_FIXED( f ) (fixed_t) ((f) * FP_ONE_EFFECT) +#define FROM_FIXED( f ) ((f) / FP_ONE_EFFECT) + +int const max_read = 2560; // determines minimum delay + +// 3 wave positions with/without surround, 2 multi (one with same config as wave) +int const simple_bufs = 3 * 2 + 2 - 1; +#endif + void Buffer_init( struct Multi_Buffer* this ) { int const spf = 2; +#ifndef GME_DISABLE_STEREO_DEPTH + // Clear buffers + + Tracked_init( &this->bufs [0].tracked ); + Tracked_init( &this->bufs [1].tracked ); + Tracked_init( &this->bufs [2].tracked ); +#else Tracked_init( &this->bufs [0] ); Tracked_init( &this->bufs [1] ); Tracked_init( &this->bufs [2] ); +#endif Mixer_init( &this->mixer ); @@ -194,6 +213,44 @@ this->samples_per_frame_ = spf; this->immediate_removal_ = true; +#ifndef GME_DISABLE_STEREO_DEPTH + int const max_bufs_ = min( extra_chans + simple_bufs, max_bufs ); + int const echo_size = max_echo_size; + + this->echo_size = max( max_read * (int) stereo, echo_size & ~1 ); + this->clock_rate_ = 0; + this->bass_freq_ = 90; + this->bufs_size = 0; + this->bufs_max = max( max_bufs_, (int) extra_chans ); + this->no_echo = true; + this->no_effects = true; + + // Internal + this->echo_size_ = this->echo_size + stereo; + this->chans_size_ = 0; + + // defaults + this->config_.enabled = false; + this->config_.delay [0] = 120; + this->config_.delay [1] = 122; + this->config_.feedback = (int)(0.2 * FP_ONE_EFFECT); + this->config_.treble = (int)(0.4 * FP_ONE_EFFECT); + + static int const sep = (int)(0.8 * FP_ONE_EFFECT); + this->config_.side_chans [0].pan = -sep; + this->config_.side_chans [1].pan = +sep; + this->config_.side_chans [0].vol = (int)(1.0 * FP_ONE_EFFECT); + this->config_.side_chans [1].vol = (int)(1.0 * FP_ONE_EFFECT); + + memset( &this->s, 0, sizeof this->s ); + Buffer_clear( this ); + + // default global effects + this->effects_.echo = (int)(0.20 * FP_ONE_EFFECT); + this->effects_.stereo = (int)(0.20 * FP_ONE_EFFECT); + this->effects_.surround = true; + this->effects_.enabled = false; +#else this->mixer.bufs [2] = &this->bufs [2]; this->mixer.bufs [0] = &this->bufs [0]; this->mixer.bufs [1] = &this->bufs [1]; @@ -201,63 +258,604 @@ this->chan.center = &this->bufs [2].blip; this->chan.left = &this->bufs [0].blip; this->chan.right = &this->bufs [1].blip; +#endif } blargg_err_t Buffer_set_sample_rate( struct Multi_Buffer* this, int rate, int msec ) { +#ifndef GME_DISABLE_STEREO_DEPTH + // extra to allow farther past-the-end pointers + this->mixer.samples_read = 0; + this->echo_size_ = this->echo_size + stereo; + if ( this->echo_size_ > (max_echo_size + stereo) ) { + // warning("Echo buffer overflow"); + this->echo_size_ = max_echo_size + stereo; + } + + this->sample_rate_ = rate; + this->length_ = msec; +#else 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 ); +#endif return 0; } void Buffer_clock_rate( struct Multi_Buffer* this, int rate ) { int i; +#ifndef GME_DISABLE_STEREO_DEPTH + this->clock_rate_ = rate; + for ( i = this->bufs_size; --i >= 0; ) + Blip_set_clock_rate( &this->bufs [i].tracked.blip, rate ); +#else for ( i = bufs_size; --i >= 0; ) Blip_set_clock_rate( &this->bufs [i].blip, rate ); +#endif } void Buffer_bass_freq( struct Multi_Buffer* this, int bass ) { int i; +#ifndef GME_DISABLE_STEREO_DEPTH + this->bass_freq_ = bass; + for ( i = this->bufs_size; --i >= 0; ) + Blip_bass_freq( &this->bufs [i].tracked.blip, bass ); +#else for ( i = bufs_size; --i >= 0; ) Blip_bass_freq( &this->bufs [i].blip, bass ); +#endif } +#ifndef GME_DISABLE_STEREO_DEPTH +void assign_buffers( struct Multi_Buffer* this ) +{ + // assign channels to buffers + int i, buf_count = 0; + for ( i = 0; i < (int) this->chans_size_; i++ ) + { + // put second two side channels at end to give priority to main channels + // in case closest matching is necessary + int x = i; + if ( i > 1 ) + x += 2; + if ( x >= (int) this->chans_size_ ) + x -= (this->chans_size_ - 2); + struct chan_t* ch = &this->chans [x]; + + int b = 0; + for ( ; b < buf_count; b++ ) + { + if ( ch->vol [0] == this->bufs [b].vol [0] && + ch->vol [1] == this->bufs [b].vol [1] && + (ch->cfg.echo == this->bufs [b].echo || !this->s.feedback) ) + break; + } + + if ( b >= buf_count ) + { + if ( buf_count < this->bufs_max ) + { + this->bufs [b].vol [0] = ch->vol [0]; + this->bufs [b].vol [1] = ch->vol [1]; + this->bufs [b].echo = ch->cfg.echo; + buf_count++; + } + else + { + // TODO: this is a mess, needs refinement + dprintf( "Effects_Buffer ran out of buffers; using closest match\n" ); + b = 0; + fixed_t best_dist = TO_FIXED( 8 ); + int h; + for ( h = buf_count; --h >= 0; ) + { + #define CALC_LEVELS( vols, sum, diff, surround ) \ + fixed_t sum, diff;\ + bool surround = false;\ + {\ + fixed_t vol_0 = vols [0];\ + if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\ + fixed_t vol_1 = vols [1];\ + if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\ + sum = vol_0 + vol_1;\ + diff = vol_0 - vol_1;\ + } + CALC_LEVELS( ch->vol, ch_sum, ch_diff, ch_surround ); + CALC_LEVELS( this->bufs [h].vol, buf_sum, buf_diff, buf_surround ); + + fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff ); + + if ( ch_surround != buf_surround ) + dist += TO_FIXED( 1 ) / 2; + + if ( this->s.feedback && ch->cfg.echo != this->bufs [h].echo ) + dist += TO_FIXED( 1 ) / 2; + + if ( best_dist > dist ) + { + best_dist = dist; + b = h; + } + } + } + } + + ch->channel.center = &this->bufs [b].tracked.blip; + } +} + +void clear_echo( struct Multi_Buffer* this ) +{ + if ( this->echo_size_ ) + memset( this->echo, 0, this->echo_size_ * sizeof this->echo [0] ); +} + +void apply_effects( struct Multi_Buffer* this ) +{ + int i; + + if ( !this->bufs_size ) + return; + + this->s.treble = this->config_.treble; + + bool echo_dirty = false; + + fixed_t old_feedback = this->s.feedback; + this->s.feedback = this->config_.feedback; + if ( !old_feedback && this->s.feedback ) + echo_dirty = true; + + // delays + for ( i = stereo; --i >= 0; ) + { + int delay = this->config_.delay [i] * this->sample_rate_ / 1000 * stereo; + delay = max( delay, (int) (max_read * stereo) ); + delay = min( delay, (int) (this->echo_size - max_read * stereo) ); + if ( this->s.delay [i] != delay ) + { + this->s.delay [i] = delay; + echo_dirty = true; + } + } + + // side channels + for ( i = 2; --i >= 0; ) + { + this->chans [i+2].cfg.vol = this->chans [i].cfg.vol = this->config_.side_chans [i].vol / 2; + this->chans [i+2].cfg.pan = this->chans [i].cfg.pan = this->config_.side_chans [i].pan; + } + + // convert volumes + for ( i = this->chans_size_; --i >= 0; ) + { + struct chan_t* ch = &this->chans [i]; + ch->vol [0] = ch->cfg.vol - (ch->cfg.vol * ch->cfg.pan) / FP_ONE_EFFECT; + ch->vol [1] = ch->cfg.vol + (ch->cfg.vol * ch->cfg.pan) / FP_ONE_EFFECT; + if ( ch->cfg.surround ) + ch->vol [0] = -ch->vol [0]; + } + + assign_buffers( this ); + + // set side channels + for ( i = this->chans_size_; --i >= 0; ) + { + struct chan_t* ch = &this->chans [i]; + ch->channel.left = this->chans [ch->cfg.echo*2 ].channel.center; + ch->channel.right = this->chans [ch->cfg.echo*2+1].channel.center; + } + + bool old_echo = !this->no_echo && !this->no_effects; + + // determine whether effects and echo are needed at all + this->no_effects = true; + this->no_echo = true; + for ( i = this->chans_size_; --i >= extra_chans; ) + { + struct chan_t* ch = &this->chans [i]; + if ( ch->cfg.echo && this->s.feedback ) + this->no_echo = false; + + if ( ch->vol [0] != TO_FIXED( 1 ) || ch->vol [1] != TO_FIXED( 1 ) ) + this->no_effects = false; + } + if ( !this->no_echo ) + this->no_effects = false; + + if ( this->chans [0].vol [0] != TO_FIXED( 1 ) || + this->chans [0].vol [1] != TO_FIXED( 0 ) || + this->chans [1].vol [0] != TO_FIXED( 0 ) || + this->chans [1].vol [1] != TO_FIXED( 1 ) ) + this->no_effects = false; + + if ( !this->config_.enabled ) + this->no_effects = true; + + if ( this->no_effects ) + { + for ( i = this->chans_size_; --i >= 0; ) + { + struct chan_t* ch = &this->chans [i]; + ch->channel.center = &this->bufs [2].tracked.blip; + ch->channel.left = &this->bufs [0].tracked.blip; + ch->channel.right = &this->bufs [1].tracked.blip; + } + } + + this->mixer.bufs [0] = &this->bufs [0].tracked; + this->mixer.bufs [1] = &this->bufs [1].tracked; + this->mixer.bufs [2] = &this->bufs [2].tracked; + + if ( echo_dirty || (!old_echo && (!this->no_echo && !this->no_effects)) ) + clear_echo( this ); + + this->channels_changed_count_++; +} +#endif + blargg_err_t Buffer_set_channel_count( struct Multi_Buffer* this, int n, int const* types ) { this->channel_count_ = n; this->channel_types_ = types; + +#ifndef GME_DISABLE_STEREO_DEPTH + this->mixer.samples_read = 0; + + this->chans_size_ = n + extra_chans; + if ( this->chans_size_ > max_chans ) { + // warning("Chans buffer overflow"); + this->chans_size_ = max_chans; + } + + this->bufs_size = min( this->bufs_max, n + extra_chans ); + + // TODO: clear buffers array here? + memset( this->bufs, 0, sizeof this->bufs ); + + int i; + for ( i = this->bufs_size; --i >= 0; ) + RETURN_ERR( Blip_set_sample_rate( &this->bufs [i].tracked.blip, this->sample_rate_, this->length_ ) ); + + for ( i = this->chans_size_; --i >= 0; ) + { + struct chan_t* ch = &this->chans [i]; + ch->cfg.vol = (int)(1.0 * FP_ONE_EFFECT); + ch->cfg.pan = (int)(0.0 * FP_ONE_EFFECT); + ch->cfg.surround = false; + ch->cfg.echo = false; + } + // side channels with echo + this->chans [2].cfg.echo = true; + this->chans [3].cfg.echo = true; + + Buffer_clock_rate( this, this->clock_rate_ ); + Buffer_bass_freq( this, this->bass_freq_ ); + apply_effects( this ); + Buffer_clear( this ); +#endif return 0; } struct channel_t Buffer_channel( struct Multi_Buffer* this, int i ) { +#ifndef GME_DISABLE_STEREO_DEPTH + i += extra_chans; + require( extra_chans <= i && i < (int) this->chans_size_ ); + return this->chans [i].channel; +#else (void) i; return this->chan; +#endif } void Buffer_clear( struct Multi_Buffer* this ) { int i; +#ifndef GME_DISABLE_STEREO_DEPTH + this->echo_pos = 0; + this->s.low_pass [0] = 0; + this->s.low_pass [1] = 0; this->mixer.samples_read = 0; + + for ( i = this->bufs_size; --i >= 0; ) + Tracked_clear( &this->bufs [i].tracked ); + clear_echo( this ); +#else + this->mixer.samples_read = 0; for ( i = bufs_size; --i >= 0; ) Tracked_clear( &this->bufs [i] ); +#endif } +#ifndef GME_DISABLE_STEREO_DEPTH +struct chan_config_t *chan_config( struct Multi_Buffer* this, int i ) +{ + return &this->chans [i + extra_chans].cfg; +} + +void Buffer_apply_config( struct Multi_Buffer* this ) +{ + struct config_t* e = &this->config_; + + e->enabled = this->effects_.enabled; + if ( e->enabled ) + { + e->delay [0] = 120; + e->delay [1] = 122; + e->feedback = (this->effects_.echo * 7) / 10; + e->treble = (int)(0.6 * FP_ONE_EFFECT) - (this->effects_.echo * 3) / 10; + + int sep = this->effects_.stereo + (int)(0.80 * FP_ONE_EFFECT); + if ( sep > (int)(1.0 * FP_ONE_EFFECT) ) + sep = (int)(1.0 * FP_ONE_EFFECT); + + e->side_chans [0].pan = -sep; + e->side_chans [1].pan = +sep; + + int i; + for ( i = this->channel_count_; --i >= 0; ) + { + struct chan_config_t* ch = chan_config( this, i ); + + ch->pan = 0; + ch->surround = this->effects_.surround; + ch->echo = false; + + int const type = (this->channel_types_ ? this->channel_types_ [i] : 0); + if ( !(type & noise_type) ) + { + int index = (type & type_index_mask) % 6 - 3; + if ( index < 0 ) + { + index += 3; + ch->surround = false; + ch->echo = true; + } + if ( index >= 1 ) + { + ch->pan = this->effects_.stereo; + if ( index == 1 ) + ch->pan = -ch->pan; + } + } + else if ( type & 1 ) + { + ch->surround = false; + } + } + } + + apply_effects( this ); +} +#endif + void Buffer_end_frame( struct Multi_Buffer* this, blip_time_t clock_count ) { int i; +#ifndef GME_DISABLE_STEREO_DEPTH + for ( i = this->bufs_size; --i >= 0; ) + Tracked_end_frame( &this->bufs [i].tracked, clock_count ); +#else for ( i = bufs_size; --i >= 0; ) Tracked_end_frame( &this->bufs [i], clock_count ); +#endif } +#ifndef GME_DISABLE_STEREO_DEPTH +void mix_effects( struct Multi_Buffer* this, blip_sample_t out_ [], int pair_count ) +{ + typedef fixed_t stereo_fixed_t [stereo]; + + // add channels with echo, do echo, add channels without echo, then convert to 16-bit and output + int echo_phase = 1; + do + { + // mix any modified buffers + { + struct buf_t* buf = this->bufs; + int bufs_remain = this->bufs_size; + do + { + if ( Tracked_non_silent( &buf->tracked ) && buf->echo == echo_phase ) + { + struct Blip_Buffer* const blip = &buf->tracked.blip; + stereo_fixed_t* BLARGG_RESTRICT out = (stereo_fixed_t*) &this->echo [this->echo_pos]; + int const bass = BLIP_READER_BASS( *blip ); + BLIP_READER_BEGIN( in, *blip ); + BLIP_READER_ADJ_( in, this->mixer.samples_read ); + fixed_t const vol_0 = buf->vol [0]; + fixed_t const vol_1 = buf->vol [1]; + + int count = (unsigned) (this->echo_size - this->echo_pos) / stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + BLIP_READER_ADJ_( in, count ); + + out += count; + int offset = -count; + do + { + fixed_t s = BLIP_READER_READ( in ); + BLIP_READER_NEXT_IDX_( in, bass, offset ); + + out [offset] [0] += s * vol_0; + out [offset] [1] += s * vol_1; + } + while ( ++offset ); + + out = (stereo_fixed_t*) this->echo; + count = remain; + } + while ( remain ); + + BLIP_READER_END( in, *blip ); + } + buf++; + } + while ( --bufs_remain ); + } + + // add echo + if ( echo_phase && !this->no_echo ) + { + fixed_t const feedback = this->s.feedback; + fixed_t const treble = this->s.treble; + + int i = 1; + do + { + fixed_t low_pass = this->s.low_pass [i]; + + fixed_t* echo_end = &this->echo [this->echo_size + i]; + fixed_t const* BLARGG_RESTRICT in_pos = &this->echo [this->echo_pos + i]; + int out_offset = this->echo_pos + i + this->s.delay [i]; + if ( out_offset >= this->echo_size ) + out_offset -= this->echo_size; + assert( out_offset < this->echo_size ); + fixed_t* BLARGG_RESTRICT out_pos = &this->echo [out_offset]; + + // break into up to three chunks to avoid having to handle wrap-around + // in middle of core loop + int remain = pair_count; + do + { + fixed_t const* pos = in_pos; + if ( pos < out_pos ) + pos = out_pos; + int count = (unsigned) ((char*) echo_end - (char const*) pos) / + (unsigned) (stereo * sizeof (fixed_t)); + if ( count > remain ) + count = remain; + remain -= count; + + in_pos += count * stereo; + out_pos += count * stereo; + int offset = -count; + do + { + low_pass += FROM_FIXED( (in_pos [offset * stereo] - low_pass) ) * treble; + out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback; + } + while ( ++offset ); + + if ( in_pos >= echo_end ) in_pos -= this->echo_size; + if ( out_pos >= echo_end ) out_pos -= this->echo_size; + } + while ( remain ); + + this->s.low_pass [i] = low_pass; + } + while ( --i >= 0 ); + } + } + while ( --echo_phase >= 0 ); + + // clamp to 16 bits + { + stereo_fixed_t const* BLARGG_RESTRICT in = (stereo_fixed_t const*) &this->echo [this->echo_pos]; + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_; + int count = (unsigned) (this->echo_size - this->echo_pos) / (unsigned) stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + in += count; + out += count; + int offset = -count; + do + { + fixed_t in_0 = FROM_FIXED( in [offset] [0] ); + fixed_t in_1 = FROM_FIXED( in [offset] [1] ); + + BLIP_CLAMP( in_0, in_0 ); + out [offset] [0] = (blip_sample_t) in_0; + + BLIP_CLAMP( in_1, in_1 ); + out [offset] [1] = (blip_sample_t) in_1; + } + while ( ++offset ); + + in = (stereo_fixed_t const*) this->echo; + count = remain; + } + while ( remain ); + } +} +#endif + int Buffer_read_samples( struct Multi_Buffer* this, blip_sample_t out [], int out_size ) { +#ifndef GME_DISABLE_STEREO_DEPTH + out_size = min( out_size, Buffer_samples_avail( this ) ); + + int pair_count = (int) (out_size >> 1); + require( pair_count * stereo == out_size ); // must read an even number of samples + if ( pair_count ) + { + if ( this->no_effects ) + { + Mixer_read_pairs( &this->mixer, 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 ( this->no_echo ) + { + // optimization: clear echo here to keep mix_effects() a leaf function + this->echo_pos = 0; + memset( this->echo, 0, count * stereo * sizeof this->echo [0] ); + } + mix_effects( this, out, count ); + + int new_echo_pos = this->echo_pos + count * stereo; + if ( new_echo_pos >= this->echo_size ) + new_echo_pos -= this->echo_size; + this->echo_pos = new_echo_pos; + assert( this->echo_pos < this->echo_size ); + + out += count * stereo; + this->mixer.samples_read += count; + pairs_remain -= count; + } + while ( pairs_remain ); + } + + if ( Buffer_samples_avail( this ) <= 0 || this->immediate_removal_ ) + { + int i; + for ( i = this->bufs_size; --i >= 0; ) + { + struct Tracked_Blip_Buffer* b = &this->bufs [i].tracked; + // TODO: might miss non-silence settling since it checks END of last read + if ( Tracked_non_silent( b ) ) + Tracked_remove_samples( b, this->mixer.samples_read ); + else + Tracked_remove_silence( b, this->mixer.samples_read ); + } + this->mixer.samples_read = 0; + } + } +#else require( (out_size & 1) == 0 ); // must read an even number of samples out_size = min( out_size, Buffer_samples_avail( this ) ); @@ -281,6 +879,6 @@ this->mixer.samples_read = 0; } } - +#endif return out_size; } Index: apps/codecs/libgme/gbs_emu.h =================================================================== --- apps/codecs/libgme/gbs_emu.h (Revision 30492) +++ apps/codecs/libgme/gbs_emu.h (Arbeitskopie) @@ -168,6 +168,36 @@ this->gain_ = g; } +static inline void Sound_set_stereo_depth( struct Gbs_Emu* this, int depth ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = (depth > 0); + c->echo = depth; + c->stereo = depth; + c->surround = true; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) depth; +#endif +} + +static inline void Sound_set_effects( struct Gbs_Emu* this, struct gme_effects_t const* in ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = in->enabled; + c->echo = in->echo; + c->stereo = in->stereo; + c->surround = in->surround; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) in; +#endif +} + // Emulation (You shouldn't touch these) blargg_err_t run_clocks( struct Gbs_Emu* this, blip_time_t duration ); Index: apps/codecs/libgme/blargg_config.h =================================================================== --- apps/codecs/libgme/blargg_config.h (Revision 30492) +++ apps/codecs/libgme/blargg_config.h (Arbeitskopie) @@ -14,6 +14,9 @@ // Uncomment if you get errors in the bool section of blargg_common.h #define BLARGG_COMPILER_HAS_BOOL 1 +// Uncomment to disable effects +// #define GME_DISABLE_STEREO_DEPTH 1 + // Uncomment to use fast gb apu implementation // #define GB_APU_FAST 1 Index: apps/codecs/libgme/multi_buffer.h =================================================================== --- apps/codecs/libgme/multi_buffer.h (Revision 30492) +++ apps/codecs/libgme/multi_buffer.h (Arbeitskopie) @@ -14,11 +14,25 @@ struct Blip_Buffer* right; }; +// GME effects configuration +struct gme_effects_t +{ + bool enabled; // false = disable all effects + + int echo; // 0.0 = none, 1.0 = lots, scaled by FP_ONE_EFFECT + int stereo; // 0.0 = channels in center, 1.0 = channels on left/right, scaled by FP_ONE_EFFECT + bool surround; // true = put some channels in back +}; + enum { type_index_mask = 0xFF }; enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; enum { stereo = 2 }; + +#ifdef GME_DISABLE_STEREO_DEPTH enum { bufs_size = 3 }; +#endif + // Tracked_Blip_Buffer struct Tracked_Blip_Buffer { struct Blip_Buffer blip; @@ -48,8 +62,71 @@ void Mixer_init( struct Stereo_Mixer* this ); void Mixer_read_pairs( struct Stereo_Mixer* this, blip_sample_t out [], int count ); +#ifdef GME_DISABLE_STEREO_DEPTH typedef struct Tracked_Blip_Buffer buf_t; +#endif +#ifndef GME_DISABLE_STEREO_DEPTH +typedef int fixed_t; +enum { extra_chans = stereo * stereo }; + +// To reduce memory usage, fewer buffers can be used (with a best-fit +// approach if there are too few), and maximum echo delay can be reduced +enum { max_bufs = 12 }; +enum { max_echo_size = 18 * 1024 + 64}; +enum { max_chans = 32 }; + +struct pan_vol_t +{ + int vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal, scaled by FP_ONE_EFFECT + int pan; // -1.0 = left, 0.0 = center, +1.0 = right, scaled by FP_ONE_EFFECT +}; + +// Global configuration +struct config_t +{ + bool enabled; // false = disable all effects + + // Current sound is echoed at adjustable left/right delay, + // with reduced treble and volume (feedback). + int treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent, scaled by FP_ONE_EFFECT + int delay [2]; // left, right delays (msec) + int feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony, scaled by FP_ONE_EFFECT + struct pan_vol_t side_chans [2]; // left and right side channel volume and pan +}; + +// Per-channel configuration. Two or more channels with matching parameters are +// optimized to internally use the same buffer. +struct chan_config_t +{ + int vol; // these only affect center channel + int pan; + bool surround; // if true, negates left volume to put sound in back + bool echo; // false = channel doesn't have any echo +}; + +struct chan_t +{ + fixed_t vol [stereo]; + struct chan_config_t cfg; + struct channel_t channel; +}; + +struct buf_t +{ + struct Tracked_Blip_Buffer tracked; + fixed_t vol [stereo]; + bool echo; +}; + +struct filter_t { + int delay [stereo]; + fixed_t treble; + fixed_t feedback; + fixed_t low_pass [stereo]; +}; +#endif + // Multi_Buffer struct Multi_Buffer { unsigned channels_changed_count_; @@ -60,17 +137,44 @@ int const *channel_types_; bool immediate_removal_; +#ifndef GME_DISABLE_STEREO_DEPTH + int clock_rate_; + int bass_freq_; + + int echo_size; + int bufs_size; + int bufs_max; + + int echo_size_; + int chans_size_; + + int echo_pos; + bool no_effects; + bool no_echo; + + struct config_t config_; + struct gme_effects_t effects_; + + struct filter_t s; + struct Stereo_Mixer mixer; + struct chan_t chans [max_chans]; + struct buf_t bufs [max_bufs]; + fixed_t echo [max_echo_size + stereo]; +#else buf_t bufs [bufs_size]; struct Stereo_Mixer mixer; struct channel_t chan; +#endif }; blargg_err_t Buffer_set_channel_count( struct Multi_Buffer* this, int n, int const* types ); // Buffers used for all channels +#ifdef GME_DISABLE_STEREO_DEPTH static inline struct Blip_Buffer* center( struct Multi_Buffer* this ) { return &this->bufs [2].blip; } static inline struct Blip_Buffer* left( struct Multi_Buffer* this ) { return &this->bufs [0].blip; } static inline struct Blip_Buffer* right( struct Multi_Buffer* this ) { return &this->bufs [1].blip; } +#endif // Initializes Multi_Buffer structure void Buffer_init( struct Multi_Buffer* this ); @@ -105,10 +209,23 @@ static inline int Buffer_samples_avail( struct Multi_Buffer* this ) { +#ifndef GME_DISABLE_STEREO_DEPTH + return (Blip_samples_avail(&this->bufs [0].tracked.blip) - this->mixer.samples_read) * 2; +#else return (Blip_samples_avail(&this->bufs [0].blip) - this->mixer.samples_read) * 2; +#endif } int Buffer_read_samples( struct Multi_Buffer* this, blip_sample_t*, int ) ICODE_ATTR; struct channel_t Buffer_channel( struct Multi_Buffer* this, int i ); +#ifndef GME_DISABLE_STEREO_DEPTH +static inline struct gme_effects_t *Buffer_config( struct Multi_Buffer* this ) +{ + return &this->effects_; +} + +void Buffer_apply_config( struct Multi_Buffer* this ); #endif + +#endif Index: apps/codecs/libgme/hes_emu.h =================================================================== --- apps/codecs/libgme/hes_emu.h (Revision 30492) +++ apps/codecs/libgme/hes_emu.h (Arbeitskopie) @@ -179,6 +179,36 @@ this->gain_ = g; } +static inline void Sound_set_stereo_depth( struct Hes_Emu* this, int depth ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = (depth > 0); + c->echo = depth; + c->stereo = depth; + c->surround = true; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) depth; +#endif +} + +static inline void Sound_set_effects( struct Hes_Emu* this, struct gme_effects_t const* in ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = in->enabled; + c->echo = in->echo; + c->stereo = in->stereo; + c->surround = in->surround; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) in; +#endif +} + // Emulation (You shouldn't touch these) void irq_changed( struct Hes_Emu* this ); Index: apps/codecs/libgme/nsf_emu.h =================================================================== --- apps/codecs/libgme/nsf_emu.h (Revision 30492) +++ apps/codecs/libgme/nsf_emu.h (Arbeitskopie) @@ -205,6 +205,36 @@ this->gain = g; } +static inline void Sound_set_stereo_depth( struct Nsf_Emu* this, int depth ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = (depth > 0); + c->echo = depth; + c->stereo = depth; + c->surround = true; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) depth; +#endif +} + +static inline void Sound_set_effects( struct Nsf_Emu* this, struct gme_effects_t const* in ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = in->enabled; + c->echo = in->echo; + c->stereo = in->stereo; + c->surround = in->surround; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) in; +#endif +} + // Emulation (You shouldn't touch these) blargg_err_t run_clocks( struct Nsf_Emu* this, blip_time_t* duration, int ); Index: apps/codecs/libgme/ay_emu.h =================================================================== --- apps/codecs/libgme/ay_emu.h (Revision 30492) +++ apps/codecs/libgme/ay_emu.h (Arbeitskopie) @@ -155,6 +155,36 @@ this->gain = g; } +static inline void Sound_set_stereo_depth( struct Ay_Emu* this, int depth ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = (depth > 0); + c->echo = depth; + c->stereo = depth; + c->surround = true; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) depth; +#endif +} + +static inline void Sound_set_effects( struct Ay_Emu* this, struct gme_effects_t const* in ) +{ +#ifndef GME_DISABLE_STEREO_DEPTH + struct gme_effects_t* c = Buffer_config( &this->stereo_buf ); + c->enabled = in->enabled; + c->echo = in->echo; + c->stereo = in->stereo; + c->surround = in->surround; + Buffer_apply_config( &this->stereo_buf ); +#else + (void) this; + (void) in; +#endif +} + // Emulation (You shouldn't touch these) void cpu_out( struct Ay_Emu* this, cpu_time_t, addr_t, int data ); void cpu_out_( struct Ay_Emu* this, cpu_time_t, addr_t, int data ); Index: apps/codecs/hes.c =================================================================== --- apps/codecs/hes.c (Revision 30492) +++ apps/codecs/hes.c (Arbeitskopie) @@ -76,6 +76,9 @@ if (hes_emu.m3u.size > 0) hes_emu.track_count = hes_emu.m3u.size; + /* Add some stereo enhancement */ + Sound_set_stereo_depth(&hes_emu, (int)(0.40 * FP_ONE_EFFECT)); + next_track: set_codec_track(track); Index: apps/codecs/nsf.c =================================================================== --- apps/codecs/nsf.c (Revision 30493) +++ apps/codecs/nsf.c (Arbeitskopie) @@ -85,6 +85,19 @@ if (nsf_emu.track_count > 1) is_multitrack = 1; + /* Add some stereo enhancement */ + Sound_set_stereo_depth(&nsf_emu, (int)(0.40 * FP_ONE_EFFECT)); + + /* or set each effect individually ... */ + /* + struct gme_effects_t ef; + ef.enabled = 1; // Must be 1 to have effects applied + ef.echo = 0.4; // 0.0 gives no echo, 1.0 maximum + ef.stereo = 0.3; // 1.0 spreads channels, 0.0 centers them + ef.surround = 1; // If 1, puts some channels in "back" + Sound_set_effects(&nsf_emu, &ef); + */ + next_track: set_codec_track(track, is_multitrack); Index: apps/codecs/ay.c =================================================================== --- apps/codecs/ay.c (Revision 30492) +++ apps/codecs/ay.c (Arbeitskopie) @@ -87,6 +87,9 @@ is_multitrack = 1; } + /* Add some stereo enhancement */ + Sound_set_stereo_depth(&ay_emu, (int)(0.40 * FP_ONE_EFFECT)); + next_track: set_codec_track(track, is_multitrack); Index: apps/codecs/sgc.c =================================================================== --- apps/codecs/sgc.c (Revision 30492) +++ apps/codecs/sgc.c (Arbeitskopie) @@ -91,6 +91,9 @@ if (sgc_emu.m3u.size > 0) sgc_emu.track_count = sgc_emu.m3u.size; + /* Add some stereo enhancement */ + Sound_set_stereo_depth(&sgc_emu, (int)(0.40 * FP_ONE_EFFECT)); + next_track: set_codec_track(track); Index: apps/codecs/vgm.c =================================================================== --- apps/codecs/vgm.c (Revision 30493) +++ apps/codecs/vgm.c (Arbeitskopie) @@ -18,9 +18,9 @@ static int16_t samples[CHUNK_SIZE] IBSS_ATTR; static struct Vgm_Emu vgm_emu; -static void *inflatebuf; /* heap for gunzip */ -static char *songbuf; /* destination for uncompressed song */ -static uint32_t songbuflen=0; /* size of the song buffer */ +static void *inflatebuf; /* heap for gunzip */ +static char *songbuf; /* destination for uncompressed song */ +static uint32_t songbuflen=0; /* size of the song buffer */ static uint32_t songlen=0; /* used size of the song buffer */ /****************** rockbox interface ******************/ @@ -68,19 +68,19 @@ DEBUGF("VGM: file load failed\n"); return CODEC_ERROR; } - + /* If couldn't get the whole buffer will trim file and put and 'end_command' at the end*/ if (n < (size_t)ci->filesize) { DEBUGF("VGM: file was trimmed\n"); } - + /* If is gzipped decompress it */ if ( get_le16( buf ) == 0x8b1f ) { wpw_init_mempool(MAINMEMBUF); inflatebuf=wpw_malloc(MAINMEMBUF,0x13500); - + /* Will use available remaining memory as output buffer */ songbuflen=wpw_available(MAINMEMBUF); @@ -98,10 +98,13 @@ ci->id3->length = Track_get_length( &vgm_emu ); } else if ((err = Vgm_load_mem(&vgm_emu, buf, n, false))) { - DEBUGF("VGM: Vgm_load failed_mem (%s)\n", err); + DEBUGF("VGM: Vgm_load_mem failed (%s)\n", err); return CODEC_ERROR; } + /* Add some stereo enhancement */ + Sound_set_stereo_depth(&vgm_emu, (int)(0.40 * FP_ONE_EFFECT)); + Vgm_start_track(&vgm_emu); /* for REPEAT_ONE we disable track limits */