Index: apps/codecs.c
===================================================================
--- apps/codecs.c	(revision 29484)
+++ apps/codecs.c	(working copy)
@@ -70,7 +70,7 @@
 
 extern void* plugin_get_audio_buffer(size_t *buffer_size);
 
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(HAVE_RECORDING)
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
 #undef open
 static int open(const char* pathname, int flags, ...)
 {
@@ -154,6 +154,7 @@
     enc_finish_chunk,
     enc_get_pcm_data,
     enc_unget_pcm_data,
+#endif /* HAVE_RECORDING */
 
     /* file */
     (open_func)PREFIX(open),
@@ -163,10 +164,10 @@
     (write_func)PREFIX(write),
     round_value_to_list32,
 
-#endif /* HAVE_RECORDING */
-
     /* new stuff at the end, sort into place next time
        the API gets incompatible */
+    strrchr,
+    strcasecmp,
 };
 
 void codec_get_full_path(char *path, const char *codec_root_fn)
Index: apps/codecs.h
===================================================================
--- apps/codecs.h	(revision 29484)
+++ apps/codecs.h	(working copy)
@@ -213,6 +213,7 @@
     void            (*enc_finish_chunk)(void);
     unsigned char * (*enc_get_pcm_data)(size_t size);
     size_t          (*enc_unget_pcm_data)(size_t size);
+#endif /* HAVE_RECORDING */
 
     /* file */
     int (*open)(const char* pathname, int flags, ...);
@@ -224,10 +225,11 @@
                                  const unsigned long list[],
                                  int count,
                                  bool signd);
-#endif
 
     /* new stuff at the end, sort into place next time
        the API gets incompatible */
+    char * (*strrchr)(const char *s, int c);
+    int (*strcasecmp)(const char *, const char *);
 };
 
 /* codec header */
Index: apps/metadata/gbs.c
===================================================================
--- apps/metadata/gbs.c	(revision 0)
+++ apps/metadata/gbs.c	(revision 0)
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "system.h"
+#include "metadata.h"
+#include "metadata_common.h"
+#include "metadata_parsers.h"
+#include "rbunicode.h"
+
+bool get_gbs_metadata(int fd, struct mp3entry* id3)
+{
+    /* Use the trackname part of the id3 structure as a temporary buffer */
+    unsigned char* buf = (unsigned char *)id3->path;
+    int read_bytes;
+    char *p;
+
+    if ((lseek(fd, 0, SEEK_SET) < 0) 
+         || ((read_bytes = read(fd, buf, 112)) < 112))
+    {
+        return false;
+    }
+
+    id3->length = 120*1000;
+    id3->vbr = false;
+    id3->filesize = filesize(fd);
+  
+    if (memcmp(buf,"GBS",3) != 0) /* GBR not supported */
+    {
+        return false;
+    }
+
+    p = id3->id3v2buf;
+
+    /* Some metadata entries have 32 bytes length */
+    /* Game */
+    memcpy(p, &buf[16], 32); *(p + 33) = '\0';
+    id3->title = p;
+    p += strlen(p)+1;
+
+    /* Artist */
+    memcpy(p, &buf[48], 32); *(p + 33) = '\0';
+    id3->artist = p;
+    p += strlen(p)+1;
+
+    /* Copyright */
+    memcpy(p, &buf[80], 32); *(p + 33) = '\0';
+    id3->album = p;
+    p += strlen(p)+1;
+        
+    return true;
+}
Index: apps/metadata/metadata_parsers.h
===================================================================
--- apps/metadata/metadata_parsers.h	(revision 29484)
+++ apps/metadata/metadata_parsers.h	(working copy)
@@ -47,3 +47,4 @@
 bool get_vox_metadata(int fd, struct mp3entry* id3);
 bool get_wave64_metadata(int fd, struct mp3entry* id3);
 bool get_tta_metadata(int fd, struct mp3entry* id3);
+bool get_gbs_metadata(int fd, struct mp3entry* id3);
Index: apps/metadata.c
===================================================================
--- apps/metadata.c	(revision 29484)
+++ apps/metadata.c	(working copy)
@@ -210,6 +210,9 @@
     /* Advanced Audio Coding High Efficiency in M4A container */
     [AFMT_MP4_AAC_HE] =
         AFMT_ENTRY("AAC-HE","aac",  NULL,       get_mp4_metadata,   "mp4\0"),
+    /* GBS (Game Boy Sound Format) */
+    [AFMT_GBS] =
+        AFMT_ENTRY("GBS",   "gbs",  NULL,       get_gbs_metadata,   "gbs\0"),
 #endif
 };
 
Index: apps/metadata.h
===================================================================
--- apps/metadata.h	(revision 29484)
+++ apps/metadata.h	(working copy)
@@ -87,6 +87,7 @@
     AFMT_WMAVOICE,     /* WMA Voice in ASF */
     AFMT_MPC_SV8,      /* Musepack SV8 */
     AFMT_MP4_AAC_HE,   /* Advanced Audio Coding (AAC-HE) in M4A container */
+    AFMT_GBS,          /* GBS (Game Boy Sound Format) */
 #endif
 
     /* add new formats at any index above this line to have a sensible order -
Index: apps/playback.c
===================================================================
--- apps/playback.c	(revision 29484)
+++ apps/playback.c	(working copy)
@@ -1425,6 +1425,7 @@
     case AFMT_NSF:
     case AFMT_SPC:
     case AFMT_SID:
+    case AFMT_GBS:
         logf("Loading atomic %d",track_id3->codectype);
         type = TYPE_ATOMIC_AUDIO;
         break;
Index: apps/SOURCES
===================================================================
--- apps/SOURCES	(revision 29484)
+++ apps/SOURCES	(working copy)
@@ -224,6 +224,7 @@
 metadata/au.c
 metadata/vox.c
 metadata/tta.c
+metadata/gbs.c
 #endif
 #ifdef HAVE_TAGCACHE
 tagcache.c
Index: apps/filetypes.c
===================================================================
--- apps/filetypes.c	(revision 29484)
+++ apps/filetypes.c	(working copy)
@@ -112,6 +112,7 @@
     { "vox", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
     { "w64", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
     { "tta", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+    { "gbs", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
 #endif
     { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
     { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
Index: apps/codecs/gbs.c
===================================================================
--- apps/codecs/gbs.c	(revision 0)
+++ apps/codecs/gbs.c	(revision 0)
@@ -0,0 +1,122 @@
+
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+
+#include <codecs/lib/codeclib.h>
+#include "libgbs/gbs_emu.h" 
+
+CODEC_HEADER
+
+/* Maximum number of bytes to process in one iteration */
+#define CHUNK_SIZE (1024*2)
+
+static int16_t samples[CHUNK_SIZE] IBSS_ATTR;
+static struct Gbs_Emu gbs_emu IDATA_ATTR CACHEALIGN_ATTR;
+
+/****************** rockbox interface ******************/
+
+static void set_codec_track(int t) {
+    Gbs_start_track(&gbs_emu, t); 
+
+    /* for REPEAT_ONE we disable track limits */
+    if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+        Track_set_fade(&gbs_emu, Track_get_length( &gbs_emu, t ), 4000);
+    }
+    ci->id3->elapsed = t*1000; /* t is track no to display */
+}
+
+/* this is the codec entry point */
+enum codec_status codec_main(void)
+{
+    blargg_err_t err;
+    uint8_t *buf;
+    size_t n;
+    int track;
+
+    /* we only render 16 bits */
+    ci->configure(DSP_SET_SAMPLE_DEPTH, 16);
+
+    /* 44 Khz, Interleaved stereo */
+    ci->configure(DSP_SET_FREQUENCY, 44100);
+    ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
+
+    Gbs_init(&gbs_emu);
+    Gbs_set_sample_rate(&gbs_emu, 44100);
+
+next_track:
+    track = 0;
+
+    DEBUGF("GBS: next_track\n");
+    if (codec_init()) {
+        return CODEC_ERROR;
+    }  
+
+    /* wait for track info to load */
+    if (codec_wait_taginfo() != 0)
+        goto request_next_track;
+
+    codec_set_replaygain(ci->id3);
+        
+    /* Read the entire file */
+    DEBUGF("GBS: request file\n");
+    ci->seek_buffer(0);
+    buf = ci->request_buffer(&n, ci->filesize);
+    if (!buf || n < (size_t)ci->filesize) {
+        DEBUGF("GBS: file load failed\n");
+        return CODEC_ERROR;
+    }
+   
+    if ((err = Gbs_load(&gbs_emu, buf, ci->filesize))) {
+        DEBUGF("GBS: Gbs_load failed (%s)\n", err);
+        return CODEC_ERROR;
+    }
+
+    /* Load m3u playlist */
+    char *p = strrchr(ci->id3->path, '.');
+    if (p) {
+        strcpy(p, ".m3u");
+        Gbs_load_m3u(&gbs_emu, ci->id3->path);
+        
+        // Copy back gbs extension
+        strcpy(p, ".gbs");
+    }
+
+    /* Give priority to m3u playlist */
+    struct info_t* info = &gbs_emu.m3u.info;
+    if (info->title)    ci->id3->title = info->title;
+    if (info->composer) ci->id3->artist = info->composer;
+    if (info->engineer) ci->id3->album = info->engineer;
+    ci->id3->length = gbs_emu.track_count*1000;
+
+next_tune:
+    set_codec_track(track);
+
+    /* The main decoder loop */
+    while (1) {
+        ci->yield();
+        if (ci->stop_codec || ci->new_track)
+            break;
+
+        if (ci->seek_time) {
+            track = ci->seek_time/1000;
+            ci->seek_complete();
+            if (track >= gbs_emu.track_count) break;
+            goto next_tune;
+        }
+
+        /* Generate audio buffer */
+        err = Gbs_play(&gbs_emu, CHUNK_SIZE, samples);
+        if (err || gbs_emu.track_ended) {
+            track++;
+            if (track >= gbs_emu.track_count) break;
+            goto next_tune;
+        }
+
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+    }
+
+request_next_track:
+    if (ci->request_next_track())
+        goto next_track; /* when we fall through here we'll reload the file */
+
+    return CODEC_OK;
+}
Index: apps/codecs/libgbs/gb_cpu.c
===================================================================
--- apps/codecs/libgbs/gb_cpu.c	(revision 0)
+++ apps/codecs/libgbs/gb_cpu.c	(revision 0)
@@ -0,0 +1,53 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "gb_cpu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+inline void set_code_page( struct Gb_Cpu* this, int i, void* p )
+{
+	byte* p2 = STATIC_CAST(byte*,p) - GB_CPU_OFFSET( i * page_size );
+	this->cpu_state_.code_map [i] = p2;
+	this->cpu_state->code_map [i] = p2;
+}
+
+void Cpu_reset( struct Gb_Cpu* this, void* unmapped )
+{
+	check( this->cpu_state == &this->cpu_state_ );
+	this->cpu_state = &this->cpu_state_;
+	
+	this->cpu_state_.time = 0;
+	
+	int i;
+	for ( i = 0; i < page_count + 1; ++i )
+		set_code_page( this, i, unmapped );
+	
+	memset( &this->r, 0, sizeof this->r );
+	
+	blargg_verify_byte_order();
+}
+
+void Cpu_map_code( struct Gb_Cpu* this, addr_t start, int size, void* data )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size  % page_size == 0 );
+	require( start + size <= mem_size );
+	
+	int offset;
+	for ( offset = 0; offset < size; offset += page_size )
+		set_code_page( this, (start + offset) >> page_bits, STATIC_CAST(char*,data) + offset );
+}
Index: apps/codecs/libgbs/SOURCES
===================================================================
--- apps/codecs/libgbs/SOURCES	(revision 0)
+++ apps/codecs/libgbs/SOURCES	(revision 0)
@@ -0,0 +1,9 @@
+blip_buffer.c
+gb_apu.c
+gb_cpu.c
+gbs_cpu.c
+gb_oscs.c
+gbs_emu.c
+multi_buffer.c
+rom_data.c
+m3u_playlist.c
Index: apps/codecs/libgbs/gb_apu.h
===================================================================
--- apps/codecs/libgbs/gb_apu.h	(revision 0)
+++ apps/codecs/libgbs/gb_apu.h	(revision 0)
@@ -0,0 +1,85 @@
+// Nintendo Game Boy sound hardware emulator with save state support
+
+// Gb_Snd_Emu 0.1.4
+#ifndef GB_APU_H
+#define GB_APU_H
+
+#include "gb_oscs.h"
+
+// Clock rate sound hardware runs at
+enum { clock_rate = 4194304 * GB_APU_OVERCLOCK };
+	
+// Registers are at io_addr to io_addr+io_size-1
+enum { io_addr = 0xFF10 };
+enum { io_size = 0x30 };
+enum { regs_size = io_size + 0x10 };
+	
+enum gb_mode_t {
+	mode_dmg,   // Game Boy monochrome
+	mode_cgb,   // Game Boy Color
+	mode_agb    // Game Boy Advance
+};
+
+// 0: Square 1, 1: Square 2, 2: Wave, 3: Noise
+enum { osc_count = 4 }; // 0 <= chan < osc_count
+
+struct Gb_Apu {
+	struct Gb_Osc*     oscs [osc_count];
+	blip_time_t last_time;          // time sound emulator has been run to
+	blip_time_t frame_period;       // clocks between each frame sequencer step
+	double      volume_;
+	bool        reduce_clicks_;
+	
+	struct Gb_Square square1;
+	struct Gb_Square square2;
+	struct Gb_Wave   wave;
+	struct Gb_Noise  noise;
+	blip_time_t     frame_time;     // time of next frame sequencer action
+	int             frame_phase;    // phase of next frame sequencer step
+	
+	uint8_t  regs [regs_size];// last values written to registers
+	
+	// large objects after everything else
+	struct Blip_Synth synth;
+};
+
+// Basics
+
+// Initializes apu
+void Apu_init( struct Gb_Apu* this );
+	
+// Emulates to time t, then writes data to addr
+void Apu_write_register( struct Gb_Apu* this, blip_time_t t, int addr, int data ); ICODE_ATTR
+	
+// Emulates to time t, then subtracts t from the current time.
+// OK if previous write call had time slightly after t.
+void Apu_end_frame( struct Gb_Apu* this,blip_time_t t ); ICODE_ATTR
+	
+// More features
+	
+// Emulates to time t, then reads from addr
+int Apu_read_register( struct Gb_Apu* this, blip_time_t t, int addr ); ICODE_ATTR
+
+// Resets hardware to state after power, BEFORE boot ROM runs. Mode selects
+// sound hardware. If agb_wave is true, enables AGB's extra wave features.
+void Apu_reset( struct Gb_Apu* this, enum gb_mode_t mode, bool agb_wave );
+	
+// Same as set_output(), but for a particular channel
+void Apu_set_output( struct Gb_Apu* this, int chan, struct Blip_Buffer* center,
+		struct Blip_Buffer* left, struct Blip_Buffer* right );
+	
+// Sets overall volume, where 1.0 is normal
+void Apu_volume( struct Gb_Apu* this, double v );
+	
+// If true, reduces clicking by disabling DAC biasing. Note that this reduces
+// emulation accuracy, since the clicks are authentic.
+void Apu_reduce_clicks( struct Gb_Apu* this, bool reduce );
+	
+// Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the
+// tempo in a music player.
+void Apu_set_tempo( struct Gb_Apu* this, double t );
+
+
+void write_osc( struct Gb_Apu* this, int reg, int old_data, int data ); ICODE_ATTR
+
+#endif
Index: apps/codecs/libgbs/gb_cpu.h
===================================================================
--- apps/codecs/libgbs/gb_cpu.h	(revision 0)
+++ apps/codecs/libgbs/gb_cpu.h	(revision 0)
@@ -0,0 +1,81 @@
+// Nintendo Game Boy CPU emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef GB_CPU_H
+#define GB_CPU_H
+
+#include "blargg_common.h"
+#include "blargg_source.h"
+
+typedef int addr_t;
+
+// Emulator reads this many bytes past end of a page
+enum { cpu_padding = 8 };
+enum { mem_size = 0x10000 };
+enum { page_bits = 13 };
+enum { page_size = 1 << page_bits };
+enum { page_count = mem_size >> page_bits };
+
+// Game Boy Z-80 registers. NOT kept updated during emulation.
+struct core_regs_t {
+	uint16_t bc, de, hl, fa;
+};
+	
+struct registers_t {
+	int pc; // more than 16 bits to allow overflow detection
+	uint16_t sp;
+
+	struct core_regs_t rp;
+};
+
+struct cpu_state_t {
+	byte* code_map [page_count + 1];
+	int time;
+};
+
+struct Gb_Cpu {	
+	struct registers_t r;
+	
+	// Base address for RST vectors, to simplify GBS player (normally 0)
+	addr_t rst_base;
+	
+	struct cpu_state_t* cpu_state; // points to state_ or a local copy within run()
+	struct cpu_state_t cpu_state_;
+};
+
+// Initializes Gb cpu
+static inline void Cpu_init( struct Gb_Cpu* this )
+{
+	this->rst_base = 0;
+	this->cpu_state = &this->cpu_state_;
+}
+
+// Clears registers and map all pages to unmapped
+void Cpu_reset( struct Gb_Cpu* this, void* unmapped );
+	
+// Maps code memory (memory accessed via the program counter). Start and size
+// must be multiple of page_size.
+void Cpu_map_code( struct Gb_Cpu* this, addr_t start, int size, void* code ); ICODE_ATTR
+			
+// Current time.
+static inline int Cpu_time( struct Gb_Cpu* this ) { return this->cpu_state->time; }
+	
+// Changes time. Must not be called during emulation.
+// Should be negative, because emulation stops once it becomes >= 0.
+static inline void Cpu_set_time( struct Gb_Cpu* this, int t ) { this->cpu_state->time = t; }
+
+#define GB_CPU_PAGE( addr ) ((unsigned) (addr) >> page_bits)
+
+#ifdef BLARGG_NONPORTABLE
+	#define GB_CPU_OFFSET( addr ) (addr)
+#else
+	#define GB_CPU_OFFSET( addr ) ((addr) & (page_size - 1))
+#endif
+
+// Accesses emulated memory as CPU does
+static inline uint8_t* Cpu_get_code( struct Gb_Cpu* this, addr_t addr )
+{
+	return this->cpu_state_.code_map [GB_CPU_PAGE( addr )] + GB_CPU_OFFSET( addr );
+}
+
+#endif
Index: apps/codecs/libgbs/blargg_common.h
===================================================================
--- apps/codecs/libgbs/blargg_common.h	(revision 0)
+++ apps/codecs/libgbs/blargg_common.h	(revision 0)
@@ -0,0 +1,150 @@
+// Sets up common environment for Shay Green's libraries.
+// To change configuration options, modify blargg_config.h, not this file.
+
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+
+#undef BLARGG_COMMON_H
+// allow blargg_config.h to #include blargg_common.h
+#include "blargg_config.h"
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+#if defined(CPU_ARM) || !defined(ROCKBOX)
+#if (CONFIG_CPU != PP5002) || defined(WIN32)
+    #undef  ICODE_ATTR
+    #define ICODE_ATTR
+
+    #undef  IDATA_ATTR
+    #define IDATA_ATTR
+
+    #undef  ICONST_ATTR
+    #define ICONST_ATTR
+
+    #undef  IBSS_ATTR
+    #define IBSS_ATTR
+#endif
+#endif
+
+// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr)
+#ifndef STATIC_CAST
+	#define STATIC_CAST(T,expr) ((T) (expr))
+#endif
+
+// blargg_err_t (0 on success, otherwise error string)
+#ifndef blargg_err_t
+	typedef const char* blargg_err_t;
+#endif
+
+#define BLARGG_4CHAR( a, b, c, d ) \
+	((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF))
+
+// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
+#ifndef BOOST_STATIC_ASSERT
+	#ifdef _MSC_VER
+		// MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified
+		#define BOOST_STATIC_ASSERT( expr ) \
+			void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] )
+	#else
+		// Some other compilers fail when declaring same function multiple times in class,
+		// so differentiate them by line
+		#define BOOST_STATIC_ASSERT( expr ) \
+			void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] )
+	#endif
+#endif
+
+// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1,
+// compiler is assumed to support bool. If undefined, availability is determined.
+#ifndef BLARGG_COMPILER_HAS_BOOL
+	#if defined (__MWERKS__)
+		#if !__option(bool)
+			#define BLARGG_COMPILER_HAS_BOOL 0
+		#endif
+	#elif defined (_MSC_VER)
+		#if _MSC_VER < 1100
+			#define BLARGG_COMPILER_HAS_BOOL 0
+		#endif
+	#elif defined (__GNUC__)
+		// supports bool
+	#elif __cplusplus < 199711
+		#define BLARGG_COMPILER_HAS_BOOL 0
+	#endif
+#endif
+#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
+	// If you get errors here, modify your blargg_config.h file
+	typedef int bool;
+	static bool true  = 1;
+	static bool false = 0;
+#endif
+
+// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
+#include <limits.h>
+
+#if INT_MAX >= 0x7FFFFFFF
+	typedef int blargg_long;
+#else
+	typedef long blargg_long;
+#endif
+
+#if UINT_MAX >= 0xFFFFFFFF
+	typedef unsigned blargg_ulong;
+#else
+	typedef unsigned long blargg_ulong;
+#endif
+
+// int8_t etc.
+
+
+// ROCKBOX: If defined, use <codeclib.h> for int_8_t etc
+#if defined (ROCKBOX)
+	#include <codecs/lib/codeclib.h>
+// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc.
+#elif defined (HAVE_STDINT_H)
+	#include <stdint.h>
+	#define BOOST
+
+// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
+#elif defined (HAVE_INTTYPES_H)
+	#include <inttypes.h>
+	#define BOOST
+
+#else
+	#if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F
+		typedef signed char     int8_t;
+		typedef unsigned char   uint8_t;
+	#else
+		// No suitable 8-bit type available
+		typedef struct see_blargg_common_h int8_t;
+		typedef struct see_blargg_common_h uint8_t;
+	#endif
+		
+	#if USHRT_MAX == 0xFFFF
+		typedef short           int16_t;
+		typedef unsigned short  uint16_t;
+	#else
+		// No suitable 16-bit type available
+		typedef struct see_blargg_common_h int16_t;
+		typedef struct see_blargg_common_h uint16_t;
+	#endif
+		
+	#if ULONG_MAX == 0xFFFFFFFF
+		typedef long            int32_t;
+		typedef unsigned long   uint32_t;
+	#elif UINT_MAX == 0xFFFFFFFF
+		typedef int             int32_t;
+		typedef unsigned int    uint32_t;
+	#else
+		// No suitable 32-bit type available
+		typedef struct see_blargg_common_h int32_t;
+		typedef struct see_blargg_common_h uint32_t;
+	#endif
+#endif
+
+#endif
+#endif
Index: apps/codecs/libgbs/gme.h
===================================================================
--- apps/codecs/libgbs/gme.h	(revision 0)
+++ apps/codecs/libgbs/gme.h	(revision 0)
@@ -0,0 +1,18 @@
+/* Game music emulator library C interface (also usable from C++) */
+
+/* Game_Music_Emu 0.5.2 */
+#ifndef GME_H
+#define GME_H
+
+#ifdef __cplusplus
+	extern "C" {
+#endif
+
+/* Error string returned by library functions, or NULL if no error (success) */
+typedef const char* gme_err_t;
+
+#ifdef __cplusplus
+	}
+#endif
+
+#endif
Index: apps/codecs/libgbs/blip_buffer.c
===================================================================
--- apps/codecs/libgbs/blip_buffer.c	(revision 0)
+++ apps/codecs/libgbs/blip_buffer.c	(revision 0)
@@ -0,0 +1,282 @@
+// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
+
+#include "blip_buffer.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Copyright (C) 2003-2006 Shay Green. this module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. this
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+	#include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
+
+void Blip_init( struct Blip_Buffer* this )
+{
+	this->factor_       = LONG_MAX;
+	this->offset_       = 0;
+	this->buffer_size_  = 0;
+	this->sample_rate_  = 0;
+	this->reader_accum_ = 0;
+	this->bass_shift_   = 0;
+	this->clock_rate_   = 0;
+	this->bass_freq_    = 16;
+	this->length_       = 0;
+	
+	// assumptions code makes about implementation-defined features
+	#ifndef NDEBUG
+		// right shift of negative value preserves sign
+		buf_t_ i = -0x7FFFFFFE;
+		assert( (i >> 1) == -0x3FFFFFFF );
+		
+		// casting to short truncates to 16 bits and sign-extends
+		i = 0x18000;
+		assert( (short) i == -0x8000 );
+	#endif
+}
+
+void Blip_stop( struct Blip_Buffer* this )
+{
+	if ( this->buffer_size_ != silent_buf_size )
+		free( this->buffer_ );
+}
+
+void Blip_clear( struct Blip_Buffer* this, int entire_buffer )
+{
+	this->offset_      = 0;
+	this->reader_accum_ = 0;
+	this->modified_    = 0;
+	if ( this->buffer_ )
+	{
+		long count = (entire_buffer ? this->buffer_size_ : Blip_samples_avail( this ));
+		memset( this->buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) );
+	}
+}
+
+blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, long new_rate, int msec )
+{
+	if ( this->buffer_size_ == silent_buf_size )
+	{
+		assert( 0 );
+		return "Internal (tried to resize Silent_Blip_Buffer)";
+	}
+	
+	// start with maximum length that resampled time can represent
+	long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
+	if ( msec != blip_max_length )
+	{
+		long s = (new_rate * (msec + 1) + 999) / 1000;
+		if ( s < new_size )
+			new_size = s;
+		else
+			assert( 0 ); // fails if requested buffer length exceeds limit
+	}
+	
+	if ( new_size > blip_buffer_max )
+		return "Out of memory";
+	
+	this->buffer_size_ = new_size;
+	assert( this->buffer_size_ != silent_buf_size );
+	
+	// update things based on the sample rate
+	this->sample_rate_ = new_rate;
+	this->length_ = new_size * 1000 / new_rate - 1;
+	if ( msec )
+		assert( this->length_ == msec ); // ensure length is same as that passed in
+	if ( this->clock_rate_ )
+		Blip_set_clock_rate( this, this->clock_rate_ );
+	Blip_bass_freq( this, this->bass_freq_ );
+	
+	Blip_clear( this, 1 );
+	
+	return 0; // success
+}
+
+/* Not sure if this affects sound quality */
+#if defined(ROCKBOX)
+  #define floor
+#endif
+
+blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, long rate )
+{
+	double ratio = (double) this->sample_rate_ / rate;
+	blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
+	assert( factor > 0 || !this->sample_rate_ ); // fails if clock/output ratio is too large
+	return (blip_resampled_time_t) factor;
+}
+
+void Blip_bass_freq( struct Blip_Buffer* this, int freq )
+{
+	this->bass_freq_ = freq;
+	int shift = 31;
+	if ( freq > 0 )
+	{
+		shift = 13;
+		long f = (freq << 16) / this->sample_rate_;
+		while ( (f >>= 1) && --shift ) { }
+	}
+	this->bass_shift_ = shift;
+}
+
+void Blip_end_frame( struct Blip_Buffer* this, blip_time_t t )
+{
+	this->offset_ += t * this->factor_;
+	assert( Blip_samples_avail( this ) <= (long) this->buffer_size_ ); // time outside buffer length
+}
+
+void Blip_remove_silence( struct Blip_Buffer* this, long count )
+{
+	assert( count <= Blip_samples_avail( this ) ); // tried to remove more samples than available
+	this->offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+}
+
+long Blip_count_samples( struct Blip_Buffer* this, blip_time_t t )
+{
+	unsigned long last_sample  = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
+	unsigned long first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
+	return (long) (last_sample - first_sample);
+}
+
+blip_time_t Blip_count_clocks( struct Blip_Buffer* this, long count )
+{
+	if ( !this->factor_ )
+	{
+		assert( 0 ); // sample rate and clock rates must be set first
+		return 0;
+	}
+	
+	if ( count > this->buffer_size_ )
+		count = this->buffer_size_;
+	blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+	return (blip_time_t) ((time - this->offset_ + this->factor_ - 1) / this->factor_);
+}
+
+void Blip_remove_samples( struct Blip_Buffer* this, long count )
+{
+	if ( count )
+	{
+		Blip_remove_silence( this, count );
+		
+		// copy remaining samples to beginning and clear old samples
+		long remain = Blip_samples_avail( this ) + blip_buffer_extra_;
+		memmove( this->buffer_, this->buffer_ + count, remain * sizeof *this->buffer_ );
+		memset( this->buffer_ + remain, 0, count * sizeof *this->buffer_ );
+	}
+}
+
+long Blip_read_samples( struct Blip_Buffer* this, blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo )
+{
+	long count = Blip_samples_avail( this );
+	if ( count > max_samples )
+		count = max_samples;
+	
+	if ( count )
+	{
+		int const bass = BLIP_READER_BASS( *this );
+		BLIP_READER_BEGIN( reader, *this );
+		
+		if ( !stereo )
+		{
+			blip_long n;
+			for ( n = count; n; --n )
+			{
+				blip_long s = BLIP_READER_READ( reader );
+				if ( (blip_sample_t) s != s )
+					s = 0x7FFF - (s >> 24);
+				*out++ = (blip_sample_t) s;
+				BLIP_READER_NEXT( reader, bass );
+			}
+		}
+		else
+		{
+			blip_long n;
+			for ( n = count; n; --n )
+			{
+				blip_long s = BLIP_READER_READ( reader );
+				if ( (blip_sample_t) s != s )
+					s = 0x7FFF - (s >> 24);
+				*out = (blip_sample_t) s;
+				out += 2;
+				BLIP_READER_NEXT( reader, bass );
+			}
+		}
+		BLIP_READER_END( reader, *this );
+		
+		Blip_remove_samples( this, count );
+	}
+	return count;
+}
+
+void Blip_mix_samples( struct Blip_Buffer* this,  blip_sample_t const* in, long count )
+{
+	if ( this->buffer_size_ == silent_buf_size )
+	{
+		assert( 0 );
+		return;
+	}
+	
+	buf_t_* out = this->buffer_ + (this->offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
+	
+	int const sample_shift = blip_sample_bits - 16;
+	int prev = 0;
+	while ( count-- )
+	{
+		blip_long s = (blip_long) *in++ << sample_shift;
+		*out += s - prev;
+		prev = s;
+		++out;
+	}
+	*out -= prev;
+}
+
+void Blip_set_modified( struct Blip_Buffer* this ) 
+{ 
+	this->modified_ = 1; 
+}
+
+int Blip_clear_modified( struct Blip_Buffer* this )
+{ 
+	int b = this->modified_;
+	this->modified_ = 0;
+	return b; 
+}
+
+blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t )
+{
+	return t * this->factor_;
+}
+
+blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
+{
+	return t * this->factor_ + this->offset_;
+}
+
+
+// Blip_Synth
+
+void Synth_init( struct Blip_Synth* this )
+{
+	this->buf = 0;
+	this->last_amp = 0;
+	this->delta_factor = 0;
+}
+
+// Set overall volume of waveform
+void Synth_volume( struct Blip_Synth* this, double v )
+{
+	this->delta_factor = (int) (v * (1L << blip_sample_bits) + 0.5);
+}
Index: apps/codecs/libgbs/gb_oscs.c
===================================================================
--- apps/codecs/libgbs/gb_oscs.c	(revision 0)
+++ apps/codecs/libgbs/gb_oscs.c	(revision 0)
@@ -0,0 +1,787 @@
+// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
+
+#include "gb_apu.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const cgb_02 = 0; // enables bug in early CGB units that causes problems in some games
+int const cgb_05 = 0; // enables CGB-05 zombie behavior
+
+int const trigger_mask   = 0x80;
+int const length_enabled = 0x40;
+
+void Osc_reset( struct Gb_Osc* this )
+{
+	this->output   = NULL;
+	this->last_amp = 0;
+	this->delay    = 0;
+	this->phase    = 0;
+	this->enabled  = false;
+}
+
+inline void Osc_update_amp( struct Gb_Osc* this, blip_time_t time, int new_amp )
+{
+	Blip_set_modified( this->output );
+	int delta = new_amp - this->last_amp;
+	if ( delta )
+	{
+		this->last_amp = new_amp;
+		Synth_offset( this->synth, time, delta, this->output );
+	}
+}
+
+// Units
+
+void Osc_clock_length( struct Gb_Osc* this )
+{
+	if ( (this->regs [4] & length_enabled) && this->length_ctr )
+	{
+		if ( --this->length_ctr <= 0 )
+			this->enabled = false;
+	}
+}
+
+void Noise_clock_envelope( struct Gb_Noise* this )
+{
+	if ( this->env_enabled && --this->env_delay <= 0 && Noise_reload_env_timer( this ) )
+	{
+		int v = this->volume + (this->osc.regs [2] & 0x08 ? +1 : -1);
+		if ( 0 <= v && v <= 15 )
+			this->volume = v;
+		else
+			this->env_enabled = false;
+	}
+}
+
+void Square_clock_envelope( struct Gb_Square* this )
+{
+	if ( this->env_enabled && --this->env_delay <= 0 && Square_reload_env_timer( this ) )
+	{
+		int v = this->volume + (this->osc.regs [2] & 0x08 ? +1 : -1);
+		if ( 0 <= v && v <= 15 )
+			this->volume = v;
+		else
+			this->env_enabled = false;
+	}
+}
+
+inline void reload_sweep_timer( struct Gb_Square* this )
+{
+	this->sweep_delay = (this->osc.regs [0] & period_mask) >> 4;
+	if ( !this->sweep_delay )
+		this->sweep_delay = 8;
+}
+
+void calc_sweep( struct Gb_Square* this, bool update )
+{
+	struct Gb_Osc* osc = &this->osc;
+	int const shift = osc->regs [0] & shift_mask;
+	int const delta = this->sweep_freq >> shift;
+	this->sweep_neg = (osc->regs [0] & 0x08) != 0;
+	int const freq = this->sweep_freq + (this->sweep_neg ? -delta : delta);
+	
+	if ( freq > 0x7FF )
+	{
+		osc->enabled = false;
+	}
+	else if ( shift && update )
+	{
+		this->sweep_freq = freq;
+		
+		osc->regs [3] = freq & 0xFF;
+		osc->regs [4] = (osc->regs [4] & ~0x07) | (freq >> 8 & 0x07);
+	}
+}
+
+void clock_sweep( struct Gb_Square* this )
+{
+	if ( --this->sweep_delay <= 0 )
+	{
+		reload_sweep_timer( this );
+		if ( this->sweep_enabled && (this->osc.regs [0] & period_mask) )
+		{
+			calc_sweep( this, true  );
+			calc_sweep( this, false );
+		}
+	}
+}
+
+int wave_access( struct Gb_Wave* this, int addr )
+{
+	if ( this->osc.enabled )
+	{
+		addr = this->osc.phase & (wave_bank_size - 1);
+		if ( this->osc.mode == mode_dmg )
+		{
+			addr++;
+			if ( this->osc.delay > clk_mul )
+				return -1; // can only access within narrow time window while playing
+		}
+		addr >>= 1;
+	}
+	return addr & 0x0F;
+}
+
+// write_register
+
+int write_trig( struct Gb_Osc* this, int frame_phase, int max_len, int old_data )
+{
+	int data = this->regs [4];
+	
+	if ( (frame_phase & 1) && !(old_data & length_enabled) && this->length_ctr )
+	{
+		if ( (data & length_enabled) || cgb_02 )
+			this->length_ctr--;
+	}
+	
+	if ( data & trigger_mask )
+	{
+		this->enabled = true;
+		if ( !this->length_ctr )
+		{
+			this->length_ctr = max_len;
+			if ( (frame_phase & 1) && (data & length_enabled) )
+				this->length_ctr--;
+		}
+	}
+	
+	if ( !this->length_ctr )
+		this->enabled = false;
+	
+	return data & trigger_mask;
+}
+
+inline void Noise_zombie_volume( struct Gb_Noise* this, int old, int data )
+{
+	int v = this->volume;
+	if ( this->osc.mode == mode_agb || cgb_05 )
+	{
+		// CGB-05 behavior, very close to AGB behavior as well
+		if ( (old ^ data) & 8 )
+		{
+			if ( !(old & 8) )
+			{
+				v++;
+				if ( old & 7 )
+					v++;
+			}
+			
+			v = 16 - v;
+		}
+		else if ( (old & 0x0F) == 8 )
+		{
+			v++;
+		}
+	}
+	else
+	{
+		// CGB-04&02 behavior, very close to MGB behavior as well
+		if ( !(old & 7) && this->env_enabled )
+			v++;
+		else if ( !(old & 8) )
+			v += 2;
+		
+		if ( (old ^ data) & 8 )
+			v = 16 - v;
+	}
+	this->volume = v & 0x0F;
+}
+
+inline void Square_zombie_volume( struct Gb_Square* this, int old, int data )
+{
+	int v = this->volume;
+	if ( this->osc.mode == mode_agb || cgb_05 )
+	{
+		// CGB-05 behavior, very close to AGB behavior as well
+		if ( (old ^ data) & 8 )
+		{
+			if ( !(old & 8) )
+			{
+				v++;
+				if ( old & 7 )
+					v++;
+			}
+			
+			v = 16 - v;
+		}
+		else if ( (old & 0x0F) == 8 )
+		{
+			v++;
+		}
+	}
+	else
+	{
+		// CGB-04&02 behavior, very close to MGB behavior as well
+		if ( !(old & 7) && this->env_enabled )
+			v++;
+		else if ( !(old & 8) )
+			v += 2;
+		
+		if ( (old ^ data) & 8 )
+			v = 16 - v;
+	}
+	this->volume = v & 0x0F;
+}
+
+bool Square_write_register( struct Gb_Square* this, int frame_phase, int reg, int old_data, int data )
+{
+	int const max_len = 64;
+	
+	switch ( reg )
+	{
+	case 1:
+		this->osc.length_ctr = max_len - (data & (max_len - 1));
+		break;
+	
+	case 2:
+		if ( !Square_dac_enabled( this ) )
+			this->osc.enabled = false;
+		
+		Square_zombie_volume( this, old_data, data );
+		
+		if ( (data & 7) && this->env_delay == 8 )
+		{
+			this->env_delay = 1;
+			Square_clock_envelope( this ); // TODO: really happens at next length clock
+		}
+		break;
+	
+	case 4:
+		if ( write_trig( &this->osc, frame_phase, max_len, old_data ) )
+		{
+			this->volume = this->osc.regs [2] >> 4;
+			Square_reload_env_timer( this );
+			this->env_enabled = true;
+			if ( frame_phase == 7 )
+				this->env_delay++;
+			if ( !Square_dac_enabled( this ) )
+				this->osc.enabled = false;
+			this->osc.delay = (this->osc.delay & (4 * clk_mul - 1)) + Square_period( this );
+			return true;
+		}
+	}
+	
+	return false;
+}
+
+inline void Noise_write_register( struct Gb_Noise* this, int frame_phase, int reg, int old_data, int data )
+{
+	int const max_len = 64;
+	
+	switch ( reg )
+	{
+	case 1:
+		this->osc.length_ctr = max_len - (data & (max_len - 1));
+		break;
+	
+	case 2:
+		if ( !Noise_dac_enabled( this ) )
+			this->osc.enabled = false;
+		
+		Noise_zombie_volume( this, old_data, data );
+		
+		if ( (data & 7) && this->env_delay == 8 )
+		{
+			this->env_delay = 1;
+			Noise_clock_envelope( this ); // TODO: really happens at next length clock
+		}
+		break;
+	
+	case 4:
+		if ( write_trig( &this->osc, frame_phase, max_len, old_data ) )
+		{
+			this->volume = this->osc.regs [2] >> 4;
+			Noise_reload_env_timer( this );
+			this->env_enabled = true;
+			if ( frame_phase == 7 )
+				this->env_delay++;
+			if ( !Noise_dac_enabled( this ) )
+				this->osc.enabled = false;
+				
+			this->osc.phase = 0x7FFF;
+			this->osc.delay += 8 * clk_mul;
+		}
+	}
+}
+
+inline void Sweep_write_register( struct Gb_Square* this, int frame_phase, int reg, int old_data, int data )
+{
+	if ( reg == 0 && this->sweep_enabled && this->sweep_neg && !(data & 0x08) )
+		this->osc.enabled = false; // sweep negate disabled after used
+	
+	if ( Square_write_register( this, frame_phase, reg, old_data, data ) )
+	{
+		this->sweep_freq = Osc_frequency( &this->osc );
+		this->sweep_neg = false;
+		reload_sweep_timer( this );
+		this->sweep_enabled = (this->osc.regs [0] & (period_mask | shift_mask)) != 0;
+		if ( this->osc.regs [0] & shift_mask )
+			calc_sweep( this, false );
+	}
+}
+
+void corrupt_wave( struct Gb_Wave* this )
+{
+	int pos = ((this->osc.phase + 1) & (wave_bank_size - 1)) >> 1;
+	if ( pos < 4 )
+		this->wave_ram [0] = this->wave_ram [pos];
+	else {
+		int i;
+		for ( i = 4; --i >= 0; )
+			this->wave_ram [i] = this->wave_ram [(pos & ~3) + i];
+	}
+}
+
+inline void Wave_write_register( struct Gb_Wave* this, int frame_phase, int reg, int old_data, int data )
+{
+	int const max_len = 256;
+	
+	switch ( reg )
+	{
+	case 0:
+		if ( !Wave_dac_enabled( this ) )
+			this->osc.enabled = false;
+		break;
+	
+	case 1:
+		this->osc.length_ctr = max_len - data;
+		break;
+	
+	case 4:
+		{
+			bool was_enabled = this->osc.enabled;
+			if ( write_trig( &this->osc, frame_phase, max_len, old_data ) )
+			{
+				if ( !Wave_dac_enabled( this ) )
+					this->osc.enabled = false;
+				else if ( this->osc.mode == mode_dmg && was_enabled &&
+						(unsigned) (this->osc.delay - 2 * clk_mul) < 2 * clk_mul )
+					corrupt_wave( this );
+			
+				this->osc.phase = 0;
+				this->osc.delay = Wave_period( this ) + 6 * clk_mul;
+			}
+		}
+	}
+}
+
+void write_osc( struct Gb_Apu* this, int reg, int old_data, int data )
+{
+	int index = (reg * 3 + 3) >> 4; // avoids divide
+	assert( index == reg / 5 );
+	reg -= index * 5;
+	switch ( index )
+	{
+	case 0: Sweep_write_register ( &this->square1, this->frame_phase, reg, old_data, data ); break;
+	case 1: Square_write_register( &this->square2, this->frame_phase, reg, old_data, data ); break;
+	case 2: Wave_write_register  ( &this->wave, this->frame_phase, reg, old_data, data ); break;
+	case 3: Noise_write_register ( &this->noise, this->frame_phase, reg, old_data, data ); break;
+	}
+}
+
+// Synthesis
+
+void Square_run( struct Gb_Square* this, blip_time_t time, blip_time_t end_time )
+{
+	// Calc duty and phase
+	static byte const duty_offsets [4] ICONST_ATTR = { 1, 1, 3, 7 };
+	static byte const duties       [4] ICONST_ATTR = { 1, 2, 4, 6 };
+
+	struct Gb_Osc* osc = &this->osc;
+	int const duty_code = osc->regs [1] >> 6;
+	int duty_offset = duty_offsets [duty_code];
+	int duty = duties [duty_code];
+	if ( osc->mode == mode_agb )
+	{
+		// AGB uses inverted duty
+		duty_offset -= duty;
+		duty = 8 - duty;
+	}
+	int ph = (osc->phase + duty_offset) & 7;
+	
+	// Determine what will be generated
+	int vol = 0;
+	struct Blip_Buffer* const out = osc->output;
+	if ( out )
+	{
+		int amp = osc->dac_off_amp;
+		if ( Square_dac_enabled( this ) )
+		{
+			if ( osc->enabled )
+				vol = this->volume;
+			
+			amp = -dac_bias;
+			if ( osc->mode == mode_agb )
+				amp = -(vol >> 1);
+			
+			// Play inaudible frequencies as constant amplitude
+			if ( Osc_frequency( osc ) >= 0x7FA && osc->delay < 32 * clk_mul )
+			{
+				amp += (vol * duty) >> 3;
+				vol = 0;
+			}
+			
+			if ( ph < duty )
+			{
+				amp += vol;
+				vol = -vol;
+			}
+		}
+		Osc_update_amp( osc, time, amp );
+	}
+	
+	// Generate wave
+	time += osc->delay;
+	if ( time < end_time )
+	{
+		int const per = Square_period( this );
+		if ( !vol )
+		{
+			#ifdef GB_APU_FAST
+				time = end_time;
+			#else
+				// Maintain phase when not playing
+				int count = (end_time - time + per - 1) / per;
+				ph += count; // will be masked below
+				time += (blip_time_t) count * per;
+			#endif
+		}
+		else
+		{
+			// Output amplitude transitions
+			int delta = vol;
+			do
+			{
+				ph = (ph + 1) & 7;
+				if ( ph == 0 || ph == duty )
+				{
+					Synth_offset_inline( osc->synth, time, delta, out );
+					delta = -delta;
+				}
+				time += per;
+			}
+			while ( time < end_time );
+			
+			if ( delta != vol )
+				osc->last_amp -= delta;
+		}
+		osc->phase = (ph - duty_offset) & 7;
+	}
+	osc->delay = time - end_time;
+}
+
+#ifndef GB_APU_FAST
+// Quickly runs LFSR for a large number of clocks. For use when noise is generating
+// no sound.
+static unsigned run_lfsr( unsigned s, unsigned mask, int count )
+{
+	bool const optimized = true; // set to false to use only unoptimized loop in middle
+	
+	// optimization used in several places:
+	// ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n)
+	
+	if ( mask == 0x4000 && optimized )
+	{
+		if ( count >= 32767 )
+			count %= 32767;
+		
+		// Convert from Fibonacci to Galois configuration,
+		// shifted left 1 bit
+		s ^= (s & 1) * 0x8000;
+		
+		// Each iteration is equivalent to clocking LFSR 255 times
+		while ( (count -= 255) > 0 )
+			s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3);
+		count += 255;
+		
+		// Each iteration is equivalent to clocking LFSR 15 times
+		// (interesting similarity to single clocking below)
+		while ( (count -= 15) > 0 )
+			s ^= ((s & 2) * (3 << 13)) ^ (s >> 1);
+		count += 15;
+		
+		// Remaining singles
+		while ( --count >= 0 )
+			s = ((s & 2) * (3 << 13)) ^ (s >> 1);
+		
+		// Convert back to Fibonacci configuration
+		s &= 0x7FFF;
+	}
+	else if ( count < 8 || !optimized )
+	{
+		// won't fully replace upper 8 bits, so have to do the unoptimized way
+		while ( --count >= 0 )
+			s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2));
+	}
+	else
+	{
+		if ( count > 127 )
+		{
+			count %= 127;
+			if ( !count )
+				count = 127; // must run at least once
+		}
+		
+		// Need to keep one extra bit of history
+		s = s << 1 & 0xFF;
+		
+		// Convert from Fibonacci to Galois configuration,
+		// shifted left 2 bits
+		s ^= (s & 2) * 0x80;
+		
+		// Each iteration is equivalent to clocking LFSR 7 times
+		// (interesting similarity to single clocking below)
+		while ( (count -= 7) > 0 )
+			s ^= ((s & 4) * (3 << 5)) ^ (s >> 1);
+		count += 7;
+		
+		// Remaining singles
+		while ( --count >= 0 )
+			s = ((s & 4) * (3 << 5)) ^ (s >> 1);
+		
+		// Convert back to Fibonacci configuration and
+		// repeat last 8 bits above significant 7
+		s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F);
+	}
+	
+	return s;
+}
+#endif
+
+void Noise_run( struct Gb_Noise* this, blip_time_t time, blip_time_t end_time )
+{
+	// Determine what will be generated
+	int vol = 0;
+	struct Gb_Osc* osc = &this->osc;
+	struct Blip_Buffer* const out = osc->output;
+	if ( out )
+	{
+		int amp = osc->dac_off_amp;
+		if ( Noise_dac_enabled( this ) )
+		{
+			if ( osc->enabled )
+				vol = this->volume;
+			
+			amp = -dac_bias;
+			if ( osc->mode == mode_agb )
+				amp = -(vol >> 1);
+			
+			if ( !(osc->phase & 1) )
+			{
+				amp += vol;
+				vol = -vol;
+			}
+		}
+		
+		// AGB negates final output
+		if ( osc->mode == mode_agb )
+		{
+			vol = -vol;
+			amp    = -amp;
+		}
+		
+		Osc_update_amp( osc, time, amp );
+	}
+	
+	// Run timer and calculate time of next LFSR clock
+	static byte const period1s [8] ICONST_ATTR = { 1, 2, 4, 6, 8, 10, 12, 14 };
+	int const period1 = period1s [osc->regs [3] & 7] * clk_mul;
+	
+	#ifdef GB_APU_FAST
+		time += delay;
+	#else
+		{
+			int extra = (end_time - time) - osc->delay;
+			int const per2 = period2( this, 8 );
+			time += osc->delay + ((this->divider ^ (per2 >> 1)) & (per2 - 1)) * period1;
+			
+			int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1);
+			this->divider = (this->divider - count) & period2_mask;
+			osc->delay = count * period1 - extra;
+		}
+	#endif
+	
+	// Generate wave
+	if ( time < end_time )
+	{
+		unsigned const mask = lfsr_mask( this );
+		unsigned bits = osc->phase;
+		
+		int per = period2( this, period1 * 8 );
+		#ifdef GB_APU_FAST
+			// Noise can be THE biggest time hog; adjust as necessary
+			int const min_period = 24;
+			if ( per < min_period )
+				per = min_period;
+		#endif
+		if ( period2_index( this ) >= 0xE )
+		{
+			time = end_time;
+		}
+		else if ( !vol )
+		{
+			#ifdef GB_APU_FAST
+				time = end_time;
+			#else
+				// Maintain phase when not playing
+				int count = (end_time - time + per - 1) / per;
+				time += (blip_time_t) count * per;
+				bits = run_lfsr( bits, ~mask, count );
+			#endif
+		}
+		else
+		{
+			struct Blip_Synth* synth = osc->synth; // cache
+			
+			// Output amplitude transitions
+			int delta = -vol;
+			do
+			{
+				unsigned changed = bits + 1;
+				bits = bits >> 1 & mask;
+				if ( changed & 2 )
+				{
+					bits |= ~mask;
+					delta = -delta;
+					Synth_offset_inline( synth, time, delta, out );
+				}
+				time += per;
+			}
+			while ( time < end_time );
+			
+			if ( delta == vol )
+				osc->last_amp += delta;
+		}
+		osc->phase = bits;
+	}
+	
+	#ifdef GB_APU_FAST
+		osc->delay = time - end_time;
+	#endif
+}
+
+void Wave_run( struct Gb_Wave* this, blip_time_t time, blip_time_t end_time )
+{
+	// Calc volume
+#ifdef GB_APU_NO_AGB
+	static byte const shifts [4] = { 4+4, 0+4, 1+4, 2+4 };
+	int const volume_idx = this->regs [2] >> 5 & 3;
+	int const volume_shift = shifts [volume_idx];
+	int const volume_mul = 1;
+#else
+	static byte const volumes [8] ICONST_ATTR = { 0, 4, 2, 1, 3, 3, 3, 3 };
+	int const volume_shift = 2 + 4;
+	int const volume_idx = this->osc.regs [2] >> 5 & (this->agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB
+	int const volume_mul = volumes [volume_idx];
+#endif
+
+	// Determine what will be generated
+	int playing = false;
+	struct Gb_Osc* osc = &this->osc;
+	struct Blip_Buffer* out = osc->output;
+	if ( out )
+	{
+		int amp = osc->dac_off_amp;
+		if ( Wave_dac_enabled( this ) )
+		{
+			// Play inaudible frequencies as constant amplitude
+			amp = 8 << 4; // really depends on average of all samples in wave
+			
+			// if delay is larger, constant amplitude won't start yet
+			if ( Osc_frequency( osc ) <= 0x7FB || osc->delay > 15 * clk_mul )
+			{
+				if ( volume_mul && volume_shift != 4+4 )
+					playing = (int) osc->enabled;
+				
+				amp = (this->sample_buf << (osc->phase << 2 & 4) & 0xF0) * playing;
+			}
+			
+			amp = ((amp * volume_mul) >> volume_shift) - dac_bias;
+		}
+		Osc_update_amp( osc, time, amp );
+	}
+	
+	// Generate wave
+	time += osc->delay;
+	if ( time < end_time )
+	{
+		byte const* wave = this->wave_ram;
+		
+		// wave size and bank
+	#ifdef GB_APU_NO_AGB
+		int const wave_mask = 0x1F;
+		int const swap_banks = 0;
+	#else
+		int const size20_mask = 0x20;
+		int const flags = osc->regs [0] & this->agb_mask;
+		int const wave_mask = (flags & size20_mask) | 0x1F;
+		int swap_banks = 0;
+		if ( flags & bank40_mask )
+		{
+			swap_banks = flags & size20_mask;
+			wave += wave_bank_size/2 - (swap_banks >> 1);
+		}
+	#endif
+		
+		int ph = osc->phase ^ swap_banks;
+		ph = (ph + 1) & wave_mask; // pre-advance
+		
+		int const per = Wave_period( this );
+		if ( !playing )
+		{
+			#ifdef GB_APU_FAST
+				time = end_time;
+			#else
+				// Maintain phase when not playing
+				int count = (end_time - time + per - 1) / per;
+				ph += count; // will be masked below
+				time += (blip_time_t) count * per;
+			#endif
+		}
+		else
+		{
+			struct Blip_Synth* synth = osc->synth; // cache
+			
+			// Output amplitude transitions
+			int lamp = osc->last_amp + dac_bias;
+			do
+			{
+				// Extract nibble
+				int nibble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0;
+				ph = (ph + 1) & wave_mask;
+				
+				// Scale by volume
+				int amp = (nibble * volume_mul) >> volume_shift;
+				
+				int delta = amp - lamp;
+				if ( delta )
+				{
+					lamp = amp;
+					Synth_offset_inline( synth, time, delta, out );
+				}
+				time += per;
+			}
+			while ( time < end_time );
+			osc->last_amp = lamp - dac_bias;
+		}
+		ph = (ph - 1) & wave_mask; // undo pre-advance and mask position
+		
+		// Keep track of last byte read
+		if ( osc->enabled )
+			this->sample_buf = wave [ph >> 1];
+		
+		osc->phase = ph ^ swap_banks; // undo swapped banks
+	}
+	osc->delay = time - end_time;
+}
Index: apps/codecs/libgbs/blargg_endian.h
===================================================================
--- apps/codecs/libgbs/blargg_endian.h	(revision 0)
+++ apps/codecs/libgbs/blargg_endian.h	(revision 0)
@@ -0,0 +1,147 @@
+// CPU Byte Order Utilities
+
+// Game_Music_Emu 0.5.2
+#ifndef BLARGG_ENDIAN
+#define BLARGG_ENDIAN
+
+#include "blargg_common.h"
+
+// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16)
+#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+		defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+	#define BLARGG_CPU_X86 1
+	#define BLARGG_CPU_CISC 1
+#endif
+
+#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc)
+	#define BLARGG_CPU_POWERPC 1
+#endif
+
+// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only
+// one may be #defined to 1. Only needed if something actually depends on byte order.
+#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
+#ifdef __GLIBC__
+	// GCC handles this for us
+	#include <endian.h>
+	#if __BYTE_ORDER == __LITTLE_ENDIAN
+		#define BLARGG_LITTLE_ENDIAN 1
+	#elif __BYTE_ORDER == __BIG_ENDIAN
+		#define BLARGG_BIG_ENDIAN 1
+	#endif
+#else
+
+#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || defined (BLARGG_CPU_X86) || \
+		(defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234)
+	#define BLARGG_LITTLE_ENDIAN 1
+#endif
+
+#if defined (MSB_FIRST)     || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \
+	defined (__mips__)      || defined (__sparc__)      ||  defined (BLARGG_CPU_POWERPC) || \
+	(defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321)
+	#define BLARGG_BIG_ENDIAN 1
+#else
+	// No endian specified; assume little-endian, since it's most common
+	#define BLARGG_LITTLE_ENDIAN 1
+#endif
+#endif
+#endif
+
+#if defined (BLARGG_LITTLE_ENDIAN) && defined(BLARGG_BIG_ENDIAN)
+	#undef BLARGG_LITTLE_ENDIAN
+	#undef BLARGG_BIG_ENDIAN
+#endif
+
+static inline void blargg_verify_byte_order( void )
+{
+	#ifndef NDEBUG
+		#if BLARGG_BIG_ENDIAN
+			volatile int i = 1;
+			assert( *(volatile char*) &i == 0 );
+		#elif BLARGG_LITTLE_ENDIAN
+			volatile int i = 1;
+			assert( *(volatile char*) &i != 0 );
+		#endif
+	#endif
+}
+
+static inline unsigned get_le16( void const* p ) {
+	return  ((unsigned char const*) p) [1] * 0x100u +
+			((unsigned char const*) p) [0];
+}
+static inline unsigned get_be16( void const* p ) {
+	return  ((unsigned char const*) p) [0] * 0x100u +
+			((unsigned char const*) p) [1];
+}
+static inline blargg_ulong get_le32( void const* p ) {
+	return  ((unsigned char const*) p) [3] * 0x01000000u +
+			((unsigned char const*) p) [2] * 0x00010000u +
+			((unsigned char const*) p) [1] * 0x00000100u +
+			((unsigned char const*) p) [0];
+}
+static inline blargg_ulong get_be32( void const* p ) {
+	return  ((unsigned char const*) p) [0] * 0x01000000u +
+			((unsigned char const*) p) [1] * 0x00010000u +
+			((unsigned char const*) p) [2] * 0x00000100u +
+			((unsigned char const*) p) [3];
+}
+static inline void set_le16( void* p, unsigned n ) {
+	((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [0] = (unsigned char) n;
+}
+static inline void set_be16( void* p, unsigned n ) {
+	((unsigned char*) p) [0] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [1] = (unsigned char) n;
+}
+static inline void set_le32( void* p, blargg_ulong n ) {
+	((unsigned char*) p) [3] = (unsigned char) (n >> 24);
+	((unsigned char*) p) [2] = (unsigned char) (n >> 16);
+	((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [0] = (unsigned char) n;
+}
+static inline void set_be32( void* p, blargg_ulong n ) {
+	((unsigned char*) p) [0] = (unsigned char) (n >> 24);
+	((unsigned char*) p) [1] = (unsigned char) (n >> 16);
+	((unsigned char*) p) [2] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [3] = (unsigned char) n;
+}
+
+#if defined(BLARGG_NONPORTABLE)
+	// Optimized implementation if byte order is known
+	#if defined(BLARGG_LITTLE_ENDIAN)
+		#define GET_LE16( addr )        (*(BOOST::uint16_t*) (addr))
+		#define GET_LE32( addr )        (*(BOOST::uint32_t*) (addr))
+		#define SET_LE16( addr, data )  (void) (*(BOOST::uint16_t*) (addr) = (data))
+		#define SET_LE32( addr, data )  (void) (*(BOOST::uint32_t*) (addr) = (data))
+	#elif defined(BLARGG_BIG_ENDIAN)
+		#define GET_BE16( addr )        (*(BOOST::uint16_t*) (addr))
+		#define GET_BE32( addr )        (*(BOOST::uint32_t*) (addr))
+		#define SET_BE16( addr, data )  (void) (*(BOOST::uint16_t*) (addr) = (data))
+		#define SET_BE32( addr, data )  (void) (*(BOOST::uint32_t*) (addr) = (data))
+	#endif
+	
+	#if defined(BLARGG_CPU_POWERPC) && defined (__MWERKS__)
+		// PowerPC has special byte-reversed instructions
+		// to do: assumes that PowerPC is running in big-endian mode
+		// to do: implement for other compilers which don't support these macros
+		#define GET_LE16( addr )        (__lhbrx( (addr), 0 ))
+		#define GET_LE32( addr )        (__lwbrx( (addr), 0 ))
+		#define SET_LE16( addr, data )  (__sthbrx( (data), (addr), 0 ))
+		#define SET_LE32( addr, data )  (__stwbrx( (data), (addr), 0 ))
+	#endif
+#endif
+
+#ifndef GET_LE16
+	#define GET_LE16( addr )        get_le16( addr )
+	#define GET_LE32( addr )        get_le32( addr )
+	#define SET_LE16( addr, data )  set_le16( addr, data )
+	#define SET_LE32( addr, data )  set_le32( addr, data )
+#endif
+
+#ifndef GET_BE16
+	#define GET_BE16( addr )        get_be16( addr )
+	#define GET_BE32( addr )        get_be32( addr )
+	#define SET_BE16( addr, data )  set_be16( addr, data )
+	#define SET_BE32( addr, data )  set_be32( addr, data )
+#endif
+
+#endif
Index: apps/codecs/libgbs/gbs_emu.c
===================================================================
--- apps/codecs/libgbs/gbs_emu.c	(revision 0)
+++ apps/codecs/libgbs/gbs_emu.c	(revision 0)
@@ -0,0 +1,643 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "gbs_emu.h"
+
+#include "blargg_endian.h"
+#include "blargg_source.h"
+
+/* Copyright (C) 2003-2006 Shay Green. this module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. this
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+const char gme_wrong_file_type [] ICONST_ATTR = "Wrong file type for this emulator";
+
+int const idle_addr = 0xF00D;
+int const tempo_unit = 16;
+
+int const stereo = 2; // number of channels for stereo
+int const silence_max = 6; // seconds
+int const silence_threshold = 0x10;
+long const fade_block_size = 512;
+int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
+
+void clear_track_vars( struct Gbs_Emu* this )
+{
+	this->current_track_   = -1;
+	this->out_time         = 0;
+	this->emu_time         = 0;
+	this->emu_track_ended_ = true;
+	this->track_ended     = true;
+	this->fade_start       = LONG_MAX / 2 + 1;
+	this->fade_step        = 1;
+	this->silence_time     = 0;
+	this->silence_count    = 0;
+	this->buf_remain       = 0;
+}
+
+void Gbs_init( struct Gbs_Emu* this )
+{	
+	this->sample_rate_ = 0;
+	this->mute_mask_   = 0;
+	this->tempo_       = 1.0;
+	
+	// Unload
+	this->header.timer_mode = 0;
+	clear_track_vars( this );
+
+	this->ignore_silence     = false;
+	this->silence_lookahead = 6;
+	this->max_initial_silence = 21;
+	Sound_set_gain( this, 1.2 );
+	
+	Apu_init( &this->apu );
+	Cpu_init( &this->cpu );
+	
+	this->tempo = tempo_unit;
+	this->sound_hardware = sound_gbs;
+	
+	// Reduce apu sound clicks?
+	Apu_reduce_clicks( &this->apu, true );
+
+	// Clear m3u playlist
+	M3u_clear( &this->m3u );
+}
+
+static blargg_err_t check_gbs_header( void const* header )
+{
+	if ( memcmp( header, "GBS", 3 ) )
+		return gme_wrong_file_type;
+	return 0;
+}
+
+// Setup
+
+blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size )
+{
+	// Unload
+	this->header.timer_mode = 0;
+	this->voice_count_ = 0;
+	clear_track_vars( this );
+
+	assert( offsetof (struct header_t,copyright [32]) == header_size );
+	RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) );
+	
+	RETURN_ERR( check_gbs_header( &this->header ) );
+	
+	/* Ignore warnings? */
+	/*if ( header_.vers != 1 )
+		warning( "Unknown file version" );
+	
+	if ( header_.timer_mode & 0x78 )
+		warning( "Invalid timer mode" ); */
+	
+	/* unsigned load_addr = get_le16( this->header.load_addr ); */
+	/* if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F ||
+			load_addr < 0x400 )
+		warning( "Invalid load/init/play address" ); */
+		
+	unsigned load_addr = get_le16( this->header.load_addr );
+	/* if ( (this->header.load_addr [1] | this->header.init_addr [1] | this->header.play_addr [1]) > 0x7F ||
+			load_addr < 0x400 )
+		warning( "Invalid load/init/play address" ); */
+		
+	this->cpu.rst_base = load_addr;
+	Rom_set_addr( &this->rom, load_addr );
+	
+	this->voice_count_ = osc_count;
+	Apu_volume( &this->apu, this->gain_ );
+	
+	// Change clock rate & setup buffer
+	this->clock_rate_ = 4194304;
+	Buffer_clock_rate( &this->buf, 4194304 );
+	this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
+	
+	// Post load
+	Sound_set_tempo( this, this->tempo_ );
+	
+	// Remute voices
+	Sound_mute_voices( this, this->mute_mask_ );
+	
+	// Reset track count
+	this->track_count = this->header.track_count;
+	
+	// Clear m3u playlist
+	M3u_clear( &this->m3u );
+	return 0;
+}
+
+// Emulation
+
+// see gb_cpu_io.h for read/write functions
+
+void Set_bank( struct Gbs_Emu* this, int n )
+{
+	addr_t addr = mask_addr( n * bank_size, this->rom.mask );
+	if ( addr == 0 && this->rom.size > bank_size )
+		addr = bank_size; // MBC1&2 behavior, bank 0 acts like bank 1
+	Cpu_map_code( &this->cpu, bank_size, bank_size, Rom_at_addr( &this->rom, addr ) );
+}
+
+void Update_timer( struct Gbs_Emu* this )
+{
+	this->play_period = 70224 / tempo_unit; /// 59.73 Hz
+	
+	if ( this->header.timer_mode & 0x04 ) 
+	{ 
+		// Using custom rate
+		static byte const rates [4] = { 6, 0, 2, 4 };
+		// TODO: emulate double speed CPU mode rather than halving timer rate
+		int double_speed = this->header.timer_mode >> 7;
+		int shift = rates [this->ram [hi_page + 7] & 3] - double_speed;
+		this->play_period = (256 - this->ram [hi_page + 6]) << shift;
+	}
+	
+	this->play_period *= this->tempo;
+}
+
+// Jumps to routine, given pointer to address in file header. Pushes idle_addr
+// as return address, NOT old PC.
+void Jsr_then_stop( struct Gbs_Emu* this, byte const addr [] )
+{
+	check( this->cpu.r.sp == get_le16( this->header.stack_ptr ) );
+	this->cpu.r.pc = get_le16( addr );
+	Write_mem( this, --this->cpu.r.sp, idle_addr >> 8 );
+	Write_mem( this, --this->cpu.r.sp, idle_addr      );
+}
+
+blargg_err_t Run_until( struct Gbs_Emu* this, int end )
+{
+	this->end_time = end;
+	Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) - end );
+	while ( true )
+	{
+		Run_cpu( this );
+		if ( Cpu_time( &this->cpu ) >= 0 )
+			break;
+		
+		if ( this->cpu.r.pc == idle_addr )
+		{
+			if ( this->next_play > this->end_time )
+			{
+				Cpu_set_time( &this->cpu, 0 );
+				break;
+			}
+			
+			if ( Cpu_time( &this->cpu ) < this->next_play - this->end_time )
+				Cpu_set_time( &this->cpu, this->next_play - this->end_time );
+			this->next_play += this->play_period;
+			Jsr_then_stop( this, this->header.play_addr );
+		}
+		else if ( this->cpu.r.pc > 0xFFFF )
+		{
+			/* warning( "PC wrapped around\n" ); */
+			this->cpu.r.pc &= 0xFFFF;
+		}
+		else
+		{
+			/* warning( "Emulation error (illegal/unsupported instruction)" ); */
+			this->cpu.r.pc = (this->cpu.r.pc + 1) & 0xFFFF;
+			Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) + 6 );
+		}
+	}
+	
+	return 0;
+}
+
+blargg_err_t End_frame( struct Gbs_Emu* this, int end )
+{
+	RETURN_ERR( Run_until( this, end ) );
+	
+	this->next_play -= end;
+	if ( this->next_play < 0 ) // happens when play routine takes too long
+	{
+		#if !defined(GBS_IGNORE_STARVED_PLAY)
+			check( false );
+		#endif
+		this->next_play = 0;
+	}
+	
+	Apu_end_frame( &this->apu, end );
+	
+	return 0;
+}
+
+blargg_err_t Run_clocks( struct Gbs_Emu* this, blip_time_t duration )
+{
+	return End_frame( this, duration );
+}
+
+blargg_err_t play_( struct Gbs_Emu* this, long count, sample_t* out )
+{
+	long remain = count;
+	while ( remain )
+	{
+		remain -= Buffer_read_samples( &this->buf, &out [count - remain], remain );
+		if ( remain )
+		{
+			if ( this->buf_changed_count != Buffer_channels_changed_count( &this->buf ) )
+			{
+				this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
+				
+				// Remute voices
+				Sound_mute_voices( this, this->mute_mask_ );
+			}
+			int msec = Buffer_length( &this->buf );
+			blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000;
+			RETURN_ERR( Run_clocks( this, clocks_emulated ) );
+			assert( clocks_emulated );
+			Buffer_end_frame( &this->buf, clocks_emulated );
+		}
+	}
+	return 0;
+}
+
+blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long rate )
+{
+	require( !this->sample_rate_ ); // sample rate can't be changed once set
+	Buffer_init( &this->buf );
+	RETURN_ERR( Buffer_set_sample_rate( &this->buf, rate, 1000 / 20 ) );
+	
+	// Set bass frequency
+	Buffer_bass_freq( &this->buf, 300 );
+	
+	this->sample_rate_ = rate;
+	return 0;
+}
+
+
+// Sound
+
+void Sound_mute_voice( struct Gbs_Emu* this, int index, bool mute )
+{
+	require( (unsigned) index < (unsigned) this->voice_count_ );
+	int bit = 1 << index;
+	int mask = this->mute_mask_ | bit;
+	if ( !mute )
+		mask ^= bit;
+	Sound_mute_voices( this, mask );
+}
+
+void Sound_mute_voices( struct Gbs_Emu* this, int mask )
+{
+	require( this->sample_rate_ ); // sample rate must be set first
+	this->mute_mask_ = mask;
+	
+	int i;
+	for ( i = this->voice_count_; i--; )
+	{
+		if ( mask & (1 << i) )
+		{
+			Apu_set_output( &this->apu, i, 0, 0, 0 );
+		}
+		else
+		{
+			struct channel_t ch = Buffer_channel( &this->buf );
+			assert( (ch.center && ch.left && ch.right) ||
+					(!ch.center && !ch.left && !ch.right) ); // all or nothing
+			Apu_set_output( &this->apu, i, ch.center, ch.left, ch.right );
+		}
+	}
+}
+
+void Sound_set_tempo( struct Gbs_Emu* this, double t )
+{
+	require( this->sample_rate_ ); // sample rate must be set first
+	double const min = 0.02;
+	double const max = 4.00;
+	if ( t < min ) t = min;
+	if ( t > max ) t = max;
+	this->tempo_ = t;
+		
+	this->tempo = (int) (tempo_unit / t + 0.5 );
+	Apu_set_tempo( &this->apu, t );
+	Update_timer( this );
+}
+
+void fill_buf( struct Gbs_Emu* this );
+blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
+{
+	clear_track_vars( this );
+	
+	// Remap track if playlist available
+	if ( this->m3u.size > 0 ) {
+		struct entry_t* e = &this->m3u.entries[track];
+		if ( e->track >= 0 )
+		{
+			track = e->track;
+			/* TODO: Is this necessary?*/
+			/* if ( !(this->m3u.type_->flags_ & 0x02) )
+				track -= e.decimal_track; */
+		}
+		
+		if ( track >= this->header.track_count )
+			track = 0;
+	}
+	
+	this->current_track_ = track;
+	
+	Buffer_clear( &this->buf );
+	
+	// Reset APU to state expected by most rips
+	static byte const sound_data [] ICONST_ATTR = {
+		0x80, 0xBF, 0x00, 0x00, 0xB8, // square 1 DAC disabled
+		0x00, 0x3F, 0x00, 0x00, 0xB8, // square 2 DAC disabled
+		0x7F, 0xFF, 0x9F, 0x00, 0xB8, // wave     DAC disabled
+		0x00, 0xFF, 0x00, 0x00, 0xB8, // noise    DAC disabled
+		0x77, 0xFF, 0x80, // max volume, all chans in center, power on
+	};
+	
+	enum sound_t mode = this->sound_hardware;
+	if ( mode == sound_gbs )
+		mode = (this->header.timer_mode & 0x80) ? sound_cgb : sound_dmg;
+		
+	Apu_reset( &this->apu, (enum gb_mode_t) mode, false );
+	Apu_write_register( &this->apu, 0, 0xFF26, 0x80 ); // power on
+	int i;
+	for ( i = 0; i < (int) sizeof sound_data; i++ )
+		Apu_write_register( &this->apu, 0, i + io_addr, sound_data [i] );
+	Apu_end_frame( &this->apu, 1 ); // necessary to get click out of the way */
+	
+	memset( this->ram, 0, 0x4000 );
+	memset( this->ram + 0x4000, 0xFF, 0x1F80 );
+	memset( this->ram + 0x5F80, 0, sizeof this->ram - 0x5F80 );
+	this->ram [hi_page] = 0; // joypad reads back as 0
+	this->ram [idle_addr - ram_addr] = 0xED; // illegal instruction
+	this->ram [hi_page + 6] = this->header.timer_modulo;
+	this->ram [hi_page + 7] = this->header.timer_mode;
+	
+	Cpu_reset( &this->cpu, this->rom.unmapped );
+	Cpu_map_code( &this->cpu, ram_addr, 0x10000 - ram_addr, this->ram );
+	Cpu_map_code( &this->cpu, 0, bank_size, Rom_at_addr( &this->rom, 0 ) );
+	Set_bank( this, this->rom.size > bank_size );
+	
+	Update_timer( this );
+	this->next_play = this->play_period;
+	this->cpu.r.rp.fa  = track;
+	this->cpu.r.sp = get_le16( this->header.stack_ptr );
+	this->cpu_time  = 0;
+	Jsr_then_stop( this, this->header.init_addr );
+	
+	this->emu_track_ended_ = false;
+	this->track_ended     = false;
+	
+	if ( !this->ignore_silence )
+	{
+		// play until non-silence or end of track
+		long end;
+		for ( end = this->max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; )
+		{
+			fill_buf( this );
+			if ( this->buf_remain | (int) this->emu_track_ended_ )
+				break;
+		}
+		
+		this->emu_time      = this->buf_remain;
+		this->out_time      = 0;
+		this->silence_time  = 0;
+		this->silence_count = 0;
+	}
+	/* return track_ended() ? warning() : 0; */
+	return 0;
+}
+
+
+// Track
+
+blargg_long msec_to_samples( blargg_long msec, long sample_rate )
+{
+	blargg_long sec = msec / 1000;
+	msec -= sec * 1000;
+	return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
+}
+
+long Track_tell( struct Gbs_Emu* this ) 
+{
+	blargg_long rate = this->sample_rate_ * stereo;
+	blargg_long sec = this->out_time / rate;
+	return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+}
+
+blargg_err_t Track_seek( struct Gbs_Emu* this, long msec )
+{
+	blargg_long time = msec_to_samples( msec, this->sample_rate_ );
+	if ( time < this->out_time )
+		RETURN_ERR( Gbs_start_track( this, this->current_track_ ) );
+	return Track_skip( this, time - this->out_time );
+}
+
+blargg_err_t skip_( struct Gbs_Emu* this, long count )
+{
+	// for long skip, mute sound
+	const long threshold = 30000;
+	if ( count > threshold )
+	{
+		int saved_mute = this->mute_mask_;
+		Sound_mute_voices( this, ~0 );
+		
+		while ( count > threshold / 2 && !this->emu_track_ended_ )
+		{
+			RETURN_ERR( play_( this, buf_size, this->buf_ ) );
+			count -= buf_size;
+		}
+		
+		Sound_mute_voices( this, saved_mute );
+	}
+	
+	while ( count && !this->emu_track_ended_ )
+	{
+		long n = buf_size;
+		if ( n > count )
+			n = count;
+		count -= n;
+		RETURN_ERR( play_( this, n, this->buf_ ) );
+	}
+	return 0;
+}
+
+blargg_err_t Track_skip( struct Gbs_Emu* this, long count )
+{
+	require( this->current_track_ >= 0 ); // start_track() must have been called already
+	this->out_time += count;
+	
+	// remove from silence and buf first
+	{
+		long n = min( count, this->silence_count );
+		this->silence_count -= n;
+		count -= n;
+		
+		n = min( count, this->buf_remain );
+		this->buf_remain -= n;
+		count -= n;
+	}
+		
+	if ( count && !this->emu_track_ended_ )
+	{
+		this->emu_time += count;
+		// End track if error
+		if ( skip_( this, count ) )
+			this->emu_track_ended_ = true;
+	}
+	
+	if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
+		this->track_ended |= this->emu_track_ended_;
+	
+	return 0;
+}
+
+// Fading
+
+void Track_set_fade( struct Gbs_Emu* this, long start_msec, long length_msec )
+{
+	this->fade_step = this->sample_rate_ * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
+	this->fade_start = msec_to_samples( start_msec, this->sample_rate_ );
+}
+
+// unit / pow( 2.0, (double) x / step )
+static int int_log( blargg_long x, int step, int unit )
+{
+	int shift = x / step;
+	int fraction = (x - shift * step) * unit / step;
+	return ((unit - fraction) + (fraction >> 1)) >> shift;
+}
+
+void handle_fade( struct Gbs_Emu* this, long out_count, sample_t* out )
+{
+	int i;
+	for ( i = 0; i < out_count; i += fade_block_size )
+	{
+		int const shift = 14;
+		int const unit = 1 << shift;
+		int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
+				this->fade_step, unit );
+		if ( gain < (unit >> fade_shift) )
+			this->track_ended = this->emu_track_ended_ = true;
+		
+		sample_t* io = &out [i];
+		int count;
+		for ( count = min( fade_block_size, out_count - i ); count; --count )
+		{
+			*io = (sample_t) ((*io * gain) >> shift);
+			++io;
+		}
+	}
+}
+
+// Silence detection
+
+void emu_play( struct Gbs_Emu* this, long count, sample_t* out )
+{
+	check( current_track_ >= 0 );
+	this->emu_time += count;
+	if ( this->current_track_ >= 0 && !this->emu_track_ended_ ) {
+		// End track if error
+		if ( play_( this, count, out ) ) this->emu_track_ended_ = true; 
+	}
+	else
+		memset( out, 0, count * sizeof *out );
+}
+
+// number of consecutive silent samples at end
+static long count_silence( sample_t* begin, long size )
+{
+	sample_t first = *begin;
+	*begin = silence_threshold; // sentinel
+	sample_t* p = begin + size;
+	while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
+	*begin = first;
+	return size - (p - begin);
+}
+
+// fill internal buffer and check it for silence
+void fill_buf( struct Gbs_Emu* this )
+{
+	assert( !this->buf_remain );
+	if ( !this->emu_track_ended_ )
+	{
+		emu_play( this, buf_size, this->buf_ );
+		long silence = count_silence( this->buf_, buf_size );
+		if ( silence < buf_size )
+		{
+			this->silence_time = this->emu_time - silence;
+			this->buf_remain   = buf_size;
+			return;
+		}
+	}
+	this->silence_count += buf_size;
+}
+
+blargg_err_t Gbs_play( struct Gbs_Emu* this, long out_count, sample_t* out )
+{
+	if ( this->track_ended )
+	{
+		memset( out, 0, out_count * sizeof *out );
+	}
+	else
+	{
+		require( this->current_track_ >= 0 );
+		require( out_count % stereo == 0 );
+		
+		assert( this->emu_time >= this->out_time );
+		
+		long pos = 0;
+		if ( this->silence_count )
+		{
+			// during a run of silence, run emulator at >=2x speed so it gets ahead
+			long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time;
+			while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
+				fill_buf( this );
+			
+			// fill with silence
+			pos = min( this->silence_count, out_count );
+			memset( out, 0, pos * sizeof *out );
+			this->silence_count -= pos;
+			
+			if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate_ )
+			{
+				this->track_ended  = this->emu_track_ended_ = true;
+				this->silence_count = 0;
+				this->buf_remain    = 0;
+			}
+		}
+		
+		if ( this->buf_remain )
+		{
+			// empty silence buf
+			long n = min( this->buf_remain, out_count - pos );
+			memcpy( &out [pos], this->buf_ + (buf_size - this->buf_remain), n * sizeof *out );
+			this->buf_remain -= n;
+			pos += n;
+		}
+		
+		// generate remaining samples normally
+		long remain = out_count - pos;
+		if ( remain )
+		{
+			emu_play( this, remain, out + pos );
+			this->track_ended |= this->emu_track_ended_;
+			
+			if ( !this->ignore_silence || this->out_time > this->fade_start )
+			{
+				// check end for a new run of silence
+				long silence = count_silence( out + pos, remain );
+				if ( silence < remain )
+					this->silence_time = this->emu_time - silence;
+				
+				if ( this->emu_time - this->silence_time >= buf_size )
+					fill_buf( this ); // cause silence detection on next play()
+			}
+		}
+		
+		if ( this->out_time > this->fade_start )
+			handle_fade( this, out_count, out );
+	}
+	this->out_time += out_count;
+	return 0;
+}
Index: apps/codecs/libgbs/gbs_cpu.c
===================================================================
--- apps/codecs/libgbs/gbs_cpu.c	(revision 0)
+++ apps/codecs/libgbs/gbs_cpu.c	(revision 0)
@@ -0,0 +1,133 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "gbs_emu.h"
+
+#include "blargg_endian.h"
+
+//#include "gb_cpu_log.h"
+
+/* Copyright (C) 2003-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifndef LOG_MEM
+	#define LOG_MEM( addr, str, data ) data
+#endif
+
+int Read_mem( struct Gbs_Emu* this, addr_t addr )
+{
+	int result = *Cpu_get_code( &this->cpu, addr );
+	if ( (unsigned) (addr - io_addr) < io_size )
+		result = Apu_read_register( &this->apu, time( this ), addr );
+	
+#ifndef NDEBUG
+	else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
+		; /* dprintf( "Unmapped read $%04X\n", (unsigned) addr ); */
+	else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 && addr != 0xFF70 && addr != 0xFF05 )
+		; /* dprintf( "Unmapped read $%04X\n", (unsigned) addr ); */
+#endif
+
+	return LOG_MEM( addr, ">", result );
+}
+
+inline void Write_io_inline( struct Gbs_Emu* this, int offset, int data, int base )
+{
+	if ( (unsigned) (offset - (io_addr - base)) < io_size )
+		Apu_write_register( &this->apu, time( this ), offset + base, data & 0xFF );
+	else if ( (unsigned) (offset - (0xFF06 - base)) < 2 )
+		Update_timer( this );
+	else if ( offset == io_base - base )
+		this->ram [base - ram_addr + offset] = 0; // keep joypad return value 0
+	else
+		this->ram [base - ram_addr + offset] = 0xFF;
+	
+	//if ( offset == 0xFFFF - base )
+	//  dprintf( "Wrote interrupt mask\n" );
+}
+
+void Write_mem( struct Gbs_Emu* this, addr_t addr, int data )
+{
+	(void) LOG_MEM( addr, "<", data );
+	
+	int offset = addr - ram_addr;
+	if ( (unsigned) offset < 0x10000 - ram_addr )
+	{
+		this->ram [offset] = data;
+		
+		offset -= 0xE000 - ram_addr;
+		if ( (unsigned) offset < 0x1F80 )
+			Write_io_inline( this, offset, data, 0xE000 );
+	}
+	else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 )
+	{
+		Set_bank( this, data & 0xFF );
+	}
+#ifndef NDEBUG
+	else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
+	{
+		/* dprintf( "Unmapped write $%04X\n", (unsigned) addr ); */
+	}
+#endif
+}
+
+void Write_io_( struct Gbs_Emu* this, int offset, int data )
+{
+	Write_io_inline( this, offset, data, io_base );
+}
+
+inline void Write_io( struct Gbs_Emu* this, int offset, int data )
+{
+	(void) LOG_MEM( offset + io_base, "<", data );
+	
+	this->ram [io_base - ram_addr + offset] = data;
+	if ( (unsigned) offset < 0x80 )
+		Write_io_( this, offset, data );
+}
+
+int Read_io( struct Gbs_Emu* this, int offset )
+{
+	int const io_base = 0xFF00;
+	int result = this->ram [io_base - ram_addr + offset];
+	
+	if ( (unsigned) (offset - (io_addr - io_base)) < io_size )
+	{
+		result = Apu_read_register( &this->apu, time( this ), offset + io_base );
+		(void) LOG_MEM( offset + io_base, ">", result );
+	}
+	else
+	{
+		check( result == read_mem( offset + io_base ) );
+	}
+	return result;
+}
+
+#define READ_FAST( emu, addr, out ) \
+{\
+	out = READ_CODE( addr );\
+	if ( (unsigned) (addr - io_addr) < io_size )\
+		out = LOG_MEM( addr, ">", Apu_read_register( &emu->apu, TIME() + emu->end_time, addr ) );\
+	else\
+		check( out == Read_mem( emu, addr ) );\
+}
+
+#define READ_MEM(  emu, addr       ) Read_mem( emu, addr )
+#define WRITE_MEM( emu, addr, data ) Write_mem( emu, addr, data )
+
+#define WRITE_IO( emu, addr, data )  Write_io( emu, addr, data )
+#define READ_IO( emu, addr, out )    out = Read_io( emu, addr )
+
+#define CPU_BEGIN \
+void Run_cpu( struct Gbs_Emu* this )\
+{ \
+	struct Gb_Cpu* cpu = &this->cpu;
+	#include "gb_cpu_run.h"
+}
Index: apps/codecs/libgbs/multi_buffer.c
===================================================================
--- apps/codecs/libgbs/multi_buffer.c	(revision 0)
+++ apps/codecs/libgbs/multi_buffer.c	(revision 0)
@@ -0,0 +1,226 @@
+// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
+
+#include "multi_buffer.h"
+
+/* Copyright (C) 2003-2006 Shay Green. this module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. this
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+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;
+}
+
+blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long rate, int msec )
+{
+	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;
+}
+
+void Buffer_clock_rate( struct Stereo_Buffer* this, long rate )
+{
+	int i;
+	for ( i = 0; i < buf_count; i++ )
+		Blip_set_clock_rate( &this->bufs [i], rate );
+}
+
+void Buffer_bass_freq( struct Stereo_Buffer* this, int bass )
+{
+	unsigned i;
+	for ( i = 0; i < buf_count; i++ )
+		Blip_bass_freq( &this->bufs [i], bass );
+}
+
+struct channel_t Buffer_channel( struct Stereo_Buffer* this )
+{
+	return this->chan;
+}
+
+void Buffer_clear( struct Stereo_Buffer* this )
+{
+	this->stereo_added = 0;
+	this->was_stereo   = false;
+	int i;
+	for ( i = 0; i < buf_count; i++ )
+		Blip_clear( &this->bufs [i], 1 );
+}
+
+void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t clock_count )
+{
+	this->stereo_added = 0;
+	unsigned i;
+	for ( i = 0; i < buf_count; i++ )
+	{
+		this->stereo_added |= Blip_clear_modified( &this->bufs [i] ) << i;
+		Blip_end_frame( &this->bufs [i], clock_count );
+	}
+}
+
+long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t* out, long 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 )
+	{
+		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] ) )
+		{
+			this->was_stereo   = this->stereo_added;
+			this->stereo_added = 0;
+		}
+	}
+	
+	return count * 2;
+}
+
+unsigned Buffer_channels_changed_count( struct Stereo_Buffer* this )
+{
+	return this->channels_changed_count_;
+}
+
+void Buffer_channels_changed( struct Stereo_Buffer* this )
+{
+	this->channels_changed_count_++;
+}
+
+void Buffer_mix_stereo( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+{
+	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] );
+}
+
+void Buffer_mix_stereo_no_center( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+{
+	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] );
+}
+
+void Buffer_mix_mono( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+{
+	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 )
+	{
+		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;
+	}
+	
+	BLIP_READER_END( center, this->bufs [0] );
+}
Index: apps/codecs/libgbs/blargg_source.h
===================================================================
--- apps/codecs/libgbs/blargg_source.h	(revision 0)
+++ apps/codecs/libgbs/blargg_source.h	(revision 0)
@@ -0,0 +1,66 @@
+// Included at the beginning of library source files, after all other #include lines
+#ifndef BLARGG_SOURCE_H
+#define BLARGG_SOURCE_H
+
+// If debugging is enabled, abort program if expr is false. Meant for checking
+// internal state and consistency. A failed assertion indicates a bug in the module.
+// void assert( bool expr );
+#include <assert.h>
+
+// If debugging is enabled and expr is false, abort program. Meant for checking
+// caller-supplied parameters and operations that are outside the control of the
+// module. A failed requirement indicates a bug outside the module.
+// void require( bool expr );
+#undef require
+#define require( expr ) assert( expr )
+
+// Like printf() except output goes to debug log file. Might be defined to do
+// nothing (not even evaluate its arguments).
+// void dprintf( const char* format, ... );
+#if defined(ROCKBOX)
+#define dprintf DEBUGF
+#else
+static inline void blargg_dprintf_( const char* fmt, ... ) { }
+#undef dprintf
+#define dprintf (1) ? (void) 0 : blargg_dprintf_
+#endif
+
+// If enabled, evaluate expr and if false, make debug log entry with source file
+// and line. Meant for finding situations that should be examined further, but that
+// don't indicate a problem. In all cases, execution continues normally.
+#undef check
+#define check( expr ) ((void) 0)
+
+// If expr yields error string, return it from current function, otherwise continue.
+#undef RETURN_ERR
+#define RETURN_ERR( expr ) do {                         \
+		blargg_err_t blargg_return_err_ = (expr);               \
+		if ( blargg_return_err_ ) return blargg_return_err_;    \
+	} while ( 0 )
+
+// If ptr is 0, return out of memory error string.
+#undef CHECK_ALLOC
+#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
+
+#ifndef max
+	#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#ifndef min
+	#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+// TODO: good idea? bad idea?
+#undef byte
+#define byte byte_
+typedef unsigned char byte;
+
+// deprecated
+#define BLARGG_CHECK_ALLOC CHECK_ALLOC
+#define BLARGG_RETURN_ERR RETURN_ERR
+
+// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check
+#ifdef BLARGG_SOURCE_BEGIN
+	#include BLARGG_SOURCE_BEGIN
+#endif
+
+#endif
Index: apps/codecs/libgbs/blip_buffer.h
===================================================================
--- apps/codecs/libgbs/blip_buffer.h	(revision 0)
+++ apps/codecs/libgbs/blip_buffer.h	(revision 0)
@@ -0,0 +1,270 @@
+// Band-limited sound synthesis buffer
+
+// Blip_Buffer 0.4.1
+#ifndef BLIP_BUFFER_H
+#define BLIP_BUFFER_H
+
+#include <assert.h>
+
+	// internal
+	#include "blargg_common.h"
+	#if INT_MAX >= 0x7FFFFFFF
+		typedef int blip_long;
+		typedef unsigned blip_ulong;
+	#else
+		typedef long blip_long;
+		typedef unsigned long blip_ulong;
+	#endif
+
+// Time unit at source clock rate
+typedef blip_long blip_time_t;
+
+// Number of bits in resample ratio fraction. Higher values give a more accurate ratio
+// but reduce maximum buffer size.
+#ifndef BLIP_BUFFER_ACCURACY
+	#define BLIP_BUFFER_ACCURACY 16
+#endif
+
+// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
+// noticeable broadband noise when synthesizing high frequency square waves.
+// Affects size of Blip_Synth objects since they store the waveform directly.
+#ifndef BLIP_PHASE_BITS
+	#define BLIP_PHASE_BITS 8
+#endif
+
+// Output samples are 16-bit signed, with a range of -32768 to 32767
+typedef short blip_sample_t;
+enum { blip_sample_max = 32767 };
+enum { blip_widest_impulse_ = 16 };
+enum { blip_buffer_extra_ = blip_widest_impulse_ + 2 };
+enum { blip_res = 1 << BLIP_PHASE_BITS };
+enum { blip_max_length = 0 };
+enum { blip_default_length = 250 };
+
+// Maximun buffer size (48Khz, 50 ms)
+enum { blip_buffer_max = 2466 };
+enum { blip_sample_bits = 30 };
+
+typedef blip_time_t buf_t_;
+/* typedef const char* blargg_err_t; */
+typedef blip_ulong blip_resampled_time_t;
+
+struct Blip_Buffer {
+	blip_ulong factor_;
+	blip_resampled_time_t offset_;
+	buf_t_ buffer_ [blip_buffer_max];
+	blip_long buffer_size_;
+	blip_long reader_accum_;
+	int bass_shift_;
+
+	long sample_rate_;
+	long clock_rate_;
+	int bass_freq_;
+	int length_;
+	int modified_;
+};
+
+// not documented yet
+void Blip_set_modified( struct Blip_Buffer* this ); ICODE_ATTR
+int Blip_clear_modified( struct Blip_Buffer* this ); ICODE_ATTR
+void Blip_remove_silence( struct Blip_Buffer* this, long count ); ICODE_ATTR
+blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t ); ICODE_ATTR
+blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t ); ICODE_ATTR
+blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, long clock_rate ); ICODE_ATTR
+
+// Initializes Blip_Buffer structure
+void Blip_init( struct Blip_Buffer* this );
+
+// Stops (clear) Blip_Buffer structure
+void Blip_stop( struct Blip_Buffer* this );
+
+// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
+// to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there
+// isn't enough memory, returns error without affecting current buffer setup.
+blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, long samples_per_sec, int msec_length );
+
+// Set number of source time units per second
+static inline void Blip_set_clock_rate( struct Blip_Buffer* this, long cps )
+{
+	this->factor_ = Blip_clock_rate_factor( this, this->clock_rate_ = cps );
+}
+
+// End current time frame of specified duration and make its samples available
+// (along with any still-unread samples) for reading with read_samples(). Begins
+// a new time frame at the end of the current frame.
+void Blip_end_frame( struct Blip_Buffer* this, blip_time_t time ); ICODE_ATTR
+
+// Read at most 'max_samples' out of buffer into 'dest', removing them from from
+// the buffer. Returns number of samples actually read and removed. If stereo is
+// true, increments 'dest' one extra time after writing each sample, to allow
+// easy interleving of two channels into a stereo output buffer.
+long Blip_read_samples( struct Blip_Buffer* this, blip_sample_t* dest, long max_samples, int stereo ); ICODE_ATTR
+
+// Additional optional features
+
+// Current output sample rate
+static inline long Blip_sample_rate( struct Blip_Buffer* this )
+{
+	return this->sample_rate_;
+}
+
+// Length of buffer, in milliseconds
+static inline int  Blip_length( struct Blip_Buffer* this )
+{
+	return this->length_;
+}
+
+// Number of source time units per second
+static inline long Blip_clock_rate( struct Blip_Buffer* this )
+{
+	return this->clock_rate_;
+}
+
+
+// Set frequency high-pass filter frequency, where higher values reduce bass more
+void Blip_bass_freq( struct Blip_Buffer* this, int frequency );
+
+// Number of samples delay from synthesis to samples read out
+static inline int  Blip_output_latency( void )
+{ 
+	return blip_widest_impulse_ / 2;
+}
+
+// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
+// false, just clears out any samples waiting rather than the entire buffer.
+void Blip_clear( struct Blip_Buffer* this, int entire_buffer );
+
+// Number of samples available for reading with read_samples()
+static inline long Blip_samples_avail( struct Blip_Buffer* this )
+{ 
+	return (long) (this->offset_ >> BLIP_BUFFER_ACCURACY);
+}
+
+// Remove 'count' samples from those waiting to be read
+void Blip_remove_samples( struct Blip_Buffer* this, long count ); ICODE_ATTR
+
+// Experimental features
+
+// Count number of clocks needed until 'count' samples will be available.
+// If buffer can't even hold 'count' samples, returns number of clocks until
+// buffer becomes full.
+blip_time_t Blip_count_clocks( struct Blip_Buffer* this, long count ); ICODE_ATTR
+
+// Number of raw samples that can be mixed within frame of specified duration.
+long Blip_count_samples( struct Blip_Buffer* this, blip_time_t duration ); ICODE_ATTR
+
+// Mix 'count' samples from 'buf' into buffer.
+void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* buf, long count ); ICODE_ATTR
+
+// Range specifies the greatest expected change in amplitude. Calculate it
+// by finding the difference between the maximum and minimum expected
+// amplitudes (max - min).
+
+struct Blip_Synth {
+	struct Blip_Buffer* buf;
+	int last_amp;
+	int delta_factor;
+};
+
+// Initializes Blip_Synth structure
+void Synth_init( struct Blip_Synth* this );
+
+// Set overall volume of waveform
+void Synth_volume( struct Blip_Synth* this, double v ); ICODE_ATTR
+/* In case we want to port the Nes emu */
+/* void Synth_volume( struct Blip_Synth *this, double v, int range ); */
+
+// Get/set Blip_Buffer used for output
+const struct Blip_Buffer* Synth_output( struct Blip_Synth* this ); ICODE_ATTR
+
+// Low-level interface
+
+	#if defined (__GNUC__) || _MSC_VER >= 1100
+		#define BLIP_RESTRICT __restrict
+	#else
+		#define BLIP_RESTRICT
+	#endif
+
+// Works directly in terms of fractional output samples. Contact author for more info.
+static inline void Synth_offset_resampled( struct Blip_Synth* this, blip_resampled_time_t time,
+	int delta, struct Blip_Buffer* blip_buf )
+{
+	// Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
+	// need for a longer buffer as set by set_sample_rate().
+	assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );
+	delta *= this->delta_factor;
+	blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
+	int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));
+
+	blip_long left = buf [0] + delta;
+
+	// Kind of crappy, but doing shift after multiply results in overflow.
+	// Alternate way of delaying multiply by delta_factor results in worse
+	// sub-sample resolution.
+	blip_long right = (delta >> BLIP_PHASE_BITS) * phase;
+	left  -= right;
+	right += buf [1];
+
+	buf [0] = left;
+	buf [1] = right;
+}
+
+// Update amplitude of waveform at given time. Using this requires a separate
+// Blip_Synth for each waveform.
+static inline void Synth_update( struct Blip_Synth* this, blip_time_t t, int amp )
+{
+	int delta = amp - this->last_amp;
+	this->last_amp = amp;
+	Synth_offset_resampled( this, t * this->buf->factor_ + this->buf->offset_, delta, this->buf );
+}
+
+// Add an amplitude transition of specified delta, optionally into specified buffer
+// rather than the one set with output(). Delta can be positive or negative.
+// The actual change in amplitude is delta * (volume / range)
+static inline void Synth_offset( struct Blip_Synth* this, blip_time_t t, int delta, struct Blip_Buffer* buf )
+{
+	Synth_offset_resampled( this, t * buf->factor_ + buf->offset_, delta, buf );
+}
+/* void Synth_offset( struct Blip_Synth* this, blip_time_t t, int delta ) {
+	Synth_offset( this, t, delta, this->buf ); } */
+
+// Same as offset(), except code is inlined for higher performance
+static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t, int delta, struct Blip_Buffer* buf )
+{
+	Synth_offset_resampled( this, t * buf->factor_ + buf->offset_, delta, buf );
+}
+/* void offset_inline( struct Blip_Synth* this, blip_time_t t, int delta ) const {
+	offset_resampled( t * this->buf->factor_ + this->buf->offset_, delta, this->buf );
+} */
+
+
+// Optimized reading from Blip_Buffer, for use in custom sample output
+
+// Begin reading from buffer. Name should be unique to the current block.
+#define BLIP_READER_BEGIN( name, blip_buffer ) \
+	buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
+	blip_long name##_reader_accum = (blip_buffer).reader_accum_
+
+// Get value to pass to BLIP_READER_NEXT()
+#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_)
+
+// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
+// code at the cost of having no bass control
+/* static int blip_reader_default_bass = 9; */
+
+// Current sample
+#define BLIP_READER_READ( name )        (name##_reader_accum >> (blip_sample_bits - 16))
+
+// Current raw sample in full internal resolution
+#define BLIP_READER_READ_RAW( name )    (name##_reader_accum)
+
+// Advance to next sample
+#define BLIP_READER_NEXT( name, bass ) \
+	(void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))
+
+// End reading samples from buffer. The number of samples read must now be removed
+// using Blip_remove_samples().
+#define BLIP_READER_END( name, blip_buffer ) \
+	(void) ((blip_buffer).reader_accum_ = name##_reader_accum)
+
+#endif
Index: apps/codecs/libgbs/gb_oscs.h
===================================================================
--- apps/codecs/libgbs/gb_oscs.h	(revision 0)
+++ apps/codecs/libgbs/gb_oscs.h	(revision 0)
@@ -0,0 +1,198 @@
+// Private oscillators used by Gb_Apu
+
+// Gb_Snd_Emu 0.1.4
+#ifndef GB_OSCS_H
+#define GB_OSCS_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+#ifndef GB_APU_OVERCLOCK
+	#define GB_APU_OVERCLOCK 1
+#endif
+
+#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1)
+	#error "GB_APU_OVERCLOCK must be a power of 2"
+#endif
+
+enum { clk_mul  = GB_APU_OVERCLOCK };
+enum { dac_bias = 7 };
+
+struct Gb_Osc {
+	struct Blip_Buffer*    outputs [4];// NULL, right, left, center
+	struct Blip_Buffer*    output;     // where to output sound
+	uint8_t* regs;       // osc's 5 registers
+	int             mode;       // mode_dmg, mode_cgb, mode_agb
+	int             dac_off_amp;// amplitude when DAC is off
+	int             last_amp;   // current amplitude in Blip_Buffer
+
+	struct Blip_Synth* synth;
+	
+	int         delay;      // clocks until frequency timer expires
+	int         length_ctr; // length counter
+	unsigned    phase;      // waveform phase (or equivalent)
+	bool        enabled;    // internal enabled flag	
+};
+
+// 11-bit frequency in NRx3 and NRx4
+static inline int Osc_frequency( struct Gb_Osc* this ) { return (this->regs [4] & 7) * 0x100 + this->regs [3]; }
+	
+void Osc_update_amp( struct Gb_Osc* this, blip_time_t, int new_amp ); ICODE_ATTR
+int Osc_write_trig( struct Gb_Osc* this, int frame_phase, int max_len, int old_data ); ICODE_ATTR
+void Osc_clock_length( struct Gb_Osc* this ); ICODE_ATTR
+void Osc_reset( struct Gb_Osc* this );
+
+// Square
+
+enum { period_mask = 0x70 };
+enum { shift_mask  = 0x07 };
+
+struct Gb_Square {
+	struct Gb_Osc osc;
+	
+	int  env_delay;
+	int  volume;
+	bool env_enabled;
+	
+	// Sweep square
+	int  sweep_freq;
+	int  sweep_delay;
+	bool sweep_enabled;
+	bool sweep_neg;
+};
+
+bool Square_write_register( struct Gb_Square* this, int frame_phase, int reg, int old_data, int data ); ICODE_ATTR
+void Square_run( struct Gb_Square* this, blip_time_t, blip_time_t ); ICODE_ATTR
+void Square_clock_envelope( struct Gb_Square* this ); ICODE_ATTR
+	 
+static inline void Square_reset( struct Gb_Square* this )
+{
+	this->env_delay = 0;
+	this->volume    = 0;
+	Osc_reset( &this->osc );
+	this->osc.delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger)
+}
+// Frequency timer period
+static inline int Square_period( struct Gb_Square* this ) { return (2048 - Osc_frequency( &this->osc )) * (4 * clk_mul); }
+static inline int Square_dac_enabled( struct Gb_Square* this) { return this->osc.regs [2] & 0xF8; }
+static inline int Square_reload_env_timer( struct Gb_Square* this )
+{
+	int raw = this->osc.regs [2] & 7;
+	this->env_delay = (raw ? raw : 8);
+	return raw;
+}
+
+// Sweep square
+
+void clock_sweep( struct Gb_Square* this ); ICODE_ATTR
+void Sweep_write_register( struct Gb_Square* this, int frame_phase, int reg, int old_data, int data ); ICODE_ATTR
+	
+static inline void Sweep_reset( struct Gb_Square* this )
+{
+	this->sweep_freq    = 0;
+	this->sweep_delay   = 0;
+	this->sweep_enabled = false;
+	this->sweep_neg     = false;
+	
+	this->env_delay = 0;
+	this->volume    = 0;
+	Osc_reset( &this->osc );
+	this->osc.delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger)
+}
+	
+void calc_sweep( struct Gb_Square* this, bool update ); ICODE_ATTR
+void reload_sweep_timer( struct Gb_Square* this ); ICODE_ATTR
+
+// Noise 
+
+enum { period2_mask = 0x1FFFF };
+	
+struct Gb_Noise {
+	struct Gb_Osc osc;
+	
+	int  env_delay;
+	int  volume;
+	bool env_enabled;
+	
+	int divider; // noise has more complex frequency divider setup
+};
+
+void Noise_run( struct Gb_Noise* this, blip_time_t, blip_time_t ); ICODE_ATTR
+void Noise_write_register( struct Gb_Noise* this, int frame_phase, int reg, int old_data, int data ); ICODE_ATTR 
+	
+static inline void Noise_reset( struct Gb_Noise* this )
+{
+	this->divider = 0;
+	
+	this->env_delay = 0;
+	this->volume    = 0;
+	Osc_reset( &this->osc );
+	this->osc.delay = 4 * clk_mul; // TODO: remove?
+}
+	
+void Noise_clock_envelope( struct Gb_Noise* this ); ICODE_ATTR
+	
+// Non-zero if DAC is enabled
+static inline int Noise_dac_enabled( struct Gb_Noise* this) { return this->osc.regs [2] & 0xF8; }
+static inline int Noise_reload_env_timer( struct Gb_Noise* this )
+{
+	int raw = this->osc.regs [2] & 7;
+	this->env_delay = (raw ? raw : 8);
+	return raw;
+}
+
+static inline int period2_index( struct Gb_Noise* this ) { return this->osc.regs [3] >> 4; }
+static inline int period2( struct Gb_Noise* this, int base ) { return base << period2_index( this ); }
+static inline unsigned lfsr_mask( struct Gb_Noise* this ) { return (this->osc.regs [3] & 0x08) ? ~0x4040 : ~0x4000; }
+
+// Wave
+
+enum { bank40_mask = 0x40 };
+enum { wave_bank_size   = 32 };
+	
+struct Gb_Wave {
+	struct Gb_Osc osc;
+	
+	int sample_buf;      // last wave RAM byte read (hardware has this as well)
+	
+	int agb_mask;        // 0xFF if AGB features enabled, 0 otherwise
+	uint8_t* wave_ram;   // 32 bytes (64 nybbles), stored in APU
+};
+
+void Wave_write_register( struct Gb_Wave* this, int frame_phase, int reg, int old_data, int data ); ICODE_ATTR
+void Wave_run( struct Gb_Wave* this, blip_time_t, blip_time_t ); ICODE_ATTR
+
+static inline void Wave_reset( struct Gb_Wave* this )
+{
+	this->sample_buf = 0;
+	Osc_reset( &this->osc );
+}
+
+// Frequency timer period
+static inline int Wave_period( struct Gb_Wave* this ) { return (2048 - Osc_frequency( &this->osc )) * (2 * clk_mul); }
+	
+// Non-zero if DAC is enabled
+static inline int Wave_dac_enabled( struct Gb_Wave* this ) { return this->osc.regs [0] & 0x80; }
+	
+void corrupt_wave( struct Gb_Wave* this );
+	
+static inline uint8_t* wave_bank( struct Gb_Wave* this ) { return &this->wave_ram [(~this->osc.regs [0] & bank40_mask) >> 2 & this->agb_mask]; }
+	
+// Wave index that would be accessed, or -1 if no access would occur
+int wave_access( struct Gb_Wave* this, int addr ); ICODE_ATTR
+
+// Reads/writes wave RAM		
+static inline int Wave_read( struct Gb_Wave* this, int addr )
+{
+	int index = wave_access( this, addr );
+	return (index < 0 ? 0xFF : wave_bank( this ) [index]);
+}
+
+static inline void Wave_write( struct Gb_Wave* this, int addr, int data )
+{
+	int index = wave_access( this, addr );
+	if ( index >= 0 )
+		wave_bank( this ) [index] = data;;
+}
+
+#endif
Index: apps/codecs/libgbs/m3u_playlist.c
===================================================================
--- apps/codecs/libgbs/m3u_playlist.c	(revision 0)
+++ apps/codecs/libgbs/m3u_playlist.c	(revision 0)
@@ -0,0 +1,413 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "m3u_playlist.h"
+#include "gbs_emu.h"
+
+#include <string.h>
+#if defined (ROCKBOX)
+#include <codecs/lib/codeclib.h>
+#else
+#include <fcntl.h>
+#endif
+
+/* Copyright (C) 2006 Shay Green. this module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. this
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+static char* skip_white( char* in )
+{
+	while ( *in == ' ' || *in == '\t' )
+		in++;
+	return in;
+}
+
+inline unsigned from_dec( unsigned n ) { return n - '0'; }
+
+static char* parse_filename( char* in, struct entry_t* entry )
+{
+	entry->file = in;
+	entry->type = "";
+	char* out = in;
+	while ( 1 )
+	{
+		int c = *in;
+		if ( !c ) break;
+		in++;
+		
+		if ( c == ',' ) // commas in filename
+		{
+			char* p = skip_white( in );
+			if ( *p == '$' || from_dec( *p ) <= 9 )
+			{
+				in = p;
+				break;
+			}
+		}
+		
+		if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix
+		{
+			entry->type = ++in;
+			while ( (c = *in) != 0 && c != ',' )
+				in++;
+			if ( c == ',' )
+			{
+				*in++ = 0; // terminate type
+				in = skip_white( in );
+			}
+			break;
+		}
+		
+		if ( c == '\\' ) // \ prefix for special characters
+		{
+			c = *in;
+			if ( !c ) break;
+			in++;
+		}
+		*out++ = (char) c;
+	}
+	*out = 0; // terminate string
+	return in;
+}
+
+static char* next_field( char* in, int* result )
+{
+	while ( 1 )
+	{
+		in = skip_white( in );
+		
+		if ( !*in )
+			break;
+		
+		if ( *in == ',' )
+		{
+			in++;
+			break;
+		}
+		
+		*result = 1;
+		in++;
+	}
+	return skip_white( in );
+}
+
+static char* parse_int_( char* in, int* out )
+{
+	int n = 0;
+	while ( 1 )
+	{
+		unsigned d = from_dec( *in );
+		if ( d > 9 )
+			break;
+		in++;
+		n = n * 10 + d;
+		*out = n;
+	}
+	return in;
+}
+
+static char* parse_int( char* in, int* out, int* result )
+{
+	return next_field( parse_int_( in, out ), result );
+}
+
+// Returns 16 or greater if not hex
+inline int from_hex_char( int h )
+{
+	h -= 0x30;
+	if ( (unsigned) h > 9 )
+		h = ((h - 0x11) & 0xDF) + 10;
+	return h;
+}
+
+static char* parse_track( char* in, struct entry_t* entry, int* result )
+{
+	if ( *in == '$' )
+	{
+		in++;
+		int n = 0;
+		while ( 1 )
+		{
+			int h = from_hex_char( *in );
+			if ( h > 15 )
+				break;
+			in++;
+			n = n * 16 + h;
+			entry->track = n;
+		}
+	}
+	else
+	{
+		in = parse_int_( in, &entry->track );
+		if ( entry->track >= 0 )
+			entry->decimal_track = 1;
+	}
+	return next_field( in, result );
+}
+
+static char* parse_time_( char* in, int* out )
+{
+	*out = -1;
+	int n = -1;
+	in = parse_int_( in, &n );
+	if ( n >= 0 )
+	{
+		*out = n;
+		while ( *in == ':' )
+		{
+			n = -1;
+			in = parse_int_( in + 1, &n );
+			if ( n >= 0 )
+				*out = *out * 60 + n;
+		}
+		*out *= 1000;
+		if ( *in == '.' )
+		{
+			n = -1;
+			in = parse_int_( in + 1, &n );
+			if ( n >= 0 )
+				*out = *out + n; 
+		}
+	}
+	return in;
+}
+
+static char* parse_time( char* in, int* out, int* result )
+{
+	return next_field( parse_time_( in, out ), result );
+}
+
+static char* parse_name( char* in )
+{
+	char* out = in;
+	while ( 1 )
+	{
+		int c = *in;
+		if ( !c ) break;
+		in++;
+		
+		if ( c == ',' ) // commas in string
+		{
+			char* p = skip_white( in );
+			if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 )
+			{
+				in = p;
+				break;
+			}
+		}
+		
+		if ( c == '\\' ) // \ prefix for special characters
+		{
+			c = *in;
+			if ( !c ) break;
+			in++;
+		}
+		*out++ = (char) c;
+	}
+	*out = 0; // terminate string
+	return in;
+}
+
+static int parse_line( char* in, struct entry_t* entry )
+{
+	int result = 0;
+	
+	// file
+	entry->file = in;
+	entry->type = "";
+	in = parse_filename( in, entry );
+	
+	// track
+	entry->track = -1;
+	entry->decimal_track = 0;
+	in = parse_track( in, entry, &result );
+	
+	// name
+	entry->name = in;
+	in = parse_name( in );
+	
+	// time
+	entry->length = -1;
+	in = parse_time( in, &entry->length, &result );
+	
+	// loop
+	entry->intro = -1;
+	entry->loop  = -1;
+	if ( *in == '-' )
+	{
+		entry->loop = entry->length;
+		in++;
+	}
+	else
+	{
+		in = parse_time_( in, &entry->loop );
+		if ( entry->loop >= 0 )
+		{
+			entry->intro = 0;
+			if ( *in == '-' ) // trailing '-' means that intro length was specified 
+			{
+				in++;
+				entry->intro = entry->loop;
+				entry->loop  = entry->length - entry->intro;
+			}
+		}
+	}
+	in = next_field( in, &result );
+	
+	// fade
+	entry->fade = -1;
+	in = parse_time( in, &entry->fade, &result );
+	
+	// repeat
+	entry->repeat = -1;
+	in = parse_int( in, &entry->repeat, &result );
+	
+	return result;
+}
+
+static void parse_comment( char* in, struct info_t* info, bool first )
+{
+	in = skip_white( in + 1 );
+	char* field = in;
+	while ( *in && *in != ':' && *in != '@' )
+		in++;
+		
+	/* Support Kaminari playlist format */
+    if ( *in == '@' ) {
+        field = in + 1;
+		
+        while ( *in && *in != ' ' && *in != '\t' )
+            in++;
+		
+        char* text = skip_white( in + 1 );
+		if ( *text )
+		{
+			*in = 0;
+			     if ( !stricmp( "Title"   , field ) ) info->title = text;
+			else if ( !stricmp( "Composer", field ) ) info->composer = text;
+			else if ( !stricmp( "Artist", field ) 
+			       || !stricmp( "Sequencer", field) 
+				   || !stricmp( "Date",      field) 
+				   || !stricmp( "Copyright", field) ) info->engineer = text;
+			else if ( !stricmp( "Ripping" , field ) ) info->ripping  = text;
+			else if ( !stricmp( "Tagging" , field ) ) info->tagging  = text;
+			else
+				text = 0;
+			if ( text )
+				return;
+			*in = ':';
+		}
+		
+		return;
+    }
+	
+	if ( *in == ':' )
+	{
+		char* text = skip_white( in + 1 );
+		if ( *text )
+		{
+			*in = 0;
+			     if ( !strcmp( "Composer", field ) ) info->composer = text;
+			else if ( !strcmp( "Engineer", field ) ) info->engineer = text;
+			else if ( !strcmp( "Ripping" , field ) ) info->ripping  = text;
+			else if ( !strcmp( "Tagging" , field ) ) info->tagging  = text;
+			else
+				text = 0;
+			if ( text )
+				return;
+			*in = ':';
+		}
+	}
+	
+	if ( first )
+		info->title = field;
+}
+
+blargg_err_t parse( struct M3u_Playlist* this )
+{
+	int const CR = 13;
+	int const LF = 10;
+	
+    *((this->data + this->raw_size - 1)) = LF; // terminate input
+	
+	this->first_error = 0;
+	bool first_comment = true;
+	int line  = 0;
+	int count = 0;
+	char* in = this->data;
+	while ( in < (this->data + this->raw_size) )
+	{
+		// find end of line and terminate it
+		line++;
+		char* begin = in;
+		while ( *in != CR && *in != LF )
+		{
+			if ( !*in )
+				return "Not an m3u playlist";
+			in++;
+		}
+		if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line
+			*in++ = 0;
+		*in++ = 0;
+		
+		// parse line
+		if ( *begin == '#' )
+		{
+			parse_comment( begin, &this->info, first_comment );
+			first_comment = false;
+		}
+		else if ( *begin )
+		{
+			if ( (int) this->size <= count )
+				this->size = count * 2 + 64;
+			
+			if ( !parse_line( begin, &this->entries [count] ) )
+				count++;
+			else if ( !this->first_error )
+				this->first_error = line;
+			first_comment = false;
+		}
+	}
+	if ( count <= 0 )
+		return "Not an m3u playlist";
+
+	this->size = count;
+	return 0;
+}
+
+blargg_err_t Gbs_load_m3u( struct Gbs_Emu* this, const char* path )
+{
+#ifndef GME_DISABLE_M3U_PLAYLIST
+	#if defined (ROCKBOX)
+		int fd = ci->open( path, O_RDONLY );
+	#else
+		int fd = open( path, O_RDONLY );
+	#endif
+		if ( fd < 0 ) return "Error when loading file";
+
+	#if defined (ROCKBOX)
+		this->m3u.raw_size = ci->read( fd, this->m3u.data, max_size );
+		ci->close( fd );
+	#else
+		this->m3u.raw_size = read( fd, this->m3u.data, max_size );
+		close( fd );
+	#endif
+
+		blargg_err_t err = parse( &this->m3u );
+		if ( err ) 
+			return err;
+
+		if ( this->m3u.size > 0 ) 
+			this->track_count = (byte) this->m3u.size; 
+#endif
+
+	return 0;
+}
Index: apps/codecs/libgbs/gbs_emu.h
===================================================================
--- apps/codecs/libgbs/gbs_emu.h	(revision 0)
+++ apps/codecs/libgbs/gbs_emu.h	(revision 0)
@@ -0,0 +1,213 @@
+// Nintendo Game Boy GBS music file emulator
+
+// Game_Music_Emu 0.5.2
+#ifndef GBS_EMU_H
+#define GBS_EMU_H
+
+#include "rom_data.h"
+#include "multi_buffer.h"
+#include "gb_apu.h"
+#include "gb_cpu.h"
+#include "m3u_playlist.h"
+
+/* typedef uint8_t byte; */
+typedef short sample_t;
+
+enum { joypad_addr = 0xFF00 };
+enum { ram_addr = 0xA000 };
+enum { hi_page = 0xFF00 - ram_addr };
+enum { io_base = 0xFF00 };
+enum { buf_size = 2048 };
+
+// Selects which sound hardware to use. AGB hardware is cleaner than the
+// others. Doesn't take effect until next start_track().
+enum sound_t {
+	sound_dmg = mode_dmg,   // Game Boy monochrome
+	sound_cgb = mode_cgb,   // Game Boy Color
+	sound_agb = mode_agb,   // Game Boy Advance
+	sound_gbs               // Use DMG/CGB based on GBS (default)
+};
+
+// GBS file header
+enum { header_size = 112 };
+struct header_t
+{
+	char tag [3];
+	byte vers;
+	byte track_count;
+	byte first_track;
+	byte load_addr [2];
+	byte init_addr [2];
+	byte play_addr [2];
+	byte stack_ptr [2];
+	byte timer_modulo;
+	byte timer_mode;
+	char game [32];
+	char author [32];
+	char copyright [32];
+};
+
+struct Gbs_Emu {
+	// Header for currently loaded file
+	struct header_t header;
+	
+	// M3u Playlist
+	struct M3u_Playlist m3u;
+	
+	// Gb cpu
+	struct Gb_Cpu cpu;
+	
+	enum sound_t sound_hardware;
+	
+	int tempo;
+	
+	// timer
+	blip_time_t cpu_time;
+	blip_time_t end_time;
+	blip_time_t play_period;
+	blip_time_t next_play;
+
+	// Sound 
+	struct Gb_Apu apu;
+	struct Stereo_Buffer buf;
+	long clock_rate_;
+	long sample_rate_;
+	unsigned buf_changed_count;
+	int voice_count_;
+	double gain_;
+	double tempo_;
+	
+	// track-specific
+	byte track_count;
+	volatile bool track_ended;
+	int current_track_;
+	blargg_long out_time;  // number of samples played since start of track
+	blargg_long emu_time;  // number of samples emulator has generated since start of track
+	bool emu_track_ended_; // emulator has reached end of track
+	
+	// fading
+	blargg_long fade_start;
+	int fade_step;
+	
+	// silence detection
+	// Disable automatic end-of-track detection and skipping of silence at beginning
+	bool ignore_silence;
+
+	int max_initial_silence;
+	int mute_mask_;
+	int silence_lookahead; // speed to run emulator when looking ahead for silence
+	long silence_time;     // number of samples where most recent silence began
+	long silence_count;    // number of samples of silence to play before using buf
+	long buf_remain;       // number of samples left in silence buffer
+	
+	sample_t buf_ [buf_size];
+	
+	// rom & ram
+	struct Rom_Data rom; 
+	byte ram [0x4000 + 0x2000 + cpu_padding];
+};
+
+
+// Basic functionality
+// Initializes Gbs_Emu structure
+void Gbs_init( struct Gbs_Emu* this );
+
+// Stops (clear) Gbs_Emu structure
+void Gbs_stop( struct Gbs_Emu* this );
+
+// Loads a file from memory
+blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size );
+
+// Set output sample rate. Must be called only once before loading file.
+blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long sample_rate );
+
+// Start a track, where 0 is the first track. Also clears warning string.
+blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int );
+
+// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
+// errors set warning string, and major errors also end track.
+blargg_err_t Gbs_play( struct Gbs_Emu* this, long count, sample_t* buf ); ICODE_ATTR
+
+// Loads an m3u playlist
+blargg_err_t Gbs_load_m3u( struct Gbs_Emu* this, const char* path );
+
+
+// Track status/control
+// Number of milliseconds (1000 msec = 1 second) played since beginning of track
+long Track_tell( struct Gbs_Emu* this );
+
+// Seek to new time in track. Seeking backwards or far forward can take a while.
+blargg_err_t Track_seek( struct Gbs_Emu* this, long msec );
+
+// Skip n samples
+blargg_err_t Track_skip( struct Gbs_Emu* this, long n );
+
+// Set start time and length of track fade out. Once fade ends track_ended() returns
+// true. Fade time can be changed while track is playing.
+void Track_set_fade( struct Gbs_Emu* this, long start_msec, long length_msec );
+
+// Get track length in milliseconds
+static inline long Track_get_length( struct Gbs_Emu* this, int n )
+{
+    long length = 120 * 1000;  /* 2 minutes */ 
+	if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
+		struct entry_t* entry = &this->m3u.entries [n];
+		length = entry->length;
+		if ( length <= 0 ) {
+			long intro = entry->intro > 0 ? entry->intro : 120 * 1000;
+			long loop = entry->loop > 0 ? entry->loop : 0;
+			length = intro + 2 * loop; /* intro + 2 loops */
+		}
+	} 
+	
+	return length;
+}
+
+
+// Sound customization
+// Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed.
+// Track length as returned by track_info() assumes a tempo of 1.0.
+void Sound_set_tempo( struct Gbs_Emu* this, double );
+	
+// Mute/unmute voice i, where voice 0 is first voice
+void Sound_mute_voice( struct Gbs_Emu* this, int index, bool mute );
+	
+// Set muting state of all voices at once using a bit mask, where -1 mutes them all,
+// 0 unmutes them all, 0x01 mutes just the first voice, etc.
+void Sound_mute_voices( struct Gbs_Emu* this, int mask );
+
+// Change overall output amplitude, where 1.0 results in minimal clamping.
+// Must be called before set_sample_rate().
+static inline void Sound_set_gain( struct Gbs_Emu* this, double g )
+{
+	assert( !this->sample_rate_ ); // you must set gain before setting sample rate
+	this->gain_ = g;
+}
+
+
+// Emulation (You shouldn't touch these)
+
+blargg_err_t Run_clocks( struct Gbs_Emu* this, blip_time_t duration ); ICODE_ATTR
+void Set_bank( struct Gbs_Emu* this, int ); ICODE_ATTR
+void Update_timer( struct Gbs_Emu* this ); ICODE_ATTR
+
+// Runs CPU until time becomes >= 0
+void Run_cpu( struct Gbs_Emu* this ); ICODE_ATTR
+
+// Reads/writes memory and I/O
+int  Read_mem( struct Gbs_Emu* this, addr_t addr ); ICODE_ATTR
+void Write_mem( struct Gbs_Emu* this, addr_t addr, int data ); ICODE_ATTR
+
+// Current time
+static inline blip_time_t time( struct Gbs_Emu* this ) 
+{
+	return Cpu_time( &this->cpu ) + this->end_time; 
+}
+
+void Jsr_then_stop( struct Gbs_Emu* this, byte const [] ); ICODE_ATTR
+void Write_io_inline( struct Gbs_Emu* this, int offset, int data, int base ); ICODE_ATTR
+void Write_io_( struct Gbs_Emu* this, int offset, int data ); ICODE_ATTR
+int  Read_io(  struct Gbs_Emu* this, int offset ); ICODE_ATTR
+void Write_io( struct Gbs_Emu* this, int offset, int data ); ICODE_ATTR
+
+#endif
Index: apps/codecs/libgbs/rom_data.c
===================================================================
--- apps/codecs/libgbs/rom_data.c	(revision 0)
+++ apps/codecs/libgbs/rom_data.c	(revision 0)
@@ -0,0 +1,76 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "rom_data.h"
+
+/* Copyright (C) 2003-2006 Shay Green. this module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. this
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <string.h>
+#include "blargg_source.h"
+
+// Rom_Data
+
+blargg_err_t Rom_load( struct Rom_Data* this, void* data, long size,
+		int header_size, void* header_out, int fill )
+{
+	long file_offset = pad_size;
+	
+	this->rom_addr = 0;
+	this->mask     = 0;
+	this->size    = 0;
+	
+	if ( size <= header_size ) // <= because there must be data after header
+		return gme_wrong_file_type;
+	
+	// Read header
+	memcpy( header_out, data, header_size );
+	
+	this->file_size = size - header_size;
+	this->file_data = (byte*) data + header_size;
+	
+	memset( this->unmapped, fill, rom_size );
+	memcpy( &this->unmapped [file_offset], this->file_data, 
+		this->file_size < pad_size ? this->file_size : pad_size );
+	
+	return 0;
+}
+
+void Rom_set_addr( struct Rom_Data* this, long addr )
+{
+	this->rom_addr = addr - bank_size - pad_extra;
+	
+	long rounded = (addr + this->file_size + bank_size - 1) / bank_size * bank_size;
+	if ( rounded <= 0 )
+	{
+		rounded = 0;
+	}
+	else
+	{
+		int shift = 0;
+		unsigned long max_addr = (unsigned long) (rounded - 1);
+		while ( max_addr >> shift )
+			shift++;
+		this->mask = (1L << shift) - 1;
+	}
+	
+	if ( addr < 0 )
+		addr = 0;
+	this->size = rounded;
+	this->rsize_ = rounded - this->rom_addr + pad_extra;
+
+	if ( 0 )
+	{
+		dprintf( "addr: %X\n", addr );
+		dprintf( "file_size: %d\n", this->file_size );
+		dprintf( "rounded: %d\n", rounded );
+		dprintf( "mask: $%X\n", this->mask );
+	}
+}
Index: apps/codecs/libgbs/blargg_config.h
===================================================================
--- apps/codecs/libgbs/blargg_config.h	(revision 0)
+++ apps/codecs/libgbs/blargg_config.h	(revision 0)
@@ -0,0 +1,36 @@
+// Library configuration. Modify this file as necessary.
+
+#ifndef BLARGG_CONFIG_H
+#define BLARGG_CONFIG_H
+
+// Uncomment to enable platform-specific optimizations
+//#define BLARGG_NONPORTABLE 1
+
+// Uncomment if automatic byte-order determination doesn't work
+#ifdef ROCKBOX_BIG_ENDIAN
+	#define BLARGG_BIG_ENDIAN 1
+#endif
+
+// Uncomment to disable m3u playlist support
+// #define GME_DISABLE_M3U_PLAYLIST 1
+
+// Uncomment if you get errors in the bool section of blargg_common.h
+#define BLARGG_COMPILER_HAS_BOOL 1
+
+// Uncomment to use fast gb apu implementation
+// #define GB_APU_FAST 1
+
+// Uncomment to remove agb emulation support
+// #define GB_APU_NO_AGB 1
+
+// To handle undefined reference to assert
+#define NDEBUG 1
+
+// Use standard config.h if present
+#define HAVE_CONFIG_H 1
+
+#ifdef HAVE_CONFIG_H
+	#include "config.h"
+#endif
+
+#endif
Index: apps/codecs/libgbs/multi_buffer.h
===================================================================
--- apps/codecs/libgbs/multi_buffer.h	(revision 0)
+++ apps/codecs/libgbs/multi_buffer.h	(revision 0)
@@ -0,0 +1,72 @@
+// Multi-channel sound buffer interface, and basic mono and stereo buffers
+
+// Blip_Buffer 0.4.1
+#ifndef MULTI_BUFFER_H
+#define MULTI_BUFFER_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+// Get indexed channel, from 0 to channel count - 1
+struct channel_t {
+	struct Blip_Buffer* center;
+	struct Blip_Buffer* left;
+	struct Blip_Buffer* right;
+};
+
+enum { type_index_mask = 0xFF };
+enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
+enum { buf_count = 3 };
+	
+struct Stereo_Buffer {
+	struct Blip_Buffer bufs [buf_count];
+	struct channel_t chan;
+	int stereo_added;
+	int was_stereo;
+	
+	unsigned channels_changed_count_;
+	long sample_rate_;
+	int length_;
+	int samples_per_frame_;
+};
+
+// Initializes Stereo_Buffer structure
+void Buffer_init( struct Stereo_Buffer* this );
+
+blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long, int msec );
+void Buffer_clock_rate( struct Stereo_Buffer* this, long );
+void Buffer_bass_freq( struct Stereo_Buffer* this, int );
+void Buffer_clear( struct Stereo_Buffer* this );
+struct channel_t Buffer_channel( struct Stereo_Buffer* this );
+void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t ); ICODE_ATTR
+	
+long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t*, long ); ICODE_ATTR
+	
+// Count of changes to channel configuration. Incremented whenever
+// a change is made to any of the Blip_Buffers for any channel.
+unsigned Buffer_channels_changed_count( struct Stereo_Buffer* this ); ICODE_ATTR
+void Buffer_channels_changed( struct Stereo_Buffer* this ); ICODE_ATTR
+	
+void Buffer_mix_stereo_no_center( struct Stereo_Buffer* this, blip_sample_t*, blargg_long ); ICODE_ATTR
+void Buffer_mix_stereo( struct Stereo_Buffer* this, blip_sample_t*, blargg_long ); ICODE_ATTR
+void Buffer_mix_mono( struct Stereo_Buffer* this, blip_sample_t*, blargg_long ); ICODE_ATTR
+
+// Number of samples per output frame (1 = mono, 2 = stereo)
+static inline int Buffer_samples_per_frame( struct Stereo_Buffer* this )
+{
+	return this->samples_per_frame_;
+}
+
+// See Blip_Buffer.h
+static inline long Buffer_sample_rate( struct Stereo_Buffer* this )
+{
+	return this->sample_rate_;
+}
+
+// Length of buffer, in milliseconds
+static inline int Buffer_length( struct Stereo_Buffer* this )
+{
+	return this->length_;
+}
+
+#endif
Index: apps/codecs/libgbs/gb_cpu_run.h
===================================================================
--- apps/codecs/libgbs/gb_cpu_run.h	(revision 0)
+++ apps/codecs/libgbs/gb_cpu_run.h	(revision 0)
@@ -0,0 +1,1187 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Macros "returning" void may use a {} statement block. */
+
+	// 0 <= addr <= 0xFFFF + page_size
+	// time functions can be used
+	int  READ_MEM(  addr_t );
+	void WRITE_MEM( addr_t, int data );
+	
+	// Access of 0xFF00 + offset
+	// 0 <= offset <= 0xFF
+	int  READ_IO(  int offset );
+	void WRITE_IO( int offset, int data );
+
+	// Often-used instructions use this instead of READ_MEM
+	void READ_FAST( addr_t, int& out );
+
+// The following can be used within macros:
+	
+	// Current time
+	cpu_time_t TIME();
+#endif
+
+/* Copyright (C) 2003-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+// Common instructions:
+//
+// 365880   FA      LD   A,(nn)
+// 355863   20      JR   NZ
+// 313655   21      LD   HL,nn
+// 274580   28      JR   Z
+// 252878   FE      CP   n
+// 230541   7E      LD   A,(HL)
+// 226209   2A      LD   A,(HL+)
+// 217467   CD      CALL
+// 212034   C9      RET
+// 208376   CB      CB prefix
+//
+//  27486   CB 7E   BIT  7,(HL)
+//  15925   CB 76   BIT  6,(HL)
+//  13035   CB 19   RR   C
+//  11557   CB 7F   BIT  7,A
+//  10898   CB 37   SWAP A
+//  10208   CB 66   BIT  4,(HL)
+
+// Allows MWCW debugger to step through code properly
+#ifdef CPU_BEGIN
+	CPU_BEGIN
+#endif
+
+#define TIME()  s.time
+
+#define CODE_PAGE( addr )   s.code_map [GB_CPU_PAGE( addr )]
+#define READ_CODE( addr )   (CODE_PAGE( addr ) [GB_CPU_OFFSET( addr )])
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const z80 = 0x80; // cz
+int const n40 = 0x40; // ph
+int const h20 = 0x20; // ph
+int const c10 = 0x10; // cz
+
+#define SET_FLAGS( in )\
+{\
+	cz = ((in) << 4 & 0x100) + (~(in) >> 7 & 1);\
+	ph = (~(in) << 2 & 0x100) + ((in) >> 1 & 0x10);\
+}
+
+// random bits in cz to catch misuse of them
+#define SET_FLAGS_DEBUG( in )\
+{\
+	cz = ((in) << 4 & 0x100) | (rand() & ~0x1FF) | ((in) & 0x80 ? 0 : (rand() & 0xFF) | 1);\
+	ph = (~(in) << 2 & 0x100) | (((in) >> 1 & 0x10) ^ BYTE( cz ));\
+}
+
+#define GET_FLAGS( out )\
+{\
+	out = (cz >> 4 & c10);\
+	out += ~ph >> 2 & n40;\
+	out += (ph ^ cz) << 1 & h20;\
+	if ( !BYTE( cz ) )\
+		out += z80;\
+}
+
+#define CC_NZ() ( BYTE( cz ))
+#define CC_Z()  (!BYTE( cz ))
+#define CC_NC() (!(cz & 0x100))
+#define CC_C()  (  cz & 0x100 )
+
+// Truncation
+#define BYTE(  n ) ((uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((int8_t  ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD(  n ) ((uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+{
+	struct cpu_state_t s;
+	cpu->cpu_state = &s;
+	memcpy( &s, &cpu->cpu_state_, sizeof s );
+	
+	union {
+		struct {
+		#ifdef BLARGG_BIG_ENDIAN
+			byte b, c, d, e, h, l, flags, a;
+		#else
+			byte c, b, e, d, l, h, a, flags;
+		#endif
+		} rg; // individual registers
+		struct core_regs_t rp; // pairs
+		
+		byte r8_ [8]; // indexed registers (use R8 macro due to endian dependence)
+		uint16_t r16 [4]; // indexed pairs
+	} reg;
+	BOOST_STATIC_ASSERT( sizeof reg.rg == 8 && sizeof reg.rp == 8 );
+
+	#ifdef BLARGG_BIG_ENDIAN
+		#define R8( n ) (reg.r8_ [n]) 
+	#elif BLARGG_LITTLE_ENDIAN
+		#define R8( n ) (reg.r8_ [(n) ^ 1]) 
+	#else
+		// Be sure "blargg_endian.h" has been #included in the file that #includes this
+		#error "Byte order of CPU must be known"
+	#endif
+	
+	#define R16( n ) (reg.r16 [n])
+	#define RG (reg.rg)
+	#define RP (reg.rp)
+	
+	RP = cpu->r.rp;
+	int pc = cpu->r.pc;
+	int sp = cpu->r.sp;
+	int ph;
+	int cz;
+	SET_FLAGS( RG.flags );
+	
+	int time = s.time;
+	
+loop:
+	
+	check( (unsigned) pc < 0x10000 + 1 ); // +1 so emulator can catch wrap-around
+	check( (unsigned) sp < 0x10000 );
+	
+	byte const* instr = CODE_PAGE( pc );
+	int op;
+	
+	if ( GB_CPU_OFFSET(~0) == ~0 )
+	{
+		op = instr [pc];
+		pc++;
+		instr += pc;
+	}
+	else
+	{
+		instr += GB_CPU_OFFSET( pc );
+		op = *instr++;
+		pc++;
+	}
+	
+#define GET_ADDR()  GET_LE16( instr )
+	
+	static byte const instr_times [256*2] ICONST_ATTR = {
+	//   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
+		 4,12, 8, 8, 4, 4, 8, 4,20, 8, 8, 8, 4, 4, 8, 4,// 0
+		 4,12, 8, 8, 4, 4, 8, 4,12, 8, 8, 8, 4, 4, 8, 4,// 1
+		 8,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 2
+		 8,12, 8, 8,12,12,12, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 3
+		 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 4
+		 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 5
+		 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 6
+		 8, 8, 8, 8, 8, 8, 0, 8, 4, 4, 4, 4, 4, 4, 8, 4,// 7
+		 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 8
+		 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 9
+		 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// A
+		 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// B
+		 8,12,16,16,12,16, 8,16, 8,16,16, 0,12,24, 8,16,// C
+		 8,12,16, 0,12,16, 8,16, 8,16,16, 0,12, 0, 8,16,// D
+		12,12, 8, 0, 0,16, 8,16,16, 4,16, 0, 0, 0, 8,16,// E
+		12,12, 8, 4, 0,16, 8,16,12, 8,16, 4, 0, 0, 8,16,// F
+		
+	// CB prefixed
+	//   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 0
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 1
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 2
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 3
+		 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 4
+		 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 5
+		 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 6
+		 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 7
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 8
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 9
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// A
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// B
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// C
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// D
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// E
+		 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// F
+	};
+	
+	if ( time >= 0 )
+		goto stop;
+	
+	time += instr_times [op];
+	
+	int data;
+	data = *instr;
+	s.time = time;
+	
+	#ifdef CPU_INSTR_HOOK
+	{ CPU_INSTR_HOOK( (pc-1), (instr-1), rg.a, rp.bc, rp.de, rp.hl, sp ); }
+	#endif
+	
+	switch ( op )
+	{
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH_( cond, clocks )\
+{\
+	pc++;\
+	if ( !(cond) )\
+		goto loop;\
+	pc = WORD( pc + SBYTE( data ) );\
+	time += clocks;\
+	goto loop;\
+}
+
+#define BRANCH( cond ) BRANCH_( cond, 4 )
+
+// Most Common
+
+	case 0x20: // JR NZ
+		BRANCH( CC_NZ() )
+	
+	case 0x21: // LD HL,IMM (common)
+		RP.hl = GET_ADDR();
+		pc += 2;
+		goto loop;
+	
+	case 0x28: // JR Z
+		BRANCH( CC_Z() )
+	
+	case 0xF2: // LD A,(0xFF00+C)
+		READ_IO( this, RG.c, RG.a );
+		goto loop;
+	
+	case 0xF0: // LD A,(0xFF00+imm)
+		pc++;
+		READ_IO( this, data, RG.a );
+		goto loop;
+	
+	{
+		int temp;
+	case 0x0A: // LD A,(BC)
+		temp = RP.bc;
+		goto ld_a_ind_comm;
+	
+	case 0x3A: // LD A,(HL-)
+		temp = RP.hl;
+		RP.hl = temp - 1;
+		goto ld_a_ind_comm;
+	
+	case 0x1A: // LD A,(DE)
+		temp = RP.de;
+		goto ld_a_ind_comm;
+	
+	case 0x2A: // LD A,(HL+) (common)
+		temp = RP.hl;
+		RP.hl = temp + 1;
+		goto ld_a_ind_comm;
+		
+	case 0xFA: // LD A,IND16 (common)
+		temp = GET_ADDR();
+		pc += 2;
+	ld_a_ind_comm:
+		READ_FAST( this, temp, RG.a );
+		goto loop;
+	}
+	
+	{
+		int temp;
+	case 0xBE: // CP (HL)
+		temp = READ_MEM( this, RP.hl );
+		goto cmp_comm;
+	
+	case 0xB8: // CP B
+	case 0xB9: // CP C
+	case 0xBA: // CP D
+	case 0xBB: // CP E
+	case 0xBC: // CP H
+	case 0xBD: // CP L
+	case 0xBF: // CP A
+		temp = R8( op & 7 );
+	cmp_comm:
+		ph = RG.a ^ temp; // N=1 H=*
+		cz = RG.a - temp; // C=* Z=*
+		goto loop;
+	}
+	
+	case 0xFE: // CP IMM
+		pc++;
+		ph = RG.a ^ data; // N=1 H=*
+		cz = RG.a - data; // C=* Z=*
+		goto loop;
+	
+	case 0x46: // LD B,(HL)
+	case 0x4E: // LD C,(HL)
+	case 0x56: // LD D,(HL)
+	case 0x5E: // LD E,(HL)
+	case 0x66: // LD H,(HL)
+	case 0x6E: // LD L,(HL)
+	case 0x7E:{// LD A,(HL)
+		int addr = RP.hl;
+		READ_FAST( this, addr, R8( op >> 3 & 7 ) );
+		goto loop;
+	}
+	
+	case 0xC4: // CNZ (next-most-common)
+		pc += 2;
+		if ( CC_Z() )
+			goto loop;
+	call:
+		time += 12;
+		pc -= 2;
+	case 0xCD: // CALL (most-common)
+		data = pc + 2;
+		pc = GET_ADDR();
+	push: {
+		int addr = WORD( sp - 1 );
+		WRITE_MEM( this, addr, (data >> 8) );
+		sp = WORD( sp - 2 );
+		WRITE_MEM( this, sp, data );
+		goto loop;
+	}
+	
+	case 0xC8: // RET Z (next-most-common)
+		if ( CC_NZ() )
+			goto loop;
+	ret:
+		time += 12;
+	case 0xD9: // RETI
+	case 0xC9:{// RET (most common)
+		pc = READ_MEM( this, sp );
+		int addr = sp + 1;
+		sp = WORD( sp + 2 );
+		pc += 0x100 * READ_MEM( this, addr );
+		goto loop;
+	}
+	
+	case 0x00: // NOP
+	case 0x40: // LD B,B
+	case 0x49: // LD C,C
+	case 0x52: // LD D,D
+	case 0x5B: // LD E,E
+	case 0x64: // LD H,H
+	case 0x6D: // LD L,L
+	case 0x7F: // LD A,A
+		goto loop;
+	
+// CB Instructions
+
+	case 0xCB:
+		time += (instr_times + 256) [data];
+		pc++;
+		// now data is the opcode
+		switch ( data ) {
+			
+		case 0x46: // BIT b,(HL)
+		case 0x4E:
+		case 0x56:
+		case 0x5E:
+		case 0x66:
+		case 0x6E:
+		case 0x76:
+		case 0x7E: {
+			int addr = RP.hl;
+			READ_FAST( this, addr, op );
+			goto bit_comm;
+		}
+		
+		case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r
+		case 0x44: case 0x45: case 0x47: case 0x48:
+		case 0x49: case 0x4A: case 0x4B: case 0x4C:
+		case 0x4D: case 0x4F: case 0x50: case 0x51:
+		case 0x52: case 0x53: case 0x54: case 0x55:
+		case 0x57: case 0x58: case 0x59: case 0x5A:
+		case 0x5B: case 0x5C: case 0x5D: case 0x5F:
+		case 0x60: case 0x61: case 0x62: case 0x63:
+		case 0x64: case 0x65: case 0x67: case 0x68:
+		case 0x69: case 0x6A: case 0x6B: case 0x6C:
+		case 0x6D: case 0x6F: case 0x70: case 0x71:
+		case 0x72: case 0x73: case 0x74: case 0x75:
+		case 0x77: case 0x78: case 0x79: case 0x7A:
+		case 0x7B: case 0x7C: case 0x7D: case 0x7F:
+			op = R8( data & 7 );
+		bit_comm:
+			ph = op >> (data >> 3 & 7) & 1;
+			cz = (cz & 0x100) + ph;
+			ph ^= 0x110; // N=0 H=1
+			goto loop;
+		
+		case 0x86: // RES b,(HL)
+		case 0x8E:
+		case 0x96:
+		case 0x9E:
+		case 0xA6:
+		case 0xAE:
+		case 0xB6:
+		case 0xBE: {
+			int temp = READ_MEM( this, RP.hl );
+			temp &= ~(1 << (data >> 3 & 7));
+			WRITE_MEM( this, RP.hl, temp );
+			goto loop;
+		}
+		
+		case 0xC6: // SET b,(HL)
+		case 0xCE:
+		case 0xD6:
+		case 0xDE:
+		case 0xE6:
+		case 0xEE:
+		case 0xF6:
+		case 0xFE: {
+			int temp = READ_MEM( this, RP.hl );
+			temp |= 1 << (data >> 3 & 7);
+			WRITE_MEM( this, RP.hl, temp );
+			goto loop;
+		}
+		
+		case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r
+		case 0xC4: case 0xC5: case 0xC7: case 0xC8:
+		case 0xC9: case 0xCA: case 0xCB: case 0xCC:
+		case 0xCD: case 0xCF: case 0xD0: case 0xD1:
+		case 0xD2: case 0xD3: case 0xD4: case 0xD5:
+		case 0xD7: case 0xD8: case 0xD9: case 0xDA:
+		case 0xDB: case 0xDC: case 0xDD: case 0xDF:
+		case 0xE0: case 0xE1: case 0xE2: case 0xE3:
+		case 0xE4: case 0xE5: case 0xE7: case 0xE8:
+		case 0xE9: case 0xEA: case 0xEB: case 0xEC:
+		case 0xED: case 0xEF: case 0xF0: case 0xF1:
+		case 0xF2: case 0xF3: case 0xF4: case 0xF5:
+		case 0xF7: case 0xF8: case 0xF9: case 0xFA:
+		case 0xFB: case 0xFC: case 0xFD: case 0xFF:
+			R8( data & 7 ) |= 1 << (data >> 3 & 7);
+			goto loop;
+
+		case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r
+		case 0x84: case 0x85: case 0x87: case 0x88:
+		case 0x89: case 0x8A: case 0x8B: case 0x8C:
+		case 0x8D: case 0x8F: case 0x90: case 0x91:
+		case 0x92: case 0x93: case 0x94: case 0x95:
+		case 0x97: case 0x98: case 0x99: case 0x9A:
+		case 0x9B: case 0x9C: case 0x9D: case 0x9F:
+		case 0xA0: case 0xA1: case 0xA2: case 0xA3:
+		case 0xA4: case 0xA5: case 0xA7: case 0xA8:
+		case 0xA9: case 0xAA: case 0xAB: case 0xAC:
+		case 0xAD: case 0xAF: case 0xB0: case 0xB1:
+		case 0xB2: case 0xB3: case 0xB4: case 0xB5:
+		case 0xB7: case 0xB8: case 0xB9: case 0xBA:
+		case 0xBB: case 0xBC: case 0xBD: case 0xBF:
+			R8( data & 7 ) &= ~(1 << (data >> 3 & 7));
+			goto loop;
+		
+		case 0x36: // SWAP (HL)
+			op = READ_MEM( this, RP.hl );
+			goto swap_comm;
+		
+		case 0x30: // SWAP B
+		case 0x31: // SWAP C
+		case 0x32: // SWAP D
+		case 0x33: // SWAP E
+		case 0x34: // SWAP H
+		case 0x35: // SWAP L
+		case 0x37: // SWAP A
+			op = R8( data & 7 );
+		swap_comm:
+			op = (op >> 4) + (op << 4);
+			cz = BYTE( op );
+			ph = cz + 0x100;
+			if ( data == 0x36 )
+				goto write_hl_op_ff;
+			R8( data & 7 ) = op;
+			goto loop;
+		
+// Shift/Rotate
+
+		case 0x26: // SLA (HL)
+			cz = 0;
+		case 0x16: // RL (HL)
+			cz = (cz >> 8 & 1) + (READ_MEM( this, RP.hl ) << 1);
+			goto rl_hl_common;
+		
+		case 0x06: // RLC (HL)
+			cz = READ_MEM( this, RP.hl );
+			cz = (cz << 1) + (cz >> 7 & 1);
+		rl_hl_common:
+			// Z=* C=*
+			ph = cz | 0x100; // N=0 H=0
+			WRITE_MEM( this, RP.hl, cz );
+			goto loop;
+		
+		case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA r
+			cz = 0;
+		case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL r
+			cz = (cz >> 8 & 1) + (R8( data & 7 ) << 1);
+			goto rl_common;
+		
+		case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC r
+			cz = R8( data & 7 );
+			cz = (cz << 1) + (cz >> 7 & 1);
+		rl_common:
+			// Z=* C=*
+			ph = cz | 0x100; // N=0 H=0
+			R8( data & 7 ) = cz;
+			goto loop;
+		
+		case 0x0E: // RRC (HL)
+			cz = READ_MEM( this, RP.hl );
+			cz += cz << 8 & 0x100;
+			goto rr_hl_common;
+		
+		case 0x2E: // SRA (HL)
+			cz = READ_MEM( this, RP.hl );
+			cz += cz << 1 & 0x100;
+			goto rr_hl_common;
+		
+		case 0x3E: // SRL (HL)
+			cz = 0;
+		case 0x1E: // RR (HL)
+			cz = (cz & 0x100) + READ_MEM( this, RP.hl );
+		rr_hl_common:
+			cz = (cz << 8) + (cz >> 1); // Z=* C=*
+			ph = cz | 0x100; // N=0 H=0
+			WRITE_MEM( this, RP.hl, cz );
+			goto loop;
+		
+		case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC r
+			cz = R8( data & 7 );
+			cz += cz << 8 & 0x100;
+			goto rr_common;
+		
+		case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA r
+			cz = R8( data & 7 );
+			cz += cz << 1 & 0x100;
+			goto rr_common;
+		
+		case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL r
+			cz = 0;
+		case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR r
+			cz = (cz & 0x100) + R8( data & 7 );
+		rr_common:
+			cz = (cz << 8) + (cz >> 1); // Z=* C=*
+			ph = cz | 0x100; // N=0 H=0
+			R8( data & 7 ) = cz;
+			goto loop;
+		
+	} // CB op
+	assert( false ); // unhandled CB op
+	
+	case 0x07: // RLCA
+		cz = RG.a >> 7;
+		goto rlc_common;
+	case 0x17: // RLA
+		cz = cz >> 8 & 1;
+	rlc_common:
+		cz  += RG.a << 1;
+		ph   = cz | 0x100;
+		RG.a = BYTE( cz );
+		cz  |= 1;
+		goto loop;
+	
+	case 0x0F: // RRCA
+		ph = RG.a << 8;
+		goto rrc_common;
+	case 0x1F: // RRA
+		ph = cz;
+	rrc_common:
+		cz = (RG.a << 8) + 1; // Z=0 C=*
+		RG.a = ((ph & 0x100) + RG.a) >> 1;
+		ph = 0x100; // N=0 H=0
+		goto loop;
+
+// Load
+
+	case 0x70: // LD (HL),B
+	case 0x71: // LD (HL),C
+	case 0x72: // LD (HL),D
+	case 0x73: // LD (HL),E
+	case 0x74: // LD (HL),H
+	case 0x75: // LD (HL),L
+	case 0x77: // LD (HL),A
+		op = R8( op & 7 );
+	write_hl_op_ff:
+		WRITE_MEM( this, RP.hl, op );
+		goto loop;
+
+	case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r
+	case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F:
+	case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57:
+	case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F:
+	case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67:
+	case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F:
+	case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D:
+		R8( op >> 3 & 7 ) = R8( op & 7 );
+		goto loop;
+
+	case 0x08: // LD IND16,SP
+		data = GET_ADDR();
+		pc += 2;
+		WRITE_MEM( this, data, sp );
+		data++;
+		WRITE_MEM( this, data, (sp >> 8) );
+		goto loop;
+	
+	case 0xF9: // LD SP,HL
+		sp = RP.hl;
+		goto loop;
+
+	case 0x31: // LD SP,IMM
+		sp = GET_ADDR();
+		pc += 2;
+		goto loop;
+	
+	case 0x01: // LD BC,IMM
+	case 0x11: // LD DE,IMM
+		R16( (unsigned) op >> 4 ) = GET_ADDR();
+		pc += 2;
+		goto loop;
+	
+	case 0xE2: // LD (0xFF00+C),A
+		WRITE_IO( this, RG.c, RG.a );
+		goto loop;
+	
+	case 0xE0: // LD (0xFF00+imm),A
+		pc++;
+		WRITE_IO( this, data, RG.a );
+		goto loop;
+	
+	{
+		int temp;
+	case 0x32: // LD (HL-),A
+		temp = RP.hl;
+		RP.hl = temp - 1;
+		goto write_data_rg_a;
+	
+	case 0x02: // LD (BC),A
+		temp = RP.bc;
+		goto write_data_rg_a;
+	
+	case 0x12: // LD (DE),A
+		temp = RP.de;
+		goto write_data_rg_a;
+	
+	case 0x22: // LD (HL+),A
+		temp = RP.hl;
+		RP.hl = temp + 1;
+		goto write_data_rg_a;
+		
+	case 0xEA: // LD IND16,A (common)
+		temp = GET_ADDR();
+		pc += 2;
+	write_data_rg_a:
+		WRITE_MEM( this, temp, RG.a );
+		goto loop;
+	}
+	
+	case 0x06: // LD B,IMM
+		RG.b = data;
+		pc++;
+		goto loop;
+	
+	case 0x0E: // LD C,IMM
+		RG.c = data;
+		pc++;
+		goto loop;
+	
+	case 0x16: // LD D,IMM
+		RG.d = data;
+		pc++;
+		goto loop;
+	
+	case 0x1E: // LD E,IMM
+		RG.e = data;
+		pc++;
+		goto loop;
+	
+	case 0x26: // LD H,IMM
+		RG.h = data;
+		pc++;
+		goto loop;
+	
+	case 0x2E: // LD L,IMM
+		RG.l = data;
+		pc++;
+		goto loop;
+	
+	case 0x36: // LD (HL),IMM
+		WRITE_MEM( this, RP.hl, data );
+		pc++;
+		goto loop;
+	
+	case 0x3E: // LD A,IMM
+		RG.a = data;
+		pc++;
+		goto loop;
+
+// Increment/decrement
+
+	case 0x03: // INC BC
+	case 0x13: // INC DE
+	case 0x23: // INC HL
+		R16( (unsigned) op >> 4 )++;
+		goto loop;
+	
+	case 0x33: // INC SP
+		sp = WORD( sp + 1 );
+		goto loop;
+
+	case 0x0B: // DEC BC
+	case 0x1B: // DEC DE
+	case 0x2B: // DEC HL
+		R16( (unsigned) op >> 4 )--;
+		goto loop;
+	
+	case 0x3B: // DEC SP
+		sp = WORD( sp - 1 );
+		goto loop;
+	
+	case 0x34: // INC (HL)
+		op = RP.hl;
+		data = READ_MEM( this, op );
+		data++;
+		WRITE_MEM( this, op, data );
+		goto inc_comm;
+	
+	case 0x04: // INC B
+	case 0x0C: // INC C (common)
+	case 0x14: // INC D
+	case 0x1C: // INC E
+	case 0x24: // INC H
+	case 0x2C: // INC L
+	case 0x3C: // INC A
+		op = op >> 3 & 7;
+		data = R8( op ) + 1;
+		R8( op ) = data;
+	inc_comm:
+		ph = data - 0x101; // N=0 H=*
+		cz = (cz & 0x100) + BYTE( data ); // C=- Z=*
+		goto loop;
+	
+	case 0x35: // DEC (HL)
+		op = RP.hl;
+		data = READ_MEM( this, op );
+		data--;
+		WRITE_MEM( this, op, data );
+		goto dec_comm;
+	
+	case 0x05: // DEC B
+	case 0x0D: // DEC C
+	case 0x15: // DEC D
+	case 0x1D: // DEC E
+	case 0x25: // DEC H
+	case 0x2D: // DEC L
+	case 0x3D: // DEC A
+		op = op >> 3 & 7;
+		data = R8( op ) - 1;
+		R8( op ) = data;
+	dec_comm:
+		ph = data + 1; // N=1 H=*
+		cz = (cz & 0x100) + BYTE( data ); // C=- Z=*
+		goto loop;
+
+// Add 16-bit
+
+	case 0xF8: // LD  HL,SP+n
+	case 0xE8:{// ADD SP,n
+		pc++;
+		int t = WORD( sp + SBYTE( data ) );
+		cz = ((BYTE( sp ) + data) & 0x100) + 1; // Z=0 C=*
+		ph = (sp ^ data ^ t) | 0x100; // N=0 H=*
+		if ( op == 0xF8 )
+		{
+			RP.hl = t;
+			goto loop;
+		}
+		sp = t;
+		goto loop;
+	}
+
+	case 0x39: // ADD HL,SP
+		data = sp;
+		goto add_hl_comm;
+	
+	case 0x09: // ADD HL,BC
+	case 0x19: // ADD HL,DE
+	case 0x29: // ADD HL,HL
+		data = R16( (unsigned) op >> 4 );
+	add_hl_comm:
+		ph = RP.hl ^ data;
+		data += RP.hl;
+		RP.hl = WORD( data );
+		ph ^= data;
+		cz = BYTE( cz ) + (data >> 8 & 0x100); // C=* Z=-
+		ph = ((ph >> 8) ^ cz) | 0x100; // N=0 H=*
+		goto loop;
+	
+	case 0x86: // ADD (HL)
+		data = READ_MEM( this, RP.hl );
+		goto add_comm;
+	
+	case 0x80: // ADD B
+	case 0x81: // ADD C
+	case 0x82: // ADD D
+	case 0x83: // ADD E
+	case 0x84: // ADD H
+	case 0x85: // ADD L
+	case 0x87: // ADD A
+		data = R8( op & 7 );
+		goto add_comm;
+	
+	case 0xC6: // ADD IMM
+		pc++;
+	add_comm:
+		ph   = (RG.a ^ data) | 0x100; // N=1 H=*
+		cz   = RG.a + data; // C=* Z=*
+		RG.a = cz;
+		goto loop;
+	
+// Add/Subtract
+
+	case 0x8E: // ADC (HL)
+		data = READ_MEM( this, RP.hl );
+		goto adc_comm;
+	
+	case 0x88: // ADC B
+	case 0x89: // ADC C
+	case 0x8A: // ADC D
+	case 0x8B: // ADC E
+	case 0x8C: // ADC H
+	case 0x8D: // ADC L
+	case 0x8F: // ADC A
+		data = R8( op & 7 );
+		goto adc_comm;
+	
+	case 0xCE: // ADC IMM
+		pc++;
+	adc_comm:
+		ph   = (RG.a ^ data) | 0x100; // N=1 H=*
+		cz   = RG.a + data + (cz >> 8 & 1); // C=* Z=*
+		RG.a = cz;
+		goto loop;
+	
+	case 0x96: // SUB (HL)
+		data = READ_MEM( this, RP.hl );
+		goto sub_comm;
+	
+	case 0x90: // SUB B
+	case 0x91: // SUB C
+	case 0x92: // SUB D
+	case 0x93: // SUB E
+	case 0x94: // SUB H
+	case 0x95: // SUB L
+	case 0x97: // SUB A
+		data = R8( op & 7 );
+		goto sub_comm;
+	
+	case 0xD6: // SUB IMM
+		pc++;
+	sub_comm:
+		ph   = RG.a ^ data; // N=1 H=*
+		cz   = RG.a - data; // C=* Z=*
+		RG.a = cz;
+		goto loop;
+	
+	case 0x9E: // SBC (HL)
+		data = READ_MEM( this, RP.hl );
+		goto sbc_comm;
+	
+	case 0x98: // SBC B
+	case 0x99: // SBC C
+	case 0x9A: // SBC D
+	case 0x9B: // SBC E
+	case 0x9C: // SBC H
+	case 0x9D: // SBC L
+	case 0x9F: // SBC A
+		data = R8( op & 7 );
+		goto sbc_comm;
+	
+	case 0xDE: // SBC IMM
+		pc++;
+	sbc_comm:
+		ph   = RG.a ^ data; // N=1 H=*
+		cz   = RG.a - data - (cz >> 8 & 1); // C=* Z=*
+		RG.a = cz;
+		goto loop;
+
+// Logical
+
+	case 0xA0: // AND B
+	case 0xA1: // AND C
+	case 0xA2: // AND D
+	case 0xA3: // AND E
+	case 0xA4: // AND H
+	case 0xA5: // AND L
+		data = R8( op & 7 );
+		goto and_comm;
+	
+	case 0xA6: // AND (HL)
+		data = READ_MEM( this, RP.hl );
+		goto and_comm;
+	case 0xE6: // AND IMM
+		pc++;
+	and_comm:
+		cz = RG.a & data; // C=0 Z=*
+		ph = ~cz; // N=0 H=1
+		RG.a = cz;
+		goto loop;
+	
+	case 0xA7: // AND A
+		cz = RG.a; // C=0 Z=*
+		ph = ~RG.a; // N=0 H=1
+		goto loop;
+
+	case 0xB0: // OR B
+	case 0xB1: // OR C
+	case 0xB2: // OR D
+	case 0xB3: // OR E
+	case 0xB4: // OR H
+	case 0xB5: // OR L
+		data = R8( op & 7 );
+		goto or_comm;
+	
+	case 0xB6: // OR (HL)
+		data = READ_MEM( this, RP.hl );
+		goto or_comm;
+	case 0xF6: // OR IMM
+		pc++;
+	or_comm:
+		cz = RG.a | data; // C=0 Z=*
+		ph = cz | 0x100; // N=0 H=0
+		RG.a = cz;
+		goto loop;
+	
+	case 0xB7: // OR A
+		cz = RG.a; // C=0 Z=*
+		ph = RG.a + 0x100; // N=0 H=0
+		goto loop;
+
+	case 0xA8: // XOR B
+	case 0xA9: // XOR C
+	case 0xAA: // XOR D
+	case 0xAB: // XOR E
+	case 0xAC: // XOR H
+	case 0xAD: // XOR L
+		data = R8( op & 7 );
+		goto xor_comm;
+	
+	case 0xAE: // XOR (HL)
+		data = READ_MEM( this, RP.hl );
+		pc--;
+	case 0xEE: // XOR IMM
+		pc++;
+	xor_comm:
+		cz = RG.a ^ data; // C=0 Z=*
+		ph = cz + 0x100; // N=0 H=0
+		RG.a = cz;
+		goto loop;
+	
+	case 0xAF: // XOR A
+		RG.a = 0;
+		cz   = 0; // C=0 Z=*
+		ph   = 0x100; // N=0 H=0
+		goto loop;
+
+// Stack
+
+	case 0xF1: // POP AF
+	case 0xC1: // POP BC
+	case 0xD1: // POP DE
+	case 0xE1: // POP HL (common)
+		data = READ_MEM( this, sp );
+		R16( op >> 4 & 3 ) = data + 0x100 * READ_MEM( this, (sp + 1) );
+		sp = WORD( sp + 2 );
+		if ( op != 0xF1 )
+			goto loop;
+		
+		SET_FLAGS( RG.a );
+		RG.a = RG.flags;
+		goto loop;
+	
+	case 0xC5: // PUSH BC
+		data = RP.bc;
+		goto push;
+	
+	case 0xD5: // PUSH DE
+		data = RP.de;
+		goto push;
+	
+	case 0xE5: // PUSH HL
+		data = RP.hl;
+		goto push;
+	
+	case 0xF5: // PUSH AF
+		GET_FLAGS( data );
+		data += RG.a << 8;
+		goto push;
+
+// Flow control
+	
+	case 0xFF: case 0xC7: case 0xCF: case 0xD7: // RST
+	case 0xDF: case 0xE7: case 0xEF: case 0xF7:
+		data = pc;
+		pc = (op & 0x38) + cpu->rst_base;
+		goto push;
+	
+	case 0xCC: // CALL Z
+		pc += 2;
+		if ( CC_Z() )
+			goto call;
+		goto loop;
+	
+	case 0xD4: // CALL NC
+		pc += 2;
+		if ( CC_NC() )
+			goto call;
+		goto loop;
+	
+	case 0xDC: // CALL C
+		pc += 2;
+		if ( CC_C() )
+			goto call;
+		goto loop;
+
+	case 0xC0: // RET NZ
+		if ( CC_NZ() )
+			goto ret;
+		goto loop;
+	
+	case 0xD0: // RET NC
+		if ( CC_NC() )
+			goto ret;
+		goto loop;
+	
+	case 0xD8: // RET C
+		if ( CC_C() )
+			goto ret;
+		goto loop;
+
+	case 0x18: // JR
+		BRANCH_( true, 0 )
+	
+	case 0x30: // JR NC
+		BRANCH( CC_NC() )
+	
+	case 0x38: // JR C
+		BRANCH( CC_C() )
+	
+	case 0xE9: // LD PC,HL
+		pc = RP.hl;
+		goto loop;
+
+	case 0xC3: // JP (next-most-common)
+		pc = GET_ADDR();
+		goto loop;
+	
+	case 0xC2: // JP NZ
+		pc += 2;
+		if ( CC_NZ() )
+			goto jp_taken;
+		time -= 4;
+		goto loop;
+	
+	case 0xCA: // JP Z (most common)
+		pc += 2;
+		if ( CC_Z() )
+			goto jp_taken;
+		time -= 4;
+		goto loop;
+	
+	jp_taken:
+		pc -= 2;
+		pc = GET_ADDR();
+		goto loop;
+	
+	case 0xD2: // JP NC
+		pc += 2;
+		if ( CC_NC() )
+			goto jp_taken;
+		time -= 4;
+		goto loop;
+	
+	case 0xDA: // JP C
+		pc += 2;
+		if ( CC_C() )
+			goto jp_taken;
+		time -= 4;
+		goto loop;
+
+// Flags
+
+	case 0x2F: // CPL
+		RG.a = ~RG.a;
+		ph = BYTE( ~cz ); // N=1 H=1
+		goto loop;
+
+	case 0x3F: // CCF
+		ph = cz | 0x100; // N=0 H=0
+		cz ^= 0x100; // C=* Z=-
+		goto loop;
+
+	case 0x37: // SCF
+		ph = cz | 0x100; // N=0 H=0
+		cz |= 0x100; // C=1 Z=-
+		goto loop;
+
+	case 0xF3: // DI
+		goto loop;
+
+	case 0xFB: // EI
+		goto loop;
+
+	case 0x27:{// DAA
+		unsigned a = RG.a;
+		int h = ph ^ cz;
+		if ( ph & 0x100 )
+		{
+			if ( (h & 0x10) || (a & 0x0F) > 9 )
+				a += 6;
+			
+			if ( (cz & 0x100) || a > 0x9F )
+				a += 0x60;
+		}
+		else
+		{
+			if ( h & 0x10 )
+				a = (a - 6) & 0xFF;
+			
+			if ( cz & 0x100 )
+				a -= 0x60;
+		}
+		cz = (cz & 0x100) | a; // C=- Z=*
+		RG.a = a;
+		ph = (ph & 0x100) + BYTE( a ); // N=- H=0
+		goto loop;
+	}
+	
+// Special
+
+	case 0x76: // HALT
+	case 0x10: // STOP
+	case 0xD3:            case 0xDB:            case 0xDD: // Illegal
+	case 0xE3: case 0xE4: case 0xEB: case 0xEC: case 0xED: // (all freeze cpu)
+	           case 0xF4:            case 0xFC: case 0xFD:
+		goto stop;
+	}
+	
+	// If this fails then an opcode isn't handled above
+	assert( false );
+	
+stop:
+	pc--;
+	
+	// copy state back
+	cpu->cpu_state_.time = time;
+	cpu->r.pc = pc;
+	cpu->r.sp = sp;
+	{
+		int t;
+		GET_FLAGS( t );
+		RG.flags = t;
+	}
+	cpu->cpu_state = &cpu->cpu_state_;
+	cpu->r.rp = RP;
+}
Index: apps/codecs/libgbs/m3u_playlist.h
===================================================================
--- apps/codecs/libgbs/m3u_playlist.h	(revision 0)
+++ apps/codecs/libgbs/m3u_playlist.h	(revision 0)
@@ -0,0 +1,57 @@
+// M3U playlist file parser, with support for subtrack information
+
+// Game_Music_Emu 0.5.2
+#ifndef M3U_PLAYLIST_H
+#define M3U_PLAYLIST_H
+
+#include "blargg_common.h"
+
+enum { max_entries = 255 };
+enum { max_size = 0x1000 };
+
+struct info_t
+{
+	char* title;
+	char* composer;
+	char* engineer;
+	char* ripping;
+	char* tagging;
+};
+
+struct entry_t
+{
+	const char* file; // filename without stupid ::TYPE suffix
+	const char* type; // if filename has ::TYPE suffix, this will be "TYPE". "" if none.
+	const char* name;
+	bool decimal_track; // true if track was specified in hex
+	                    // integers are -1 if not present
+	int track;  // 1-based
+	int length; // milliseconds
+	int intro;
+	int loop;
+	int fade;
+	int repeat; // count
+};
+
+struct M3u_Playlist {
+	int size;	
+	long raw_size;
+	int first_error;
+	
+	struct info_t info;
+	struct entry_t entries [max_entries];
+	char data [max_size];
+};
+
+// Initialize M3u_Playlist structure
+static inline void M3u_clear( struct M3u_Playlist* this )
+{
+	this->first_error = 0;
+	memset( this->entries, 0, sizeof this->entries );
+	memset( this->data, 0, sizeof this->data );
+	memset( &this->info, 0, sizeof this->info );
+	this->raw_size = 0;
+	this->size = 0;
+}
+
+#endif
Index: apps/codecs/libgbs/rom_data.h
===================================================================
--- apps/codecs/libgbs/rom_data.h	(revision 0)
+++ apps/codecs/libgbs/rom_data.h	(revision 0)
@@ -0,0 +1,72 @@
+// Common aspects of emulators which use rom data
+
+// Game_Music_Emu 0.5.2
+#ifndef ROM_DATA_H
+#define ROM_DATA_H
+
+#include "blargg_common.h"
+#include "blargg_source.h"
+
+// ROM data handler, used by several Classic_Emu derivitives. Loads file data
+// with padding on both sides, allowing direct use in bank mapping. The main purpose
+// is to allow all file data to be loaded with only one read() call (for efficiency).
+
+extern const char gme_wrong_file_type []; // declared in gme.h
+
+enum { bank_size = 0x4000 };
+enum { pad_extra = 8 };
+enum { pad_size = bank_size + pad_extra };
+enum { rom_size = 2 * pad_size };
+
+struct  Rom_Data {	
+	byte* file_data;
+	long file_size;
+	
+	blargg_long rom_addr;
+	blargg_long mask;
+	blargg_long size; // TODO: eliminate
+	blargg_long rsize_;
+	
+	// Unmapped space
+	byte unmapped [rom_size];
+};
+
+// Load file data, using already-loaded header 'h' if not NULL. Copy header
+// from loaded file data into *out and fill unmapped bytes with 'fill'.
+blargg_err_t Rom_load( struct Rom_Data* this, void* data, long size, int header_size, void* header_out, int fill );
+
+// Set address that file data should start at
+void Rom_set_addr( struct Rom_Data* this, long addr );
+
+// Mask address to nearest power of two greater than size()
+static inline blargg_long mask_addr( blargg_long addr, blargg_long mask )
+{
+	#ifdef check
+		check( addr <= mask );
+	#endif
+	return addr & mask;
+}
+
+// Pointer to page starting at addr. Returns unmapped() if outside data.
+static inline byte* Rom_at_addr( struct Rom_Data* this, blargg_long addr )
+{
+	blargg_ulong offset = mask_addr( addr, this->mask ) - this->rom_addr;
+	if ( offset > (blargg_ulong) (this->rsize_ - pad_size) )
+		offset = 0; // unmapped
+			
+	if ( offset < pad_size	) return &this->unmapped [offset];
+	else return &this->file_data [offset - pad_size];
+}
+
+
+#ifndef GME_APU_HOOK
+	#define GME_APU_HOOK( emu, addr, data ) ((void) 0)
+#endif
+
+#ifndef GME_FRAME_HOOK
+	#define GME_FRAME_HOOK( emu ) ((void) 0)
+#else
+	#define GME_FRAME_HOOK_DEFINED 1
+#endif
+
+#endif
Index: apps/codecs/libgbs/gb_apu.c
===================================================================
--- apps/codecs/libgbs/gb_apu.c	(revision 0)
+++ apps/codecs/libgbs/gb_apu.c	(revision 0)
@@ -0,0 +1,410 @@
+// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
+
+#include "gb_apu.h"
+
+//#include "gb_apu_logger.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const vol_reg    = 0xFF24;
+int const stereo_reg = 0xFF25;
+int const status_reg = 0xFF26;
+int const wave_ram   = 0xFF30;
+
+int const power_mask = 0x80;
+
+inline int calc_output( struct Gb_Apu* this, int osc )
+{
+	int bits = this->regs [stereo_reg - io_addr] >> osc;
+	return (bits >> 3 & 2) | (bits & 1);
+}
+
+void Apu_set_output( struct Gb_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
+{
+	// Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+	require( !center || (center && !left && !right) || (center && left && right) );
+	require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
+	
+	if ( !center || !left || !right )
+	{
+		left  = center;
+		right = center;
+	}
+	
+	struct Gb_Osc* o = this->oscs [i];
+	o->outputs [1] = right;
+	o->outputs [2] = left;
+	o->outputs [3] = center;
+	o->output = o->outputs [calc_output( this, i )];
+}
+
+void synth_volume( struct Gb_Apu* this, int iv )
+{
+	double v = this->volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv;
+	Synth_volume( &this->synth, v );
+}
+
+void apply_volume( struct Gb_Apu* this )
+{
+	// TODO: Doesn't handle differing left and right volumes (panning).
+	// Not worth the complexity.
+	int data  = this->regs [vol_reg - io_addr];
+	int left  = data >> 4 & 7;
+	int right = data & 7;
+	//if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 );
+	//if ( left != right ) dprintf( "l: %d r: %d\n", left, right );
+	synth_volume( this, max( left, right ) + 1 );
+}
+
+void Apu_volume( struct Gb_Apu* this, double v )
+{
+	if ( this->volume_ != v )
+	{
+		this->volume_ = v;
+		apply_volume( this );
+	}
+}
+
+void reset_regs( struct Gb_Apu* this )
+{
+	int i;
+	for ( i = 0; i < 0x20; i++ )
+		this->regs [i] = 0;
+	
+	Sweep_reset ( &this->square1 );
+	Square_reset( &this->square2 );
+	Wave_reset  ( &this->wave );
+	Noise_reset ( &this->noise );
+	
+	apply_volume( this );
+}
+
+void reset_lengths( struct Gb_Apu* this )
+{
+	this->square1.osc.length_ctr = 64;
+	this->square2.osc.length_ctr = 64;
+	this->wave   .osc.length_ctr = 256;
+	this->noise  .osc.length_ctr = 64;
+}
+
+void Apu_reduce_clicks( struct Gb_Apu* this, bool reduce )
+{
+	this->reduce_clicks_ = reduce;
+	
+	// Click reduction makes DAC off generate same output as volume 0
+	int dac_off_amp = 0;
+	if ( reduce && this->wave.osc.mode != mode_agb ) // AGB already eliminates clicks
+		dac_off_amp = -dac_bias;
+
+	int i;
+	for ( i = 0; i < osc_count; i++ )
+		this->oscs [i]->dac_off_amp = dac_off_amp;
+	
+	// AGB always eliminates clicks on wave channel using same method
+	if ( this->wave.osc.mode == mode_agb )
+		this->wave.osc.dac_off_amp = -dac_bias;
+}
+
+void Apu_reset( struct Gb_Apu* this, enum gb_mode_t mode, bool agb_wave )
+{
+	// Hardware mode
+	if ( agb_wave )
+		mode = mode_agb; // using AGB wave features implies AGB hardware
+	this->wave.agb_mask = agb_wave ? 0xFF : 0;
+	int i;
+	for ( i = 0; i < osc_count; i++ )
+		this->oscs [i]->mode = mode;
+	Apu_reduce_clicks( this, this->reduce_clicks_ );
+	
+	// Reset state
+	this->frame_time  = 0;
+	this->last_time   = 0;
+	this->frame_phase = 0;
+	
+	reset_regs( this );
+	reset_lengths( this );
+	
+	// Load initial wave RAM
+	static byte const initial_wave [2] [16] ICONST_ATTR = {
+		{0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA},
+		{0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF},
+	};
+	int b;
+	for ( b = 2; --b >= 0; )
+	{
+		// Init both banks (does nothing if not in AGB mode)
+		// TODO: verify that this works
+		Apu_write_register( this, 0, 0xFF1A, b * 0x40 );
+		unsigned i;
+		for ( i = 0; i < sizeof initial_wave [0]; i++ )
+			Apu_write_register( this, 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] );
+	}
+}
+
+void Apu_set_tempo( struct Gb_Apu* this, double t )
+{
+	this->frame_period = 4194304 / 512; // 512 Hz
+	if ( t != 1.0 )
+		this->frame_period = t ? (blip_time_t) (this->frame_period / t) : (blip_time_t) (0);
+}
+
+void Apu_init( struct Gb_Apu* this )
+{
+	this->wave.wave_ram = &this->regs [wave_ram - io_addr];
+	
+    Synth_init( &this->synth );
+	
+	this->oscs [0] = &this->square1.osc;
+	this->oscs [1] = &this->square2.osc;
+	this->oscs [2] = &this->wave.osc;
+	this->oscs [3] = &this->noise.osc;
+	
+	int i;
+	for ( i = osc_count; --i >= 0; )
+	{
+		struct Gb_Osc* o = this->oscs [i];
+		o->regs        = &this->regs [i * 5];
+		o->output      = NULL;
+		o->outputs [0] = NULL;
+		o->outputs [1] = NULL;
+		o->outputs [2] = NULL;
+		o->outputs [3] = NULL;
+		o->synth       = &this->synth;       
+	}
+	
+	this->reduce_clicks_ = false;
+	Apu_set_tempo( this, 1.0 );
+	this->volume_ = 1.0;
+	Apu_reset( this, mode_cgb, false );
+}
+
+void run_until_( struct Gb_Apu* this, blip_time_t end_time )
+{
+	if ( !this->frame_period )
+		this->frame_time += end_time - this->last_time;
+	
+	while ( true )
+	{
+		// run oscillators
+		blip_time_t time = end_time;
+		if ( time > this->frame_time )
+			time = this->frame_time;
+		
+		Square_run( &this->square1, this->last_time, time );
+		Square_run( &this->square2, this->last_time, time );
+		Wave_run  ( &this->wave, this->last_time, time );
+		Noise_run ( &this->noise, this->last_time, time );
+		this->last_time = time;
+		
+		if ( time == end_time )
+			break;
+		
+		// run frame sequencer
+		assert( this->frame_period );
+		this->frame_time += this->frame_period * clk_mul;
+		switch ( this->frame_phase++ )
+		{
+		case 2:
+		case 6:
+			// 128 Hz
+			clock_sweep( &this->square1 );
+		case 0:
+		case 4:
+			// 256 Hz
+			Osc_clock_length( &this->square1.osc );
+			Osc_clock_length( &this->square2.osc);
+			Osc_clock_length( &this->wave.osc);
+			Osc_clock_length( &this->noise.osc);
+			break;
+		
+		case 7:
+			// 64 Hz
+			this->frame_phase = 0;
+			Square_clock_envelope( &this->square1 );
+			Square_clock_envelope( &this->square2 );
+			Noise_clock_envelope( &this->noise );
+		}
+	}
+}
+
+inline void run_until( struct Gb_Apu* this, blip_time_t time )
+{
+	require( time >= this->last_time ); // end_time must not be before previous time
+	if ( time > this->last_time )
+		run_until_( this, time );
+}
+
+void Apu_end_frame( struct Gb_Apu* this, blip_time_t end_time )
+{
+	#ifdef LOG_FRAME
+		LOG_FRAME( end_time );
+	#endif
+	
+	if ( end_time > this->last_time )
+		run_until( this, end_time );
+	
+	this->frame_time -= end_time;
+	assert( this->frame_time >= 0 );
+	
+	this->last_time -= end_time;
+	assert( this->last_time >= 0 );
+}
+
+void silence_osc( struct Gb_Apu* this, struct Gb_Osc* o )
+{
+	int delta = -o->last_amp;
+	if ( this->reduce_clicks_ )
+		delta += o->dac_off_amp;
+	
+	if ( delta )
+	{
+		o->last_amp = o->dac_off_amp;
+		if ( o->output )
+		{
+			Blip_set_modified( o->output );
+			Synth_offset( &this->synth, this->last_time, delta, o->output );
+		}
+	}
+}
+
+void apply_stereo( struct Gb_Apu* this )
+{
+	int i;
+	for ( i = osc_count; --i >= 0; )
+	{
+		struct Gb_Osc* o = this->oscs [i];
+		struct Blip_Buffer* out = o->outputs [calc_output( this, i )];
+		if ( o->output != out )
+		{
+			silence_osc( this, o );
+			o->output = out;
+		}
+	}
+}
+
+void Apu_write_register( struct Gb_Apu* this, blip_time_t time, int addr, int data )
+{
+	require( (unsigned) data < 0x100 );
+	
+	int reg = addr - io_addr;
+	if ( (unsigned) reg >= io_size )
+	{
+		require( false );
+		return;
+	}
+	
+	#ifdef LOG_WRITE
+		LOG_WRITE( time, addr, data );
+	#endif
+	
+	if ( addr < status_reg && !(this->regs [status_reg - io_addr] & power_mask) )
+	{
+		// Power is off
+		
+		// length counters can only be written in DMG mode
+		if ( this->wave.osc.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) )
+			return;
+		
+		if ( reg < 10 )
+			data &= 0x3F; // clear square duty
+	}
+	
+	run_until( this, time );
+	
+	if ( addr >= wave_ram )
+	{
+		Wave_write( &this->wave, addr, data );
+	}
+	else
+	{
+		int old_data = this->regs [reg];
+		this->regs [reg] = data;
+		
+		if ( addr < vol_reg )
+		{
+			// Oscillator
+			write_osc( this, reg, old_data, data );
+		}
+		else if ( addr == vol_reg && data != old_data )
+		{
+			// Master volume
+			int i;
+			for ( i = osc_count; --i >= 0; )
+				silence_osc( this, this->oscs [i] );
+			
+			apply_volume( this );
+		}
+		else if ( addr == stereo_reg )
+		{
+			// Stereo panning
+			apply_stereo( this );
+		}
+		else if ( addr == status_reg && (data ^ old_data) & power_mask )
+		{
+			// Power control
+			this->frame_phase = 0;
+			int i;
+			for ( i = osc_count; --i >= 0; )
+				silence_osc( this, this->oscs [i] );
+		
+			reset_regs( this );
+			if ( this->wave.osc.mode != mode_dmg )
+				reset_lengths( this );
+			
+			this->regs [status_reg - io_addr] = data;
+		}
+	}
+}
+
+int Apu_read_register( struct Gb_Apu* this, blip_time_t time, int addr )
+{
+	if ( addr >= status_reg )
+		run_until( this, time );
+	
+	int reg = addr - io_addr;
+	if ( (unsigned) reg >= io_size )
+	{
+		require( false );
+		return 0;
+	}
+	
+	if ( addr >= wave_ram )
+		return Wave_read( &this->wave, addr );
+	
+	// Value read back has some bits always set
+	static byte const masks [] ICONST_ATTR = {
+		0x80,0x3F,0x00,0xFF,0xBF,
+		0xFF,0x3F,0x00,0xFF,0xBF,
+		0x7F,0xFF,0x9F,0xFF,0xBF,
+		0xFF,0xFF,0x00,0x00,0xBF,
+		0x00,0x00,0x70,
+		0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
+	};
+	int mask = masks [reg];
+	if ( this->wave.agb_mask && (reg == 10 || reg == 12) )
+		mask = 0x1F; // extra implemented bits in wave regs on AGB
+	int data = this->regs [reg] | mask;
+	
+	// Status register
+	if ( addr == status_reg )
+	{
+		data &= 0xF0;
+		data |= (int) this->square1.osc.enabled << 0;
+		data |= (int) this->square2.osc.enabled << 1;
+		data |= (int) this->wave   .osc.enabled << 2;
+		data |= (int) this->noise  .osc.enabled << 3;
+	}
+	
+	return data;
+}
Index: apps/codecs/libgbs/libgbs.make
===================================================================
--- apps/codecs/libgbs/libgbs.make	(revision 0)
+++ apps/codecs/libgbs/libgbs.make	(revision 0)
@@ -0,0 +1,17 @@
+
+# libgbs
+GBSLIB := $(CODECDIR)/libgbs.a
+GBSLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libgbs/SOURCES)
+GBSLIB_OBJ := $(call c2obj, $(GBSLIB_SRC))
+OTHER_SRC += $(GBSLIB_SRC)
+
+$(GBSLIB): $(GBSLIB_OBJ)
+	$(SILENT)$(shell rm -f $@)
+	$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
+
+GBSFLAGS = $(filter-out -O%,$(CODECFLAGS)) -fno-strict-aliasing
+GBSFLAGS += -O1
+
+$(CODECDIR)/libgbs/%.o: $(ROOTDIR)/apps/codecs/libgbs/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(GBSFLAGS) -c $< -o $@
Index: apps/codecs/lib/codeclib.c
===================================================================
--- apps/codecs/lib/codeclib.c	(revision 29484)
+++ apps/codecs/lib/codeclib.c	(working copy)
@@ -105,6 +105,11 @@
     return(ci->strlen(s));
 }
 
+char *strrchr(const char *s, int c)
+{
+    return(ci->strrchr(s, c));
+}
+
 char *strcpy(char *dest, const char *src)
 {
     return(ci->strcpy(dest,src));
@@ -120,6 +125,11 @@
     return(ci->strcmp(s1,s2));
 }
 
+int strcasecmp(const char *s1, const char *s2)
+{
+    return(ci->strcasecmp(s1, s2));
+}
+
 void *memcpy(void *dest, const void *src, size_t n)
 {
     return(ci->memcpy(dest,src,n));
Index: apps/codecs/lib/codeclib.h
===================================================================
--- apps/codecs/lib/codeclib.h	(revision 29484)
+++ apps/codecs/lib/codeclib.h	(working copy)
@@ -59,6 +59,7 @@
 void *memmove(void *s1, const void *s2, size_t n);
 
 size_t strlen(const char *s);
+char *strrchr(const char *s, int c);
 char *strcpy(char *dest, const char *src);
 char *strcat(char *dest, const char *src);
 
@@ -66,6 +67,7 @@
  * breaks if we write down strcmp's prototype */
 #undef strcmp
 int strcmp(const char *s1, const char *s2);
+int strcasecmp(const char *s1, const char *s2);
 
 void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
 
Index: apps/codecs/codecs.make
===================================================================
--- apps/codecs/codecs.make	(revision 29484)
+++ apps/codecs/codecs.make	(working copy)
@@ -43,6 +43,7 @@
 include $(APPSDIR)/codecs/libatrac/libatrac.make
 include $(APPSDIR)/codecs/libpcm/libpcm.make
 include $(APPSDIR)/codecs/libtta/libtta.make
+include $(APPSDIR)/codecs/libgbs/libgbs.make
 
 # compile flags for codecs
 CODECFLAGS = $(CFLAGS) -fstrict-aliasing -I$(APPSDIR)/codecs \
@@ -93,6 +94,7 @@
 $(CODECDIR)/vox.codec : $(CODECDIR)/libpcm.a
 $(CODECDIR)/wav64.codec : $(CODECDIR)/libpcm.a
 $(CODECDIR)/tta.codec : $(CODECDIR)/libtta.a
+$(CODECDIR)/gbs.codec : $(CODECDIR)/libgbs.a
 
 $(CODECS): $(CODECLIB) # this must be last in codec dependency list
 
Index: apps/codecs/SOURCES
===================================================================
--- apps/codecs/SOURCES	(revision 29484)
+++ apps/codecs/SOURCES	(working copy)
@@ -33,6 +33,7 @@
 wav64.c
 tta.c
 wmapro.c
+gbs.c
 
 #ifdef HAVE_RECORDING
 
