summaryrefslogtreecommitdiff
path: root/apps/codecs/spc
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-07-16 21:16:52 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-07-16 21:16:52 +0000
commit84501494ceabbb05292f944721389a76cf691483 (patch)
tree4d7cc664cd272fa367cbe6f4fc9d9ed500708f02 /apps/codecs/spc
parent8552eff9e46ede8968ea13cc172e1c61856bee18 (diff)
Forgot to add a file as usual.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13920 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/spc')
-rw-r--r--apps/codecs/spc/spc_codec.h399
1 files changed, 399 insertions, 0 deletions
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 <assert.h>
+
+/** 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_ */