From 84501494ceabbb05292f944721389a76cf691483 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Mon, 16 Jul 2007 21:16:52 +0000 Subject: Forgot to add a file as usual. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13920 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/spc/spc_codec.h | 399 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 apps/codecs/spc/spc_codec.h diff --git a/apps/codecs/spc/spc_codec.h b/apps/codecs/spc/spc_codec.h new file mode 100644 index 0000000000..f2677df04a --- /dev/null +++ b/apps/codecs/spc/spc_codec.h @@ -0,0 +1,399 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Adam Gashlin (hcs) + * Copyright (C) 2004-2007 Shay Green (blargg) + * Copyright (C) 2002 Brad Martin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ +/* DSP Based on Brad Martin's OpenSPC DSP emulator */ +/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */ + +#ifndef _SPC_CODEC_H_ +#define _SPC_CODEC_H_ + +/* rather than comment out asserts, just define NDEBUG */ +#define NDEBUG +#include + +/** 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 + + /* 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 +#endif +#else + /* Don't cache BRR waves */ + #define SPC_BRRCACHE 0 + + /* Allow gaussian interpolation */ + #define SPC_NOINTERP 0 + + /* Allow echo processing */ + #define SPC_NOECHO 0 +#endif + +/* Samples per channel per iteration */ +#ifdef CPU_COLDFIRE +#define WAV_CHUNK_SIZE 1024 +#else +#define WAV_CHUNK_SIZE 2048 +#endif + +#ifdef CPU_ARM + #undef ICODE_ATTR + #define ICODE_ATTR + + #undef IDATA_ATTR + #define IDATA_ATTR +#endif + +/**************** Little-endian handling ****************/ + +static inline unsigned get_le16( void const* p ) +{ + return ((unsigned char const*) p) [1] * 0x100u + + ((unsigned char const*) p) [0]; +} + +static inline int get_le16s( void const* p ) +{ + return ((signed char const*) p) [1] * 0x100 + + ((unsigned char const*) p) [0]; +} + +static inline void set_le16( void* p, unsigned n ) +{ + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} + +#define GET_LE16( addr ) get_le16( addr ) +#define SET_LE16( addr, data ) set_le16( addr, data ) +#define INT16A( addr ) (*(uint16_t*) (addr)) +#define INT16SA( addr ) (*(int16_t*) (addr)) + +#ifdef ROCKBOX_LITTLE_ENDIAN + #define GET_LE16A( addr ) (*(uint16_t*) (addr)) + #define GET_LE16SA( addr ) (*( int16_t*) (addr)) + #define SET_LE16A( addr, data ) (void) (*(uint16_t*) (addr) = (data)) +#else + #define GET_LE16A( addr ) get_le16 ( addr ) + #define GET_LE16SA( addr ) get_le16s( addr ) + #define SET_LE16A( addr, data ) set_le16 ( addr, data ) +#endif + +struct Spc_Emu; +#define THIS struct Spc_Emu* const this + +/* The CPU portion (shock!) */ + +struct cpu_regs_t +{ + long pc; /* more than 16 bits to allow overflow detection */ + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; +}; + +struct cpu_ram_t +{ + union { + uint8_t padding1 [0x100]; + uint16_t align; + } padding1 [1]; + uint8_t ram [0x10000]; + uint8_t padding2 [0x100]; +}; + +#undef RAM +#define RAM ram.ram +extern struct cpu_ram_t ram; + +long CPU_run( THIS, long start_time ) ICODE_ATTR; +void CPU_Init( THIS ); + +/* The DSP portion (awe!) */ +enum { VOICE_COUNT = 8 }; +enum { REGISTER_COUNT = 128 }; + +struct raw_voice_t +{ + int8_t volume [2]; + uint8_t rate [2]; + uint8_t waveform; + uint8_t adsr [2]; /* envelope rates for attack, decay, and sustain */ + uint8_t gain; /* envelope gain (if not using ADSR) */ + int8_t envx; /* current envelope level */ + int8_t outx; /* current sample */ + int8_t unused [6]; +}; + +struct globals_t +{ + int8_t unused1 [12]; + int8_t volume_0; /* 0C Main Volume Left (-.7) */ + int8_t echo_feedback; /* 0D Echo Feedback (-.7) */ + int8_t unused2 [14]; + int8_t volume_1; /* 1C Main Volume Right (-.7) */ + int8_t unused3 [15]; + int8_t echo_volume_0; /* 2C Echo Volume Left (-.7) */ + uint8_t pitch_mods; /* 2D Pitch Modulation on/off for each voice */ + int8_t unused4 [14]; + int8_t echo_volume_1; /* 3C Echo Volume Right (-.7) */ + uint8_t noise_enables; /* 3D Noise output on/off for each voice */ + int8_t unused5 [14]; + uint8_t key_ons; /* 4C Key On for each voice */ + uint8_t echo_ons; /* 4D Echo on/off for each voice */ + int8_t unused6 [14]; + uint8_t key_offs; /* 5C key off for each voice + (instantiates release mode) */ + uint8_t wave_page; /* 5D source directory (wave table offsets) */ + int8_t unused7 [14]; + uint8_t flags; /* 6C flags and noise freq */ + uint8_t echo_page; /* 6D */ + int8_t unused8 [14]; + uint8_t wave_ended; /* 7C */ + uint8_t echo_delay; /* 7D ms >> 4 */ + char unused9 [2]; +}; + +enum state_t +{ /* -1, 0, +1 allows more efficient if statements */ + state_decay = -1, + state_sustain = 0, + state_attack = +1, + state_release = 2 +}; + +struct cache_entry_t +{ + int16_t const* samples; + unsigned end; /* past-the-end position */ + unsigned loop; /* number of samples in loop */ + unsigned start_addr; +}; + +enum { BRR_BLOCK_SIZE = 16 }; +enum { BRR_CACHE_SIZE = 0x20000 + 32} ; + +struct voice_t +{ +#if SPC_BRRCACHE + int16_t const* samples; + long wave_end; + int wave_loop; +#else + int16_t samples [3 + BRR_BLOCK_SIZE + 1]; + int block_header; /* header byte from current block */ +#endif + uint8_t const* addr; + short volume [2]; + long position;/* position in samples buffer, with 12-bit fraction */ + short envx; + short env_mode; + short env_timer; + short key_on_delay; +}; + +#if SPC_BRRCACHE +/* a little extra for samples that go past end */ +extern int16_t BRRcache [BRR_CACHE_SIZE]; +#endif + +enum { FIR_BUF_HALF = 8 }; + +#ifdef 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 +}; +#endif /* CPU_COLDFIRE */ + +struct Spc_Dsp +{ + union + { + struct raw_voice_t voice [VOICE_COUNT]; + uint8_t reg [REGISTER_COUNT]; + struct globals_t g; + int16_t align; + } r; + + unsigned echo_pos; + int keys_down; + int noise_count; + uint16_t noise; /* also read as int16_t */ + +#ifdef 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]; +#else + /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */ + int fir_pos; /* (0 to 7) */ + int fir_buf [FIR_BUF_HALF * 2] [2]; + /* copy of echo FIR constants as int, for faster access */ + int fir_coeff [VOICE_COUNT]; +#endif + + struct voice_t voice_state [VOICE_COUNT]; + +#if SPC_BRRCACHE + uint8_t oldsize; + struct cache_entry_t wave_entry [256]; + struct cache_entry_t wave_entry_old [256]; +#endif +}; + +struct src_dir +{ + char start [2]; + char loop [2]; +}; + +void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) ICODE_ATTR; +void DSP_reset( struct Spc_Dsp* this ); + +static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out ) +{ + /* Should we just fill the buffer with silence? Flags won't be cleared */ + /* during this run so it seems it should keep resetting every sample. */ + if ( this->r.g.flags & 0x80 ) + DSP_reset( this ); + + DSP_run_( this, count, out ); +} + +/**************** SPC emulator ****************/ +/* 1.024 MHz clock / 32000 samples per second */ +enum { CLOCKS_PER_SAMPLE = 32 }; + +enum { EXTRA_CLOCKS = CLOCKS_PER_SAMPLE / 2 }; + +/* using this disables timer (since this will always be in the future) */ +enum { TIMER_DISABLED_TIME = 127 }; + +enum { ROM_SIZE = 64 }; +enum { ROM_ADDR = 0xFFC0 }; + +enum { TIMER_COUNT = 3 }; + +struct Timer +{ + long next_tick; + int period; + int count; + int shift; + int enabled; + int counter; +}; + +void Timer_run_( struct Timer* t, long time ) ICODE_ATTR; + +static inline void Timer_run( struct Timer* t, long time ) +{ + if ( time >= t->next_tick ) + Timer_run_( t, time ); +} + +struct Spc_Emu +{ + uint8_t cycle_table [0x100]; + struct cpu_regs_t r; + + int32_t* sample_buf; + long next_dsp; + int rom_enabled; + int extra_cycles; + + struct Timer timer [TIMER_COUNT]; + + /* large objects at end */ + struct Spc_Dsp dsp; + uint8_t extra_ram [ROM_SIZE]; + uint8_t boot_rom [ROM_SIZE]; +}; + +enum { SPC_FILE_SIZE = 0x10180 }; + +struct spc_file_t +{ + char signature [27]; + char unused [10]; + uint8_t pc [2]; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; + char unused2 [212]; + uint8_t ram [0x10000]; + uint8_t dsp [128]; + uint8_t ipl_rom [128]; +}; + +void SPC_Init( THIS ); + +int SPC_load_spc( THIS, const void* data, long size ); + +/**************** DSP interaction ****************/ +void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR; + +static inline int DSP_read( struct Spc_Dsp* this, int i ) +{ + assert( (unsigned) i < REGISTER_COUNT ); + return this->r.reg [i]; +} + +void SPC_run_dsp_( THIS, long time ) ICODE_ATTR; + +static inline void SPC_run_dsp( THIS, long time ) +{ + if ( time >= this->next_dsp ) + SPC_run_dsp_( this, time ); +} + +int SPC_read( THIS, unsigned addr, long const time ) ICODE_ATTR; +void SPC_write( THIS, unsigned addr, int data, long const time ) ICODE_ATTR; + +/**************** Sample generation ****************/ +int SPC_play( THIS, long count, int32_t* out ) ICODE_ATTR; + +#endif /* _SPC_CODEC_H_ */ -- cgit v1.2.3