diff options
| author | Michael Sevakis <jethead71@rockbox.org> | 2007-11-18 19:03:45 +0000 |
|---|---|---|
| committer | Michael Sevakis <jethead71@rockbox.org> | 2007-11-18 19:03:45 +0000 |
| commit | 1a41c8afeefd4884a5369c79808c68b3514eec8b (patch) | |
| tree | 9200eef9f09c86fd19929733ab45f1a3acee6152 /apps/codecs/spc | |
| parent | 984a6b023684de78e41dae320721b4b28f17f108 (diff) | |
| download | rockbox-1a41c8afeefd4884a5369c79808c68b3514eec8b.zip rockbox-1a41c8afeefd4884a5369c79808c68b3514eec8b.tar.gz rockbox-1a41c8afeefd4884a5369c79808c68b3514eec8b.tar.bz2 rockbox-1a41c8afeefd4884a5369c79808c68b3514eec8b.tar.xz | |
SPC Codec: Run SPC emulation on COP and audio sample processing on CPU on dual-core PortalPlayer targets.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15673 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/spc')
| -rw-r--r-- | apps/codecs/spc/spc_codec.h | 100 | ||||
| -rw-r--r-- | apps/codecs/spc/spc_dsp.c | 232 |
2 files changed, 242 insertions, 90 deletions
diff --git a/apps/codecs/spc/spc_codec.h b/apps/codecs/spc/spc_codec.h index f2677df..c785acc 100644 --- a/apps/codecs/spc/spc_codec.h +++ b/apps/codecs/spc/spc_codec.h @@ -32,38 +32,51 @@ /** Basic configuration options **/ -/* TGB is the only target fast enough for gaussian and realtime BRR decode */ -/* echo is almost fast enough but not quite */ -#ifndef TOSHIBA_GIGABEAT_F - /* Cache BRR waves */ - #define SPC_BRRCACHE 1 +#define SPC_DUAL_CORE 1 - /* Disable gaussian interpolation */ - #define SPC_NOINTERP 1 - -#ifndef CPU_COLDFIRE - /* Disable echo processing */ - #define SPC_NOECHO 1 -#else - /* Enable echo processing */ - #define SPC_NOECHO 0 +#if !defined(SPC_DUAL_CORE) || NUM_CORES == 1 +#undef SPC_DUAL_CORE +#define SPC_DUAL_CORE 0 #endif -#else + +/* TGB is the only target fast enough for gaussian and realtime BRR decode */ +/* echo is almost fast enough but not quite */ +#if defined(TOSHIBA_GIGABEAT_F) || defined(SIMULATOR) /* Don't cache BRR waves */ #define SPC_BRRCACHE 0 /* Allow gaussian interpolation */ #define SPC_NOINTERP 0 + + /* Allow echo processing */ + #define SPC_NOECHO 0 +#elif defined(CPU_COLDFIRE) + /* Cache BRR waves */ + #define SPC_BRRCACHE 1 + /* Disable gaussian interpolation */ + #define SPC_NOINTERP 1 + /* Allow echo processing */ #define SPC_NOECHO 0 -#endif +#elif defined (CPU_PP) && SPC_DUAL_CORE + /* Cache BRR waves */ + #define SPC_BRRCACHE 1 + + /* Disable gaussian interpolation */ + #define SPC_NOINTERP 1 -/* Samples per channel per iteration */ -#ifdef CPU_COLDFIRE -#define WAV_CHUNK_SIZE 1024 + /* Allow echo processing */ + #define SPC_NOECHO 0 #else -#define WAV_CHUNK_SIZE 2048 + /* Cache BRR waves */ + #define SPC_BRRCACHE 1 + + /* Disable gaussian interpolation */ + #define SPC_NOINTERP 1 + + /* Disable echo processing */ + #define SPC_NOECHO 1 #endif #ifdef CPU_ARM @@ -72,6 +85,26 @@ #undef IDATA_ATTR #define IDATA_ATTR + + #undef ICONST_ATTR + #define ICONST_ATTR + + #undef IBSS_ATTR + #define IBSS_ATTR + +#if SPC_DUAL_CORE + #undef NOCACHEBSS_ATTR + #define NOCACHEBSS_ATTR __attribute__ ((section(".ibss"))) + #undef NOCACHEDATA_ATTR + #define NOCACHEDATA_ATTR __attribute__((section(".idata"))) +#endif +#endif + +/* Samples per channel per iteration */ +#if defined(CPU_PP) && NUM_CORES == 1 +#define WAV_CHUNK_SIZE 2048 +#else +#define WAV_CHUNK_SIZE 1024 #endif /**************** Little-endian handling ****************/ @@ -231,16 +264,26 @@ extern int16_t BRRcache [BRR_CACHE_SIZE]; enum { FIR_BUF_HALF = 8 }; -#ifdef CPU_COLDFIRE +#if defined(CPU_COLDFIRE) /* global because of the large aligment requirement for hardware masking - * L-R interleaved 16-bit samples for easy loading and mac.w use. */ enum { - FIR_BUF_SIZE = FIR_BUF_HALF * sizeof ( int32_t ), - FIR_BUF_MASK = ~FIR_BUF_SIZE + FIR_BUF_CNT = FIR_BUF_HALF, + FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ), + FIR_BUF_ALIGN = FIR_BUF_SIZE * 2, + FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) - 1)) +}; +#elif defined (CPU_ARM) +enum +{ + FIR_BUF_CNT = FIR_BUF_HALF * 2 * 2, + FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ), + FIR_BUF_ALIGN = FIR_BUF_SIZE, + FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) * 2 - 1)) }; -#endif /* CPU_COLDFIRE */ +#endif /* CPU_* */ struct Spc_Dsp { @@ -257,14 +300,19 @@ struct Spc_Dsp int noise_count; uint16_t noise; /* also read as int16_t */ -#ifdef CPU_COLDFIRE +#if defined(CPU_COLDFIRE) /* circularly hardware masked address */ int32_t *fir_ptr; /* wrapped address just behind current position - allows mac.w to increment and mask fir_ptr */ int32_t *last_fir_ptr; /* copy of echo FIR constants as int16_t for use with mac.w */ - int16_t fir_coeff[VOICE_COUNT]; + int16_t fir_coeff [VOICE_COUNT]; +#elif defined (CPU_ARM) + /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */ + int32_t *fir_ptr; + /* copy of echo FIR constants as int32_t, for faster access */ + int32_t fir_coeff [VOICE_COUNT]; #else /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */ int fir_pos; /* (0 to 7) */ diff --git a/apps/codecs/spc/spc_dsp.c b/apps/codecs/spc/spc_dsp.c index 8881788..19986fd 100644 --- a/apps/codecs/spc/spc_dsp.c +++ b/apps/codecs/spc/spc_dsp.c @@ -25,14 +25,13 @@ #include "spc_codec.h" #include "spc_profiler.h" -#ifdef CPU_COLDFIRE -static int32_t fir_buf[FIR_BUF_HALF] - __attribute__ ((aligned (FIR_BUF_SIZE*2))) IBSS_ATTR; +#if defined(CPU_COLDFIRE) || defined (CPU_ARM) +int32_t fir_buf[FIR_BUF_CNT] + __attribute__ ((aligned (FIR_BUF_ALIGN*1))) IBSS_ATTR; #endif - #if SPC_BRRCACHE /* a little extra for samples that go past end */ -int16_t BRRcache [0x20000 + 32]; +int16_t BRRcache [BRR_CACHE_SIZE]; #endif void DSP_write( struct Spc_Dsp* this, int i, int data ) @@ -58,11 +57,12 @@ void DSP_write( struct Spc_Dsp* this, int i, int data ) /* if ( n < -32768 ) out = -32768; */ /* if ( n > 32767 ) out = 32767; */ -#define CLAMP16( n, out )\ -{\ - if ( (int16_t) n != n )\ - out = 0x7FFF ^ (n >> 31);\ -} +#define CLAMP16( n ) \ +({ \ + if ( (int16_t) n != n ) \ + n = 0x7FFF ^ (n >> 31); \ + n; \ +}) #if SPC_BRRCACHE static void decode_brr( struct Spc_Dsp* this, unsigned start_addr, @@ -181,7 +181,7 @@ static void decode_brr( struct Spc_Dsp* this, unsigned start_addr, smp2 = smp1; } - CLAMP16( delta, delta ); + delta = CLAMP16( delta ); smp1 = (int16_t) (delta * 2); /* sign-extend */ } while ( (offset += 4) != 0 ); @@ -359,7 +359,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) #define VOICE_RATE(x) (INT16A(raw_voice->rate) & 0x3FFF) #define IF_RBE(...) #endif /* ROCKBOX_BIG_ENDIAN */ - + #if !SPC_NOINTERP int const slow_gaussian = (this->r.g.pitch_mods >> 1) | this->r.g.noise_enables; @@ -431,7 +431,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) /* Envelope */ { - int const env_range = 0x800; + int const ENV_RANGE = 0x800; int env_mode = voice->env_mode; int adsr0 = raw_voice->adsr [0]; int env_timer; @@ -482,14 +482,14 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) int envx = voice->envx; - int const step = env_range / 64; + int const step = ENV_RANGE / 64; envx += step; if ( t == 15 ) - envx += env_range / 2 - step; + envx += ENV_RANGE / 2 - step; - if ( envx >= env_range ) + if ( envx >= ENV_RANGE ) { - envx = env_range - 1; + envx = ENV_RANGE - 1; voice->env_mode = state_decay; } voice->envx = envx; @@ -516,7 +516,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) int mode = t >> 5; if ( mode <= 5 ) /* decay */ { - int step = env_range / 64; + int step = ENV_RANGE / 64; if ( mode == 5 ) /* exponential */ { envx--; /* envx *= 255 / 256 */ @@ -531,14 +531,14 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) } else /* attack */ { - int const step = env_range / 64; + int const step = ENV_RANGE / 64; envx += step; if ( mode == 7 && - envx >= env_range * 3 / 4 + step ) - envx += env_range / 256 - step; + envx >= ENV_RANGE * 3 / 4 + step ) + envx += ENV_RANGE / 256 - step; - if ( envx >= env_range ) - envx = env_range - 1; + if ( envx >= ENV_RANGE ) + envx = ENV_RANGE - 1; } voice->envx = envx; /* TODO: should this be 8? */ @@ -550,7 +550,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) else /* state_release */ { int envx = voice->envx; - if ( (envx -= env_range / 256) > 0 ) + if ( (envx -= ENV_RANGE / 256) > 0 ) { voice->envx = envx; raw_voice->envx = envx >> 8; @@ -683,7 +683,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) smp2 = smp1; } - CLAMP16( delta, delta ); + delta = CLAMP16( delta ); smp1 = (int16_t) (delta * 2); /* sign-extend */ } while ( (offset += 4) != 0 ); @@ -778,7 +778,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) output = (output + rev [1] * interp [2]) >> 12; output = (int16_t) (output * 2); output += ((rev [0] * interp [3]) >> 12) * 2; - CLAMP16( output, output ); + output = CLAMP16( output ); } output = (output * voice->envx) >> 11 & ~1; @@ -788,7 +788,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) prev_outx = output; raw_voice->outx = (int8_t) (output >> 8); } - #else + #else /* SPCNOINTERP */ /* two-point linear interpolation */ #ifdef CPU_COLDFIRE int amp_0 = (int16_t)this->noise; @@ -822,7 +822,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) /* output = y0 + (result >> 12) */ "asr.l %[sh], %[y1] \r\n" "add.l %[y0], %[y1] \r\n" - : [f]"+&d"(f), [y0]"=&a"(y0), [y1]"=&d"(amp_0) + : [f]"+d"(f), [y0]"=&a"(y0), [y1]"=&d"(amp_0) : [s]"a"(voice->samples), [sh]"d"(12) ); } @@ -861,17 +861,49 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) "movclr.l %%acc1, %[amp_1] \r\n" : [amp_0]"=r"(amp_0), [amp_1]"=r"(amp_1) ); - #else + #elif defined (CPU_ARM) + int amp_0, amp_1; + + if ( (this->r.g.noise_enables & vbit) != 0 ) { + amp_0 = *(int16_t *)&this->noise; + } else { + uint32_t f = voice->position; + amp_0 = (uint32_t)voice->samples; - /* Try this one out on ARM and see - similar to above but the asm - on coldfire removes a redundant register load worth 1 or 2%; - switching to loading two samples at once may help too. That's - done above and while 6 to 7% faster on cf over two 16 bit loads - it makes it endian dependant. - - measured small improvement (~1.5%) - hcs - */ + asm volatile( + "mov %[y1], %[f], lsr #12 \r\n" + "eor %[f], %[f], %[y1], lsl #12 \r\n" + "add %[y1], %[y0], %[y1], lsl #1 \r\n" + "ldrsh %[y0], [%[y1], #2] \r\n" + "ldrsh %[y1], [%[y1], #4] \r\n" + "sub %[y1], %[y1], %[y0] \r\n" + "mul %[f], %[y1], %[f] \r\n" + "add %[y0], %[y0], %[f], asr #12 \r\n" + : [f]"+r"(f), [y0]"+r"(amp_0), [y1]"=&r"(amp_1) + ); + } + + voice->position += rate; + + asm volatile( + "mul %[amp_1], %[amp_0], %[envx] \r\n" + "mov %[amp_0], %[amp_1], asr #11 \r\n" + "mov %[amp_1], %[amp_0], asr #8 \r\n" + : [amp_0]"+r"(amp_0), [amp_1]"=&r"(amp_1) + : [envx]"r"(voice->envx) + ); + + prev_outx = amp_0; + raw_voice->outx = (int8_t)amp_1; + asm volatile( + "mul %[amp_1], %[amp_0], %[vol_1] \r\n" + "mul %[amp_0], %[vol_0], %[amp_0] \r\n" + : [amp_0]"+r"(amp_0), [amp_1]"+r"(amp_1) + : [vol_0]"r"((int)voice->volume[0]), + [vol_1]"r"((int)voice->volume[1]) + ); + #else /* Unoptimized CPU */ int output; if ( (this->r.g.noise_enables & vbit) == 0 ) @@ -884,19 +916,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) } voice->position += rate; - - /* old version */ -#if 0 - int fraction = voice->position & 0xFFF; - short const* const pos = voice->samples + (voice->position >> 12); - voice->position += rate; - int output = - (pos [2] * fraction + pos [1] * (0x1000 - fraction)) >> 12; - /* no interpolation (hardly faster, and crappy sounding) */ - /*int output = pos [0];*/ - if ( this->r.g.noise_enables & vbit ) - output = *(int16_t*) &this->noise; -#endif + output = (output * voice->envx) >> 11; /* duplicated here to give compiler more to run in parallel */ @@ -905,8 +925,8 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) prev_outx = output; raw_voice->outx = (int8_t) (output >> 8); - #endif /* CPU_COLDFIRE */ - #endif + #endif /* CPU_* */ + #endif /* SPCNOINTERP */ #if SPC_BRRCACHE if ( voice->position >= voice->wave_end ) @@ -1033,7 +1053,7 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) "or.l %[sh], %[e0] \r\n" /* save final feedback into echo buffer */ "move.l %[e0], (%[echo_ptr]) \r\n" - : [e0]"+&d"(echo_0), [e1]"+&d"(echo_1) + : [e0]"+d"(echo_0), [e1]"+d"(echo_1) : [out_0]"r"(out_0), [out_1]"r"(out_1), [ef]"r"((int)this->r.g.echo_feedback), [echo_ptr]"a"((int32_t *)echo_ptr), @@ -1056,7 +1076,88 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) out_buf [ 0] = out_0; out_buf [WAV_CHUNK_SIZE] = out_1; out_buf ++; - #else /* !CPU_COLDFIRE */ + #elif defined (CPU_ARM) + /* Read feedback from echo buffer */ + int echo_pos = this->echo_pos; + uint8_t* const echo_ptr = RAM + + ((this->r.g.echo_page * 0x100 + echo_pos) & 0xFFFF); + echo_pos += 4; + if ( echo_pos >= (this->r.g.echo_delay & 15) * 0x800 ) + echo_pos = 0; + this->echo_pos = echo_pos; + + int fb_0 = GET_LE16SA( echo_ptr ); + int fb_1 = GET_LE16SA( echo_ptr + 2 ); + + /* Keep last 8 samples */ + int32_t *fir_ptr = this->fir_ptr; + + /* Apply FIR */ + asm volatile ( + "str %[fb_0], [%[fir_p]], #4 \r\n" + "str %[fb_1], [%[fir_p]], #4 \r\n" + /* duplicate at +8 eliminates wrap checking below */ + "str %[fb_0], [%[fir_p], #56] \r\n" + "str %[fb_1], [%[fir_p], #60] \r\n" + : [fir_p]"+r"(fir_ptr) + : [fb_0]"r"(fb_0), [fb_1]"r"(fb_1) + ); + + this->fir_ptr = (int32_t *)((intptr_t)fir_ptr & FIR_BUF_MASK); + int32_t *fir_coeff = this->fir_coeff; + + asm volatile ( + "ldmia %[fir_c]!, { r0-r1 } \r\n" + "ldmia %[fir_p]!, { r4-r5 } \r\n" + "mul %[fb_0], r0, %[fb_0] \r\n" + "mul %[fb_1], r0, %[fb_1] \r\n" + "mla %[fb_0], r4, r1, %[fb_0] \r\n" + "mla %[fb_1], r5, r1, %[fb_1] \r\n" + "ldmia %[fir_c]!, { r0-r1 } \r\n" + "ldmia %[fir_p]!, { r2-r5 } \r\n" + "mla %[fb_0], r2, r0, %[fb_0] \r\n" + "mla %[fb_1], r3, r0, %[fb_1] \r\n" + "mla %[fb_0], r4, r1, %[fb_0] \r\n" + "mla %[fb_1], r5, r1, %[fb_1] \r\n" + "ldmia %[fir_c]!, { r0-r1 } \r\n" + "ldmia %[fir_p]!, { r2-r5 } \r\n" + "mla %[fb_0], r2, r0, %[fb_0] \r\n" + "mla %[fb_1], r3, r0, %[fb_1] \r\n" + "mla %[fb_0], r4, r1, %[fb_0] \r\n" + "mla %[fb_1], r5, r1, %[fb_1] \r\n" + "ldmia %[fir_c]!, { r0-r1 } \r\n" + "ldmia %[fir_p]!, { r2-r5 } \r\n" + "mla %[fb_0], r2, r0, %[fb_0] \r\n" + "mla %[fb_1], r3, r0, %[fb_1] \r\n" + "mla %[fb_0], r4, r1, %[fb_0] \r\n" + "mla %[fb_1], r5, r1, %[fb_1] \r\n" + : [fb_0]"+r"(fb_0), [fb_1]"+r"(fb_1), + [fir_p]"+r"(fir_ptr), [fir_c]"+r"(fir_coeff) + : + : "r0", "r1", "r2", "r3", "r4", "r5" + ); + + /* Generate output */ + int amp_0 = (chans_0 * global_vol_0 + fb_0 * this->r.g.echo_volume_0) + >> global_muting; + int amp_1 = (chans_1 * global_vol_1 + fb_1 * this->r.g.echo_volume_1) + >> global_muting; + + out_buf [ 0] = amp_0; + out_buf [WAV_CHUNK_SIZE] = amp_1; + out_buf ++; + + if ( !(this->r.g.flags & 0x20) ) + { + /* Feedback into echo buffer */ + int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14); + int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14); + e0 = CLAMP16( e0 ); + SET_LE16A( echo_ptr , e0 ); + e1 = CLAMP16( e1 ); + SET_LE16A( echo_ptr + 2, e1 ); + } + #else /* Unoptimized CPU */ /* Read feedback from echo buffer */ int echo_pos = this->echo_pos; uint8_t* const echo_ptr = RAM + @@ -1102,25 +1203,25 @@ void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) out_buf [WAV_CHUNK_SIZE] = amp_1; out_buf ++; - /* Feedback into echo buffer */ - int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14); - int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14); if ( !(this->r.g.flags & 0x20) ) { - CLAMP16( e0, e0 ); + /* Feedback into echo buffer */ + int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14); + int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14); + e0 = CLAMP16( e0 ); SET_LE16A( echo_ptr , e0 ); - CLAMP16( e1, e1 ); + e1 = CLAMP16( e1 ); SET_LE16A( echo_ptr + 2, e1 ); } - #endif /* CPU_COLDFIRE */ - #else + #endif /* CPU_* */ + #else /* SPCNOECHO == 1*/ /* Generate output */ int amp_0 = (chans_0 * global_vol_0) >> global_muting; int amp_1 = (chans_1 * global_vol_1) >> global_muting; out_buf [ 0] = amp_0; out_buf [WAV_CHUNK_SIZE] = amp_1; out_buf ++; - #endif + #endif /* SPCNOECHO */ } while ( --count ); #if 0 @@ -1155,10 +1256,13 @@ void DSP_reset( struct Spc_Dsp* this ) this->wave_entry [i].start_addr = -1; #endif -#ifdef CPU_COLDFIRE - this->fir_ptr = fir_buf; +#if defined(CPU_COLDFIRE) + this->fir_ptr = fir_buf; this->last_fir_ptr = &fir_buf [7]; ci->memset( fir_buf, 0, sizeof fir_buf ); +#elif defined (CPU_ARM) + this->fir_ptr = fir_buf; + ci->memset( fir_buf, 0, sizeof fir_buf ); #else this->fir_pos = 0; ci->memset( this->fir_buf, 0, sizeof this->fir_buf ); |