Submit initial patch from FS#12176. Adds support for several new game music formats (AY, GBS, HES, KSS, SGC, VGM and VGZ) and replaces the current NSF and NSFE with a new implementation based on a port of the Game Music Emu library 'GME'. This first submit does not cover the full functionality provided by the author's original patch: Coleco-SGV is not supported, some GME-specific m3u-support has been removed and IRAM is not used yet. Further changes are very likely to follow this submit. Thanks to Mauricio Garrido.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30264 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/SOURCES b/apps/SOURCES
index 79642e1..26e53d1 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -227,6 +227,12 @@
 metadata/au.c
 metadata/vox.c
 metadata/tta.c
+metadata/ay.c
+metadata/gbs.c
+metadata/hes.c
+metadata/sgc.c
+metadata/vgm.c
+metadata/kss.c
 #endif
 #ifdef HAVE_TAGCACHE
 tagcache.c
diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES
index d950ffd..508969b 100644
--- a/apps/codecs/SOURCES
+++ b/apps/codecs/SOURCES
@@ -33,6 +33,12 @@
 wav64.c
 tta.c
 wmapro.c
+ay.c
+gbs.c
+hes.c
+sgc.c
+vgm.c
+kss.c
 
 #ifdef HAVE_RECORDING
 
diff --git a/apps/codecs/ay.c b/apps/codecs/ay.c
new file mode 100644
index 0000000..baccf3d
--- /dev/null
+++ b/apps/codecs/ay.c
@@ -0,0 +1,137 @@
+
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+
+#include <codecs/lib/codeclib.h>
+#include "libgme/ay_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 Ay_Emu ay_emu IDATA_ATTR CACHEALIGN_ATTR;
+
+/****************** rockbox interface ******************/
+
+static void set_codec_track(int t, int multitrack) {
+    Ay_start_track(&ay_emu, t); 
+
+    /* for REPEAT_ONE we disable track limits */
+    if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+        Track_set_fade(&ay_emu, Track_get_length( &ay_emu, t ) - 4000, 4000);
+    }
+    if (multitrack) ci->set_elapsed(t*1000); /* t is track no to display */
+    else ci->set_elapsed(0);
+}
+
+/* this is the codec entry point */
+enum codec_status codec_main(enum codec_entry_call_reason reason)
+{
+    if (reason == CODEC_LOAD) {
+        /* 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);
+
+        Ay_init(&ay_emu);
+        Ay_set_sample_rate(&ay_emu, 44100);
+    }
+
+    return CODEC_OK;
+}
+
+/* this is called for each file to process */
+enum codec_status codec_run(void)
+{
+    blargg_err_t err;
+    uint8_t *buf;
+    size_t n;
+    int track, is_multitrack;
+    intptr_t param;
+    uint32_t elapsed_time;
+
+    /* reset values */
+    track = is_multitrack = 0;
+    elapsed_time = 0;
+
+    DEBUGF("AY: next_track\n");
+    if (codec_init()) {
+        return CODEC_ERROR;
+    }  
+
+    codec_set_replaygain(ci->id3);
+        
+    /* Read the entire file */
+    DEBUGF("AY: request file\n");
+    ci->seek_buffer(0);
+    buf = ci->request_buffer(&n, ci->filesize);
+    if (!buf || n < (size_t)ci->filesize) {
+        DEBUGF("AY: file load failed\n");
+        return CODEC_ERROR;
+    }
+   
+    if ((err = Ay_load_mem(&ay_emu, buf, ci->filesize))) {
+        DEBUGF("AY: Ay_load failed (%s)\n", err);
+        return CODEC_ERROR;
+    }
+
+    /* Update internal track count */
+    if (ay_emu.m3u.size > 0)
+        ay_emu.track_count = ay_emu.m3u.size;
+
+    /* Check if file has multiple tracks */
+    if (ay_emu.track_count > 1) {
+        is_multitrack = 1;
+    }
+
+next_track:
+    set_codec_track(track, is_multitrack);
+
+    /* The main decoder loop */
+    while (1) {
+        enum codec_command_action action = ci->get_command(&param);
+
+        if (action == CODEC_ACTION_HALT)
+            break;
+
+        if (action == CODEC_ACTION_SEEK_TIME) {
+            if (is_multitrack) {
+                track = param/1000;
+                ci->seek_complete();
+                if (track >= ay_emu.track_count) break;
+                goto next_track;
+            }
+
+            ci->set_elapsed(param);
+            elapsed_time = param;
+            Track_seek(&ay_emu, param);
+            ci->seek_complete();
+            
+            /* Set fade again */
+            if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+                Track_set_fade(&ay_emu, Track_get_length( &ay_emu, track ) - 4000, 4000);
+            }
+        }
+
+        /* Generate audio buffer */
+        err = Ay_play(&ay_emu, CHUNK_SIZE, samples);
+        if (err || ay_emu.track_ended) {
+            track++;
+            if (track >= ay_emu.track_count) break;
+            goto next_track;
+        }
+
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+
+        /* Set elapsed time for one track files */
+        if (!is_multitrack) {
+            elapsed_time += (CHUNK_SIZE / 2) / 44.1;
+            ci->set_elapsed(elapsed_time);
+        }
+    }
+
+    return CODEC_OK;
+}
diff --git a/apps/codecs/codecs.make b/apps/codecs/codecs.make
index 1a5dd8f..19ff60b 100644
--- a/apps/codecs/codecs.make
+++ b/apps/codecs/codecs.make
@@ -43,6 +43,13 @@
 include $(APPSDIR)/codecs/libatrac/libatrac.make
 include $(APPSDIR)/codecs/libpcm/libpcm.make
 include $(APPSDIR)/codecs/libtta/libtta.make
+include $(APPSDIR)/codecs/libgme/libay.make
+include $(APPSDIR)/codecs/libgme/libgbs.make
+include $(APPSDIR)/codecs/libgme/libhes.make
+include $(APPSDIR)/codecs/libgme/libnsf.make
+include $(APPSDIR)/codecs/libgme/libsgc.make
+include $(APPSDIR)/codecs/libgme/libvgm.make
+include $(APPSDIR)/codecs/libgme/libkss.make
 
 # compile flags for codecs
 CODECFLAGS = $(CFLAGS) -fstrict-aliasing -I$(APPSDIR)/codecs \
@@ -93,6 +100,13 @@
 $(CODECDIR)/vox.codec : $(CODECDIR)/libpcm.a
 $(CODECDIR)/wav64.codec : $(CODECDIR)/libpcm.a
 $(CODECDIR)/tta.codec : $(CODECDIR)/libtta.a
+$(CODECDIR)/ay.codec : $(CODECDIR)/libay.a
+$(CODECDIR)/gbs.codec : $(CODECDIR)/libgbs.a
+$(CODECDIR)/hes.codec : $(CODECDIR)/libhes.a
+$(CODECDIR)/nsf.codec : $(CODECDIR)/libnsf.a
+$(CODECDIR)/sgc.codec : $(CODECDIR)/libsgc.a
+$(CODECDIR)/vgm.codec : $(CODECDIR)/libvgm.a
+$(CODECDIR)/kss.codec : $(CODECDIR)/libkss.a
 
 $(CODECS): $(CODECLIB) # this must be last in codec dependency list
 
diff --git a/apps/codecs/gbs.c b/apps/codecs/gbs.c
new file mode 100644
index 0000000..bc6d31e
--- /dev/null
+++ b/apps/codecs/gbs.c
@@ -0,0 +1,108 @@
+
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+
+#include <codecs/lib/codeclib.h>
+#include "libgme/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->set_elapsed(t*1000); /* t is track no to display */
+}
+
+/* this is the codec entry point */
+enum codec_status codec_main(enum codec_entry_call_reason reason)
+{
+    if (reason == CODEC_LOAD) {
+        /* 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);
+    }
+
+    return CODEC_OK;
+}
+
+/* this is called for each file to process */
+enum codec_status codec_run(void)
+{
+    blargg_err_t err;
+    uint8_t *buf;
+    size_t n;
+    intptr_t param;
+    int track = 0;
+
+    DEBUGF("GBS: next_track\n");
+    if (codec_init()) {
+        return CODEC_ERROR;
+    }
+
+    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;
+    }
+
+    /* Update internal track count */
+    if (gbs_emu.m3u.size > 0)
+        gbs_emu.track_count = gbs_emu.m3u.size;
+
+next_track:
+    set_codec_track(track);
+
+    /* The main decoder loop */
+    while (1) {
+        enum codec_command_action action = ci->get_command(&param);
+
+        if (action == CODEC_ACTION_HALT)
+            break;
+
+        if (action == CODEC_ACTION_SEEK_TIME) {
+            track = param/1000;
+            ci->seek_complete();
+            if (track >= gbs_emu.track_count) break;
+            goto next_track;
+        }
+
+        /* 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_track;
+        }
+
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+    }
+
+    return CODEC_OK;
+}
diff --git a/apps/codecs/hes.c b/apps/codecs/hes.c
new file mode 100644
index 0000000..598d787
--- /dev/null
+++ b/apps/codecs/hes.c
@@ -0,0 +1,108 @@
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+
+#include <string.h>
+#include "codeclib.h"
+#include "libgme/hes_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 Hes_Emu hes_emu IDATA_ATTR CACHEALIGN_ATTR;
+
+/****************** rockbox interface ******************/
+
+static void set_codec_track(int t) {
+    Hes_start_track(&hes_emu, t); 
+
+    /* for REPEAT_ONE we disable track limits */
+    if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+        Track_set_fade(&hes_emu, Track_get_length( &hes_emu, t ), 4000);
+    }
+    ci->set_elapsed(t*1000); /* t is track no to display */
+}
+
+/* this is the codec entry point */
+enum codec_status codec_main(enum codec_entry_call_reason reason)
+{
+    if (reason == CODEC_LOAD) {
+        /* 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);
+
+        Hes_init(&hes_emu);
+        Hes_set_sample_rate(&hes_emu, 44100);
+    }
+
+    return CODEC_OK;
+}
+
+/* this is called for each file to process */
+enum codec_status codec_run(void)
+{
+    blargg_err_t err;
+    uint8_t *buf;
+    size_t n;
+    intptr_t param;
+    int track = 0;
+    
+    DEBUGF("HES: next_track\n");
+    if (codec_init()) {
+        return CODEC_ERROR;
+    }  
+
+    codec_set_replaygain(ci->id3);
+
+    /* Read the entire file */
+    DEBUGF("HES: request file\n");
+    ci->seek_buffer(0);
+    buf = ci->request_buffer(&n, ci->filesize);
+    if (!buf || n < (size_t)ci->filesize) {
+        DEBUGF("HES: file load failed\n");
+        return CODEC_ERROR;
+    }
+
+    if ((err = Hes_load(&hes_emu, buf, ci->filesize))) {
+        DEBUGF("HES: Hes_load failed (%s)\n", err);
+        return CODEC_ERROR;
+    }
+
+    /* Update internal track count */
+    if (hes_emu.m3u.size > 0)
+        hes_emu.track_count = hes_emu.m3u.size;
+
+next_track:
+    set_codec_track(track);
+
+    /* The main decoder loop */
+    while ( 1 ) {
+        enum codec_command_action action = ci->get_command(&param);
+
+        if (action == CODEC_ACTION_HALT)
+            break;
+
+        if (action == CODEC_ACTION_SEEK_TIME) {
+            track = param/1000;
+            ci->seek_complete();
+            if (track >= hes_emu.track_count) break;
+            goto next_track;
+        }
+
+        /* Generate audio buffer */
+        err = Hes_play(&hes_emu, CHUNK_SIZE, samples);
+        if (err || hes_emu.track_ended) {
+            track++;
+            if (track >= hes_emu.track_count) break;
+            goto next_track;
+        }
+
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+    }
+
+    return CODEC_OK;
+}
diff --git a/apps/codecs/kss.c b/apps/codecs/kss.c
new file mode 100644
index 0000000..9db1652
--- /dev/null
+++ b/apps/codecs/kss.c
@@ -0,0 +1,113 @@
+
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+
+#include <codecs/lib/codeclib.h>
+#include "libgme/kss_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 Kss_Emu kss_emu IDATA_ATTR CACHEALIGN_ATTR;
+
+/****************** rockbox interface ******************/
+
+static void set_codec_track(int t) {
+    Kss_start_track(&kss_emu, t); 
+
+    /* for REPEAT_ONE we disable track limits */
+    if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+        Track_set_fade(&kss_emu, Track_get_length( &kss_emu, t ), 4000);
+    }
+    ci->set_elapsed(t*1000); /* t is track no to display */
+}
+
+/* this is the codec entry point */
+enum codec_status codec_main(enum codec_entry_call_reason reason)
+{
+    if (reason == CODEC_LOAD) {
+        /* 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);
+
+        Kss_init(&kss_emu);
+        Kss_set_sample_rate(&kss_emu, 44100);
+    }
+
+    return CODEC_OK;
+}
+
+/* this is called for each file to process */
+enum codec_status codec_run(void)
+{
+    blargg_err_t err;
+    uint8_t *buf;
+    size_t n;
+    int track;
+    intptr_t param;
+    uint32_t elapsed_time;
+
+    /* reset values */
+    track = 0;
+    elapsed_time = 0;
+
+    DEBUGF("KSS: next_track\n");
+    if (codec_init()) {
+        return CODEC_ERROR;
+    }  
+
+    codec_set_replaygain(ci->id3);
+        
+    /* Read the entire file */
+    DEBUGF("KSS: request file\n");
+    ci->seek_buffer(0);
+    buf = ci->request_buffer(&n, ci->filesize);
+    if (!buf || n < (size_t)ci->filesize) {
+        DEBUGF("KSS: file load failed\n");
+        return CODEC_ERROR;
+    }
+   
+    if ((err = Kss_load_mem(&kss_emu, buf, ci->filesize))) {
+        DEBUGF("KSS: Kss_load failed (%s)\n", err);
+        return CODEC_ERROR;
+    }
+
+    /* Update internal track count */
+    if (kss_emu.m3u.size > 0)
+        kss_emu.track_count = kss_emu.m3u.size;
+
+next_track:
+    set_codec_track(track);
+
+    /* The main decoder loop */
+    while (1) {
+        enum codec_command_action action = ci->get_command(&param);
+
+        if (action == CODEC_ACTION_HALT)
+            break;
+
+        if (action == CODEC_ACTION_SEEK_TIME) {
+                track = param/1000;
+                ci->seek_complete();
+                if (track >= kss_emu.track_count) break;
+                goto next_track;
+        }
+
+        /* Generate audio buffer */
+        err = Kss_play(&kss_emu, CHUNK_SIZE, samples);
+        if (err || kss_emu.track_ended) {
+            track++;
+            if (track >= kss_emu.track_count) break;
+            goto next_track;
+        }
+
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+    }
+
+    return CODEC_OK;
+}
diff --git a/apps/codecs/libgme/2413tone.h b/apps/codecs/libgme/2413tone.h
new file mode 100644
index 0000000..e4366ab
--- /dev/null
+++ b/apps/codecs/libgme/2413tone.h
@@ -0,0 +1,20 @@
+/* YM2413 tone by okazaki@angel.ne.jp */

+0x49,0x4c,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x61,0x61,0x1e,0x17,0xf0,0x7f,0x00,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x13,0x41,0x16,0x0e,0xfd,0xf4,0x23,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x03,0x01,0x9a,0x04,0xf3,0xf3,0x13,0xf3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x11,0x61,0x0e,0x07,0xfa,0x64,0x70,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x22,0x16,0x05,0xf0,0x71,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x61,0x1d,0x07,0x82,0x80,0x17,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x23,0x21,0x2d,0x16,0x90,0x90,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x21,0x1b,0x06,0x64,0x65,0x10,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x21,0x0b,0x1a,0x85,0xa0,0x70,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x23,0x01,0x83,0x10,0xff,0xb4,0x10,0xf4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x97,0xc1,0x20,0x07,0xff,0xf4,0x22,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x61,0x00,0x0c,0x05,0xc2,0xf6,0x40,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x01,0x56,0x03,0x94,0xc2,0x03,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x01,0x89,0x03,0xf1,0xe4,0xf0,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

diff --git a/apps/codecs/libgme/281btone.h b/apps/codecs/libgme/281btone.h
new file mode 100644
index 0000000..1300523
--- /dev/null
+++ b/apps/codecs/libgme/281btone.h
@@ -0,0 +1,20 @@
+/* YMF281B tone by Chabin */

+0x49,0x4c,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

diff --git a/apps/codecs/libgme/AYSOURCES b/apps/codecs/libgme/AYSOURCES
new file mode 100644
index 0000000..51253fe
--- /dev/null
+++ b/apps/codecs/libgme/AYSOURCES
@@ -0,0 +1,6 @@
+ay_apu.c
+ay_cpu.c
+ay_emu.c
+blip_buffer.c
+multi_buffer.c
+z80_cpu.c
diff --git a/apps/codecs/libgme/GBSSOURCES b/apps/codecs/libgme/GBSSOURCES
new file mode 100644
index 0000000..5548fd8
--- /dev/null
+++ b/apps/codecs/libgme/GBSSOURCES
@@ -0,0 +1,8 @@
+gb_apu.c
+gb_cpu.c
+gbs_cpu.c
+gb_oscs.c
+gbs_emu.c
+blip_buffer.c
+multi_buffer.c
+rom_data.c
diff --git a/apps/codecs/libgme/HESSOURCES b/apps/codecs/libgme/HESSOURCES
new file mode 100644
index 0000000..58a38f2
--- /dev/null
+++ b/apps/codecs/libgme/HESSOURCES
@@ -0,0 +1,7 @@
+hes_apu.c
+hes_apu_adpcm.c
+hes_cpu.c
+hes_emu.c
+blip_buffer.c
+multi_buffer.c
+rom_data.c
diff --git a/apps/codecs/libgme/KSSSOURCES b/apps/codecs/libgme/KSSSOURCES
new file mode 100644
index 0000000..a934bec
--- /dev/null
+++ b/apps/codecs/libgme/KSSSOURCES
@@ -0,0 +1,14 @@
+ay_apu.c
+kss_cpu.c
+kss_emu.c
+kss_scc_apu.c
+opl_apu.c
+sms_apu.c
+ymdeltat.c
+z80_cpu.c
+blip_buffer.c
+multi_buffer.c
+rom_data.c
+emu2413.c
+emu8950.c
+emuadpcm.c
diff --git a/apps/codecs/libgme/NSFSOURCES b/apps/codecs/libgme/NSFSOURCES
new file mode 100644
index 0000000..d96e1d3
--- /dev/null
+++ b/apps/codecs/libgme/NSFSOURCES
@@ -0,0 +1,15 @@
+nes_apu.c
+nes_cpu.c
+nes_fds_apu.c
+nes_fme7_apu.c
+nes_namco_apu.c
+nes_oscs.c
+nes_vrc6_apu.c
+nes_vrc7_apu.c
+nsf_cpu.c
+nsf_emu.c
+nsfe_info.c
+blip_buffer.c
+multi_buffer.c
+rom_data.c
+emu2413.c
diff --git a/apps/codecs/libgme/SGCSOURCES b/apps/codecs/libgme/SGCSOURCES
new file mode 100644
index 0000000..72b06ef
--- /dev/null
+++ b/apps/codecs/libgme/SGCSOURCES
@@ -0,0 +1,10 @@
+sgc_cpu.c
+sgc_emu.c
+sms_apu.c
+sms_fm_apu.c
+ym2413_emu.c
+z80_cpu.c
+blip_buffer.c
+multi_buffer.c
+rom_data.c
+emu2413.c
diff --git a/apps/codecs/libgme/VGMSOURCES b/apps/codecs/libgme/VGMSOURCES
new file mode 100644
index 0000000..ed32bac
--- /dev/null
+++ b/apps/codecs/libgme/VGMSOURCES
@@ -0,0 +1,12 @@
+blip_buffer.c
+multi_buffer.c
+resampler.c
+sms_apu.c
+vgm_emu.c
+emu2413.c
+ym2413_emu.c
+ym2612_emu.c
+inflate/bbfuncs.c
+inflate/inflate.c
+inflate/mallocer.c
+inflate/mbreader.c
diff --git a/apps/codecs/libgme/ay_apu.c b/apps/codecs/libgme/ay_apu.c
new file mode 100644
index 0000000..a2ec299
--- /dev/null
+++ b/apps/codecs/libgme/ay_apu.c
@@ -0,0 +1,413 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "ay_apu.h"
+
+/* Copyright (C) 2006-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"
+
+// Emulation inaccuracies:
+// * Noise isn't run when not in use
+// * Changes to envelope and noise periods are delayed until next reload
+// * Super-sonic tone should attenuate output to about 60%, not 50%
+
+// Tones above this frequency are treated as disabled tone at half volume.
+// Power of two is more efficient (avoids division).
+int const inaudible_freq = 16384;
+
+int const period_factor = 16;
+
+static byte const amp_table [16] =
+{
+#define ENTRY( n ) (byte) (n * ay_amp_range + 0.5)
+	// With channels tied together and 1K resistor to ground (as datasheet recommends),
+	// output nearly matches logarithmic curve as claimed. Approx. 1.5 dB per step.
+	ENTRY(0.000000),ENTRY(0.007813),ENTRY(0.011049),ENTRY(0.015625),
+	ENTRY(0.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500),
+	ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000),
+	ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000),
+	
+	/*
+	// Measured from an AY-3-8910A chip with date code 8611.
+	
+	// Direct voltages without any load (very linear)
+	ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785),
+	ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032),
+	ENTRY(0.360215),ENTRY(0.494624),ENTRY(0.594624),ENTRY(0.672043),
+	ENTRY(0.766129),ENTRY(0.841935),ENTRY(0.926882),ENTRY(1.000000),
+	// With only some load
+	ENTRY(0.000000),ENTRY(0.011940),ENTRY(0.017413),ENTRY(0.024876),
+	ENTRY(0.036318),ENTRY(0.054229),ENTRY(0.072637),ENTRY(0.122388),
+	ENTRY(0.174129),ENTRY(0.239303),ENTRY(0.323881),ENTRY(0.410945),
+	ENTRY(0.527363),ENTRY(0.651741),ENTRY(0.832338),ENTRY(1.000000),
+	*/
+#undef ENTRY
+};
+
+static byte const modes [8] =
+{
+#define MODE( a0,a1, b0,b1, c0,c1 ) \
+		(a0 | a1<<1 | b0<<2 | b1<<3 | c0<<4 | c1<<5)
+	MODE( 1,0, 1,0, 1,0 ),
+	MODE( 1,0, 0,0, 0,0 ),
+	MODE( 1,0, 0,1, 1,0 ),
+	MODE( 1,0, 1,1, 1,1 ),
+	MODE( 0,1, 0,1, 0,1 ),
+	MODE( 0,1, 1,1, 1,1 ),
+	MODE( 0,1, 1,0, 0,1 ),
+	MODE( 0,1, 0,0, 0,0 ),
+};
+
+void set_output( struct Ay_Apu* this, struct Blip_Buffer* b )
+{
+	int i;
+	for ( i = 0; i < ay_osc_count; ++i )
+		Ay_apu_set_output( this, i, b );
+}
+
+void Ay_apu_init( struct Ay_Apu* this )
+{
+	Synth_init( &this->synth_ );
+	
+	// build full table of the upper 8 envelope waveforms
+	int m;
+	for ( m = 8; m--; )
+	{
+		byte* out = this->env_modes [m];
+		int x, y, flags = modes [m];
+		for ( x = 3; --x >= 0; )
+		{
+			int amp = flags & 1;
+			int end = flags >> 1 & 1;
+			int step = end - amp;
+			amp *= 15;
+			for ( y = 16; --y >= 0; )
+			{
+				*out++ = amp_table [amp];
+				amp += step;
+			}
+			flags >>= 2;
+		}
+	}
+	
+	set_output( this, NULL );
+	Ay_apu_volume( this, 1.0 );
+	Ay_apu_reset( this );
+}
+
+void Ay_apu_reset( struct Ay_Apu* this )
+{
+	this->addr_       = 0;
+	this->last_time   = 0;
+	this->noise_delay = 0;
+	this->noise_lfsr  = 1;
+	
+	struct osc_t* osc;
+	for ( osc = &this->oscs [ay_osc_count]; osc != this->oscs; )
+	{
+		osc--;
+		osc->period   = period_factor;
+		osc->delay    = 0;
+		osc->last_amp = 0;
+		osc->phase    = 0;
+	}
+	
+	int i;
+	for ( i = sizeof this->regs; --i >= 0; )
+		this->regs [i] = 0;
+	this->regs [7] = 0xFF;
+	write_data_( this, 13, 0 );
+}
+
+int Ay_apu_read( struct Ay_Apu* this )
+{
+	static byte const masks [ay_reg_count] = { 
+		0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0x3F,
+		0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0x00, 0x00
+	};
+	return this->regs [this->addr_] & masks [this->addr_];
+}
+
+void write_data_( struct Ay_Apu* this, int addr, int data )
+{
+	assert( (unsigned) addr < ay_reg_count );
+	
+	/* if ( (unsigned) addr >= 14 )
+		dprintf( "Wrote to I/O port %02X\n", (int) addr ); */
+	
+	// envelope mode
+	if ( addr == 13 )
+	{
+		if ( !(data & 8) ) // convert modes 0-7 to proper equivalents
+			data = (data & 4) ? 15 : 9;
+		this->env_wave = this->env_modes [data - 7];
+		this->env_pos = -48;
+		this->env_delay = 0; // will get set to envelope period in run_until()
+	}
+	this->regs [addr] = data;
+	
+	// handle period changes accurately
+	int i = addr >> 1;
+	if ( i < ay_osc_count )
+	{
+		blip_time_t period = (this->regs [i * 2 + 1] & 0x0F) * (0x100 * period_factor) +
+				this->regs [i * 2] * period_factor;
+		if ( !period )
+			period = period_factor;
+		
+		// adjust time of next timer expiration based on change in period
+		struct osc_t* osc = &this->oscs [i];
+		if ( (osc->delay += period - osc->period) < 0 )
+			osc->delay = 0;
+		osc->period = period;
+	}
+	
+	// TODO: same as above for envelope timer, and it also has a divide by two after it
+}
+
+int const noise_off = 0x08;
+int const tone_off  = 0x01;
+
+void run_until( struct Ay_Apu* this, blip_time_t final_end_time )
+{
+	require( final_end_time >= this->last_time );
+	
+	// noise period and initial values
+	blip_time_t const noise_period_factor = period_factor * 2; // verified
+	blip_time_t noise_period = (this->regs [6] & 0x1F) * noise_period_factor;
+	if ( !noise_period )
+		noise_period = noise_period_factor;
+	blip_time_t const old_noise_delay = this->noise_delay;
+	unsigned const old_noise_lfsr = this->noise_lfsr;
+	
+	// envelope period
+	blip_time_t const env_period_factor = period_factor * 2; // verified
+	blip_time_t env_period = (this->regs [12] * 0x100 + this->regs [11]) * env_period_factor;
+	if ( !env_period )
+		env_period = env_period_factor; // same as period 1 on my AY chip
+	if ( !this->env_delay )
+		this->env_delay = env_period;
+	
+	// run each osc separately
+	int index;
+	for ( index = 0; index < ay_osc_count; index++ )
+	{
+		struct osc_t* const osc = &this->oscs [index];
+		int osc_mode = this->regs [7] >> index;
+		
+		// output
+		struct Blip_Buffer* const osc_output = osc->output;
+		if ( !osc_output )
+			continue;
+		Blip_set_modified( osc_output );
+		
+		// period
+		int half_vol = 0;
+		blip_time_t inaudible_period = (unsigned) (Blip_clock_rate( osc_output ) +
+				inaudible_freq) / (unsigned) (inaudible_freq * 2);
+		if ( osc->period <= inaudible_period && !(osc_mode & tone_off) )
+		{
+			half_vol = 1; // Actually around 60%, but 50% is close enough
+			osc_mode |= tone_off;
+		}
+		
+		// envelope
+		blip_time_t start_time = this->last_time;
+		blip_time_t end_time   = final_end_time;
+		int const vol_mode = this->regs [0x08 + index];
+		int volume = amp_table [vol_mode & 0x0F] >> half_vol;
+		int osc_env_pos = this->env_pos;
+		if ( vol_mode & 0x10 )
+		{
+			volume = this->env_wave [osc_env_pos] >> half_vol;
+			// use envelope only if it's a repeating wave or a ramp that hasn't finished
+			if ( !(this->regs [13] & 1) || osc_env_pos < -32 )
+			{
+				end_time = start_time + this->env_delay;
+				if ( end_time >= final_end_time )
+					end_time = final_end_time;
+				
+				//if ( !(regs [12] | regs [11]) )
+				//  dprintf( "Used envelope period 0\n" );
+			}
+			else if ( !volume )
+			{
+				osc_mode = noise_off | tone_off;
+			}
+		}
+		else if ( !volume )
+		{
+			osc_mode = noise_off | tone_off;
+		}
+		
+		// tone time
+		blip_time_t const period = osc->period;
+		blip_time_t time = start_time + osc->delay;
+		if ( osc_mode & tone_off ) // maintain tone's phase when off
+		{
+			int count = (final_end_time - time + period - 1) / period;
+			time += count * period;
+			osc->phase ^= count & 1;
+		}
+		
+		// noise time
+		blip_time_t ntime = final_end_time;
+		unsigned noise_lfsr = 1;
+		if ( !(osc_mode & noise_off) )
+		{
+			ntime = start_time + old_noise_delay;
+			noise_lfsr = old_noise_lfsr;
+			//if ( (regs [6] & 0x1F) == 0 )
+			//  dprintf( "Used noise period 0\n" );
+		}
+		
+		// The following efficiently handles several cases (least demanding first):
+		// * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC
+		// * Just tone or just noise, envelope disabled
+		// * Envelope controlling tone and/or noise
+		// * Tone and noise disabled, envelope enabled with high frequency
+		// * Tone and noise together
+		// * Tone and noise together with envelope
+		
+		// this loop only runs one iteration if envelope is disabled. If envelope
+		// is being used as a waveform (tone and noise disabled), this loop will
+		// still be reasonably efficient since the bulk of it will be skipped.
+		while ( 1 )
+		{
+			// current amplitude
+			int amp = 0;
+			if ( (osc_mode | osc->phase) & 1 & (osc_mode >> 3 | noise_lfsr) )
+				amp = volume;
+			{
+				int delta = amp - osc->last_amp;
+				if ( delta )
+				{
+					osc->last_amp = amp;
+					Synth_offset( &this->synth_, start_time, delta, osc_output );
+				}
+			}
+			
+			// Run wave and noise interleved with each catching up to the other.
+			// If one or both are disabled, their "current time" will be past end time,
+			// so there will be no significant performance hit.
+			if ( ntime < end_time || time < end_time )
+			{
+				// Since amplitude was updated above, delta will always be +/- volume,
+				// so we can avoid using last_amp every time to calculate the delta.
+				int delta = amp * 2 - volume;
+				int delta_non_zero = delta != 0;
+				int phase = osc->phase | (osc_mode & tone_off); assert( tone_off == 0x01 );
+				do
+				{
+					// run noise
+					blip_time_t end = end_time;
+					if ( end_time > time ) end = time;
+					if ( phase & delta_non_zero )
+					{
+						while ( ntime <= end ) // must advance *past* time to avoid hang
+						{
+							int changed = noise_lfsr + 1;
+							noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1);
+							if ( changed & 2 )
+							{
+								delta = -delta;
+								Synth_offset( &this->synth_, ntime, delta, osc_output );
+							}
+							ntime += noise_period;
+						}
+					}
+					else
+					{
+						// 20 or more noise periods on average for some music
+						int remain = end - ntime;
+						int count = remain / noise_period;
+						if ( remain >= 0 )
+							ntime += noise_period + count * noise_period;
+					}
+					
+					// run tone
+					end = end_time;
+					if ( end_time > ntime ) end = ntime;
+					if ( noise_lfsr & delta_non_zero )
+					{
+						while ( time < end )
+						{
+							delta = -delta;
+							Synth_offset( &this->synth_, time, delta, osc_output );
+							time += period;
+							
+							// alternate (less-efficient) implementation
+							//phase ^= 1;
+						}
+						phase = (unsigned) (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1);
+						check( phase == (delta > 0) );
+					}
+					else
+					{
+						// loop usually runs less than once
+						//SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period );
+						
+						while ( time < end )
+						{
+							time += period;
+							phase ^= 1;
+						}
+					}
+				}
+				while ( time < end_time || ntime < end_time );
+				
+				osc->last_amp = (delta + volume) >> 1;
+				if ( !(osc_mode & tone_off) )
+					osc->phase = phase;
+			}
+			
+			if ( end_time >= final_end_time )
+				break; // breaks first time when envelope is disabled
+			
+			// next envelope step
+			if ( ++osc_env_pos >= 0 )
+				osc_env_pos -= 32;
+			volume = this->env_wave [osc_env_pos] >> half_vol;
+			
+			start_time = end_time;
+			end_time += env_period;
+			if ( end_time > final_end_time )
+				end_time = final_end_time;
+		}
+		osc->delay = time - final_end_time;
+		
+		if ( !(osc_mode & noise_off) )
+		{
+			this->noise_delay = ntime - final_end_time;
+			this->noise_lfsr = noise_lfsr;
+		}
+	}
+	
+	// TODO: optimized saw wave envelope?
+	
+	// maintain envelope phase
+	blip_time_t remain = final_end_time - this->last_time - this->env_delay;
+	if ( remain >= 0 )
+	{
+		int count = (remain + env_period) / env_period;
+		this->env_pos += count;
+		if ( this->env_pos >= 0 )
+			this->env_pos = (this->env_pos & 31) - 32;
+		remain -= count * env_period;
+		assert( -remain <= env_period );
+	}
+	this->env_delay = -remain;
+	assert( this->env_delay > 0 );
+	assert( this->env_pos < 0 );
+	
+	this->last_time = final_end_time;
+}
diff --git a/apps/codecs/libgme/ay_apu.h b/apps/codecs/libgme/ay_apu.h
new file mode 100644
index 0000000..ccdd204
--- /dev/null
+++ b/apps/codecs/libgme/ay_apu.h
@@ -0,0 +1,79 @@
+// AY-3-8910 sound chip ulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef AY_APU_H
+#define AY_APU_H
+
+#include "blargg_common.h"
+#include "blargg_source.h"
+#include "blip_buffer.h"
+
+// Number of registers
+enum { ay_reg_count = 16 };
+enum { ay_osc_count = 3 };
+enum { ay_amp_range = 255 };
+
+struct osc_t
+{
+	blip_time_t  period;
+	blip_time_t  delay;
+	short        last_amp;
+	short        phase;
+	struct Blip_Buffer* output;
+};
+
+struct Ay_Apu {
+	struct osc_t oscs [ay_osc_count];
+	
+	blip_time_t last_time;
+	byte        addr_;
+	byte        regs [ay_reg_count];
+	
+	blip_time_t noise_delay;
+	unsigned    noise_lfsr;
+	
+	blip_time_t env_delay;
+	byte const* env_wave;
+	int         env_pos;
+	byte        env_modes [8] [48]; // values already passed through volume table
+		
+	struct Blip_Synth synth_; // used by Ay_Core for beeper sound
+};
+	
+void Ay_apu_init( struct Ay_Apu* this );
+
+// Writes to address register
+static inline void Ay_apu_write_addr( struct Ay_Apu* this, int data ) { this->addr_ = data & 0x0F; }
+	
+// Emulates to time t, then writes to current data register
+void run_until( struct Ay_Apu* this, blip_time_t final_end_time ) ICODE_ATTR;;
+void write_data_( struct Ay_Apu* this, int addr, int data ) ICODE_ATTR;
+static inline void Ay_apu_write_data( struct Ay_Apu* this, blip_time_t t, int data )  { run_until( this, t ); write_data_( this, this->addr_, data ); }
+	
+// Reads from current data register
+int Ay_apu_read( struct Ay_Apu* this );
+	
+// Resets sound chip
+void Ay_apu_reset( struct Ay_Apu* this );
+		
+// Sets overall volume, where 1.0 is normal
+static inline void Ay_apu_volume( struct Ay_Apu* this, double v ) { Synth_volume( &this->synth_, 0.7/ay_osc_count/ay_amp_range * v ); }
+
+static inline void Ay_apu_set_output( struct Ay_Apu* this, int i, struct Blip_Buffer* out )
+{
+	assert( (unsigned) i < ay_osc_count );
+	this->oscs [i].output = out;
+}
+
+// Emulates to time t, then subtracts t from the current time.
+// OK if previous write call had time slightly after t.
+static inline void Ay_apu_end_frame( struct Ay_Apu* this, blip_time_t time )
+{
+	if ( time > this->last_time )
+		run_until( this, time );
+	
+	this->last_time -= time;
+	assert( this->last_time >= 0 );
+}
+
+#endif
diff --git a/apps/codecs/libgme/ay_cpu.c b/apps/codecs/libgme/ay_cpu.c
new file mode 100644
index 0000000..5fbfe7c
--- /dev/null
+++ b/apps/codecs/libgme/ay_cpu.c
@@ -0,0 +1,59 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "ay_emu.h"
+
+#include "blargg_endian.h"
+//#include "z80_cpu_log.h"
+
+/* Copyright (C) 2006-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"
+
+void cpu_out( struct Ay_Emu* this, cpu_time_t time, addr_t addr, int data )
+{
+	if ( (addr & 0xFF) == 0xFE )
+	{
+		check( !cpc_mode );
+		this->spectrum_mode = !this->cpc_mode;
+		
+		// beeper_mask and last_beeper are 0 if (cpc_mode || !beeper_output)
+		if ( (data &= this->beeper_mask) != this->last_beeper )
+		{
+			this->last_beeper = data;
+			int delta = -this->beeper_delta;
+			this->beeper_delta = delta;
+			struct Blip_Buffer* bb = this->beeper_output;
+			Blip_set_modified( bb );
+			Synth_offset( &this->apu.synth_, time, delta, bb );
+		}
+	}
+	else
+	{
+		cpu_out_( this, time, addr, data );
+	}
+}
+
+#define OUT_PORT( addr, data )  cpu_out( this, TIME(), addr, data )
+#define IN_PORT( addr )  0xFF // cpu in
+#define FLAT_MEM                mem
+
+#define CPU_BEGIN \
+bool run_cpu( struct Ay_Emu* this, cpu_time_t end_time ) \
+{\
+	struct Z80_Cpu* cpu = &this->cpu; \
+	Z80_set_end_time( cpu, end_time ); \
+	byte* const mem = this->mem.ram; // cache
+	
+	#include "z80_cpu_run.h"
+	
+	return warning;
+}
diff --git a/apps/codecs/libgme/ay_emu.c b/apps/codecs/libgme/ay_emu.c
new file mode 100644
index 0000000..dc775cb
--- /dev/null
+++ b/apps/codecs/libgme/ay_emu.c
@@ -0,0 +1,783 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/

+

+#include "ay_emu.h"

+

+#include "blargg_endian.h"

+

+/* Copyright (C) 2006-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"

+

+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)

+

+const char* const gme_wrong_file_type = "Wrong file type for this emulator";

+

+// TODO: probably don't need detailed errors as to why file is corrupt

+

+int const spectrum_clock = 3546900; // 128K Spectrum

+int const spectrum_period = 70908;

+

+//int const spectrum_clock = 3500000; // 48K Spectrum

+//int const spectrum_period = 69888;

+

+int const cpc_clock = 2000000;

+

+void clear_track_vars( struct Ay_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       = INT_MAX / 2 + 1;

+	this->fade_step        = 1;

+	this->silence_time     = 0;

+	this->silence_count    = 0;

+	this->buf_remain       = 0;

+	/* warning(); // clear warning */

+}

+

+void Ay_init( struct Ay_Emu *this )

+{

+	this->sample_rate  = 0;

+	this->mute_mask_   = 0;

+	this->tempo        = 1.0;

+	this->gain         = 1.0;

+	this->track_count  = 0;

+	

+	// defaults

+	this->max_initial_silence = 2;

+	this->ignore_silence      = false;

+	

+	this->voice_count = 0;

+	clear_track_vars( this );

+	this->beeper_output = NULL;

+	disable_beeper( this );

+	

+	Ay_apu_init( &this->apu );

+	Z80_init( &this->cpu );

+

+	this->silence_lookahead = 6 ;

+}

+

+// Track info

+

+// Given pointer to 2-byte offset of data, returns pointer to data, or NULL if

+// offset is 0 or there is less than min_size bytes of data available.

+static byte const* get_data( struct file_t const* file, byte const ptr [], int min_size )

+{

+	int offset = (int16_t) get_be16( ptr );

+	int pos  = ptr - (byte const*) file->header;

+	int size = file->end - (byte const*) file->header;

+	assert( (unsigned) pos <= (unsigned) size - 2 );

+	int limit = size - min_size;

+	if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit )

+		return NULL;

+	return ptr + offset;

+}

+

+static blargg_err_t parse_header( byte const in [], int size, struct file_t* out )

+{

+	if ( size < header_size )

+		return gme_wrong_file_type;

+	

+	out->header = (struct header_t const*) in;

+	out->end    = in + size;

+	struct header_t const* h = (struct header_t const*) in;

+	if ( memcmp( h->tag, "ZXAYEMUL", 8 ) )

+		return gme_wrong_file_type;

+	

+	out->tracks = get_data( out, h->track_info, (h->max_track + 1) * 4 );

+	if ( !out->tracks )

+		return "missing track data";

+

+	return 0;

+}

+

+long Track_get_length( struct Ay_Emu* this, int n )

+{

+	long length = 0;

+

+	byte const* track_info = get_data( &this->file, this->file.tracks + n * 4 + 2, 6 );

+	if ( track_info )

+		length = get_be16( track_info + 4 ) * (1000 / 50); // frames to msec 

+

+	if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {

+		struct entry_t* entry = &this->m3u.entries [n];

+		length = entry->length;

+	} 

+	

+	if ( length <= 0 )

+		length = 120 * 1000;  /* 2 minutes */ 

+

+	return length;

+}

+

+// Setup

+

+void change_clock_rate( struct Ay_Emu *this, long rate )

+{

+	this->clock_rate_ = rate;

+	Buffer_clock_rate( &this->stereo_buf, rate );

+}

+

+blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], int size )

+{

+	assert( offsetof (struct header_t,track_info [2]) == header_size );

+	

+	RETURN_ERR( parse_header( in, size, &this->file ) );

+	

+	/* if ( file.header->vers > 2 )

+		warning( "Unknown file version" ); */

+	

+	this->voice_count = ay_osc_count + 1; // +1 for beeper

+	Ay_apu_volume( &this->apu, this->gain );

+	

+	// Setup buffer

+	change_clock_rate( this, spectrum_clock );

+	this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );

+	

+	Sound_set_tempo( this, this->tempo );

+	

+	// Remute voices

+	Sound_mute_voices( this, this->mute_mask_ );

+	

+	this->track_count = this->file.header->max_track + 1;

+	this->m3u.size = 0;

+	return 0;

+}

+

+void set_beeper_output( struct Ay_Emu *this, struct Blip_Buffer* b )

+{

+	this->beeper_output = b;

+	if ( b && !this->cpc_mode )

+		this->beeper_mask = 0x10;

+	else

+		disable_beeper( this );

+}

+

+void set_voice( struct Ay_Emu *this, int i, struct Blip_Buffer* center )

+{

+	if ( i >= ay_osc_count )

+		set_beeper_output( this, center );

+	else

+		Ay_apu_set_output( &this->apu, i, center );

+}

+

+blargg_err_t run_clocks( struct Ay_Emu *this, blip_time_t* duration, int msec )

+{

+#if defined(ROCKBOX)

+	(void) msec;

+#endif

+

+	cpu_time_t *end = duration;	

+	struct Z80_Cpu* cpu = &this->cpu;

+	Z80_set_time( cpu, 0 );

+	

+	// Since detection of CPC mode will halve clock rate during the frame

+	// and thus generate up to twice as much sound, we must generate half

+	// as much until mode is known.

+	if ( !(this->spectrum_mode | this->cpc_mode) )

+		*end /= 2;

+	

+	while ( Z80_time( cpu ) < *end )

+	{

+		run_cpu( this, min( *end, this->next_play ) );

+		

+		if ( Z80_time( cpu ) >= this->next_play )

+		{

+			// next frame

+			this->next_play += this->play_period;

+			

+			if ( cpu->r.iff1 )

+			{

+				// interrupt enabled

+				

+				if ( this->mem.ram [cpu->r.pc] == 0x76 )

+					cpu->r.pc++; // advance past HALT instruction

+				

+				cpu->r.iff1 = 0;

+				cpu->r.iff2 = 0;

+				

+				this->mem.ram [--cpu->r.sp] = (byte) (cpu->r.pc >> 8);

+				this->mem.ram [--cpu->r.sp] = (byte) (cpu->r.pc);

+				

+				// fixed interrupt

+				cpu->r.pc = 0x38;

+				Z80_adjust_time( cpu, 12 );

+				

+				if ( cpu->r.im == 2 )

+				{

+					// vectored interrupt

+					addr_t addr = cpu->r.i * 0x100 + 0xFF;

+					cpu->r.pc = this->mem.ram [(addr + 1) & 0xFFFF] * 0x100 + this->mem.ram [addr];

+					Z80_adjust_time( cpu, 6 );

+				}

+			}

+		}

+	}

+	

+	// End time frame

+	*end = Z80_time( cpu );

+	this->next_play -= *end;

+	check( this->next_play >= 0 );

+	Z80_adjust_time( cpu, -*end );

+	Ay_apu_end_frame( &this->apu, *end );

+	return 0;

+}

+

+// Emulation

+

+void cpu_out_( struct Ay_Emu *this, cpu_time_t time, addr_t addr, int data )

+{

+	// Spectrum

+	if ( !this->cpc_mode )

+	{

+		switch ( addr & 0xFEFF )

+		{

+		case 0xFEFD:

+			this->spectrum_mode = true;

+			Ay_apu_write_addr( &this->apu, data );

+			return;

+		

+		case 0xBEFD:

+			this->spectrum_mode = true;

+			Ay_apu_write_data( &this->apu, time, data );

+			return;

+		}

+	}

+	

+	// CPC

+	if ( !this->spectrum_mode )

+	{

+		switch ( addr >> 8 )

+		{

+		case 0xF6:

+			switch ( data & 0xC0 )

+			{

+			case 0xC0:

+				Ay_apu_write_addr( &this->apu, this->cpc_latch );

+				goto enable_cpc;

+			

+			case 0x80:

+				Ay_apu_write_data( &this->apu, time, this->cpc_latch );

+				goto enable_cpc;

+			}

+			break;

+		

+		case 0xF4:

+			this->cpc_latch = data;

+			goto enable_cpc;

+		}

+	}

+	

+	/* dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); */

+	return;

+	

+enable_cpc:

+	if ( !this->cpc_mode )

+	{

+		this->cpc_mode = true;

+		disable_beeper( this );

+	

+		change_clock_rate( this, cpc_clock );

+		Sound_set_tempo( this, this->tempo );

+	}

+}

+

+blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, long rate )

+{

+	require( !this->sample_rate ); // sample rate can't be changed once set

+	Buffer_init( &this->stereo_buf );

+	RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );

+	

+	// Set buffer bass

+	Buffer_bass_freq( &this->stereo_buf, 160 );

+	

+	this->sample_rate = rate;

+	return 0;

+}

+

+void Sound_mute_voice( struct Ay_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 Ay_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) )

+		{

+			set_voice( this, i, 0 );

+		}

+		else

+		{

+			struct channel_t ch = Buffer_channel( &this->stereo_buf );

+			assert( (ch.center && ch.left && ch.right) ||

+					(!ch.center && !ch.left && !ch.right) ); // all or nothing

+			set_voice( this, i, ch.center );

+		}

+	}

+}

+

+void Sound_set_tempo( struct Ay_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;

+	

+	int p = spectrum_period;

+	if ( this->clock_rate_ != spectrum_clock )

+		p = this->clock_rate_ / 50;

+	

+	this->play_period = (blip_time_t) (p / t);

+}

+

+void fill_buf( struct Ay_Emu *this ) ICODE_ATTR;;

+blargg_err_t Ay_start_track( struct Ay_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];

+		track = e->track;

+	}

+	

+	this->current_track = track;

+	Buffer_clear( &this->stereo_buf );

+	

+	byte* const mem = this->mem.ram;

+	

+	memset( mem + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET

+	memset( mem + 0x0100, 0xFF, 0x4000 - 0x100 );

+	memset( mem + ram_addr, 0x00, mem_size - ram_addr );

+

+	// locate data blocks

+	byte const* const data = get_data( &this->file, this->file.tracks + track * 4 + 2, 14 );

+	if ( !data )

+		return "file data missing";

+	

+	byte const* const more_data = get_data( &this->file, data + 10, 6 );

+	if ( !more_data )

+		return "file data missing";

+	

+	byte const* blocks = get_data( &this->file, data + 12, 8 );

+	if ( !blocks )

+		return "file data missing";

+	

+	// initial addresses

+	unsigned addr = get_be16( blocks );

+	if ( !addr )

+		return "file data missing";

+	

+	unsigned init = get_be16( more_data + 2 );

+	if ( !init )

+		init = addr;

+	

+	// copy blocks into memory

+	do

+	{

+		blocks += 2;

+		unsigned len = get_be16( blocks ); blocks += 2;

+		if ( addr + len > mem_size )

+		{

+			/* warning( "Bad data block size" ); */

+			len = mem_size - addr;

+		}

+		check( len );

+		byte const* in = get_data( &this->file, blocks, 0 ); blocks += 2;

+		if ( len > (unsigned) (this->file.end - in) )

+		{

+			/* warning( "File data missing" ); */

+			len = this->file.end - in;

+		}

+

+		memcpy( mem + addr, in, len );

+		

+		if ( this->file.end - blocks < 8 )

+		{

+			/* warning( "File data missing" ); */

+			break;

+		}

+	}

+	while ( (addr = get_be16( blocks )) != 0 );

+	

+	// copy and configure driver

+	static byte const passive [] = {

+		0xF3,       // DI

+		0xCD, 0, 0, // CALL init

+		0xED, 0x5E, // LOOP: IM 2

+		0xFB,       // EI

+		0x76,       // HALT

+		0x18, 0xFA  // JR LOOP

+	};

+	static byte const active [] = {

+		0xF3,       // DI

+		0xCD, 0, 0, // CALL init

+		0xED, 0x56, // LOOP: IM 1

+		0xFB,       // EI

+		0x76,       // HALT

+		0xCD, 0, 0, // CALL play

+		0x18, 0xF7  // JR LOOP

+	};

+	memcpy( mem, passive, sizeof passive );

+	int const play_addr = get_be16( more_data + 4 );

+	if ( play_addr )

+	{

+		memcpy( mem, active, sizeof active );

+		mem [ 9] = play_addr;

+		mem [10] = play_addr >> 8;

+	}

+	mem [2] = init;

+	mem [3] = init >> 8;

+	

+	mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET)

+	

+	// start at spectrum speed

+	change_clock_rate( this, spectrum_clock );

+	Sound_set_tempo( this, this->tempo );

+	

+	struct registers_t r;

+	memset( &r, 0, sizeof(struct registers_t) );

+	

+	r.sp = get_be16( more_data );

+	r.b.a     = r.b.b = r.b.d = r.b.h = data [8];

+	r.b.flags = r.b.c = r.b.e = r.b.l = data [9];

+	r.alt.w = r.w;

+	r.ix = r.iy = r.w.hl;

+	

+	memset( this->mem.padding1, 0xFF, sizeof this->mem.padding1 );

+	

+	int const mirrored = 0x80; // this much is mirrored after end of memory

+	memset( this->mem.ram + mem_size + mirrored, 0xFF, sizeof this->mem.ram - mem_size - mirrored );

+	memcpy( this->mem.ram + mem_size, this->mem.ram, mirrored ); // some code wraps around (ugh)

+	

+	Z80_reset( &this->cpu, this->mem.padding1, this->mem.padding1 );

+	Z80_map_mem( &this->cpu, 0, mem_size, this->mem.ram, this->mem.ram );

+	this->cpu.r = r;

+	

+	this->beeper_delta   = (int) (ay_amp_range * 0.8);

+	this->last_beeper    = 0;

+	this->next_play      = this->play_period;

+	this->spectrum_mode  = false;

+	this->cpc_mode       = false;

+	this->cpc_latch      = 0;

+	set_beeper_output( this, this->beeper_output );

+	Ay_apu_reset( &this->apu );

+	

+	// a few tunes rely on channels having tone enabled at the beginning

+	Ay_apu_write_addr( &this->apu, 7 );

+	Ay_apu_write_data( &this->apu, 0, 0x38 );

+	

+	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;

+}

+

+// Tell/Seek

+

+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 Ay_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 Ay_Emu *this, long msec )

+{

+	blargg_long time = msec_to_samples( msec, this->sample_rate );

+	if ( time < this->out_time )

+		RETURN_ERR( Ay_start_track( this, this->current_track ) );

+	return Track_skip( this, time - this->out_time );

+}

+

+blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out ) ICODE_ATTR;

+blargg_err_t skip_( struct Ay_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 Ay_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 Ay_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 Ay_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 Ay_Emu *this, long count, sample_t* out )

+{

+	check( current_track_ >= 0 );

+	this->emu_time += count;

+	if ( this->current_track >= 0 && !this->emu_track_ended_ ) {

+		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 Ay_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 Ay_play( struct Ay_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 );

+		

+		// prints nifty graph of how far ahead we are when searching for silence

+		//debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );

+		

+		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;

+}

+

+blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out )

+{

+	long remain = count;

+	while ( remain )

+	{

+		remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );

+		if ( remain )

+		{

+			if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )

+			{

+				this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );

+				

+				// Remute voices

+				Sound_mute_voices( this, this->mute_mask_ );

+			}

+			int msec = Buffer_length( &this->stereo_buf );

+			blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000 - 100;

+			RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );

+			assert( clocks_emulated );

+			Buffer_end_frame( &this->stereo_buf, clocks_emulated );

+		}

+	}

+	return 0;

+}

diff --git a/apps/codecs/libgme/ay_emu.h b/apps/codecs/libgme/ay_emu.h
new file mode 100644
index 0000000..9125216
--- /dev/null
+++ b/apps/codecs/libgme/ay_emu.h
@@ -0,0 +1,172 @@
+// Sinclair Spectrum AY music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef AY_EMU_H
+#define AY_EMU_H
+
+#include "blargg_source.h"
+
+#include "multi_buffer.h"
+#include "z80_cpu.h"
+#include "ay_apu.h"
+#include "m3u_playlist.h"
+
+typedef short sample_t;
+
+// 64K memory to load code and data into before starting track. Caller
+// must parse the AY file.
+enum { mem_size = 0x10000 };
+enum { ram_addr = 0x4000 }; // where official RAM starts
+enum { buf_size = 2048 };
+
+// AY file header
+enum { header_size = 0x14 };
+struct header_t
+{
+	byte tag        [8];
+	byte vers;
+	byte player;
+	byte unused     [2];
+	byte author     [2];
+	byte comment    [2];
+	byte max_track;
+	byte first_track;
+	byte track_info [2];
+};
+
+struct file_t {
+	struct header_t const* header;
+	byte const* tracks;
+	byte const* end;    // end of file data
+};
+
+struct mem_t {
+	uint8_t padding1 [0x100];
+	uint8_t ram      [mem_size + 0x100];
+};
+
+struct Ay_Emu {
+	struct file_t file;
+
+	struct Blip_Buffer* beeper_output;
+	int          beeper_delta;
+	int          last_beeper;
+	int          beeper_mask;
+	
+	addr_t play_addr;
+	cpu_time_t play_period;
+	cpu_time_t next_play;
+	
+	int  cpc_latch;
+	bool spectrum_mode;
+	bool cpc_mode;
+	
+	// general
+	int max_initial_silence;
+	int voice_count;
+	int mute_mask_;
+	double tempo;
+	double gain;
+	
+	long sample_rate;
+	
+	// track-specific
+	int current_track;
+	int track_count;
+	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
+	volatile bool track_ended;
+	bool emu_track_ended_; // emulator has reached end of track
+	
+	// fading
+	blargg_long fade_start;
+	int fade_step;
+	
+	// silence detection
+	bool ignore_silence;
+	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
+
+	long clock_rate_;
+	unsigned buf_changed_count;
+	
+	// M3u Playlist
+	struct M3u_Playlist m3u;
+	
+	// large items
+	struct Ay_Apu apu;
+	sample_t buf [buf_size];
+	struct Stereo_Buffer stereo_buf; // NULL if using custom buffer
+	struct Z80_Cpu cpu;
+	struct mem_t mem;
+};
+
+// Basic functionality (see Gme_File.h for file loading/track info functions)
+void Ay_init( struct Ay_Emu* this );
+
+blargg_err_t Ay_load_mem( struct Ay_Emu* this, byte const in [], int size );
+
+// Set output sample rate. Must be called only once before loading file.
+blargg_err_t Ay_set_sample_rate( struct Ay_Emu* this, long sample_rate );
+
+// Start a track, where 0 is the first track. Also clears warning string.
+blargg_err_t Ay_start_track( struct Ay_Emu* this, int track );
+
+// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
+// errors set warning string, and major errors also end track.
+blargg_err_t Ay_play( struct Ay_Emu* this, long count, sample_t* buf );
+
+
+// Track status/control
+
+// Number of milliseconds (1000 msec = 1 second) played since beginning of track
+long Track_tell( struct Ay_Emu* this );
+
+// Seek to new time in track. Seeking backwards or far forward can take a while.
+blargg_err_t Track_seek( struct Ay_Emu* this, long msec );
+	
+// Skip n samples
+blargg_err_t Track_skip( struct Ay_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 Ay_Emu* this, long start_msec, long length_msec );
+
+// Get track length in milliseconds
+long Track_get_length( struct Ay_Emu* this, int n );
+
+// 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 Ay_Emu* this, double t );
+	
+// Mute/unmute voice i, where voice 0 is first voice
+void Sound_mute_voice( struct Ay_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 Ay_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 Ay_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)
+void cpu_out( struct Ay_Emu* this, cpu_time_t, addr_t, int data );
+void cpu_out_( struct Ay_Emu* this, cpu_time_t, addr_t, int data );
+bool run_cpu( struct Ay_Emu* this, cpu_time_t end );
+
+static inline void disable_beeper( struct Ay_Emu *this )
+{
+	this->beeper_mask = 0;
+	this->last_beeper = 0;
+}
+	
+#endif
diff --git a/apps/codecs/libgme/blargg_common.h b/apps/codecs/libgme/blargg_common.h
new file mode 100644
index 0000000..be34379
--- /dev/null
+++ b/apps/codecs/libgme/blargg_common.h
@@ -0,0 +1,159 @@
+// 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(ROCKBOX)
+#include "codeclib.h"
+#endif
+
+#if 1 /* IRAM configuration is not yet active for all libGME codecs. */
+    #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
+
+// BLARGG_RESTRICT: equivalent to C99's restrict, where supported
+#if __GNUC__ >= 3 || _MSC_VER >= 1100
+	#define BLARGG_RESTRICT __restrict
+#else
+	#define BLARGG_RESTRICT
+#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
diff --git a/apps/codecs/libgme/blargg_config.h b/apps/codecs/libgme/blargg_config.h
new file mode 100644
index 0000000..6490c15
--- /dev/null
+++ b/apps/codecs/libgme/blargg_config.h
@@ -0,0 +1,42 @@
+// 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 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

+

+// Uncomment to emulate only nes apu

+// #define NSF_EMU_APU_ONLY 1

+

+// Uncomment to remove vrc7 apu support

+// #define NSF_EMU_NO_VRC7 1

+

+// Uncomment to remove fmopl apu support

+// #define KSS_EMU_NO_FMOPL 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

diff --git a/apps/codecs/libgme/blargg_endian.h b/apps/codecs/libgme/blargg_endian.h
new file mode 100644
index 0000000..ae55d7f
--- /dev/null
+++ b/apps/codecs/libgme/blargg_endian.h
@@ -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
diff --git a/apps/codecs/libgme/blargg_source.h b/apps/codecs/libgme/blargg_source.h
new file mode 100644
index 0000000..4bea02a
--- /dev/null
+++ b/apps/codecs/libgme/blargg_source.h
@@ -0,0 +1,71 @@
+// 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 );
+#if defined(ROCKBOX)
+#undef require
+#define require( expr )
+#else
+#undef require
+#define require( expr ) assert( expr )
+#endif
+
+// 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
diff --git a/apps/codecs/libgme/blip_buffer.c b/apps/codecs/libgme/blip_buffer.c
new file mode 100644
index 0000000..3061f68
--- /dev/null
+++ b/apps/codecs/libgme/blip_buffer.c
@@ -0,0 +1,285 @@
+// 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)
+double floor(double x) {
+	if ( x > 0 ) return (int)x;
+	return (int)(x-0.9999999999999999);
+}
+#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);
+}
diff --git a/apps/codecs/libgme/blip_buffer.h b/apps/codecs/libgme/blip_buffer.h
new file mode 100644
index 0000000..84ed6e6
--- /dev/null
+++ b/apps/codecs/libgme/blip_buffer.h
@@ -0,0 +1,279 @@
+// 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;
+
+// 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 );
+}
+
+// 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 );
+}
+
+// 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_)
+
+// 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)
+	
+#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)
+
+#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\
+	name##_reader_accum -= name##_reader_accum >> (bass);\
+	name##_reader_accum += name##_reader_buf [(idx)];\
+}
+
+//// BLIP_CLAMP
+
+#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+		defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+	#define BLIP_X86 1
+	#define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
+#else
+	#define BLIP_CLAMP_( in ) (blip_sample_t) in != in
+#endif
+
+// Clamp sample to blip_sample_t range
+#define BLIP_CLAMP( sample, out )\
+	{ if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 31) ^ 0x7FFF; }
+
+#endif
diff --git a/apps/codecs/libgme/emu2413.c b/apps/codecs/libgme/emu2413.c
new file mode 100644
index 0000000..5def504
--- /dev/null
+++ b/apps/codecs/libgme/emu2413.c
@@ -0,0 +1,1958 @@
+/***********************************************************************************

+

+  emu2413.c -- YM2413 emulator written by Mitsutaka Okazaki 2001

+

+  2001 01-08 : Version 0.10 -- 1st version.

+  2001 01-15 : Version 0.20 -- semi-public version.

+  2001 01-16 : Version 0.30 -- 1st public version.

+  2001 01-17 : Version 0.31 -- Fixed bassdrum problem.

+             : Version 0.32 -- LPF implemented.

+  2001 01-18 : Version 0.33 -- Fixed the drum problem, refine the mix-down method.

+                            -- Fixed the LFO bug.

+  2001 01-24 : Version 0.35 -- Fixed the drum problem, 

+                               support undocumented EG behavior.

+  2001 02-02 : Version 0.38 -- Improved the performance.

+                               Fixed the hi-hat and cymbal model.

+                               Fixed the default percussive datas.

+                               Noise reduction.

+                               Fixed the feedback problem.

+  2001 03-03 : Version 0.39 -- Fixed some drum bugs.

+                               Improved the performance.

+  2001 03-04 : Version 0.40 -- Improved the feedback.

+                               Change the default table size.

+                               Clock and Rate can be changed during play.

+  2001 06-24 : Version 0.50 -- Improved the hi-hat and the cymbal tone.

+                               Added VRC7 patch (OPLL_reset_patch is changed).

+                               Fixed OPLL_reset() bug.

+                               Added OPLL_setMask, OPLL_getMask and OPLL_toggleMask.

+                               Added OPLL_writeIO.

+  2001 09-28 : Version 0.51 -- Removed the noise table.

+  2002 01-28 : Version 0.52 -- Added Stereo mode.

+  2002 02-07 : Version 0.53 -- Fixed some drum bugs.

+  2002 02-20 : Version 0.54 -- Added the best quality mode.

+  2002 03-02 : Version 0.55 -- Removed OPLL_init & OPLL_close.

+  2002 05-30 : Version 0.60 -- Fixed HH&CYM generator and all voice datas.

+  2004 04-10 : Version 0.61 -- Added YMF281B tone (defined by Chabin).

+  

+  2011 03-22 : --------------- Modified by gama to use precalculated tables.

+

+  References: 

+    fmopl.c        -- 1999,2000 written by Tatsuyuki Satoh (MAME development).

+    fmopl.c(fixed) -- (C) 2002 Jarek Burczynski.

+    s_opl.c        -- 2001 written by Mamiya (NEZplug development).

+    fmgen.cpp      -- 1999,2000 written by cisc.

+    fmpac.ill      -- 2000 created by NARUTO.

+    MSX-Datapack

+    YMU757 data sheet

+    YM2143 data sheet

+

+**************************************************************************************/

+#include <stdio.h>

+#include <stdlib.h>

+#include <string.h>

+#include <math.h>

+#include "emu2413.h"

+

+#include "emutables.h"

+#if !defined(ROCKBOX)

+	#define EMU2413_CALCUL_TABLES

+#else

+	#define EMU2413_COMPACTION

+	#include "emutables.h"

+#endif

+

+#if defined(EMU2413_COMPACTION) && !defined(ROCKBOX)

+#define OPLL_TONE_NUM 1

+static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 16] = {

+  {

+#include "2413tone.h"

+   }

+};

+#else

+#define OPLL_TONE_NUM 3

+static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 16] = {

+  { 

+#include "2413tone.h" 

+  },

+  {

+#include "vrc7tone.h"

+   },

+  {

+#include "281btone.h"

+  }

+};

+#endif

+

+/* Size of Sintable ( 8 -- 18 can be used. 9 recommended.) */

+#define PG_BITS 9

+#define PG_WIDTH (1<<PG_BITS)

+

+/* Phase increment counter */

+#define DP_BITS 18

+#define DP_WIDTH (1<<DP_BITS)

+#define DP_BASE_BITS (DP_BITS - PG_BITS)

+

+/* Dynamic range (Accuracy of sin table) */

+#define DB_BITS 8

+#define DB_STEP (48.0/(1<<DB_BITS))

+#define DB_MUTE (1<<DB_BITS)

+

+/* Dynamic range of envelope */

+#define EG_STEP 0.375

+#define EG_BITS 7

+#define EG_MUTE (1<<EG_BITS)

+

+/* Dynamic range of total level */

+#define TL_STEP 0.75

+#define TL_BITS 6

+#define TL_MUTE (1<<TL_BITS)

+

+/* Dynamic range of sustine level */

+#define SL_STEP 3.0

+#define SL_BITS 4

+#define SL_MUTE (1<<SL_BITS)

+

+#define EG2DB(d) ((d)*(e_int32)(EG_STEP/DB_STEP))

+#define TL2EG(d) ((d)*(e_int32)(TL_STEP/EG_STEP))

+#define SL2EG(d) ((d)*(e_int32)(SL_STEP/EG_STEP))

+

+#define DB_POS(x) (e_uint32)((x)/DB_STEP)

+#define DB_NEG(x) (e_uint32)(DB_MUTE+DB_MUTE+(x)/DB_STEP)

+

+/* Bits for liner value */

+#define DB2LIN_AMP_BITS 8

+#define SLOT_AMP_BITS (DB2LIN_AMP_BITS)

+

+/* Bits for envelope phase incremental counter */

+#define EG_DP_BITS 22

+#define EG_DP_WIDTH (1<<EG_DP_BITS)

+

+/* Bits for Pitch and Amp modulator */

+#define PM_PG_BITS 8

+#define PM_PG_WIDTH (1<<PM_PG_BITS)

+#define PM_DP_BITS 16

+#define PM_DP_WIDTH (1<<PM_DP_BITS)

+#define AM_PG_BITS 8

+#define AM_PG_WIDTH (1<<AM_PG_BITS)

+#define AM_DP_BITS 16

+#define AM_DP_WIDTH (1<<AM_DP_BITS)

+

+/* PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200) */

+#define PM_AMP_BITS 8

+#define PM_AMP (1<<PM_AMP_BITS)

+

+/* PM speed(Hz) and depth(cent) */

+#define PM_SPEED 6.4

+#define PM_DEPTH 13.75

+

+/* AM speed(Hz) and depth(dB) */

+#define AM_SPEED 3.6413

+#define AM_DEPTH 4.875

+

+/* Cut the lower b bit(s) off. */

+#define HIGHBITS(c,b) ((c)>>(b))

+

+/* Leave the lower b bit(s). */

+#define LOWBITS(c,b) ((c)&((1<<(b))-1))

+

+/* Expand x which is s bits to d bits. */

+#define EXPAND_BITS(x,s,d) ((x)<<((d)-(s)))

+

+/* Expand x which is s bits to d bits and fill expanded bits '1' */

+#define EXPAND_BITS_X(x,s,d) (((x)<<((d)-(s)))|((1<<((d)-(s)))-1))

+

+/* Adjust envelope speed which depends on sampling rate. */

+#define RATE_ADJUST(x) (rate==49716?(e_uint32)x:(e_uint32)((double)(x)*clk/72/rate + 0.5))        /* added 0.5 to round the value*/

+

+#define MOD(o,x) (&(o)->slot[(x)<<1])

+#define CAR(o,x) (&(o)->slot[((x)<<1)|1])

+

+#define BIT(s,b) (((s)>>(b))&1)

+

+/* Input clock */

+static e_uint32 clk = 844451141;

+/* Sampling rate */

+static e_uint32 rate = 3354932;

+

+/* WaveTable for each envelope amp */

+static e_uint16 fullsintable[PG_WIDTH];

+static e_uint16 halfsintable[PG_WIDTH];

+

+static e_uint16 *waveform[2] = { fullsintable, halfsintable };

+

+/* LFO Table */

+static e_int32 pmtable[PM_PG_WIDTH];

+static e_int32 amtable[AM_PG_WIDTH];

+

+/* Phase delta for LFO */

+static e_uint32 pm_dphase;

+static e_uint32 am_dphase;

+

+/* dB to Liner table */

+static e_int16 DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2];

+

+/* Liner to Log curve conversion table (for Attack rate). */

+static e_uint16 AR_ADJUST_TABLE[1 << EG_BITS];

+

+/* Empty voice data */

+static OPLL_PATCH null_patch = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

+

+/* Basic voice Data */

+static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2];

+

+/* Definition of envelope mode */

+enum OPLL_EG_STATE 

+{ READY, ATTACK, DECAY, SUSHOLD, SUSTINE, RELEASE, SETTLE, FINISH };

+

+/* Phase incr table for Attack */

+static e_uint32 dphaseARTable[16][16];

+/* Phase incr table for Decay and Release */

+static e_uint32 dphaseDRTable[16][16];

+

+/* KSL + TL Table */

+static e_uint32 tllTable[16][8][1 << TL_BITS][4];

+static e_int32 rksTable[2][8][2];

+

+/* We may not have too much SRAM in rockbox */

+#if !defined(ROCKBOX)

+/* Phase incr table for PG */

+static e_uint32 dphaseTable[512][8][16];

+#endif

+

+/***************************************************

+ 

+                  Create tables

+ 

+****************************************************/

+INLINE static e_int32

+Min (e_int32 i, e_int32 j)

+{

+  if (i < j)

+    return i;

+  else

+    return j;

+}

+

+/* Table for AR to LogCurve. */

+static void

+makeAdjustTable (void)

+{

+  e_int32 i;

+

+  AR_ADJUST_TABLE[0] = (1 << EG_BITS) - 1;

+  for (i = 1; i < (1<<EG_BITS); i++)

+  #ifdef EMU2413_CALCUL_TABLES

+    AR_ADJUST_TABLE[i] = (e_uint16) ((double) (1<<EG_BITS)-1 - ((1<<EG_BITS)-1)*log(i)/log(127));

+  #else

+    AR_ADJUST_TABLE[i] = ar_adjust_coeff[i];

+  #endif

+}

+

+

+/* Table for dB(0 -- (1<<DB_BITS)-1) to Liner(0 -- DB2LIN_AMP_WIDTH) */

+static void

+makeDB2LinTable (void)

+{

+  e_int32 i;

+  for (i = 0; i < DB_MUTE + DB_MUTE; i++)

+  {

+  #ifdef EMU2413_CALCUL_TABLES

+    DB2LIN_TABLE[i] = (e_int16) ((double) ((1 << DB2LIN_AMP_BITS) - 1) * pow (10, -(double) i * DB_STEP / 20));

+  #else

+    DB2LIN_TABLE[i] = db2lin_coeff[i];

+  #endif

+    if (i >= DB_MUTE) DB2LIN_TABLE[i] = 0;

+    DB2LIN_TABLE[i + DB_MUTE + DB_MUTE] = (e_int16) (-DB2LIN_TABLE[i]);

+  }

+}

+

+#ifdef EMU2413_CALCUL_TABLES

+/* Liner(+0.0 - +1.0) to dB((1<<DB_BITS) - 1 -- 0) */

+static e_int32

+lin2db (double d)

+{

+  if (d == 0)

+    return (DB_MUTE - 1);

+  else

+    return Min (-(e_int32) (20.0 * log10 (d) / DB_STEP), DB_MUTE-1);  /* 0 -- 127 */

+}

+#endif

+

+/* Sin Table */

+static void

+makeSinTable (void)

+{

+  e_int32 i;

+

+  for (i = 0; i < PG_WIDTH / 4; i++)

+  #ifdef EMU2413_CALCUL_TABLES

+    fullsintable[i] = (e_uint32) lin2db (sin (2.0 * PI * i / PG_WIDTH) );

+  #else

+    fullsintable[i] = sin_coeff[i];

+  #endif

+

+  for (i = 0; i < PG_WIDTH / 4; i++)

+  {

+    fullsintable[PG_WIDTH / 2 - 1 - i] = fullsintable[i];

+  }

+

+  for (i = 0; i < PG_WIDTH / 2; i++)

+  {

+    fullsintable[PG_WIDTH / 2 + i] = (e_uint32) (DB_MUTE + DB_MUTE + fullsintable[i]);

+  }

+

+  for (i = 0; i < PG_WIDTH / 2; i++)

+    halfsintable[i] = fullsintable[i];

+  for (i = PG_WIDTH / 2; i < PG_WIDTH; i++)

+    halfsintable[i] = fullsintable[0];

+}

+

+static double saw(double phase)

+{

+  if(phase <= PI/2)

+    return phase * 2 / PI ;

+  else if(phase <= PI*3/2)

+    return 2.0 - ( phase * 2 / PI );

+  else

+    return -4.0 + phase * 2 / PI;

+}

+

+/* Table for Pitch Modulator */

+static void

+makePmTable (void)

+{

+  e_int32 i;

+

+  for (i = 0; i < PM_PG_WIDTH; i++)

+    /* pmtable[i] = (e_int32) ((double) PM_AMP * pow (2, (double) PM_DEPTH * sin (2.0 * PI * i / PM_PG_WIDTH) / 1200)); */

+  #ifdef EMU2413_CALCUL_TABLES

+    pmtable[i] = (e_int32) ((double) PM_AMP * pow (2, (double) PM_DEPTH * saw (2.0 * PI * i / PM_PG_WIDTH) / 1200));    

+  #else

+    pmtable[i] = pm_coeff[i];

+  #endif

+}

+

+/* Table for Amp Modulator */

+static void

+makeAmTable (void)

+{

+  e_int32 i;

+

+  for (i = 0; i < AM_PG_WIDTH; i++)

+    /* amtable[i] = (e_int32) ((double) AM_DEPTH / 2 / DB_STEP * (1.0 + sin (2.0 * PI * i / PM_PG_WIDTH))); */

+    amtable[i] = (e_int32) ((double) AM_DEPTH / 2 / DB_STEP * (1.0 + saw (2.0 * PI * i / PM_PG_WIDTH)));

+}

+

+#if !defined(ROCKBOX)

+/* Phase increment counter table */

+static void

+makeDphaseTable (void)

+{

+  e_uint32 fnum, block, ML;

+  e_uint32 mltable[16] =

+    { 1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2 };

+

+  for (fnum = 0; fnum < 512; fnum++)

+    for (block = 0; block < 8; block++)

+      for (ML = 0; ML < 16; ML++)

+        dphaseTable[fnum][block][ML] = RATE_ADJUST (((fnum * mltable[ML]) << block) >> (20 - DP_BITS));

+}

+#endif

+

+static void

+makeTllTable (void)

+{

+#define dB2(x) ((x)*2)

+

+  static double kltable[16] = {

+    dB2 (0.000), dB2 (9.000), dB2 (12.000), dB2 (13.875), dB2 (15.000), dB2 (16.125), dB2 (16.875), dB2 (17.625),

+    dB2 (18.000), dB2 (18.750), dB2 (19.125), dB2 (19.500), dB2 (19.875), dB2 (20.250), dB2 (20.625), dB2 (21.000)

+  };

+

+  e_int32 tmp;

+  e_int32 fnum, block, TL, KL;

+

+  for (fnum = 0; fnum < 16; fnum++)

+    for (block = 0; block < 8; block++)

+      for (TL = 0; TL < 64; TL++)

+        for (KL = 0; KL < 4; KL++)

+        {

+          if (KL == 0)

+          {

+            tllTable[fnum][block][TL][KL] = TL2EG (TL);

+          }

+          else

+          {

+            tmp = (e_int32) (kltable[fnum] - dB2 (3.000) * (7 - block));

+            if (tmp <= 0)

+              tllTable[fnum][block][TL][KL] = TL2EG (TL);

+            else

+              tllTable[fnum][block][TL][KL] = (e_uint32) ((tmp >> (3 - KL)) / EG_STEP) + TL2EG (TL);

+          }

+        }

+}

+

+#ifdef USE_SPEC_ENV_SPEED

+static double attacktime[16][4] = {

+  {0, 0, 0, 0},

+  {1730.15, 1400.60, 1153.43, 988.66},

+  {865.08, 700.30, 576.72, 494.33},

+  {432.54, 350.15, 288.36, 247.16},

+  {216.27, 175.07, 144.18, 123.58},

+  {108.13, 87.54, 72.09, 61.79},

+  {54.07, 43.77, 36.04, 30.90},

+  {27.03, 21.88, 18.02, 15.45},

+  {13.52, 10.94, 9.01, 7.72},

+  {6.76, 5.47, 4.51, 3.86},

+  {3.38, 2.74, 2.25, 1.93},

+  {1.69, 1.37, 1.13, 0.97},

+  {0.84, 0.70, 0.60, 0.54},

+  {0.50, 0.42, 0.34, 0.30},

+  {0.28, 0.22, 0.18, 0.14},

+  {0.00, 0.00, 0.00, 0.00}

+};

+

+static double decaytime[16][4] = {

+  {0, 0, 0, 0},

+  {20926.60, 16807.20, 14006.00, 12028.60},

+  {10463.30, 8403.58, 7002.98, 6014.32},

+  {5231.64, 4201.79, 3501.49, 3007.16},

+  {2615.82, 2100.89, 1750.75, 1503.58},

+  {1307.91, 1050.45, 875.37, 751.79},

+  {653.95, 525.22, 437.69, 375.90},

+  {326.98, 262.61, 218.84, 187.95},

+  {163.49, 131.31, 109.42, 93.97},

+  {81.74, 65.65, 54.71, 46.99},

+  {40.87, 32.83, 27.36, 23.49},

+  {20.44, 16.41, 13.68, 11.75},

+  {10.22, 8.21, 6.84, 5.87},

+  {5.11, 4.10, 3.42, 2.94},

+  {2.55, 2.05, 1.71, 1.47},

+  {1.27, 1.27, 1.27, 1.27}

+};

+#endif

+

+/* Rate Table for Attack */

+static void

+makeDphaseARTable (void)

+{

+  e_int32 AR, Rks, RM, RL;

+

+#ifdef USE_SPEC_ENV_SPEED

+  e_uint32 attacktable[16][4];

+

+  for (RM = 0; RM < 16; RM++)

+    for (RL = 0; RL < 4; RL++)

+    {

+      if (RM == 0)

+        attacktable[RM][RL] = 0;

+      else if (RM == 15)

+        attacktable[RM][RL] = EG_DP_WIDTH;

+      else

+        attacktable[RM][RL] = (e_uint32) ((double) (1 << EG_DP_BITS) / (attacktime[RM][RL] * 3579545 / 72000));

+

+    }

+#endif

+

+  for (AR = 0; AR < 16; AR++)

+    for (Rks = 0; Rks < 16; Rks++)

+    {

+      RM = AR + (Rks >> 2);

+      RL = Rks & 3;

+      if (RM > 15)

+        RM = 15;

+      switch (AR)

+      {

+      case 0:

+        dphaseARTable[AR][Rks] = 0;

+        break;

+      case 15:

+        dphaseARTable[AR][Rks] = 0;/*EG_DP_WIDTH;*/ 

+        break;

+      default:

+#ifdef USE_SPEC_ENV_SPEED

+        dphaseARTable[AR][Rks] = RATE_ADJUST (attacktable[RM][RL]);

+#else

+        dphaseARTable[AR][Rks] = RATE_ADJUST ((3 * (RL + 4) << (RM + 1)));

+#endif

+        break;

+      }

+    }

+}

+

+/* Rate Table for Decay and Release */

+static void

+makeDphaseDRTable (void)

+{

+  e_int32 DR, Rks, RM, RL;

+

+#ifdef USE_SPEC_ENV_SPEED

+  e_uint32 decaytable[16][4];

+

+  for (RM = 0; RM < 16; RM++)

+    for (RL = 0; RL < 4; RL++)

+      if (RM == 0)

+        decaytable[RM][RL] = 0;

+      else

+        decaytable[RM][RL] = (e_uint32) ((double) (1 << EG_DP_BITS) / (decaytime[RM][RL] * 3579545 / 72000));

+#endif

+

+  for (DR = 0; DR < 16; DR++)

+    for (Rks = 0; Rks < 16; Rks++)

+    {

+      RM = DR + (Rks >> 2);

+      RL = Rks & 3;

+      if (RM > 15)

+        RM = 15;

+      switch (DR)

+      {

+      case 0:

+        dphaseDRTable[DR][Rks] = 0;

+        break;

+      default:

+#ifdef USE_SPEC_ENV_SPEED

+        dphaseDRTable[DR][Rks] = RATE_ADJUST (decaytable[RM][RL]);

+#else

+        dphaseDRTable[DR][Rks] = RATE_ADJUST ((RL + 4) << (RM - 1));

+#endif

+        break;

+      }

+    }

+}

+

+static void

+makeRksTable (void)

+{

+

+  e_int32 fnum8, block, KR;

+

+  for (fnum8 = 0; fnum8 < 2; fnum8++)

+    for (block = 0; block < 8; block++)

+      for (KR = 0; KR < 2; KR++)

+      {

+        if (KR != 0)

+          rksTable[fnum8][block][KR] = (block << 1) + fnum8;

+        else

+          rksTable[fnum8][block][KR] = block >> 1;

+      }

+}

+

+void

+OPLL_dump2patch (const e_uint8 * dump, OPLL_PATCH * patch)

+{

+  patch[0].AM = (dump[0] >> 7) & 1;

+  patch[1].AM = (dump[1] >> 7) & 1;

+  patch[0].PM = (dump[0] >> 6) & 1;

+  patch[1].PM = (dump[1] >> 6) & 1;

+  patch[0].EG = (dump[0] >> 5) & 1;

+  patch[1].EG = (dump[1] >> 5) & 1;

+  patch[0].KR = (dump[0] >> 4) & 1;

+  patch[1].KR = (dump[1] >> 4) & 1;

+  patch[0].ML = (dump[0]) & 15;

+  patch[1].ML = (dump[1]) & 15;

+  patch[0].KL = (dump[2] >> 6) & 3;

+  patch[1].KL = (dump[3] >> 6) & 3;

+  patch[0].TL = (dump[2]) & 63;

+  patch[0].FB = (dump[3]) & 7;

+  patch[0].WF = (dump[3] >> 3) & 1;

+  patch[1].WF = (dump[3] >> 4) & 1;

+  patch[0].AR = (dump[4] >> 4) & 15;

+  patch[1].AR = (dump[5] >> 4) & 15;

+  patch[0].DR = (dump[4]) & 15;

+  patch[1].DR = (dump[5]) & 15;

+  patch[0].SL = (dump[6] >> 4) & 15;

+  patch[1].SL = (dump[7] >> 4) & 15;

+  patch[0].RR = (dump[6]) & 15;

+  patch[1].RR = (dump[7]) & 15;

+}

+

+void

+OPLL_getDefaultPatch (e_int32 type, e_int32 num, OPLL_PATCH * patch)

+{

+  OPLL_dump2patch (default_inst[type] + num * 16, patch);

+}

+

+static void

+makeDefaultPatch ( void )

+{

+  e_int32 i, j;

+

+  for (i = 0; i < OPLL_TONE_NUM; i++)

+    for (j = 0; j < 19; j++)

+      OPLL_getDefaultPatch (i, j, &default_patch[i][j * 2]);

+

+}

+

+void

+OPLL_setPatch (OPLL * opll, const e_uint8 * dump)

+{

+  OPLL_PATCH patch[2];

+  int i;

+

+  for (i = 0; i < 19; i++)

+  {

+    OPLL_dump2patch (dump + i * 16, patch);

+    memcpy (&opll->patch[i*2+0], &patch[0], sizeof (OPLL_PATCH));

+    memcpy (&opll->patch[i*2+1], &patch[1], sizeof (OPLL_PATCH));

+  }

+}

+

+void

+OPLL_patch2dump (const OPLL_PATCH * patch, e_uint8 * dump)

+{

+  dump[0] = (e_uint8) ((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML);

+  dump[1] = (e_uint8) ((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML);

+  dump[2] = (e_uint8) ((patch[0].KL << 6) + patch[0].TL);

+  dump[3] = (e_uint8) ((patch[1].KL << 6) + (patch[1].WF << 4) + (patch[0].WF << 3) + patch[0].FB);

+  dump[4] = (e_uint8) ((patch[0].AR << 4) + patch[0].DR);

+  dump[5] = (e_uint8) ((patch[1].AR << 4) + patch[1].DR);

+  dump[6] = (e_uint8) ((patch[0].SL << 4) + patch[0].RR);

+  dump[7] = (e_uint8) ((patch[1].SL << 4) + patch[1].RR);

+  dump[8] = 0;

+  dump[9] = 0;

+  dump[10] = 0;

+  dump[11] = 0;

+  dump[12] = 0;

+  dump[13] = 0;

+  dump[14] = 0;

+  dump[15] = 0;

+}

+

+/************************************************************

+

+                      Calc Parameters

+

+************************************************************/

+

+INLINE static e_uint32

+calc_eg_dphase (OPLL_SLOT * slot)

+{

+

+  switch (slot->eg_mode)

+  {

+  case ATTACK:

+    return dphaseARTable[slot->patch->AR][slot->rks];

+

+  case DECAY:

+    return dphaseDRTable[slot->patch->DR][slot->rks];

+

+  case SUSHOLD:

+    return 0;

+

+  case SUSTINE:

+    return dphaseDRTable[slot->patch->RR][slot->rks];

+

+  case RELEASE:

+    if (slot->sustine)

+      return dphaseDRTable[5][slot->rks];

+    else if (slot->patch->EG)

+      return dphaseDRTable[slot->patch->RR][slot->rks];

+    else

+      return dphaseDRTable[7][slot->rks];

+

+  case SETTLE:

+    return dphaseDRTable[15][0];

+

+  case FINISH:

+    return 0;

+

+  default:

+    return 0;

+  }

+}

+

+/*************************************************************

+

+                    OPLL internal interfaces

+

+*************************************************************/

+#define SLOT_BD1 12

+#define SLOT_BD2 13

+#define SLOT_HH 14

+#define SLOT_SD 15

+#define SLOT_TOM 16

+#define SLOT_CYM 17

+

+/* We will set this dinamically, but not sure if this affects playback */

+#if defined(ROCKBOX)

+INLINE static void

+UPDATE_PG(OPLL_SLOT * slot)

+{

+  static const e_uint32 mltable[16] =

+    { 1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2 };

+

+  slot->dphase = RATE_ADJUST (((slot->fnum * mltable[slot->patch->ML]) << slot->block) >> (20 - DP_BITS));

+}

+#else

+#define UPDATE_PG(S)  (S)->dphase = dphaseTable[(S)->fnum][(S)->block][(S)->patch->ML]

+#endif

+

+#define UPDATE_TLL(S)\

+(((S)->type==0)?\

+((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->patch->TL][(S)->patch->KL]):\

+((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->volume][(S)->patch->KL]))

+#define UPDATE_RKS(S) (S)->rks = rksTable[((S)->fnum)>>8][(S)->block][(S)->patch->KR]

+#define UPDATE_WF(S)  (S)->sintbl = waveform[(S)->patch->WF]

+#define UPDATE_EG(S)  (S)->eg_dphase = calc_eg_dphase(S)

+#define UPDATE_ALL(S)\

+  UPDATE_PG(S);\

+  UPDATE_TLL(S);\

+  UPDATE_RKS(S);\

+  UPDATE_WF(S); \

+  UPDATE_EG(S)                  /* EG should be updated last. */

+

+

+/* Slot key on  */

+INLINE static void

+slotOn (OPLL_SLOT * slot)

+{

+  slot->eg_mode = ATTACK;

+  slot->eg_phase = 0;

+  slot->phase = 0;

+  UPDATE_EG(slot);

+}

+

+/* Slot key on without reseting the phase */

+INLINE static void

+slotOn2 (OPLL_SLOT * slot)

+{

+  slot->eg_mode = ATTACK;

+  slot->eg_phase = 0;

+  UPDATE_EG(slot);

+}

+

+/* Slot key off */

+INLINE static void

+slotOff (OPLL_SLOT * slot)

+{

+  if (slot->eg_mode == ATTACK)

+    slot->eg_phase = EXPAND_BITS (AR_ADJUST_TABLE[HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS)], EG_BITS, EG_DP_BITS);

+  slot->eg_mode = RELEASE;

+  UPDATE_EG(slot);

+}

+

+/* Channel key on */

+INLINE static void

+keyOn (OPLL * opll, e_int32 i)

+{

+  if (!opll->slot_on_flag[i * 2])

+    slotOn (MOD(opll,i));

+  if (!opll->slot_on_flag[i * 2 + 1])

+    slotOn (CAR(opll,i));

+  opll->key_status[i] = 1;

+}

+

+/* Channel key off */

+INLINE static void

+keyOff (OPLL * opll, e_int32 i)

+{

+  if (opll->slot_on_flag[i * 2 + 1])

+    slotOff (CAR(opll,i));

+  opll->key_status[i] = 0;

+}

+

+INLINE static void

+keyOn_BD (OPLL * opll)

+{

+  keyOn (opll, 6);

+}

+INLINE static void

+keyOn_SD (OPLL * opll)

+{

+  if (!opll->slot_on_flag[SLOT_SD])

+    slotOn (CAR(opll,7));

+}

+INLINE static void

+keyOn_TOM (OPLL * opll)

+{

+  if (!opll->slot_on_flag[SLOT_TOM])

+    slotOn (MOD(opll,8));

+}

+INLINE static void

+keyOn_HH (OPLL * opll)

+{

+  if (!opll->slot_on_flag[SLOT_HH])

+    slotOn2 (MOD(opll,7));

+}

+INLINE static void

+keyOn_CYM (OPLL * opll)

+{

+  if (!opll->slot_on_flag[SLOT_CYM])

+    slotOn2 (CAR(opll,8));

+}

+

+/* Drum key off */

+INLINE static void

+keyOff_BD (OPLL * opll)

+{

+  keyOff (opll, 6);

+}

+INLINE static void

+keyOff_SD (OPLL * opll)

+{

+  if (opll->slot_on_flag[SLOT_SD])

+    slotOff (CAR(opll,7));

+}

+INLINE static void

+keyOff_TOM (OPLL * opll)

+{

+  if (opll->slot_on_flag[SLOT_TOM])

+    slotOff (MOD(opll,8));

+}

+INLINE static void

+keyOff_HH (OPLL * opll)

+{

+  if (opll->slot_on_flag[SLOT_HH])

+    slotOff (MOD(opll,7));

+}

+INLINE static void

+keyOff_CYM (OPLL * opll)

+{

+  if (opll->slot_on_flag[SLOT_CYM])

+    slotOff (CAR(opll,8));

+}

+

+/* Change a voice */

+INLINE static void

+setPatch (OPLL * opll, e_int32 i, e_int32 num)

+{

+  opll->patch_number[i] = num;

+  MOD(opll,i)->patch = &opll->patch[num * 2 + 0];

+  CAR(opll,i)->patch = &opll->patch[num * 2 + 1];

+}

+

+/* Change a rhythm voice */

+INLINE static void

+setSlotPatch (OPLL_SLOT * slot, OPLL_PATCH * patch)

+{

+  slot->patch = patch;

+}

+

+/* Set sustine parameter */

+INLINE static void

+setSustine (OPLL * opll, e_int32 c, e_int32 sustine)

+{

+  CAR(opll,c)->sustine = sustine;

+  if (MOD(opll,c)->type)

+    MOD(opll,c)->sustine = sustine;

+}

+

+/* Volume : 6bit ( Volume register << 2 ) */

+INLINE static void

+setVolume (OPLL * opll, e_int32 c, e_int32 volume)

+{

+  CAR(opll,c)->volume = volume;

+}

+

+INLINE static void

+setSlotVolume (OPLL_SLOT * slot, e_int32 volume)

+{

+  slot->volume = volume;

+}

+

+/* Set F-Number ( fnum : 9bit ) */

+INLINE static void

+setFnumber (OPLL * opll, e_int32 c, e_int32 fnum)

+{

+  CAR(opll,c)->fnum = fnum;

+  MOD(opll,c)->fnum = fnum;

+}

+

+/* Set Block data (block : 3bit ) */

+INLINE static void

+setBlock (OPLL * opll, e_int32 c, e_int32 block)

+{

+  CAR(opll,c)->block = block;

+  MOD(opll,c)->block = block;

+}

+

+/* Change Rhythm Mode */

+INLINE static void

+update_rhythm_mode (OPLL * opll)

+{

+  if (opll->patch_number[6] & 0x10)

+  {

+    if (!(opll->slot_on_flag[SLOT_BD2] | (opll->reg[0x0e] & 32)))

+    {

+      opll->slot[SLOT_BD1].eg_mode = FINISH;

+      opll->slot[SLOT_BD2].eg_mode = FINISH;

+      setPatch (opll, 6, opll->reg[0x36] >> 4);

+    }

+  }

+  else if (opll->reg[0x0e] & 32)

+  {

+    opll->patch_number[6] = 16;

+    opll->slot[SLOT_BD1].eg_mode = FINISH;

+    opll->slot[SLOT_BD2].eg_mode = FINISH;

+    setSlotPatch (&opll->slot[SLOT_BD1], &opll->patch[16 * 2 + 0]);

+    setSlotPatch (&opll->slot[SLOT_BD2], &opll->patch[16 * 2 + 1]);

+  }

+

+  if (opll->patch_number[7] & 0x10)

+  {

+    if (!((opll->slot_on_flag[SLOT_HH] && opll->slot_on_flag[SLOT_SD]) | (opll->reg[0x0e] & 32)))

+    {

+      opll->slot[SLOT_HH].type = 0;

+      opll->slot[SLOT_HH].eg_mode = FINISH;

+      opll->slot[SLOT_SD].eg_mode = FINISH;

+      setPatch (opll, 7, opll->reg[0x37] >> 4);

+    }

+  }

+  else if (opll->reg[0x0e] & 32)

+  {

+    opll->patch_number[7] = 17;

+    opll->slot[SLOT_HH].type = 1;

+    opll->slot[SLOT_HH].eg_mode = FINISH;

+    opll->slot[SLOT_SD].eg_mode = FINISH;

+    setSlotPatch (&opll->slot[SLOT_HH], &opll->patch[17 * 2 + 0]);

+    setSlotPatch (&opll->slot[SLOT_SD], &opll->patch[17 * 2 + 1]);

+  }

+

+  if (opll->patch_number[8] & 0x10)

+  {

+    if (!((opll->slot_on_flag[SLOT_CYM] && opll->slot_on_flag[SLOT_TOM]) | (opll->reg[0x0e] & 32)))

+    {

+      opll->slot[SLOT_TOM].type = 0;

+      opll->slot[SLOT_TOM].eg_mode = FINISH;

+      opll->slot[SLOT_CYM].eg_mode = FINISH;

+      setPatch (opll, 8, opll->reg[0x38] >> 4);

+    }

+  }

+  else if (opll->reg[0x0e] & 32)

+  {

+    opll->patch_number[8] = 18;

+    opll->slot[SLOT_TOM].type = 1;

+    opll->slot[SLOT_TOM].eg_mode = FINISH;

+    opll->slot[SLOT_CYM].eg_mode = FINISH;

+    setSlotPatch (&opll->slot[SLOT_TOM], &opll->patch[18 * 2 + 0]);

+    setSlotPatch (&opll->slot[SLOT_CYM], &opll->patch[18 * 2 + 1]);

+  }

+}

+

+INLINE static void

+update_key_status (OPLL * opll)

+{

+  int ch;

+

+  for (ch = 0; ch < 9; ch++)

+    opll->slot_on_flag[ch * 2] = opll->slot_on_flag[ch * 2 + 1] = (opll->reg[0x20 + ch]) & 0x10;

+

+  if (opll->reg[0x0e] & 32)

+  {

+    opll->slot_on_flag[SLOT_BD1] |= (opll->reg[0x0e] & 0x10);

+    opll->slot_on_flag[SLOT_BD2] |= (opll->reg[0x0e] & 0x10);

+    opll->slot_on_flag[SLOT_SD] |= (opll->reg[0x0e] & 0x08);

+    opll->slot_on_flag[SLOT_HH] |= (opll->reg[0x0e] & 0x01);

+    opll->slot_on_flag[SLOT_TOM] |= (opll->reg[0x0e] & 0x04);

+    opll->slot_on_flag[SLOT_CYM] |= (opll->reg[0x0e] & 0x02);

+  }

+}

+

+void

+OPLL_copyPatch (OPLL * opll, e_int32 num, OPLL_PATCH * patch)

+{

+  memcpy (&opll->patch[num], patch, sizeof (OPLL_PATCH));

+}

+

+/***********************************************************

+

+                      Initializing

+

+***********************************************************/

+

+static void

+OPLL_SLOT_reset (OPLL_SLOT * slot, int type)

+{

+  slot->type = type;

+  slot->sintbl = waveform[0];

+  slot->phase = 0;

+  slot->dphase = 0;

+  slot->output[0] = 0;

+  slot->output[1] = 0;

+  slot->feedback = 0;

+  slot->eg_mode = FINISH;

+  slot->eg_phase = EG_DP_WIDTH;

+  slot->eg_dphase = 0;

+  slot->rks = 0;

+  slot->tll = 0;

+  slot->sustine = 0;

+  slot->fnum = 0;

+  slot->block = 0;

+  slot->volume = 0;

+  slot->pgout = 0;

+  slot->egout = 0;

+  slot->patch = &null_patch;

+}

+

+static void

+internal_refresh (void)

+{

+#if !defined(ROCKBOX)

+  makeDphaseTable ();

+#endif

+  makeDphaseARTable ();

+  makeDphaseDRTable ();

+  pm_dphase = (e_uint32) RATE_ADJUST (PM_SPEED * PM_DP_WIDTH / (clk / 72));

+  am_dphase = (e_uint32) RATE_ADJUST (AM_SPEED * AM_DP_WIDTH / (clk / 72));

+}

+

+static void

+maketables (e_uint32 c, e_uint32 r)

+{

+  if (c != clk)

+  {

+    clk = c;

+    makePmTable ();

+    makeAmTable ();

+    makeDB2LinTable ();

+    makeAdjustTable ();

+    makeTllTable ();

+    makeRksTable ();

+    makeSinTable ();

+    makeDefaultPatch ();

+  }

+

+  if (r != rate)

+  {

+    rate = r;

+    internal_refresh ();

+  }

+}

+

+void

+OPLL_new (OPLL *opll, e_uint32 clk, e_uint32 rate)

+{

+  e_int32 i;

+

+  maketables (clk, rate);

+

+  memset(opll, 0, sizeof (OPLL));

+  for (i = 0; i < 19 * 2; i++)

+    memcpy(&opll->patch[i],&null_patch,sizeof(OPLL_PATCH));

+

+  opll->mask = 0;

+

+  OPLL_reset (opll);

+  OPLL_reset_patch (opll, 0);

+}

+

+

+void

+OPLL_delete (OPLL * opll)

+{

+  (void) opll;

+}

+

+

+/* Reset patch datas by system default. */

+void

+OPLL_reset_patch (OPLL * opll, e_int32 type)

+{

+  e_int32 i;

+

+  for (i = 0; i < 19 * 2; i++)

+    OPLL_copyPatch (opll, i, &default_patch[type % OPLL_TONE_NUM][i]);

+}

+

+/* Reset whole of OPLL except patch datas. */

+void

+OPLL_reset (OPLL * opll)

+{

+  e_int32 i;

+

+  if (!opll)

+    return;

+

+  opll->adr = 0;

+  opll->out = 0;

+

+  opll->pm_phase = 0;

+  opll->am_phase = 0;

+

+  opll->noise_seed = 0xffff;

+  opll->mask = 0;

+

+  for (i = 0; i <18; i++)

+    OPLL_SLOT_reset(&opll->slot[i], i%2);

+

+  for (i = 0; i < 9; i++)

+  {

+    opll->key_status[i] = 0;

+    setPatch (opll, i, 0);

+  }

+

+  for (i = 0; i < 0x40; i++)

+    OPLL_writeReg (opll, i, 0);

+

+#ifndef EMU2413_COMPACTION

+  opll->realstep = (e_uint32) ((1 << 31) / rate);

+  opll->opllstep = (e_uint32) ((1 << 31) / (clk / 72));

+  opll->oplltime = 0;

+  for (i = 0; i < 14; i++)

+    opll->pan[i] = 2;

+  opll->sprev[0] = opll->sprev[1] = 0;

+  opll->snext[0] = opll->snext[1] = 0;

+#endif

+}

+

+/* Force Refresh (When external program changes some parameters). */

+void

+OPLL_forceRefresh (OPLL * opll)

+{

+  e_int32 i;

+

+  if (opll == NULL)

+    return;

+

+  for (i = 0; i < 9; i++)

+    setPatch(opll,i,opll->patch_number[i]);

+

+  for (i = 0; i < 18; i++)

+  {

+    UPDATE_PG (&opll->slot[i]);

+    UPDATE_RKS (&opll->slot[i]);

+    UPDATE_TLL (&opll->slot[i]);

+    UPDATE_WF (&opll->slot[i]);

+    UPDATE_EG (&opll->slot[i]);

+  }

+}

+

+void

+OPLL_set_rate (OPLL * opll, e_uint32 r)

+{

+  if (rate == r) return;

+  if (opll->quality)

+    rate = 49716;

+  else

+    rate = r;

+  internal_refresh ();

+  rate = r;

+}

+

+void

+OPLL_set_quality (OPLL * opll, e_uint32 q)

+{

+  opll->quality = q;

+  OPLL_set_rate (opll, rate);

+}

+

+/*********************************************************

+

+                 Generate wave data

+

+*********************************************************/

+/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 2PI). */

+#if ( SLOT_AMP_BITS - PG_BITS ) > 0

+#define wave2_2pi(e)  ( (e) >> ( SLOT_AMP_BITS - PG_BITS ))

+#else

+#define wave2_2pi(e)  ( (e) << ( PG_BITS - SLOT_AMP_BITS ))

+#endif

+

+/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). */

+#if ( SLOT_AMP_BITS - PG_BITS - 1 ) == 0

+#define wave2_4pi(e)  (e)

+#elif ( SLOT_AMP_BITS - PG_BITS - 1 ) > 0

+#define wave2_4pi(e)  ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 1 ))

+#else

+#define wave2_4pi(e)  ( (e) << ( 1 + PG_BITS - SLOT_AMP_BITS ))

+#endif

+

+/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). */

+#if ( SLOT_AMP_BITS - PG_BITS - 2 ) == 0

+#define wave2_8pi(e)  (e)

+#elif ( SLOT_AMP_BITS - PG_BITS - 2 ) > 0

+#define wave2_8pi(e)  ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 2 ))

+#else

+#define wave2_8pi(e)  ( (e) << ( 2 + PG_BITS - SLOT_AMP_BITS ))

+#endif

+

+/* Update AM, PM unit */

+static void

+update_ampm (OPLL * opll)

+{

+  opll->pm_phase = (opll->pm_phase + pm_dphase) & (PM_DP_WIDTH - 1);

+  opll->am_phase = (opll->am_phase + am_dphase) & (AM_DP_WIDTH - 1);

+  opll->lfo_am = amtable[HIGHBITS (opll->am_phase, AM_DP_BITS - AM_PG_BITS)];

+  opll->lfo_pm = pmtable[HIGHBITS (opll->pm_phase, PM_DP_BITS - PM_PG_BITS)];

+}

+

+/* PG */

+INLINE static void

+calc_phase (OPLL_SLOT * slot, e_int32 lfo)

+{

+  if (slot->patch->PM)

+    slot->phase += (slot->dphase * lfo) >> PM_AMP_BITS;

+  else

+    slot->phase += slot->dphase;

+

+  slot->phase &= (DP_WIDTH - 1);

+

+  slot->pgout = HIGHBITS (slot->phase, DP_BASE_BITS);

+}

+

+/* Update Noise unit */

+static void

+update_noise (OPLL * opll)

+{

+   if(opll->noise_seed&1) opll->noise_seed ^= 0x8003020;

+   opll->noise_seed >>= 1;

+}

+

+/* EG */

+static void

+calc_envelope (OPLL_SLOT * slot, e_int32 lfo)

+{

+#define S2E(x) (SL2EG((e_int32)(x/SL_STEP))<<(EG_DP_BITS-EG_BITS))

+

+  static e_uint32 SL[16] = {

+    S2E (0.0), S2E (3.0), S2E (6.0), S2E (9.0), S2E (12.0), S2E (15.0), S2E (18.0), S2E (21.0),

+    S2E (24.0), S2E (27.0), S2E (30.0), S2E (33.0), S2E (36.0), S2E (39.0), S2E (42.0), S2E (48.0)

+  };

+

+  e_uint32 egout;

+

+  switch (slot->eg_mode)

+  {

+  case ATTACK:

+    egout = AR_ADJUST_TABLE[HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS)];

+    slot->eg_phase += slot->eg_dphase;

+    if((EG_DP_WIDTH & slot->eg_phase)||(slot->patch->AR==15))

+    {

+      egout = 0;

+      slot->eg_phase = 0;

+      slot->eg_mode = DECAY;

+      UPDATE_EG (slot);

+    }

+    break;

+

+  case DECAY:

+    egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS);

+    slot->eg_phase += slot->eg_dphase;

+    if (slot->eg_phase >= SL[slot->patch->SL])

+    {

+      if (slot->patch->EG)

+      {

+        slot->eg_phase = SL[slot->patch->SL];

+        slot->eg_mode = SUSHOLD;

+        UPDATE_EG (slot);

+      }

+      else

+      {

+        slot->eg_phase = SL[slot->patch->SL];

+        slot->eg_mode = SUSTINE;

+        UPDATE_EG (slot);

+      }

+    }

+    break;

+

+  case SUSHOLD:

+    egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS);

+    if (slot->patch->EG == 0)

+    {

+      slot->eg_mode = SUSTINE;

+      UPDATE_EG (slot);

+    }

+    break;

+

+  case SUSTINE:

+  case RELEASE:

+    egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS);

+    slot->eg_phase += slot->eg_dphase;

+    if (egout >= (1 << EG_BITS))

+    {

+      slot->eg_mode = FINISH;

+      egout = (1 << EG_BITS) - 1;

+    }

+    break;

+

+  case SETTLE:

+    egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS);

+    slot->eg_phase += slot->eg_dphase;

+    if (egout >= (1 << EG_BITS))

+    {

+      slot->eg_mode = ATTACK;

+      egout = (1 << EG_BITS) - 1;

+      UPDATE_EG(slot);

+    }

+    break;

+

+  case FINISH:

+    egout = (1 << EG_BITS) - 1;

+    break;

+

+  default:

+    egout = (1 << EG_BITS) - 1;

+    break;

+  }

+

+  if (slot->patch->AM)

+    egout = EG2DB (egout + slot->tll) + lfo;

+  else

+    egout = EG2DB (egout + slot->tll);

+

+  if (egout >= DB_MUTE)

+    egout = DB_MUTE - 1;

+  

+  slot->egout = egout | 3;

+}

+

+/* CARRIOR */

+INLINE static e_int32

+calc_slot_car (OPLL_SLOT * slot, e_int32 fm)

+{

+  if (slot->egout >= (DB_MUTE - 1))

+  {

+    slot->output[0] = 0;

+  }

+  else

+  {

+    slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout+wave2_8pi(fm))&(PG_WIDTH-1)] + slot->egout];

+  }

+

+  slot->output[1] = (slot->output[1] + slot->output[0]) >> 1;

+  return slot->output[1];

+}

+

+/* MODULATOR */

+INLINE static e_int32

+calc_slot_mod (OPLL_SLOT * slot)

+{

+  e_int32 fm;

+

+  slot->output[1] = slot->output[0];

+

+  if (slot->egout >= (DB_MUTE - 1))

+  {

+    slot->output[0] = 0;

+  }

+  else if (slot->patch->FB != 0)

+  {

+    fm = wave2_4pi (slot->feedback) >> (7 - slot->patch->FB);

+    slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout+fm)&(PG_WIDTH-1)] + slot->egout];

+  }

+  else

+  {

+    slot->output[0] = DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout];

+  }

+

+  slot->feedback = (slot->output[1] + slot->output[0]) >> 1;

+

+  return slot->feedback;

+

+}

+

+/* TOM */

+INLINE static e_int32

+calc_slot_tom (OPLL_SLOT * slot)

+{

+  if (slot->egout >= (DB_MUTE - 1))

+    return 0;

+

+  return DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout];

+

+}

+

+/* SNARE */

+INLINE static e_int32

+calc_slot_snare (OPLL_SLOT * slot, e_uint32 noise)

+{

+  if(slot->egout>=(DB_MUTE-1))

+    return 0;

+  

+  if(BIT(slot->pgout,7))

+    return DB2LIN_TABLE[(noise?DB_POS(0.0):DB_POS(15.0))+slot->egout];

+  else

+    return DB2LIN_TABLE[(noise?DB_NEG(0.0):DB_NEG(15.0))+slot->egout];

+}

+

+/* 

+  TOP-CYM 

+ */

+INLINE static e_int32

+calc_slot_cym (OPLL_SLOT * slot, e_uint32 pgout_hh)

+{

+  e_uint32 dbout;

+

+  if (slot->egout >= (DB_MUTE - 1)) 

+    return 0;

+  else if( 

+      /* the same as fmopl.c */

+      ((BIT(pgout_hh,PG_BITS-8)^BIT(pgout_hh,PG_BITS-1))|BIT(pgout_hh,PG_BITS-7)) ^

+      /* different from fmopl.c */

+     (BIT(slot->pgout,PG_BITS-7)&!BIT(slot->pgout,PG_BITS-5))

+    )

+    dbout = DB_NEG(3.0);

+  else

+    dbout = DB_POS(3.0);

+

+  return DB2LIN_TABLE[dbout + slot->egout];

+}

+

+/* 

+  HI-HAT 

+*/

+INLINE static e_int32

+calc_slot_hat (OPLL_SLOT *slot, e_int32 pgout_cym, e_uint32 noise)

+{

+  e_uint32 dbout;

+

+  if (slot->egout >= (DB_MUTE - 1)) 

+    return 0;

+  else if( 

+      /* the same as fmopl.c */

+      ((BIT(slot->pgout,PG_BITS-8)^BIT(slot->pgout,PG_BITS-1))|BIT(slot->pgout,PG_BITS-7)) ^

+      /* different from fmopl.c */

+      (BIT(pgout_cym,PG_BITS-7)&!BIT(pgout_cym,PG_BITS-5))

+    )

+  {

+    if(noise)

+      dbout = DB_NEG(12.0);

+    else

+      dbout = DB_NEG(24.0);

+  }

+  else

+  {

+    if(noise)

+      dbout = DB_POS(12.0);

+    else

+      dbout = DB_POS(24.0);

+  }

+

+  return DB2LIN_TABLE[dbout + slot->egout];

+}

+

+static e_int16

+calc (OPLL * opll)

+{

+  e_int32 i;

+

+  update_ampm (opll);

+  update_noise (opll);

+

+  for (i = 0; i < 18; i++)

+  {

+    calc_phase(&opll->slot[i],opll->lfo_pm);

+    calc_envelope(&opll->slot[i],opll->lfo_am);

+  }

+

+  e_uint32 channel_mask = opll->mask;

+  for (i = 0; i < 9; i++) {

+    if (CAR(opll,i)->eg_mode != FINISH)

+      channel_mask |= (1 << i);

+  }

+

+  e_int32 mix = 0;

+  

+  /* CH6 */

+  if (opll->patch_number[6] & 0x10) {

+    if (channel_mask & OPLL_MASK_CH (6)) {

+      mix += calc_slot_car (CAR(opll,6), calc_slot_mod(MOD(opll,6)));

+      channel_mask &= ~(1 << 6);

+	}

+  }

+

+  /* CH7 */

+  if (opll->patch_number[7] & 0x10) {

+    if (MOD(opll,7)->eg_mode != FINISH)

+      mix += calc_slot_hat (MOD(opll,7), CAR(opll,8)->pgout, opll->noise_seed&1);

+    if (channel_mask & OPLL_MASK_SD) {

+      mix -= calc_slot_snare (CAR(opll,7), opll->noise_seed&1);

+      channel_mask &= ~OPLL_MASK_SD;

+	}

+  }

+

+  /* CH8 */

+  if (opll->patch_number[8] & 0x10) {

+    if (MOD(opll,8)->eg_mode != FINISH)

+      mix += calc_slot_tom (MOD(opll,8));

+    if (channel_mask & OPLL_MASK_CYM) {

+      mix -= calc_slot_cym (CAR(opll,8), MOD(opll,7)->pgout);

+      channel_mask &= ~OPLL_MASK_CYM;

+    }

+  }

+  

+  mix <<= 1;

+  

+  opll->current_mask = channel_mask;

+  for (i = 0; channel_mask; channel_mask >>= 1, ++i) {

+    if (channel_mask & 1) {

+      mix += calc_slot_car (CAR(opll,i), calc_slot_mod(MOD(opll,i)));

+    }

+  }

+

+  return (e_int16) mix << 3;

+}

+

+void

+OPLL_set_internal_mute(OPLL * opll, e_uint32 mute)

+{

+  opll->internal_mute = mute;

+}

+

+e_uint32

+OPLL_is_internal_muted(OPLL * opll)

+{

+  return opll->internal_mute; 

+}

+

+e_uint32

+check_mute_helper(OPLL * opll)

+{		

+  for (int i = 0; i < 6; i++) {

+    /* if (ch[i].car.eg_mode != FINISH) return 0; */

+    if (!(opll->current_mask & OPLL_MASK_CH (i)) && (CAR(opll,i)->eg_mode != FINISH)) return 0;

+  }

+

+  if (!(opll->reg[0x0e] & 0x20)) {

+    for(int i = 6; i < 9; i++) {

+      /* if (ch[i].car.eg_mode != FINISH) return 0; */

+      if (!(opll->current_mask & OPLL_MASK_CH (i)) && (CAR(opll,i)->eg_mode != FINISH)) return 0;

+    }

+  } else {

+    /* if (ch[6].car.eg_mode != FINISH) return false;

+    if (ch[7].mod.eg_mode != FINISH) return false;

+    if (ch[7].car.eg_mode != FINISH) return false;

+    if (ch[8].mod.eg_mode != FINISH) return false;

+    if (ch[8].car.eg_mode != FINISH) return false; */

+    if (!(opll->current_mask & OPLL_MASK_CH (6)) && (CAR(opll,6)->eg_mode != FINISH)) return 0;

+    if (!(opll->current_mask & OPLL_MASK_CH (7)) && (MOD(opll,7)->eg_mode != FINISH)) return 0;

+    if (!(opll->current_mask & OPLL_MASK_CH (7)) && (CAR(opll,7)->eg_mode != FINISH)) return 0;

+    if (!(opll->current_mask & OPLL_MASK_CH (8)) && (MOD(opll,8)->eg_mode != FINISH)) return 0;

+    if (!(opll->current_mask & OPLL_MASK_CH (8)) && (CAR(opll,8)->eg_mode != FINISH)) return 0;

+  }

+  

+  return 1;    /* nothing is playing, then mute */

+}

+

+void

+check_mute(OPLL * opll)

+{

+  OPLL_set_internal_mute (opll, check_mute_helper (opll));

+}

+

+EMU2413_API e_int16 *OPLL_update_buffer(OPLL * opll, e_uint32 length)

+{

+  e_int16* buf = opll->buffer;

+  while (length--) {

+    *(buf++) = calc (opll);

+  }

+  check_mute (opll);

+

+  return opll->buffer;

+}

+

+#ifdef EMU2413_COMPACTION

+e_int16

+OPLL_calc (OPLL * opll)

+{

+  return calc (opll);

+}

+#else

+e_int16

+OPLL_calc (OPLL * opll)

+{

+  if (!opll->quality)

+    return calc (opll);

+

+  while (opll->realstep > opll->oplltime)

+  {

+    opll->oplltime += opll->opllstep;

+    opll->prev = opll->next;

+    opll->next = calc (opll);

+  }

+

+  opll->oplltime -= opll->realstep;

+  opll->out = (e_int16) (((double) opll->next * (opll->opllstep - opll->oplltime)

+                          + (double) opll->prev * opll->oplltime) / opll->opllstep);

+

+  return (e_int16) opll->out;

+}

+#endif

+

+e_uint32

+OPLL_setMask (OPLL * opll, e_uint32 mask)

+{

+  e_uint32 ret;

+

+  if (opll)

+  {

+    ret = opll->mask;

+    opll->mask = mask;

+    return ret;

+  }

+  else

+    return 0;

+}

+

+e_uint32

+OPLL_toggleMask (OPLL * opll, e_uint32 mask)

+{

+  e_uint32 ret;

+

+  if (opll)

+  {

+    ret = opll->mask;

+    opll->mask ^= mask;

+    return ret;

+  }

+  else

+    return 0;

+}

+

+/****************************************************

+

+                       I/O Ctrl

+

+*****************************************************/

+

+void

+OPLL_writeReg (OPLL * opll, e_uint32 reg, e_uint32 data)

+{

+  e_int32 i, v, ch;

+

+  data = data & 0xff;

+  reg = reg & 0x3f;

+  opll->reg[reg] = (e_uint8) data;

+

+  switch (reg)

+  {

+  case 0x00:

+    opll->patch[0].AM = (data >> 7) & 1;

+    opll->patch[0].PM = (data >> 6) & 1;

+    opll->patch[0].EG = (data >> 5) & 1;

+    opll->patch[0].KR = (data >> 4) & 1;

+    opll->patch[0].ML = (data) & 15;

+    for (i = 0; i < 9; i++)

+    {

+      if (opll->patch_number[i] == 0)

+      {

+        UPDATE_PG (MOD(opll,i));

+        UPDATE_RKS (MOD(opll,i));

+        UPDATE_EG (MOD(opll,i));

+      }

+    }

+    break;

+

+  case 0x01:

+    opll->patch[1].AM = (data >> 7) & 1;

+    opll->patch[1].PM = (data >> 6) & 1;

+    opll->patch[1].EG = (data >> 5) & 1;

+    opll->patch[1].KR = (data >> 4) & 1;

+    opll->patch[1].ML = (data) & 15;

+    for (i = 0; i < 9; i++)

+    {

+      if (opll->patch_number[i] == 0)

+      {

+        UPDATE_PG (CAR(opll,i));

+        UPDATE_RKS (CAR(opll,i));

+        UPDATE_EG (CAR(opll,i));

+      }

+    }

+    break;

+

+  case 0x02:

+    opll->patch[0].KL = (data >> 6) & 3;

+    opll->patch[0].TL = (data) & 63;

+    for (i = 0; i < 9; i++)

+    {

+      if (opll->patch_number[i] == 0)

+      {

+        UPDATE_TLL(MOD(opll,i));

+      }

+    }

+    break;

+

+  case 0x03:

+    opll->patch[1].KL = (data >> 6) & 3;

+    opll->patch[1].WF = (data >> 4) & 1;

+    opll->patch[0].WF = (data >> 3) & 1;

+    opll->patch[0].FB = (data) & 7;

+    for (i = 0; i < 9; i++)

+    {

+      if (opll->patch_number[i] == 0)

+      {

+        UPDATE_WF(MOD(opll,i));

+        UPDATE_WF(CAR(opll,i));

+      }

+    }

+    break;

+

+  case 0x04:

+    opll->patch[0].AR = (data >> 4) & 15;

+    opll->patch[0].DR = (data) & 15;

+    for (i = 0; i < 9; i++)

+    {

+      if (opll->patch_number[i] == 0)

+      {

+        UPDATE_EG (MOD(opll,i));

+      }

+    }

+    break;

+

+  case 0x05:

+    opll->patch[1].AR = (data >> 4) & 15;

+    opll->patch[1].DR = (data) & 15;

+    for (i = 0; i < 9; i++)

+    {

+      if (opll->patch_number[i] == 0)

+      {

+        UPDATE_EG(CAR(opll,i));

+      }

+    }

+    break;

+

+  case 0x06:

+    opll->patch[0].SL = (data >> 4) & 15;

+    opll->patch[0].RR = (data) & 15;

+    for (i = 0; i < 9; i++)

+    {

+      if (opll->patch_number[i] == 0)

+      {

+        UPDATE_EG (MOD(opll,i));

+      }

+    }

+    break;

+

+  case 0x07:

+    opll->patch[1].SL = (data >> 4) & 15;

+    opll->patch[1].RR = (data) & 15;

+    for (i = 0; i < 9; i++)

+    {

+      if (opll->patch_number[i] == 0)

+      {

+        UPDATE_EG (CAR(opll,i));

+      }

+    }

+    break;

+

+  case 0x0e:

+    update_rhythm_mode (opll);

+    if (data & 32)

+    {

+      if (data & 0x10)

+        keyOn_BD (opll);

+      else

+        keyOff_BD (opll);

+      if (data & 0x8)

+        keyOn_SD (opll);

+      else

+        keyOff_SD (opll);

+      if (data & 0x4)

+        keyOn_TOM (opll);

+      else

+        keyOff_TOM (opll);

+      if (data & 0x2)

+        keyOn_CYM (opll);

+      else

+        keyOff_CYM (opll);

+      if (data & 0x1)

+        keyOn_HH (opll);

+      else

+        keyOff_HH (opll);

+    }

+    update_key_status (opll);

+

+    UPDATE_ALL (MOD(opll,6));

+    UPDATE_ALL (CAR(opll,6));

+    UPDATE_ALL (MOD(opll,7));

+    UPDATE_ALL (CAR(opll,7));

+    UPDATE_ALL (MOD(opll,8));

+    UPDATE_ALL (CAR(opll,8));

+

+    break;

+

+  case 0x0f:

+    break;

+

+  case 0x10:

+  case 0x11:

+  case 0x12:

+  case 0x13:

+  case 0x14:

+  case 0x15:

+  case 0x16:

+  case 0x17:

+  case 0x18:

+    ch = reg - 0x10;

+    setFnumber (opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8));

+    UPDATE_ALL (MOD(opll,ch));

+    UPDATE_ALL (CAR(opll,ch));

+    break;

+

+  case 0x20:

+  case 0x21:

+  case 0x22:

+  case 0x23:

+  case 0x24:

+  case 0x25:

+  case 0x26:

+  case 0x27:

+  case 0x28:

+    ch = reg - 0x20;

+    setFnumber (opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]);

+    setBlock (opll, ch, (data >> 1) & 7);

+    setSustine (opll, ch, (data >> 5) & 1);

+    if (data & 0x10)

+      keyOn (opll, ch);

+    else

+      keyOff (opll, ch);

+    UPDATE_ALL (MOD(opll,ch));

+    UPDATE_ALL (CAR(opll,ch));

+    update_key_status (opll);

+    update_rhythm_mode (opll);

+    break;

+

+  case 0x30:

+  case 0x31:

+  case 0x32:

+  case 0x33:

+  case 0x34:

+  case 0x35:

+  case 0x36:

+  case 0x37:

+  case 0x38:

+    i = (data >> 4) & 15;

+    v = data & 15;

+    if ((opll->reg[0x0e] & 32) && (reg >= 0x36))

+    {

+      switch (reg)

+      {

+      case 0x37:

+        setSlotVolume (MOD(opll,7), i << 2);

+        break;

+      case 0x38:

+        setSlotVolume (MOD(opll,8), i << 2);

+        break;

+      default:

+        break;

+      }

+    }

+    else

+    {

+      setPatch (opll, reg - 0x30, i);

+    }

+    setVolume (opll, reg - 0x30, v << 2);

+    UPDATE_ALL (MOD(opll,reg - 0x30));

+    UPDATE_ALL (CAR(opll,reg - 0x30));

+    break;

+

+  default:

+    break;

+

+  }

+}

+

+void

+OPLL_writeIO (OPLL * opll, e_uint32 adr, e_uint32 val)

+{

+  if (adr & 1)

+    OPLL_writeReg (opll, opll->adr, val);

+  else

+    opll->adr = val;

+}

+

+e_uint32

+OPLL_read(OPLL * opll, e_uint32 a)

+{

+  if( !(a&1) )

+    {

+	/* status port */

+	return opll->status;

+  }

+  return 0xff;

+}

+

+#ifndef EMU2413_COMPACTION

+/* STEREO MODE (OPT) */

+void

+OPLL_set_pan (OPLL * opll, e_uint32 ch, e_uint32 pan)

+{

+  opll->pan[ch & 15] = pan & 3;

+}

+

+static void

+calc_stereo (OPLL * opll, e_int32 out[2])

+{

+  e_int32 b[4] = { 0, 0, 0, 0 };        /* Ignore, Right, Left, Center */

+  e_int32 r[4] = { 0, 0, 0, 0 };        /* Ignore, Right, Left, Center */

+  e_int32 i;

+

+  update_ampm (opll);

+  update_noise (opll);

+

+  for(i=0;i<18;i++)

+  {

+    calc_phase(&opll->slot[i],opll->lfo_pm);

+    calc_envelope(&opll->slot[i],opll->lfo_am);

+  }

+

+  for (i = 0; i < 6; i++)

+    if (!(opll->mask & OPLL_MASK_CH (i)) && (CAR(opll,i)->eg_mode != FINISH))

+      b[opll->pan[i]] += calc_slot_car (CAR(opll,i), calc_slot_mod (MOD(opll,i)));

+

+

+  if (opll->patch_number[6] <= 15)

+  {

+    if (!(opll->mask & OPLL_MASK_CH (6)) && (CAR(opll,6)->eg_mode != FINISH))

+      b[opll->pan[6]] += calc_slot_car (CAR(opll,6), calc_slot_mod (MOD(opll,6)));

+  }

+  else

+  {

+    if (!(opll->mask & OPLL_MASK_BD) && (CAR(opll,6)->eg_mode != FINISH))

+      r[opll->pan[9]] += calc_slot_car (CAR(opll,6), calc_slot_mod (MOD(opll,6)));

+  }

+

+  if (opll->patch_number[7] <= 15)

+  {

+    if (!(opll->mask & OPLL_MASK_CH (7)) && (CAR (opll,7)->eg_mode != FINISH))

+      b[opll->pan[7]] += calc_slot_car (CAR (opll,7), calc_slot_mod (MOD (opll,7)));

+  }

+  else

+  {

+    if (!(opll->mask & OPLL_MASK_HH) && (MOD (opll,7)->eg_mode != FINISH))

+      r[opll->pan[10]] += calc_slot_hat (MOD (opll,7), CAR(opll,8)->pgout, opll->noise_seed&1);

+    if (!(opll->mask & OPLL_MASK_SD) && (CAR (opll,7)->eg_mode != FINISH))

+      r[opll->pan[11]] -= calc_slot_snare (CAR (opll,7), opll->noise_seed&1);

+  }

+

+  if (opll->patch_number[8] <= 15)

+  {

+    if (!(opll->mask & OPLL_MASK_CH (8)) && (CAR (opll,8)->eg_mode != FINISH))

+      b[opll->pan[8]] += calc_slot_car (CAR (opll,8), calc_slot_mod (MOD (opll,8)));

+  }

+  else

+  {

+    if (!(opll->mask & OPLL_MASK_TOM) && (MOD (opll,8)->eg_mode != FINISH))

+      r[opll->pan[12]] += calc_slot_tom (MOD (opll,8));

+    if (!(opll->mask & OPLL_MASK_CYM) && (CAR (opll,8)->eg_mode != FINISH))

+      r[opll->pan[13]] -= calc_slot_cym (CAR (opll,8), MOD(opll,7)->pgout);

+  }

+

+  out[1] = (b[1] + b[3] + ((r[1] + r[3]) << 1)) <<3;

+  out[0] = (b[2] + b[3] + ((r[2] + r[3]) << 1)) <<3;

+}

+

+void

+OPLL_calc_stereo (OPLL * opll, e_int32 out[2])

+{

+  if (!opll->quality)

+  {

+    calc_stereo (opll, out);

+    return;

+  }

+

+  while (opll->realstep > opll->oplltime)

+  {

+    opll->oplltime += opll->opllstep;

+    opll->sprev[0] = opll->snext[0];

+    opll->sprev[1] = opll->snext[1];

+    calc_stereo (opll, opll->snext);

+  }

+

+  opll->oplltime -= opll->realstep;

+  out[0] = (e_int16) (((double) opll->snext[0] * (opll->opllstep - opll->oplltime)

+                       + (double) opll->sprev[0] * opll->oplltime) / opll->opllstep);

+  out[1] = (e_int16) (((double) opll->snext[1] * (opll->opllstep - opll->oplltime)

+                       + (double) opll->sprev[1] * opll->oplltime) / opll->opllstep);

+}

+#endif /* EMU2413_COMPACTION */

diff --git a/apps/codecs/libgme/emu2413.h b/apps/codecs/libgme/emu2413.h
new file mode 100644
index 0000000..9ee4513
--- /dev/null
+++ b/apps/codecs/libgme/emu2413.h
@@ -0,0 +1,164 @@
+#ifndef _EMU2413_H_

+#define _EMU2413_H_

+

+#include "blargg_common.h"

+#include "emutypes.h"

+

+#ifdef EMU2413_DLL_EXPORTS

+  #define EMU2413_API __declspec(dllexport)

+#elif defined(EMU2413_DLL_IMPORTS)

+  #define EMU2413_API __declspec(dllimport)

+#else

+  #define EMU2413_API

+#endif

+

+#ifdef __cplusplus

+extern "C" {

+#endif

+

+#define AUDIO_MONO_BUFFER_SIZE    1024

+

+#define PI 3.14159265358979323846

+

+enum OPLL_TONE_ENUM {OPLL_2413_TONE=0, OPLL_VRC7_TONE=1, OPLL_281B_TONE=2} ;

+

+/* voice data */

+typedef struct __OPLL_PATCH {

+  e_uint32 TL,FB,EG,ML,AR,DR,SL,RR,KR,KL,AM,PM,WF ;

+} OPLL_PATCH ;

+

+/* slot */

+typedef struct __OPLL_SLOT {

+

+  OPLL_PATCH *patch;  

+

+  e_int32 type ;          /* 0 : modulator 1 : carrier */

+

+  /* OUTPUT */

+  e_int32 feedback ;

+  e_int32 output[2] ;   /* Output value of slot */

+

+  /* for Phase Generator (PG) */

+  e_uint16 *sintbl ;    /* Wavetable */

+  e_uint32 phase ;      /* Phase */

+  e_uint32 dphase ;     /* Phase increment amount */

+  e_uint32 pgout ;      /* output */

+

+  /* for Envelope Generator (EG) */

+  e_int32 fnum ;          /* F-Number */

+  e_int32 block ;         /* Block */

+  e_int32 volume ;        /* Current volume */

+  e_int32 sustine ;       /* Sustine 1 = ON, 0 = OFF */

+  e_uint32 tll ;	      /* Total Level + Key scale level*/

+  e_uint32 rks ;        /* Key scale offset (Rks) */

+  e_int32 eg_mode ;       /* Current state */

+  e_uint32 eg_phase ;   /* Phase */

+  e_uint32 eg_dphase ;  /* Phase increment amount */

+  e_uint32 egout ;      /* output */

+

+} OPLL_SLOT ;

+

+/* Mask */

+#define OPLL_MASK_CH(x) (1<<(x))

+#define OPLL_MASK_HH (1<<(9))

+#define OPLL_MASK_CYM (1<<(10))

+#define OPLL_MASK_TOM (1<<(11))

+#define OPLL_MASK_SD (1<<(12))

+#define OPLL_MASK_BD (1<<(13))

+#define OPLL_MASK_RHYTHM ( OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD )

+

+/* opll */

+typedef struct __OPLL {

+

+  e_uint32 adr ;

+  e_int32 out ;

+

+#ifndef EMU2413_COMPACTION

+  e_uint32 realstep ;

+  e_uint32 oplltime ;

+  e_uint32 opllstep ;

+  e_int32 prev, next ;

+  e_int32 sprev[2],snext[2];

+  e_uint32 pan[16];

+#endif

+

+  /* Register */

+  e_uint8 reg[0x40] ; 

+  e_int32 slot_on_flag[18] ;

+

+  /* Pitch Modulator */

+  e_uint32 pm_phase ;

+  e_int32 lfo_pm ;

+

+  /* Amp Modulator */

+  e_int32 am_phase ;

+  e_int32 lfo_am ;

+

+  e_uint32 quality;

+

+  /* Noise Generator */

+  e_uint32 noise_seed ;

+

+  /* Channel Data */

+  e_int32 patch_number[9];

+  e_int32 key_status[9] ;

+

+  /* Slot */

+  OPLL_SLOT slot[18] ;

+

+  /* Voice Data */

+  OPLL_PATCH patch[19*2] ;

+  e_int32 patch_update[2] ; /* flag for check patch update */

+

+  e_uint32 mask ;

+  e_uint32 current_mask;

+  e_uint32 status;

+  

+  e_uint32 internal_mute;

+  e_int16 buffer[AUDIO_MONO_BUFFER_SIZE];

+} OPLL ;

+

+/* Create Object */

+EMU2413_API void OPLL_new(OPLL *, e_uint32 clk, e_uint32 rate) ;

+EMU2413_API void OPLL_delete(OPLL *) ;

+

+/* Setup */

+EMU2413_API void OPLL_reset(OPLL *) ;

+EMU2413_API void OPLL_reset_patch(OPLL *, e_int32) ;

+EMU2413_API void OPLL_set_rate(OPLL *opll, e_uint32 r) ;

+EMU2413_API void OPLL_set_quality(OPLL *opll, e_uint32 q) ;

+EMU2413_API void OPLL_set_pan(OPLL *, e_uint32 ch, e_uint32 pan);

+EMU2413_API void OPLL_set_internal_mute(OPLL *, e_uint32 mute);

+EMU2413_API e_uint32 OPLL_is_internal_muted(OPLL *);

+

+/* Port/Register access */

+EMU2413_API void OPLL_writeIO(OPLL *, e_uint32 reg, e_uint32 val); ICODE_ATTR

+EMU2413_API void OPLL_writeReg(OPLL *, e_uint32 reg, e_uint32 val); ICODE_ATTR

+EMU2413_API e_uint32 OPLL_read(OPLL *, e_uint32 port);

+

+/* Synthsize */

+EMU2413_API e_int16 OPLL_calc(OPLL *) ; ICODE_ATTR

+EMU2413_API void OPLL_calc_stereo(OPLL *, e_int32 out[2]) ; ICODE_ATTR

+EMU2413_API e_int16 *OPLL_update_buffer(OPLL *, e_uint32 length) ; ICODE_ATTR

+

+/* Misc */

+EMU2413_API void OPLL_setPatch(OPLL *, const e_uint8 *dump) ;

+EMU2413_API void OPLL_copyPatch(OPLL *, e_int32, OPLL_PATCH *) ;

+EMU2413_API void OPLL_forceRefresh(OPLL *) ;

+/* Utility */

+EMU2413_API void OPLL_dump2patch(const e_uint8 *dump, OPLL_PATCH *patch) ;

+EMU2413_API void OPLL_patch2dump(const OPLL_PATCH *patch, e_uint8 *dump) ;

+EMU2413_API void OPLL_getDefaultPatch(e_int32 type, e_int32 num, OPLL_PATCH *) ;

+

+/* Channel Mask */

+EMU2413_API e_uint32 OPLL_setMask(OPLL *, e_uint32 mask) ;

+EMU2413_API e_uint32 OPLL_toggleMask(OPLL *, e_uint32 mask) ;

+

+#define dump2patch OPLL_dump2patch

+

+#ifdef __cplusplus

+}

+#endif

+

+#endif

+

diff --git a/apps/codecs/libgme/emu8950.c b/apps/codecs/libgme/emu8950.c
new file mode 100644
index 0000000..2198239
--- /dev/null
+++ b/apps/codecs/libgme/emu8950.c
@@ -0,0 +1,1198 @@
+/*
+  * This file is based on:
+  *    Y8950.cc -- Y8950 emulator from the openMSX team
+  *    ported to c by gama
+  *
+  * The openMSX version is based on:
+  *    emu8950.c -- Y8950 emulator written by Mitsutaka Okazaki 2001
+  *    heavily rewritten to fit openMSX structure
+  */
+
+#include <math.h>
+#include "emu8950.h"
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4355 )
+#endif
+
+#if !defined(ROCKBOX)
+	#define EMU8950_CALCUL_TABLES
+#else
+	#include "opltables.h"
+#endif
+
+// dB to Liner table
+static short dB2LinTab[(2*DB_MUTE)*2];
+// Dynamic range
+static unsigned int dphaseNoiseTable[1024][8];
+// LFO Table
+int pmtable[2][PM_PG_WIDTH];
+int amtable[2][AM_PG_WIDTH];
+
+/** WaveTable for each envelope amp. */
+static int sintable[PG_WIDTH];
+   /** Phase incr table for Attack. */
+static unsigned int dphaseARTable[16][16];
+/** Phase incr table for Decay and Release. */
+static unsigned int dphaseDRTable[16][16];
+/** KSL + TL Table. */
+static int tllTable[16][8][1<<TL_BITS][4];
+static int rksTable[2][8][2];
+/** Since we wont change clock rate in rockbox we can
+    skip this table */
+#if !defined(ROCKBOX)
+/** Phase incr table for PG. */
+static unsigned int dphaseTable[1024][8][16];
+#endif
+
+/** Liner to Log curve conversion table (for Attack rate). */
+static int AR_ADJUST_TABLE[1<<EG_BITS];
+
+//**************************************************//
+//                                                  //
+//  Helper functions                                //
+//                                                  //
+//**************************************************//
+
+#define ALIGN(d, SS, SD) (d*(int)(SS/SD))
+
+inline static int DB_POS(int x)
+{
+	return (int)(x/DB_STEP);
+}
+
+inline static int DB_NEG(int x)
+{
+	return (int)(2*DB_MUTE+x/DB_STEP);
+}
+
+// Cut the lower b bits off
+inline static int HIGHBITS(int c, int b)
+{
+	return c >> b;
+}
+// Leave the lower b bits
+inline static int LOWBITS(int c, int b)
+{
+	return c & ((1<<b)-1);
+}
+// Expand x which is s bits to d bits
+inline static int EXPAND_BITS(int x, int s, int d)
+{
+	return x << (d-s);
+}
+
+//**************************************************//
+//                                                  //
+//                  Create tables                   //
+//                                                  //
+//**************************************************//
+
+// Table for AR to LogCurve. 
+static void makeAdjustTable(void)
+{
+	AR_ADJUST_TABLE[0] = 1 << EG_BITS;
+	for (int i = 1; i < (1 << EG_BITS); i++)
+	#ifdef EMU8950_CALCUL_TABLES
+		AR_ADJUST_TABLE[i] = (int)((double)(1 << EG_BITS) - 1 -
+		         (1 << EG_BITS) * log((double)i) / log((double)(1 << EG_BITS))) >> 1;        
+	#else
+		AR_ADJUST_TABLE[i] = ar_adjust_coeff[i-1];
+	#endif
+}
+
+// Table for dB(0 -- (1<<DB_BITS)) to Liner(0 -- DB2LIN_AMP_WIDTH) 
+static void makeDB2LinTable(void)
+{
+	int i;
+	for (i=0; i < 2*DB_MUTE; i++) {
+		dB2LinTab[i] = (i<DB_MUTE) ?
+		#ifdef EMU8950_CALCUL_TABLES
+			(int)((double)((1<<DB2LIN_AMP_BITS)-1)*pow((double)10,-(double)i*DB_STEP/20)) :
+		#else
+			db2lin_coeff[i] :
+		#endif
+			0;
+		dB2LinTab[i + 2*DB_MUTE] = -dB2LinTab[i];
+	}
+}
+
+// Sin Table 
+static void makeSinTable(void)
+{
+	int i;
+	for (i=0; i < PG_WIDTH/4; i++)
+	#ifdef EMU8950_CALCUL_TABLES
+		sintable[i] = lin2db(sin(2.0*MPI*i/PG_WIDTH));
+	#else
+		sintable[i] = sin_coeff[i];
+	#endif
+	for (int i=0; i < PG_WIDTH/4; i++)
+		sintable[PG_WIDTH/2 - 1 - i] = sintable[i];
+	for (int i=0; i < PG_WIDTH/2; i++)
+		sintable[PG_WIDTH/2 + i] = 2*DB_MUTE + sintable[i];
+}
+
+void makeDphaseNoiseTable(int sampleRate, int clockRate)
+{
+	for (int i=0; i<1024; i++)
+		for (int j=0; j<8; j++)
+			dphaseNoiseTable[i][j] = rate_adjust(i<<j, sampleRate, clockRate);
+}
+
+// Table for Pitch Modulator 
+void makePmTable(void)
+{
+	int i;
+	for (i=0; i < PM_PG_WIDTH; i++)
+	#ifdef EMU8950_CALCUL_TABLES 
+		pmtable[0][i] = (int)((double)PM_AMP * pow(2.,(double)PM_DEPTH*sin(2.0*MPI*i/PM_PG_WIDTH)/1200));
+	#else
+		pmtable[0][i] = pm0_coeff[i];
+	#endif
+	for (i=0; i < PM_PG_WIDTH; i++)
+	#ifdef EMU8950_CALCUL_TABLES
+		pmtable[1][i] = (int)((double)PM_AMP * pow(2.,(double)PM_DEPTH2*sin(2.0*MPI*i/PM_PG_WIDTH)/1200));
+	#else
+		pmtable[1][i] = pm1_coeff[i];
+	#endif
+}
+
+// Table for Amp Modulator 
+void makeAmTable(void)
+{
+	int i;
+	for (i=0; i<AM_PG_WIDTH; i++)
+	#ifdef EMU8950_CALCUL_TABLES 
+		amtable[0][i] = (int)((double)AM_DEPTH/2/DB_STEP * (1.0 + sin(2.0*MPI*i/PM_PG_WIDTH)));
+	#else
+		amtable[0][i] = am0_coeff[i];
+	#endif
+	for (i=0; i<AM_PG_WIDTH; i++)
+	#ifdef EMU8950_CALCUL_TABLES 
+		amtable[1][i] = (int)((double)AM_DEPTH2/2/DB_STEP * (1.0 + sin(2.0*MPI*i/PM_PG_WIDTH)));
+	#else
+		amtable[1][i] = am1_coeff[i];
+	#endif
+}
+
+#if !defined(ROCKBOX)
+// Phase increment counter table  
+static void makeDphaseTable(int sampleRate, int clockRate)
+{
+	int mltable[16] = {
+		1,1*2,2*2,3*2,4*2,5*2,6*2,7*2,8*2,9*2,10*2,10*2,12*2,12*2,15*2,15*2
+	};
+
+	int fnum, block, ML;
+	for (fnum=0; fnum<1024; fnum++)
+		for (block=0; block<8; block++)
+			for (ML=0; ML<16; ML++)
+				dphaseTable[fnum][block][ML] = 
+					rate_adjust((((fnum * mltable[ML]) << block) >> (21 - DP_BITS)), sampleRate, clockRate);
+}
+#endif
+
+static void makeTllTable(void)
+{
+	#define dB2(x) (int)((x)*2)
+	static int kltable[16] = {
+		dB2( 0.000),dB2( 9.000),dB2(12.000),dB2(13.875),
+		dB2(15.000),dB2(16.125),dB2(16.875),dB2(17.625),
+		dB2(18.000),dB2(18.750),dB2(19.125),dB2(19.500),
+		dB2(19.875),dB2(20.250),dB2(20.625),dB2(21.000)
+	};
+
+	int fnum, block, TL, KL;
+	for (fnum=0; fnum<16; fnum++)
+		for (block=0; block<8; block++)
+			for (TL=0; TL<64; TL++)
+				for (KL=0; KL<4; KL++) {
+					if (KL==0) {
+						tllTable[fnum][block][TL][KL] = ALIGN(TL, TL_STEP, EG_STEP);
+					} else {
+						int tmp = kltable[fnum] - dB2(3.000) * (7 - block);
+						if (tmp <= 0)
+							tllTable[fnum][block][TL][KL] = ALIGN(TL, TL_STEP, EG_STEP);
+						else 
+							tllTable[fnum][block][TL][KL] = (int)((tmp>>(3-KL))/EG_STEP) + ALIGN(TL, TL_STEP, EG_STEP);
+					}
+				}
+}
+
+
+// Rate Table for Attack 
+static void makeDphaseARTable(int sampleRate, int clockRate)
+{
+	int AR, Rks;
+	for (AR=0; AR<16; AR++)
+		for (Rks=0; Rks<16; Rks++) {
+			int RM = AR + (Rks>>2);
+			int RL = Rks&3;
+			if (RM>15) RM=15;
+			switch (AR) { 
+			case 0:
+				dphaseARTable[AR][Rks] = 0;
+				break;
+			case 15:
+				dphaseARTable[AR][Rks] = EG_DP_WIDTH;
+				break;
+			default:
+				dphaseARTable[AR][Rks] = rate_adjust((3*(RL+4) << (RM+1)), sampleRate, clockRate);
+				break;
+			}
+		}
+}
+
+// Rate Table for Decay 
+static void makeDphaseDRTable(int sampleRate, int clockRate)
+{
+	int DR, Rks;
+	for (DR=0; DR<16; DR++)
+		for (Rks=0; Rks<16; Rks++) {
+			int RM = DR + (Rks>>2);
+			int RL = Rks&3;
+			if (RM>15) RM=15;
+			switch (DR) { 
+			case 0:
+				dphaseDRTable[DR][Rks] = 0;
+				break;
+			default:
+				dphaseDRTable[DR][Rks] = rate_adjust((RL+4) << (RM-1), sampleRate, clockRate);
+				break;
+			}
+		}
+}
+
+static void makeRksTable(void)
+{
+	int fnum9, block, KR;
+	for (fnum9=0; fnum9<2; fnum9++)
+		for (block=0; block<8; block++)
+			for (KR=0; KR<2; KR++) {
+				rksTable[fnum9][block][KR] = (KR != 0) ?
+					(block<<1) + fnum9:
+					 block>>1;
+			}
+}
+
+//**********************************************************//
+//                                                          //
+//  Patch                                                   //
+//                                                          //
+//**********************************************************//
+
+
+void patchReset(struct Patch* patch)
+{
+	patch->AM = patch->PM = patch->EG = false;
+	patch->KR = patch->ML = patch->KL = patch->TL =
+	patch->FB = patch->AR = patch->DR = patch->SL = patch->RR = 0;
+}
+
+
+//**********************************************************//
+//                                                          //
+//  Slot                                                    //
+//                                                          //
+//**********************************************************//
+
+
+static inline void slotUpdatePG(struct Slot* slot)
+{
+#if defined(ROCKBOX)
+	static const int mltable[16] = {
+		1,1*2,2*2,3*2,4*2,5*2,6*2,7*2,8*2,9*2,10*2,10*2,12*2,12*2,15*2,15*2
+	};
+
+	slot->dphase = ((slot->fnum * mltable[slot->patch.ML]) << slot->block) >> (21 - DP_BITS);
+#else
+	slot->dphase = dphaseTable[slot->fnum][slot->block][slot->patch.ML];
+#endif
+}
+
+static inline void slotUpdateTLL(struct Slot* slot)
+{
+	slot->tll = tllTable[slot->fnum>>6][slot->block][slot->patch.TL][slot->patch.KL];
+}
+
+static inline void slotUpdateRKS(struct Slot* slot)
+{
+	slot->rks = rksTable[slot->fnum>>9][slot->block][slot->patch.KR];
+}
+
+static inline void slotUpdateEG(struct Slot* slot)
+{
+	switch (slot->eg_mode) {
+		case ATTACK:
+			slot->eg_dphase = dphaseARTable[slot->patch.AR][slot->rks];
+			break;
+		case DECAY:
+			slot->eg_dphase = dphaseDRTable[slot->patch.DR][slot->rks];
+			break;
+		case SUSTINE:
+			slot->eg_dphase = dphaseDRTable[slot->patch.RR][slot->rks];
+			break;
+		case RELEASE:
+			slot->eg_dphase = slot->patch.EG ?
+			            dphaseDRTable[slot->patch.RR][slot->rks]:
+			            dphaseDRTable[7]       [slot->rks];
+			break;
+		case SUSHOLD:
+		case FINISH:
+			slot->eg_dphase = 0;
+			break;
+	}
+}
+
+static inline void slotUpdateAll(struct Slot* slot)
+{
+	slotUpdatePG(slot);
+	slotUpdateTLL(slot);
+	slotUpdateRKS(slot);
+	slotUpdateEG(slot); // EG should be last 
+}
+
+void slotReset(struct Slot* slot)
+{
+	slot->phase = 0;
+	slot->dphase = 0;
+	slot->output[0] = 0;
+	slot->output[1] = 0;
+	slot->feedback = 0;
+	slot->eg_mode = FINISH;
+	slot->eg_phase = EG_DP_WIDTH;
+	slot->eg_dphase = 0;
+	slot->rks = 0;
+	slot->tll = 0;
+	slot->fnum = 0;
+	slot->block = 0;
+	slot->pgout = 0;
+	slot->egout = 0;
+	slot->slotStatus = false;
+	patchReset(&slot->patch);
+	slotUpdateAll(slot);
+}
+
+// Slot key on  
+static inline void slotOn(struct Slot* slot)
+{
+	if (!slot->slotStatus) {
+		slot->slotStatus = true;
+		slot->eg_mode = ATTACK;
+		slot->phase = 0;
+		slot->eg_phase = 0;
+	}
+}
+
+// Slot key off 
+static inline void slotOff(struct Slot* slot)
+{
+	if (slot->slotStatus) {
+		slot->slotStatus = false;
+		if (slot->eg_mode == ATTACK)
+			slot->eg_phase = EXPAND_BITS(AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS-EG_BITS)], EG_BITS, EG_DP_BITS);
+		slot->eg_mode = RELEASE;
+	}
+}
+
+
+//**********************************************************//
+//                                                          //
+//  OPLChannel                                                 //
+//                                                          //
+//**********************************************************//
+
+
+void channelReset(struct OPLChannel* ch)
+{
+	slotReset(&ch->mod);
+	slotReset(&ch->car);
+	ch->alg = false;
+}
+
+// Set F-Number ( fnum : 10bit ) 
+static void channelSetFnumber(struct OPLChannel* ch, int fnum)
+{
+	ch->car.fnum = fnum;
+	ch->mod.fnum = fnum;
+}
+
+// Set Block data (block : 3bit ) 
+static void channelSetBlock(struct OPLChannel* ch, int block)
+{
+	ch->car.block = block;
+	ch->mod.block = block;
+}
+
+// OPLChannel key on 
+static void keyOn(struct OPLChannel* ch)
+{
+	slotOn(&ch->mod);
+	slotOn(&ch->car);
+}
+
+// OPLChannel key off 
+static void keyOff(struct OPLChannel* ch)
+{
+	slotOff(&ch->mod);
+	slotOff(&ch->car);
+}
+
+
+//**********************************************************//
+//                                                          //
+//  Y8950                                                   //
+//                                                          //
+//**********************************************************//
+
+void OPL_init(struct Y8950* this, byte* ramBank, int sampleRam)
+{
+	this->clockRate = CLK_FREQ;
+	
+	ADPCM_init(&this->adpcm, this, ramBank, sampleRam);
+	
+	makePmTable();
+	makeAmTable();
+	
+	makeAdjustTable();
+	makeDB2LinTable();
+	makeTllTable();
+	makeRksTable();
+	makeSinTable();
+
+	int i;
+	for (i=0; i<9; i++) {
+		// TODO cleanup
+		this->slot[i*2+0] = &(this->ch[i].mod);
+		this->slot[i*2+1] = &(this->ch[i].car);
+		this->ch[i].mod.plfo_am = &this->lfo_am;
+		this->ch[i].mod.plfo_pm = &this->lfo_pm;
+		this->ch[i].car.plfo_am = &this->lfo_am;
+		this->ch[i].car.plfo_pm = &this->lfo_pm;
+	}
+
+	OPL_reset(this);
+}
+
+void OPL_setSampleRate(struct Y8950* this, int sampleRate, int clockRate)
+{
+	this->clockRate = clockRate;
+	ADPCM_setSampleRate(&this->adpcm, sampleRate, clockRate);
+	
+#if !defined(ROCKBOX)
+	makeDphaseTable(sampleRate, clockRate);
+#endif
+	makeDphaseARTable(sampleRate, clockRate);
+	makeDphaseDRTable(sampleRate, clockRate);
+	makeDphaseNoiseTable(sampleRate, clockRate);
+	this->pm_dphase = rate_adjust(PM_SPEED * PM_DP_WIDTH / (clockRate/72), sampleRate, clockRate);
+	this->am_dphase = rate_adjust(AM_SPEED * AM_DP_WIDTH / (clockRate/72), sampleRate, clockRate);
+}
+
+// Reset whole of opl except patch datas.
+void OPL_reset(struct Y8950* this)
+{
+	int i;
+	for (i=0; i<9; i++)
+		channelReset(&this->ch[i]);
+	this->output[0] = 0;
+	this->output[1] = 0;
+
+
+    this->dacSampleVolume = 0;
+    this->dacOldSampleVolume = 0;
+    this->dacSampleVolumeSum = 0;
+    this->dacCtrlVolume = 0;
+    this->dacDaVolume = 0;
+    this->dacEnabled = 0;
+
+	this->rythm_mode = false;
+	this->am_mode = 0;
+	this->pm_mode = 0;
+	this->pm_phase = 0;
+	this->am_phase = 0;
+	this->noise_seed = 0xffff;
+	this->noiseA = 0;
+	this->noiseB = 0;
+	this->noiseA_phase = 0;
+	this->noiseB_phase = 0;
+	this->noiseA_dphase = 0;
+	this->noiseB_dphase = 0;
+
+	for (i = 0; i < 0x100; ++i) 
+		this->reg[i] = 0x00;
+
+	this->reg[0x04] = 0x18;
+	this->reg[0x19] = 0x0F; // fixes 'Thunderbirds are Go'
+	this->status = 0x00;
+	this->statusMask = 0;
+	/* irq.reset(); */
+	
+	ADPCM_reset(&this->adpcm);
+	OPL_setInternalMute(this, true);	// muted
+}
+
+
+// Drum key on
+static inline void keyOn_BD(struct Y8950* this)  { keyOn(&this->ch[6]); }
+static inline void keyOn_HH(struct Y8950* this)  { slotOn(&this->ch[7].mod); }
+static inline void keyOn_SD(struct Y8950* this)  { slotOn(&this->ch[7].car); }
+static inline void keyOn_TOM(struct Y8950* this) { slotOn(&this->ch[8].mod); }
+static inline void keyOn_CYM(struct Y8950* this) { slotOn(&this->ch[8].car); }
+
+// Drum key off
+static inline void keyOff_BD(struct Y8950* this) { keyOff(&this->ch[6]); }
+static inline void keyOff_HH(struct Y8950* this) { slotOff(&this->ch[7].mod); }
+static inline void keyOff_SD(struct Y8950* this) { slotOff(&this->ch[7].car); }
+static inline void keyOff_TOM(struct Y8950* this){ slotOff(&this->ch[8].mod); }
+static inline void keyOff_CYM(struct Y8950* this){ slotOff(&this->ch[8].car); }
+
+// Change Rhythm Mode
+inline void setRythmMode(struct Y8950* this, int data)
+{
+	bool newMode = (data & 32) != 0;
+	if (this->rythm_mode != newMode) {
+		this->rythm_mode = newMode;
+		if (!this->rythm_mode) {
+			// ON->OFF
+			this->ch[6].mod.eg_mode = FINISH; // BD1
+			this->ch[6].mod.slotStatus = false;
+			this->ch[6].car.eg_mode = FINISH; // BD2
+			this->ch[6].car.slotStatus = false;
+			this->ch[7].mod.eg_mode = FINISH; // HH
+			this->ch[7].mod.slotStatus = false;
+			this->ch[7].car.eg_mode = FINISH; // SD
+			this->ch[7].car.slotStatus = false;
+			this->ch[8].mod.eg_mode = FINISH; // TOM
+			this->ch[8].mod.slotStatus = false;
+			this->ch[8].car.eg_mode = FINISH; // CYM
+			this->ch[8].car.slotStatus = false;
+		}
+	}
+}
+
+//********************************************************//
+//                                                        //
+// Generate wave data                                     //
+//                                                        //
+//********************************************************//
+
+// Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). 
+inline static int wave2_4pi(int e)
+{
+	int shift =  SLOT_AMP_BITS - PG_BITS - 1;
+	if (shift > 0)
+		return e >> shift;
+	else
+		return e << -shift;
+}
+
+// Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). 
+inline static int wave2_8pi(int e)
+{
+	int shift = SLOT_AMP_BITS - PG_BITS - 2;
+	if (shift > 0)
+		return e >> shift;
+	else
+		return e << -shift;
+}
+
+static inline void update_noise(struct Y8950* this)
+{
+	if (this->noise_seed & 1)
+		this->noise_seed ^= 0x24000;
+	this->noise_seed >>= 1;
+	this->whitenoise = this->noise_seed&1 ? DB_POS(6) : DB_NEG(6);
+
+	this->noiseA_phase += this->noiseA_dphase;
+	this->noiseB_phase += this->noiseB_dphase;
+
+	this->noiseA_phase &= (0x40<<11) - 1;
+	if ((this->noiseA_phase>>11)==0x3f)
+		this->noiseA_phase = 0;
+	this->noiseA = this->noiseA_phase&(0x03<<11)?DB_POS(6):DB_NEG(6);
+
+	this->noiseB_phase &= (0x10<<11) - 1;
+	this->noiseB = this->noiseB_phase&(0x0A<<11)?DB_POS(6):DB_NEG(6);
+}
+
+static inline void update_ampm(struct Y8950* this)
+{
+	this->pm_phase = (this->pm_phase + this->pm_dphase)&(PM_DP_WIDTH - 1);
+	this->am_phase = (this->am_phase + this->am_dphase)&(AM_DP_WIDTH - 1);
+	this->lfo_am = amtable[this->am_mode][HIGHBITS(this->am_phase, AM_DP_BITS - AM_PG_BITS)];
+	this->lfo_pm = pmtable[this->pm_mode][HIGHBITS(this->pm_phase, PM_DP_BITS - PM_PG_BITS)];
+}
+
+static inline void calc_phase(struct Slot* slot)
+{
+	if (slot->patch.PM)
+		slot->phase += (slot->dphase * (*slot->plfo_pm)) >> PM_AMP_BITS;
+	else
+		slot->phase += slot->dphase;
+	slot->phase &= (DP_WIDTH - 1);
+	slot->pgout = HIGHBITS(slot->phase, DP_BASE_BITS);
+}
+
+static inline void calc_envelope(struct Slot* slot)
+{
+	#define S2E(x) (ALIGN((unsigned int)(x/SL_STEP),SL_STEP,EG_STEP)<<(EG_DP_BITS-EG_BITS)) 
+	static unsigned int SL[16] = {
+		S2E( 0), S2E( 3), S2E( 6), S2E( 9), S2E(12), S2E(15), S2E(18), S2E(21),
+		S2E(24), S2E(27), S2E(30), S2E(33), S2E(36), S2E(39), S2E(42), S2E(93)
+	};
+	
+	switch (slot->eg_mode) {
+	case ATTACK:
+		slot->eg_phase += slot->eg_dphase;
+		if (EG_DP_WIDTH & slot->eg_phase) {
+			slot->egout = 0;
+			slot->eg_phase= 0;
+			slot->eg_mode = DECAY;
+			slotUpdateEG(slot);
+		} else {
+			slot->egout = AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS)];
+		}
+		break;
+
+	case DECAY:
+		slot->eg_phase += slot->eg_dphase;
+		slot->egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS);
+		if (slot->eg_phase >= SL[slot->patch.SL]) {
+			if (slot->patch.EG) {
+				slot->eg_phase = SL[slot->patch.SL];
+				slot->eg_mode = SUSHOLD;
+				slotUpdateEG(slot);
+			} else {
+				slot->eg_phase = SL[slot->patch.SL];
+				slot->eg_mode = SUSTINE;
+				slotUpdateEG(slot);
+			}
+			slot->egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS);
+		}
+		break;
+
+	case SUSHOLD:
+		slot->egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS);
+		if (!slot->patch.EG) {
+			slot->eg_mode = SUSTINE;
+			slotUpdateEG(slot);
+		}
+		break;
+
+	case SUSTINE:
+	case RELEASE:
+		slot->eg_phase += slot->eg_dphase;
+		slot->egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); 
+		if (slot->egout >= (1<<EG_BITS)) {
+			slot->eg_mode = FINISH;
+			slot->egout = (1<<EG_BITS) - 1;
+		}
+		break;
+
+	case FINISH:
+		slot->egout = (1<<EG_BITS) - 1;
+		break;
+	}
+
+	if (slot->patch.AM)
+		slot->egout = ALIGN(slot->egout+slot->tll,EG_STEP,DB_STEP) + (*slot->plfo_am);
+	else 
+		slot->egout = ALIGN(slot->egout+slot->tll,EG_STEP,DB_STEP);
+	if (slot->egout >= DB_MUTE)
+		slot->egout = DB_MUTE-1;
+}
+
+inline static int calc_slot_car(struct Slot* slot, int fm)
+{
+	calc_envelope(slot); 
+	calc_phase(slot);
+	if (slot->egout>=(DB_MUTE-1))
+		return 0;
+	return dB2LinTab[sintable[(slot->pgout+wave2_8pi(fm))&(PG_WIDTH-1)] + slot->egout];
+}
+
+inline static int calc_slot_mod(struct Slot* slot)
+{
+	slot->output[1] = slot->output[0];
+	calc_envelope(slot); 
+	calc_phase(slot);
+
+	if (slot->egout>=(DB_MUTE-1)) {
+		slot->output[0] = 0;
+	} else if (slot->patch.FB!=0) {
+		int fm = wave2_4pi(slot->feedback) >> (7-slot->patch.FB);
+		slot->output[0] = dB2LinTab[sintable[(slot->pgout+fm)&(PG_WIDTH-1)] + slot->egout];
+	} else
+		slot->output[0] = dB2LinTab[sintable[slot->pgout] + slot->egout];
+	
+	slot->feedback = (slot->output[1] + slot->output[0])>>1;
+	return slot->feedback;
+}
+
+// TOM
+inline static int calc_slot_tom(struct Slot* slot)
+{
+	calc_envelope(slot); 
+	calc_phase(slot);
+	if (slot->egout>=(DB_MUTE-1))
+		return 0;
+	return dB2LinTab[sintable[slot->pgout] + slot->egout];
+}
+
+// SNARE
+inline static int calc_slot_snare(struct Slot* slot, int whitenoise)
+{
+	calc_envelope(slot);
+	calc_phase(slot);
+	if (slot->egout>=(DB_MUTE-1))
+		return 0;
+	if (slot->pgout & (1<<(PG_BITS-1))) {
+		return (dB2LinTab[slot->egout] + dB2LinTab[slot->egout+whitenoise]) >> 1;
+	} else {
+		return (dB2LinTab[2*DB_MUTE + slot->egout] + dB2LinTab[slot->egout+whitenoise]) >> 1;
+	}
+}
+
+// TOP-CYM
+inline static int calc_slot_cym(struct Slot* slot, int a, int b)
+{
+	calc_envelope(slot);
+	if (slot->egout>=(DB_MUTE-1)) {
+		return 0;
+	} else {
+		return (dB2LinTab[slot->egout+a] + dB2LinTab[slot->egout+b]) >> 1;
+	}
+}
+
+// HI-HAT
+inline static int calc_slot_hat(struct Slot* slot, int a, int b, int whitenoise)
+{
+	calc_envelope(slot);
+	if (slot->egout>=(DB_MUTE-1)) {
+		return 0;
+	} else {
+		return (dB2LinTab[slot->egout+whitenoise] + dB2LinTab[slot->egout+a] + dB2LinTab[slot->egout+b]) >>2;
+	}
+}
+
+
+static inline int calcSample(struct Y8950* this, int channelMask)
+{
+	// while muted update_ampm() and update_noise() aren't called, probably ok
+	update_ampm(this);
+	update_noise(this);      
+
+	int mix = 0;
+
+	if (this->rythm_mode) {
+		// TODO wasn't in original source either
+		calc_phase(&this->ch[7].mod);
+		calc_phase(&this->ch[8].car);
+
+		if (channelMask & (1 << 6))
+			mix += calc_slot_car(&this->ch[6].car, calc_slot_mod(&this->ch[6].mod));
+		if (this->ch[7].mod.eg_mode != FINISH)
+			mix += calc_slot_hat(&this->ch[7].mod, this->noiseA, this->noiseB, this->whitenoise);
+		if (channelMask & (1 << 7))
+			mix += calc_slot_snare(&this->ch[7].car, this->whitenoise);
+		if (this->ch[8].mod.eg_mode != FINISH)
+			mix += calc_slot_tom(&this->ch[8].mod);
+		if (channelMask & (1 << 8))
+			mix += calc_slot_cym(&this->ch[8].car, this->noiseA, this->noiseB);	
+
+		channelMask &= (1<< 6) - 1;
+		mix *= 2;
+	}
+	struct OPLChannel *cp;
+	for (cp = this->ch; channelMask; channelMask >>=1, cp++) {
+		if (channelMask & 1) {
+			if (cp->alg)
+				mix += calc_slot_car(&cp->car, 0) +
+				       calc_slot_mod(&cp->mod);
+			else
+				mix += calc_slot_car(&cp->car, 
+				         calc_slot_mod(&cp->mod));
+		}
+	}
+	
+	mix += ADPCM_calcSample(&this->adpcm);
+
+	return (mix*this->maxVolume) >> (DB2LIN_AMP_BITS - 1);
+}
+
+bool checkMuteHelper(struct Y8950* this)
+{
+	int i;
+	struct OPLChannel *ch = this->ch;	
+	for (i = 0; i < 6; i++) {
+		if (ch[i].car.eg_mode != FINISH) return false;
+	}
+	if (!this->rythm_mode) {
+		for(i = 6; i < 9; i++) {
+			if (ch[i].car.eg_mode != FINISH) return false;
+		}
+	} else {
+		if (ch[6].car.eg_mode != FINISH) return false;
+		if (ch[7].mod.eg_mode != FINISH) return false;
+		if (ch[7].car.eg_mode != FINISH) return false;
+		if (ch[8].mod.eg_mode != FINISH) return false;
+		if (ch[8].car.eg_mode != FINISH) return false;
+	}
+	
+	return ADPCM_muted(&this->adpcm);
+}
+
+void checkMute(struct Y8950* this)
+{
+	bool mute = checkMuteHelper(this);
+	//PRT_DEBUG("Y8950: muted " << mute);
+	OPL_setInternalMute(this, mute);
+}
+
+int* OPL_updateBuffer(struct Y8950* this, int length)
+{
+	//PRT_DEBUG("Y8950: update buffer");
+
+	if (OPL_isInternalMuted(this) && !this->dacEnabled) {
+		return 0;
+	}
+
+    this->dacCtrlVolume = this->dacSampleVolume - this->dacOldSampleVolume + 0x3fe7 * this->dacCtrlVolume / 0x4000;
+    this->dacOldSampleVolume = this->dacSampleVolume;
+
+	int channelMask = 0, i;
+	struct OPLChannel *ch = this->ch;
+	for (i = 9; i--; ) {
+		channelMask <<= 1;
+		if (ch[i].car.eg_mode != FINISH) channelMask |= 1;
+	}
+
+	int* buf = this->buffer;
+	while (length--) {
+        int sample = calcSample(this, channelMask);
+
+        this->dacCtrlVolume = 0x3fe7 * this->dacCtrlVolume / 0x4000;
+        this->dacDaVolume += 2 * (this->dacCtrlVolume - this->dacDaVolume) / 3;
+        sample += 48 * this->dacDaVolume;
+		*(buf++) = sample;
+	}
+
+    this->dacEnabled = this->dacDaVolume;
+
+	checkMute(this);
+	return this->buffer;
+}
+
+void OPL_setInternalVolume(struct Y8950* this, short newVolume)
+{
+	this->maxVolume = newVolume;
+}
+
+//**************************************************//
+//                                                  //
+//                       I/O Ctrl                   //
+//                                                  //
+//**************************************************//
+
+void OPL_writeReg(struct Y8950* this, byte rg, byte data)
+{
+	//PRT_DEBUG("Y8950 write " << (int)rg << " " << (int)data);
+	int stbl[32] = {
+		 0, 2, 4, 1, 3, 5,-1,-1,
+		 6, 8,10, 7, 9,11,-1,-1,
+		12,14,16,13,15,17,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1
+	};
+
+	//TODO only for registers that influence sound
+	//TODO also ADPCM
+
+	switch (rg & 0xe0) {
+	case 0x00: {
+		switch (rg) {
+		case 0x01: // TEST
+			// TODO
+			// Y8950 MSX-AUDIO Test register $01 (write only)
+			//
+			// Bit	Description
+			//
+			// 7	Reset LFOs - seems to force the LFOs to their initial values (eg.
+			//	maximum amplitude, zero phase deviation)
+			//
+			// 6	something to do with ADPCM - bit 0 of the status register is
+			//	affected by setting this bit (PCM BSY)
+			//
+			// 5	No effect? - Waveform select enable in YM3812 OPL2 so seems
+			//	reasonable that this bit wouldn't have been used in OPL
+			//
+			// 4	No effect?
+			//
+			// 3	Faster LFOs - increases the frequencies of the LFOs and (maybe)
+			//	the timers (cf. YM2151 test register)
+			//
+			// 2	Reset phase generators - No phase generator output, but envelope
+			//	generators still work (can hear a transient when they are gated)
+			//
+			// 1	No effect?
+			//
+			// 0	Reset envelopes - Envelope generator outputs forced to maximum,
+			//	so all enabled voices sound at maximum
+			this->reg[rg] = data;
+			break;
+
+		case 0x02: // TIMER1 (reso. 80us)
+			this->reg[rg] = data;
+			break;
+
+		case 0x03: // TIMER2 (reso. 320us) 
+			this->reg[rg] = data;
+			break;
+
+		case 0x04: // FLAG CONTROL 
+			if (data & R04_IRQ_RESET) {
+				OPL_resetStatus(this, 0x78);	// reset all flags
+			} else {
+				OPL_changeStatusMask(this, (~data) & 0x78);
+				this->reg[rg] = data;
+			}
+			break;
+
+		case 0x06: // (KEYBOARD OUT) 
+			this->reg[rg] = data;
+			break;
+		
+		case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET
+		case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM 
+		case 0x09: // START ADDRESS (L) 
+		case 0x0A: // START ADDRESS (H) 
+		case 0x0B: // STOP ADDRESS (L) 
+		case 0x0C: // STOP ADDRESS (H) 
+		case 0x0D: // PRESCALE (L) 
+		case 0x0E: // PRESCALE (H) 
+		case 0x0F: // ADPCM-DATA 
+		case 0x10: // DELTA-N (L) 
+		case 0x11: // DELTA-N (H) 
+		case 0x12: // ENVELOP CONTROL
+		case 0x1A: // PCM-DATA
+			this->reg[rg] = data;
+			ADPCM_writeReg(&this->adpcm, rg, data);
+			break;
+		
+		case 0x15: // DAC-DATA  (bit9-2)
+			this->reg[rg] = data;
+			if (this->reg[0x08] & 0x04) {
+                static int damp[] = { 256, 279, 304, 332, 362, 395, 431, 470 };
+				int sample = (short)(256 * this->reg[0x15] + this->reg[0x16]) * 128 / damp[this->reg[0x17]];
+                this->dacSampleVolume = sample;
+                this->dacEnabled = 1;
+			}
+			break;
+		case 0x16: //           (bit1-0)
+			this->reg[rg] = data & 0xC0;
+			break;
+		case 0x17: //           (exponent)
+			this->reg[rg] = data & 0x07;
+			break;
+		
+		case 0x18: // I/O-CONTROL (bit3-0)
+			// TODO
+			// 0 -> input
+			// 1 -> output
+			this->reg[rg] = data;
+			break;
+		
+		case 0x19: // I/O-DATA (bit3-0)
+			// TODO
+			this->reg[rg] = data;
+			break;
+		}
+		
+		break;
+	}
+	case 0x20: {
+		int s = stbl[rg&0x1f];
+		if (s >= 0) {
+			this->slot[s]->patch.AM = (data>>7)&1;
+			this->slot[s]->patch.PM = (data>>6)&1;
+			this->slot[s]->patch.EG = (data>>5)&1;
+			this->slot[s]->patch.KR = (data>>4)&1;
+			this->slot[s]->patch.ML = (data)&15;
+			slotUpdateAll(this->slot[s]);
+		}
+		this->reg[rg] = data;
+		break;
+	}
+	case 0x40: {
+		int s = stbl[rg&0x1f];
+		if (s >= 0) {
+			this->slot[s]->patch.KL = (data>>6)&3;
+			this->slot[s]->patch.TL = (data)&63;
+			slotUpdateAll(this->slot[s]);
+		}
+		this->reg[rg] = data;
+		break;
+	} 
+	case 0x60: {
+		int s = stbl[rg&0x1f];
+		if (s >= 0) {
+			this->slot[s]->patch.AR = (data>>4)&15;
+			this->slot[s]->patch.DR = (data)&15;
+			slotUpdateEG(this->slot[s]);
+		}
+		this->reg[rg] = data;
+		break;
+	} 
+	case 0x80: {
+		int s = stbl[rg&0x1f];
+		if (s >= 0) {
+			this->slot[s]->patch.SL = (data>>4)&15;
+			this->slot[s]->patch.RR = (data)&15;
+			slotUpdateEG(this->slot[s]);
+		}
+		this->reg[rg] = data;
+		break;
+	} 
+	case 0xa0: {
+		if (rg==0xbd) {
+			this->am_mode = (data>>7)&1;
+			this->pm_mode = (data>>6)&1;
+			
+			setRythmMode(this, data);
+			if (this->rythm_mode) {
+				if (data&0x10) keyOn_BD(this);  else keyOff_BD(this);
+				if (data&0x08) keyOn_SD(this);  else keyOff_SD(this);
+				if (data&0x04) keyOn_TOM(this); else keyOff_TOM(this);
+				if (data&0x02) keyOn_CYM(this); else keyOff_CYM(this);
+				if (data&0x01) keyOn_HH(this);  else keyOff_HH(this);
+			}
+			slotUpdateAll(&this->ch[6].mod);
+			slotUpdateAll(&this->ch[6].car);
+			slotUpdateAll(&this->ch[7].mod);
+			slotUpdateAll(&this->ch[7].car);
+			slotUpdateAll(&this->ch[8].mod);
+			slotUpdateAll(&this->ch[8].car);
+
+			this->reg[rg] = data;
+			break;
+		}
+		if ((rg&0xf) > 8) {
+			// 0xa9-0xaf 0xb9-0xbf
+			break;
+		}
+		if (!(rg&0x10)) {
+			// 0xa0-0xa8
+			int c = rg-0xa0;
+			int fNum = data + ((this->reg[rg+0x10]&3)<<8);
+			int block = (this->reg[rg+0x10]>>2)&7;
+			channelSetFnumber(&this->ch[c], fNum);
+			switch (c) {
+				case 7: this->noiseA_dphase = dphaseNoiseTable[fNum][block];
+					break;
+				case 8: this->noiseB_dphase = dphaseNoiseTable[fNum][block];
+					break;
+			}
+			slotUpdateAll(&this->ch[c].car);
+			slotUpdateAll(&this->ch[c].mod);
+			this->reg[rg] = data;
+		} else {
+			// 0xb0-0xb8
+			int c = rg-0xb0;
+			int fNum = ((data&3)<<8) + this->reg[rg-0x10];
+			int block = (data>>2)&7;
+			channelSetFnumber(&this->ch[c], fNum);
+			channelSetBlock(&this->ch[c], block);
+			switch (c) {
+				case 7: this->noiseA_dphase = dphaseNoiseTable[fNum][block];
+					break;
+				case 8: this->noiseB_dphase = dphaseNoiseTable[fNum][block];
+					break;
+			}
+			if (data&0x20)
+				keyOn(&this->ch[c]);
+			else
+				keyOff(&this->ch[c]);
+			slotUpdateAll(&this->ch[c].mod);
+			slotUpdateAll(&this->ch[c].car);
+			this->reg[rg] = data;
+		}
+		break;
+	}
+	case 0xc0: {
+		if (rg > 0xc8)
+			break;
+		int c = rg-0xC0;
+		this->slot[c*2]->patch.FB = (data>>1)&7;
+		this->ch[c].alg = data&1;
+		this->reg[rg] = data;
+	}
+	}
+
+	//TODO only for registers that influence sound
+	checkMute(this);
+}
+
+byte OPL_readReg(struct Y8950* this, byte rg)
+{	
+	byte result;
+	switch (rg) {
+		case 0x05: // (KEYBOARD IN)
+		    result = 0xff;
+			break;
+		
+		case 0x0f: // ADPCM-DATA
+		case 0x13: //  ???
+		case 0x14: //  ???
+		case 0x1a: // PCM-DATA
+			result = ADPCM_readReg(&this->adpcm, rg);
+			break;
+		
+		case 0x19: // I/O DATA   TODO
+		    /* result =  ~(switchGetAudio() ?	0 :	0x04); */
+		    result =  0;
+            break;
+		default:
+			result = 255;
+	}
+	//PRT_DEBUG("Y8950 read " << (int)rg<<" "<<(int)result);
+	return result;
+}
+
+byte OPL_readStatus(struct Y8950* this)
+{
+	OPL_setStatus(this, STATUS_BUF_RDY);	// temp hack
+	byte tmp = this->status & (0x80 | this->statusMask);
+	//PRT_DEBUG("Y8950 read status " << (int)tmp);
+	return tmp | 0x06;	// bit 1 and 2 are always 1
+}
+
+
+void OPL_setStatus(struct Y8950* this, byte flags)
+{
+	this->status |= flags;
+	if (this->status & this->statusMask) {
+		this->status |= 0x80;
+		/* irq.set(); */
+	}
+}
+void OPL_resetStatus(struct Y8950* this, byte flags)
+{
+	this->status &= ~flags;
+	if (!(this->status & this->statusMask)) {
+		this->status &= 0x7f;
+		/* irq.reset(); */
+	}
+}
+void OPL_changeStatusMask(struct Y8950* this, byte newMask)
+{
+	this->statusMask = newMask;
+	this->status &= this->statusMask;
+	if (this->status) {
+		this->status |= 0x80;
+		/* irq.set(); */
+	} else {
+		this->status &= 0x7f;
+		/* irq.reset(); */
+	}
+}
diff --git a/apps/codecs/libgme/emu8950.h b/apps/codecs/libgme/emu8950.h
new file mode 100644
index 0000000..88d17b9
--- /dev/null
+++ b/apps/codecs/libgme/emu8950.h
@@ -0,0 +1,248 @@
+#ifndef __Y8950_HH__
+#define __Y8950_HH__
+
+#include "blargg_common.h"
+#include "emuadpcm.h"
+
+#define AUDIO_MONO_BUFFER_SIZE    1024
+
+// Dynamic range of envelope
+static const double EG_STEP = 0.1875;
+#define EG_BITS  9
+#define EG_MUTE  (1<<EG_BITS)
+// Dynamic range of sustine level
+static const double SL_STEP = 3.0;
+static const int SL_BITS = 4;
+#define SL_MUTE  (1<<SL_BITS)
+// Size of Sintable ( 1 -- 18 can be used, but 7 -- 14 recommended.)
+#define PG_BITS  10
+#define PG_WIDTH  (1<<PG_BITS)
+// Phase increment counter
+static const int DP_BITS = 19;
+#define DP_WIDTH  (1<<DP_BITS)
+#define DP_BASE_BITS  (DP_BITS - PG_BITS)
+// Bits for envelope phase incremental counter
+static const int EG_DP_BITS = 23;
+#define EG_DP_WIDTH  (1<<EG_DP_BITS)
+// Dynamic range of total level
+static const double TL_STEP = 0.75;
+#define TL_BITS  6
+#define TL_MUTE  (1<<TL_BITS)
+
+static const double DB_STEP = 0.1875;
+#define DB_BITS  9
+#define DB_MUTE  (1<<DB_BITS)
+// PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200)
+static const int PM_AMP_BITS = 8;
+#define PM_AMP  (1<<PM_AMP_BITS)
+
+
+
+static const int CLK_FREQ = 3579545;
+static const double MPI = 3.14159265358979;
+// PM speed(Hz) and depth(cent)
+static const double PM_SPEED = 6.4;
+static const double PM_DEPTH = (13.75/2);
+static const double PM_DEPTH2 = 13.75;
+// AM speed(Hz) and depth(dB)
+static const double AM_SPEED = 3.7;
+static const double AM_DEPTH = 1.0;
+static const double AM_DEPTH2 = 4.8;
+// Bits for liner value
+static const int DB2LIN_AMP_BITS = 11;
+#define SLOT_AMP_BITS  DB2LIN_AMP_BITS
+
+// Bits for Pitch and Amp modulator
+#define PM_PG_BITS  8
+#define PM_PG_WIDTH  (1<<PM_PG_BITS)
+static const int PM_DP_BITS = 16;
+#define PM_DP_WIDTH  (1<<PM_DP_BITS)
+#define AM_PG_BITS  8
+#define AM_PG_WIDTH  (1<<AM_PG_BITS)
+static const int AM_DP_BITS = 16;
+#define AM_DP_WIDTH  (1<<AM_DP_BITS)
+
+// Bitmask for register 0x04
+/** Timer1 Start. */
+static const int R04_ST1          = 0x01;
+/** Timer2 Start. */
+static const int R04_ST2          = 0x02;
+// not used
+//static const int R04            = 0x04;
+/** Mask 'Buffer Ready'. */
+static const int R04_MASK_BUF_RDY = 0x08;
+/** Mask 'End of sequence'. */
+static const int R04_MASK_EOS     = 0x10;
+/** Mask Timer2 flag. */
+static const int R04_MASK_T2      = 0x20;
+/** Mask Timer1 flag. */
+static const int R04_MASK_T1      = 0x40;
+/** IRQ RESET. */
+static const int R04_IRQ_RESET    = 0x80;
+
+// Bitmask for status register
+#define STATUS_EOS      (R04_MASK_EOS)
+#define STATUS_BUF_RDY  (R04_MASK_BUF_RDY)
+#define STATUS_T2       (R04_MASK_T2)
+#define STATUS_T1       (R04_MASK_T1)
+
+// Definition of envelope mode
+enum { ATTACK,DECAY,SUSHOLD,SUSTINE,RELEASE,FINISH };
+
+struct Patch {
+	bool AM, PM, EG;
+	byte KR; // 0-1
+	byte ML; // 0-15
+	byte KL; // 0-3
+	byte TL; // 0-63
+	byte FB; // 0-7
+	byte AR; // 0-15
+	byte DR; // 0-15
+	byte SL; // 0-15
+	byte RR; // 0-15
+};
+
+void patchReset(struct Patch* p);
+
+struct Slot {
+	// OUTPUT
+	int feedback;
+	/** Output value of slot. */
+	int output[5];
+
+	// for Phase Generator (PG)
+	/** Phase. */
+	unsigned int phase;
+	/** Phase increment amount. */
+	unsigned int dphase;
+	/** Output. */
+	int pgout;
+
+	// for Envelope Generator (EG)
+	/** F-Number. */
+	int fnum;
+	/** Block. */
+	int block;
+	/** Total Level + Key scale level. */
+	int tll;
+	/** Key scale offset (Rks). */
+	int rks;
+	/** Current state. */
+	int eg_mode;
+	/** Phase. */
+	unsigned int eg_phase;
+	/** Phase increment amount. */
+	unsigned int eg_dphase;
+	/** Output. */
+	int egout;
+
+	bool slotStatus;
+	struct Patch patch;
+
+	// refer to Y8950->
+	int *plfo_pm;
+	int *plfo_am;
+};
+
+void slotReset(struct Slot* slot);
+
+
+struct OPLChannel {
+	bool alg;
+	struct Slot mod, car;
+};
+
+void channelReset(struct OPLChannel* ch);
+
+
+struct Y8950
+{
+	int adr;
+	int output[2];
+	// Register
+	byte reg[0x100];
+	bool rythm_mode;
+	// Pitch Modulator
+	int pm_mode;
+	unsigned int pm_phase;
+	// Amp Modulator
+	int am_mode;
+	unsigned int am_phase;
+
+	// Noise Generator
+	int noise_seed;
+	int whitenoise;
+	int noiseA;
+	int noiseB;
+	unsigned int noiseA_phase;
+	unsigned int noiseB_phase;
+	unsigned int noiseA_dphase;
+	unsigned int noiseB_dphase;
+
+	// Channel & Slot
+	struct OPLChannel ch[9];
+	struct Slot *slot[18];
+
+	unsigned int pm_dphase;
+	int lfo_pm;
+	unsigned int am_dphase;
+	int lfo_am;
+
+	int maxVolume;
+	bool internalMuted;
+	
+	int clockRate;
+
+	/** STATUS Register. */
+	byte status;
+	/** bit=0 -> masked. */
+	byte statusMask;
+	/* MsxAudioIRQHelper irq; */
+
+	// ADPCM
+	struct Y8950Adpcm adpcm;
+
+	/** 13-bit (exponential) DAC. */
+	/* DACSound16S dac13; */
+    
+    // DAC stuff
+    int dacSampleVolume;
+    int dacOldSampleVolume;
+    int dacSampleVolumeSum;
+    int dacCtrlVolume;
+    int dacDaVolume;
+    int dacEnabled;
+    
+    // Internal buffer
+    int buffer[AUDIO_MONO_BUFFER_SIZE];
+};
+
+void OPL_init(struct Y8950* this_, byte* ramBank, int sampleRam);
+
+void OPL_reset(struct Y8950* this_);
+void OPL_writeReg(struct Y8950* this_, byte reg, byte data);
+byte OPL_readReg(struct Y8950* this_, byte reg);
+byte OPL_readStatus(struct Y8950* this_);
+static inline void OPL_setInternalMute(struct Y8950* this_, bool muted) { this_->internalMuted = muted; }
+static inline bool OPL_isInternalMuted(struct Y8950* this_) { return this_->internalMuted; }
+    
+void OPL_setSampleRate(struct Y8950* this_, int sampleRate, int clockRate);
+int* OPL_updateBuffer(struct Y8950* this_, int length);
+
+// SoundDevice
+void OPL_setInternalVolume(struct Y8950* this_, short maxVolume);
+
+void OPL_setStatus(struct Y8950* this_, byte flags);
+void OPL_resetStatus(struct Y8950* this_, byte flags);
+void OPL_changeStatusMask(struct Y8950* this_, byte newMask);
+
+
+// Adjust envelope speed which depends on sampling rate
+static inline unsigned int rate_adjust(double x, int rate, int clk)
+{
+	double tmp = x * clk / 72 / rate + 0.5; // +0.5 to round
+//	assert (tmp <= 4294967295U);
+	return (unsigned int)tmp;
+}
+
+#endif
diff --git a/apps/codecs/libgme/emuadpcm.c b/apps/codecs/libgme/emuadpcm.c
new file mode 100644
index 0000000..1f2d5ae
--- /dev/null
+++ b/apps/codecs/libgme/emuadpcm.c
@@ -0,0 +1,298 @@
+/*
+  * This file is based on:
+  *    Y8950Adpcm.cc -- Y8950 ADPCM emulator from the openMSX team
+  *    ported to c by gama
+  *
+  * The openMSX version is based on:
+  *    emuadpcm.c -- Y8950 ADPCM emulator written by Mitsutaka Okazaki 2001
+  *    heavily rewritten to fit openMSX structure
+  */
+
+#include <string.h>
+
+#include "emuadpcm.h"
+#include "emu8950.h"
+
+// Relative volume between ADPCM part and FM part, 
+// value experimentally found by Manuel Bilderbeek
+const int ADPCM_VOLUME = 356;
+
+// Bitmask for register 0x07
+static const int R07_RESET        = 0x01;
+//static const int R07            = 0x02;.      // not used
+//static const int R07            = 0x04;.      // not used
+const int R07_SP_OFF       = 0x08;
+const int R07_REPEAT       = 0x10;
+const int R07_MEMORY_DATA  = 0x20;
+const int R07_REC          = 0x40;
+const int R07_START        = 0x80;
+
+//Bitmask for register 0x08
+const int R08_ROM          = 0x01;
+const int R08_64K          = 0x02;
+const int R08_DA_AD        = 0x04;
+const int R08_SAMPL        = 0x08;
+//const int R08            = 0x10;.      // not used
+//const int R08            = 0x20;.      // not used
+const int R08_NOTE_SET     = 0x40;
+const int R08_CSM          = 0x80;
+
+const int DMAX = 0x6000;
+const int DMIN = 0x7F;
+const int DDEF = 0x7F;
+
+const int DECODE_MAX = 32767;
+const int DECODE_MIN = -32768;
+
+#define GETA_BITS  14
+#define MAX_STEP   (1<<(16+GETA_BITS))
+
+
+//**************************************************//
+//                                                  //
+//  Helper functions                                //
+//                                                  //
+//**************************************************//
+
+int CLAP(int min, int x, int max)
+{
+	return (x < min) ? min : ((max < x) ? max : x);
+}
+
+//**********************************************************//
+//                                                          //
+//  Y8950Adpcm                                              //
+//                                                          //
+//**********************************************************//
+
+
+void ADPCM_init(struct Y8950Adpcm* this_, struct Y8950* y8950_, byte* ramBank, int sampleRam)
+
+{
+	this_->y8950 = y8950_;
+	this_->ramBank = ramBank;
+	this_->ramSize = sampleRam;
+	memset(this_->ramBank, 0xFF, this_->ramSize);
+	this_->volume = 0;
+}
+
+void restart(struct Y8950Adpcm* this_);
+void ADPCM_reset(struct Y8950Adpcm* this_)
+{
+	this_->playing = false;
+	this_->startAddr = 0;
+	this_->stopAddr = 7;
+	this_->memPntr = 0;
+	this_->delta = 0;
+	this_->step = 0;
+	this_->addrMask = (1 << 19) - 1;
+	this_->reg7 = 0;
+	this_->reg15 = 0;
+	ADPCM_writeReg(this_, 0x12, 255);	// volume
+	restart(this_);
+}
+
+void ADPCM_setSampleRate(struct Y8950Adpcm* this_, int sr, int clk)
+{
+	this_->sampleRate = sr;
+	this_->clockRate = clk;
+}
+
+bool ADPCM_muted(struct Y8950Adpcm* this_)
+{
+	return (!this_->playing) || (this_->reg7 & R07_SP_OFF);
+}
+
+//**************************************************//
+//                                                  //
+//                       I/O Ctrl                   //
+//                                                  //
+//**************************************************//
+
+void restart(struct Y8950Adpcm* this_)
+{
+	this_->playAddr = this_->startAddr & this_->addrMask;
+	this_->nowStep = MAX_STEP - this_->step;
+	this_->out = this_->output = 0;
+	this_->diff = DDEF;
+	this_->nextLeveling = 0;
+	this_->sampleStep = 0;
+	this_->volumeWStep = (int)((double)this_->volume * this_->step / MAX_STEP);
+}
+
+void ADPCM_writeReg(struct Y8950Adpcm* this_, byte rg, byte data)
+{
+	switch (rg) {
+		case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET
+			this_->reg7 = data;
+			if (this_->reg7 & R07_RESET) {
+				this_->playing = false;
+			} else if (data & R07_START) {
+				this_->playing = true;
+				restart(this_);
+			}
+			break;
+
+		case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM 
+			this_->romBank = data & R08_ROM;
+			this_->addrMask = data & R08_64K ? (1<<17)-1 : (1<<19)-1;
+			break;
+
+		case 0x09: // START ADDRESS (L)
+			this_->startAddr = (this_->startAddr & 0x7F800) | (data << 3);
+			this_->memPntr = 0;
+			break;
+		case 0x0A: // START ADDRESS (H) 
+			this_->startAddr = (this_->startAddr & 0x007F8) | (data << 11);
+			this_->memPntr = 0;
+			break;
+
+		case 0x0B: // STOP ADDRESS (L)
+			this_->stopAddr = (this_->stopAddr & 0x7F807) | (data << 3);
+			break;
+		case 0x0C: // STOP ADDRESS (H) 
+			this_->stopAddr = (this_->stopAddr & 0x007FF) | (data << 11);
+			break;
+
+
+		case 0x0F: // ADPCM-DATA
+			// TODO check this
+			//if ((reg7 & R07_REC) && (reg7 & R07_MEMORY_DATA)) {
+			{
+				int tmp = ((this_->startAddr + this_->memPntr) & this_->addrMask) / 2;
+				tmp = (tmp < this_->ramSize) ? tmp : (tmp & (this_->ramSize - 1)); 
+				if (!this_->romBank) {
+					this_->ramBank[tmp] = data;
+				}
+				//PRT_DEBUG("Y8950Adpcm: mem " << tmp << " " << (int)data);
+				this_->memPntr += 2;
+				if ((this_->startAddr + this_->memPntr) > this_->stopAddr) {
+					OPL_setStatus(this_->y8950, STATUS_EOS);
+				}
+			}
+			OPL_setStatus(this_->y8950, STATUS_BUF_RDY);
+			break;
+
+		case 0x10: // DELTA-N (L) 
+			this_->delta = (this_->delta & 0xFF00) | data;
+			this_->step = rate_adjust(this_->delta<<GETA_BITS, this_->sampleRate, this_->clockRate);
+			this_->volumeWStep = (int)((double)this_->volume * this_->step / MAX_STEP);
+			break;
+		case 0x11: // DELTA-N (H) 
+			this_->delta = (this_->delta & 0x00FF) | (data << 8);
+			this_->step = rate_adjust(this_->delta<<GETA_BITS, this_->sampleRate, this_->clockRate);
+			this_->volumeWStep = (int)((double)this_->volume * this_->step / MAX_STEP);
+			break;
+
+		case 0x12: { // ENVELOP CONTROL 
+			int oldVol = this_->volume;
+			this_->volume = (data * ADPCM_VOLUME) >> 8;
+			if (oldVol != 0) {
+				double factor = (double)this_->volume / (double)oldVol;
+				this_->output =     (int)((double)this_->output     * factor);
+				this_->sampleStep = (int)((double)this_->sampleStep * factor);
+			}
+			this_->volumeWStep = (int)((double)this_->volume * this_->step / MAX_STEP);
+			break;
+		}
+		case 0x0D: // PRESCALE (L) 
+		case 0x0E: // PRESCALE (H) 
+		case 0x15: // DAC-DATA  (bit9-2)
+		case 0x16: //           (bit1-0)
+		case 0x17: //           (exponent)
+		case 0x1A: // PCM-DATA
+			// not implemented
+			break;
+	}
+}
+
+byte ADPCM_readReg(struct Y8950Adpcm* this_, byte rg)
+{
+	byte result;
+	switch (rg) {
+		case 0x0F: { // ADPCM-DATA
+			// TODO don't advance pointer when playing???
+			int adr = ((this_->startAddr + this_->memPntr) & this_->addrMask) / 2;
+			if (this_->romBank || (adr >= this_->ramSize)) {
+				result = 0xFF;
+			} else {
+				result = this_->ramBank[adr];
+			}
+			this_->memPntr += 2;
+			if ((this_->startAddr + this_->memPntr) > this_->stopAddr) {
+				OPL_setStatus(this_->y8950, STATUS_EOS);
+			}
+			break;
+		}
+		case 0x13: // TODO check
+			result = this_->out & 0xFF;
+			break;
+		case 0x14: // TODO check
+			result = this_->out / 256;
+			break;
+		default:
+			result = 255;
+	}
+	//PRT_DEBUG("Y8950Adpcm: read "<<(int)rg<<" "<<(int)result);
+	return result;
+}
+
+int ADPCM_calcSample(struct Y8950Adpcm* this_)
+{
+	// This table values are from ymdelta.c by Tatsuyuki Satoh.
+	static const int F1[16] = { 1,   3,   5,   7,   9,  11,  13,  15,
+				   -1,  -3,  -5,  -7,  -9, -11, -13, -15};
+	static const int F2[16] = {57,  57,  57,  57,  77, 102, 128, 153,
+				   57,  57,  57,  57,  77, 102, 128, 153};
+	
+	if (ADPCM_muted(this_)) {
+		return 0;
+	}
+	this_->nowStep += this_->step;
+	if (this_->nowStep >= MAX_STEP) {
+		int nowLeveling;
+		do {
+			this_->nowStep -= MAX_STEP;
+			unsigned long val;
+			if (!(this_->playAddr & 1)) {
+				// n-th nibble
+				int tmp = this_->playAddr / 2;
+				if (this_->romBank || (tmp >= this_->ramSize)) {
+					this_->reg15 = 0xFF;
+				} else {
+					this_->reg15 = this_->ramBank[tmp];
+				}
+				val = this_->reg15 >> 4;
+			} else {
+				// (n+1)-th nibble
+				val = this_->reg15 & 0x0F;
+			}
+			int prevOut = this_->out;
+			this_->out = CLAP(DECODE_MIN, this_->out + (this_->diff * F1[val]) / 8,
+			           DECODE_MAX);
+			this_->diff = CLAP(DMIN, (this_->diff * F2[val]) / 64, DMAX);
+			int deltaNext = this_->out - prevOut;
+			nowLeveling = this_->nextLeveling;
+			this_->nextLeveling = prevOut + deltaNext / 2;
+		
+			this_->playAddr++;
+			if (this_->playAddr > this_->stopAddr) {
+				if (this_->reg7 & R07_REPEAT) {
+					restart(this_);
+				} else {
+					this_->playing = false;
+					//y8950.setStatus(Y8950::STATUS_EOS);
+				}
+			}
+		} while (this_->nowStep >= MAX_STEP);
+		this_->sampleStep = (this_->nextLeveling - nowLeveling) * this_->volumeWStep;
+		this_->output = nowLeveling * this_->volume;
+		
+		/* TODO: Used fixed point math here */
+	#if !defined(ROCKBOX)
+		this_->output += (int)((double)this_->sampleStep * ((double)this_->nowStep/(double)this_->step));
+	#endif
+	}
+	this_->output += this_->sampleStep;
+	return this_->output >> 12;
+}
diff --git a/apps/codecs/libgme/emuadpcm.h b/apps/codecs/libgme/emuadpcm.h
new file mode 100644
index 0000000..0fc39a1
--- /dev/null
+++ b/apps/codecs/libgme/emuadpcm.h
@@ -0,0 +1,52 @@
+#ifndef __Y8950ADPCM_HH__
+#define __Y8950ADPCM_HH__
+
+#include "blargg_common.h"
+#include "blargg_source.h"
+#include "msxtypes.h"
+
+typedef unsigned short word;
+typedef unsigned __int64 uint64;
+struct Y8950;
+
+struct Y8950Adpcm
+{
+	struct Y8950* y8950;
+
+	int sampleRate;
+	int clockRate;
+	
+	int ramSize;
+	int startAddr;
+	int stopAddr;
+	int playAddr;
+	int addrMask;
+	int memPntr;
+	bool romBank;
+	byte* ramBank;
+
+	bool playing;
+	int volume;
+	word delta;
+	unsigned int nowStep, step;
+	int out, output;
+	int diff;
+	int nextLeveling;
+	int sampleStep;
+	int volumeWStep;
+
+	byte reg7;
+	byte reg15;
+};
+
+
+void ADPCM_init(struct Y8950Adpcm* this_, struct Y8950* y8950, byte* ramBank, int sampleRam);
+void ADPCM_reset(struct Y8950Adpcm* this_);
+void ADPCM_setSampleRate(struct Y8950Adpcm* this_, int sr, int clk);
+bool ADPCM_muted(struct Y8950Adpcm* this_);
+void ADPCM_writeReg(struct Y8950Adpcm* this_, byte rg, byte data);
+byte ADPCM_readReg(struct Y8950Adpcm* this_, byte rg);
+int ADPCM_calcSample(struct Y8950Adpcm* this_);
+
+
+#endif 
diff --git a/apps/codecs/libgme/emutables.h b/apps/codecs/libgme/emutables.h
new file mode 100644
index 0000000..53fb324
--- /dev/null
+++ b/apps/codecs/libgme/emutables.h
@@ -0,0 +1,170 @@
+#ifndef _EMUTABLES_H_

+#define _EMUTABLES_H_

+

+/* Precalculated emu2413 tables for use in Rockbox, 

+   Calculated for 44Khz sampling rate */

+

+#include "emutypes.h"

+

+static const e_uint16 sin_coeff[] ICONST_ATTR = {

+	255,   203,   171,   152,   139,   129,   120, 

+	113,   107,   102,    97,    92,    88,    85, 

+	 81,    78,    75,    72,    70,    67,    65, 

+	 63,    61,    59,    57,    55,    53,    52, 

+	 50,    48,    47,    45,    44,    43,    41, 

+	 40,    39,    38,    37,    35,    34,    33, 

+	 32,    31,    30,    29,    28,    28,    27, 

+	 26,    25,    24,    23,    23,    22,    21, 

+	 21,    20,    19,    19,    18,    17,    17, 

+	 16,    16,    15,    14,    14,    13,    13, 

+	 12,    12,    11,    11,    11,    10,    10, 

+	  9,     9,     8,     8,     8,     7,     7, 

+	  7,     6,     6,     6,     5,     5,     5, 

+	  4,     4,     4,     4,     3,     3,     3, 

+	  3,     2,     2,     2,     2,     2,     2, 

+	  1,     1,     1,     1,     1,     1,     1, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0, 

+};

+

+static const e_int32 pm_coeff[] ICONST_ATTR = {

+	256,   256,   256,   256,   256,   256,   256, 

+	256,   256,   256,   256,   256,   256,   256, 

+	256,   256,   256,   256,   256,   256,   256, 

+	256,   256,   256,   256,   256,   256,   256, 

+	256,   256,   256,   256,   257,   257,   257, 

+	257,   257,   257,   257,   257,   257,   257, 

+	257,   257,   257,   257,   257,   257,   257, 

+	257,   257,   257,   257,   257,   257,   257, 

+	257,   257,   257,   257,   257,   257,   257, 

+	258,   258,   258,   257,   257,   257,   257, 

+	257,   257,   257,   257,   257,   257,   257, 

+	257,   257,   257,   257,   257,   257,   257, 

+	257,   257,   257,   257,   257,   257,   257, 

+	257,   257,   257,   257,   257,   257,   256, 

+	256,   256,   256,   256,   256,   256,   256, 

+	256,   256,   256,   256,   256,   256,   256, 

+	256,   256,   256,   256,   256,   256,   256, 

+	256,   256,   256,   256,   256,   256,   256, 

+	256,   256,   256,   255,   255,   255,   255, 

+	255,   255,   255,   255,   255,   255,   255, 

+	255,   255,   255,   255,   255,   255,   255, 

+	255,   255,   255,   255,   255,   255,   255, 

+	255,   255,   255,   255,   255,   255,   254, 

+	254,   254,   254,   254,   254,   254,   254, 

+	254,   254,   254,   254,   254,   254,   254, 

+	254,   254,   254,   254,   254,   254,   254, 

+	254,   254,   254,   254,   254,   254,   254, 

+	254,   254,   254,   253,   254,   254,   254, 

+	254,   254,   254,   254,   254,   254,   254, 

+	254,   254,   254,   254,   254,   254,   254, 

+	254,   254,   254,   254,   254,   254,   254, 

+	254,   254,   254,   254,   254,   254,   254, 

+	254,   255,   255,   255,   255,   255,   255, 

+	255,   255,   255,   255,   255,   255,   255, 

+	255,   255,   255,   255,   255,   255,   255, 

+	255,   255,   255,   255,   255,   255,   255, 

+	255,   255,   255,   255, 

+};

+

+static const e_int16 db2lin_coeff[]  ICONST_ATTR = {

+	255,   249,   244,   239,   233,   228,   224, 

+	219,   214,   209,   205,   201,   196,   192, 

+	188,   184,   180,   176,   172,   169,   165, 

+	162,   158,   155,   151,   148,   145,   142, 

+	139,   136,   133,   130,   127,   125,   122, 

+	119,   117,   114,   112,   109,   107,   105, 

+	102,   100,    98,    96,    94,    92,    90, 

+	 88,    86,    84,    82,    81,    79,    77, 

+	 76,    74,    72,    71,    69,    68,    66, 

+	 65,    64,    62,    61,    60,    58,    57, 

+	 56,    55,    53,    52,    51,    50,    49, 

+	 48,    47,    46,    45,    44,    43,    42, 

+	 41,    40,    39,    38,    38,    37,    36, 

+	 35,    34,    34,    33,    32,    32,    31, 

+	 30,    30,    29,    28,    28,    27,    27, 

+	 26,    25,    25,    24,    24,    23,    23, 

+	 22,    22,    21,    21,    20,    20,    19, 

+	 19,    19,    18,    18,    17,    17,    17, 

+	 16,    16,    16,    15,    15,    15,    14, 

+	 14,    14,    13,    13,    13,    12,    12, 

+	 12,    12,    11,    11,    11,    11,    10, 

+	 10,    10,    10,    10,     9,     9,     9, 

+	  9,     8,     8,     8,     8,     8,     8, 

+	  7,     7,     7,     7,     7,     7,     6, 

+	  6,     6,     6,     6,     6,     6,     5, 

+	  5,     5,     5,     5,     5,     5,     5, 

+	  5,     4,     4,     4,     4,     4,     4, 

+	  4,     4,     4,     4,     3,     3,     3, 

+	  3,     3,     3,     3,     3,     3,     3, 

+	  3,     3,     3,     2,     2,     2,     2, 

+	  2,     2,     2,     2,     2,     2,     2, 

+	  2,     2,     2,     2,     2,     2,     2, 

+	  2,     1,     1,     1,     1,     1,     1, 

+	  1,     1,     1,     1,     1,     1,     1, 

+	  1,     1,     1,     1,     1,     1,     1, 

+	  1,     1,     1,     1,     1,     1,     1, 

+	  1,     1,     1,     1,     1,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0,     0,     0,     0,     0,     0,     0, 

+	  0, 

+};

+

+static const e_uint16 ar_adjust_coeff[] ICONST_ATTR = {

+	127,  108,   98,   90,   84,   80,   75, 

+	 72,   69,   66,   64,   61,   59,   57, 

+	 56,   54,   52,   51,   49,   48,   47, 

+	 45,   44,   43,   42,   41,   40,   39, 

+	 38,   37,   36,   36,   35,   34,   33, 

+	 33,   32,   31,   30,   30,   29,   29, 

+	 28,   27,   27,   26,   26,   25,   24, 

+	 24,   23,   23,   22,   22,   21,   21, 

+	 21,   20,   20,   19,   19,   18,   18, 

+	 17,   17,   17,   16,   16,   15,   15, 

+	 15,   14,   14,   14,   13,   13,   13, 

+	 12,   12,   12,   11,   11,   11,   10, 

+	 10,   10,    9,    9,    9,    9,    8, 

+	  8,    8,    7,    7,    7,    7,    6, 

+	  6,    6,    6,    5,    5,    5,    4, 

+	  4,    4,    4,    4,    3,    3,    3, 

+	  3,    2,    2,    2,    2,    1,    1, 

+	  1,    1,    1,    0,    0,    0,    0, 

+	  0, 

+};

+

+#endif

diff --git a/apps/codecs/libgme/emutypes.h b/apps/codecs/libgme/emutypes.h
new file mode 100644
index 0000000..bf5d7e1
--- /dev/null
+++ b/apps/codecs/libgme/emutypes.h
@@ -0,0 +1,41 @@
+#ifndef _EMUTYPES_H_

+#define _EMUTYPES_H_

+

+#if defined(_MSC_VER)

+#define INLINE __forceinline

+#elif defined(__GNUC__)

+#define INLINE __inline__

+#elif defined(_MWERKS_)

+#define INLINE inline

+#else

+#define INLINE

+#endif

+

+#if defined(EMU_DLL_IMPORTS)

+#define EMU2149_DLL_IMPORTS

+#define EMU2212_DLL_IMPORTS

+#define EMU2413_DLL_IMPORTS

+#define EMU8950_DLL_IMPORTS

+#define EMU76489_DLL_IMPORTS

+#endif

+

+#ifdef __cplusplus

+extern "C" {

+#endif

+

+typedef unsigned int e_uint;

+typedef signed int e_int;

+

+typedef unsigned char e_uint8 ;

+typedef signed char e_int8 ;

+

+typedef unsigned short e_uint16 ;

+typedef signed short e_int16 ;

+

+typedef unsigned int e_uint32 ;

+typedef signed int e_int32 ;

+

+#ifdef __cplusplus

+}

+#endif

+#endif

diff --git a/apps/codecs/libgme/gb_apu.c b/apps/codecs/libgme/gb_apu.c
new file mode 100644
index 0000000..a441645
--- /dev/null
+++ b/apps/codecs/libgme/gb_apu.c
@@ -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;
+}
diff --git a/apps/codecs/libgme/gb_apu.h b/apps/codecs/libgme/gb_apu.h
new file mode 100644
index 0000000..f1457a2
--- /dev/null
+++ b/apps/codecs/libgme/gb_apu.h
@@ -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
diff --git a/apps/codecs/libgme/gb_cpu.c b/apps/codecs/libgme/gb_cpu.c
new file mode 100644
index 0000000..ae19cc0
--- /dev/null
+++ b/apps/codecs/libgme/gb_cpu.c
@@ -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 );
+}
diff --git a/apps/codecs/libgme/gb_cpu.h b/apps/codecs/libgme/gb_cpu.h
new file mode 100644
index 0000000..3a3b1d6
--- /dev/null
+++ b/apps/codecs/libgme/gb_cpu.h
@@ -0,0 +1,80 @@
+// 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 {	
+	// Base address for RST vectors, to simplify GBS player (normally 0)
+	addr_t rst_base;
+	
+	struct registers_t r;
+	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
diff --git a/apps/codecs/libgme/gb_cpu_run.h b/apps/codecs/libgme/gb_cpu_run.h
new file mode 100644
index 0000000..86f06fa
--- /dev/null
+++ b/apps/codecs/libgme/gb_cpu_run.h
@@ -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;
+}
diff --git a/apps/codecs/libgme/gb_oscs.c b/apps/codecs/libgme/gb_oscs.c
new file mode 100644
index 0000000..6d607d7
--- /dev/null
+++ b/apps/codecs/libgme/gb_oscs.c
@@ -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;
+}
diff --git a/apps/codecs/libgme/gb_oscs.h b/apps/codecs/libgme/gb_oscs.h
new file mode 100644
index 0000000..0cc9d3f
--- /dev/null
+++ b/apps/codecs/libgme/gb_oscs.h
@@ -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
diff --git a/apps/codecs/libgme/gbs_cpu.c b/apps/codecs/libgme/gbs_cpu.c
new file mode 100644
index 0000000..5a27bf6
--- /dev/null
+++ b/apps/codecs/libgme/gbs_cpu.c
@@ -0,0 +1,120 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "gbs_emu.h"
+#include "blargg_endian.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 );
+
+	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;
+}
+
+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"
+}
diff --git a/apps/codecs/libgme/gbs_emu.c b/apps/codecs/libgme/gbs_emu.c
new file mode 100644
index 0000000..693e84a
--- /dev/null
+++ b/apps/codecs/libgme/gbs_emu.c
@@ -0,0 +1,631 @@
+// 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 );
+	
+	Rom_init( &this->rom, 0x4000 );
+	
+	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 );
+}
+
+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;
+	this->m3u.size = 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->stereo_buf, 4194304 );
+	this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_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;
+	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 * this->rom.bank_size, this->rom.mask );
+	if ( addr == 0 && this->rom.size > this->rom.bank_size )
+		addr = this->rom.bank_size; // MBC1&2 behavior, bank 0 acts like bank 1
+	Cpu_map_code( &this->cpu, this->rom.bank_size, this->rom.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->stereo_buf, &out [count - remain], remain );
+		if ( remain )
+		{
+			if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
+			{
+				this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
+				
+				// Remute voices
+				Sound_mute_voices( this, this->mute_mask_ );
+			}
+			int msec = Buffer_length( &this->stereo_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->stereo_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->stereo_buf );
+	RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
+	
+	// Set bass frequency
+	Buffer_bass_freq( &this->stereo_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->stereo_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];
+		track = e->track;
+	}
+
+	this->current_track_ = track;
+	
+	Buffer_clear( &this->stereo_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, this->rom.bank_size, Rom_at_addr( &this->rom, 0 ) );
+	Set_bank( this, this->rom.size > this->rom.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;
+}
diff --git a/apps/codecs/libgme/gbs_emu.h b/apps/codecs/libgme/gbs_emu.h
new file mode 100644
index 0000000..c107264
--- /dev/null
+++ b/apps/codecs/libgme/gbs_emu.h
@@ -0,0 +1,204 @@
+// 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 {
+	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 
+	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
+	
+	// Larger items at the end
+	// Header for currently loaded file
+	struct header_t header;
+	
+	// M3u Playlist
+	struct M3u_Playlist m3u;
+	
+	struct Gb_Apu apu;
+	struct Gb_Cpu cpu;
+	struct Stereo_Buffer stereo_buf;
+	
+	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;
+
+// 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;
+	} 
+	
+	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
diff --git a/apps/codecs/libgme/gme.h b/apps/codecs/libgme/gme.h
new file mode 100644
index 0000000..d680370
--- /dev/null
+++ b/apps/codecs/libgme/gme.h
@@ -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
diff --git a/apps/codecs/libgme/gme_types.h b/apps/codecs/libgme/gme_types.h
new file mode 100644
index 0000000..06226f4
--- /dev/null
+++ b/apps/codecs/libgme/gme_types.h
@@ -0,0 +1,21 @@
+#ifndef GME_TYPES_H
+#define GME_TYPES_H
+
+/*
+ * This is a default gme_types.h for use when *not* using
+ * CMake.  If CMake is in use gme_types.h.in will be
+ * processed instead.
+ */
+#define USE_GME_AY
+#define USE_GME_GBS
+#define USE_GME_GYM
+#define USE_GME_HES
+#define USE_GME_KSS
+#define USE_GME_NSF
+#define USE_GME_NSFE
+#define USE_GME_SAP
+#define USE_GME_SPC
+/* VGM and VGZ are a package deal */
+#define USE_GME_VGM
+
+#endif /* GME_TYPES_H */
diff --git a/apps/codecs/libgme/hes_apu.c b/apps/codecs/libgme/hes_apu.c
new file mode 100644
index 0000000..a3af054
--- /dev/null
+++ b/apps/codecs/libgme/hes_apu.c
@@ -0,0 +1,315 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "hes_apu.h"
+#include <string.h>
+
+/* 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"
+
+enum { center_waves = 1 }; // reduces asymmetry and clamping when starting notes
+
+static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ) ICODE_ATTR;
+static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
+{
+	static short const log_table [32] ICONST_ATTR = { // ~1.5 db per step
+		#define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5)
+		ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ),
+		ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ),
+		ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ),
+		ENTRY( 0.037163 ),ENTRY( 0.044194 ),ENTRY( 0.052556 ),ENTRY( 0.062500 ),
+		ENTRY( 0.074325 ),ENTRY( 0.088388 ),ENTRY( 0.105112 ),ENTRY( 0.125000 ),
+		ENTRY( 0.148651 ),ENTRY( 0.176777 ),ENTRY( 0.210224 ),ENTRY( 0.250000 ),
+		ENTRY( 0.297302 ),ENTRY( 0.353553 ),ENTRY( 0.420448 ),ENTRY( 0.500000 ),
+		ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ),
+		#undef ENTRY
+	};
+	
+	int vol = (osc->control & 0x1F) - 0x1E * 2;
+	
+	int left  = vol + (osc->balance >> 3 & 0x1E) + (this->balance >> 3 & 0x1E);
+	if ( left  < 0 ) left  = 0;
+	
+	int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E);
+	if ( right < 0 ) right = 0;
+	
+	left  = log_table [left ];
+	right = log_table [right];
+	
+	// optimizing for the common case of being centered also allows easy
+	// panning using Effects_Buffer
+	osc->outputs [0] = osc->chans [0]; // center
+	osc->outputs [1] = 0;
+	if ( left != right )
+	{
+		osc->outputs [0] = osc->chans [1]; // left
+		osc->outputs [1] = osc->chans [2]; // right
+	}
+	
+	if ( center_waves )
+	{
+		osc->last_amp [0] += (left  - osc->volume [0]) * 16;
+		osc->last_amp [1] += (right - osc->volume [1]) * 16;
+	}
+	
+	osc->volume [0] = left;
+	osc->volume [1] = right;
+}
+
+void Apu_init( struct Hes_Apu* this )
+{
+	struct Hes_Osc* osc = &this->oscs [osc_count];
+	do
+	{
+		osc--;
+		osc->outputs [0] = 0;
+		osc->outputs [1] = 0;
+		osc->chans [0] = 0;
+		osc->chans [1] = 0;
+		osc->chans [2] = 0;
+	}
+	while ( osc != this->oscs );
+	
+	Apu_reset( this );
+}
+
+void Apu_reset( struct Hes_Apu* this )
+{
+	this->latch   = 0;
+	this->balance = 0xFF;
+	
+	struct Hes_Osc* osc = &this->oscs [osc_count];
+	do
+	{
+		osc--;
+		memset( osc, 0, offsetof (struct Hes_Osc,outputs) );
+		osc->noise_lfsr = 1;
+		osc->control    = 0x40;
+		osc->balance    = 0xFF;
+	}
+	while ( osc != this->oscs );
+}
+
+void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
+{
+	require( (unsigned) index < osc_count );
+	this->oscs [index].chans [0] = center;
+	this->oscs [index].chans [1] = left;
+	this->oscs [index].chans [2] = right;
+	
+	struct Hes_Osc* osc = &this->oscs [osc_count];
+	do
+	{
+		osc--;
+		Apu_balance_changed( this, osc );
+	}
+	while ( osc != this->oscs );
+}
+
+void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth_, blip_time_t end_time )
+{
+	struct Blip_Buffer* const osc_outputs_0 = this->outputs [0]; // cache often-used values
+	if ( osc_outputs_0 && this->control & 0x80 )
+	{
+		int dac = this->dac;
+		
+		int const volume_0 = this->volume [0];
+		{
+			int delta = dac * volume_0 - this->last_amp [0];
+			if ( delta )
+				Synth_offset( synth_, this->last_time, delta, osc_outputs_0 );
+			Blip_set_modified( osc_outputs_0 );
+		}
+		
+		struct Blip_Buffer* const osc_outputs_1 = this->outputs [1];
+		int const volume_1 = this->volume [1];
+		if ( osc_outputs_1 )
+		{
+			int delta = dac * volume_1 - this->last_amp [1];
+			if ( delta )
+				Synth_offset( synth_, this->last_time, delta, osc_outputs_1 );
+			Blip_set_modified( osc_outputs_1 );
+		}
+		
+		blip_time_t time = this->last_time + this->delay;
+		if ( time < end_time )
+		{
+			if ( this->noise & 0x80 )
+			{
+				if ( volume_0 | volume_1 )
+				{
+					// noise
+					int const period = (32 - (this->noise & 0x1F)) * 64; // TODO: correct?
+					unsigned noise_lfsr = this->noise_lfsr;
+					do
+					{
+						int new_dac = 0x1F & -(noise_lfsr >> 1 & 1);
+						// Implemented using "Galios configuration"
+						// TODO: find correct LFSR algorithm
+						noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1));
+						//noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1));
+						int delta = new_dac - dac;
+						if ( delta )
+						{
+							dac = new_dac;
+							Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 );
+							if ( osc_outputs_1 )
+								Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 );
+						}
+						time += period;
+					}
+					while ( time < end_time );
+					
+					this->noise_lfsr = noise_lfsr;
+					assert( noise_lfsr );
+				}
+			}
+			else if ( !(this->control & 0x40) )
+			{
+				// wave
+				int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop
+				int period = this->period * 2;
+				if ( period >= 14 && (volume_0 | volume_1) )
+				{
+					do
+					{
+						int new_dac = this->wave [phase];
+						phase = (phase + 1) & 0x1F;
+						int delta = new_dac - dac;
+						if ( delta )
+						{
+							dac = new_dac;
+							Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 );
+							if ( osc_outputs_1 )
+								Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 );
+						}
+						time += period;
+					}
+					while ( time < end_time );
+				}
+				else
+				{
+					if ( !period )
+					{
+						// TODO: Gekisha Boy assumes that period = 0 silences wave
+						//period = 0x1000 * 2;
+						period = 1;
+						//if ( !(volume_0 | volume_1) )
+						//  dprintf( "Used period 0\n" );
+					}
+					
+					// maintain phase when silent
+					blargg_long count = (end_time - time + period - 1) / period;
+					phase += count; // phase will be masked below
+					time += count * period;
+				}
+				this->phase = (phase - 1) & 0x1F; // undo pre-advance
+			}
+		}
+		time -= end_time;
+		if ( time < 0 )
+			time = 0;
+		this->delay = time;
+		
+		this->dac = dac;
+		this->last_amp [0] = dac * volume_0;
+		this->last_amp [1] = dac * volume_1;
+	}
+	this->last_time = end_time;
+}
+
+void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data )
+{
+	if ( addr == 0x800 )
+	{
+		this->latch = data & 7;
+	}
+	else if ( addr == 0x801 )
+	{
+		if ( this->balance != data )
+		{
+			this->balance = data;
+			
+			struct Hes_Osc* osc = &this->oscs [osc_count];
+			do
+			{
+				osc--;
+				Osc_run_until( osc, &this->synth, time );
+				Apu_balance_changed( this, this->oscs );
+			}
+			while ( osc != this->oscs );
+		}
+	}
+	else if ( this->latch < osc_count )
+	{
+		struct Hes_Osc* osc = &this->oscs [this->latch];
+		Osc_run_until( osc, &this->synth, time );
+		switch ( addr )
+		{
+		case 0x802:
+			osc->period = (osc->period & 0xF00) | data;
+			break;
+		
+		case 0x803:
+			osc->period = (osc->period & 0x0FF) | ((data & 0x0F) << 8);
+			break;
+		
+		case 0x804:
+			if ( osc->control & 0x40 & ~data )
+				osc->phase = 0;
+			osc->control = data;
+			Apu_balance_changed( this, osc );
+			break;
+		
+		case 0x805:
+			osc->balance = data;
+			Apu_balance_changed( this, osc );
+			break;
+		
+		case 0x806:
+			data &= 0x1F;
+			if ( !(osc->control & 0x40) )
+			{
+				osc->wave [osc->phase] = data;
+				osc->phase = (osc->phase + 1) & 0x1F;
+			}
+			else if ( osc->control & 0x80 )
+			{
+				osc->dac = data;
+			}
+			break;
+		
+		 case 0x807:
+		 	if ( osc >= &this->oscs [4] )
+		 		osc->noise = data;
+		 	break;
+		 case 0x809:
+		 	if ( !(data & 0x80) && (data & 0x03) != 0 ) {
+		 		dprintf( "HES LFO not supported\n" );
+			}
+		}
+	}
+}
+
+void Apu_end_frame( struct Hes_Apu* this, blip_time_t end_time )
+{
+	struct Hes_Osc* osc = &this->oscs [osc_count];
+	do
+	{
+		osc--;
+		if ( end_time > osc->last_time )
+			Osc_run_until( osc, &this->synth, end_time );
+		assert( osc->last_time >= end_time );
+		osc->last_time -= end_time;
+	}
+	while ( osc != this->oscs );
+}
diff --git a/apps/codecs/libgme/hes_apu.h b/apps/codecs/libgme/hes_apu.h
new file mode 100644
index 0000000..8a49a5a
--- /dev/null
+++ b/apps/codecs/libgme/hes_apu.h
@@ -0,0 +1,55 @@
+// Turbo Grafx 16 (PC Engine) PSG sound chip emulator
+
+// Game_Music_Emu 0.5.2
+#ifndef HES_APU_H
+#define HES_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+enum { amp_range = 0x8000 };
+enum { osc_count = 6 };
+enum { start_addr = 0x0800 };
+enum { end_addr   = 0x0809 };
+
+struct Hes_Osc
+{
+	unsigned char wave [32];
+	short volume [2];
+	int last_amp [2];
+	int delay;
+	int period;
+	unsigned char noise;
+	unsigned char phase;
+	unsigned char balance;
+	unsigned char dac;
+	blip_time_t last_time;
+	
+	struct Blip_Buffer* outputs [2];
+	struct Blip_Buffer* chans [3];
+	unsigned noise_lfsr;
+	unsigned char control;
+};
+
+void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth, blip_time_t ) ICODE_ATTR;
+
+struct Hes_Apu {
+	struct Hes_Osc oscs [osc_count];
+	
+	int latch;
+	int balance;
+	struct Blip_Synth synth;
+};
+
+// Init HES apu sound chip
+void Apu_init( struct Hes_Apu* this );
+
+// Reset HES apu couns chip
+void Apu_reset( struct Hes_Apu* this );
+
+void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) ICODE_ATTR;
+void Apu_write_data( struct Hes_Apu* this, blip_time_t, int addr, int data ) ICODE_ATTR;
+void Apu_end_frame( struct Hes_Apu* this, blip_time_t ) ICODE_ATTR;
+
+static inline void Apu_volume( struct Hes_Apu* this, double v ) { Synth_volume( &this->synth, 1.8 / osc_count / amp_range * v ); }
+#endif
diff --git a/apps/codecs/libgme/hes_apu_adpcm.c b/apps/codecs/libgme/hes_apu_adpcm.c
new file mode 100644
index 0000000..b2f78ff
--- /dev/null
+++ b/apps/codecs/libgme/hes_apu_adpcm.c
@@ -0,0 +1,297 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/

+

+#include "hes_apu_adpcm.h"

+

+/* Copyright (C) 2006-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 */

+

+

+void Adpcm_init( struct Hes_Apu_Adpcm* this )

+{

+	this->output = NULL;

+	memset( &this->state, 0, sizeof( this->state ) );

+	Adpcm_reset( this );

+}

+

+void Adpcm_reset( struct Hes_Apu_Adpcm* this )

+{

+	this->last_time = 0;

+	this->next_timer = 0;

+	this->last_amp = 0;

+

+	memset( &this->state.pcmbuf, 0, sizeof(this->state.pcmbuf) );

+	memset( &this->state.port, 0, sizeof(this->state.port) );

+

+	this->state.ad_sample = 0;

+	this->state.ad_ref_index = 0;

+

+	this->state.addr = 0;

+	this->state.freq = 0;

+	this->state.writeptr = 0;

+	this->state.readptr = 0;

+	this->state.playflag = 0;

+	this->state.repeatflag = 0;

+	this->state.length = 0;

+	this->state.volume = 0xFF;

+	this->state.fadetimer = 0;

+	this->state.fadecount = 0;

+}

+

+static short stepsize[49] = {

+  16,  17,  19,  21,  23,  25,  28,

+  31,  34,  37,  41,  45,  50,  55,

+  60,  66,  73,  80,  88,  97, 107,

+ 118, 130, 143, 157, 173, 190, 209,

+ 230, 253, 279, 307, 337, 371, 408,

+ 449, 494, 544, 598, 658, 724, 796,

+ 876, 963,1060,1166,1282,1411,1552

+};

+

+static int Adpcm_decode( struct Hes_Apu_Adpcm* this,int code ) ICODE_ATTR;

+static int Adpcm_decode( struct Hes_Apu_Adpcm* this,int code )

+{

+	struct State* state = &this->state;

+	int step = stepsize[state->ad_ref_index];

+	int delta;

+	int c = code & 7;

+#if 1

+	delta = 0;

+	if ( c & 4 ) delta += step;

+	step >>= 1;

+	if ( c & 2 ) delta += step;

+	step >>= 1;

+	if ( c & 1 ) delta += step;

+	step >>= 1;

+	delta += step;

+#else

+	delta = ( ( c + c + 1 ) * step ) / 8; // maybe faster, but introduces rounding

+#endif

+	if ( c != code )

+	{

+		state->ad_sample -= delta;

+		if ( state->ad_sample < -2048 )

+			state->ad_sample = -2048;

+	}

+	else

+	{

+		state->ad_sample += delta;

+		if ( state->ad_sample > 2047 )

+			state->ad_sample = 2047;

+	}

+

+	static int const steps [8] ICONST_ATTR = {

+		-1, -1, -1, -1, 2, 4, 6, 8

+	};

+	state->ad_ref_index += steps [c];

+	if ( state->ad_ref_index < 0 )

+		state->ad_ref_index = 0;

+	else if ( state->ad_ref_index > 48 )

+		state->ad_ref_index = 48;

+

+	return state->ad_sample;

+}

+

+static void Adpcm_run_until( struct Hes_Apu_Adpcm* this, blip_time_t end_time ) ICODE_ATTR;

+static void Adpcm_run_until( struct Hes_Apu_Adpcm* this, blip_time_t end_time )

+{

+	struct State* state = &this->state;

+	int volume = state->volume;

+	int fadetimer = state->fadetimer;

+	int fadecount = state->fadecount;

+	int last_time = this->last_time;

+	double next_timer = this->next_timer;

+	int last_amp = this->last_amp;

+	

+	struct Blip_Buffer* output = this->output; // cache often-used values

+

+	while ( state->playflag && last_time < end_time )

+	{

+		while ( last_time >= next_timer )

+		{

+			if ( fadetimer )

+			{

+				if ( fadecount > 0 )

+				{

+					fadecount--;

+					volume = 0xFF * fadecount / fadetimer;

+				}

+				else if ( fadecount < 0 )

+				{

+					fadecount++;

+					volume = 0xFF - ( 0xFF * fadecount / fadetimer );

+				}

+			}

+			next_timer += 7159.091;

+		}

+		int amp;

+		if ( state->ad_low_nibble )

+		{

+			amp = Adpcm_decode( this, state->pcmbuf[ state->playptr ] & 0x0F );

+			state->ad_low_nibble = false;

+			state->playptr++;

+			state->playedsamplecount++;

+			if ( state->playedsamplecount == state->playlength )

+			{

+				state->playflag = 0;

+			}

+		}

+		else

+		{

+			amp = Adpcm_decode( this, state->pcmbuf[ state->playptr ] >> 4 );

+			state->ad_low_nibble = true;

+		}

+		amp = amp * volume / 0xFF;

+		int delta = amp - last_amp;

+		if ( output && delta )

+		{

+			last_amp = amp;

+			Synth_offset_inline( &this->synth, last_time, delta, output );

+		}

+		last_time += state->freq;

+	}

+

+	if ( !state->playflag )

+	{

+		while ( next_timer <= end_time ) next_timer += 7159.091;

+		last_time = end_time;

+	}

+	

+	this->last_time  = last_time;

+	this->next_timer = next_timer;

+	this->last_amp   = last_amp;

+	state->volume = volume;

+	state->fadetimer = fadetimer;

+	state->fadecount = fadecount;

+}

+

+void Adpcm_write_data( struct Hes_Apu_Adpcm* this, blip_time_t time, int addr, int data )

+{

+	if ( time > this->last_time ) Adpcm_run_until( this, time );

+	struct State* state = &this->state;

+

+	data &= 0xFF;

+	state->port[ addr & 15 ] = data;

+	switch ( addr & 15 )

+	{

+	case 8:

+		state->addr &= 0xFF00;

+		state->addr |= data;

+		break;

+	case 9:

+		state->addr &= 0xFF;

+		state->addr |= data << 8;

+		break;

+	case 10:

+		state->pcmbuf[ state->writeptr++ ] = data;

+		state->playlength ++;

+		break;

+	case 11:

+		dprintf("ADPCM DMA 0x%02X", data);

+		break;

+	case 13:

+		if ( data & 0x80 )

+		{

+			state->addr = 0;

+			state->freq = 0;

+			state->writeptr = 0;

+			state->readptr = 0;

+			state->playflag = 0;

+			state->repeatflag = 0;

+			state->length = 0;

+			state->volume = 0xFF;

+		}

+		if ( ( data & 3 ) == 3 )

+		{

+			state->writeptr = state->addr;

+		}

+		if ( data & 8 )

+		{

+			state->readptr = state->addr ? state->addr - 1 : state->addr;

+		}

+		if ( data & 0x10 )

+		{

+			state->length = state->addr;

+		}

+		state->repeatflag = data & 0x20;

+		state->playflag = data & 0x40;

+		if ( state->playflag )

+		{

+			state->playptr = state->readptr;

+			state->playlength = state->length + 1;

+			state->playedsamplecount = 0;

+			state->ad_sample = 0;

+			state->ad_low_nibble = false;

+		}

+		break;

+	case 14:

+		state->freq = 7159091 / ( 32000 / ( 16 - ( data & 15 ) ) );

+		break;

+	case 15:

+		switch ( data & 15 )

+		{

+		case 0:

+		case 8:

+		case 12:

+			state->fadetimer = -100;

+			state->fadecount = state->fadetimer;

+			break;

+		case 10:

+			state->fadetimer = 5000;

+			state->fadecount = state->fadetimer;

+			break;

+		case 14:

+			state->fadetimer = 1500;

+			state->fadecount = state->fadetimer;

+			break;

+		}

+		break;

+	}

+}

+

+int Adpcm_read_data( struct Hes_Apu_Adpcm* this, blip_time_t time, int addr )

+{

+	if ( time > this->last_time ) Adpcm_run_until( this, time );

+

+	struct State* state = &this->state;

+	switch ( addr & 15 )

+	{

+	case 10:

+		return state->pcmbuf [state->readptr++];

+	case 11:

+		return state->port [11] & ~1;

+	case 12:

+		if (!state->playflag)

+		{

+			state->port [12] |= 1;

+			state->port [12] &= ~8;

+		}

+		else

+		{

+			state->port [12] &= ~1;

+			state->port [12] |= 8;

+		}

+		return state->port [12];

+	case 13:

+		return state->port [13];

+	}

+

+	return 0xFF;

+}

+

+void Adpcm_end_frame( struct Hes_Apu_Adpcm* this, blip_time_t end_time )

+{

+	Adpcm_run_until( this, end_time );

+	this->last_time -= end_time;

+	this->next_timer -= (double)end_time;

+	check( last_time >= 0 );

+	if ( this->output )

+		Blip_set_modified( this->output );

+}

diff --git a/apps/codecs/libgme/hes_apu_adpcm.h b/apps/codecs/libgme/hes_apu_adpcm.h
new file mode 100644
index 0000000..5478f2b
--- /dev/null
+++ b/apps/codecs/libgme/hes_apu_adpcm.h
@@ -0,0 +1,89 @@
+// Turbo Grafx 16 (PC Engine) ADPCM sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef HES_APU_ADPCM_H
+#define HES_APU_ADPCM_H
+
+#include "blargg_source.h"
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+enum { adpcm_amp_range = 2048 };
+enum { adpcm_osc_count = 1 }; // 0 <= chan < osc_count
+
+// Registers are at io_addr to io_addr+io_size-1
+enum { io_addr = 0x1800 };
+enum { io_size = 0x400 };
+
+struct State
+{
+	byte           pcmbuf [0x10000];
+	byte           port [0x10];
+	int            ad_sample;
+	int            ad_ref_index;
+	bool           ad_low_nibble;
+	int            freq;
+	unsigned short addr;
+	unsigned short writeptr;
+	unsigned short readptr;
+	unsigned short playptr;
+	byte           playflag;
+	byte           repeatflag;
+	int            length;
+	int            playlength;
+	int            playedsamplecount;
+	int            volume;
+	int            fadetimer;
+	int            fadecount;
+};
+
+struct Hes_Apu_Adpcm {
+	struct State state;
+	struct Blip_Synth synth;
+
+	struct Blip_Buffer* output;
+	blip_time_t  last_time;
+	double       next_timer;
+	int          last_amp;
+};
+
+// Init HES adpcm sound chip
+void Adpcm_init( struct Hes_Apu_Adpcm* this );
+
+// Rest HES adpcm sound chip
+void Adpcm_reset( struct Hes_Apu_Adpcm* this );
+
+// Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0,
+// output is mono.
+static inline void Adpcm_set_output( struct Hes_Apu_Adpcm* this, int chan, 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) chan < adpcm_osc_count ); // fails if you pass invalid osc index
+
+#if defined(ROCKBOX)
+    (void) chan;
+#endif
+
+	if ( !center || !left || !right )
+	{
+		left  = center;
+		right = center;
+	}
+	
+	this->output = center;
+}
+
+// Emulates to time t, then writes data to addr
+void Adpcm_write_data( struct Hes_Apu_Adpcm* this, blip_time_t t, int addr, int data ) ICODE_ATTR;
+	
+// Emulates to time t, then reads from addr
+int Adpcm_read_data( struct Hes_Apu_Adpcm* this, blip_time_t t, int addr ) ICODE_ATTR;
+
+// Emulates to time t, then subtracts t from the current time.
+// OK if previous write call had time slightly after t.
+void Adpcm_end_frame( struct Hes_Apu_Adpcm* this,blip_time_t t ) ICODE_ATTR;
+
+// Sets overall volume, where 1.0 is normal
+static inline void Adpcm_volume( struct Hes_Apu_Adpcm* this, double v )	{ Synth_volume( &this->synth, 0.6 / adpcm_osc_count / adpcm_amp_range * v ); }
+#endif
diff --git a/apps/codecs/libgme/hes_cpu.c b/apps/codecs/libgme/hes_cpu.c
new file mode 100644
index 0000000..08dfb5e
--- /dev/null
+++ b/apps/codecs/libgme/hes_cpu.c
@@ -0,0 +1,1321 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "hes_cpu.h"
+
+#include "blargg_endian.h"
+
+//#include "hes_cpu_log.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 */
+
+// TODO: support T flag, including clearing it at appropriate times?
+
+// all zero-page should really use whatever is at page 1, but that would
+// reduce efficiency quite a bit
+int const ram_addr = 0x2000;
+
+#define FLUSH_TIME()    (void) (s.time = s_time)
+#define CACHE_TIME()    (void) (s_time = s.time)
+
+#include "hes_cpu_io.h"
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_NONPORTABLE
+	#define PAGE_OFFSET( addr ) (addr)
+#else
+	#define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
+#endif
+
+// status flags
+int const st_n = 0x80;
+int const st_v = 0x40;
+int const st_t = 0x20;
+int const st_b = 0x10;
+int const st_d = 0x08;
+int const st_i = 0x04;
+int const st_z = 0x02;
+int const st_c = 0x01;
+
+void Cpu_init( struct Hes_Cpu* this )
+{
+	this->state = &this->state_;
+}
+
+void Cpu_reset( struct Hes_Cpu* this )
+{
+	check( this->state == &state_ );
+	this->state = &this->state_;
+	
+	this->state_.time = 0;
+	this->state_.base = 0;
+	this->irq_time   = future_hes_time;
+	this->end_time   = future_hes_time;
+	
+	this->r.status = st_i;
+	this->r.sp     = 0;
+	this->r.pc     = 0;
+	this->r.a      = 0;
+	this->r.x      = 0;
+	this->r.y      = 0;
+	
+	blargg_verify_byte_order();
+}
+
+void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank )
+{
+	assert( (unsigned) reg <= page_count ); // allow page past end to be set
+	assert( (unsigned) bank < 0x100 );
+	this->cpu.mmr [reg] = bank;
+	uint8_t const* code = CPU_SET_MMR( this, reg, bank );
+	this->cpu.state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift );
+}
+
+#define TIME    (s_time + s.base)
+
+#define READ( addr )            CPU_READ( this, (addr), TIME )
+#define WRITE( addr, data )     {CPU_WRITE( this, (addr), (data), TIME );}
+#define READ_LOW( addr )        (cpu->ram [(int) (addr)])
+#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
+#define READ_PROG( addr )       (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )])
+
+#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
+#define GET_SP()        ((sp - 1) & 0xFF)
+#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
+
+// even on x86, using short and unsigned char was slower
+typedef int         fint16;
+typedef unsigned    fuint16;
+typedef unsigned    fuint8;
+typedef blargg_long fint32;
+
+bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time )
+{
+	bool illegal_encountered = false;
+		
+	// Set cpu end time
+	struct Hes_Cpu* cpu = &this->cpu;
+	cpu->state->time += Cpu_update_end_time( cpu, cpu->r.status, (cpu->end_time = end_time), cpu->irq_time );
+	
+	struct state_t s = cpu->state_;
+	cpu->state = &s;
+	
+	// even on x86, using s.time in place of s_time was slower
+	fint16 s_time = s.time;
+	
+	struct registers_t* r = &cpu->r;
+	
+	// registers
+	fuint16 pc = r->pc;
+	fuint8 a = r->a;
+	fuint8 x = r->x;
+	fuint8 y = r->y;
+	fuint16 sp;
+	SET_SP( r->sp );
+	
+	#define IS_NEG (nz & 0x8080)
+	
+	#define CALC_STATUS( out ) do {\
+		out = status & (st_v | st_d | st_i);\
+		out |= ((nz >> 8) | nz) & st_n;\
+		out |= c >> 8 & st_c;\
+		if ( !(nz & 0xFF) ) out |= st_z;\
+	} while ( 0 )
+
+	#define SET_STATUS( in ) do {\
+		status = in & (st_v | st_d | st_i);\
+		nz = in << 8;\
+		c = nz;\
+		nz |= ~in & st_z;\
+	} while ( 0 )
+	
+	fuint8 status;
+	fuint16 c;  // carry set if (c & 0x100) != 0
+	fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
+	{
+		fuint8 temp = r->status;
+		SET_STATUS( temp );
+	}
+	
+	goto loop;
+branch_not_taken:
+	s_time -= 2;
+loop:
+	
+	#ifndef NDEBUG
+	{
+		hes_time_t correct = end_time_;
+		if ( !(status & st_i) && correct > irq_time_ )
+			correct = irq_time_;
+		check( s.base == correct );
+		/*
+		static long count;
+		if ( count == 1844 ) Debugger();
+		if ( s.base != correct ) dprintf( "%ld\n", count );
+		count++;
+		*/
+	}
+	#endif
+
+	check( (unsigned) GET_SP() < 0x100 );
+	check( (unsigned) a < 0x100 );
+	check( (unsigned) x < 0x100 );
+	
+	uint8_t const* instr = s.code_map [pc >> page_shift];
+	fuint8 opcode;
+	
+	// TODO: eliminate this special case
+	#ifdef BLARGG_NONPORTABLE
+		opcode = instr [pc];
+		pc++;
+		instr += pc;
+	#else
+		instr += PAGE_OFFSET( pc );
+		opcode = *instr++;
+		pc++;
+	#endif
+	
+	// TODO: each reference lists slightly different timing values, ugh
+	static uint8_t const clock_table [256] ICONST_ATTR =
+	{// 0 1 2  3 4 5 6 7 8 9 A B C D E F
+		1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0
+		4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1
+		7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2
+		4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3
+		7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4
+		4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5
+		7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6
+		4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7
+		4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8
+		4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9
+		2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A
+		4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B
+		2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C
+		4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D
+		2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E
+		4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F
+	}; // 0x00 was 8
+	
+	fuint16 data;
+	data = clock_table [opcode];
+	if ( (s_time += data) >= 0 )
+		goto possibly_out_of_time;
+almost_out_of_time:
+	
+	data = *instr;
+	
+	#ifdef HES_CPU_LOG_H
+		log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2],
+				instr [3], instr [4], instr [5] );
+		//log_opcode( opcode );
+	#endif
+	
+	switch ( opcode )
+	{
+possibly_out_of_time:
+		if ( s_time < (int) data )
+			goto almost_out_of_time;
+		s_time -= data;
+		goto out_of_time;
+
+// Macros
+
+#define GET_MSB()           (instr [1])
+#define ADD_PAGE( out )     (pc++, out = data + 0x100 * GET_MSB());
+#define GET_ADDR()          GET_LE16( instr )
+
+// TODO: is the penalty really always added? the original 6502 was much better
+//#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
+#define PAGE_CROSS_PENALTY( lsb )
+
+// Branch
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH( cond )\
+{\
+	fint16 offset = (int8_t) data;\
+	pc++;\
+	if ( !(cond) ) goto branch_not_taken;\
+	pc = (uint16_t) (pc + offset);\
+	goto loop;\
+}
+
+	case 0xF0: // BEQ
+		BRANCH( !((uint8_t) nz) );
+	
+	case 0xD0: // BNE
+		BRANCH( (uint8_t) nz );
+	
+	case 0x10: // BPL
+		BRANCH( !IS_NEG );
+	
+	case 0x90: // BCC
+		BRANCH( !(c & 0x100) )
+	
+	case 0x30: // BMI
+		BRANCH( IS_NEG )
+	
+	case 0x50: // BVC
+		BRANCH( !(status & st_v) )
+	
+	case 0x70: // BVS
+		BRANCH( status & st_v )
+	
+	case 0xB0: // BCS
+		BRANCH( c & 0x100 )
+	
+	case 0x80: // BRA
+	branch_taken:
+		BRANCH( true );
+	
+	case 0xFF:
+		if ( pc == idle_addr + 1 )
+			goto idle_done;
+	case 0x0F: // BBRn
+	case 0x1F:
+	case 0x2F:
+	case 0x3F:
+	case 0x4F:
+	case 0x5F:
+	case 0x6F:
+	case 0x7F:
+	case 0x8F: // BBSn
+	case 0x9F:
+	case 0xAF:
+	case 0xBF:
+	case 0xCF:
+	case 0xDF:
+	case 0xEF: {
+		fuint16 t = 0x101 * READ_LOW( data );
+		t ^= 0xFF;
+		pc++;
+		data = GET_MSB();
+		BRANCH( t & (1 << (opcode >> 4)) )
+	}
+	
+	case 0x4C: // JMP abs
+		pc = GET_ADDR();
+		goto loop;
+	
+	case 0x7C: // JMP (ind+X)
+		data += x;
+	case 0x6C:{// JMP (ind)
+		data += 0x100 * GET_MSB();
+		pc = GET_LE16( &READ_PROG( data ) );
+		goto loop;
+	}
+	
+// Subroutine
+
+	case 0x44: // BSR
+		WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
+		sp = (sp - 2) | 0x100;
+		WRITE_LOW( sp, pc );
+		goto branch_taken;
+	
+	case 0x20: { // JSR
+		fuint16 temp = pc + 1;
+		pc = GET_ADDR();
+		WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
+		sp = (sp - 2) | 0x100;
+		WRITE_LOW( sp, temp );
+		goto loop;
+	}
+	
+	case 0x60: // RTS
+		pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
+		pc += 1 + READ_LOW( sp );
+		sp = (sp - 0xFE) | 0x100;
+		goto loop;
+	
+	case 0x00: // BRK
+		goto handle_brk;
+	
+// Common
+
+	case 0xBD:{// LDA abs,X
+		PAGE_CROSS_PENALTY( data + x );
+		fuint16 addr = GET_ADDR() + x;
+		pc += 2;
+		CPU_READ_FAST( this, addr, TIME, nz );
+		a = nz;
+		goto loop;
+	}
+	
+	case 0x9D:{// STA abs,X
+		fuint16 addr = GET_ADDR() + x;
+		pc += 2;
+		CPU_WRITE_FAST( this, addr, a, TIME );
+		goto loop;
+	}
+	
+	case 0x95: // STA zp,x
+		data = (uint8_t) (data + x);
+	case 0x85: // STA zp
+		pc++;
+		WRITE_LOW( data, a );
+		goto loop;
+	
+	case 0xAE:{// LDX abs
+		fuint16 addr = GET_ADDR();
+		pc += 2;
+		CPU_READ_FAST( this, addr, TIME, nz );
+		x = nz;
+		goto loop;
+	}
+	
+	case 0xA5: // LDA zp
+		a = nz = READ_LOW( data );
+		pc++;
+		goto loop;
+	
+// Load/store
+	
+	{
+		fuint16 addr;
+	case 0x91: // STA (ind),Y
+		addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
+		addr += READ_LOW( data ) + y;
+		pc++;
+		goto sta_ptr;
+	
+	case 0x81: // STA (ind,X)
+		data = (uint8_t) (data + x);
+	case 0x92: // STA (ind)
+		addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
+		addr += READ_LOW( data );
+		pc++;
+		goto sta_ptr;
+	
+	case 0x99: // STA abs,Y
+		data += y;
+	case 0x8D: // STA abs
+		addr = data + 0x100 * GET_MSB();
+		pc += 2;
+	sta_ptr:
+		CPU_WRITE_FAST( this, addr, a, TIME );
+		goto loop;
+	}
+	
+	{
+		fuint16 addr;
+	case 0xA1: // LDA (ind,X)
+		data = (uint8_t) (data + x);
+	case 0xB2: // LDA (ind)
+		addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
+		addr += READ_LOW( data );
+		pc++;
+		goto a_nz_read_addr;
+	
+	case 0xB1:// LDA (ind),Y
+		addr = READ_LOW( data ) + y;
+		PAGE_CROSS_PENALTY( addr );
+		addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
+		pc++;
+		goto a_nz_read_addr;
+	
+	case 0xB9: // LDA abs,Y
+		data += y;
+		PAGE_CROSS_PENALTY( data );
+	case 0xAD: // LDA abs
+		addr = data + 0x100 * GET_MSB();
+		pc += 2;
+	a_nz_read_addr:
+		CPU_READ_FAST( this, addr, TIME, nz );
+		a = nz;
+		goto loop;
+	}
+
+	case 0xBE:{// LDX abs,y
+		PAGE_CROSS_PENALTY( data + y );
+		fuint16 addr = GET_ADDR() + y;
+		pc += 2;
+		FLUSH_TIME();
+		x = nz = READ( addr );
+		CACHE_TIME();
+		goto loop;
+	}
+	
+	case 0xB5: // LDA zp,x
+		a = nz = READ_LOW( (uint8_t) (data + x) );
+		pc++;
+		goto loop;
+	
+	case 0xA9: // LDA #imm
+		pc++;
+		a  = data;
+		nz = data;
+		goto loop;
+
+// Bit operations
+
+	case 0x3C: // BIT abs,x
+		data += x;
+	case 0x2C:{// BIT abs
+		fuint16 addr;
+		ADD_PAGE( addr );
+		FLUSH_TIME();
+		nz = READ( addr );
+		CACHE_TIME();
+		goto bit_common;
+	}
+	case 0x34: // BIT zp,x
+		data = (uint8_t) (data + x);
+	case 0x24: // BIT zp
+		data = READ_LOW( data );
+	case 0x89: // BIT imm
+		nz = data;
+	bit_common:
+		pc++;
+		status &= ~st_v;
+		status |= nz & st_v;
+		if ( nz & a )
+			goto loop; // Z should be clear, and nz must be non-zero if nz & a is
+		nz <<= 8; // set Z flag without affecting N flag
+		goto loop;
+		
+	{
+		fuint16 addr;
+		
+	case 0xB3: // TST abs,x
+		addr = GET_MSB() + x;
+		goto tst_abs;
+	
+	case 0x93: // TST abs
+		addr = GET_MSB();
+	tst_abs:
+		addr += 0x100 * instr [2];
+		pc++;
+		FLUSH_TIME();
+		nz = READ( addr );
+		CACHE_TIME();
+		goto tst_common;
+	}
+	
+	case 0xA3: // TST zp,x
+		nz = READ_LOW( (uint8_t) (GET_MSB() + x) );
+		goto tst_common;
+	
+	case 0x83: // TST zp
+		nz = READ_LOW( GET_MSB() );
+	tst_common:
+		pc += 2;
+		status &= ~st_v;
+		status |= nz & st_v;
+		if ( nz & data )
+			goto loop; // Z should be clear, and nz must be non-zero if nz & data is
+		nz <<= 8; // set Z flag without affecting N flag
+		goto loop;
+	
+	{
+		fuint16 addr;
+	case 0x0C: // TSB abs
+	case 0x1C: // TRB abs
+		addr = GET_ADDR();
+		pc++;
+		goto txb_addr;
+	
+	// TODO: everyone lists different behaviors for the status flags, ugh
+	case 0x04: // TSB zp
+	case 0x14: // TRB zp
+		addr = data + ram_addr;
+	txb_addr:
+		FLUSH_TIME();
+		nz = a | READ( addr );
+		if ( opcode & 0x10 )
+			nz ^= a; // bits from a will already be set, so this clears them
+		status &= ~st_v;
+		status |= nz & st_v;
+		pc++;
+		WRITE( addr, nz );
+		CACHE_TIME();
+		goto loop;
+	}
+	
+	case 0x07: // RMBn
+	case 0x17:
+	case 0x27:
+	case 0x37:
+	case 0x47:
+	case 0x57:
+	case 0x67:
+	case 0x77:
+		pc++;
+		READ_LOW( data ) &= ~(1 << (opcode >> 4));
+		goto loop;
+	
+	case 0x87: // SMBn
+	case 0x97:
+	case 0xA7:
+	case 0xB7:
+	case 0xC7:
+	case 0xD7:
+	case 0xE7:
+	case 0xF7:
+		pc++;
+		READ_LOW( data ) |= 1 << ((opcode >> 4) - 8);
+		goto loop;
+	
+// Load/store
+	
+	case 0x9E: // STZ abs,x
+		data += x;
+	case 0x9C: // STZ abs
+		ADD_PAGE( data );
+		pc++;
+		FLUSH_TIME();
+		WRITE( data, 0 );
+		CACHE_TIME();
+		goto loop;
+	
+	case 0x74: // STZ zp,x
+		data = (uint8_t) (data + x);
+	case 0x64: // STZ zp
+		pc++;
+		WRITE_LOW( data, 0 );
+		goto loop;
+	
+	case 0x94: // STY zp,x
+		data = (uint8_t) (data + x);
+	case 0x84: // STY zp
+		pc++;
+		WRITE_LOW( data, y );
+		goto loop;
+	
+	case 0x96: // STX zp,y
+		data = (uint8_t) (data + y);
+	case 0x86: // STX zp
+		pc++;
+		WRITE_LOW( data, x );
+		goto loop;
+	
+	case 0xB6: // LDX zp,y
+		data = (uint8_t) (data + y);
+	case 0xA6: // LDX zp
+		data = READ_LOW( data );
+	case 0xA2: // LDX #imm
+		pc++;
+		x = data;
+		nz = data;
+		goto loop;
+	
+	case 0xB4: // LDY zp,x
+		data = (uint8_t) (data + x);
+	case 0xA4: // LDY zp
+		data = READ_LOW( data );
+	case 0xA0: // LDY #imm
+		pc++;
+		y = data;
+		nz = data;
+		goto loop;
+	
+	case 0xBC: // LDY abs,X
+		data += x;
+		PAGE_CROSS_PENALTY( data );
+	case 0xAC:{// LDY abs
+		fuint16 addr = data + 0x100 * GET_MSB();
+		pc += 2;
+		FLUSH_TIME();
+		y = nz = READ( addr );
+		CACHE_TIME();
+		goto loop;
+	}
+	
+	{
+		fuint8 temp;
+	case 0x8C: // STY abs
+		temp = y;
+		goto store_abs;
+	
+	case 0x8E: // STX abs
+		temp = x;
+	store_abs:
+		{
+			fuint16 addr = GET_ADDR();
+			pc += 2;
+			FLUSH_TIME();
+			WRITE( addr, temp );
+			CACHE_TIME();
+			goto loop;
+		}	
+	}
+
+// Compare
+
+	case 0xEC:{// CPX abs
+		fuint16 addr = GET_ADDR();
+		pc++;
+		FLUSH_TIME();
+		data = READ( addr );
+		CACHE_TIME();
+		goto cpx_data;
+	}
+	
+	case 0xE4: // CPX zp
+		data = READ_LOW( data );
+	case 0xE0: // CPX #imm
+	cpx_data:
+		nz = x - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xFF;
+		goto loop;
+	
+	case 0xCC:{// CPY abs
+		fuint16 addr = GET_ADDR();
+		pc++;
+		FLUSH_TIME();
+		data = READ( addr );
+		CACHE_TIME();
+		goto cpy_data;
+	}
+	
+	case 0xC4: // CPY zp
+		data = READ_LOW( data );
+	case 0xC0: // CPY #imm
+	cpy_data:
+		nz = y - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xFF;
+		goto loop;
+	
+// Logical
+
+#define ARITH_ADDR_MODES( op )\
+	case op - 0x04: /* (ind,x) */\
+		data = (uint8_t) (data + x);\
+	case op + 0x0D: /* (ind) */\
+		data = 0x100 * READ_LOW( (uint8_t) (data + 1) ) + READ_LOW( data );\
+		goto ptr##op;\
+	case op + 0x0C:{/* (ind),y */\
+		fuint16 temp = READ_LOW( data ) + y;\
+		PAGE_CROSS_PENALTY( temp );\
+		data = temp + 0x100 * READ_LOW( (uint8_t) (data + 1) );\
+		goto ptr##op;\
+	}\
+	case op + 0x10: /* zp,X */\
+		data = (uint8_t) (data + x);\
+	case op + 0x00: /* zp */\
+		data = READ_LOW( data );\
+		goto imm##op;\
+	case op + 0x14: /* abs,Y */\
+		data += y;\
+		goto ind##op;\
+	case op + 0x18: /* abs,X */\
+		data += x;\
+	ind##op:\
+		PAGE_CROSS_PENALTY( data );\
+	case op + 0x08: /* abs */\
+		ADD_PAGE( data );\
+	ptr##op:\
+		FLUSH_TIME();\
+		data = READ( data );\
+		CACHE_TIME();\
+	case op + 0x04: /* imm */\
+	imm##op:
+
+	ARITH_ADDR_MODES( 0xC5 ) // CMP
+		nz = a - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xFF;
+		goto loop;
+	
+	ARITH_ADDR_MODES( 0x25 ) // AND
+		nz = (a &= data);
+		pc++;
+		goto loop;
+	
+	ARITH_ADDR_MODES( 0x45 ) // EOR
+		nz = (a ^= data);
+		pc++;
+		goto loop;
+	
+	ARITH_ADDR_MODES( 0x05 ) // ORA
+		nz = (a |= data);
+		pc++;
+		goto loop;
+	
+// Add/subtract
+
+	ARITH_ADDR_MODES( 0xE5 ) // SBC
+		data ^= 0xFF;
+		goto adc_imm;
+	
+	ARITH_ADDR_MODES( 0x65 ) // ADC
+	adc_imm: {
+		if ( status & st_d ) {
+			dprintf( "Decimal mode not supported\n" );
+		}
+		fint16 carry = c >> 8 & 1;
+		fint16 ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
+		status &= ~st_v;
+		status |= ov >> 2 & 0x40;
+		c = nz = a + data + carry;
+		pc++;
+		a = (uint8_t) nz;
+		goto loop;
+	}
+	
+// Shift/rotate
+
+	case 0x4A: // LSR A
+		c = 0;
+	case 0x6A: // ROR A
+		nz = c >> 1 & 0x80;
+		c = a << 8;
+		nz |= a >> 1;
+		a = nz;
+		goto loop;
+
+	case 0x0A: // ASL A
+		nz = a << 1;
+		c = nz;
+		a = (uint8_t) nz;
+		goto loop;
+
+	case 0x2A: { // ROL A
+		nz = a << 1;
+		fint16 temp = c >> 8 & 1;
+		c = nz;
+		nz |= temp;
+		a = (uint8_t) nz;
+		goto loop;
+	}
+	
+	case 0x5E: // LSR abs,X
+		data += x;
+	case 0x4E: // LSR abs
+		c = 0;
+	case 0x6E: // ROR abs
+	ror_abs: {
+		ADD_PAGE( data );
+		FLUSH_TIME();
+		int temp = READ( data );
+		nz = (c >> 1 & 0x80) | (temp >> 1);
+		c = temp << 8;
+		goto rotate_common;
+	}
+	
+	case 0x3E: // ROL abs,X
+		data += x;
+		goto rol_abs;
+	
+	case 0x1E: // ASL abs,X
+		data += x;
+	case 0x0E: // ASL abs
+		c = 0;
+	case 0x2E: // ROL abs
+	rol_abs:
+		ADD_PAGE( data );
+		nz = c >> 8 & 1;
+		FLUSH_TIME();
+		nz |= (c = READ( data ) << 1);
+	rotate_common:
+		pc++;
+		WRITE( data, (uint8_t) nz );
+		CACHE_TIME();
+		goto loop;
+	
+	case 0x7E: // ROR abs,X
+		data += x;
+		goto ror_abs;
+	
+	case 0x76: // ROR zp,x
+		data = (uint8_t) (data + x);
+		goto ror_zp;
+	
+	case 0x56: // LSR zp,x
+		data = (uint8_t) (data + x);
+	case 0x46: // LSR zp
+		c = 0;
+	case 0x66: // ROR zp
+	ror_zp: {
+		int temp = READ_LOW( data );
+		nz = (c >> 1 & 0x80) | (temp >> 1);
+		c = temp << 8;
+		goto write_nz_zp;
+	}
+	
+	case 0x36: // ROL zp,x
+		data = (uint8_t) (data + x);
+		goto rol_zp;
+	
+	case 0x16: // ASL zp,x
+		data = (uint8_t) (data + x);
+	case 0x06: // ASL zp
+		c = 0;
+	case 0x26: // ROL zp
+	rol_zp:
+		nz = c >> 8 & 1;
+		nz |= (c = READ_LOW( data ) << 1);
+		goto write_nz_zp;
+	
+// Increment/decrement
+
+#define INC_DEC_AXY( reg, n ) reg = (uint8_t) (nz = reg + n); goto loop;
+
+	case 0x1A: // INA
+		INC_DEC_AXY( a, +1 )
+	
+	case 0xE8: // INX
+		INC_DEC_AXY( x, +1 )
+	
+	case 0xC8: // INY
+		INC_DEC_AXY( y, +1 )
+
+	case 0x3A: // DEA
+		INC_DEC_AXY( a, -1 )
+	
+	case 0xCA: // DEX
+		INC_DEC_AXY( x, -1 )
+	
+	case 0x88: // DEY
+		INC_DEC_AXY( y, -1 )
+	
+	case 0xF6: // INC zp,x
+		data = (uint8_t) (data + x);
+	case 0xE6: // INC zp
+		nz = 1;
+		goto add_nz_zp;
+	
+	case 0xD6: // DEC zp,x
+		data = (uint8_t) (data + x);
+	case 0xC6: // DEC zp
+		nz = (unsigned) -1;
+	add_nz_zp:
+		nz += READ_LOW( data );
+	write_nz_zp:
+		pc++;
+		WRITE_LOW( data, nz );
+		goto loop;
+	
+	case 0xFE: // INC abs,x
+		data = x + GET_ADDR();
+		goto inc_ptr;
+	
+	case 0xEE: // INC abs
+		data = GET_ADDR();
+	inc_ptr:
+		nz = 1;
+		goto inc_common;
+	
+	case 0xDE: // DEC abs,x
+		data = x + GET_ADDR();
+		goto dec_ptr;
+	
+	case 0xCE: // DEC abs
+		data = GET_ADDR();
+	dec_ptr:
+		nz = (unsigned) -1;
+	inc_common:
+		FLUSH_TIME();
+		nz += READ( data );
+		pc += 2;
+		WRITE( data, (uint8_t) nz );
+		CACHE_TIME();
+		goto loop;
+		
+// Transfer
+
+	case 0xA8: // TAY
+		y  = a;
+		nz = a;
+		goto loop;
+	
+	case 0x98: // TYA
+		a  = y;
+		nz = y;
+		goto loop;
+	
+	case 0xAA: // TAX
+		x  = a;
+		nz = a;
+		goto loop;
+		
+	case 0x8A: // TXA
+		a  = x;
+		nz = x;
+		goto loop;
+
+	case 0x9A: // TXS
+		SET_SP( x ); // verified (no flag change)
+		goto loop;
+	
+	case 0xBA: // TSX
+		x = nz = GET_SP();
+		goto loop;
+	
+	#define SWAP_REGS( r1, r2 ) {\
+		fuint8 t = r1;\
+		r1 = r2;\
+		r2 = t;\
+		goto loop;\
+	}
+	
+	case 0x02: // SXY
+		SWAP_REGS( x, y );
+	
+	case 0x22: // SAX
+		SWAP_REGS( a, x );
+	
+	case 0x42: // SAY
+		SWAP_REGS( a, y );
+	
+	case 0x62: // CLA
+		a = 0;
+		goto loop;
+	
+	case 0x82: // CLX
+		x = 0;
+		goto loop;
+	
+	case 0xC2: // CLY
+		y = 0;
+		goto loop;
+	
+// Stack
+	
+	case 0x48: // PHA
+		PUSH( a );
+		goto loop;
+		
+	case 0xDA: // PHX
+		PUSH( x );
+		goto loop;
+		
+	case 0x5A: // PHY
+		PUSH( y );
+		goto loop;
+		
+	case 0x40:{// RTI
+		fuint8 temp = READ_LOW( sp );
+		pc  = READ_LOW( 0x100 | (sp - 0xFF) );
+		pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
+		sp = (sp - 0xFD) | 0x100;
+		data = status;
+		SET_STATUS( temp );
+		r->status = status; // update externally-visible I flag
+		if ( (data ^ status) & st_i )
+		{
+			hes_time_t new_time = cpu->end_time;
+			if ( !(status & st_i) && new_time > cpu->irq_time )
+				new_time = cpu->irq_time;
+			blargg_long delta = s.base - new_time;
+			s.base = new_time;
+			s_time += delta;
+		}
+		goto loop;
+	}
+	
+	#define POP()  READ_LOW( sp ); sp = (sp - 0xFF) | 0x100
+	
+	case 0x68: // PLA
+		a = nz = POP();
+		goto loop;
+	
+	case 0xFA: // PLX
+		x = nz = POP();
+		goto loop;
+	
+	case 0x7A: // PLY
+		y = nz = POP();
+		goto loop;
+	
+	case 0x28:{// PLP
+		fuint8 temp = POP();
+		fuint8 changed = status ^ temp;
+		SET_STATUS( temp );
+		if ( !(changed & st_i) )
+			goto loop; // I flag didn't change
+		if ( status & st_i )
+			goto handle_sei;
+		goto handle_cli;
+	}
+	#undef POP
+	
+	case 0x08: { // PHP
+		fuint8 temp;
+		CALC_STATUS( temp );
+		PUSH( temp | st_b );
+		goto loop;
+	}
+	
+// Flags
+
+	case 0x38: // SEC
+		c = (unsigned) ~0;
+		goto loop;
+	
+	case 0x18: // CLC
+		c = 0;
+		goto loop;
+		
+	case 0xB8: // CLV
+		status &= ~st_v;
+		goto loop;
+	
+	case 0xD8: // CLD
+		status &= ~st_d;
+		goto loop;
+	
+	case 0xF8: // SED
+		status |= st_d;
+		goto loop;
+	
+	case 0x58: // CLI
+		if ( !(status & st_i) )
+			goto loop;
+		status &= ~st_i;
+	handle_cli: {
+		r->status = status; // update externally-visible I flag
+		blargg_long delta = s.base - cpu->irq_time;
+		if ( delta <= 0 )
+		{
+			if ( TIME < cpu->irq_time )
+				goto loop;
+			goto delayed_cli;
+		}
+		s.base = cpu->irq_time;
+		s_time += delta;
+		if ( s_time < 0 )
+			goto loop;
+		
+		if ( delta >= s_time + 1 )
+		{
+			// delayed irq until after next instruction
+			s.base += s_time + 1;
+			s_time = -1;
+			cpu->irq_time = s.base; // TODO: remove, as only to satisfy debug check in loop
+			goto loop;
+		}
+	delayed_cli:
+		dprintf( "Delayed CLI not supported\n" ); // TODO: implement
+		goto loop;
+	}
+	
+	case 0x78: // SEI
+		if ( status & st_i )
+			goto loop;
+		status |= st_i;
+	handle_sei: {
+		r->status = status; // update externally-visible I flag
+		blargg_long delta = s.base - cpu->end_time;
+		s.base = cpu->end_time;
+		s_time += delta;
+		if ( s_time < 0 )
+			goto loop;
+		dprintf( "Delayed SEI not supported\n" ); // TODO: implement
+		goto loop;
+	}
+	
+// Special
+	
+	case 0x53:{// TAM
+		fuint8 const bits = data; // avoid using data across function call
+		pc++;
+		int i;
+		for ( i = 0; i < 8; i++ )
+			if ( bits & (1 << i) )
+				/* this->cpu.set_mmr( i, a ); */
+				Cpu_set_mmr( this, i, a );
+		goto loop;
+	}
+	
+	case 0x43:{// TMA
+		pc++;
+		byte const* in = cpu->mmr;
+		do
+		{
+			if ( data & 1 )
+				a = *in;
+			in++;
+		}
+		while ( (data >>= 1) != 0 );
+		goto loop;
+	}
+	
+	case 0x03: // ST0
+	case 0x13: // ST1
+	case 0x23:{// ST2
+		fuint16 addr = opcode >> 4;
+		if ( addr )
+			addr++;
+		pc++;
+		FLUSH_TIME();
+		CPU_WRITE_VDP( this, addr, data, TIME );
+		CACHE_TIME();
+		goto loop;
+	}
+	
+	case 0xEA: // NOP
+		goto loop;
+
+	case 0x54: // CSL
+		dprintf( "CSL not supported\n" );
+		illegal_encountered = true;
+		goto loop;
+	
+	case 0xD4: // CSH
+		goto loop;
+	
+	case 0xF4: { // SET
+		//fuint16 operand = GET_MSB();
+		dprintf( "SET not handled\n" );
+		//switch ( data )
+		//{
+		//}
+		illegal_encountered = true;
+		goto loop;
+	}
+	
+// Block transfer
+
+	{
+		fuint16 in_alt;
+		fint16 in_inc;
+		fuint16 out_alt;
+		fint16 out_inc;
+		
+	case 0xE3: // TIA
+		in_alt  = 0;
+		goto bxfer_alt;
+	
+	case 0xF3: // TAI
+		in_alt  = 1;
+	bxfer_alt:
+		in_inc  = in_alt ^ 1;
+		out_alt = in_inc;
+		out_inc = in_alt;
+		goto bxfer;
+	
+	case 0xD3: // TIN
+		in_inc  = 1;
+		out_inc = 0;
+		goto bxfer_no_alt;
+	
+	case 0xC3: // TDD
+		in_inc  = -1;
+		out_inc = -1;
+		goto bxfer_no_alt;
+	
+	case 0x73: // TII
+		in_inc  = 1;
+		out_inc = 1;
+	bxfer_no_alt:
+		in_alt  = 0;
+		out_alt = 0;
+	bxfer: {
+			fuint16 in    = GET_LE16( instr + 0 );
+			fuint16 out   = GET_LE16( instr + 2 );
+			int     count = GET_LE16( instr + 4 );
+			if ( !count )
+				count = 0x10000;
+			pc += 6;
+			WRITE_LOW( 0x100 | (sp - 1), y );
+			WRITE_LOW( 0x100 | (sp - 2), a );
+			WRITE_LOW( 0x100 | (sp - 3), x );
+			FLUSH_TIME();
+			do
+			{
+				// TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O
+				fuint8 t = READ( in );
+				in += in_inc;
+				in &= 0xFFFF;
+				s.time += 6;
+				if ( in_alt )
+					in_inc = -in_inc;
+				WRITE( out, t );
+				out += out_inc;
+				out &= 0xFFFF;
+				if ( out_alt )
+					out_inc = -out_inc;
+			}
+			while ( --count );
+			CACHE_TIME();
+			goto loop;
+		}
+	}
+
+// Illegal
+
+	default:
+		assert( (unsigned) opcode <= 0xFF );
+		dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
+		illegal_encountered = true;
+		goto loop;
+	}
+	assert( false );
+	
+	int result_;
+handle_brk:
+	pc++;
+	result_ = 6;
+	
+interrupt:
+	{
+		s_time += 7;
+		
+		WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
+		WRITE_LOW( 0x100 | (sp - 2), pc );
+		pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ );
+		
+		sp = (sp - 3) | 0x100;
+		fuint8 temp;
+		CALC_STATUS( temp );
+		if ( result_ == 6 )
+			temp |= st_b;
+		WRITE_LOW( sp, temp );
+		
+		status &= ~st_d;
+		status |= st_i;
+		r->status = status; // update externally-visible I flag
+		
+		blargg_long delta = s.base - cpu->end_time;
+		s.base = cpu->end_time;
+		s_time += delta;
+		goto loop;
+	}
+	
+idle_done:
+	s_time = 0;
+out_of_time:
+	pc--;
+	FLUSH_TIME();
+	CPU_DONE( this, TIME, result_ );
+	CACHE_TIME();
+	if ( result_ > 0 )
+		goto interrupt;
+	if ( s_time < 0 )
+		goto loop;
+	
+	s.time = s_time;
+	
+	r->pc = pc;
+	r->sp = GET_SP();
+	r->a = a;
+	r->x = x;
+	r->y = y;
+	
+	{
+		fuint8 temp;
+		CALC_STATUS( temp );
+		r->status = temp;
+	}
+	
+	cpu->state_ = s;
+	cpu->state = &cpu->state_;
+	
+	return illegal_encountered;
+}
+
diff --git a/apps/codecs/libgme/hes_cpu.h b/apps/codecs/libgme/hes_cpu.h
new file mode 100644
index 0000000..f3bcf7d
--- /dev/null
+++ b/apps/codecs/libgme/hes_cpu.h
@@ -0,0 +1,95 @@
+// PC Engine CPU emulator for use with HES music files
+
+// Game_Music_Emu 0.5.2
+#ifndef HES_CPU_H
+#define HES_CPU_H
+
+#include "blargg_common.h"
+
+typedef blargg_long hes_time_t; // clock cycle count
+typedef unsigned hes_addr_t; // 16-bit address
+
+struct Hes_Emu;
+
+enum { future_hes_time = LONG_MAX / 2 + 1 };
+enum { page_size = 0x2000 };
+enum { page_shift = 13 };
+enum { page_count = 8 };
+
+// Attempt to execute instruction here results in CPU advancing time to
+// lesser of irq_time() and end_time() (or end_time() if IRQs are
+// disabled)
+enum { idle_addr = 0x1FFF };
+	
+// Can read this many bytes past end of a page
+enum { cpu_padding = 8 };
+enum { irq_inhibit = 0x04 };
+
+
+// Cpu state
+struct state_t {
+	uint8_t const* code_map [page_count + 1];
+	hes_time_t base;
+	blargg_long time;
+};
+
+// Cpu registers
+struct registers_t {
+	uint16_t pc;
+	uint8_t a;
+	uint8_t x;
+	uint8_t y;
+	uint8_t status;
+	uint8_t sp;
+};
+
+struct Hes_Cpu {
+	struct registers_t r;
+
+	hes_time_t irq_time;
+	hes_time_t end_time;
+	
+	struct state_t* state; // points to state_ or a local copy within run()
+	struct state_t state_;
+	
+	// page mapping registers
+	uint8_t mmr [page_count + 1];
+	uint8_t ram [page_size];
+};
+
+// Init cpu state
+void Cpu_init( struct Hes_Cpu* this );
+
+// Reset hes cpu
+void Cpu_reset( struct Hes_Cpu* this );
+
+// Set end_time and run CPU from current time. Returns true if any illegal
+// instructions were encountered.
+bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time ) ICODE_ATTR;
+
+void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank ) ICODE_ATTR;
+
+// Time of ning of next instruction to be executed
+static inline hes_time_t Cpu_time( struct Hes_Cpu* this )
+{
+	return this->state->time + this->state->base;
+}
+
+static inline uint8_t const* Cpu_get_code( struct Hes_Cpu* this, hes_addr_t addr )
+{
+	return this->state->code_map [addr >> page_shift] + addr
+	#if !defined (BLARGG_NONPORTABLE)
+		% (unsigned) page_size
+	#endif
+	;
+}
+
+static inline int Cpu_update_end_time( struct Hes_Cpu* this, uint8_t reg_status, hes_time_t t, hes_time_t irq )
+{
+	if ( irq < t && !(reg_status & irq_inhibit) ) t = irq;
+	int delta = this->state->base - t;
+	this->state->base = t;
+	return delta;
+}
+
+#endif
diff --git a/apps/codecs/libgme/hes_cpu_io.h b/apps/codecs/libgme/hes_cpu_io.h
new file mode 100644
index 0000000..6b49c69
--- /dev/null
+++ b/apps/codecs/libgme/hes_cpu_io.h
@@ -0,0 +1,72 @@
+
+#include "hes_emu.h"
+
+#include "blargg_source.h"
+
+int Cpu_read( struct Hes_Emu* this, hes_addr_t addr )
+{
+	check( addr <= 0xFFFF );
+	int result = *Cpu_get_code( &this->cpu, addr );
+	if ( this->cpu.mmr [addr >> page_shift] == 0xFF )
+		result = Emu_cpu_read( this, addr );
+	return result;
+}
+
+void Cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
+{
+	check( addr <= 0xFFFF );
+	byte* out = this->write_pages [addr >> page_shift];
+	addr &= page_size - 1;
+	if ( out )
+		out [addr] = data;
+	else if ( this->cpu.mmr [addr >> page_shift] == 0xFF )
+		Emu_cpu_write( this, addr, data );
+}
+
+#define CPU_READ_FAST( emu, addr, time, out ) \
+	CPU_READ_FAST_( emu, addr, time, out )
+
+#define CPU_READ_FAST_( emu, addr, time, out ) \
+{\
+	out = READ_PROG( addr );\
+	if ( emu->cpu.mmr [addr >> page_shift] == 0xFF )\
+	{\
+		FLUSH_TIME();\
+		out = Emu_cpu_read( emu, addr );\
+		CACHE_TIME();\
+	}\
+}
+
+#define CPU_WRITE_FAST( emu, addr, data, time ) \
+	CPU_WRITE_FAST_( emu, addr, data, time )
+
+#define CPU_WRITE_FAST_( emu, addr, data, time ) \
+{\
+	byte* out = emu->write_pages [addr >> page_shift];\
+	addr &= page_size - 1;\
+	if ( out )\
+	{\
+		out [addr] = data;\
+	}\
+	else if ( emu->cpu.mmr [addr >> page_shift] == 0xFF )\
+	{\
+		FLUSH_TIME();\
+		Emu_cpu_write( emu, addr, data );\
+		CACHE_TIME();\
+	}\
+}
+
+#define CPU_READ( emu, addr, time ) \
+	Cpu_read( emu, addr )
+
+#define CPU_WRITE( emu, addr, data, time ) \
+	Cpu_write( emu, addr, data )
+
+#define CPU_WRITE_VDP( emu, addr, data, time ) \
+	Cpu_write_vdp( emu, addr, data )
+
+#define CPU_SET_MMR( emu, page, bank ) \
+	Emu_cpu_set_mmr( emu, page, bank )
+
+#define CPU_DONE( emu, time, result_out ) \
+	result_out = Cpu_done( emu )
diff --git a/apps/codecs/libgme/hes_emu.c b/apps/codecs/libgme/hes_emu.c
new file mode 100644
index 0000000..a44eded
--- /dev/null
+++ b/apps/codecs/libgme/hes_emu.c
@@ -0,0 +1,877 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "hes_emu.h"
+
+#include "blargg_endian.h"
+#include "blargg_source.h"
+
+/* 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 */
+
+int const timer_mask  = 0x04;
+int const vdp_mask    = 0x02;
+int const i_flag_mask = 0x04;
+int const unmapped    = 0xFF;
+
+long const period_60hz = 262 * 455L; // scanlines * clocks per scanline
+
+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)
+
+const char gme_wrong_file_type [] ICONST_ATTR = "Wrong file type for this emulator";
+
+void clear_track_vars( struct Hes_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 Hes_init( struct Hes_Emu* this )
+{
+	this->sample_rate_ = 0;
+	this->mute_mask_   = 0;
+	this->tempo_       = 1.0;
+
+	// defaults
+	this->max_initial_silence = 2;
+	this->ignore_silence     = false;
+
+	// Unload
+	this->voice_count_ = 0;
+	clear_track_vars( this );
+
+	this->timer.raw_load = 0;
+	this->silence_lookahead = 6;
+	Sound_set_gain( this, 1.11 );
+
+	Rom_init( &this->rom, 0x2000 );
+
+	Apu_init( &this->apu );
+	Adpcm_init( &this->adpcm );
+	Cpu_init( &this->cpu );
+
+	/* Set default track count */
+	this->track_count = 255;
+}
+
+static blargg_err_t check_hes_header( void const* header )
+{
+	if ( memcmp( header, "HESM", 4 ) )
+		return gme_wrong_file_type;
+	return 0;
+}
+
+// Setup
+
+blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
+{
+	// Unload
+	this->voice_count_ = 0;
+	clear_track_vars( this );
+             
+	assert( offsetof (struct header_t,unused [4]) == header_size );
+	RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, unmapped ) );
+	
+	RETURN_ERR( check_hes_header( this->header.tag ) );
+	
+	/* if ( header_.vers != 0 )
+		warning( "Unknown file version" );
+	
+	if ( memcmp( header_.data_tag, "DATA", 4 ) )
+		warning( "Data header missing" );
+	
+	if ( memcmp( header_.unused, "\0\0\0\0", 4 ) )
+		warning( "Unknown header data" ); */
+	
+	// File spec supports multiple blocks, but I haven't found any, and
+	// many files have bad sizes in the only block, so it's simpler to
+	// just try to load the damn data as best as possible.
+	
+	long addr = get_le32( this->header.addr );
+	/* long rom_size = get_le32( this->header.size ); */
+	long const rom_max = 0x100000;
+	if ( addr & ~(rom_max - 1) )
+	{
+		/* warning( "Invalid address" ); */
+		addr &= rom_max - 1;
+	}
+	/* if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
+		warning( "Invalid size" );
+	
+	if ( rom_size != rom.file_size() )
+	{
+		if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) )
+			warning( "Multiple DATA not supported" );
+		else if ( size < rom.file_size() )
+			warning( "Extra file data" );
+		else
+			warning( "Missing file data" );
+	} */
+	
+	Rom_set_addr( &this->rom, addr );
+	
+	this->voice_count_ = osc_count + adpcm_osc_count;
+	
+	Apu_volume( &this->apu, this->gain_ );
+	Adpcm_volume( &this->adpcm, this->gain_ );
+
+    // Setup buffer	
+	this->clock_rate_ = 7159091;
+	Buffer_clock_rate( &this->stereo_buf, 7159091 );
+	this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
+	
+	Sound_set_tempo( this, this->tempo_ );
+	Sound_mute_voices( this, this->mute_mask_ );
+	
+	// Reset track count
+	this->track_count = 255;
+	this->m3u.size = 0;
+	return 0;
+}
+
+
+// Emulation
+
+void recalc_timer_load( struct Hes_Emu* this ) ICODE_ATTR;
+void recalc_timer_load( struct Hes_Emu* this )
+{
+	this->timer.load = this->timer.raw_load * this->timer_base + 1;
+}
+
+// Hardware
+
+void irq_changed( struct Hes_Emu* this ) ICODE_ATTR;
+void run_until( struct Hes_Emu* this, hes_time_t present ) ICODE_ATTR;
+void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
+{
+	switch ( addr )
+	{
+	case 0:
+		this->vdp.latch = data & 0x1F;
+		break;
+	
+	case 2:
+		if ( this->vdp.latch == 5 )
+		{
+			/* if ( data & 0x04 )
+				warning( "Scanline interrupt unsupported" ); */
+			run_until( this, Cpu_time( &this->cpu ) );
+			this->vdp.control = data;
+			irq_changed( this );
+		}
+		else
+		{
+			dprintf( "VDP not supported: $%02X <- $%02X\n", this->vdp.latch, data );
+		}
+		break;
+	
+	case 3:
+		dprintf( "VDP MSB not supported: $%02X <- $%02X\n", this->vdp.latch, data );
+		break;
+	}
+}
+
+int Cpu_done( struct Hes_Emu* this )
+{
+	check( time() >= end_time() ||
+			(!(r.status & i_flag_mask) && time() >= irq_time()) );
+	
+	if ( !(this->cpu.r.status & i_flag_mask) )
+	{
+		hes_time_t present = Cpu_time( &this->cpu );
+		
+		if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
+		{
+			this->timer.fired = true;
+			this->irq.timer = future_hes_time;
+			irq_changed( this ); // overkill, but not worth writing custom code
+			#if defined (GME_FRAME_HOOK_DEFINED)
+			{
+				unsigned const threshold = period_60hz / 30;
+				unsigned long elapsed = present - last_frame_hook;
+				if ( elapsed - period_60hz + threshold / 2 < threshold )
+				{
+					last_frame_hook = present;
+					GME_FRAME_HOOK( this );
+				}
+			}
+			#endif
+			return 0x0A;
+		}
+		
+		if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
+		{
+			// work around for bugs with music not acknowledging VDP
+			//run_until( present );
+			//irq.vdp = future_hes_time;
+			//irq_changed();
+			#if defined(GME_FRAME_HOOK_DEFINED)
+				last_frame_hook = present;
+				GME_FRAME_HOOK( this );
+			#endif
+			return 0x08;
+		}
+	}
+	return 0;
+}
+
+void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
+{
+	hes_time_t time = Cpu_time( &this->cpu );
+	if ( (unsigned) (addr - start_addr) <= end_addr - start_addr )
+	{
+		GME_APU_HOOK( this, addr - apu.start_addr, data );
+		// avoid going way past end when a long block xfer is writing to I/O space
+		hes_time_t t = min( time, this->cpu.end_time + 8 );
+		Apu_write_data( &this->apu, t, addr, data );
+		return;
+	}
+	
+	if ( (unsigned) (addr - io_addr) < io_size )
+	{
+		hes_time_t t = min( time, this->cpu.end_time + 6 );
+		Adpcm_write_data( &this->adpcm, t, addr, data );
+		return;
+	}
+	
+	switch ( addr )
+	{
+	case 0x0000:
+	case 0x0002:
+	case 0x0003:
+		Cpu_write_vdp( this, addr, data );
+		return;
+	
+	case 0x0C00: {
+		run_until( this, time );
+		this->timer.raw_load = (data & 0x7F) + 1;
+		recalc_timer_load( this );
+		this->timer.count = this->timer.load;
+		break;
+	}
+	
+	case 0x0C01:
+		data &= 1;
+		if ( this->timer.enabled == data )
+			return;
+		run_until( this, time );
+		this->timer.enabled = data;
+		if ( data )
+			this->timer.count = this->timer.load;
+		break;
+	
+	case 0x1402:
+		run_until( this, time );
+		this->irq.disables = data;
+		
+		// flag questionable values
+		if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) {
+			dprintf( "Int mask: $%02X\n", data );
+		}
+		break;
+	
+	case 0x1403:
+		run_until( this, time );
+		if ( this->timer.enabled )
+			this->timer.count = this->timer.load;
+		this->timer.fired = false;
+		break;
+	
+#ifndef NDEBUG
+	case 0x1000: // I/O port
+	case 0x0402: // palette
+	case 0x0403:
+	case 0x0404:
+	case 0x0405:
+		return;
+		
+	default:
+		dprintf( "unmapped write $%04X <- $%02X\n", addr, data );
+		return;
+#endif
+	}
+	
+	irq_changed( this );
+}
+
+int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
+{
+	hes_time_t time = Cpu_time( &this->cpu );
+	addr &= page_size - 1;
+	switch ( addr )
+	{
+	case 0x0000:
+		if ( this->irq.vdp > time )
+			return 0;
+		this->irq.vdp = future_hes_time;
+		run_until( this, time );
+		irq_changed( this );
+		return 0x20;
+		
+	case 0x0002:
+	case 0x0003:
+		dprintf( "VDP read not supported: %d\n", addr );
+		return 0;
+	
+	case 0x0C01:
+		//return timer.enabled; // TODO: remove?
+	case 0x0C00:
+		run_until( this, time );
+		dprintf( "Timer count read\n" );
+		return (unsigned) (this->timer.count - 1) / this->timer_base;
+	
+	case 0x1402:
+		return this->irq.disables;
+	
+	case 0x1403:
+		{
+			int status = 0;
+			if ( this->irq.timer <= time ) status |= timer_mask;
+			if ( this->irq.vdp   <= time ) status |= vdp_mask;
+			return status;
+		}
+		
+	case 0x180A:
+	case 0x180B:
+	case 0x180C:
+	case 0x180D:
+		return Adpcm_read_data( &this->adpcm, time, addr );
+		
+	#ifndef NDEBUG
+		case 0x1000: // I/O port
+		// case 0x180C: // CD-ROM
+		// case 0x180D:
+			break;
+		
+		default:
+			dprintf( "unmapped read  $%04X\n", addr );
+	#endif
+	}
+	
+	return unmapped;
+}
+
+// see hes_cpu_io.h for core read/write functions
+
+// Emulation
+
+void run_until( struct Hes_Emu* this, hes_time_t present )
+{
+	while ( this->vdp.next_vbl < present )
+		this->vdp.next_vbl += this->play_period;
+	
+	hes_time_t elapsed = present - this->timer.last_time;
+	if ( elapsed > 0 )
+	{
+		if ( this->timer.enabled )
+		{
+			this->timer.count -= elapsed;
+			if ( this->timer.count <= 0 )
+				this->timer.count += this->timer.load;
+		}
+		this->timer.last_time = present;
+	}
+}
+
+void irq_changed( struct Hes_Emu* this )
+{
+	hes_time_t present = Cpu_time( &this->cpu );
+	
+	if ( this->irq.timer > present )
+	{
+		this->irq.timer = future_hes_time;
+		if ( this->timer.enabled && !this->timer.fired )
+			this->irq.timer = present + this->timer.count;
+	}
+	
+	if ( this->irq.vdp > present )
+	{
+		this->irq.vdp = future_hes_time;
+		if ( this->vdp.control & 0x08 )
+			this->irq.vdp = this->vdp.next_vbl;
+	}
+	
+	hes_time_t time = future_hes_time;
+	if ( !(this->irq.disables & timer_mask) ) time = this->irq.timer;
+	if ( !(this->irq.disables &   vdp_mask) ) time = min( time, this->irq.vdp );
+	
+	// Set cpu irq time
+	this->cpu.state->time += Cpu_update_end_time( &this->cpu, this->cpu.r.status, 
+					this->cpu.end_time, (this->cpu.irq_time = time) );
+}
+
+static void adjust_time( blargg_long* time, hes_time_t delta ) ICODE_ATTR;
+static void adjust_time( blargg_long* time, hes_time_t delta )
+{
+	if ( *time < future_hes_time )
+	{
+		*time -= delta;
+		if ( *time < 0 )
+			*time = 0;
+	}
+}
+
+blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ ) ICODE_ATTR;
+blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
+{
+	blip_time_t duration = *duration_; // cache
+	
+	Cpu_run( this, duration );
+	/* warning( "Emulation error (illegal instruction)" ); */
+	
+	check( time() >= duration );
+	//check( time() - duration < 20 ); // Txx instruction could cause going way over
+	
+	run_until( this, duration );
+	
+	// end time frame
+	this->timer.last_time -= duration;
+	this->vdp.next_vbl    -= duration;
+	#if defined (GME_FRAME_HOOK_DEFINED)
+		last_frame_hook -= *duration;
+	#endif
+
+	// End cpu frame
+	this->cpu.state_.base -= duration;
+	if ( this->cpu.irq_time < future_hes_time ) this->cpu.irq_time -= duration;
+	if ( this->cpu.end_time < future_hes_time ) this->cpu.end_time -= duration;
+	
+	adjust_time( &this->irq.timer, duration );
+	adjust_time( &this->irq.vdp,   duration );
+	Apu_end_frame( &this->apu, duration );
+	Adpcm_end_frame( &this->adpcm, duration );
+	
+	return 0;
+}
+
+blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out ) ICODE_ATTR;
+blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out )
+{
+	long remain = count;
+	while ( remain )
+	{
+		remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
+		if ( remain )
+		{
+			if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
+			{
+				this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
+				// Remute voices
+				Sound_mute_voices( this, this->mute_mask_ );
+			}
+
+			int msec = Buffer_length( &this->stereo_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->stereo_buf, clocks_emulated );
+		}
+	}
+	return 0;
+}
+
+
+// Music emu
+
+blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long rate )
+{
+	require( !this->sample_rate_ ); // sample rate can't be changed once set
+	Buffer_init( &this->stereo_buf );
+	RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
+	
+	// Set bass frequency
+	Buffer_bass_freq( &this->stereo_buf, 60 );
+	
+	this->sample_rate_ = rate;
+	return 0;
+}
+
+void Sound_mute_voice( struct Hes_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 Hes_Emu* this, int mask )
+{
+	require( this->sample_rate_ ); // sample rate must be set first
+	this->mute_mask_ = mask;
+
+	// Set adpcm voice
+	struct channel_t ch = Buffer_channel( &this->stereo_buf );
+	if ( mask & (1 << this->voice_count_ ) )
+		Adpcm_set_output( &this->adpcm, 0, 0, 0, 0 );
+	else
+		Adpcm_set_output( &this->adpcm, 0, ch.center, ch.left, ch.right );
+	
+	// Set apu voices
+	int i = this->voice_count_ - 1;
+	for ( ; i--; )
+	{
+		if ( mask & (1 << i) )
+		{
+			Apu_osc_output( &this->apu, i, 0, 0, 0 );
+		}
+		else
+		{	
+			assert( (ch.center && ch.left && ch.right) ||
+					(!ch.center && !ch.left && !ch.right) ); // all or nothing
+			Apu_osc_output( &this->apu, i, ch.center, ch.left, ch.right );
+		}
+	}
+}
+
+void Sound_set_tempo( struct Hes_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->play_period = (hes_time_t) (period_60hz / t);
+	this->timer_base = (int) (1024 / t);
+	recalc_timer_load( this );
+	this->tempo_ = t;
+}
+
+void fill_buf( struct Hes_Emu* this ) ICODE_ATTR;
+blargg_err_t Hes_start_track( struct Hes_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];
+		track = e->track;
+	}
+	
+	this->current_track_ = track;
+	
+	Buffer_clear( &this->stereo_buf );
+	
+	memset( this->cpu.ram, 0, sizeof this->cpu.ram ); // some HES music relies on zero fill
+	memset( this->sgx, 0, sizeof this->sgx );
+	
+	Apu_reset( &this->apu );
+	Adpcm_reset( &this->adpcm );
+	Cpu_reset( &this->cpu );
+	
+	unsigned i;
+	for ( i = 0; i < sizeof this->header.banks; i++ )
+		Cpu_set_mmr( this, i, this->header.banks [i] );
+	Cpu_set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
+	
+	this->irq.disables  = timer_mask | vdp_mask;
+	this->irq.timer     = future_hes_time;
+	this->irq.vdp       = future_hes_time;
+	
+	this->timer.enabled = false;
+	this->timer.raw_load= 0x80;
+	this->timer.count   = this->timer.load;
+	this->timer.fired   = false;
+	this->timer.last_time = 0;
+	
+	this->vdp.latch     = 0;
+	this->vdp.control   = 0;
+	this->vdp.next_vbl  = 0;
+	
+	this->cpu.ram [0x1FF] = (idle_addr - 1) >> 8;
+	this->cpu.ram [0x1FE] = (idle_addr - 1) & 0xFF;
+	this->cpu.r.sp = 0xFD;
+	this->cpu.r.pc = get_le16( this->header.init_addr );
+	this->cpu.r.a  = track;
+	
+	recalc_timer_load( this );
+	this->last_frame_hook = 0;
+	
+	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;
+}
+
+// Tell/Seek
+
+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 Hes_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 Hes_Emu* this, long msec )
+{
+	blargg_long time = msec_to_samples( msec, this->sample_rate_ );
+	if ( time < this->out_time )
+		RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
+	return Track_skip( this, time - this->out_time );
+}
+
+blargg_err_t skip_( struct Hes_Emu* this, long count ) ICODE_ATTR;
+blargg_err_t skip_( struct Hes_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 Hes_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 Hes_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 ) ICODE_ATTR;
+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 Hes_Emu* this, long out_count, sample_t* out ) ICODE_ATTR;
+void handle_fade( struct Hes_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 Hes_Emu* this, long count, sample_t* out ) ICODE_ATTR;
+void emu_play( struct Hes_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 ) ICODE_ATTR;
+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 Hes_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 Hes_play( struct Hes_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 );
+		
+		// prints nifty graph of how far ahead we are when searching for silence
+		//dprintf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
+		
+		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;
+}
diff --git a/apps/codecs/libgme/hes_emu.h b/apps/codecs/libgme/hes_emu.h
new file mode 100644
index 0000000..18dbe0d
--- /dev/null
+++ b/apps/codecs/libgme/hes_emu.h
@@ -0,0 +1,229 @@
+// TurboGrafx-16/PC Engine HES music file emulator
+
+// Game_Music_Emu 0.5.2
+#ifndef HES_EMU_H
+#define HES_EMU_H
+
+#include "blargg_source.h"
+
+#include "multi_buffer.h"
+#include "rom_data.h"
+#include "hes_apu.h"
+#include "hes_apu_adpcm.h"
+#include "hes_cpu.h"
+#include "m3u_playlist.h"
+
+typedef short sample_t;
+
+enum { buf_size = 2048 };
+
+// HES file header
+enum { header_size = 0x20 };
+struct header_t
+{
+	byte tag [4];
+	byte vers;
+	byte first_track;
+	byte init_addr [2];
+	byte banks [8];
+	byte data_tag [4];
+	byte size [4];
+	byte addr [4];
+	byte unused [4];
+};
+
+
+struct timer_t {
+	hes_time_t last_time;
+	blargg_long count;
+	blargg_long load;
+	int raw_load;
+	byte enabled;
+	byte fired;
+};
+	
+struct vdp_t {
+	hes_time_t next_vbl;
+	byte latch;
+	byte control;
+};
+	
+struct irq_t {
+	hes_time_t timer;
+	hes_time_t vdp;
+	byte disables;
+};
+
+
+struct Hes_Emu {
+	hes_time_t play_period;
+	hes_time_t last_frame_hook;
+	int timer_base;
+	
+	struct timer_t timer;
+	struct vdp_t vdp;
+	struct irq_t irq;
+	
+	// Sound
+	long clock_rate_;
+	long sample_rate_;
+	unsigned buf_changed_count;
+	int voice_count_;
+	double tempo_;
+	double gain_;
+	
+	// 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
+	
+	// Larger files at the end
+	// Header for currently loaded file
+	struct header_t header;
+	
+	// M3u Playlist
+	struct M3u_Playlist m3u;
+	
+	// Hes Cpu
+	byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space	
+	struct Hes_Cpu cpu;
+	
+	struct Hes_Apu apu;
+	struct Hes_Apu_Adpcm adpcm;
+
+	struct Stereo_Buffer stereo_buf;
+	sample_t buf [buf_size];
+	
+	// rom & ram
+	struct Rom_Data rom;
+	byte sgx [3 * page_size + cpu_padding];
+};
+
+
+// Basic functionality
+// Initializes Hes_Emu structure
+void Hes_init( struct Hes_Emu* this );
+
+// Stops (clear) Hes_Emu structure
+void Hes_stop( struct Hes_Emu* this );
+
+// Loads a file from memory
+blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size );
+
+// Set output sample rate. Must be called only once before loading file.
+blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long sample_rate );
+
+// Start a track, where 0 is the first track. Also clears warning string.
+blargg_err_t Hes_start_track( struct Hes_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 Hes_play( struct Hes_Emu* this, long count, sample_t* buf ) ICODE_ATTR;
+
+// Track status/control
+// Number of milliseconds (1000 msec = 1 second) played since ning of track
+long Track_tell( struct Hes_Emu* this );
+
+// Seek to new time in track. Seeking backwards or far forward can take a while.
+blargg_err_t Track_seek( struct Hes_Emu* this, long msec );
+
+// Skip n samples
+blargg_err_t Track_skip( struct Hes_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 Hes_Emu* this, long start_msec, long length_msec );
+
+// Get track length in milliseconds
+static inline long Track_get_length( struct Hes_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;
+	} 
+	
+	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 Hes_Emu* this, double );
+
+// Mute/unmute voice i, where voice 0 is first voice
+void Sound_mute_voice( struct Hes_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 Hes_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 Hes_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)
+
+int Cpu_read( struct Hes_Emu* this, hes_addr_t ) ICODE_ATTR;
+void Cpu_write( struct Hes_Emu* this, hes_addr_t, int ) ICODE_ATTR;
+void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data ) ICODE_ATTR;
+int Cpu_done( struct Hes_Emu* this ) ICODE_ATTR;
+
+int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t ) ICODE_ATTR;
+void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t, int data ) ICODE_ATTR;
+
+static inline byte const* Emu_cpu_set_mmr( struct Hes_Emu* this, int page, int bank )
+{
+	this->write_pages [page] = 0;
+	if ( bank < 0x80 )
+		return Rom_at_addr( &this->rom, bank * (blargg_long) page_size );
+	
+	byte* data = 0;
+	switch ( bank )
+	{
+		case 0xF8:
+			data = this->cpu.ram;
+			break;
+		
+		case 0xF9:
+		case 0xFA:
+		case 0xFB:
+			data = &this->sgx [(bank - 0xF9) * page_size];
+			break;
+		
+		default:
+			if ( bank != 0xFF ) {
+				dprintf( "Unmapped bank $%02X\n", bank );
+			}
+			return this->rom.unmapped;
+	}
+	
+	this->write_pages [page] = data;
+	return data;
+}
+
+#endif
diff --git a/apps/codecs/libgme/inflate/bbfuncs.c b/apps/codecs/libgme/inflate/bbfuncs.c
new file mode 100644
index 0000000..3b23c3b
--- /dev/null
+++ b/apps/codecs/libgme/inflate/bbfuncs.c
@@ -0,0 +1,147 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "bbfuncs.h"
+
+#if defined(ROCKBOX)
+#include "codeclib.h"
+#endif
+
+void error_die(const char* msg)
+{	
+    (void)msg;
+}
+
+void error_msg(const char* msg)
+{
+	(void)msg;
+}
+
+size_t safe_read(struct mbreader_t *md, void *buf, size_t count)
+{
+    ssize_t n;
+
+    do {
+        n = mbread(md, buf, count);
+    } while (n < 0&&n!=-1);
+
+    return n;
+}
+
+/*
+ * Read all of the supplied buffer from a file This does multiple reads as
+ *necessary. Returns the amount read, or -1 on an error. A short read is
+ *returned on an end of file.
+ */
+ssize_t full_read(struct mbreader_t *md, void *buf, size_t len)
+{
+    ssize_t cc;
+    ssize_t total;
+
+    total = 0;
+
+    while (len)
+    {
+        cc = safe_read(md, buf, len);
+
+        if (cc < 0)
+            return cc;    /* read() returns -1 on failure. */
+
+        if (cc == 0)
+            break;
+
+        buf = ((char *)buf) + cc;
+        total += cc;
+        len -= cc;
+    }
+
+    return total;
+}
+
+/* Die with an error message if we can't read the entire buffer. */
+void xread(struct mbreader_t *md, void *buf, ssize_t count)
+{
+    if (count)
+    {
+        ssize_t size = full_read(md, buf, count);
+        if (size != count)
+            error_die("short read");
+    }
+}
+
+/* Die with an error message if we can't read one character. */
+unsigned char xread_char(struct mbreader_t *md)
+{
+    unsigned char tmp;
+
+    xread(md, &tmp, 1);
+
+    return tmp;
+}
+
+void check_header_gzip(struct mbreader_t *src_md)
+{
+    union {
+        unsigned char raw[8];
+        struct {
+            unsigned char method;
+            unsigned char flags;
+            unsigned int mtime;
+            unsigned char xtra_flags;
+            unsigned char os_flags;
+        } formatted;
+    } header;
+
+    xread(src_md, header.raw, 8);
+
+    /* Check the compression method */
+    if (header.formatted.method != 8)
+        error_die("Unknown compression method");
+
+    if (header.formatted.flags & 0x04)
+    {
+        /* bit 2 set: extra field present */
+        unsigned char extra_short;
+
+        extra_short = xread_char(src_md) + (xread_char(src_md) << 8);
+        while (extra_short > 0)
+        {
+            /* Ignore extra field */
+            xread_char(src_md);
+            extra_short--;
+        }
+    }
+
+    /* Discard original name if any */
+    if (header.formatted.flags & 0x08)
+        /* bit 3 set: original file name present */
+        while(xread_char(src_md) != 0) ;
+
+    /* Discard file comment if any */
+    if (header.formatted.flags & 0x10)
+        /* bit 4 set: file comment present */
+        while(xread_char(src_md) != 0) ;
+
+    /* Read the header checksum */
+    if (header.formatted.flags & 0x02)
+    {
+        xread_char(src_md);
+        xread_char(src_md);
+    }
+}
diff --git a/apps/codecs/libgme/inflate/bbfuncs.h b/apps/codecs/libgme/inflate/bbfuncs.h
new file mode 100644
index 0000000..fe03ec1
--- /dev/null
+++ b/apps/codecs/libgme/inflate/bbfuncs.h
@@ -0,0 +1,33 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef BBFUNCS_H
+#define BBFUNCS_H
+
+#include "mbreader.h"
+
+void error_die(const char* msg);
+void error_msg(const char* msg);
+size_t safe_read(struct mbreader_t *md, void *buf, size_t count);
+ssize_t full_read(struct mbreader_t *md, void *buf, size_t len);
+void xread(struct mbreader_t *md, void *buf, ssize_t count);
+unsigned char xread_char(struct mbreader_t *md);
+void check_header_gzip(struct mbreader_t *md);
+
+#endif
diff --git a/apps/codecs/libgme/inflate/inflate.c b/apps/codecs/libgme/inflate/inflate.c
new file mode 100644
index 0000000..807dee3
--- /dev/null
+++ b/apps/codecs/libgme/inflate/inflate.c
@@ -0,0 +1,1159 @@
+/*
+ * gunzip implementation for wikiviewer (c) Frederik M.J.V., 2006.
+ * some bug fixes by Adam Gashlin gunzip implementation for busybox
+ *
+ * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
+ * based on gzip sources
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
+ *well as stdin/stdout, and to generally behave itself wrt command line
+ *handling.
+ *
+ * General cleanup to better adhere to the style guide and make use of standard
+ *busybox functions by Glenn McGrath <bug1@iinet.net.au>
+ *
+ * read_gz interface + associated hacking by Laurence Anderson
+ *
+ * Fixed huft_build() so decoding end-of-block code does not grab more bits than
+ *necessary (this is required by unzip applet), added inflate_cleanup() to free
+ *leaked bytebuffer memory (used in unzip.c), and some minor style guide
+ *cleanups by Ed Clark
+ *
+ * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ *Copyright (C) 1992-1993 Jean-loup Gailly The unzip code was written and put in
+ *the public domain by Mark Adler. Portions of the lzw code are derived from the
+ *public domain 'compress'
+ * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
+ *Ken Turkowski, Dave Mack and Peter Jannesen.
+ *
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <inttypes.h>
+#ifndef NULL
+#define NULL 0
+#endif
+#define ENABLE_DESKTOP 0
+#define USE_DESKTOP(...)
+#include "mallocer.h"
+#include "bbfuncs.h"
+#include "inflate.h"
+#include "mallocer.h"
+
+#define TRIM_FILE_ON_ERROR 1
+
+typedef struct huft_s {
+    unsigned char e;    /* number of extra bits or operation */
+    unsigned char b;    /* number of bits in this code or subcode */
+    union {
+        unsigned short n;    /* literal, length base, or distance base */
+        struct huft_s *t;    /* pointer to next level of table */
+    } v;
+} huft_t;
+
+/*static void *mainmembuf;*/
+static void *huftbuffer1;
+static void *huftbuffer2;
+
+#define HUFT_MMP1 8
+#define HUFT_MMP2 9
+
+static struct mbreader_t *gunzip_src_md;
+static unsigned int gunzip_bytes_out;    /* number of output bytes */
+static unsigned int gunzip_outbuf_count;    /* bytes in output buffer */
+
+/* gunzip_window size--must be a power of two, and at least 32K for zip's
+   deflate method */
+enum {
+    gunzip_wsize = 0x8000
+};
+
+static unsigned char *gunzip_window;
+static uint32_t ifl_total;
+
+static uint32_t gunzip_crc;
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16    /* maximum bit length of any code (16 for explode) */
+#define N_MAX 288    /* maximum number of codes in any set */
+
+/* bitbuffer */
+static unsigned int gunzip_bb;    /* bit buffer */
+static unsigned char gunzip_bk;    /* bits in bit buffer */
+
+/* These control the size of the bytebuffer */
+static unsigned int bytebuffer_max = 0x8000;
+static unsigned char *bytebuffer = NULL;
+static unsigned int bytebuffer_offset = 0;
+static unsigned int bytebuffer_size = 0;
+
+static const unsigned short mask_bits[] = {
+    0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* Copy lengths for literal codes 257..285 */
+static const unsigned short cplens[] = {
+    3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+    67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+/* note: see note #13 above about the 258 in this list. */
+/* Extra bits for literal codes 257..285 */
+static const unsigned char cplext[] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+    5, 5, 5, 0, 99, 99
+};                        /* 99==invalid */
+
+/* Copy offsets for distance codes 0..29 */
+static const unsigned short cpdist[] = {
+    1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+    769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+/* Extra bits for distance codes */
+static const unsigned char cpdext[] = {
+    0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+    11, 11, 12, 12, 13, 13
+};
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+/* Order of the bit length code lengths */
+static const unsigned char border[] = {
+    16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+static const uint32_t crc_table[256]= {
+    0,1996959894,-301047508,-1727442502,124634137,1886057615,
+    -379345611,-1637575261,249268274,2044508324,-522852066,
+    -1747789432,162941995,2125561021,-407360249,-1866523247,
+    498536548,1789927666,-205950648,-2067906082,450548861,
+    1843258603,-187386543,-2083289657,325883990,1684777152,
+    -43845254,-1973040660,335633487,1661365465,-99664541,
+    -1928851979,997073096,1281953886,-715111964,-1570279054,
+    1006888145,1258607687,-770865667,-1526024853,901097722,
+    1119000684,-608450090,-1396901568,853044451,1172266101,
+    -589951537,-1412350631,651767980,1373503546,-925412992,
+    -1076862698,565507253,1454621731,-809855591,-1195530993,
+    671266974,1594198024,-972236366,-1324619484,795835527,
+    1483230225,-1050600021,-1234817731,1994146192,31158534,
+    -1731059524,-271249366,1907459465,112637215,-1614814043,
+    -390540237,2013776290,251722036,-1777751922,-519137256,
+    2137656763,141376813,-1855689577,-429695999,1802195444,
+    476864866,-2056965928,-228458418,1812370925,453092731,
+    -2113342271,-183516073,1706088902,314042704,-1950435094,
+    -54949764,1658658271,366619977,-1932296973,-69972891,
+    1303535960,984961486,-1547960204,-725929758,1256170817,
+    1037604311,-1529756563,-740887301,1131014506,879679996,
+    -1385723834,-631195440,1141124467,855842277,-1442165665,
+    -586318647,1342533948,654459306,-1106571248,-921952122,
+    1466479909,544179635,-1184443383,-832445281,1591671054,
+    702138776,-1328506846,-942167884,1504918807,783551873,
+    -1212326853,-1061524307,-306674912,-1698712650,62317068,
+    1957810842,-355121351,-1647151185,81470997,1943803523,
+    -480048366,-1805370492,225274430,2053790376,-468791541,
+    -1828061283,167816743,2097651377,-267414716,-2029476910,
+    503444072,1762050814,-144550051,-2140837941,426522225,
+    1852507879,-19653770,-1982649376,282753626,1742555852,
+    -105259153,-1900089351,397917763,1622183637,-690576408,
+    -1580100738,953729732,1340076626,-776247311,-1497606297,
+    1068828381,1219638859,-670225446,-1358292148,906185462,
+    1090812512,-547295293,-1469587627,829329135,1181335161,
+    -882789492,-1134132454,628085408,1382605366,-871598187,
+    -1156888829,570562233,1426400815,-977650754,-1296233688,
+    733239954,1555261956,-1026031705,-1244606671,752459403,
+    1541320221,-1687895376,-328994266,1969922972,40735498,
+    -1677130071,-351390145,1913087877,83908371,-1782625662,
+    -491226604,2075208622,213261112,-1831694693,-438977011,
+    2094854071,198958881,-2032938284,-237706686,1759359992,
+    534414190,-2118248755,-155638181,1873836001,414664567,
+    -2012718362,-15766928,1711684554,285281116,-1889165569,
+    -127750551,1634467795,376229701,-1609899400,-686959890,
+    1308918612,956543938,-1486412191,-799009033,1231636301,
+    1047427035,-1362007478,-640263460,1088359270,936918000,
+    -1447252397,-558129467,1202900863,817233897,-1111625188,
+    -893730166,1404277552,615818150,-1160759803,-841546093,
+    1423857449,601450431,-1285129682,-1000256840,1567103746,
+    711928724,-1274298825,-1022587231,1510334235,755167117
+};
+
+static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current,
+                                   const unsigned int required)
+{
+    while (*current < required)
+    {
+        if (bytebuffer_offset >= bytebuffer_size)
+        {
+            /* Leave the first 4 bytes empty so we can always unwind the
+               bitbuffer to the front of the bytebuffer, leave 4 bytes free at
+               end of tail so we can easily top up buffer in
+               check_trailer_gzip() */
+            if (1 > (bytebuffer_size = safe_read(gunzip_src_md, &bytebuffer[4],
+                                                 bytebuffer_max - 8)))
+                error_die("unexpected end of file");
+
+            bytebuffer_size += 4;
+            bytebuffer_offset = 4;
+        }
+
+        bitbuffer |= ((unsigned int) bytebuffer[bytebuffer_offset]) << *current;
+        bytebuffer_offset++;
+        *current += 8;
+    }
+    return(bitbuffer);
+}
+
+/*
+ * Free the malloc'ed tables built by huft_build(), which makes a linked list of
+ *the tables it made, with the links in a dummy first entry of each table.
+ * t: table to free
+ */
+static int huft_free(huft_t * t,unsigned char bufnum)
+{
+    wpw_reset_mempool(bufnum);
+    if(t==0)
+    {
+    }
+
+    return 0;
+}
+
+/* Given a list of code lengths and a maximum table size, make a set of tables
+   to decode that set of codes.  Return zero on success, one if the given code
+   set is incomplete (the tables are still built in this case), two if the input
+   is invalid (all zero length codes or an oversubscribed set of lengths), and
+   three if not enough memory.
+ *
+ * b:    code lengths in bits (all assumed <= BMAX) n:    number of codes
+ *(assumed <= N_MAX) s:    number of simple-valued codes (0..s-1) d:    list of
+ *base values for non-simple codes e:    list of extra bits for non-simple codes
+ *t:    result: starting table m:    maximum lookup bits, returns actual bufnum:
+ *the number of the memory pool to fetch memory from
+ */
+static
+int huft_build(unsigned int *b, const unsigned int n,
+               const unsigned int s, const unsigned short *d,
+               const unsigned char *e, huft_t ** t, unsigned int *m,
+               unsigned char bufnum)
+{
+    unsigned a=0;                /* counter for codes of length k */
+    unsigned c[BMAX + 1];        /* bit length count table */
+    unsigned eob_len=0;            /* length of end-of-block code (value 256) */
+    unsigned f=0;                /* i repeats in table every f entries */
+    int g=0;                    /* maximum code length */
+    int htl=0;                    /* table level */
+    unsigned i=0;                /* counter, current code */
+    unsigned j=0;                /* counter */
+    int k=0;                    /* number of bits in current code */
+    unsigned *p;                /* pointer into c[], b[], or v[] */
+    huft_t *q;                    /* points to current table */
+    huft_t r;                    /* table entry for structure assignment */
+    huft_t *u[BMAX];            /* table stack */
+    unsigned v[N_MAX];            /* values in order of bit length */
+    int ws[BMAX+1];                /* bits decoded stack */
+    int w=0;                    /* bits decoded */
+    unsigned x[BMAX + 1];        /* bit offsets, then code stack */
+    unsigned *xp;                /* pointer into x */
+    int y=0;                    /* number of dummy codes added */
+    unsigned z=0;                /* number of entries in current table */
+
+    /* Length of EOB code, if any */
+    eob_len = n > 256 ? b[256] : BMAX;
+
+    /* Generate counts for each bit length */
+    memset((void *)c, 0, sizeof(c));
+    p = b;
+    i = n;
+    do {
+        c[*p]++;    /* assume all entries <= BMAX */
+        p++;        /* Can't combine with above line (Solaris bug) */
+    } while (--i);
+    if (c[0] == n)   /* null input--all zero length codes */
+    {
+        *t = (huft_t *) NULL;
+        *m = 0;
+        return 2;
+    }
+
+    /* Find minimum and maximum length, bound *m by those */
+    for (j = 1; (c[j] == 0) && (j <= BMAX); j++) ;
+
+    k = j; /* minimum code length */
+    for (i = BMAX; (c[i] == 0) && i; i--) ;
+
+    g = i; /* maximum code length */
+    *m = (*m < j) ? j : ((*m > i) ? i : *m);
+
+    /* Adjust last length count to fill out codes, if needed */
+    for (y = 1 << j; j < i; j++, y <<= 1)
+    {
+        if ((y -= c[j]) < 0)
+            return 2;  /* bad input: more codes than bits */
+    }
+
+    if ((y -= c[i]) < 0)
+        return 2;
+
+    c[i] += y;
+
+    /* Generate starting offsets into the value table for each length */
+    x[1] = j = 0;
+    p = c + 1;
+    xp = x + 2;
+    while (--i)   /* note that i == g from above */
+    {
+        *xp++ = (j += *p++);
+    }
+
+    /* Make a table of values in order of bit lengths */
+    p = b;
+    i = 0;
+    do {
+        if ((j = *p++) != 0)
+            v[x[j]++] = i;
+    } while (++i < n);
+
+    /* Generate the Huffman codes and for each, make the table entries */
+    x[0] = i = 0;            /* first Huffman code is zero */
+    p = v;                    /* grab values in bit order */
+    htl = -1;                /* no tables yet--level -1 */
+    w = ws[0] = 0;            /* bits decoded */
+    u[0] = (huft_t *) NULL;    /* just to keep compilers happy */
+    q = (huft_t *) NULL;    /* ditto */
+    z = 0;                    /* ditto */
+
+    /* go through the bit lengths (k already is bits in shortest code) */
+    for (; k <= g; k++)
+    {
+        a = c[k];
+        while (a--)
+        {
+            /* here i is the Huffman code of length k bits for value *p */
+            /* make tables up to required level */
+            while (k > ws[htl + 1])
+            {
+                w = ws[++htl];
+
+                /* compute minimum size table less than or equal to *m bits */
+                z = (z = g - w) > *m ? *m : z; /* upper limit on table size */
+                if ((f = 1 << (j = k - w)) > a + 1)   /* try a k-w bit table */
+                {   /* too few codes for k-w bit table */
+                    f -= a + 1; /* deduct codes from patterns left */
+                    xp = c + k;
+                    while (++j < z)   /* try smaller tables up to z bits */
+                    {
+                        if ((f <<= 1) <= *++xp)
+                            break;  /* enough codes to use up j bits */
+
+                        f -= *xp; /* else deduct codes from patterns */
+                    }
+                }
+
+                j = ((unsigned)(w + j) > eob_len && (unsigned)w < eob_len)
+                    ? eob_len - w : j;      /* make EOB code end at table */
+                z = 1 << j;    /* table entries for j-bit table */
+                ws[htl+1] = w + j;    /* set bits decoded in stack */
+
+                /* allocate and link in new table */
+                q = (huft_t *) wpw_malloc(bufnum,(z + 1) * sizeof(huft_t));
+                if(q==0)
+                    return 3;
+
+                *t = q + 1;    /* link to list for huft_free() */
+                t = &(q->v.t);
+                u[htl] = ++q;    /* table starts after link */
+
+                /* connect to last table, if there is one */
+                if (htl)
+                {
+                    x[htl] = i; /* save pattern for backing up */
+
+                    /* bits to dump before this table */
+                    r.b = (unsigned char) (w - ws[htl - 1]);
+                    r.e = (unsigned char) (16 + j); /* bits in this table */
+                    r.v.t = q; /* pointer to this table */
+                    j = (i & ((1 << w) - 1)) >> ws[htl - 1];
+                    u[htl - 1][j] = r; /* connect to last table */
+                }
+            }
+
+            /* set up table entry in r */
+            r.b = (unsigned char) (k - w);
+            if (p >= v + n)
+                r.e = 99;  /* out of values--invalid code */
+            else if (*p < s)
+            {
+                r.e = (unsigned char) (*p < 256 ? 16 : 15);    /* 256 is EOB
+                                                                  code */
+                r.v.n = (unsigned short) (*p++); /* simple code is just the
+                                                    value */
+            }
+            else
+            {
+                r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists
+                                                  */
+                r.v.n = d[*p++ - s];
+            }
+
+            /* fill code-like entries with r */
+            f = 1 << (k - w);
+            for (j = i >> w; j < z; j += f)
+            {
+                q[j] = r;
+            }
+
+            /* backwards increment the k-bit code i */
+            for (j = 1 << (k - 1); i &j; j >>= 1)
+            {
+                i ^= j;
+            }
+            i ^= j;
+
+            /* backup over finished tables */
+            while ((i & ((1 << w) - 1)) != x[htl])
+            {
+                w = ws[--htl];
+            }
+        }
+    }
+
+    /* return actual size of base table */
+    *m = ws[1];
+
+    /* Return true (1) if we were given an incomplete table */
+    return y != 0 && g != 1;
+}
+
+/*
+ * inflate (decompress) the codes in a deflated (compressed) block. Return an
+ *error code or zero if it all goes ok.
+ *
+ * tl, td: literal/length and distance decoder tables bl, bd: number of bits
+ *decoded by tl[] and td[]
+ */
+static int inflate_codes_resumeCopy = 0;
+static int inflate_codes(huft_t * my_tl, huft_t * my_td,
+                         const unsigned int my_bl, const unsigned int my_bd,
+                         int setup)
+{
+    static unsigned int e;        /* table entry flag/number of extra bits */
+    static unsigned int n, d;    /* length and index for copy */
+    static unsigned int w;        /* current gunzip_window position */
+    static huft_t *t;            /* pointer to table entry */
+    static unsigned int ml, md;    /* masks for bl and bd bits */
+    static unsigned int b;        /* bit buffer */
+    static unsigned int k;        /* number of bits in bit buffer */
+    static huft_t *tl, *td;
+    static unsigned int bl, bd;
+
+    if (setup)   /* 1st time we are called, copy in variables */
+    {
+        tl = my_tl;
+        td = my_td;
+        bl = my_bl;
+        bd = my_bd;
+        /* make local copies of globals */
+        b = gunzip_bb;                /* initialize bit buffer */
+        k = gunzip_bk;
+        w = gunzip_outbuf_count;            /* initialize gunzip_window position
+                                             */
+
+        /* inflate the coded data */
+        ml = mask_bits[bl];    /* precompute masks for speed */
+        md = mask_bits[bd];
+        return 0; /* Don't actually do anything the first time */
+    }
+
+    if (inflate_codes_resumeCopy) goto do_copy;
+
+    while (1)              /* do until end of block */
+    {
+        b = fill_bitbuffer(b, &k, bl);
+        if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
+            do {
+                if (e == 99)
+                    error_die("inflate_codes error 1");
+
+                b >>= t->b;
+                k -= t->b;
+                e -= 16;
+                b = fill_bitbuffer(b, &k, e);
+            } while ((e =
+                          (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
+
+        b >>= t->b;
+        k -= t->b;
+        if (e == 16)      /* then it's a literal */
+        {
+            gunzip_window[w++] = (unsigned char) t->v.n;
+            if (w == gunzip_wsize)
+            {
+                gunzip_outbuf_count = (w);
+                w = 0;
+                return 1; /* We have a block to read */
+            }
+        }
+        else            /* it's an EOB or a length */
+        {   /* exit if end of block */
+            if (e == 15)
+                break;
+
+            /* get length of block to copy */
+            b = fill_bitbuffer(b, &k, e);
+            n = t->v.n + ((unsigned) b & mask_bits[e]);
+            b >>= e;
+            k -= e;
+
+            /* decode distance of block to copy */
+            b = fill_bitbuffer(b, &k, bd);
+            if ((e = (t = td + ((unsigned) b & md))->e) > 16)
+                do {
+                    if (e == 99)
+                        error_die("inflate_codes error 2");
+
+                    b >>= t->b;
+                    k -= t->b;
+                    e -= 16;
+                    b = fill_bitbuffer(b, &k, e);
+                } while ((e =
+                              (t =
+                                   t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
+
+            b >>= t->b;
+            k -= t->b;
+            b = fill_bitbuffer(b, &k, e);
+            d = w - t->v.n - ((unsigned) b & mask_bits[e]);
+            b >>= e;
+            k -= e;
+
+            /* do the copy */
+do_copy:        do {
+                n -= (e =
+                          (e =
+                               gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
+                /* copy to new buffer to prevent possible overwrite */
+                if (w - d >= e)      /* (this test assumes unsigned comparison)
+                                      */
+                {
+                    memcpy(gunzip_window + w, gunzip_window + d, e);
+                    w += e;
+                    d += e;
+                }
+                else
+                {
+                    /* do it slow to avoid memcpy() overlap */
+                    /* !NOMEMCPY */
+                    do {
+                        gunzip_window[w++] = gunzip_window[d++];
+                    } while (--e);
+                }
+
+                if (w == gunzip_wsize)
+                {
+                    gunzip_outbuf_count = (w);
+                    if (n) inflate_codes_resumeCopy = 1;
+                    else inflate_codes_resumeCopy = 0;
+
+                    w = 0;
+                    return 1;
+                }
+            } while (n);
+            inflate_codes_resumeCopy = 0;
+        }
+    }
+
+    /* restore the globals from the locals */
+    gunzip_outbuf_count = w;    /* restore global gunzip_window pointer */
+    gunzip_bb = b;                /* restore global bit buffer */
+    gunzip_bk = k;
+
+    /* normally just after call to inflate_codes, but save code by putting it
+       here */
+    /* free the decoding tables, return */
+    huft_free(tl,HUFT_MMP1);
+    huft_free(td,HUFT_MMP2);
+
+    /* done */
+    return 0;
+}
+
+static int inflate_stored(int my_n, int my_b_stored, int my_k_stored, int setup)
+{
+    static unsigned int n, b_stored, k_stored, w;
+    if (setup)
+    {
+        n = my_n;
+        b_stored = my_b_stored;
+        k_stored = my_k_stored;
+        w = gunzip_outbuf_count;    /* initialize gunzip_window position */
+        return 0; /* Don't do anything first time */
+    }
+
+    /* read and output the compressed data */
+    while (n--)
+    {
+        b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
+        gunzip_window[w++] = (unsigned char) b_stored;
+        if (w == gunzip_wsize)
+        {
+            gunzip_outbuf_count = (w);
+            w = 0;
+            b_stored >>= 8;
+            k_stored -= 8;
+            return 1; /* We have a block */
+        }
+
+        b_stored >>= 8;
+        k_stored -= 8;
+    }
+
+    /* restore the globals from the locals */
+    gunzip_outbuf_count = w;        /* restore global gunzip_window pointer */
+    gunzip_bb = b_stored;    /* restore global bit buffer */
+    gunzip_bk = k_stored;
+    return 0; /* Finished */
+}
+
+/*
+ * decompress an inflated block e: last block flag
+ *
+ * GLOBAL VARIABLES: bb, kk,
+ */
+/* Return values: -1 = inflate_stored, -2 = inflate_codes */
+static int inflate_block(int *e)
+{
+    unsigned t;            /* block type */
+    unsigned int b;    /* bit buffer */
+    unsigned int k;    /* number of bits in bit buffer */
+
+    /* make local bit buffer */
+
+    b = gunzip_bb;
+    k = gunzip_bk;
+
+    /* read in last block bit */
+    b = fill_bitbuffer(b, &k, 1);
+    *e = (int) b & 1;
+    b >>= 1;
+    k -= 1;
+
+    /* read in block type */
+    b = fill_bitbuffer(b, &k, 2);
+    t = (unsigned) b & 3;
+    b >>= 2;
+    k -= 2;
+
+    /* restore the global bit buffer */
+    gunzip_bb = b;
+    gunzip_bk = k;
+
+    /* inflate that block type */
+    switch (t)
+    {
+    case 0:            /* Inflate stored */
+    {
+        unsigned int n=0;    /* number of bytes in block */
+        unsigned int b_stored=0;    /* bit buffer */
+        unsigned int k_stored=0;    /* number of bits in bit buffer */
+
+        /* make local copies of globals */
+        b_stored = gunzip_bb;    /* initialize bit buffer */
+        k_stored = gunzip_bk;
+
+        /* go to byte boundary */
+        n = k_stored & 7;
+        b_stored >>= n;
+        k_stored -= n;
+
+        /* get the length and its complement */
+        b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
+        n = ((unsigned) b_stored & 0xffff);
+        b_stored >>= 16;
+        k_stored -= 16;
+
+        b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
+        if (n != (unsigned) ((~b_stored) & 0xffff))
+            return 1;    /* error in compressed data */
+
+        b_stored >>= 16;
+        k_stored -= 16;
+
+        inflate_stored(n, b_stored, k_stored, 1); /* Setup inflate_stored */
+        return -1;
+    }
+    case 1:            /* Inflate fixed decompress an inflated type 1 (fixed
+                          Huffman codes) block.  We should either replace this
+                          with a custom decoder, or at least precompute the
+                          Huffman tables.
+                        */
+    {
+        int i;            /* temporary variable */
+        huft_t *tl;        /* literal/length code table */
+        huft_t *td;        /* distance code table */
+        unsigned int bl;            /* lookup bits for tl */
+        unsigned int bd;            /* lookup bits for td */
+        unsigned int l[288];    /* length list for huft_build */
+
+        /* set up literal table */
+        for (i = 0; i < 144; i++)
+        {
+            l[i] = 8;
+        }
+        for (; i < 256; i++)
+        {
+            l[i] = 9;
+        }
+        for (; i < 280; i++)
+        {
+            l[i] = 7;
+        }
+        for (; i < 288; i++)      /* make a complete, but wrong code set */
+        {
+            l[i] = 8;
+        }
+        bl = 7;
+        if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl,HUFT_MMP1)) != 0)
+            return i;
+
+        /* set up distance table */
+        for (i = 0; i < 30; i++)      /* make an incomplete code set */
+        {
+            l[i] = 5;
+        }
+        bd = 5;
+        if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd,HUFT_MMP2)) > 1)
+        {
+            huft_free(tl,HUFT_MMP1);
+            return i;
+        }
+
+        /* decompress until an end-of-block code */
+        inflate_codes(tl, td, bl, bd, 1); /* Setup inflate_codes */
+
+        /* huft_free code moved into inflate_codes */
+
+        return -2;
+    }
+    case 2:            /* Inflate dynamic */
+    {
+        const int dbits = 6;    /* bits in base distance lookup table */
+        const int lbits = 9;    /* bits in base literal/length lookup table */
+
+        huft_t *tl;        /* literal/length code table */
+        huft_t *td;        /* distance code table */
+        unsigned int i;            /* temporary variables */
+        unsigned int j;
+        unsigned int l;        /* last length */
+        unsigned int m;        /* mask for bit lengths table */
+        unsigned int n;        /* number of lengths to get */
+        unsigned int bl;            /* lookup bits for tl */
+        unsigned int bd;            /* lookup bits for td */
+        unsigned int nb;    /* number of bit length codes */
+        unsigned int nl;    /* number of literal/length codes */
+        unsigned int nd;    /* number of distance codes */
+
+        unsigned int ll[286 + 30];    /* literal/length and distance code
+                                         lengths */
+        unsigned int b_dynamic;    /* bit buffer */
+        unsigned int k_dynamic;    /* number of bits in bit buffer */
+
+        /* make local bit buffer */
+        b_dynamic = gunzip_bb;
+        k_dynamic = gunzip_bk;
+
+        /* read in table lengths */
+        b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+        nl = 257 + ((unsigned int) b_dynamic & 0x1f);    /* number of
+                                                            literal/length codes
+                                                          */
+
+        b_dynamic >>= 5;
+        k_dynamic -= 5;
+        b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+        nd = 1 + ((unsigned int) b_dynamic & 0x1f);    /* number of distance
+                                                          codes */
+
+        b_dynamic >>= 5;
+        k_dynamic -= 5;
+        b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4);
+        nb = 4 + ((unsigned int) b_dynamic & 0xf);    /* number of bit length
+                                                         codes */
+
+        b_dynamic >>= 4;
+        k_dynamic -= 4;
+        if (nl > 286 || nd > 30)
+            return 1;    /* bad lengths */
+
+        /* read in bit-length-code lengths */
+        for (j = 0; j < nb; j++)
+        {
+            b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+            ll[border[j]] = (unsigned int) b_dynamic & 7;
+            b_dynamic >>= 3;
+            k_dynamic -= 3;
+        }
+        for (; j < 19; j++)
+        {
+            ll[border[j]] = 0;
+        }
+
+        /* build decoding table for trees--single level, 7 bit lookup */
+        bl = 7;
+        i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl,HUFT_MMP1);
+        if (i != 0)
+        {
+            if (i == 1)
+                huft_free(tl,HUFT_MMP1);
+
+            return i;    /* incomplete code set */
+        }
+
+        /* read in literal and distance code lengths */
+        n = nl + nd;
+        m = mask_bits[bl];
+        i = l = 0;
+        while ((unsigned int) i < n)
+        {
+            b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl);
+            j = (td = tl + ((unsigned int) b_dynamic & m))->b;
+            b_dynamic >>= j;
+            k_dynamic -= j;
+            j = td->v.n;
+            if (j < 16)      /* length of code in bits (0..15) */
+                ll[i++] = l = j;    /* save last length in l */
+            else if (j == 16)        /* repeat last length 3 to 6 times */
+            {
+                b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2);
+                j = 3 + ((unsigned int) b_dynamic & 3);
+                b_dynamic >>= 2;
+                k_dynamic -= 2;
+                if ((unsigned int) i + j > n)
+                    return 1;
+
+                while (j--)
+                {
+                    ll[i++] = l;
+                }
+            }
+            else if (j == 17)        /* 3 to 10 zero length codes */
+            {
+                b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+                j = 3 + ((unsigned int) b_dynamic & 7);
+                b_dynamic >>= 3;
+                k_dynamic -= 3;
+                if ((unsigned int) i + j > n)
+                    return 1;
+
+                while (j--)
+                {
+                    ll[i++] = 0;
+                }
+                l = 0;
+            }
+            else        /* j == 18: 11 to 138 zero length codes */
+            {
+                b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7);
+                j = 11 + ((unsigned int) b_dynamic & 0x7f);
+                b_dynamic >>= 7;
+                k_dynamic -= 7;
+                if ((unsigned int) i + j > n)
+                    return 1;
+
+                while (j--)
+                {
+                    ll[i++] = 0;
+                }
+                l = 0;
+            }
+        }
+
+        /* free decoding table for trees */
+        huft_free(tl,HUFT_MMP1);
+
+        /* restore the global bit buffer */
+        gunzip_bb = b_dynamic;
+        gunzip_bk = k_dynamic;
+
+        /* build the decoding tables for literal/length and distance codes */
+        bl = lbits;
+
+        if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl,HUFT_MMP1)) != 0)
+        {
+            if (i == 1)
+            {
+                error_die("Incomplete literal tree");
+                huft_free(tl,HUFT_MMP1);
+            }
+
+            return i;    /* incomplete code set */
+        }
+
+        bd = dbits;
+        if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd,HUFT_MMP2)) != 0)
+        {
+            if (i == 1)
+            {
+                error_die("incomplete distance tree");
+                huft_free(td,HUFT_MMP2);
+            }
+
+            huft_free(tl,HUFT_MMP1);
+            return i;    /* incomplete code set */
+        }
+
+        /* decompress until an end-of-block code */
+        inflate_codes(tl, td, bl, bd, 1); /* Setup inflate_codes */
+
+        /* huft_free code moved into inflate_codes */
+
+        return -2;
+    }
+    default:
+        /* bad block type */
+        error_die("bad block type");
+    }
+    return 0;
+}
+
+static void calculate_gunzip_crc(void)
+{
+    unsigned int n;
+    for (n = 0; n < gunzip_outbuf_count; n++)
+    {
+        gunzip_crc = crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff]
+                     ^ (gunzip_crc >> 8);
+    }
+    gunzip_bytes_out += gunzip_outbuf_count;
+}
+
+static int inflate_get_next_window_method = -1; /* Method == -1 for stored, -2
+                                                   for codes */
+static int inflate_get_next_window_e = 0;
+static int inflate_get_next_window_needAnotherBlock = 1;
+
+static int inflate_get_next_window(void)
+{
+    gunzip_outbuf_count = 0;
+
+    while(1)
+    {
+        int ret=0;
+        if (inflate_get_next_window_needAnotherBlock)
+        {
+            if(inflate_get_next_window_e)
+            {
+                calculate_gunzip_crc();
+                inflate_get_next_window_e = 0;
+                inflate_get_next_window_needAnotherBlock = 1;
+                return 0;
+            } /* Last block */
+
+            inflate_get_next_window_method = inflate_block(&inflate_get_next_window_e);
+            inflate_get_next_window_needAnotherBlock = 0;
+        }
+
+        switch (inflate_get_next_window_method)
+        {
+        case -1:    ret = inflate_stored(0,0,0,0);
+            break;
+        case -2:    ret = inflate_codes(0,0,0,0,0);
+            break;
+        default:
+            error_die("inflate error");
+        }
+
+        if (ret == 1)
+        {
+            calculate_gunzip_crc();
+            return 1; /* More data left */
+        }
+        else inflate_get_next_window_needAnotherBlock = 1;    /* End of that
+                                                                 block */
+    }
+    /* Doesnt get here */
+}
+
+/* Initialise bytebuffer, be careful not to overfill the buffer */
+static void inflate_init(unsigned int bufsize)
+{
+    /* Set the bytebuffer size, default is same as gunzip_wsize */
+    bytebuffer_max = bufsize + 8;
+    bytebuffer_offset = 4;
+    bytebuffer_size = 0;
+}
+
+static void inflate_cleanup(void)
+{
+    /* free(bytebuffer); */
+}
+
+USE_DESKTOP(long long) static int
+inflate_unzip(struct mbreader_t *in,char* outbuffer,uint32_t outbuflen)
+{
+    USE_DESKTOP(long long total = 0; )
+    typedef void (*sig_type)(int);
+
+    /* Allocate all global buffers (for DYN_ALLOC option) */
+    gunzip_outbuf_count = 0;
+    gunzip_bytes_out = 0;
+    gunzip_src_md = in;
+
+    /* initialize gunzip_window, bit buffer */
+    gunzip_bk = 0;
+    gunzip_bb = 0;
+
+    /* Create the crc table */
+    gunzip_crc = ~0;
+
+    /* Allocate space for buffer */
+    while(1)
+    {
+        int ret = inflate_get_next_window();
+        if((signed int)outbuflen-(signed int)gunzip_outbuf_count<0)
+        {
+            error_msg("write_error");
+		#ifdef TRIM_FILE_ON_ERROR
+            return USE_DESKTOP(total) + 0;
+        #else
+        	return -1;
+        #endif
+        }
+
+        memcpy(outbuffer,gunzip_window,gunzip_outbuf_count);
+        outbuffer+=sizeof(char)*gunzip_outbuf_count;
+        ifl_total+=sizeof(char)*gunzip_outbuf_count;
+        outbuflen-=gunzip_outbuf_count;
+        USE_DESKTOP(total += gunzip_outbuf_count; )
+        if (ret == 0) break;
+    }
+
+    /* Store unused bytes in a global buffer so calling applets can access it */
+    if (gunzip_bk >= 8)
+    {
+        /* Undo too much lookahead. The next read will be byte aligned so we can
+           discard unused bits in the last meaningful byte. */
+        bytebuffer_offset--;
+        bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff;
+        gunzip_bb >>= 8;
+        gunzip_bk -= 8;
+    }
+
+    return USE_DESKTOP(total) + 0;
+}
+
+USE_DESKTOP(long long) static int
+inflate_gunzip(struct mbreader_t *in,char* outbuffer,uint32_t outbuflen)
+{
+    uint32_t stored_crc = 0;
+    unsigned int count;
+    USE_DESKTOP(long long total = ) inflate_unzip(in, outbuffer,outbuflen);
+
+    USE_DESKTOP(if (total < 0) return total;
+
+                )
+
+    /* top up the input buffer with the rest of the trailer */
+    count = bytebuffer_size - bytebuffer_offset;
+    if (count < 8)
+    {
+        xread(in, &bytebuffer[bytebuffer_size], 8 - count);
+        bytebuffer_size += 8 - count;
+    }
+
+    for (count = 0; count != 4; count++)
+    {
+        stored_crc |= (bytebuffer[bytebuffer_offset] << (count * 8));
+        bytebuffer_offset++;
+    }
+
+    /* Validate decompression - crc */
+    if (stored_crc != (~gunzip_crc))
+    {
+        error_msg("crc error");
+    
+	#ifdef TRIM_FILE_ON_ERROR
+	    return USE_DESKTOP(total) + 0;
+	#else
+		return -1;
+	#endif
+    }
+
+    /* Validate decompression - size */
+    if ((signed int)gunzip_bytes_out !=
+        (bytebuffer[bytebuffer_offset] | (bytebuffer[bytebuffer_offset+1] << 8) |
+         (bytebuffer[bytebuffer_offset+2] << 16) | (bytebuffer[bytebuffer_offset+3] << 24)))
+    {
+        error_msg("incorrect length");
+        return -1;
+    }
+
+    return USE_DESKTOP(total) + 0;
+}
+
+/*An allocated memory buffer at least 0x13100 (72448) bytes long*/
+uint32_t decompress(const char *inbuffer,uint32_t inbuflen,char* outbuffer,uint32_t outbuflen,uint32_t offset,char* membuf)
+{
+    signed char status=0;
+    int exitcode=0;
+    struct mbreader_t src_md;
+    ifl_total=0;
+    /* reset statics */
+    inflate_codes_resumeCopy = 0;
+    inflate_get_next_window_method = -1; /* Method == -1 for stored, -2 for
+                                            codes */
+    inflate_get_next_window_e = 0;
+    inflate_get_next_window_needAnotherBlock = 1;
+    /* init */
+    inflate_init(0x8000-8);
+    /*Memory init*/
+    huftbuffer1=membuf;
+    huftbuffer2=membuf+0x2A00;
+    gunzip_window=membuf+0x2A00+0xA00;
+    bytebuffer=membuf+0x2A00+0xA00+0x8000;
+    wpw_init_mempool_pdm(HUFT_MMP1,(unsigned char*)huftbuffer1,0x2A00);
+    wpw_init_mempool_pdm(HUFT_MMP2,(unsigned char*)huftbuffer2,0xA00);
+
+	/* Initialize memory buffer reader */
+    src_md.ptr = inbuffer;
+    src_md.size = inbuflen;
+    src_md.offset = offset;
+    
+    if ((exitcode=xread_char(&src_md)) == 0x1f)
+    {
+        unsigned char magic2;
+        magic2 = xread_char(&src_md);
+        if (magic2 == 0x8b)
+        {
+            check_header_gzip(&src_md); /* FIXME: xfunc? _or_die? */
+            status = inflate_gunzip(&src_md, outbuffer,outbuflen);
+        }
+        else
+        {
+            error_msg("invalid magic");
+            exitcode = -1;
+        }
+
+        if (status < 0)
+        {
+            error_msg("error inflating");
+            exitcode = -1;
+        }
+    }
+    else
+    {
+        error_msg("invalid magic");
+        exitcode = -1;
+    }
+
+    inflate_cleanup();
+    wpw_destroy_mempool(HUFT_MMP1);
+    wpw_destroy_mempool(HUFT_MMP2);
+
+    if(exitcode==-1)
+        return 0;
+
+    return ifl_total;
+}
diff --git a/apps/codecs/libgme/inflate/inflate.h b/apps/codecs/libgme/inflate/inflate.h
new file mode 100644
index 0000000..0516462
--- /dev/null
+++ b/apps/codecs/libgme/inflate/inflate.h
@@ -0,0 +1,30 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef INFLATE_H
+#define INFLATE_H
+#include <inttypes.h>
+
+#if defined(ROCKBOX)
+#include "codeclib.h"
+#endif
+
+uint32_t decompress(const char *inbuffer,uint32_t inbuflen,char* outbuffer,uint32_t outbuflen,
+                    uint32_t offset,char* membuf);
+#endif
diff --git a/apps/codecs/libgme/inflate/mallocer.c b/apps/codecs/libgme/inflate/mallocer.c
new file mode 100644
index 0000000..41abedd
--- /dev/null
+++ b/apps/codecs/libgme/inflate/mallocer.c
@@ -0,0 +1,86 @@
+
+/*
+   Based on the wiki viewer mallocer 
+   Copyright (C) 2005 Dave Chapman
+   
+   @ Modified to decompress memory buffer by gama
+ */
+
+#include "mallocer.h"
+#include "codeclib.h"
+
+unsigned char* mallocbuffer[MEMPOOL_MAX];
+long memory_ptr[MEMPOOL_MAX];
+size_t buffersize[MEMPOOL_MAX];
+
+int wpw_init_mempool(unsigned char mempool)
+{
+    memory_ptr[mempool] = 0;
+    mallocbuffer[mempool] = (unsigned char *)ci->codec_get_buffer(&buffersize[mempool]);
+    // memset(mallocbuf[mempool], 0, bufsize[mempool]);
+    return 0;
+}
+
+int wpw_init_mempool_pdm(unsigned char mempool,
+                         unsigned char* mem,long memsize)
+{
+    memory_ptr[mempool] = 0;
+    mallocbuffer[mempool] = mem;
+    buffersize[mempool]=memsize;
+    return 0;
+}
+
+void wpw_reset_mempool(unsigned char mempool)
+{
+    memory_ptr[mempool]=0;
+}
+
+void wpw_destroy_mempool(unsigned char mempool)
+{
+    memory_ptr[mempool] = 0;
+    mallocbuffer[mempool] =0;
+    buffersize[mempool]=0;
+}
+
+long wpw_available(unsigned char mempool)
+{
+    return buffersize[mempool]-memory_ptr[mempool];
+}
+
+void* wpw_malloc(unsigned char mempool,size_t size)
+{
+    void* x;
+
+    if (memory_ptr[mempool] + size > buffersize[mempool] )
+        return NULL;
+
+    x=&mallocbuffer[mempool][memory_ptr[mempool]];
+    memory_ptr[mempool]+=(size+3)&~3; /* Keep memory 32-bit aligned */
+
+    return(x);
+}
+
+void* wpw_calloc(unsigned char mempool,size_t nmemb, size_t size)
+{
+    void* x;
+    x = wpw_malloc(mempool,nmemb*size);
+    if (x == NULL)
+        return NULL;
+
+    memset(x,0,nmemb*size);
+    return(x);
+}
+
+void wpw_free(unsigned char mempool,void* ptr)
+{
+    (void)ptr;
+    (void)mempool;
+}
+
+void* wpw_realloc(unsigned char mempool,void* ptr, size_t size)
+{
+    void* x;
+    (void)ptr;
+    x = wpw_malloc(mempool,size);
+    return(x);
+}
diff --git a/apps/codecs/libgme/inflate/mallocer.h b/apps/codecs/libgme/inflate/mallocer.h
new file mode 100644
index 0000000..0916434
--- /dev/null
+++ b/apps/codecs/libgme/inflate/mallocer.h
@@ -0,0 +1,16 @@
+
+#define MEMPOOL_MAX 10
+#include <inttypes.h>
+#include <stdlib.h>
+
+int wpw_init_mempool(unsigned char mempool);
+int wpw_init_mempool_pdm(unsigned char mempool,
+                         unsigned char* mem,long memsize);
+
+void wpw_reset_mempool(unsigned char mempool);
+void wpw_destroy_mempool(unsigned char mempool);
+void* wpw_malloc(unsigned char mempool,size_t size);
+void* wpw_calloc(unsigned char mempool,size_t nmemb, size_t size);
+void wpw_free(unsigned char mempool,void* ptr);
+void* wpw_realloc(unsigned char mempool,void* ptr, size_t size);
+long wpw_available(unsigned char mempool);
diff --git a/apps/codecs/libgme/inflate/mbreader.c b/apps/codecs/libgme/inflate/mbreader.c
new file mode 100644
index 0000000..96e45cd
--- /dev/null
+++ b/apps/codecs/libgme/inflate/mbreader.c
@@ -0,0 +1,16 @@
+

+/* Memory buffer reader, simulates file read 

+   @ gama 

+*/

+

+#include "mbreader.h"

+

+int mbread(struct mbreader_t *md, void *buf, size_t n)

+{

+	if (!md) return -1;

+	size_t read_bytes = (md->offset+n) > md->size ? 

+		md->size-md->offset : n;

+	memcpy(buf,md->ptr + md->offset,read_bytes);

+	md->offset += read_bytes;

+	return read_bytes;

+}

diff --git a/apps/codecs/libgme/inflate/mbreader.h b/apps/codecs/libgme/inflate/mbreader.h
new file mode 100644
index 0000000..6427f18
--- /dev/null
+++ b/apps/codecs/libgme/inflate/mbreader.h
@@ -0,0 +1,15 @@
+

+#ifndef MBREADER_H

+#define MBREADER_H

+

+#include "codeclib.h"

+

+struct mbreader_t {

+	const char *ptr;

+	size_t size;

+	size_t offset;

+};

+

+int mbread(struct mbreader_t *md, void *buf, size_t n);

+

+#endif

diff --git a/apps/codecs/libgme/kss_cpu.c b/apps/codecs/libgme/kss_cpu.c
new file mode 100644
index 0000000..891a7df
--- /dev/null
+++ b/apps/codecs/libgme/kss_cpu.c
@@ -0,0 +1,35 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/

+

+#include "kss_emu.h"

+

+#include "blargg_endian.h"

+//#include "z80_cpu_log.h"

+

+/* Copyright (C) 2006-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"

+

+#define OUT_PORT(  addr, data ) cpu_out( this, TIME(), addr, data )

+#define IN_PORT(   addr       ) cpu_in( this, TIME(), addr )

+#define WRITE_MEM( addr, data ) {FLUSH_TIME(); cpu_write( this, addr, data );}

+#define IDLE_ADDR               idle_addr

+

+#define CPU_BEGIN \

+bool run_cpu( struct Kss_Emu* this, kss_time_t end_time )\

+{\

+	struct Z80_Cpu *cpu = &this->cpu; \

+	Z80_set_end_time( cpu, end_time );

+

+	#include "z80_cpu_run.h"

+	

+	return warning;

+}

diff --git a/apps/codecs/libgme/kss_emu.c b/apps/codecs/libgme/kss_emu.c
new file mode 100644
index 0000000..b010342
--- /dev/null
+++ b/apps/codecs/libgme/kss_emu.c
@@ -0,0 +1,883 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+
+#include "kss_emu.h"
+
+#include "blargg_endian.h"
+
+/* 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"
+
+long const clock_rate = 3579545;
+
+const char gme_wrong_file_type [] = "Wrong file type for this emulator";
+
+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 Kss_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       = INT_MAX / 2 + 1;
+	this->fade_step        = 1;
+	this->silence_time     = 0;
+	this->silence_count    = 0;
+	this->buf_remain       = 0;
+	// warning(); // clear warning
+}
+
+static blargg_err_t init_opl_apu( enum opl_type_t type, struct Opl_Apu* out )
+{
+	blip_time_t const period = 72;
+	int const rate = clock_rate / period;
+	return Opl_init( out, rate * period, rate, period, type );
+}
+
+void Kss_init( struct Kss_Emu* this )
+{
+	this->sample_rate   = 0;
+	this->mute_mask_    = 0;
+	this->tempo       = 1.0;
+	this->gain        = 1.0;
+	this->chip_flags = 0;
+	
+	// defaults
+	this->max_initial_silence = 2;
+	this->silence_lookahead   = 6;
+	this->ignore_silence     = false;
+	
+	this->voice_count = 0;
+	clear_track_vars( this );
+	
+	memset( this->unmapped_read, 0xFF, sizeof this->unmapped_read );
+	
+	// Init all stuff
+	Buffer_init( &this->stereo_buffer );
+	
+	Z80_init( &this->cpu );
+	Rom_init( &this->rom, page_size );
+	
+	// Initialize all apus just once (?)
+	Sms_apu_init( &this->sms.psg);
+	Ay_apu_init( &this->msx.psg );
+	Scc_init( &this->msx.scc );
+	
+#ifndef KSS_EMU_NO_FMOPL
+	init_opl_apu( type_smsfmunit, &this->sms.fm );
+	init_opl_apu( type_msxmusic, &this->msx.music );
+	init_opl_apu( type_msxaudio, &this->msx.audio );
+#endif
+}
+
+// Track info
+
+static blargg_err_t check_kss_header( void const* header )
+{
+	if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) )
+		return gme_wrong_file_type;
+	return 0;
+}
+
+// Setup
+
+void update_gain( struct Kss_Emu* this )
+{
+	double g = this->gain;
+	if ( msx_music_enabled( this ) || msx_audio_enabled( this ) 
+	  || sms_fm_enabled( this ) )
+	{
+		g *= 0.75;
+	}
+	else
+	{
+		if ( this->scc_accessed )
+			g *= 1.2;
+	}
+	
+	if ( sms_psg_enabled( this ) ) Sms_apu_volume( &this->sms.psg, g );
+	if ( sms_fm_enabled( this )  ) Opl_volume( &this->sms.fm, g );
+	if ( msx_psg_enabled( this ) ) Ay_apu_volume( &this->msx.psg, g );
+	if ( msx_scc_enabled( this ) ) Scc_volume( &this->msx.scc, g );
+	if ( msx_music_enabled( this ) ) Opl_volume( &this->msx.music, g );
+	if ( msx_audio_enabled( this ) ) Opl_volume( &this->msx.audio, g );
+}
+
+blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
+{
+	/* warning( core.warning() ); */
+	memset( &this->header, 0, sizeof this->header );
+	assert( offsetof (header_t,msx_audio_vol) == header_size - 1 );
+	RETURN_ERR( Rom_load( &this->rom, data, size, header_base_size, &this->header, 0 ) );
+	
+	RETURN_ERR( check_kss_header( this->header.tag ) );
+	
+	this->chip_flags = 0;
+	this->header.last_track [0] = 255;
+	if ( this->header.tag [3] == 'C' )
+	{
+		if ( this->header.extra_header )
+		{
+			this->header.extra_header = 0;
+			/* warning( "Unknown data in header" ); */
+		}
+		if ( this->header.device_flags & ~0x0F )
+		{
+			this->header.device_flags &= 0x0F;
+			/* warning( "Unknown data in header" ); */
+		}
+	}
+	else if ( this->header.extra_header )
+	{
+		if ( this->header.extra_header != header_ext_size )
+		{
+			this->header.extra_header = 0;
+			/* warning( "Invalid extra_header_size" ); */
+		}
+		else
+		{
+			memcpy( this->header.data_size, this->rom.file_data, header_ext_size );
+		}
+	}
+	
+	#ifndef NDEBUG
+	{
+		int ram_mode = this->header.device_flags & 0x84; // MSX
+		if ( this->header.device_flags & 0x02 ) // SMS
+			ram_mode = (this->header.device_flags & 0x88);
+		
+		if ( ram_mode )
+			blargg_dprintf_( "RAM not supported\n" ); // TODO: support
+	}
+	#endif
+
+	this->track_count = get_le16( this->header.last_track ) + 1;
+	this->m3u.size = 0;
+
+	this->scc_enabled = false;
+	if ( this->header.device_flags & 0x02 ) // Sega Master System
+	{
+		int const osc_count = sms_osc_count + opl_osc_count;
+
+		// sms.psg
+		this->voice_count = sms_osc_count;
+		this->chip_flags |= sms_psg_flag;
+
+		// sms.fm
+		if ( this->header.device_flags & 0x01 )
+		{
+			this->voice_count = osc_count;
+			this->chip_flags |= sms_fm_flag;
+		}
+	}
+	else // MSX
+	{
+		int const osc_count = ay_osc_count + opl_osc_count;
+
+		// msx.psg
+		this->voice_count = ay_osc_count;
+		this->chip_flags |= msx_psg_flag;
+
+		/* if ( this->header.device_flags & 0x10 )
+			warning( "MSX stereo not supported" ); */
+
+		// msx.music
+		if ( this->header.device_flags & 0x01 )
+		{
+			this->voice_count = osc_count;
+			this->chip_flags |= msx_music_flag;
+		}
+
+	#ifndef KSS_EMU_NO_FMOPL
+		// msx.audio
+		if ( this->header.device_flags & 0x08 )
+		{
+			this->voice_count = osc_count;
+			this->chip_flags |= msx_audio_flag;
+		}
+	#endif
+
+		if ( !(this->header.device_flags & 0x80) )
+		{
+			if ( !(this->header.device_flags & 0x84) )
+				this->scc_enabled = scc_enabled_true;
+
+			// msx.scc
+			this->chip_flags |= msx_scc_flag;
+			this->voice_count = ay_osc_count + scc_osc_count;
+		}
+	}
+
+	this->silence_lookahead = 6;
+	if ( sms_fm_enabled( this ) || msx_music_enabled( this ) || msx_audio_enabled( this ) )
+	{
+		if ( !Opl_supported() )
+			; /* warning( "FM sound not supported" ); */
+		else
+			this->silence_lookahead = 3; // Opl_Apu is really slow
+	}
+
+	this->clock_rate_ = clock_rate;
+	Buffer_clock_rate( &this->stereo_buffer, clock_rate );
+	this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer );
+	
+	Sound_set_tempo( this, this->tempo );
+	Sound_mute_voices( this, this->mute_mask_ );
+	return 0;
+}
+
+void set_voice( struct Kss_Emu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
+{
+	if ( sms_psg_enabled( this ) ) // Sega Master System
+	{
+		i -= sms_osc_count;
+		if ( i < 0 )
+		{
+			Sms_apu_set_output( &this->sms.psg, i + sms_osc_count, center, left, right );
+			return;
+		}
+
+		if ( sms_fm_enabled( this ) && i < opl_osc_count )
+			Opl_set_output( &this->sms.fm, center );
+	}
+	else if ( msx_psg_enabled( this ) ) // MSX
+	{
+		i -= ay_osc_count;
+		if ( i < 0 )
+		{
+			Ay_apu_set_output( &this->msx.psg, i + ay_osc_count, center );
+			return;
+		}
+
+		if ( msx_scc_enabled( this )   && i < scc_osc_count   ) Scc_set_output( &this->msx.scc, i, center );
+		if ( msx_music_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.music, center );
+		if ( msx_audio_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.audio, center );
+	}
+}
+
+// Emulation
+
+void jsr( struct Kss_Emu* this, byte const addr [] )
+{
+	this->ram [--this->cpu.r.sp] = idle_addr >> 8;
+	this->ram [--this->cpu.r.sp] = idle_addr & 0xFF;
+	this->cpu.r.pc = get_le16( addr );
+}
+
+void set_bank( struct Kss_Emu* this, int logical, int physical )
+{
+	int const bank_size = (16 * 1024L) >> (this->header.bank_mode >> 7 & 1);
+	
+	int addr = 0x8000;
+	if ( logical && bank_size == 8 * 1024 )
+		addr = 0xA000;
+	
+	physical -= this->header.first_bank;
+	if ( (unsigned) physical >= (unsigned) this->bank_count )
+	{
+		byte* data = this->ram + addr;
+		Z80_map_mem( &this->cpu, addr, bank_size, data, data );
+	}
+	else
+	{
+		int offset, phys = physical * bank_size;
+		for ( offset = 0; offset < bank_size; offset += page_size )
+			Z80_map_mem( &this->cpu, addr + offset, page_size,
+					this->unmapped_write, Rom_at_addr( &this->rom, phys + offset ) );
+
+	}
+}
+
+void cpu_write( struct Kss_Emu* this, addr_t addr, int data )
+{
+	*Z80_write( &this->cpu, addr ) = data;
+	if ( (addr & this->scc_enabled) == 0x8000 ) {
+		// TODO: SCC+ support
+
+		data &= 0xFF;
+		switch ( addr )
+		{
+		case 0x9000:
+			set_bank( this, 0, data );
+			return;
+
+		case 0xB000:
+			set_bank( this, 1, data );
+			return;
+
+		case 0xBFFE: // selects between mapping areas (we just always enable both)
+			if ( data == 0 || data == 0x20 )
+				return;
+		}
+
+		int scc_addr = (addr & 0xDFFF) - 0x9800;
+		if ( msx_scc_enabled( this ) && (unsigned) scc_addr < 0xB0 )
+		{
+			this->scc_accessed = true;
+			//if ( (unsigned) (scc_addr - 0x90) < 0x10 )
+			//  scc_addr -= 0x10; // 0x90-0x9F mirrors to 0x80-0x8F
+			if ( scc_addr < scc_reg_count )
+				Scc_write( &this->msx.scc, Z80_time( &this->cpu ), addr, data );
+			return;
+		}
+	}
+}
+
+void cpu_out( struct Kss_Emu* this, kss_time_t time, kss_addr_t addr, int data )
+{
+	data &= 0xFF;
+	switch ( addr & 0xFF )
+	{
+	case 0xA0:
+		if ( msx_psg_enabled( this ) )
+			Ay_apu_write_addr( &this->msx.psg, data );
+		return;
+
+	case 0xA1:
+		if ( msx_psg_enabled( this ) )
+			Ay_apu_write_data( &this->msx.psg, time, data );
+		return;
+
+	case 0x06:
+		if ( sms_psg_enabled( this ) && (this->header.device_flags & 0x04) )
+		{
+			Sms_apu_write_ggstereo( &this->sms.psg, time, data );
+			return;
+		}
+		break;
+
+	case 0x7E:
+	case 0x7F:
+		if ( sms_psg_enabled( this ) )
+		{
+			Sms_apu_write_data( &this->sms.psg, time, data );
+			return;
+		}
+		break;
+
+	#define OPL_WRITE_HANDLER( base, name, opl )\
+		case base  : if ( name##_enabled( this ) ) { Opl_write_addr( opl,       data ); return; } break;\
+		case base+1: if ( name##_enabled( this ) ) { Opl_write_data( opl, time, data ); return; } break;
+
+	OPL_WRITE_HANDLER( 0x7C, msx_music, &this->msx.music )
+	OPL_WRITE_HANDLER( 0xC0, msx_audio, &this->msx.audio )
+	OPL_WRITE_HANDLER( 0xF0, sms_fm, &this->sms.fm    )
+
+	case 0xFE:
+		set_bank( this, 0, data );
+		return;
+
+	#ifndef NDEBUG
+	case 0xA8: // PPI
+		return;
+	#endif
+	}
+
+	/* cpu_out( time, addr, data ); */
+}
+
+int cpu_in( struct Kss_Emu* this, kss_time_t time, kss_addr_t addr )
+{
+	switch ( addr & 0xFF )
+	{
+	case 0xC0:
+	case 0xC1:
+		if ( msx_audio_enabled( this ) )
+			return Opl_read( &this->msx.audio, time, addr & 1 );
+		break;
+
+	case 0xA2:
+		if ( msx_psg_enabled( this ) )
+			return Ay_apu_read( &this->msx.psg );
+		break;
+
+	#ifndef NDEBUG
+	case 0xA8: // PPI
+		return 0;
+	#endif
+	}
+
+	/* return cpu_in( time, addr ); */
+	return 0xFF;
+}
+
+blargg_err_t run_clocks( struct Kss_Emu* this, blip_time_t* duration_ )
+{
+	blip_time_t duration = *duration_;
+	RETURN_ERR( end_frame( this, duration ) );
+
+	if ( sms_psg_enabled( this ) ) Sms_apu_end_frame( &this->sms.psg, duration );
+	if ( sms_fm_enabled( this )  ) Opl_end_frame( &this->sms.fm, duration );
+	if ( msx_psg_enabled( this ) ) Ay_apu_end_frame( &this->msx.psg, duration );
+	if ( msx_scc_enabled( this ) ) Scc_end_frame( &this->msx.scc, duration );
+	if ( msx_music_enabled( this ) ) Opl_end_frame( &this->msx.music, duration );
+	if ( msx_audio_enabled( this ) ) Opl_end_frame( &this->msx.audio, duration );
+
+	return 0;
+}
+
+blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t end )
+{
+	while ( Z80_time( &this->cpu ) < end )
+	{
+		kss_time_t next = min( end, this->next_play );
+		run_cpu( this, next );
+		if ( this->cpu.r.pc == idle_addr )
+			Z80_set_time( &this->cpu, next );
+		
+		if ( Z80_time( &this->cpu ) >= this->next_play )
+		{
+			this->next_play += this->play_period;
+			if ( this->cpu.r.pc == idle_addr )
+			{
+				if ( !this->gain_updated )
+				{
+					this->gain_updated = true;
+					update_gain( this );
+				}
+				
+				jsr( this, this->header.play_addr );
+			}
+		}
+	}
+	
+	this->next_play -= end;
+	check( this->next_play >= 0 );
+	Z80_adjust_time( &this->cpu, -end );
+
+	return 0;
+}
+
+// MUSIC
+
+
+blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long rate )
+{
+	require( !this->sample_rate ); // sample rate can't be changed once set
+	RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buffer, rate, 1000 / 20 ) );
+	
+	// Set bass frequency
+	Buffer_bass_freq( &this->stereo_buffer, 180 );
+	this->sample_rate = rate;
+	return 0;
+}
+
+void Sound_mute_voice( struct Kss_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 Kss_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) )
+		{
+			set_voice( this, i, 0, 0, 0 );
+		}
+		else
+		{
+			struct channel_t ch = Buffer_channel( &this->stereo_buffer );
+			assert( (ch.center && ch.left && ch.right) ||
+					(!ch.center && !ch.left && !ch.right) ); // all or nothing
+			set_voice( this, i, ch.center, ch.left, ch.right );
+		}
+	}
+}
+
+void Sound_set_tempo( struct Kss_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;
+	
+	blip_time_t period =
+		(this->header.device_flags & 0x40 ? clock_rate / 50 : clock_rate / 60);
+	this->play_period = (blip_time_t) (period / t);
+}
+
+void fill_buf( struct Kss_Emu* this );
+blargg_err_t Kss_start_track( struct Kss_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];
+		track = e->track;
+	}
+
+	this->current_track = track;
+	
+	Buffer_clear( &this->stereo_buffer );
+	
+	if ( sms_psg_enabled( this ) ) Sms_apu_reset( &this->sms.psg, 0, 0 );
+	if ( sms_fm_enabled( this )  ) Opl_reset( &this->sms.fm );
+	if ( msx_psg_enabled( this ) ) Ay_apu_reset( &this->msx.psg );
+	if ( msx_scc_enabled( this ) ) Scc_reset( &this->msx.scc );
+	if ( msx_music_enabled( this ) ) Opl_reset( &this->msx.music );
+	if ( msx_audio_enabled( this ) ) Opl_reset( &this->msx.audio );
+
+	this->scc_accessed = false;
+	update_gain( this );
+
+	memset( this->ram, 0xC9, 0x4000 );
+	memset( this->ram + 0x4000, 0, sizeof this->ram - 0x4000 );
+	
+	// copy driver code to lo RAM
+	static byte const bios [] = {
+		0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG
+		0xD3, 0xA0, 0xDB, 0xA2, 0xC9                    // $0009: RDPSG
+	};
+	static byte const vectors [] = {
+		0xC3, 0x01, 0x00,   // $0093: WRTPSG vector
+		0xC3, 0x09, 0x00,   // $0096: RDPSG vector
+	};
+	memcpy( this->ram + 0x01, bios,    sizeof bios );
+	memcpy( this->ram + 0x93, vectors, sizeof vectors );
+	
+	// copy non-banked data into RAM
+	int load_addr = get_le16( this->header.load_addr );
+	int orig_load_size = get_le16( this->header.load_size );
+	int load_size = min( orig_load_size, (int) this->rom.file_size );
+	load_size = min( load_size, (int) mem_size - load_addr );
+	/* if ( load_size != orig_load_size )
+		warning( "Excessive data size" ); */
+	memcpy( this->ram + load_addr, this->rom.file_data + this->header.extra_header, load_size );
+	
+	Rom_set_addr( &this->rom, -load_size - this->header.extra_header );
+	
+	// check available bank data
+	int const bank_size = (16 * 1024L) >> (this->header.bank_mode >> 7 & 1);
+	int max_banks = (this->rom.file_size - load_size + bank_size - 1) / bank_size;
+	this->bank_count = this->header.bank_mode & 0x7F;
+	if ( this->bank_count > max_banks )
+	{
+		this->bank_count = max_banks;
+		/* warning( "Bank data missing" ); */
+	}
+	//dprintf( "load_size : $%X\n", load_size );
+	//dprintf( "bank_size : $%X\n", bank_size );
+	//dprintf( "bank_count: %d (%d claimed)\n", bank_count, this->header.bank_mode & 0x7F );
+	
+	this->ram [idle_addr] = 0xFF;
+	Z80_reset( &this->cpu, this->unmapped_write, this->unmapped_read );
+	Z80_map_mem( &this->cpu, 0, mem_size, this->ram, this->ram );
+	
+	this->cpu.r.sp = 0xF380;
+	this->cpu.r.b.a = track;
+	this->cpu.r.b.h = 0;
+	this->next_play = this->play_period;
+	this->gain_updated = false;
+	jsr( 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;
+}
+
+// Tell/Seek
+
+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 Kss_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 Kss_Emu* this, long msec )
+{
+	blargg_long time = msec_to_samples( msec, this->sample_rate );
+	if ( time < this->out_time )
+		RETURN_ERR( Kss_start_track( this, this->current_track ) );
+	return Track_skip( this, time - this->out_time );
+}
+
+blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out );
+blargg_err_t skip_( struct Kss_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 Kss_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;
+		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 Kss_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 Kss_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 Kss_Emu* this, long count, sample_t* out )
+{
+	check( current_track_ >= 0 );
+	this->emu_time += count;
+	if ( this->current_track >= 0 && !this->emu_track_ended_ ) {
+		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 Kss_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 Kss_play( struct Kss_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 );
+		
+		// prints nifty graph of how far ahead we are when searching for silence
+		//debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
+		
+		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;
+}
+
+blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out )
+{
+	long remain = count;
+	while ( remain )
+	{
+		remain -= Buffer_read_samples( &this->stereo_buffer, &out [count - remain], remain );
+		if ( remain )
+		{
+			if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buffer ) )
+			{
+				this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer );
+				Sound_mute_voices( this, this->mute_mask_ );
+			}
+			int msec = Buffer_length( &this->stereo_buffer );
+			/* blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; */
+			blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
+			RETURN_ERR( run_clocks( this, &clocks_emulated ) );
+			assert( clocks_emulated );
+			Buffer_end_frame( &this->stereo_buffer, clocks_emulated );
+		}
+	}
+	return 0;
+}
+
diff --git a/apps/codecs/libgme/kss_emu.h b/apps/codecs/libgme/kss_emu.h
new file mode 100644
index 0000000..81db2ae
--- /dev/null
+++ b/apps/codecs/libgme/kss_emu.h
@@ -0,0 +1,228 @@
+// MSX computer KSS music file emulator
+
+// Game_Music_Emu 0.5.5
+#ifndef KSS_EMU_H
+#define KSS_EMU_H
+
+#include "gme.h"
+#include "blargg_common.h"
+
+#include "rom_data.h"
+#include "multi_buffer.h"
+
+#include "kss_scc_apu.h"
+#include "z80_cpu.h"
+#include "sms_apu.h"
+#include "ay_apu.h"
+#include "opl_apu.h"
+#include "m3u_playlist.h"
+
+typedef short sample_t;
+typedef int kss_time_t;
+typedef int kss_addr_t;
+typedef struct Z80_Cpu Kss_Cpu;
+
+// Sound chip flags
+enum {
+	sms_psg_flag   = 1 << 0,
+	sms_fm_flag    = 1 << 1,
+	msx_psg_flag   = 1 << 2,
+	msx_scc_flag   = 1 << 3,
+	msx_music_flag = 1 << 4,
+	msx_audio_flag = 1 << 5
+};
+
+enum { idle_addr = 0xFFFF };
+enum { scc_enabled_true = 0xC000 };
+enum { mem_size = 0x10000 };
+enum { buf_size = 2048 };
+
+// KSS file header
+enum { header_size = 0x20 };
+enum { header_base_size = 0x10 };
+enum { header_ext_size = header_size - header_base_size };
+
+struct header_t
+{
+	byte tag [4];
+	byte load_addr [2];
+	byte load_size [2];
+	byte init_addr [2];
+	byte play_addr [2];
+	byte first_bank;
+	byte bank_mode;
+	byte extra_header;
+	byte device_flags;
+		
+	// KSSX extended data, if extra_header==0x10
+	byte data_size [4];
+	byte unused [4];
+	byte first_track [2];
+	byte last_track [2]; // if no extended data, we set this to 0xFF
+	byte psg_vol;
+	byte scc_vol;
+	byte msx_music_vol;
+	byte msx_audio_vol;
+};
+
+struct sms_t {
+	struct Sms_Apu psg;
+	struct Opl_Apu fm;
+};
+		
+struct msx_t {
+	struct Ay_Apu  psg;
+	struct Scc_Apu scc;
+	struct Opl_Apu music;
+	struct Opl_Apu audio;
+};
+
+struct Kss_Emu {
+	struct header_t header;
+	
+	int chip_flags;
+	bool scc_accessed;
+	bool gain_updated;
+	
+	int track_count;
+	
+	unsigned scc_enabled; // 0 or 0xC000
+	int bank_count;
+	
+	blip_time_t play_period;
+	blip_time_t next_play;
+	int ay_latch;
+	
+	// general
+	int max_initial_silence;
+	int voice_count;
+	int mute_mask_;
+	double tempo;
+	double gain;
+	
+	long sample_rate;
+	
+	// track-specific
+	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
+	volatile bool track_ended;
+	
+	// fading
+	blargg_long fade_start;
+	int fade_step;
+	
+	// silence detection
+	int silence_lookahead; // speed to run emulator when looking ahead for silence
+	bool ignore_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
+	
+	struct Stereo_Buffer stereo_buffer; // NULL if using custom buffer
+	long clock_rate_;
+	unsigned buf_changed_count;
+	
+		// M3u Playlist
+	struct M3u_Playlist m3u;
+	
+	// large items
+	sample_t buf [buf_size];
+	
+	struct sms_t sms;
+	struct msx_t msx;
+
+	Kss_Cpu cpu;
+	struct Rom_Data rom;
+		
+	byte unmapped_read  [0x100];
+	byte unmapped_write [page_size];
+	byte ram [mem_size + cpu_padding];
+};
+
+// Basic functionality (see Gme_File.h for file loading/track info functions)
+
+void Kss_init( struct Kss_Emu* this );
+blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size );
+blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t );
+
+// Set output sample rate. Must be called only once before loading file.
+blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long sample_rate );
+
+// Start a track, where 0 is the first track. Also clears warning string.
+blargg_err_t Kss_start_track( struct Kss_Emu* this, int track );
+
+// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
+// errors set warning string, and major errors also end track.
+blargg_err_t Kss_play( struct Kss_Emu* this, long count, sample_t* buf ) ICODE_ATTR;
+
+// Track status/control
+
+// Number of milliseconds (1000 msec = 1 second) played since beginning of track
+long Track_tell( struct Kss_Emu* this );
+
+// Seek to new time in track. Seeking backwards or far forward can take a while.
+blargg_err_t Track_seek( struct Kss_Emu* this, long msec );
+
+// Skip n samples
+blargg_err_t Track_skip( struct Kss_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 Kss_Emu* this, long start_msec, long length_msec );
+
+// Get track length in milliseconds
+static inline long Track_get_length( struct Kss_Emu* this, int n )
+{
+	long length = 0;
+	
+	if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
+		struct entry_t* entry = &this->m3u.entries [n];
+		length = entry->length;
+	} 
+	
+	if ( length <= 0 )
+		length = 120 * 1000;  /* 2 minutes */ 
+
+	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 Kss_Emu* this, double t );
+
+// Mute/unmute voice i, where voice 0 is first voice
+void Sound_mute_voice( struct Kss_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 Kss_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 Kss_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
+void cpu_write( struct Kss_Emu* this, kss_addr_t, int ) ICODE_ATTR;
+int  cpu_in( struct Kss_Emu* this, kss_time_t, kss_addr_t ) ICODE_ATTR;
+void cpu_out( struct Kss_Emu* this, kss_time_t, kss_addr_t, int ) ICODE_ATTR;
+
+void cpu_write_( struct Kss_Emu* this, kss_addr_t addr, int data ) ICODE_ATTR;
+bool run_cpu( struct Kss_Emu* this, kss_time_t end ) ICODE_ATTR;
+void jsr( struct Kss_Emu* this, byte const addr [] ) ICODE_ATTR;
+
+static inline int sms_psg_enabled( struct Kss_Emu* this )   { return this->chip_flags & sms_psg_flag;  }
+static inline int sms_fm_enabled( struct Kss_Emu* this )    { return this->chip_flags & sms_fm_flag;   }
+static inline int msx_psg_enabled( struct Kss_Emu* this )   { return this->chip_flags & msx_psg_flag;  }
+static inline int msx_scc_enabled( struct Kss_Emu* this )   { return this->chip_flags & msx_scc_flag;  }
+static inline int msx_music_enabled( struct Kss_Emu* this ) { return this->chip_flags & msx_music_flag;}
+static inline int msx_audio_enabled( struct Kss_Emu* this ) { return this->chip_flags & msx_audio_flag;}
+
+#endif
diff --git a/apps/codecs/libgme/kss_scc_apu.c b/apps/codecs/libgme/kss_scc_apu.c
new file mode 100644
index 0000000..0e71b1c
--- /dev/null
+++ b/apps/codecs/libgme/kss_scc_apu.c
@@ -0,0 +1,166 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "kss_scc_apu.h"
+
+/* Copyright (C) 2006-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"
+
+// Tones above this frequency are treated as disabled tone at half volume.
+// Power of two is more efficient (avoids division).
+extern int const inaudible_freq;
+
+int const wave_size = 0x20;
+
+static void set_output( struct Scc_Apu* this, struct Blip_Buffer* buf )
+{
+	int i;
+	for ( i = 0; i < scc_osc_count; ++i )
+		Scc_set_output( this, i, buf );
+}
+
+void Scc_volume( struct Scc_Apu* this, double v )
+{
+	Synth_volume( &this->synth, 0.43 / scc_osc_count / scc_amp_range * v );
+}
+
+void Scc_reset( struct Scc_Apu* this )
+{
+	this->last_time = 0;
+
+	int i;
+	for ( i = scc_osc_count; --i >= 0; )
+		memset( &this->oscs [i], 0, offsetof (struct scc_osc_t,output) );
+
+	memset( this->regs, 0, sizeof this->regs );
+}
+
+void Scc_init( struct Scc_Apu* this )
+{
+	Synth_init( &this->synth);
+	
+	set_output( this, NULL );
+	Scc_volume( this, 1.0 );
+	Scc_reset( this );
+}
+
+static void run_until( struct Scc_Apu* this, blip_time_t end_time )
+{
+	int index;
+	for ( index = 0; index < scc_osc_count; index++ )
+	{
+		struct scc_osc_t* osc = &this->oscs [index];
+
+		struct Blip_Buffer* const output = osc->output;
+		if ( !output )
+			continue;
+
+		blip_time_t period = (this->regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 +
+				this->regs [0xA0 + index * 2] + 1;
+		int volume = 0;
+		if ( this->regs [0xAF] & (1 << index) )
+		{
+			blip_time_t inaudible_period = (unsigned) (Blip_clock_rate( output ) +
+					inaudible_freq * 32) / (unsigned) (inaudible_freq * 16);
+			if ( period > inaudible_period )
+				volume = (this->regs [0xAA + index] & 0x0F) * (scc_amp_range / 256 / 15);
+		}
+
+		int8_t const* wave = (int8_t*) this->regs + index * wave_size;
+		/*if ( index == osc_count - 1 )
+			wave -= wave_size; // last two oscs share same wave RAM*/
+
+		{
+			int delta = wave [osc->phase] * volume - osc->last_amp;
+			if ( delta )
+			{
+				osc->last_amp += delta;
+				Blip_set_modified( output );
+				Synth_offset( &this->synth, this->last_time, delta, output );
+			}
+		}
+
+		blip_time_t time = this->last_time + osc->delay;
+		if ( time < end_time )
+		{
+			int phase = osc->phase;
+			if ( !volume )
+			{
+				// maintain phase
+				int count = (end_time - time + period - 1) / period;
+				phase += count; // will be masked below
+				time  += count * period;
+			}
+			else
+			{
+				int last_wave = wave [phase];
+				phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop
+				do
+				{
+					int delta = wave [phase] - last_wave;
+					phase = (phase + 1) & (wave_size - 1);
+					if ( delta )
+					{
+						last_wave += delta;
+						Synth_offset_inline( &this->synth, time, delta * volume, output );
+					}
+					time += period;
+				}
+				while ( time < end_time );
+
+				osc->last_amp = last_wave * volume;
+				Blip_set_modified( output );
+				phase--; // undo pre-advance
+			}
+			osc->phase = phase & (wave_size - 1);
+		}
+		osc->delay = time - end_time;
+	}
+	this->last_time = end_time;
+}
+
+void Scc_write( struct Scc_Apu* this, blip_time_t time, int addr, int data )
+{
+	//assert( (unsigned) addr < reg_count );
+	assert( ( addr >= 0x9800 && addr <= 0x988F ) || ( addr >= 0xB800 && addr <= 0xB8AF ) );
+	run_until( this, time );
+
+	addr -= 0x9800;
+	if ( ( unsigned ) addr < 0x90 )
+	{
+	    if ( ( unsigned ) addr < 0x60 )
+            this->regs [addr] = data;
+        else if ( ( unsigned ) addr < 0x80 )
+        {
+            this->regs [addr] = this->regs[addr + 0x20] = data;
+        }
+        else if ( ( unsigned ) addr < 0x90 )
+        {
+            this->regs [addr + 0x20] = data;
+        }
+	}
+	else
+	{
+	    addr -= 0xB800 - 0x9800;
+	    if ( ( unsigned ) addr < 0xB0 )
+            this->regs [addr] = data;
+	}
+}
+
+void Scc_end_frame( struct Scc_Apu* this, blip_time_t end_time )
+{
+	if ( end_time > this->last_time )
+		run_until( this, end_time );
+
+	this->last_time -= end_time;
+	assert( this->last_time >= 0 );
+}
diff --git a/apps/codecs/libgme/kss_scc_apu.h b/apps/codecs/libgme/kss_scc_apu.h
new file mode 100644
index 0000000..f31228a
--- /dev/null
+++ b/apps/codecs/libgme/kss_scc_apu.h
@@ -0,0 +1,51 @@
+// Konami SCC sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef KSS_SCC_APU_H
+#define KSS_SCC_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+enum { scc_reg_count = 0xB0 }; // 0 <= reg < reg_count
+enum { scc_osc_count = 5 };
+enum { scc_amp_range = 0x8000 };
+
+struct scc_osc_t
+{
+	int delay;
+	int phase;
+	int last_amp;
+	struct Blip_Buffer* output;
+};
+
+struct Scc_Apu {
+	struct scc_osc_t oscs [scc_osc_count];
+	blip_time_t last_time;
+	unsigned char regs [scc_reg_count];
+
+	struct Blip_Synth synth;
+};
+
+void Scc_init( struct Scc_Apu* this );
+
+// Resets sound chip
+void Scc_reset( struct Scc_Apu* this );
+
+// Set overall volume, where 1.0 is normal
+void Scc_volume( struct Scc_Apu* this, double v );
+	
+static inline void Scc_set_output( struct Scc_Apu* this, int index, struct Blip_Buffer* b )
+{
+	assert( (unsigned) index < scc_osc_count );
+	this->oscs [index].output = b;
+}
+
+// Emulates to time t, then writes data to reg
+void Scc_write( struct Scc_Apu* this, blip_time_t time, 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 Scc_end_frame( struct Scc_Apu* this, blip_time_t end_time ) ICODE_ATTR;
+
+#endif
diff --git a/apps/codecs/libgme/libay.make b/apps/codecs/libgme/libay.make
new file mode 100644
index 0000000..5eee8ac
--- /dev/null
+++ b/apps/codecs/libgme/libay.make
@@ -0,0 +1,21 @@
+
+# libay
+AYLIB := $(CODECDIR)/libay.a
+AYLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libgme/AYSOURCES)
+AYLIB_OBJ := $(call c2obj, $(AYLIB_SRC))
+OTHER_SRC += $(AYLIB_SRC)
+
+$(AYLIB): $(AYLIB_OBJ)
+	$(SILENT)$(shell rm -f $@)
+	$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
+
+AYFLAGS = $(filter-out -O%,$(CODECFLAGS)) -fno-strict-aliasing -DGME_AY_TYPE
+ifeq ($(CPU),arm)
+   AYFLAGS += -O3
+else
+   AYFLAGS += -O2
+endif
+
+$(CODECDIR)/libgme/%.o: $(ROOTDIR)/apps/codecs/libgme/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(AYFLAGS) -c $< -o $@
diff --git a/apps/codecs/libgme/libgbs.make b/apps/codecs/libgme/libgbs.make
new file mode 100644
index 0000000..cf6ff01
--- /dev/null
+++ b/apps/codecs/libgme/libgbs.make
@@ -0,0 +1,21 @@
+
+# libgbs
+GBSLIB := $(CODECDIR)/libgbs.a
+GBSLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libgme/GBSSOURCES)
+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 -DGME_GBS_TYPE
+ifeq ($(CPU),arm)
+   GBSFLAGS += -O3
+else
+   GBSFLAGS += -O2
+endif
+
+$(CODECDIR)/libgme/%.o: $(ROOTDIR)/apps/codecs/libgme/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(GBSFLAGS) -c $< -o $@
diff --git a/apps/codecs/libgme/libhes.make b/apps/codecs/libgme/libhes.make
new file mode 100644
index 0000000..e001856
--- /dev/null
+++ b/apps/codecs/libgme/libhes.make
@@ -0,0 +1,21 @@
+
+# libhes
+HESLIB := $(CODECDIR)/libhes.a
+HESLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libgme/HESSOURCES)
+HESLIB_OBJ := $(call c2obj, $(HESLIB_SRC))
+OTHER_SRC += $(HESLIB_SRC)
+
+$(HESLIB): $(HESLIB_OBJ)
+	$(SILENT)$(shell rm -f $@)
+	$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
+
+HESFLAGS = $(filter-out -O%,$(CODECFLAGS)) -fno-strict-aliasing -DGME_HES_TYPE
+ifeq ($(CPU),arm)
+   HESFLAGS += -O3
+else
+   HESFLAGS += -O2
+endif
+
+$(CODECDIR)/libgme/%.o: $(ROOTDIR)/apps/codecs/libgme/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(HESFLAGS) -c $< -o $@
diff --git a/apps/codecs/libgme/libkss.make b/apps/codecs/libgme/libkss.make
new file mode 100644
index 0000000..0e2dd54
--- /dev/null
+++ b/apps/codecs/libgme/libkss.make
@@ -0,0 +1,21 @@
+
+# libkss
+KSSLIB := $(CODECDIR)/libkss.a
+KSSLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libgme/KSSSOURCES)
+KSSLIB_OBJ := $(call c2obj, $(KSSLIB_SRC))
+OTHER_SRC += $(KSSLIB_SRC)
+
+$(KSSLIB): $(KSSLIB_OBJ)
+	$(SILENT)$(shell rm -f $@)
+	$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
+
+KSSFLAGS = $(filter-out -O%,$(CODECFLAGS)) -fno-strict-aliasing -DGME_KSS_TYPE
+ifeq ($(CPU),arm)
+   KSSFLAGS += -O3
+else
+   KSSFLAGS += -O2
+endif
+
+$(CODECDIR)/libgme/%.o: $(ROOTDIR)/apps/codecs/libgme/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(KSSFLAGS) -c $< -o $@
diff --git a/apps/codecs/libgme/libnsf.make b/apps/codecs/libgme/libnsf.make
new file mode 100644
index 0000000..8b9df75
--- /dev/null
+++ b/apps/codecs/libgme/libnsf.make
@@ -0,0 +1,21 @@
+
+# libnsf
+NSFLIB := $(CODECDIR)/libnsf.a
+NSFLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libgme/NSFSOURCES)
+NSFLIB_OBJ := $(call c2obj, $(NSFLIB_SRC))
+OTHER_SRC += $(NSFLIB_SRC)
+
+$(NSFLIB): $(NSFLIB_OBJ)
+	$(SILENT)$(shell rm -f $@)
+	$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
+
+NSFFLAGS = $(filter-out -O%,$(CODECFLAGS)) -fno-strict-aliasing -DGME_NSF_TYPE
+ifeq ($(CPU),arm)
+   NSFFLAGS += -O3
+else
+   NSFFLAGS += -O2
+endif
+
+$(CODECDIR)/libgme/%.o: $(ROOTDIR)/apps/codecs/libgme/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(NSFFLAGS) -c $< -o $@
diff --git a/apps/codecs/libgme/libsgc.make b/apps/codecs/libgme/libsgc.make
new file mode 100644
index 0000000..0defe78
--- /dev/null
+++ b/apps/codecs/libgme/libsgc.make
@@ -0,0 +1,21 @@
+
+# libsgc
+SGCLIB := $(CODECDIR)/libsgc.a
+SGCLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libgme/SGCSOURCES)
+SGCLIB_OBJ := $(call c2obj, $(SGCLIB_SRC))
+OTHER_SRC += $(SGCLIB_SRC)
+
+$(SGCLIB): $(SGCLIB_OBJ)
+	$(SILENT)$(shell rm -f $@)
+	$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
+
+SGCFLAGS = $(filter-out -O%,$(CODECFLAGS)) -fno-strict-aliasing -DGME_SGC_TYPE
+ifeq ($(CPU),arm)
+   SGCFLAGS += -O3
+else
+   SGCFLAGS += -O2
+endif
+
+$(CODECDIR)/libgme/%.o: $(ROOTDIR)/apps/codecs/libgme/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(SGCFLAGS) -c $< -o $@
diff --git a/apps/codecs/libgme/libvgm.make b/apps/codecs/libgme/libvgm.make
new file mode 100644
index 0000000..f0e7cbb
--- /dev/null
+++ b/apps/codecs/libgme/libvgm.make
@@ -0,0 +1,21 @@
+
+# libvgm
+VGMLIB := $(CODECDIR)/libvgm.a
+VGMLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libgme/VGMSOURCES)
+VGMLIB_OBJ := $(call c2obj, $(VGMLIB_SRC))
+OTHER_SRC += $(VGMLIB_SRC)
+
+$(VGMLIB): $(VGMLIB_OBJ)
+	$(SILENT)$(shell rm -f $@)
+	$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
+
+VGMFLAGS = $(filter-out -O%,$(CODECFLAGS)) -fno-strict-aliasing -DGME_VGM_TYPE
+ifeq ($(CPU),arm)
+   VGMFLAGS += -O3
+else
+   VGMFLAGS += -O2
+endif
+
+$(CODECDIR)/libgme/%.o: $(ROOTDIR)/apps/codecs/libgme/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(VGMFLAGS) -c $< -o $@
diff --git a/apps/codecs/libgme/m3u_playlist.h b/apps/codecs/libgme/m3u_playlist.h
new file mode 100644
index 0000000..06a5d30
--- /dev/null
+++ b/apps/codecs/libgme/m3u_playlist.h
@@ -0,0 +1,31 @@
+// M3U entries parser, with support for subtrack information
+
+// Game_Music_Emu 0.5.2
+#ifndef M3U_PLAYLIST_H
+#define M3U_PLAYILST_H
+
+#include "blargg_common.h"
+
+struct entry_t
+{
+	unsigned char track;  // 1-based
+	int length; // milliseconds
+};
+
+/* Short version of the m3u playlist */
+struct M3u_Playlist
+{
+	unsigned char size;
+	struct entry_t *entries;
+};
+
+static inline void M3u_load_data(struct M3u_Playlist* this, void *addr)
+{
+	if( addr == NULL ) return;
+	/* m3u entries data must be at offset 100,
+		the first 99 bytes are used by metadata info */
+	this->size = *(unsigned char *)(addr + 99);
+	this->entries = (struct entry_t *)(addr+100);
+}
+
+#endif
diff --git a/apps/codecs/libgme/msxtypes.h b/apps/codecs/libgme/msxtypes.h
new file mode 100644
index 0000000..6224e07
--- /dev/null
+++ b/apps/codecs/libgme/msxtypes.h
@@ -0,0 +1,36 @@
+#ifndef MSX_TYPES
+#define MSX_TYPES
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef __GNUC__
+#define __int64 long long
+#endif
+
+#ifdef _WIN32
+#define DIR_SEPARATOR "\\"
+#else
+#define DIR_SEPARATOR "/"
+#endif
+
+/* So far, only support for MSVC types
+ */
+typedef unsigned char    UInt8;
+#ifndef __CARBON__
+typedef unsigned short   UInt16;
+typedef unsigned int     UInt32;
+typedef unsigned __int64 UInt64;
+#endif
+typedef signed   char    Int8;
+typedef signed   short   Int16;
+typedef signed   int     Int32;
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/apps/codecs/libgme/multi_buffer.c b/apps/codecs/libgme/multi_buffer.c
new file mode 100644
index 0000000..26cb8cd
--- /dev/null
+++ b/apps/codecs/libgme/multi_buffer.c
@@ -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] );
+}
diff --git a/apps/codecs/libgme/multi_buffer.h b/apps/codecs/libgme/multi_buffer.h
new file mode 100644
index 0000000..26f3023
--- /dev/null
+++ b/apps/codecs/libgme/multi_buffer.h
@@ -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
diff --git a/apps/codecs/libgme/nes_apu.c b/apps/codecs/libgme/nes_apu.c
new file mode 100644
index 0000000..8f1f376
--- /dev/null
+++ b/apps/codecs/libgme/nes_apu.c
@@ -0,0 +1,393 @@
+// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/
+
+#include "nes_apu.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"
+
+int const amp_range = 15;
+
+void Apu_init( struct Nes_Apu* this )
+{
+	this->tempo_ = 1.0;
+	this->dmc.apu = this;
+	this->dmc.prg_reader = NULL;
+	this->irq_notifier_ = NULL;
+	
+	Synth_init( &this->square_synth );
+	Synth_init( &this->triangle.synth );
+	Synth_init( &this->noise.synth );
+	Synth_init( &this->dmc.synth );
+	
+	Square_set_synth( &this->square1, &this->square_synth );
+	Square_set_synth( &this->square2, &this->square_synth );
+	
+	this->oscs [0] = &this->square1.osc;
+	this->oscs [1] = &this->square2.osc;
+	this->oscs [2] = &this->triangle.osc;
+	this->oscs [3] = &this->noise.osc;
+	this->oscs [4] = &this->dmc.osc;
+	
+	Apu_output( this, NULL );
+	Apu_volume( this, 1.0 );
+	Apu_reset( this, false, 0 );
+}
+
+static double nonlinear_tnd_gain( void ) { return 0.75; }
+void Apu_enable_nonlinear( struct Nes_Apu* this, double v )
+{
+	this->dmc.nonlinear = true;
+	Synth_volume( &this->square_synth, 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v );
+	
+	const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
+	Synth_volume( &this->triangle.synth, 3.0 * tnd );
+	Synth_volume( &this->noise.synth, 2.0 * tnd );
+	Synth_volume( &this->dmc.synth, tnd );
+	
+	this->square1 .osc.last_amp = 0;
+	this->square2 .osc.last_amp = 0;
+	this->triangle.osc.last_amp = 0;
+	this->noise   .osc.last_amp = 0;
+	this->dmc     .osc.last_amp = 0;
+}
+
+void Apu_volume( struct Nes_Apu* this, double v )
+{
+	this->dmc.nonlinear = false;
+	Synth_volume( &this->square_synth,  0.1128  / amp_range * v );
+	Synth_volume( &this->triangle.synth,0.12765 / amp_range * v );
+	Synth_volume( &this->noise.synth,   0.0741  / amp_range * v );
+	Synth_volume( &this->dmc.synth,     0.42545 / 127 * v );
+}
+
+void Apu_output( struct Nes_Apu* this, struct Blip_Buffer* buffer )
+{
+	int i;
+	for ( i = 0; i < apu_osc_count; i++ )
+		Apu_osc_output( this, i, buffer );
+}
+
+void Apu_set_tempo( struct Nes_Apu* this, double t )
+{
+	this->tempo_ = t;
+	this->frame_period = (this->dmc.pal_mode ? 8314 : 7458);
+	if ( t != 1.0 )
+		this->frame_period = (int) (this->frame_period / t) & ~1; // must be even
+}
+
+void Apu_reset( struct Nes_Apu* this, bool pal_mode, int initial_dmc_dac )
+{
+	this->dmc.pal_mode = pal_mode;
+	Apu_set_tempo( this, this->tempo_ );
+	
+	Square_reset( &this->square1 );
+	Square_reset( &this->square2 );
+	Triangle_reset( &this->triangle );
+	Noise_reset( &this->noise );
+	Dmc_reset( &this->dmc );
+	
+	this->last_time = 0;
+	this->last_dmc_time = 0;
+	this->osc_enables = 0;
+	this->irq_flag = false;
+	this->earliest_irq_ = apu_no_irq;
+	this->frame_delay = 1;
+	Apu_write_register( this, 0, 0x4017, 0x00 );
+	Apu_write_register( this, 0, 0x4015, 0x00 );
+	
+	addr_t addr;
+	for ( addr = apu_io_addr; addr <= 0x4013; addr++ )
+		Apu_write_register( this, 0, addr, (addr & 3) ? 0x00 : 0x10 );
+	
+	this->dmc.dac = initial_dmc_dac;
+	if ( !this->dmc.nonlinear )
+		this->triangle.osc.last_amp = 15;
+	if ( !this->dmc.nonlinear ) // TODO: remove?
+		this->dmc.osc.last_amp = initial_dmc_dac; // prevent output transition
+}
+
+void Apu_irq_changed( struct Nes_Apu* this )
+{
+	nes_time_t new_irq = this->dmc.next_irq;
+	if ( this->dmc.irq_flag | this->irq_flag ) {
+		new_irq = 0;
+	}
+	else if ( new_irq > this->next_irq ) {
+		new_irq = this->next_irq;
+	}
+	
+	if ( new_irq != this->earliest_irq_ ) {
+		this->earliest_irq_ = new_irq;
+		if ( this->irq_notifier_ )
+			this->irq_notifier_( this->irq_data );
+	}
+}
+
+// frames
+
+void Apu_run_until( struct Nes_Apu* this, nes_time_t end_time )
+{
+	require( end_time >= this->last_dmc_time );
+	if ( end_time > Apu_next_dmc_read_time( this ) )
+	{
+		nes_time_t start = this->last_dmc_time;
+		this->last_dmc_time = end_time;
+		Dmc_run( &this->dmc, start, end_time );
+	}
+}
+
+void run_until_( struct Nes_Apu* this, nes_time_t end_time )
+{
+	require( end_time >= this->last_time );
+	
+	if ( end_time == this->last_time )
+		return;
+	
+	if ( this->last_dmc_time < end_time )
+	{
+		nes_time_t start = this->last_dmc_time;
+		this->last_dmc_time = end_time;
+		Dmc_run( &this->dmc, start, end_time );
+	}
+	
+	while ( true )
+	{
+		// earlier of next frame time or end time
+		nes_time_t time = this->last_time + this->frame_delay;
+		if ( time > end_time )
+			time = end_time;
+		this->frame_delay -= time - this->last_time;
+		
+		// run oscs to present
+		Square_run( &this->square1, this->last_time, time );
+		Square_run( &this->square2, this->last_time, time );
+		Triangle_run( &this->triangle, this->last_time, time );
+		Noise_run( &this->noise, this->last_time, time );
+		this->last_time = time;
+		
+		if ( time == end_time )
+			break; // no more frames to run
+		
+		// take frame-specific actions
+		this->frame_delay = this->frame_period;
+		switch ( this->frame++ )
+		{
+			case 0:
+				if ( !(this->frame_mode & 0xC0) ) {
+		 			this->next_irq = time + this->frame_period * 4 + 2;
+		 			this->irq_flag = true;
+		 		}
+		 		// fall through
+		 	case 2:
+		 		// clock length and sweep on frames 0 and 2
+				Osc_clock_length( &this->square1.osc, 0x20 );
+				Osc_clock_length( &this->square2.osc, 0x20 );
+				Osc_clock_length( &this->noise.osc, 0x20 );
+				Osc_clock_length( &this->triangle.osc, 0x80 ); // different bit for halt flag on triangle
+				
+				Square_clock_sweep( &this->square1, -1 );
+				Square_clock_sweep( &this->square2, 0 );
+				
+				// frame 2 is slightly shorter in mode 1
+				if ( this->dmc.pal_mode && this->frame == 3 )
+					this->frame_delay -= 2;
+		 		break;
+		 	
+			case 1:
+				// frame 1 is slightly shorter in mode 0
+				if ( !this->dmc.pal_mode )
+					this->frame_delay -= 2;
+				break;
+			
+		 	case 3:
+		 		this->frame = 0;
+		 		
+		 		// frame 3 is almost twice as long in mode 1
+		 		if ( this->frame_mode & 0x80 )
+					this->frame_delay += this->frame_period - (this->dmc.pal_mode ? 2 : 6);
+				break;
+		}
+		
+		// clock envelopes and linear counter every frame
+		Triangle_clock_linear_counter( &this->triangle );
+		Square_clock_envelope( &this->square1 );
+		Square_clock_envelope( &this->square2 );
+		Noise_clock_envelope( &this->noise );
+	}
+}
+
+static inline void zero_apu_osc( struct Nes_Osc* osc, struct Blip_Synth* synth, nes_time_t time )
+{
+	struct Blip_Buffer* output = osc->output;
+	int last_amp = osc->last_amp;
+	osc->last_amp = 0;
+	if ( output && last_amp )
+		Synth_offset( synth, time, -osc->last_amp, output );
+}
+
+void Apu_end_frame( struct Nes_Apu* this, nes_time_t end_time )
+{
+	if ( end_time > this->last_time )
+		run_until_( this, end_time );
+	
+	if ( this->dmc.nonlinear )
+	{
+		zero_apu_osc( &this->square1.osc, this->square1.synth, this->last_time );
+		zero_apu_osc( &this->square2.osc, this->square2.synth, this->last_time );
+		zero_apu_osc( &this->triangle.osc, &this->triangle.synth, this->last_time );
+		zero_apu_osc( &this->noise.osc, &this->noise.synth, this->last_time );
+		zero_apu_osc( &this->dmc.osc, &this->dmc.synth, this->last_time );
+	}
+	
+	// make times relative to new frame
+	this->last_time -= end_time;
+	require( this->last_time >= 0 );
+	
+	this->last_dmc_time -= end_time;
+	require( this->last_dmc_time >= 0 );
+	
+	if ( this->next_irq != apu_no_irq ) {
+		this->next_irq -= end_time;
+		check( this->next_irq >= 0 );
+	}
+	if ( this->dmc.next_irq != apu_no_irq ) {
+		this->dmc.next_irq -= end_time;
+		check( this->dmc.next_irq >= 0 );
+	}
+	if ( this->earliest_irq_ != apu_no_irq ) {
+		this->earliest_irq_ -= end_time;
+		if ( this->earliest_irq_ < 0 )
+			this->earliest_irq_ = 0;
+	}
+}
+
+// registers
+
+static const unsigned char length_table [0x20] ICONST_ATTR = {
+	0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
+	0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, 
+	0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
+	0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
+};
+
+void Apu_write_register( struct Nes_Apu* this, nes_time_t time, addr_t addr, int data )
+{
+	require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx)
+	require( (unsigned) data <= 0xFF );
+	
+	// Ignore addresses outside range
+	if ( (unsigned) (addr - apu_io_addr) >= apu_io_size )
+		return;
+	
+	run_until_( this, time );
+	
+	if ( addr < 0x4014 )
+	{
+		// Write to channel
+		int osc_index = (addr - apu_io_addr) >> 2;
+		struct Nes_Osc* osc = this->oscs [osc_index];
+		
+		int reg = addr & 3;
+		osc->regs [reg] = data;
+		osc->reg_written [reg] = true;
+		
+		if ( osc_index == 4 )
+		{
+			// handle DMC specially
+			Dmc_write_register( &this->dmc, reg, data );
+		}
+		else if ( reg == 3 )
+		{
+			// load length counter
+			if ( (this->osc_enables >> osc_index) & 1 )
+				osc->length_counter = length_table [(data >> 3) & 0x1F];
+			
+			// reset square phase
+			if ( osc_index == 0 ) this->square1.phase = square_phase_range - 1;
+			else if ( osc_index == 1 )	this->square2.phase = square_phase_range - 1;
+		}
+	}
+	else if ( addr == 0x4015 )
+	{
+		// Channel enables
+		int i;
+		for ( i = apu_osc_count; i--; )
+			if ( !((data >> i) & 1) )
+				this->oscs [i]->length_counter = 0;
+		
+		bool recalc_irq = this->dmc.irq_flag;
+		this->dmc.irq_flag = false;
+		
+		int old_enables = this->osc_enables;
+		this->osc_enables = data;
+		if ( !(data & 0x10) ) {
+			this->dmc.next_irq = apu_no_irq;
+			recalc_irq = true;
+		}
+		else if ( !(old_enables & 0x10) ) {
+			Dmc_start( &this->dmc ); // dmc just enabled
+		}
+		
+		if ( recalc_irq )
+			Apu_irq_changed( this );
+	}
+	else if ( addr == 0x4017 )
+	{
+		// Frame mode
+		this->frame_mode = data;
+		
+		bool irq_enabled = !(data & 0x40);
+		this->irq_flag &= irq_enabled;
+		this->next_irq = apu_no_irq;
+		
+		// mode 1
+		this->frame_delay = (this->frame_delay & 1);
+		this->frame = 0;
+		
+		if ( !(data & 0x80) )
+		{
+			// mode 0
+			this->frame = 1;
+			this->frame_delay += this->frame_period;
+			if ( irq_enabled )
+				this->next_irq = time + this->frame_delay + this->frame_period * 3 + 1;
+		}
+		
+		Apu_irq_changed( this );
+	}
+}
+
+int Apu_read_status( struct Nes_Apu* this, nes_time_t time )
+{
+	run_until_( this, time - 1 );
+	
+	int result = (this->dmc.irq_flag << 7) | (this->irq_flag << 6);
+	
+	int i;
+	for ( i = 0; i < apu_osc_count; i++ )
+		if ( this->oscs [i]->length_counter )
+			result |= 1 << i;
+	
+	run_until_( this, time );
+	
+	if ( this->irq_flag )
+	{
+		result |= 0x40;
+		this->irq_flag = false;
+		Apu_irq_changed( this );
+	}
+	
+	//debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result );
+	
+	return result;
+}
diff --git a/apps/codecs/libgme/nes_apu.h b/apps/codecs/libgme/nes_apu.h
new file mode 100644
index 0000000..6a2c280
--- /dev/null
+++ b/apps/codecs/libgme/nes_apu.h
@@ -0,0 +1,134 @@
+// NES 2A03 APU sound chip emulator
+
+// Nes_Snd_Emu 0.1.8
+#ifndef NES_APU_H
+#define NES_APU_H
+
+#include "blargg_common.h"
+#include "nes_oscs.h"
+
+enum { apu_status_addr = 0x4015 };
+enum { apu_osc_count = 5 };
+enum { apu_no_irq = INT_MAX / 2 + 1 };
+enum { apu_irq_waiting = 0 };
+
+enum { apu_io_addr = 0x4000 };
+enum { apu_io_size = 0x18 };
+
+struct apu_state_t;
+
+struct Nes_Apu {	
+	nes_time_t last_dmc_time;	
+	int osc_enables;
+	
+	struct Nes_Osc*            oscs [apu_osc_count];
+	struct Nes_Square          square1;
+	struct Nes_Square          square2;
+	struct Nes_Noise           noise;
+	struct Nes_Triangle        triangle;
+	struct Nes_Dmc             dmc;
+	
+	double tempo_;
+	nes_time_t last_time; // has been run until this time in current frame
+	nes_time_t earliest_irq_;
+	nes_time_t next_irq;
+	int frame_period;
+	int frame_delay; // cycles until frame counter runs next
+	int frame; // current frame (0-3)
+	int frame_mode;
+	bool irq_flag;
+	
+	void (*irq_notifier_)( void* user_data );
+	void* irq_data;
+	
+	Synth square_synth; // shared by squares
+};
+
+// Init Nes apu
+void Apu_init( struct Nes_Apu* this );
+
+// Set buffer to generate all sound into, or disable sound if NULL
+void Apu_output( struct Nes_Apu* this, struct Blip_Buffer* ) ICODE_ATTR;
+	
+// All time values are the number of cpu clock cycles relative to the
+// beginning of the current time frame. Before resetting the cpu clock
+// count, call end_frame( last_cpu_time ).
+
+// Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
+void Apu_write_register( struct Nes_Apu* this, nes_time_t, addr_t, int data ) ICODE_ATTR;
+	
+// Read from status register at 0x4015
+int Apu_read_status( struct Nes_Apu* this, nes_time_t ) ICODE_ATTR;
+	
+// Run all oscillators up to specified time, end current time frame, then
+// start a new time frame at time 0. Time frames have no effect on emulation
+// and each can be whatever length is convenient.
+void Apu_end_frame( struct Nes_Apu* this, nes_time_t ) ICODE_ATTR;
+	
+// Additional optional features (can be ignored without any problem)
+
+// Reset internal frame counter, registers, and all oscillators.
+// Use PAL timing if pal_timing is true, otherwise use NTSC timing.
+// Set the DMC oscillator's initial DAC value to initial_dmc_dac without
+// any audible click.
+void Apu_reset( struct Nes_Apu* this, bool pal_mode, int initial_dmc_dac );
+	
+// Adjust frame period
+void Apu_set_tempo( struct Nes_Apu* this, double );
+	
+// Set overall volume (default is 1.0)
+void Apu_volume( struct Nes_Apu* this, double );
+	
+// Run DMC until specified time, so that any DMC memory reads can be
+// accounted for (i.e. inserting cpu wait states).
+void Apu_run_until( struct Nes_Apu* this, nes_time_t ) ICODE_ATTR;
+
+// Set sound output of specific oscillator to buffer. If buffer is NULL,
+// the specified oscillator is muted and emulation accuracy is reduced.
+// The oscillators are indexed as follows: 0) Square 1, 1) Square 2,
+// 2) Triangle, 3) Noise, 4) DMC.
+static inline void Apu_osc_output( struct Nes_Apu* this, int osc, struct Blip_Buffer* buf )
+{
+	assert( (unsigned) osc < apu_osc_count );
+	this->oscs [osc]->output = buf;
+}
+
+// Set memory reader callback used by DMC oscillator to fetch samples.
+// When callback is invoked, 'user_data' is passed unchanged as the
+// first parameter.
+static inline void Apu_dmc_reader( struct Nes_Apu* this, int (*func)( void*, addr_t ), void* user_data )
+{
+	this->dmc.prg_reader_data = user_data;
+	this->dmc.prg_reader = func;
+}
+
+// Set IRQ time callback that is invoked when the time of earliest IRQ
+// may have changed, or NULL to disable. When callback is invoked,
+// 'user_data' is passed unchanged as the first parameter.
+static inline void Apu_irq_notifier( struct Nes_Apu* this, void (*func)( void* user_data ), void* user_data )
+{
+	this->irq_notifier_ = func;
+	this->irq_data = user_data;
+}
+
+// Count number of DMC reads that would occur if 'run_until( t )' were executed.
+// If last_read is not NULL, set *last_read to the earliest time that
+// 'count_dmc_reads( time )' would result in the same result.
+static inline int Apu_count_dmc_reads( struct Nes_Apu* this, nes_time_t time, nes_time_t* last_read )
+{
+	return Dmc_count_reads( &this->dmc, time, last_read );
+}
+
+static inline nes_time_t Dmc_next_read_time( struct Nes_Dmc* this )
+{
+	if ( this->osc.length_counter == 0 )
+		return apu_no_irq; // not reading
+	
+	return this->apu->last_dmc_time + this->osc.delay + (long) (this->bits_remain - 1) * this->period;
+}
+
+// Time when next DMC memory read will occur
+static inline nes_time_t Apu_next_dmc_read_time( struct Nes_Apu* this ) { return Dmc_next_read_time( &this->dmc ); }
+void Apu_irq_changed( struct Nes_Apu* this ) ICODE_ATTR;
+
+#endif
diff --git a/apps/codecs/libgme/nes_cpu.c b/apps/codecs/libgme/nes_cpu.c
new file mode 100644
index 0000000..d255cf5
--- /dev/null
+++ b/apps/codecs/libgme/nes_cpu.c
@@ -0,0 +1,62 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "nes_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 Nes_Cpu* this, int i, void const* p )
+{
+	byte const* p2 = STATIC_CAST(byte const*,p) - NES_CPU_OFFSET( i * page_size );
+	this->cpu_state->code_map [i] = p2;
+	this->cpu_state_.code_map [i] = p2;
+}
+
+void Cpu_map_code( struct Nes_Cpu* this, addr_t start, int size, void const* data, int mirror_size )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size  % page_size == 0 );
+	require( start + size <= 0x10000 );
+	require( mirror_size % page_size == 0 );
+	
+	int offset;
+	for ( offset = 0; offset < size; offset += page_size )
+		set_code_page( this, NES_CPU_PAGE( start + offset ),
+				STATIC_CAST(char const*,data) + (offset & ((unsigned) mirror_size - 1)) );
+}
+
+void Cpu_reset( struct Nes_Cpu* this, void const* unmapped_page )
+{
+	check( this->cpu_state == &this->cpu_state_ );
+	this->cpu_state = &this->cpu_state_;
+	
+	this->r.flags = irq_inhibit_mask;
+	this->r.sp = 0xFF;
+	this->r.pc = 0;
+	this->r.a  = 0;
+	this->r.x  = 0;
+	this->r.y  = 0;
+	
+	this->cpu_state_.time = 0;
+	this->cpu_state_.base = 0;
+	this->irq_time = future_time;
+	this->end_time = future_time;
+	
+	set_code_page( this, page_count, unmapped_page );
+	Cpu_map_code( this, 0, 0x10000, unmapped_page, page_size );
+	
+	blargg_verify_byte_order();
+}
diff --git a/apps/codecs/libgme/nes_cpu.h b/apps/codecs/libgme/nes_cpu.h
new file mode 100644
index 0000000..7129ffd
--- /dev/null
+++ b/apps/codecs/libgme/nes_cpu.h
@@ -0,0 +1,106 @@
+// NES cpu emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef NES_CPU_H
+#define NES_CPU_H
+
+#include "blargg_common.h"
+#include "blargg_source.h"
+
+typedef int nes_time_t;
+typedef int addr_t;
+
+enum { page_bits = 11 };
+enum { page_size = 1 << page_bits };
+enum { page_count = 0x10000 >> page_bits };
+
+// Unmapped page should be filled with this
+enum { halt_opcode = 0x22 };
+
+enum { future_time = INT_MAX/2 + 1 };
+enum { irq_inhibit_mask = 0x04 };
+	
+// Can read this many bytes past end of a page
+enum { cpu_padding = 8 };
+
+struct registers_t {
+	uint16_t pc;
+	uint8_t a;
+	uint8_t x;
+	uint8_t y;
+	uint8_t flags;
+	uint8_t sp;
+};
+
+struct cpu_state_t {
+	uint8_t const* code_map [page_count + 1];
+	nes_time_t base;
+	int time;
+};
+
+struct Nes_Cpu {
+	// NES 6502 registers. NOT kept updated during emulation.
+	struct registers_t r;
+	nes_time_t irq_time;
+	nes_time_t end_time;
+	
+	struct cpu_state_t* cpu_state; // points to cpu_state_ or a local copy
+	struct cpu_state_t cpu_state_;
+};
+
+static inline void Cpu_init( struct Nes_Cpu* this ) { this->cpu_state = &this->cpu_state_; }
+
+// Clears registers and maps all pages to unmapped_page
+void Cpu_reset( struct Nes_Cpu* this, void const* unmapped_page );
+	
+// Maps code memory (memory accessed via the program counter). Start and size
+// must be multiple of page_size. If mirror_size is non-zero, the first
+// mirror_size bytes are repeated over the range. mirror_size must be a
+// multiple of page_size.
+void Cpu_map_code( struct Nes_Cpu* this, addr_t start, int size, void const* code, int mirror_size );
+	
+// Time of beginning of next instruction to be executed
+static inline nes_time_t Cpu_time( struct Nes_Cpu* this ) { return this->cpu_state->time + this->cpu_state->base; }
+static inline void Cpu_set_time( struct Nes_Cpu* this, nes_time_t t ) { this->cpu_state->time = t - this->cpu_state->base; }
+static inline void Cpu_adjust_time( struct Nes_Cpu* this, int delta ) { this->cpu_state->time += delta; }
+	
+// Clocks past end (negative if before)
+static inline int Cpu_time_past_end( struct Nes_Cpu* this ) { return this->cpu_state->time; }
+			
+#define NES_CPU_PAGE( addr ) ((unsigned) (addr) >> page_bits)
+
+#ifdef BLARGG_NONPORTABLE
+	#define NES_CPU_OFFSET( addr ) (addr)
+#else
+	#define NES_CPU_OFFSET( addr ) ((addr) & (page_size - 1))
+#endif
+
+// Accesses emulated memory as cpu does
+static inline uint8_t const* Cpu_get_code( struct Nes_Cpu* this, addr_t addr )
+{
+	return this->cpu_state_.code_map [NES_CPU_PAGE( addr )] + NES_CPU_OFFSET( addr );
+}
+
+static inline void Cpu_update_end_time( struct Nes_Cpu* this, nes_time_t end, nes_time_t irq )
+{
+	if ( end > irq && !(this->r.flags & irq_inhibit_mask) )
+		end = irq;
+	
+	this->cpu_state->time += this->cpu_state->base - end;
+	this->cpu_state->base = end;
+}
+
+// Time of next IRQ
+static inline void Cpu_set_irq_time( struct Nes_Cpu* this, nes_time_t t )
+{
+	this->irq_time = t;
+	Cpu_update_end_time( this, this->end_time, t );
+}
+
+static inline void Cpu_set_end_time( struct Nes_Cpu* this, nes_time_t t )
+{
+	this->end_time = t;
+	Cpu_update_end_time( this, t, this->irq_time );
+}   
+
+#endif
diff --git a/apps/codecs/libgme/nes_cpu_io.h b/apps/codecs/libgme/nes_cpu_io.h
new file mode 100644
index 0000000..4f9d416
--- /dev/null
+++ b/apps/codecs/libgme/nes_cpu_io.h
@@ -0,0 +1,94 @@
+
+#include "nsf_emu.h"
+
+#ifndef NSF_EMU_APU_ONLY
+	#include "nes_namco_apu.h"
+	#include "nes_fds_apu.h"
+	#include "nes_mmc5_apu.h"
+#endif
+
+#include "blargg_source.h"
+
+int Cpu_read( struct Nsf_Emu* this, nes_addr_t addr )
+{
+	int result = this->cpu.low_mem [addr & 0x7FF];
+	if ( addr & 0xE000 )
+	{
+		result = *Cpu_get_code( &this->cpu, addr );
+		if ( addr < sram_addr ) 
+		{
+			if ( addr == status_addr )
+				result = Apu_read_status( &this->apu, Cpu_time( &this->cpu ) );
+			else 
+			{
+				#ifndef NSF_EMU_APU_ONLY
+					if ( namco_enabled( this ) && addr == namco_data_reg_addr )
+						return Namco_read_data( &this->namco );
+			
+					if ( fds_enabled( this ) && (unsigned) (addr - fds_io_addr) < fds_io_size )
+						return Fds_read( &this->fds, Cpu_time( &this->cpu ), addr );
+			
+					if ( mmc5_enabled( this ) ) {
+						int i = addr - 0x5C00;
+						if ( (unsigned) i < mmc5_exram_size )
+							return this->mmc5.exram [i];
+		
+						int m = addr - 0x5205;
+						if ( (unsigned) m < 2 )
+							return (this->mmc5_mul [0] * this->mmc5_mul [1]) >> (m * 8) & 0xFF;
+					}
+				#endif
+				result = addr >> 8; // simulate open bus
+			}
+		}
+	}
+	
+	/* if ( addr != 0x2002 )
+		debug_printf( "Read unmapped $%.4X\n", (unsigned) addr ); */
+	
+	return result;
+}
+
+void Cpu_write( struct Nsf_Emu* this, nes_addr_t addr, int data )
+{	
+	int offset = addr - sram_addr;
+	if ( (unsigned) offset < sram_size )
+	{
+		this->sram [offset] = data;
+	}
+	else
+	{
+		// after sram because cpu handles most low_ram accesses internally already
+		int temp = addr & (low_ram_size-1); // also handles wrap-around
+		if ( !(addr & 0xE000) )
+		{
+			this->cpu.low_mem [temp] = data;
+		}
+		else
+		{
+			int bank = addr - banks_addr;
+			if ( (unsigned) bank < bank_count )
+			{
+				Write_bank( this, bank, data );
+			}
+			else if ( (unsigned) (addr - start_addr) <= end_addr - start_addr )
+			{
+				Apu_write_register(  &this->apu, Cpu_time( &this->cpu ), addr, data );
+			}
+			else
+			{
+			#ifndef NSF_EMU_APU_ONLY
+				// 0x8000-0xDFFF is writable
+				int i = addr - 0x8000;
+				if ( fds_enabled( this ) && (unsigned) i < fdsram_size )
+					fdsram( this ) [i] = data;
+				else
+			#endif
+				Cpu_write_misc( this, addr, data );
+			}
+		}
+	}
+}
+
+#define CPU_READ( emu, addr, time )         Cpu_read( emu, addr )
+#define CPU_WRITE( emu, addr, data, time )  Cpu_write( emu, addr, data )
diff --git a/apps/codecs/libgme/nes_cpu_run.h b/apps/codecs/libgme/nes_cpu_run.h
new file mode 100644
index 0000000..5b964d5
--- /dev/null
+++ b/apps/codecs/libgme/nes_cpu_run.h
@@ -0,0 +1,1122 @@
+// NES 6502 cpu emulator run function

+

+#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.

+- Except where noted, time() and related functions will NOT work

+correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and

+CACHE_TIME() allow the time changing functions to work.

+- 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 );

+	// 0 <= READ_MEM() <= 0xFF

+	

+	// 0 <= addr <= 0x1FF

+	int  READ_LOW(  addr_t );

+	void WRITE_LOW( addr_t, int data );

+	// 0 <= READ_LOW() <= 0xFF

+

+	// Often-used instructions attempt these before using a normal memory access.

+	// Optional; defaults to READ_MEM() and WRITE_MEM()

+	bool CAN_READ_FAST( addr_t ); // if true, uses result of READ_FAST

+	void READ_FAST( addr_t, int& out ); // ALWAYS called BEFORE CAN_READ_FAST

+	bool CAN_WRITE_FAST( addr_t ); // if true, uses WRITE_FAST instead of WRITE_MEM

+	void WRITE_FAST( addr_t, int data );

+

+	// Used by instructions most often used to access the NES PPU (LDA abs and BIT abs).

+	// Optional; defaults to READ_MEM.

+	void READ_PPU(  addr_t, int& out );

+	// 0 <= out <= 0xFF

+

+// The following can be used within macros:

+	

+	// Current time

+	time_t TIME();

+	

+	// Allows use of time functions

+	void FLUSH_TIME();

+	

+	// Must be used before end of macro if FLUSH_TIME() was used earlier

+	void CACHE_TIME();

+

+// Configuration (optional; commented behavior if defined)

+	

+	// Emulates dummy reads for indexed instructions

+	#define NES_CPU_DUMMY_READS 1

+	

+	// Optimizes as if map_code( 0, 0x10000 + cpu_padding, FLAT_MEM ) is always in effect

+	#define FLAT_MEM my_mem_array

+	

+	// Expanded just before beginning of code, to help debugger

+	#define CPU_BEGIN void my_run_cpu() {

+	

+#endif

+

+/* 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 */

+

+// Allows MWCW debugger to step through code properly

+#ifdef CPU_BEGIN

+	CPU_BEGIN

+#endif

+

+// Time

+#define TIME()          (s_time + s.base)

+#define FLUSH_TIME()    {s.time = s_time - time_offset;}

+#define CACHE_TIME()    {s_time = s.time + time_offset;}

+

+// Defaults

+#ifndef CAN_WRITE_FAST

+	#define CAN_WRITE_FAST( addr )      0

+	#define WRITE_FAST( addr, data )

+#endif

+

+#ifndef CAN_READ_FAST

+	#define CAN_READ_FAST( addr )       0

+	#define READ_FAST( addr, out )

+#endif

+

+#ifndef READ_PPU

+	#define READ_PPU( addr, out )\

+	{\

+		FLUSH_TIME();\

+		out = READ_MEM( addr );\

+		CACHE_TIME();\

+	}

+#endif

+

+#define READ_STACK  READ_LOW

+#define WRITE_STACK WRITE_LOW

+

+// Dummy reads

+#ifdef NES_CPU_DUMMY_READS

+	// TODO: optimize time handling

+	#define DUMMY_READ( addr, idx ) \

+		if ( (addr & 0xFF) < idx )\

+		{\

+			int const time_offset = 1;\

+			FLUSH_TIME();\

+			READ_MEM( (addr - 0x100) );\

+			CACHE_TIME();\

+		}

+#else

+	#define DUMMY_READ( addr, idx )

+#endif

+

+// Code

+#ifdef FLAT_MEM

+	#define CODE_PAGE(   addr ) (FLAT_MEM)

+	#define CODE_OFFSET( addr ) (addr)

+#else

+	#define CODE_PAGE( addr )   (s.code_map [NES_CPU_PAGE( addr )])

+	#define CODE_OFFSET( addr ) NES_CPU_OFFSET( addr )

+#endif

+#define READ_CODE( addr )   (CODE_PAGE( addr ) [CODE_OFFSET( addr )])

+

+// Stack

+#define SET_SP( v ) (sp = ((v) + 1) | 0x100)

+#define GET_SP()    ((sp - 1) & 0xFF)

+#define SP( o )     ((sp + (o - (o>0)*0x100)) | 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 */

+

+// Flags with hex value for clarity when used as mask.

+// Stored in indicated variable during emulation.

+int const n80 = 0x80; // nz

+int const v40 = 0x40; // flags

+int const r20 = 0x20;

+int const b10 = 0x10;

+int const d08 = 0x08; // flags

+int const i04 = 0x04; // flags

+int const z02 = 0x02; // nz

+int const c01 = 0x01; // c

+

+#define IS_NEG (nz & 0x8080)

+

+#define GET_FLAGS( out ) \

+{\

+	out = flags & (v40 | d08 | i04);\

+	out += ((nz >> 8) | nz) & n80;\

+	out += c >> 8 & c01;\

+	if ( !BYTE( nz ) )\

+		out += z02;\

+}

+

+#define SET_FLAGS( in ) \

+{\

+	flags = in & (v40 | d08 | i04);\

+	c = nz = in << 8;\

+	nz += ~in & z02;\

+}

+

+{

+	int const time_offset = 0;

+	

+	// Local state

+	struct cpu_state_t s;

+	#ifdef FLAT_MEM

+		s.base = cpu->cpu_state_.base;

+	#else

+		s = cpu->cpu_state_;

+	#endif

+	cpu->cpu_state = &s;

+	int s_time = cpu->cpu_state_.time; // helps even on x86

+	

+	// Registers

+	int pc = cpu->r.pc;

+	int a  = cpu->r.a;

+	int x  = cpu->r.x;

+	int y  = cpu->r.y;

+	int sp;

+	SET_SP( cpu->r.sp );

+	

+	// Flags

+	int flags;

+	int c;  // carry set if (c & 0x100) != 0

+	int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0

+	{

+		int temp = cpu->r.flags;

+		SET_FLAGS( temp );

+	}

+	

+loop:

+	

+	// Check all values

+	check( (unsigned) sp - 0x100 < 0x100 );

+	check( (unsigned) pc < 0x10000 );

+	check( (unsigned) a < 0x100 );

+	check( (unsigned) x < 0x100 );

+	check( (unsigned) y < 0x100 );

+	

+	// Read instruction

+	byte const* instr = CODE_PAGE( pc );

+	int opcode;

+	

+	if ( CODE_OFFSET(~0) == ~0 )

+	{

+		opcode = instr [pc];

+		pc++;

+		instr += pc;

+	}

+	else

+	{

+		instr += CODE_OFFSET( pc );

+		opcode = *instr++;

+		pc++;

+	}

+	

+	// local to function in case it helps optimizer

+	static byte const clock_table [256] =

+	{// 0 1 2 3 4 5 6 7 8 9 A B C D E F

+		0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0

+		2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1

+		6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2

+		2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3

+		6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4

+		2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5

+		6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6

+		2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7

+		2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8

+		2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9

+		2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A

+		2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B

+		2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C

+		2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D

+		2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E

+		2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F

+	}; // 0x00 was 7 and 0x22 was 2

+	

+	// Update time

+	if ( s_time >= 0 )

+		goto out_of_time;

+	

+	#ifdef CPU_INSTR_HOOK

+	{ CPU_INSTR_HOOK( (pc-1), (&instr [-1]), a, x, y, GET_SP(), TIME() ); }

+	#endif

+	

+	s_time += clock_table [opcode];

+	

+	int data;

+	data = *instr;

+	

+	switch ( opcode )

+	{

+

+// Macros

+

+#define GET_MSB()       (instr [1])

+#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB())

+#define GET_ADDR()      GET_LE16( instr )

+

+#define PAGE_PENALTY( lsb ) s_time += (lsb) >> 8;

+

+#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop;

+

+#define IND_Y( cross, out ) {\

+		int temp = READ_LOW( data ) + y;\

+		out = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\

+		cross( temp );\

+	}

+	

+#define IND_X( out ) {\

+		int temp = data + x;\

+		out = 0x100 * READ_LOW( BYTE( temp + 1 ) ) + READ_LOW( BYTE( temp ) );\

+	}

+	

+#define ARITH_ADDR_MODES( op )\

+case op - 0x04: /* (ind,x) */\

+	IND_X( data )\

+	goto ptr##op;\

+case op + 0x0C: /* (ind),y */\

+	IND_Y( PAGE_PENALTY, data )\

+	goto ptr##op;\

+case op + 0x10: /* zp,X */\

+	data = BYTE( data + x );\

+case op + 0x00: /* zp */\

+	data = READ_LOW( data );\

+	goto imm##op;\

+case op + 0x14: /* abs,Y */\

+	data += y;\

+	goto ind##op;\

+case op + 0x18: /* abs,X */\

+	data += x;\

+ind##op:\

+	PAGE_PENALTY( data );\

+case op + 0x08: /* abs */\

+	ADD_PAGE( data );\

+ptr##op:\

+	FLUSH_TIME();\

+	data = READ_MEM( data );\

+	CACHE_TIME();\

+case op + 0x04: /* imm */\

+imm##op:

+

+// TODO: more efficient way to handle negative branch that wraps PC around

+#define BRANCH( cond )\

+{\

+	++pc;\

+	if ( !(cond) ) goto loop;\

+	s_time++;\

+	int offset = SBYTE( data );\

+	s_time += (BYTE(pc) + offset) >> 8 & 1;\

+	pc = WORD( pc + offset );\

+	goto loop;\

+}

+

+// Often-Used

+

+	case 0xB5: // LDA zp,x

+		a = nz = READ_LOW( BYTE( data + x ) );

+		pc++;

+		goto loop;

+	

+	case 0xA5: // LDA zp

+		a = nz = READ_LOW( data );

+		pc++;

+		goto loop;

+	

+	case 0xD0: // BNE

+		BRANCH( BYTE( nz ) );

+	

+	case 0x20: { // JSR

+		int temp = pc + 1;

+		pc = GET_ADDR();

+		WRITE_STACK( SP( -1 ), temp >> 8 );

+		sp = SP( -2 );

+		WRITE_STACK( sp, temp );

+		goto loop;

+	}

+	

+	case 0x4C: // JMP abs

+		pc = GET_ADDR();

+		goto loop;

+	

+	case 0xE8: // INX

+		INC_DEC( x, 1 )

+	

+	case 0x10: // BPL

+		BRANCH( !IS_NEG )

+	

+	ARITH_ADDR_MODES( 0xC5 ) // CMP

+		nz = a - data;

+		pc++;

+		c = ~nz;

+		nz &= 0xFF;

+		goto loop;

+	

+	case 0x30: // BMI

+		BRANCH( IS_NEG )

+	

+	case 0xF0: // BEQ

+		BRANCH( !BYTE( nz ) );

+	

+	case 0x95: // STA zp,x

+		data = BYTE( data + x );

+	case 0x85: // STA zp

+		pc++;

+		WRITE_LOW( data, a );

+		goto loop;

+	

+	case 0xC8: // INY

+		INC_DEC( y, 1 )

+

+	case 0xA8: // TAY

+		y  = a;

+		nz = a;

+		goto loop;

+	

+	case 0x98: // TYA

+		a  = y;

+		nz = y;

+		goto loop;

+	

+	case 0xAD:{// LDA abs

+		int addr = GET_ADDR();

+		pc += 2;

+		READ_PPU( addr, a = nz );

+		goto loop;

+	}

+	

+	case 0x60: // RTS

+		pc = 1 + READ_STACK( sp );

+		pc += 0x100 * READ_STACK( SP( 1 ) );

+		sp = SP( 2 );

+		goto loop;

+	

+	{

+		int addr;

+		

+	case 0x8D: // STA abs

+		addr = GET_ADDR();

+		pc += 2;

+		if ( CAN_WRITE_FAST( addr ) )

+		{

+			WRITE_FAST( addr, a );

+			goto loop;

+		}

+	sta_ptr:

+		FLUSH_TIME();

+		WRITE_MEM( addr, a );

+		CACHE_TIME();

+		goto loop;

+	

+	case 0x99: // STA abs,Y

+		addr = y + GET_ADDR();

+		pc += 2;

+		if ( CAN_WRITE_FAST( addr ) )

+		{

+			WRITE_FAST( addr, a );

+			goto loop;

+		}

+		goto sta_abs_x;

+	

+	case 0x9D: // STA abs,X (slightly more common than STA abs)

+		addr = x + GET_ADDR();

+		pc += 2;

+		if ( CAN_WRITE_FAST( addr ) )

+		{

+			WRITE_FAST( addr, a );

+			goto loop;

+		}

+		DUMMY_READ( addr, x );

+	sta_abs_x:

+		FLUSH_TIME();

+		WRITE_MEM( addr, a );

+		CACHE_TIME();

+		goto loop;

+	

+	case 0x91: // STA (ind),Y

+		#define NO_PAGE_PENALTY( lsb )

+		IND_Y( NO_PAGE_PENALTY, addr )

+		pc++;

+		DUMMY_READ( addr, y );

+		goto sta_ptr;

+	

+	case 0x81: // STA (ind,X)

+		IND_X( addr )

+		pc++;

+		goto sta_ptr;

+	

+	}

+	

+	case 0xA9: // LDA #imm

+		pc++;

+		a  = data;

+		nz = data;

+		goto loop;

+

+	// common read instructions

+	{

+		int addr;

+		

+	case 0xA1: // LDA (ind,X)

+		IND_X( addr )

+		pc++;

+		goto a_nz_read_addr;

+	

+	case 0xB1:// LDA (ind),Y

+		addr = READ_LOW( data ) + y;

+		PAGE_PENALTY( addr );

+		addr += 0x100 * READ_LOW( BYTE( data + 1 ) );

+		pc++;

+		READ_FAST( addr, a = nz );

+		if ( CAN_READ_FAST( addr ) )

+			goto loop;

+		DUMMY_READ( addr, y );

+		goto a_nz_read_addr;

+	

+	case 0xB9: // LDA abs,Y

+		PAGE_PENALTY( data + y );

+		addr = GET_ADDR() + y;

+		pc += 2;

+		READ_FAST( addr, a = nz );

+		if ( CAN_READ_FAST( addr ) )

+			goto loop;

+		goto a_nz_read_addr;

+	

+	case 0xBD: // LDA abs,X

+		PAGE_PENALTY( data + x );

+		addr = GET_ADDR() + x;

+		pc += 2;

+		READ_FAST( addr, a = nz );

+		if ( CAN_READ_FAST( addr ) )

+			goto loop;

+		DUMMY_READ( addr, x );

+	a_nz_read_addr:

+		FLUSH_TIME();

+		a = nz = READ_MEM( addr );

+		CACHE_TIME();

+		goto loop;

+	

+	}

+

+// Branch

+

+	case 0x50: // BVC

+		BRANCH( !(flags & v40) )

+	

+	case 0x70: // BVS

+		BRANCH( flags & v40 )

+	

+	case 0xB0: // BCS

+		BRANCH( c & 0x100 )

+	

+	case 0x90: // BCC

+		BRANCH( !(c & 0x100) )

+	

+// Load/store

+	

+	case 0x94: // STY zp,x

+		data = BYTE( data + x );

+	case 0x84: // STY zp

+		pc++;

+		WRITE_LOW( data, y );

+		goto loop;

+	

+	case 0x96: // STX zp,y

+		data = BYTE( data + y );

+	case 0x86: // STX zp

+		pc++;

+		WRITE_LOW( data, x );

+		goto loop;

+	

+	case 0xB6: // LDX zp,y

+		data = BYTE( data + y );

+	case 0xA6: // LDX zp

+		data = READ_LOW( data );

+	case 0xA2: // LDX #imm

+		pc++;

+		x = data;

+		nz = data;

+		goto loop;

+	

+	case 0xB4: // LDY zp,x

+		data = BYTE( data + x );

+	case 0xA4: // LDY zp

+		data = READ_LOW( data );

+	case 0xA0: // LDY #imm

+		pc++;

+		y = data;

+		nz = data;

+		goto loop;

+	

+	case 0xBC: // LDY abs,X

+		data += x;

+		PAGE_PENALTY( data );

+	case 0xAC:{// LDY abs

+		int addr = data + 0x100 * GET_MSB();

+		pc += 2;

+		FLUSH_TIME();

+		y = nz = READ_MEM( addr );

+		CACHE_TIME();

+		goto loop;

+	}

+	

+	case 0xBE: // LDX abs,y

+		data += y;

+		PAGE_PENALTY( data );

+	case 0xAE:{// LDX abs

+		int addr = data + 0x100 * GET_MSB();

+		pc += 2;

+		FLUSH_TIME();

+		x = nz = READ_MEM( addr );

+		CACHE_TIME();

+		goto loop;

+	}

+	

+	{

+		int temp;

+	case 0x8C: // STY abs

+		temp = y;

+		goto store_abs;

+	

+	case 0x8E: // STX abs

+		temp = x;

+	store_abs:

+		{

+			int addr = GET_ADDR();

+			pc += 2;

+			if ( CAN_WRITE_FAST( addr ) )

+			{

+				WRITE_FAST( addr, temp );

+				goto loop;

+			}

+			FLUSH_TIME();

+			WRITE_MEM( addr, temp );

+			CACHE_TIME();

+			goto loop;

+		}

+	}

+

+// Compare

+

+	case 0xEC: {// CPX abs

+		int addr = GET_ADDR();

+		pc++;

+		FLUSH_TIME();

+		data = READ_MEM( addr );

+		CACHE_TIME();

+		goto cpx_data;

+	}

+	

+	case 0xE4: // CPX zp

+		data = READ_LOW( data );

+	case 0xE0: // CPX #imm

+	cpx_data:

+		nz = x - data;

+		pc++;

+		c = ~nz;

+		nz &= 0xFF;

+		goto loop;

+	

+	case 0xCC:{// CPY abs

+		int addr = GET_ADDR();

+		pc++;

+		FLUSH_TIME();

+		data = READ_MEM( addr );

+		CACHE_TIME();

+		goto cpy_data;

+	}

+	

+	case 0xC4: // CPY zp

+		data = READ_LOW( data );

+	case 0xC0: // CPY #imm

+	cpy_data:

+		nz = y - data;

+		pc++;

+		c = ~nz;

+		nz &= 0xFF;

+		goto loop;

+	

+// Logical

+

+	ARITH_ADDR_MODES( 0x25 ) // AND

+		nz = (a &= data);

+		pc++;

+		goto loop;

+	

+	ARITH_ADDR_MODES( 0x45 ) // EOR

+		nz = (a ^= data);

+		pc++;

+		goto loop;

+	

+	ARITH_ADDR_MODES( 0x05 ) // ORA

+		nz = (a |= data);

+		pc++;

+		goto loop;

+	

+	case 0x2C:{// BIT abs

+		int addr = GET_ADDR();

+		pc += 2;

+		READ_PPU( addr, nz );

+		flags = (flags & ~v40) + (nz & v40);

+		if ( a & nz )

+			goto loop;

+		nz <<= 8; // result must be zero, even if N bit is set

+		goto loop;

+	}

+	

+	case 0x24: // BIT zp

+		nz = READ_LOW( data );

+		pc++;

+		flags = (flags & ~v40) + (nz & v40);

+		if ( a & nz )

+			goto loop; // Z should be clear, and nz must be non-zero if nz & a is

+		nz <<= 8; // set Z flag without affecting N flag

+		goto loop;

+		

+// Add/subtract

+

+	ARITH_ADDR_MODES( 0xE5 ) // SBC

+	case 0xEB: // unofficial equivalent

+		data ^= 0xFF;

+		goto adc_imm;

+	

+	ARITH_ADDR_MODES( 0x65 ) // ADC

+	adc_imm: {

+		int carry = c >> 8 & 1;

+		int ov = (a ^ 0x80) + carry + SBYTE( data );

+		flags = (flags & ~v40) + (ov >> 2 & v40);

+		c = nz = a + data + carry;

+		pc++;

+		a = BYTE( nz );

+		goto loop;

+	}

+	

+// Shift/rotate

+

+	case 0x4A: // LSR A

+		c = 0;

+	case 0x6A: // ROR A

+		nz = c >> 1 & 0x80;

+		c = a << 8;

+		nz += a >> 1;

+		a = nz;

+		goto loop;

+

+	case 0x0A: // ASL A

+		nz = a << 1;

+		c = nz;

+		a = BYTE( nz );

+		goto loop;

+

+	case 0x2A: { // ROL A

+		nz = a << 1;

+		int temp = c >> 8 & 1;

+		c = nz;

+		nz += temp;

+		a = BYTE( nz );

+		goto loop;

+	}

+	

+	case 0x5E: // LSR abs,X

+		data += x;

+	case 0x4E: // LSR abs

+		c = 0;

+	case 0x6E: // ROR abs

+	ror_abs: {

+		ADD_PAGE( data );

+		FLUSH_TIME();

+		int temp = READ_MEM( data );

+		nz = (c >> 1 & 0x80) + (temp >> 1);

+		c = temp << 8;

+		goto rotate_common;

+	}

+	

+	case 0x3E: // ROL abs,X

+		data += x;

+		goto rol_abs;

+	

+	case 0x1E: // ASL abs,X

+		data += x;

+	case 0x0E: // ASL abs

+		c = 0;

+	case 0x2E: // ROL abs

+	rol_abs:

+		ADD_PAGE( data );

+		nz = c >> 8 & 1;

+		FLUSH_TIME();

+		nz += (c = READ_MEM( data ) << 1);

+	rotate_common:

+		pc++;

+		WRITE_MEM( data, BYTE( nz ) );

+		CACHE_TIME();

+		goto loop;

+	

+	case 0x7E: // ROR abs,X

+		data += x;

+		goto ror_abs;

+	

+	case 0x76: // ROR zp,x

+		data = BYTE( data + x );

+		goto ror_zp;

+	

+	case 0x56: // LSR zp,x

+		data = BYTE( data + x );

+	case 0x46: // LSR zp

+		c = 0;

+	case 0x66: // ROR zp

+	ror_zp: {

+		int temp = READ_LOW( data );

+		nz = (c >> 1 & 0x80) + (temp >> 1);

+		c = temp << 8;

+		goto write_nz_zp;

+	}

+	

+	case 0x36: // ROL zp,x

+		data = BYTE( data + x );

+		goto rol_zp;

+	

+	case 0x16: // ASL zp,x

+		data = BYTE( data + x );

+	case 0x06: // ASL zp

+		c = 0;

+	case 0x26: // ROL zp

+	rol_zp:

+		nz = c >> 8 & 1;

+		nz += (c = READ_LOW( data ) << 1);

+		goto write_nz_zp;

+	

+// Increment/decrement

+

+	case 0xCA: // DEX

+		INC_DEC( x, -1 )

+	

+	case 0x88: // DEY

+		INC_DEC( y, -1 )

+	

+	case 0xF6: // INC zp,x

+		data = BYTE( data + x );

+	case 0xE6: // INC zp

+		nz = 1;

+		goto add_nz_zp;

+	

+	case 0xD6: // DEC zp,x

+		data = BYTE( data + x );

+	case 0xC6: // DEC zp

+		nz = -1;

+	add_nz_zp:

+		nz += READ_LOW( data );

+	write_nz_zp:

+		pc++;

+		WRITE_LOW( data, nz );

+		goto loop;

+	

+	case 0xFE: // INC abs,x

+		data = x + GET_ADDR();

+		goto inc_ptr;

+	

+	case 0xEE: // INC abs

+		data = GET_ADDR();

+	inc_ptr:

+		nz = 1;

+		goto inc_common;

+	

+	case 0xDE: // DEC abs,x

+		data = x + GET_ADDR();

+		goto dec_ptr;

+	

+	case 0xCE: // DEC abs

+		data = GET_ADDR();

+	dec_ptr:

+		nz = -1;

+	inc_common:

+		FLUSH_TIME();

+		pc += 2;

+		nz += READ_MEM( data );

+		WRITE_MEM( data, BYTE( nz ) );

+		CACHE_TIME();

+		goto loop;

+		

+// Transfer

+

+	case 0xAA: // TAX

+		x = nz = a;

+		goto loop;

+		

+	case 0x8A: // TXA

+		a = nz = x;

+		goto loop;

+

+	case 0x9A: // TXS

+		SET_SP( x ); // verified (no flag change)

+		goto loop;

+	

+	case 0xBA: // TSX

+		x = nz = GET_SP();

+		goto loop;

+	

+// Stack

+	

+	case 0x48: // PHA

+		sp = SP( -1 );

+		WRITE_STACK( sp, a );

+		goto loop;

+		

+	case 0x68: // PLA

+		a = nz = READ_STACK( sp );

+		sp = SP( 1 );

+		goto loop;

+		

+	case 0x40:{// RTI

+		pc  = READ_STACK( SP( 1 ) );

+		pc += READ_STACK( SP( 2 ) ) * 0x100;

+		int temp = READ_STACK( sp );

+		sp = SP( 3 );

+		data = flags;

+		SET_FLAGS( temp );

+		cpu->r.flags = flags; // update externally-visible I flag

+		int delta = s.base - cpu->irq_time;

+		if ( delta <= 0 ) goto loop; // end_time < irq_time

+		if ( flags & i04 ) goto loop;

+		s_time += delta;

+		s.base = cpu->irq_time;

+		goto loop;

+	}

+	

+	case 0x28:{// PLP

+		int temp = READ_STACK( sp );

+		sp = SP( 1 );

+		int changed = flags ^ temp;

+		SET_FLAGS( temp );

+		if ( !(changed & i04) )

+			goto loop; // I flag didn't change

+		if ( flags & i04 )

+			goto handle_sei;

+		goto handle_cli;

+	}

+	

+	case 0x08:{// PHP

+		int temp;

+		GET_FLAGS( temp );

+		sp = SP( -1 );

+		WRITE_STACK( sp, temp | (b10 | r20) );

+		goto loop;

+	}

+	

+	case 0x6C:{// JMP (ind)

+		data = GET_ADDR();

+		byte const* page = CODE_PAGE( data );

+		pc = page [CODE_OFFSET( data )];

+		data = (data & 0xFF00) + ((data + 1) & 0xFF);

+		pc += page [CODE_OFFSET( data )] * 0x100;

+		goto loop;

+	}

+	

+	case 0x00: // BRK

+		goto handle_brk;

+	

+// Flags

+

+	case 0x38: // SEC

+		c = 0x100;

+		goto loop;

+	

+	case 0x18: // CLC

+		c = 0;

+		goto loop;

+		

+	case 0xB8: // CLV

+		flags &= ~v40;

+		goto loop;

+	

+	case 0xD8: // CLD

+		flags &= ~d08;

+		goto loop;

+	

+	case 0xF8: // SED

+		flags |= d08;

+		goto loop;

+	

+	case 0x58: // CLI

+		if ( !(flags & i04) )

+			goto loop;

+		flags &= ~i04;

+	handle_cli: {

+		//dprintf( "CLI at %d\n", TIME );

+		cpu->r.flags = flags; // update externally-visible I flag

+		int delta = s.base - cpu->irq_time;

+		if ( delta <= 0 )

+		{

+			if ( TIME() < cpu->irq_time )

+				goto loop;

+			goto delayed_cli;

+		}

+		s.base = cpu->irq_time;

+		s_time += delta;

+		if ( s_time < 0 )

+			goto loop;

+		

+		if ( delta >= s_time + 1 )

+		{

+			// delayed irq until after next instruction

+			s.base += s_time + 1;

+			s_time = -1;

+			goto loop;

+		}

+		

+		// TODO: implement

+	delayed_cli:

+		dprintf( "Delayed CLI not emulated\n" );

+		goto loop;

+	}

+	

+	case 0x78: // SEI

+		if ( flags & i04 )

+			goto loop;

+		flags |= i04;

+	handle_sei: {

+		cpu->r.flags = flags; // update externally-visible I flag

+		int delta = s.base - cpu->end_time;

+		s.base = cpu->end_time;

+		s_time += delta;

+		if ( s_time < 0 )

+			goto loop;

+		

+		dprintf( "Delayed SEI not emulated\n" );

+		goto loop;

+	}

+	

+// Unofficial

+	

+	// SKW - skip word

+	case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:

+		PAGE_PENALTY( data + x );

+	case 0x0C:

+		pc++;

+	// SKB - skip byte

+	case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64:

+	case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:

+		pc++;

+		goto loop;

+	

+	// NOP

+	case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:

+		goto loop;

+	

+	case halt_opcode: // HLT - halt processor

+		if ( pc-- > 0x10000 )

+		{

+			// handle wrap-around (assumes caller has put page of HLT at 0x10000)

+			pc = WORD( pc );

+			goto loop;

+		}

+	case 0x02: case 0x12:            case 0x32: case 0x42: case 0x52:

+	case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:

+		goto stop;

+	

+// Unimplemented

+	

+	case 0xFF:  // force 256-entry jump table for optimization purposes

+		c |= 1; // compiler doesn't know that this won't affect anything

+	default:

+		check( (unsigned) opcode < 0x100 );

+		

+		#ifdef UNIMPL_INSTR

+			UNIMPL_INSTR();

+		#endif

+		

+		// At least skip over proper number of bytes instruction uses

+		static unsigned char const illop_lens [8] = {

+			0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0

+		};

+		int opcode = instr [-1];

+		int len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;

+		if ( opcode == 0x9C )

+			len = 2;

+		pc += len;

+		

+		// Account for extra clock

+		if ( (opcode >> 4) == 0x0B )

+		{

+			if ( opcode == 0xB3 )

+				data = READ_LOW( data );

+			if ( opcode != 0xB7 )

+				PAGE_PENALTY( data + y );

+		}

+		goto loop;

+	}

+	assert( false ); // catch missing 'goto loop' or accidental 'break'

+	

+	int result_;

+handle_brk:

+	pc++;

+	result_ = b10 | 4;

+	

+#ifdef CPU_DONE

+interrupt:

+#endif

+	{

+		s_time += 7;

+		

+		// Save PC and read vector

+		WRITE_STACK( SP( -1 ), pc >> 8 );

+		WRITE_STACK( SP( -2 ), pc );

+		pc = GET_LE16( &READ_CODE( 0xFFFA ) + (result_ & 4) );

+		

+		// Save flags

+		int temp;

+		GET_FLAGS( temp );

+		temp |= r20 + (result_ & b10); // B flag set for BRK

+		sp = SP( -3 );

+		WRITE_STACK( sp, temp );

+		

+		// Update I flag in externally-visible flags

+		cpu->r.flags = (flags |= i04);

+		

+		// Update time

+		int delta = s.base - cpu->end_time;

+		if ( delta >= 0 )

+			goto loop;

+		s_time += delta;

+		s.base = cpu->end_time;

+		goto loop;

+	}

+	

+out_of_time:

+	pc--;

+	

+	// Optional action that triggers interrupt or changes irq/end time

+	#ifdef CPU_DONE

+	{

+		CPU_DONE( result_ );

+		if ( result_ >= 0 )

+			goto interrupt;

+		if ( s_time < 0 )

+			goto loop;

+	}

+	#endif

+stop:

+	

+	// Flush cached state

+	cpu->r.pc = pc;

+	cpu->r.sp = GET_SP();

+	cpu->r.a  = a;

+	cpu->r.x  = x;

+	cpu->r.y  = y;

+

+	int temp;

+	GET_FLAGS( temp );

+	cpu->r.flags = temp;

+	

+	cpu->cpu_state_.base = s.base;

+	cpu->cpu_state_.time = s_time;

+	cpu->cpu_state = &cpu->cpu_state_;

+}

diff --git a/apps/codecs/libgme/nes_fds_apu.c b/apps/codecs/libgme/nes_fds_apu.c
new file mode 100644
index 0000000..5142141
--- /dev/null
+++ b/apps/codecs/libgme/nes_fds_apu.c
@@ -0,0 +1,291 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "nes_fds_apu.h"
+
+/* 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"
+
+int const fract_range = 65536;
+
+void Fds_init( struct Nes_Fds_Apu* this )
+{
+	Synth_init( &this->synth );
+		
+	this->lfo_tempo = lfo_base_tempo;
+	Fds_set_output( this, 0, NULL );
+	Fds_volume( this, 1.0 );
+	Fds_reset( this );
+}
+
+void Fds_reset( struct Nes_Fds_Apu* this )
+{
+	memset( this->regs_, 0, sizeof this->regs_ );
+	memset( this->mod_wave, 0, sizeof this->mod_wave );
+	
+	this->last_time     = 0;
+	this->env_delay     = 0;
+	this->sweep_delay   = 0;
+	this->wave_pos      = 0;
+	this->last_amp      = 0;
+	this->wave_fract    = fract_range;
+	this->mod_fract     = fract_range;
+	this->mod_pos       = 0;
+	this->mod_write_pos = 0;
+	
+	static byte const initial_regs [0x0B] ICONST_ATTR = {
+		0x80,       // disable envelope
+		0, 0, 0xC0, // disable wave and lfo
+		0x80,       // disable sweep
+		0, 0, 0x80, // disable modulation
+		0, 0, 0xFF  // LFO period // TODO: use 0xE8 as FDS ROM does?
+	};
+	int i;
+	for ( i = 0; i < (int) sizeof initial_regs; i++ )
+	{
+		// two writes to set both gain and period for envelope registers
+		Fds_write_( this, fds_io_addr + fds_wave_size + i, 0 );
+		Fds_write_( this, fds_io_addr + fds_wave_size + i, initial_regs [i] );
+	}
+}
+
+void Fds_write_( struct Nes_Fds_Apu* this, unsigned addr, int data )
+{
+	unsigned reg = addr - fds_io_addr;
+	if ( reg < fds_io_size )
+	{
+		if ( reg < fds_wave_size )
+		{
+			if ( *regs_nes (this, 0x4089) & 0x80 )
+				this->regs_ [reg] = data & fds_wave_sample_max;
+		}
+		else
+		{
+			this->regs_ [reg] = data;
+			switch ( addr )
+			{
+			case 0x4080:
+				if ( data & 0x80 )
+					this->env_gain = data & 0x3F;
+				else
+					this->env_speed = (data & 0x3F) + 1;
+				break;
+			
+			case 0x4084:
+				if ( data & 0x80 )
+					this->sweep_gain = data & 0x3F;
+				else
+					this->sweep_speed = (data & 0x3F) + 1;
+				break;
+			
+			case 0x4085:
+				this->mod_pos = this->mod_write_pos;
+				*regs_nes (this, 0x4085) = data & 0x7F;
+				break;
+			
+			case 0x4088:
+				if ( *regs_nes (this, 0x4087) & 0x80 )
+				{
+					int pos = this->mod_write_pos;
+					data &= 0x07;
+					this->mod_wave [pos    ] = data;
+					this->mod_wave [pos + 1] = data;
+					this->mod_write_pos = (pos     + 2) & (fds_wave_size - 1);
+					this->mod_pos       = (this->mod_pos + 2) & (fds_wave_size - 1);
+				}
+				break;
+			}
+		}
+	}
+}
+
+void Fds_set_tempo( struct Nes_Fds_Apu* this, double t )
+{
+	this->lfo_tempo = lfo_base_tempo;
+	if ( t != 1.0 )
+	{
+		this->lfo_tempo = (int) ((double) lfo_base_tempo / t + 0.5);
+		if ( this->lfo_tempo <= 0 )
+			this->lfo_tempo = 1;
+	}
+}
+
+void Fds_run_until( struct Nes_Fds_Apu* this, blip_time_t final_end_time )
+{
+	int const wave_freq = (*regs_nes (this, 0x4083) & 0x0F) * 0x100 + *regs_nes (this, 0x4082);
+	struct Blip_Buffer* const output_ = this->output_;
+	if ( wave_freq && output_ && !((*regs_nes (this, 0x4089) | *regs_nes (this, 0x4083)) & 0x80) )
+	{
+		Blip_set_modified( output_ );
+		
+		// master_volume
+		#define MVOL_ENTRY( percent ) (fds_master_vol_max * percent + 50) / 100
+		static unsigned char const master_volumes [4] = {
+			MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 )
+		};
+		int const master_volume = master_volumes [*regs_nes (this, 0x4089) & 0x03];
+		
+		// lfo_period
+		blip_time_t lfo_period = *regs_nes (this, 0x408A) * this->lfo_tempo;
+		if ( *regs_nes (this, 0x4083) & 0x40 )
+			lfo_period = 0;
+		
+		// sweep setup
+		blip_time_t sweep_time = this->last_time + this->sweep_delay;
+		blip_time_t const sweep_period = lfo_period * this->sweep_speed;
+		if ( !sweep_period || *regs_nes (this, 0x4084) & 0x80 )
+			sweep_time = final_end_time;
+		
+		// envelope setup
+		blip_time_t env_time = this->last_time + this->env_delay;
+		blip_time_t const env_period = lfo_period * this->env_speed;
+		if ( !env_period || *regs_nes (this, 0x4080) & 0x80 )
+			env_time = final_end_time;
+		
+		// modulation
+		int mod_freq = 0;
+		if ( !(*regs_nes (this, 0x4087) & 0x80) )
+			mod_freq = (*regs_nes (this, 0x4087) & 0x0F) * 0x100 + *regs_nes (this, 0x4086);
+		
+		blip_time_t end_time = this->last_time;
+		do
+		{
+			// sweep
+			if ( sweep_time <= end_time )
+			{
+				sweep_time += sweep_period;
+				int mode = *regs_nes (this, 0x4084) >> 5 & 2;
+				int new_sweep_gain = this->sweep_gain + mode - 1;
+				if ( (unsigned) new_sweep_gain <= (unsigned) 0x80 >> mode )
+					this->sweep_gain = new_sweep_gain;
+				else
+					*regs_nes (this, 0x4084) |= 0x80; // optimization only
+			}
+			
+			// envelope
+			if ( env_time <= end_time )
+			{
+				env_time += env_period;
+				int mode = *regs_nes (this, 0x4080) >> 5 & 2;
+				int new_env_gain = this->env_gain + mode - 1;
+				if ( (unsigned) new_env_gain <= (unsigned) 0x80 >> mode )
+					this->env_gain = new_env_gain;
+				else
+					*regs_nes (this, 0x4080) |= 0x80; // optimization only
+			}
+			
+			// new end_time
+			blip_time_t const start_time = end_time;
+			end_time = final_end_time;
+			if ( end_time > env_time   ) end_time = env_time;
+			if ( end_time > sweep_time ) end_time = sweep_time;
+			
+			// frequency modulation
+			int freq = wave_freq;
+			if ( mod_freq )
+			{
+				// time of next modulation clock
+				blip_time_t mod_time = start_time + (this->mod_fract + mod_freq - 1) / mod_freq;
+				if ( end_time > mod_time )
+					end_time = mod_time;
+				
+				// run modulator up to next clock and save old sweep_bias
+				int sweep_bias = *regs_nes (this, 0x4085);
+				this->mod_fract -= (end_time - start_time) * mod_freq;
+				if ( this->mod_fract <= 0 )
+				{
+					this->mod_fract += fract_range;
+					check( (unsigned) this->mod_fract <= fract_range );
+					
+					static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 };
+					int mod = this->mod_wave [this->mod_pos];
+					this->mod_pos = (this->mod_pos + 1) & (fds_wave_size - 1);
+					int new_sweep_bias = (sweep_bias + mod_table [mod]) & 0x7F;
+					if ( mod == 4 )
+						new_sweep_bias = 0;
+					*regs_nes (this, 0x4085) = new_sweep_bias;
+				}
+				
+				// apply frequency modulation
+				sweep_bias = (sweep_bias ^ 0x40) - 0x40;
+				int factor = sweep_bias * this->sweep_gain;
+				int extra = factor & 0x0F;
+				factor >>= 4;
+				if ( extra )
+				{
+					factor--;
+					if ( sweep_bias >= 0 )
+						factor += 3;
+				}
+				if ( factor > 193 ) factor -= 258;
+				if ( factor < -64 ) factor += 256;
+				freq += (freq * factor) >> 6;
+				if ( freq <= 0 )
+					continue;
+			}
+			
+			// wave
+			int wave_fract = this->wave_fract;
+			blip_time_t delay = (wave_fract + freq - 1) / freq;
+			blip_time_t time = start_time + delay;
+			
+			if ( time <= end_time )
+			{
+				// at least one wave clock within start_time...end_time
+				
+				blip_time_t const min_delay = fract_range / freq;
+				int wave_pos = this->wave_pos;
+				
+				int volume = this->env_gain;
+				if ( volume > fds_vol_max )
+					volume = fds_vol_max;
+				volume *= master_volume;
+				
+				int const min_fract = min_delay * freq;
+				
+				do
+				{
+					// clock wave
+					int amp = this->regs_ [wave_pos] * volume;
+					wave_pos = (wave_pos + 1) & (fds_wave_size - 1);
+					int delta = amp - this->last_amp;
+					if ( delta )
+					{
+						this->last_amp = amp;
+						Synth_offset_inline( &this->synth, time, delta, output_ );
+					}
+					
+					wave_fract += fract_range - delay * freq;
+					check( unsigned (fract_range - wave_fract) < freq );
+					
+					// delay until next clock
+					delay = min_delay;
+					if ( wave_fract > min_fract )
+						delay++;
+					check( delay && delay == (wave_fract + freq - 1) / freq );
+					
+					time += delay;
+				}
+				while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong
+				
+				this->wave_pos = wave_pos;
+			}
+			this->wave_fract = wave_fract - (end_time - (time - delay)) * freq;
+			check( this->wave_fract > 0 );
+		}
+		while ( end_time < final_end_time );
+		
+		this->env_delay   = env_time   - final_end_time; check( env_delay >= 0 );
+		this->sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 );
+	}
+	this->last_time = final_end_time;
+}
diff --git a/apps/codecs/libgme/nes_fds_apu.h b/apps/codecs/libgme/nes_fds_apu.h
new file mode 100644
index 0000000..1360e44
--- /dev/null
+++ b/apps/codecs/libgme/nes_fds_apu.h
@@ -0,0 +1,116 @@
+// NES FDS sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef NES_FDS_APU_H
+#define NES_FDS_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+enum { lfo_base_tempo = 8 };
+enum { fds_osc_count = 1 };
+
+enum { fds_io_addr = 0x4040 };
+enum { fds_io_size = 0x53 };
+
+enum { fds_wave_size       = 0x40 };
+enum { fds_master_vol_max  =   10 };
+enum { fds_vol_max         = 0x20 };
+enum { fds_wave_sample_max = 0x3F };
+
+struct Nes_Fds_Apu {
+	unsigned char regs_ [fds_io_size];// last written value to registers
+	int lfo_tempo; // normally 8; adjusted by set_tempo()   
+	
+	int env_delay;
+	int env_speed;
+	int env_gain;
+	
+	int sweep_delay;
+	int sweep_speed;
+	int sweep_gain;
+	
+	int wave_pos;
+	int last_amp;
+	blip_time_t wave_fract;
+	
+	int mod_fract;
+	int mod_pos;
+	int mod_write_pos;
+	unsigned char mod_wave [fds_wave_size];
+	
+	// synthesis
+	blip_time_t last_time;
+	struct Blip_Buffer* output_;
+	struct Blip_Synth synth;
+};
+
+// init
+void Fds_init( struct Nes_Fds_Apu* this );
+// setup
+void Fds_set_tempo( struct Nes_Fds_Apu* this, double t );
+	
+// emulation
+void Fds_reset( struct Nes_Fds_Apu* this );
+
+static inline void Fds_volume( struct Nes_Fds_Apu* this, double v )
+{
+	Synth_volume( &this->synth, 0.14 / fds_master_vol_max / fds_vol_max / fds_wave_sample_max * v );
+}
+
+static inline void Fds_set_output( struct Nes_Fds_Apu* this, int i, struct Blip_Buffer* b )
+{
+#if defined(ROCKBOX)
+	(void) i;
+#endif
+
+	assert( (unsigned) i < fds_osc_count );
+	this->output_ = b;
+}
+
+void Fds_run_until( struct Nes_Fds_Apu* this, blip_time_t ) ICODE_ATTR;
+static inline void Fds_end_frame( struct Nes_Fds_Apu* this, blip_time_t end_time )
+{
+	if ( end_time > this->last_time )
+		Fds_run_until( this, end_time );
+	this->last_time -= end_time;
+	assert( this->last_time >= 0 );
+}
+
+void Fds_write_( struct Nes_Fds_Apu* this, unsigned addr, int data ) ICODE_ATTR;
+static inline void Fds_write( struct Nes_Fds_Apu* this, blip_time_t time, unsigned addr, int data )
+{
+	Fds_run_until( this, time );
+	Fds_write_( this, addr, data );
+}
+
+static inline int Fds_read( struct Nes_Fds_Apu* this, blip_time_t time, unsigned addr )
+{
+	Fds_run_until( this, time );
+	
+	int result = 0xFF;
+	switch ( addr )
+	{
+	case 0x4090:
+		result = this->env_gain;
+		break;
+	
+	case 0x4092:
+		result = this->sweep_gain;
+		break;
+	
+	default:
+		{
+			unsigned i = addr - fds_io_addr;
+			if ( i < fds_wave_size )
+				result = this->regs_ [i];
+		}
+	}
+	
+	return result | 0x40;
+}
+
+// allow access to registers by absolute address (i.e. 0x4080)
+static inline unsigned char* regs_nes( struct Nes_Fds_Apu* this, unsigned addr ) { return &this->regs_ [addr - fds_io_addr]; }
+
+#endif
diff --git a/apps/codecs/libgme/nes_fme7_apu.c b/apps/codecs/libgme/nes_fme7_apu.c
new file mode 100644
index 0000000..cb5ed93
--- /dev/null
+++ b/apps/codecs/libgme/nes_fme7_apu.c
@@ -0,0 +1,135 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+
+#include "nes_fme7_apu.h"
+
+#include <string.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"
+
+void Fme7_init( struct Nes_Fme7_Apu* this )
+{
+	Synth_init( &this->synth );
+	
+	Fme7_output( this, NULL );
+	Fme7_volume( this, 1.0 );
+	Fme7_reset( this );
+}
+
+void Fme7_reset( struct Nes_Fme7_Apu* this )
+{
+	this->last_time = 0;
+	
+	int i;
+	for ( i = 0; i < fme7_osc_count; i++ )
+		this->oscs [i].last_amp = 0;
+	
+	this->latch = 0;
+	memset( this->regs, 0, sizeof this->regs);
+	memset( this->phases, 0, sizeof this->phases );
+	memset( this->delays, 0, sizeof this->delays );
+}
+
+static unsigned char const amp_table [16] ICONST_ATTR =
+{
+	#define ENTRY( n ) (unsigned char) (n * amp_range + 0.5)
+	ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156),
+	ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624),
+	ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498),
+	ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000)
+	#undef ENTRY
+};
+
+void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
+{
+	require( end_time >= this->last_time );
+	
+	int index;
+	for ( index = 0; index < fme7_osc_count; index++ )
+	{
+		int mode = this->regs [7] >> index;
+		int vol_mode = this->regs [010 + index];
+		int volume = amp_table [vol_mode & 0x0F];
+		
+		struct Blip_Buffer* const osc_output = this->oscs [index].output;
+		if ( !osc_output )
+			continue;
+		/* osc_output->set_modified(); */
+		Blip_set_modified( osc_output );
+		
+		// check for unsupported mode
+		#ifndef NDEBUG
+			if ( (mode & 011) <= 001 && vol_mode & 0x1F )
+				debug_printf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n",
+						mode, vol_mode & 0x1F );
+		#endif
+		
+		if ( (mode & 001) | (vol_mode & 0x10) )
+			volume = 0; // noise and envelope aren't supported
+		
+		// period
+		int const period_factor = 16;
+		unsigned period = (this->regs [index * 2 + 1] & 0x0F) * 0x100 * period_factor +
+				this->regs [index * 2] * period_factor;
+		if ( period < 50 ) // around 22 kHz
+		{
+			volume = 0;
+			if ( !period ) // on my AY-3-8910A, period doesn't have extra one added
+				period = period_factor;
+		}
+		
+		// current amplitude
+		int amp = volume;
+		if ( !this->phases [index] )
+			amp = 0;
+		{
+			int delta = amp - this->oscs [index].last_amp;
+			if ( delta )
+			{
+				this->oscs [index].last_amp = amp;
+				Synth_offset( &this->synth, this->last_time, delta, osc_output );
+			}
+		}
+		
+		blip_time_t time = this->last_time + this->delays [index];
+		if ( time < end_time )
+		{
+			int delta = amp * 2 - volume;
+			if ( volume )
+			{
+				do
+				{
+					delta = -delta;
+					Synth_offset_inline( &this->synth, time, delta, osc_output );
+					time += period;
+				}
+				while ( time < end_time );
+				
+				this->oscs [index].last_amp = (delta + volume) >> 1;
+				this->phases [index] = (delta > 0);
+			}
+			else
+			{
+				// maintain phase when silent
+				int count = (end_time - time + period - 1) / period;
+				this->phases [index] ^= count & 1;
+				time += (blargg_long) count * period;
+			}
+		}
+		
+		this->delays [index] = time - end_time;
+	}
+	
+	this->last_time = end_time;
+}
+
diff --git a/apps/codecs/libgme/nes_fme7_apu.h b/apps/codecs/libgme/nes_fme7_apu.h
new file mode 100644
index 0000000..47b93e5
--- /dev/null
+++ b/apps/codecs/libgme/nes_fme7_apu.h
@@ -0,0 +1,90 @@
+// Sunsoft FME-7 sound emulator
+
+// Game_Music_Emu 0.5.5
+#ifndef NES_FME7_APU_H
+#define NES_FME7_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+enum { fme7_reg_count = 14 };
+
+// Mask and addresses of registers
+enum { fme7_addr_mask = 0xE000 };
+enum { fme7_data_addr = 0xE000 };
+enum { fme7_latch_addr = 0xC000 };
+enum { fme7_osc_count = 3 };
+
+enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff
+
+struct osc_t {
+	struct Blip_Buffer* output;
+	int last_amp;
+};
+
+// static unsigned char const amp_table [16];
+
+struct Nes_Fme7_Apu {
+	// fme7 apu state
+	uint8_t regs [fme7_reg_count];
+	uint8_t phases [3]; // 0 or 1
+	uint8_t latch;
+	uint16_t delays [3]; // a, b, c
+	
+	struct osc_t oscs [fme7_osc_count];
+	blip_time_t last_time;
+	
+	struct Blip_Synth synth;
+};
+
+// See Nes_Apu.h for reference
+void Fme7_init( struct Nes_Fme7_Apu* this );
+void Fme7_reset( struct Nes_Fme7_Apu* this );
+	
+static inline void Fme7_volume( struct Nes_Fme7_Apu* this, double v )
+{
+	Synth_volume( &this->synth, 0.38 / amp_range * v ); // to do: fine-tune
+}
+
+static inline void Fme7_osc_output( struct Nes_Fme7_Apu* this, int i, struct Blip_Buffer* buf )
+{
+	assert( (unsigned) i < fme7_osc_count );
+	this->oscs [i].output = buf;
+}
+
+static inline void Fme7_output( struct Nes_Fme7_Apu* this, struct Blip_Buffer* buf )
+{
+	int i;
+	for ( i = 0; i < fme7_osc_count; i++ )
+		Fme7_osc_output( this, i, buf );
+}
+
+// (addr & addr_mask) == latch_addr
+static inline void Fme7_write_latch( struct Nes_Fme7_Apu* this, int data ) { this->latch = data; }
+
+// (addr & addr_mask) == data_addr
+void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time ) ICODE_ATTR;
+static inline void Fme7_write_data( struct Nes_Fme7_Apu* this, blip_time_t time, int data )
+{
+	if ( (unsigned) this->latch >= fme7_reg_count )
+	{
+		#ifdef debug_printf
+			debug_printf( "FME7 write to %02X (past end of sound registers)\n", (int) latch );
+		#endif
+		return;
+	}
+	
+	Fme7_run_until( this, time );
+	this->regs [this->latch] = data;
+}
+
+static inline void Fme7_end_frame( struct Nes_Fme7_Apu* this, blip_time_t time )
+{
+	if ( time > this->last_time )
+		Fme7_run_until( this, time );
+	
+	assert( this->last_time >= time );
+	this->last_time -= time;
+}
+
+#endif
diff --git a/apps/codecs/libgme/nes_mmc5_apu.h b/apps/codecs/libgme/nes_mmc5_apu.h
new file mode 100644
index 0000000..b696b49
--- /dev/null
+++ b/apps/codecs/libgme/nes_mmc5_apu.h
@@ -0,0 +1,61 @@
+// NES MMC5 sound chip emulator
+
+// Nes_Snd_Emu 0.2.0-pre
+#ifndef NES_MMC5_APU_H
+#define NES_MMC5_APU_H
+
+#include "blargg_common.h"
+#include "nes_apu.h"
+
+enum { mmc5_regs_addr = 0x5000 };
+enum { mmc5_regs_size = 0x16 };
+enum { mmc5_osc_count  = 3 };
+enum { mmc5_exram_size = 1024 };
+
+struct Nes_Mmc5_Apu {
+	struct Nes_Apu apu;
+	unsigned char exram [mmc5_exram_size];
+};
+
+static inline void Mmc5_init( struct Nes_Mmc5_Apu* this )
+{
+	Apu_init( &this->apu );
+}
+
+static inline void Mmc5_set_output( struct Nes_Mmc5_Apu* this, int i, struct Blip_Buffer* b )
+{
+	// in: square 1, square 2, PCM
+	// out: square 1, square 2, skipped, skipped, PCM
+	if ( i > 1 )
+		i += 2;
+	Apu_osc_output( &this->apu, i, b );
+}
+
+static inline void Mmc5_write_register( struct Nes_Mmc5_Apu* this, blip_time_t time, unsigned addr, int data )
+{
+	switch ( addr )
+	{
+	case 0x5015: // channel enables
+		data &= 0x03; // enable the square waves only
+		// fall through
+	case 0x5000: // Square 1
+	case 0x5002:
+	case 0x5003:
+	case 0x5004: // Square 2
+	case 0x5006:
+	case 0x5007:
+	case 0x5011: // DAC
+		Apu_write_register( &this->apu, time, addr - 0x1000, data );
+		break;
+	
+	case 0x5010: // some things write to this for some reason
+		break;
+	
+#ifdef BLARGG_DEBUG_H
+	default:
+			dprintf( "Unmapped MMC5 APU write: $%04X <- $%02X\n", addr, data );
+#endif
+	}
+}
+
+#endif
diff --git a/apps/codecs/libgme/nes_namco_apu.c b/apps/codecs/libgme/nes_namco_apu.c
new file mode 100644
index 0000000..0fca501
--- /dev/null
+++ b/apps/codecs/libgme/nes_namco_apu.c
@@ -0,0 +1,133 @@
+// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/
+
+#include "nes_namco_apu.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"
+
+void Namco_init( struct Nes_Namco_Apu* this )
+{
+	Synth_init( &this->synth );
+	
+	Namco_output( this, NULL );
+	Namco_volume( this, 1.0 );
+	Namco_reset( this );
+}
+
+void Namco_reset( struct Nes_Namco_Apu* this )
+{
+	this->last_time = 0;
+	this->addr_reg = 0;
+	
+	int i;
+	for ( i = 0; i < namco_reg_count; i++ )
+		this->reg [i] = 0;
+	
+	for ( i = 0; i < namco_osc_count; i++ )
+	{
+		struct Namco_Osc* osc = &this->oscs [i];
+		osc->delay = 0;
+		osc->last_amp = 0;
+		osc->wave_pos = 0;
+	}
+}
+
+void Namco_output( struct Nes_Namco_Apu* this, struct Blip_Buffer* buf )
+{
+	int i;
+	for ( i = 0; i < namco_osc_count; i++ )
+		Namco_osc_output( this, i, buf );
+}
+
+void Namco_end_frame( struct Nes_Namco_Apu* this, blip_time_t time )
+{
+	if ( time > this->last_time )
+		Namco_run_until( this, time );
+	
+	assert( this->last_time >= time );
+	this->last_time -= time;
+}
+
+void Namco_run_until( struct Nes_Namco_Apu* this, blip_time_t nes_end_time )
+{
+	int active_oscs = (this->reg [0x7F] >> 4 & 7) + 1;
+	int i;
+	for ( i = namco_osc_count - active_oscs; i < namco_osc_count; i++ )
+	{
+		struct Namco_Osc* osc = &this->oscs [i];
+		struct Blip_Buffer* output = osc->output;
+		if ( !output )
+			continue;
+		/* output->set_modified(); */
+		Blip_set_modified( output );
+		
+		blip_resampled_time_t time =
+				Blip_resampled_time( output, this->last_time ) + osc->delay;
+		blip_resampled_time_t end_time = Blip_resampled_time( output, nes_end_time );
+		osc->delay = 0;
+		if ( time < end_time )
+		{
+			const uint8_t* osc_reg = &this->reg [i * 8 + 0x40];
+			if ( !(osc_reg [4] & 0xE0) )
+				continue;
+			
+			int volume = osc_reg [7] & 15;
+			if ( !volume )
+				continue;
+			
+			blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
+			if ( freq < 64 * active_oscs )
+				continue; // prevent low frequencies from excessively delaying freq changes
+			blip_resampled_time_t period =
+					/* output->resampled_duration( 983040 ) / freq * active_oscs; */
+					Blip_resampled_duration( output, 983040 ) / freq * active_oscs;
+			
+			int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4;
+			if ( !wave_size )
+				continue;
+			
+			int last_amp = osc->last_amp;
+			int wave_pos = osc->wave_pos;
+			
+			do
+			{
+				// read wave sample
+				int addr = wave_pos + osc_reg [6];
+				int sample = this->reg [addr >> 1] >> (addr << 2 & 4);
+				wave_pos++;
+				sample = (sample & 15) * volume;
+				
+				// output impulse if amplitude changed
+				int delta = sample - last_amp;
+				if ( delta )
+				{
+					last_amp = sample;
+					Synth_offset_resampled( &this->synth, time, delta, output );
+				}
+				
+				// next sample
+				time += period;
+				if ( wave_pos >= wave_size )
+					wave_pos = 0;
+			}
+			while ( time < end_time );
+			
+			osc->wave_pos = wave_pos;
+			osc->last_amp = last_amp;
+		}
+		osc->delay = time - end_time;
+	}
+	
+	this->last_time = nes_end_time;
+}
+
diff --git a/apps/codecs/libgme/nes_namco_apu.h b/apps/codecs/libgme/nes_namco_apu.h
new file mode 100644
index 0000000..b53b698
--- /dev/null
+++ b/apps/codecs/libgme/nes_namco_apu.h
@@ -0,0 +1,71 @@
+// Namco 106 sound chip emulator
+
+// Nes_Snd_Emu 0.1.8
+#ifndef NES_NAMCO_APU_H
+#define NES_NAMCO_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+struct namco_state_t;
+
+enum { namco_osc_count = 8 };
+enum { namco_addr_reg_addr = 0xF800 };
+enum { namco_data_reg_addr = 0x4800 };
+enum { namco_reg_count = 0x80 };
+
+struct Namco_Osc {
+	blargg_long delay;
+	struct Blip_Buffer* output;
+	short last_amp;
+	short wave_pos;
+};
+
+struct Nes_Namco_Apu {	
+	struct Namco_Osc oscs [namco_osc_count];
+	
+	blip_time_t last_time;
+	int addr_reg;
+	
+	uint8_t reg [namco_reg_count];
+
+	struct Blip_Synth synth;
+};
+
+// See Nes_Apu.h for reference.
+void Namco_init( struct Nes_Namco_Apu* this );
+void Namco_output( struct Nes_Namco_Apu* this, struct Blip_Buffer* );
+	
+void Namco_reset( struct Nes_Namco_Apu* this );
+void Namco_end_frame( struct Nes_Namco_Apu* this, blip_time_t ) ICODE_ATTR;
+	
+static inline uint8_t* namco_access( struct Nes_Namco_Apu* this )
+{
+	int addr = this->addr_reg & 0x7F;
+	if ( this->addr_reg & 0x80 )
+		this->addr_reg = (addr + 1) | 0x80;
+	return &this->reg [addr];
+}
+
+static inline void Namco_volume( struct Nes_Namco_Apu* this, double v ) { Synth_volume( &this->synth, 0.10 / namco_osc_count * v / 15.0 ); }
+
+// Write-only address register is at 0xF800
+static inline void Namco_write_addr( struct Nes_Namco_Apu* this, int v ) { this->addr_reg = v; }
+
+static inline int Namco_read_data( struct Nes_Namco_Apu* this ) { return *namco_access( this ); }
+
+static inline void Namco_osc_output( struct Nes_Namco_Apu* this, int i, struct Blip_Buffer* buf )
+{
+	assert( (unsigned) i < namco_osc_count );
+	this->oscs [i].output = buf;
+}
+
+// Read/write data register is at 0x4800
+void Namco_run_until( struct Nes_Namco_Apu* this, blip_time_t ) ICODE_ATTR;
+static inline void Namco_write_data( struct Nes_Namco_Apu* this, blip_time_t time, int data )
+{
+	Namco_run_until( this, time );
+	*namco_access( this ) = data;
+}
+
+#endif
diff --git a/apps/codecs/libgme/nes_oscs.c b/apps/codecs/libgme/nes_oscs.c
new file mode 100644
index 0000000..f04d5fa
--- /dev/null
+++ b/apps/codecs/libgme/nes_oscs.c
@@ -0,0 +1,583 @@
+// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/
+
+#include "nes_apu.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"
+
+// Nes_Osc
+
+void Osc_clock_length( struct Nes_Osc* this, int halt_mask )
+{
+	if ( this->length_counter && !(this->regs [0] & halt_mask) )
+		this->length_counter--;
+}
+
+// Nes_Square
+
+void Square_clock_envelope( struct Nes_Square* this )
+{
+	struct Nes_Osc* osc = &this->osc;
+	int period = osc->regs [0] & 15;
+	if ( osc->reg_written [3] ) {
+		osc->reg_written [3] = false;
+		this->env_delay = period;
+		this->envelope = 15;
+	}
+	else if ( --this->env_delay < 0 ) {
+		this->env_delay = period;
+		if ( this->envelope | (osc->regs [0] & 0x20) )
+			this->envelope = (this->envelope - 1) & 15;
+	}
+}
+
+int Square_volume( struct Nes_Square* this )
+{
+	struct Nes_Osc* osc = &this->osc;
+	return osc->length_counter == 0 ? 0 : (osc->regs [0] & 0x10) ? (osc->regs [0] & 15) : this->envelope;
+}
+
+void Square_clock_sweep( struct Nes_Square* this, int negative_adjust )
+{
+	struct Nes_Osc* osc = &this->osc;
+	int sweep = osc->regs [1];
+	
+	if ( --this->sweep_delay < 0 )
+	{
+		osc->reg_written [1] = true;
+		
+		int period = Osc_period( osc );
+		int shift = sweep & shift_mask;
+		if ( shift && (sweep & 0x80) && period >= 8 )
+		{
+			int offset = period >> shift;
+			
+			if ( sweep & negate_flag )
+				offset = negative_adjust - offset;
+			
+			if ( period + offset < 0x800 )
+			{
+				period += offset;
+				// rewrite period
+				osc->regs [2] = period & 0xFF;
+				osc->regs [3] = (osc->regs [3] & ~7) | ((period >> 8) & 7);
+			}
+		}
+	}
+	
+	if ( osc->reg_written [1] ) {
+		osc->reg_written [1] = false;
+		this->sweep_delay = (sweep >> 4) & 7;
+	}
+}
+
+// TODO: clean up
+inline nes_time_t Square_maintain_phase( struct Nes_Square* this, nes_time_t time, nes_time_t end_time,
+		nes_time_t timer_period )
+{
+	nes_time_t remain = end_time - time;
+	if ( remain > 0 )
+	{
+		int count = (remain + timer_period - 1) / timer_period;
+		this->phase = (this->phase + count) & (square_phase_range - 1);
+		time += (blargg_long) count * timer_period;
+	}
+	return time;
+}
+
+void Square_run( struct Nes_Square* this, nes_time_t time, nes_time_t end_time )
+{
+	struct Nes_Osc* osc = &this->osc;
+	const int period = Osc_period( osc );
+	const int timer_period = (period + 1) * 2;
+	
+	if ( !osc->output )
+	{
+		osc->delay = Square_maintain_phase( this, time + osc->delay, end_time, timer_period ) - end_time;
+		return;
+	}
+	
+	Blip_set_modified( osc->output );
+	
+	int offset = period >> (osc->regs [1] & shift_mask);
+	if ( osc->regs [1] & negate_flag )
+		offset = 0;
+	
+	const int volume = Square_volume( this );
+	if ( volume == 0 || period < 8 || (period + offset) >= 0x800 )
+	{
+		if ( osc->last_amp ) {
+			Synth_offset( this->synth, time, -osc->last_amp, osc->output );
+			osc->last_amp = 0;
+		}
+		
+		time += osc->delay;
+		time = Square_maintain_phase( this, time, end_time, timer_period );
+	}
+	else
+	{
+		// handle duty select
+		int duty_select = (osc->regs [0] >> 6) & 3;
+		int duty = 1 << duty_select; // 1, 2, 4, 2
+		int amp = 0;
+		if ( duty_select == 3 ) {
+			duty = 2; // negated 25%
+			amp = volume;
+		}
+		if ( this->phase < duty )
+			amp ^= volume;
+		
+		{
+			int delta = Osc_update_amp( osc, amp );
+			if ( delta )
+				Synth_offset( this->synth, time, delta, osc->output );
+		}
+		
+		time += osc->delay;
+		if ( time < end_time )
+		{
+			struct Blip_Buffer* const output = osc->output;
+			Synth* synth = this->synth;
+			int delta = amp * 2 - volume;
+			int phase = this->phase;
+			
+			do {
+				phase = (phase + 1) & (square_phase_range - 1);
+				if ( phase == 0 || phase == duty ) {
+					delta = -delta;
+					Synth_offset_inline( synth, time, delta, output );
+				}
+				time += timer_period;
+			}
+			while ( time < end_time );
+			
+			osc->last_amp = (delta + volume) >> 1;
+			this->phase = phase;
+		}
+	}
+	
+	osc->delay = time - end_time;
+}
+
+// Nes_Triangle
+
+void Triangle_clock_linear_counter( struct Nes_Triangle* this )
+{
+	struct Nes_Osc* osc = &this->osc;
+	if ( osc->reg_written [3] )
+		this->linear_counter = osc->regs [0] & 0x7F;
+	else if ( this->linear_counter )
+		this->linear_counter--;
+	
+	if ( !(osc->regs [0] & 0x80) )
+		osc->reg_written [3] = false;
+}
+
+inline int Triangle_calc_amp( struct Nes_Triangle* this )
+{
+	int amp = Triangle_phase_range - this->phase;
+	if ( amp < 0 )
+		amp = this->phase - (Triangle_phase_range + 1);
+	return amp;
+}
+
+// TODO: clean up
+inline nes_time_t Triangle_maintain_phase( struct Nes_Triangle* this, nes_time_t time, nes_time_t end_time,
+		nes_time_t timer_period )
+{
+	nes_time_t remain = end_time - time;
+	if ( remain > 0 )
+	{
+		int count = (remain + timer_period - 1) / timer_period;
+		this->phase = ((unsigned) this->phase + 1 - count) & (Triangle_phase_range * 2 - 1);
+		this->phase++;
+		time += (blargg_long) count * timer_period;
+	}
+	return time;
+}
+
+void Triangle_run( struct Nes_Triangle* this, nes_time_t time, nes_time_t end_time )
+{
+	struct Nes_Osc* osc = &this->osc;
+	const int timer_period = Osc_period( osc ) + 1;
+	if ( !osc->output )
+	{
+		time += osc->delay;
+		osc->delay = 0;
+		if ( osc->length_counter && this->linear_counter && timer_period >= 3 )
+			osc->delay = Triangle_maintain_phase( this, time, end_time, timer_period ) - end_time;
+		return;
+	}
+	
+	Blip_set_modified( osc->output );
+	
+	// to do: track phase when period < 3
+	// to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
+	
+	int delta = Osc_update_amp( osc, Triangle_calc_amp( this ) );
+	if ( delta )
+		Synth_offset( &this->synth, time, delta, osc->output );
+	
+	time += osc->delay;
+	if ( osc->length_counter == 0 || this->linear_counter == 0 || timer_period < 3 )
+	{
+		time = end_time;
+	}
+	else if ( time < end_time )
+	{
+		struct Blip_Buffer* const output = osc->output;
+		
+		int phase = this->phase;
+		int volume = 1;
+		if ( phase > Triangle_phase_range ) {
+			phase -= Triangle_phase_range;
+			volume = -volume;
+		}
+		
+		do {
+			if ( --phase == 0 ) {
+				phase = Triangle_phase_range;
+				volume = -volume;
+			}
+			else {
+				Synth_offset_inline( &this->synth, time, volume, output );
+			}
+			
+			time += timer_period;
+		}
+		while ( time < end_time );
+		
+		if ( volume < 0 )
+			phase += Triangle_phase_range;
+		this->phase = phase;
+		osc->last_amp = Triangle_calc_amp( this );
+ 	}
+	osc->delay = time - end_time;
+}
+
+// Nes_Dmc
+
+void Dmc_reset( struct Nes_Dmc* this )
+{
+	this->address = 0;
+	this->dac = 0;
+	this->buf = 0;
+	this->bits_remain = 1;
+	this->bits = 0;
+	this->buf_full = false;
+	this->silence = true;
+	this->next_irq = apu_no_irq;
+	this->irq_flag = false;
+	this->irq_enabled = false;
+	
+	Osc_reset( &this->osc );
+	this->period = 0x1AC;
+}
+
+void Dmc_recalc_irq( struct Nes_Dmc* this )
+{
+	struct Nes_Osc* osc = &this->osc;
+	nes_time_t irq = apu_no_irq;
+	if ( this->irq_enabled && osc->length_counter )
+		irq = this->apu->last_dmc_time + osc->delay +
+				((osc->length_counter - 1) * 8 + this->bits_remain - 1) * (nes_time_t) (this->period) + 1;
+	if ( irq != this->next_irq ) {
+		this->next_irq = irq;
+		Apu_irq_changed( this->apu );
+	}
+}
+
+int Dmc_count_reads( struct Nes_Dmc* this, nes_time_t time, nes_time_t* last_read )
+{
+	struct Nes_Osc* osc = &this->osc;
+	if ( last_read )
+		*last_read = time;
+	
+	if ( osc->length_counter == 0 )
+		return 0; // not reading
+	
+	nes_time_t first_read = Dmc_next_read_time( this );
+	nes_time_t avail = time - first_read;
+	if ( avail <= 0 )
+		return 0;
+	
+	int count = (avail - 1) / (this->period * 8) + 1;
+	if ( !(osc->regs [0] & loop_flag) && count > osc->length_counter )
+		count = osc->length_counter;
+	
+	if ( last_read )
+	{
+		*last_read = first_read + (count - 1) * (this->period * 8) + 1;
+		check( *last_read <= time );
+		check( count == count_reads( *last_read, NULL ) );
+		check( count - 1 == count_reads( *last_read - 1, NULL ) );
+	}
+	
+	return count;
+}
+
+static short const dmc_period_table [2] [16] ICONST_ATTR = {
+	{428, 380, 340, 320, 286, 254, 226, 214, // NTSC
+	190, 160, 142, 128, 106,  84,  72,  54},
+
+	{398, 354, 316, 298, 276, 236, 210, 198, // PAL
+	176, 148, 132, 118,  98,  78,  66,  50}
+};
+
+inline void Dmc_reload_sample( struct Nes_Dmc* this )
+{
+	this->address = 0x4000 + this->osc.regs [2] * 0x40;
+	this->osc.length_counter = this->osc.regs [3] * 0x10 + 1;
+}
+
+static byte const dac_table [128] ICONST_ATTR =
+{
+	 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14,
+	15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27,
+	27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38,
+	39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49,
+	50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59,
+	59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67,
+	68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75,
+	76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83,
+};
+
+void Dmc_write_register( struct Nes_Dmc* this, int addr, int data )
+{
+	if ( addr == 0 )
+	{
+		this->period = dmc_period_table [this->pal_mode] [data & 15];
+		this->irq_enabled = (data & 0xC0) == 0x80; // enabled only if loop disabled
+		this->irq_flag &= this->irq_enabled;
+		Dmc_recalc_irq( this );
+	}
+	else if ( addr == 1 )
+	{
+		int old_dac = this->dac;
+		this->dac = data & 0x7F;
+		
+		// adjust last_amp so that "pop" amplitude will be properly non-linear
+		// with respect to change in dac
+		int faked_nonlinear = this->dac - (dac_table [this->dac] - dac_table [old_dac]);
+		if ( !this->nonlinear )
+			this->osc.last_amp = faked_nonlinear;
+	}
+}
+
+void Dmc_start( struct Nes_Dmc* this )
+{
+	Dmc_reload_sample( this );
+	Dmc_fill_buffer( this );
+	Dmc_recalc_irq( this );
+}
+
+void Dmc_fill_buffer( struct Nes_Dmc* this )
+{
+	if ( !this->buf_full && this->osc.length_counter )
+	{
+		require( this->prg_reader ); // prg_reader must be set
+		this->buf = this->prg_reader( this->prg_reader_data, 0x8000u + this->address );
+		this->address = (this->address + 1) & 0x7FFF;
+		this->buf_full = true;
+		if ( --this->osc.length_counter == 0 )
+		{
+			if ( this->osc.regs [0] & loop_flag ) {
+				Dmc_reload_sample( this );
+			}
+			else {
+				this->apu->osc_enables &= ~0x10;
+				this->irq_flag = this->irq_enabled;
+				this->next_irq = apu_no_irq;
+				Apu_irq_changed( this->apu );
+			}
+		}
+	}
+}
+
+void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
+{
+	struct Nes_Osc* osc = &this->osc;
+	int delta = Osc_update_amp( osc, this->dac );
+	if ( !osc->output )
+	{
+		this->silence = true;
+	}
+	else
+	{
+		Blip_set_modified( osc->output );
+		if ( delta )
+			Synth_offset( &this->synth, time, delta, osc->output );
+	}
+	
+	time += osc->delay;
+	if ( time < end_time )
+	{
+		int bits_remain = this->bits_remain;
+		if ( this->silence && !this->buf_full )
+		{
+			int count = (end_time - time + this->period - 1) / this->period;
+			bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1;
+			time += count * this->period;
+		}
+		else
+		{
+			struct Blip_Buffer* const output = osc->output;
+			const int period = this->period;
+			int bits = this->bits;
+			int dac = this->dac;
+			
+			do
+			{
+				if ( !this->silence )
+				{
+					int step = (bits & 1) * 4 - 2;
+					bits >>= 1;
+					if ( (unsigned) (dac + step) <= 0x7F ) {
+						dac += step;
+						Synth_offset_inline( &this->synth, time, step, output );
+					}
+				}
+				
+				time += period;
+				
+				if ( --bits_remain == 0 )
+				{
+					bits_remain = 8;
+					if ( !this->buf_full ) {
+						this->silence = true;
+					}
+					else {
+						this->silence = false;
+						bits = this->buf;
+						this->buf_full = false;
+						if ( !output )
+							this->silence = true;
+						Dmc_fill_buffer( this );
+					}
+				}
+			}
+			while ( time < end_time );
+			
+			this->dac = dac;
+			osc->last_amp = dac;
+			this->bits = bits;
+		}
+		this->bits_remain = bits_remain;
+	}
+	osc->delay = time - end_time;
+}
+
+// Nes_Noise
+
+static short const noise_period_table [16] ICONST_ATTR = {
+	0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0,
+	0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4
+};
+
+void Noise_clock_envelope( struct Nes_Noise* this )
+{
+	struct Nes_Osc* osc = &this->osc;
+	int period = osc->regs [0] & 15;
+	if ( osc->reg_written [3] ) {
+		osc->reg_written [3] = false;
+		this->env_delay = period;
+		this->envelope = 15;
+	}
+	else if ( --this->env_delay < 0 ) {
+		this->env_delay = period;
+		if ( this->envelope | (osc->regs [0] & 0x20) )
+			this->envelope = (this->envelope - 1) & 15;
+	}
+}
+
+int Noise_volume( struct Nes_Noise* this )
+{
+	struct Nes_Osc* osc = &this->osc;
+	return osc->length_counter == 0 ? 0 : (osc->regs [0] & 0x10) ? (osc->regs [0] & 15) : this->envelope;
+}
+
+void Noise_run( struct Nes_Noise* this, nes_time_t time, nes_time_t end_time )
+{
+	struct Nes_Osc* osc = &this->osc;
+	int period = noise_period_table [osc->regs [2] & 15];
+	
+	if ( !osc->output )
+	{
+		// TODO: clean up
+		time += osc->delay;
+		osc->delay = time + (end_time - time + period - 1) / period * period - end_time;
+		return;
+	}
+	
+	Blip_set_modified( osc->output );
+	
+	const int volume = Noise_volume( this );
+	int amp = (this->noise & 1) ? volume : 0;
+	{
+		int delta = Osc_update_amp( osc, amp );
+		if ( delta )
+			Synth_offset( &this->synth, time, delta, osc->output );
+	}
+	
+	time += osc->delay;
+	if ( time < end_time )
+	{
+		const int mode_flag = 0x80;
+		
+		if ( !volume )
+		{
+			// round to next multiple of period
+			time += (end_time - time + period - 1) / period * period;
+			
+			// approximate noise cycling while muted, by shuffling up noise register
+			// to do: precise muted noise cycling?
+			if ( !(osc->regs [2] & mode_flag) ) {
+				int feedback = (this->noise << 13) ^ (this->noise << 14);
+				this->noise = (feedback & 0x4000) | (this->noise >> 1);
+			}
+		}
+		else
+		{
+			struct Blip_Buffer* const output = osc->output;
+			
+			// using resampled time avoids conversion in synth.offset()
+			blip_resampled_time_t rperiod = Blip_resampled_duration( output, period );
+			blip_resampled_time_t rtime = Blip_resampled_time( output, time );
+			
+			int noise = this->noise;
+			int delta = amp * 2 - volume;
+			const int tap = (osc->regs [2] & mode_flag ? 8 : 13);
+			
+			do {
+				int feedback = (noise << tap) ^ (noise << 14);
+				time += period;
+				
+				if ( (noise + 1) & 2 ) {
+					// bits 0 and 1 of noise differ
+					delta = -delta;
+					Synth_offset_resampled( &this->synth, rtime, delta, output );
+				}
+				
+				rtime += rperiod;
+				noise = (feedback & 0x4000) | (noise >> 1);
+			}
+			while ( time < end_time );
+			
+			osc->last_amp = (delta + volume) >> 1;
+			this->noise = noise;
+		}
+	}
+	
+	osc->delay = time - end_time;
+}
+
diff --git a/apps/codecs/libgme/nes_oscs.h b/apps/codecs/libgme/nes_oscs.h
new file mode 100644
index 0000000..a358e01
--- /dev/null
+++ b/apps/codecs/libgme/nes_oscs.h
@@ -0,0 +1,165 @@
+// Private oscillators used by Nes_Apu
+
+// Nes_Snd_Emu 0.1.8
+#ifndef NES_OSCS_H
+#define NES_OSCS_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+#include "nes_cpu.h"
+
+struct Nes_Apu;
+
+struct Nes_Osc
+{
+	unsigned char regs [4];
+	bool reg_written [4];
+	struct Blip_Buffer* output;
+	int length_counter;// length counter (0 if unused by oscillator)
+	int delay;      // delay until next (potential) transition
+	int last_amp;   // last amplitude oscillator was outputting
+};
+
+void Osc_clock_length( struct Nes_Osc* this, int halt_mask );
+static inline int Osc_period( struct Nes_Osc* this ) 
+{
+	return (this->regs [3] & 7) * 0x100 + (this->regs [2] & 0xFF);
+}
+
+static inline void Osc_reset( struct Nes_Osc* this ) 
+{
+	this->delay = 0;
+	this->last_amp = 0;
+}
+
+static inline int Osc_update_amp( struct Nes_Osc* this, int amp ) 
+{
+	int delta = amp - this->last_amp;
+	this->last_amp = amp;
+	return delta;
+}
+
+// Nes_Square
+
+enum { negate_flag = 0x08 };
+enum { shift_mask = 0x07 };
+enum { square_phase_range = 8 };
+
+typedef struct Blip_Synth Synth;
+	
+struct Nes_Square
+{
+	struct Nes_Osc osc;
+	int envelope;
+	int env_delay;
+	int phase;
+	int sweep_delay;
+		
+	Synth* synth; // shared between squares
+};
+
+static inline void Square_set_synth( struct Nes_Square* this, Synth* s ) { this->synth = s; }
+	
+void Square_clock_sweep( struct Nes_Square* this, int adjust );
+void Square_run( struct Nes_Square* this, nes_time_t, nes_time_t );
+
+static inline void Square_reset( struct Nes_Square* this ) 
+{
+	this->sweep_delay = 0;
+	this->envelope = 0;
+	this->env_delay = 0;
+	Osc_reset( &this->osc );
+}
+
+void Square_clock_envelope( struct Nes_Square* this );
+int Square_volume( struct Nes_Square* this );
+		
+// Nes_Triangle
+
+enum { Triangle_phase_range = 16 };
+	
+struct Nes_Triangle
+{
+	struct Nes_Osc osc;
+		
+	int phase;
+	int linear_counter;
+	struct Blip_Synth synth;
+};
+
+void Triangle_run( struct Nes_Triangle* this, nes_time_t, nes_time_t );
+void Triangle_clock_linear_counter( struct Nes_Triangle* this );
+
+static inline void Triangle_reset( struct Nes_Triangle* this )
+{
+	this->linear_counter = 0;
+	this->phase = 1;
+	Osc_reset( &this->osc );
+}
+
+// Nes_Noise
+struct Nes_Noise
+{
+	struct Nes_Osc osc;
+	
+	int envelope;
+	int env_delay;
+	int noise;
+	struct Blip_Synth synth;
+};
+
+void Noise_clock_envelope( struct Nes_Noise* this );
+int Noise_volume( struct Nes_Noise* this );
+void Noise_run( struct Nes_Noise* this, nes_time_t, nes_time_t );
+
+static inline void Noise_reset( struct Nes_Noise* this )
+{
+	this->noise = 1 << 14;
+	this->envelope = 0;
+	this->env_delay = 0;
+	Osc_reset( &this->osc );
+}
+
+// Nes_Dmc
+
+enum { loop_flag = 0x40 };
+	
+struct Nes_Dmc
+{
+	struct Nes_Osc osc;
+		
+	int address;    // address of next byte to read
+	int period;
+	int buf;
+	int bits_remain;
+	int bits;
+	bool buf_full;
+	bool silence;
+	
+	int dac;
+	
+	nes_time_t next_irq;
+	bool irq_enabled;
+	bool irq_flag;
+	bool pal_mode;
+	bool nonlinear;
+	
+ 	int (*prg_reader)( void*, addr_t ); // needs to be initialized to prg read function
+	void* prg_reader_data;
+	
+	struct Nes_Apu* apu;
+	
+	struct Blip_Synth synth;
+};
+
+void Dmc_start( struct Nes_Dmc* this );
+void Dmc_write_register( struct Nes_Dmc* this, int, int ) ICODE_ATTR;
+void Dmc_run( struct Nes_Dmc* this, nes_time_t, nes_time_t ) ICODE_ATTR;
+void Dmc_recalc_irq( struct Nes_Dmc* this ) ICODE_ATTR;
+void Dmc_fill_buffer( struct Nes_Dmc* this ) ICODE_ATTR;
+void Dmc_reload_sample( struct Nes_Dmc* this ) ICODE_ATTR;
+void Dmc_reset( struct Nes_Dmc* this ) ICODE_ATTR;
+
+int Dmc_count_reads( struct Nes_Dmc* this, nes_time_t, nes_time_t* ) ICODE_ATTR;
+
+#endif
diff --git a/apps/codecs/libgme/nes_vrc6_apu.c b/apps/codecs/libgme/nes_vrc6_apu.c
new file mode 100644
index 0000000..0aba81e
--- /dev/null
+++ b/apps/codecs/libgme/nes_vrc6_apu.c
@@ -0,0 +1,191 @@
+// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/
+
+#include "nes_vrc6_apu.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"
+
+void Vrc6_init( struct Nes_Vrc6_Apu* this )
+{
+	Synth_init( &this->saw_synth );
+	Synth_init( &this->square_synth );
+	
+	Vrc6_output( this, NULL );
+	Vrc6_volume( this, 1.0 );
+	Vrc6_reset( this );
+}
+
+void Vrc6_reset( struct Nes_Vrc6_Apu* this )
+{
+	this->last_time = 0;
+	int i;
+	for ( i = 0; i < vrc6_osc_count; i++ )
+	{
+		struct Vrc6_Osc* osc = &this->oscs [i];
+		int j;
+		for ( j = 0; j < vrc6_reg_count; j++ )
+			osc->regs [j] = 0;
+		osc->delay = 0;
+		osc->last_amp = 0;
+		osc->phase = 1;
+		osc->amp = 0;
+	}
+}
+
+void Vrc6_output( struct Nes_Vrc6_Apu* this, struct Blip_Buffer* buf )
+{
+	int i;
+	for ( i = 0; i < vrc6_osc_count; i++ )
+		Vrc6_osc_output( this, i, buf );
+}
+
+void run_square( struct Nes_Vrc6_Apu* this, struct Vrc6_Osc* osc, blip_time_t end_time );
+void run_saw( struct Nes_Vrc6_Apu* this, blip_time_t end_time );
+void Vrc6_run_until( struct Nes_Vrc6_Apu* this, blip_time_t time )
+{
+	require( time >= this->last_time );
+	run_square( this, &this->oscs [0], time );
+	run_square( this, &this->oscs [1], time );
+	run_saw( this, time );
+	this->last_time = time;
+}
+
+void Vrc6_write_osc( struct Nes_Vrc6_Apu* this, blip_time_t time, int osc_index, int reg, int data )
+{
+	require( (unsigned) osc_index < vrc6_osc_count );
+	require( (unsigned) reg < vrc6_reg_count );
+	
+	Vrc6_run_until( this, time );
+	this->oscs [osc_index].regs [reg] = data;
+}
+
+void Vrc6_end_frame( struct Nes_Vrc6_Apu* this, blip_time_t time )
+{
+	if ( time > this->last_time )
+		Vrc6_run_until( this, time );
+	
+	assert( this->last_time >= time );
+	this->last_time -= time;
+}
+
+void run_square( struct Nes_Vrc6_Apu* this, struct Vrc6_Osc* osc, blip_time_t end_time )
+{
+	struct Blip_Buffer* output = osc->output;
+	if ( !output )
+		return;
+	Blip_set_modified( output );
+	
+	int volume = osc->regs [0] & 15;
+	if ( !(osc->regs [2] & 0x80) )
+		volume = 0;
+	
+	int gate = osc->regs [0] & 0x80;
+	int duty = ((osc->regs [0] >> 4) & 7) + 1;
+	int delta = ((gate || osc->phase < duty) ? volume : 0) - osc->last_amp;
+	blip_time_t time = this->last_time;
+	if ( delta )
+	{
+		osc->last_amp += delta;
+		Synth_offset( &this->square_synth, time, delta, output );
+	}
+	
+	time += osc->delay;
+	osc->delay = 0;
+	int period = Vrc6_osc_period( osc );
+	if ( volume && !gate && period > 4 )
+	{
+		if ( time < end_time )
+		{
+			int phase = osc->phase;
+			
+			do
+			{
+				phase++;
+				if ( phase == 16 )
+				{
+					phase = 0;
+					osc->last_amp = volume;
+					Synth_offset( &this->square_synth, time, volume, output );
+				}
+				if ( phase == duty )
+				{
+					osc->last_amp = 0;
+					Synth_offset( &this->square_synth, time, -volume, output );
+				}
+				time += period;
+			}
+			while ( time < end_time );
+			
+			osc->phase = phase;
+		}
+		osc->delay = time - end_time;
+	}
+}
+
+void run_saw( struct Nes_Vrc6_Apu* this, blip_time_t end_time )
+{
+	struct Vrc6_Osc* osc = &this->oscs [2];
+	struct Blip_Buffer* output = osc->output;
+	if ( !output )
+		return;
+	Blip_set_modified( output );
+	
+	int amp = osc->amp;
+	int amp_step = osc->regs [0] & 0x3F;
+	blip_time_t time = this->last_time;
+	int last_amp = osc->last_amp;
+	if ( !(osc->regs [2] & 0x80) || !(amp_step | amp) )
+	{
+		osc->delay = 0;
+		int delta = (amp >> 3) - last_amp;
+		last_amp = amp >> 3;
+		Synth_offset( &this->saw_synth, time, delta, output );
+	}
+	else
+	{
+		time += osc->delay;
+		if ( time < end_time )
+		{
+			int period = Vrc6_osc_period( osc ) * 2;
+			int phase = osc->phase;
+			
+			do
+			{
+				if ( --phase == 0 )
+				{
+					phase = 7;
+					amp = 0;
+				}
+				
+				int delta = (amp >> 3) - last_amp;
+				if ( delta )
+				{
+					last_amp = amp >> 3;
+					Synth_offset( &this->saw_synth, time, delta, output );
+				}
+				
+				time += period;
+				amp = (amp + amp_step) & 0xFF;
+			}
+			while ( time < end_time );
+			
+			osc->phase = phase;
+			osc->amp = amp;
+		}
+		
+		osc->delay = time - end_time;
+	}
+	
+	osc->last_amp = last_amp;
+}
+
diff --git a/apps/codecs/libgme/nes_vrc6_apu.h b/apps/codecs/libgme/nes_vrc6_apu.h
new file mode 100644
index 0000000..540438f
--- /dev/null
+++ b/apps/codecs/libgme/nes_vrc6_apu.h
@@ -0,0 +1,62 @@
+// Konami VRC6 sound chip emulator
+
+// Nes_Snd_Emu 0.1.8
+#ifndef NES_VRC6_APU_H
+#define NES_VRC6_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+enum { vrc6_osc_count = 3 };
+enum { vrc6_reg_count = 3 };
+enum { vrc6_base_addr = 0x9000 };
+enum { vrc6_addr_step = 0x1000 };
+
+struct Vrc6_Osc
+{
+	uint8_t regs [vrc6_reg_count];
+	struct Blip_Buffer* output;
+	int delay;
+	int last_amp;
+	int phase;
+	int amp; // only used by saw
+};
+
+static inline int Vrc6_osc_period( struct Vrc6_Osc* this )
+{
+	return (this->regs [2] & 0x0F) * 0x100 + this->regs [1] + 1;
+}
+
+struct Nes_Vrc6_Apu {
+	struct Vrc6_Osc oscs [vrc6_osc_count];
+	blip_time_t last_time;
+	
+	struct Blip_Synth saw_synth;
+	struct Blip_Synth square_synth;
+};
+
+// See Nes_Apu.h for reference
+void Vrc6_init( struct Nes_Vrc6_Apu* this );
+void Vrc6_reset( struct Nes_Vrc6_Apu* this );
+void Vrc6_output( struct Nes_Vrc6_Apu* this, struct Blip_Buffer* );
+void Vrc6_end_frame( struct Nes_Vrc6_Apu* this, blip_time_t ) ICODE_ATTR;
+	
+// Oscillator 0 write-only registers are at $9000-$9002
+// Oscillator 1 write-only registers are at $A000-$A002
+// Oscillator 2 write-only registers are at $B000-$B002
+void Vrc6_write_osc( struct Nes_Vrc6_Apu* this, blip_time_t, int osc, int reg, int data ) ICODE_ATTR;
+
+static inline void Vrc6_osc_output( struct Nes_Vrc6_Apu* this, int i, struct Blip_Buffer* buf )
+{
+	assert( (unsigned) i < vrc6_osc_count );
+	this->oscs [i].output = buf;
+}
+
+static inline void Vrc6_volume( struct Nes_Vrc6_Apu* this, double v )
+{
+	double const factor = 0.0967 * 2;
+	Synth_volume( &this->saw_synth, factor / 31 * v );
+	Synth_volume( &this->square_synth, factor * 0.5 / 15 * v );
+}
+
+#endif
diff --git a/apps/codecs/libgme/nes_vrc7_apu.c b/apps/codecs/libgme/nes_vrc7_apu.c
new file mode 100644
index 0000000..d8768bf
--- /dev/null
+++ b/apps/codecs/libgme/nes_vrc7_apu.c
@@ -0,0 +1,89 @@
+

+#include "nes_vrc7_apu.h"

+#include "blargg_source.h"

+

+int const period = 36; // NES CPU clocks per FM clock

+

+void Vrc7_init( struct Nes_Vrc7_Apu* this )

+{

+	Synth_init( &this->synth );

+	

+	OPLL_new ( &this->opll, 3579545, 3579545 / 72 );

+    OPLL_reset_patch( &this->opll, OPLL_VRC7_TONE );

+	

+	this->osc.output = 0;

+	this->osc.last_amp = 0;

+	this->mask = 0;

+

+	Vrc7_volume( this, 1.0 );

+	Vrc7_reset( this );

+}

+

+void Vrc7_reset( struct Nes_Vrc7_Apu* this )

+{

+	this->addr      = 0;

+	this->next_time = 0;

+	this->osc.last_amp = 0;

+

+	OPLL_reset (&this->opll);	

+	OPLL_setMask(&this->opll, this->mask);

+}

+

+void Vrc7_set_rate( struct Nes_Vrc7_Apu* this, double r )

+{

+	OPLL_set_quality( &this->opll, r < 44100 ? 0 : 1 );

+    OPLL_set_rate( &this->opll, (e_uint32)r );

+}

+

+void Vrc7_write_reg( struct Nes_Vrc7_Apu* this, int data )

+{

+	this->addr = data;

+}

+

+void Vrc7_run_until( struct Nes_Vrc7_Apu* this, blip_time_t end_time );

+void Vrc7_write_data( struct Nes_Vrc7_Apu* this, blip_time_t time, int data )

+{	

+	if ( time > this->next_time )

+		Vrc7_run_until( this, time );

+

+	OPLL_writeIO( &this->opll, 0, this->addr );

+	OPLL_writeIO( &this->opll, 1, data );

+}

+

+void Vrc7_end_frame( struct Nes_Vrc7_Apu* this, blip_time_t time )

+{

+	if ( time > this->next_time )

+		Vrc7_run_until( this, time );

+	

+	this->next_time -= time;

+	assert( this->next_time >= 0 );

+	

+	if ( this->osc.output )

+		Blip_set_modified( this->osc.output );

+}

+

+void Vrc7_run_until( struct Nes_Vrc7_Apu* this, blip_time_t end_time )

+{

+	require( end_time > this->next_time );

+

+	blip_time_t time = this->next_time;

+	OPLL* opll = &this->opll; // cache

+	struct Blip_Buffer* const output = this-> osc.output;

+	if ( output )

+	{

+		do

+		{

+			int amp = OPLL_calc( opll ) << 1;

+			int delta = amp - this->osc.last_amp;

+			if ( delta )

+			{

+				this->osc.last_amp = amp;

+				Synth_offset_inline( &this->synth, time, delta, output );

+			}

+			time += period;

+		}

+		while ( time < end_time );

+	}

+

+	this->next_time = time;

+}

diff --git a/apps/codecs/libgme/nes_vrc7_apu.h b/apps/codecs/libgme/nes_vrc7_apu.h
new file mode 100644
index 0000000..5453e6b
--- /dev/null
+++ b/apps/codecs/libgme/nes_vrc7_apu.h
@@ -0,0 +1,52 @@
+// Konami VRC7 sound chip emulator
+
+#ifndef NES_VRC7_APU_H
+#define NES_VRC7_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+#include "emu2413.h"
+
+enum { vrc7_osc_count = 6 };
+
+struct vrc7_osc_t {
+	struct Blip_Buffer* output;
+	int last_amp;
+};
+
+struct Nes_Vrc7_Apu {
+	OPLL opll;
+	int addr;
+	blip_time_t next_time;
+	struct vrc7_osc_t osc;
+	struct Blip_Synth synth;
+	e_uint32 mask;
+};
+
+// See Nes_Apu.h for reference
+void Vrc7_init( struct Nes_Vrc7_Apu* this );
+void Vrc7_reset( struct Nes_Vrc7_Apu* this );
+void Vrc7_set_rate( struct Nes_Vrc7_Apu* this, double r );
+void Vrc7_end_frame( struct Nes_Vrc7_Apu* this, blip_time_t ) ICODE_ATTR;
+
+void Vrc7_write_reg( struct Nes_Vrc7_Apu* this, int reg ) ICODE_ATTR;
+void Vrc7_write_data( struct Nes_Vrc7_Apu* this, blip_time_t, int data ) ICODE_ATTR;
+
+void output_changed( struct Nes_Vrc7_Apu* this );
+static inline void Vrc7_set_output( struct Nes_Vrc7_Apu* this, int i, struct Blip_Buffer* buf )
+{
+	assert( (unsigned) i < vrc7_osc_count );
+	this->mask |= 1 << i;
+
+	// Will use OPLL_setMask to mute voices
+	if ( buf ) {
+		this->mask ^= 1 << i;
+		this->osc.output = buf;
+	}
+}
+
+// DB2LIN_AMP_BITS == 11, * 2
+static inline void Vrc7_volume( struct Nes_Vrc7_Apu* this, double v ) { Synth_volume( &this->synth, 1.0 / 3 / 4096 * v ); }
+
+#endif
diff --git a/apps/codecs/libgme/nsf_cpu.c b/apps/codecs/libgme/nsf_cpu.c
new file mode 100644
index 0000000..1f44bd3
--- /dev/null
+++ b/apps/codecs/libgme/nsf_cpu.c
@@ -0,0 +1,115 @@
+// Normal cpu for NSF emulator
+
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "nsf_emu.h"
+
+#include "blargg_endian.h"
+
+#ifdef BLARGG_DEBUG_H
+	//#define CPU_LOG_START 1000000
+	//#include "nes_cpu_log.h"
+	#undef LOG_MEM
+#endif
+
+/* 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"
+
+#ifndef LOG_MEM
+	#define LOG_MEM( addr, str, data ) data
+#endif
+
+int read_mem( struct Nsf_Emu* this, addr_t addr )
+{
+	int result = this->low_ram [addr & (low_ram_size-1)]; // also handles wrap-around
+	if ( addr & 0xE000 )
+	{
+		result = *Cpu_get_code( &this->cpu, addr );
+		if ( addr < sram_addr )
+		{
+			if ( addr == apu_status_addr )
+				result = Apu_read_status( &this->apu, Cpu_time( &this->cpu ) );
+			else
+				result = cpu_read( this, addr );
+		}
+	}
+	return LOG_MEM( addr, ">", result );
+}
+
+void write_mem( struct Nsf_Emu* this, addr_t addr, int data )
+{
+	(void) LOG_MEM( addr, "<", data );
+	
+	int offset = addr - sram_addr;
+	if ( (unsigned) offset < sram_size )
+	{
+		sram( this ) [offset] = data;
+	}
+	else
+	{
+		// after sram because cpu handles most low_ram accesses internally already
+		int temp = addr & (low_ram_size-1); // also handles wrap-around
+		if ( !(addr & 0xE000) )
+		{
+			this->low_ram [temp] = data;
+		}
+		else
+		{
+			int bank = addr - banks_addr;
+			if ( (unsigned) bank < bank_count )
+			{
+				write_bank( this, bank, data );
+			}
+			else if ( (unsigned) (addr - apu_io_addr) < apu_io_size )
+			{
+				Apu_write_register( &this->apu, Cpu_time( &this->cpu ), addr, data );
+			}
+			else
+			{
+			#ifndef NSF_EMU_APU_ONLY
+				// 0x8000-0xDFFF is writable
+				int i = addr - 0x8000;
+				if ( fds_enabled( this ) && (unsigned) i < fdsram_size )
+					fdsram( this ) [i] = data;
+				else
+			#endif
+				cpu_write( this, addr, data );
+			}
+		}
+	}
+}
+
+#define READ_LOW(  addr       ) (LOG_MEM( addr, ">", this->low_ram [addr] ))
+#define WRITE_LOW( addr, data ) (LOG_MEM( addr, "<", this->low_ram [addr] = data ))
+
+#define CAN_WRITE_FAST( addr )  (addr < low_ram_size)
+#define WRITE_FAST              WRITE_LOW
+
+// addr < 0x2000 || addr >= 0x8000
+#define CAN_READ_FAST( addr )   ((addr ^ 0x8000) < 0xA000)
+#define READ_FAST( addr, out  ) (LOG_MEM( addr, ">", out = READ_CODE( addr ) ))
+
+#define READ_MEM(  addr       ) read_mem(  this, addr )
+#define WRITE_MEM( addr, data ) write_mem( this, addr, data )
+
+#define CPU_BEGIN \
+bool run_cpu_until( struct Nsf_Emu* this, nes_time_t end ) \
+{ \
+	struct Nes_Cpu* cpu = &this->cpu; \
+	Cpu_set_end_time( cpu, end ); \
+	if ( *Cpu_get_code( cpu, cpu->r.pc ) != halt_opcode ) \
+	{
+		#include "nes_cpu_run.h"
+	}
+	return Cpu_time_past_end( cpu ) < 0;
+}
diff --git a/apps/codecs/libgme/nsf_emu.c b/apps/codecs/libgme/nsf_emu.c
new file mode 100644
index 0000000..c805780
--- /dev/null
+++ b/apps/codecs/libgme/nsf_emu.c
@@ -0,0 +1,1105 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+
+#include "nsf_emu.h"
+#include "multi_buffer.h"
+
+#include "blargg_endian.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"
+
+const char gme_wrong_file_type [] ICONST_ATTR = "Wrong file type for this emulator";
+long const clock_divisor = 12;
+
+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)
+
+// number of frames until play interrupts init
+int const initial_play_delay = 7; // KikiKaikai needed this to work
+int const rom_addr  = 0x8000;
+
+void clear_track_vars( struct Nsf_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       = INT_MAX / 2 + 1;
+	this->fade_step        = 1;
+	this->silence_time     = 0;
+	this->silence_count    = 0;
+	this->buf_remain       = 0;
+}
+
+static int pcm_read( void* emu, addr_t addr )
+{
+	return *Cpu_get_code( &((struct Nsf_Emu*) emu)->cpu, addr );
+}
+
+void Nsf_init( struct Nsf_Emu* this )
+{
+	this->sample_rate = 0;
+	this->mute_mask_   = 0;
+	this->tempo        = 1.0;
+	this->gain         = 1.0;
+	
+	// defaults
+	this->max_initial_silence = 2;
+	this->ignore_silence     = false;
+	this->voice_count = 0;
+	
+	// Set sound gain
+	Sound_set_gain( this, 1.2 );
+	
+	// Unload
+	clear_track_vars( this );
+	
+	// Init rom
+	Rom_init( &this->rom, 0x1000 );
+	
+	// Init & clear nsfe info
+	Info_init( &this->info );
+	Info_unload( &this->info ); // TODO: extremely hacky!
+	
+	Cpu_init( &this->cpu );
+	Apu_init( &this->apu );
+	Apu_dmc_reader( &this->apu, pcm_read, this );
+}
+
+// Setup
+
+blargg_err_t init_sound( struct Nsf_Emu* this )
+{
+	/* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) )
+		warning( "Uses unsupported audio expansion hardware" ); **/
+	
+	this->voice_count = apu_osc_count;
+	
+	double adjusted_gain = 1.0 / 0.75 * this->gain;
+	
+	#ifdef NSF_EMU_APU_ONLY
+	{
+		if ( this->header_.chip_flags )
+			set_warning( "Uses unsupported audio expansion hardware" );
+	}
+	#else
+	{
+		if ( vrc6_enabled( this ) )
+		{
+			Vrc6_init( &this->vrc6 );
+			adjusted_gain *= 0.75;
+			
+			this->voice_count += vrc6_osc_count;
+		}
+			
+		if ( fme7_enabled( this ) )
+		{
+			Fme7_init( &this->fme7 );
+			adjusted_gain *= 0.75;
+			
+			this->voice_count += fme7_osc_count;
+		}
+		
+		if ( mmc5_enabled( this ) )
+		{
+			Mmc5_init( &this->mmc5 );
+			adjusted_gain *= 0.75;
+			
+			this->voice_count += mmc5_osc_count;
+		}
+		
+		if ( fds_enabled( this ) )
+		{
+			Fds_init( &this->fds );
+			adjusted_gain *= 0.75;
+			
+			this->voice_count += fds_osc_count ;
+		}
+		
+		if ( namco_enabled( this ) )
+		{
+			Namco_init( &this->namco );
+			adjusted_gain *= 0.75;
+			
+			this->voice_count += namco_osc_count;
+		}
+		
+		if ( vrc7_enabled( this ) )
+		{
+			#ifndef NSF_EMU_NO_VRC7
+				Vrc7_init( &this->vrc7 );
+				Vrc7_set_rate( &this->vrc7, this->sample_rate );
+			#endif
+
+			adjusted_gain *= 0.75;
+			
+			this->voice_count += vrc7_osc_count;
+		}
+		
+		if ( vrc7_enabled( this )  ) Vrc7_volume( &this->vrc7, adjusted_gain );
+		if ( namco_enabled( this ) ) Namco_volume( &this->namco, adjusted_gain );
+		if ( vrc6_enabled( this )  ) Vrc6_volume( &this->vrc6, adjusted_gain );
+		if ( fme7_enabled( this )  ) Fme7_volume( &this->fme7, adjusted_gain );
+		if ( mmc5_enabled( this )  ) Apu_volume( &this->mmc5.apu, adjusted_gain );
+		if ( fds_enabled( this )   ) Fds_volume( &this->fds, adjusted_gain );
+	}
+	#endif
+	
+	if ( adjusted_gain > this->gain )
+		adjusted_gain = this->gain;
+	
+	Apu_volume( &this->apu, adjusted_gain );
+	
+	return 0;
+}
+
+// Header stuff
+bool valid_tag( struct header_t* this )
+{
+	return 0 == memcmp( this->tag, "NESM\x1A", 5 );
+}
+
+// True if file supports only PAL speed
+static bool pal_only( struct header_t* this )
+{
+	return (this->speed_flags & 3) == 1;
+}
+	
+static double clock_rate( struct header_t* this )
+{
+	return pal_only( this ) ? 1662607.125 : 1789772.727272727;
+}
+
+int play_period( struct header_t* this )
+{
+	// NTSC
+	int         clocks   = 29780;
+	int         value    = 0x411A;
+	byte const* rate_ptr = this->ntsc_speed;
+	
+	// PAL
+	if ( pal_only( this ) )
+	{
+		clocks   = 33247;
+		value    = 0x4E20;
+		rate_ptr = this->pal_speed;
+	}
+	
+	// Default rate
+	int rate = get_le16( rate_ptr );
+	if ( rate == 0 )
+		rate = value;
+	
+	// Custom rate
+	if ( rate != value )
+		clocks = (int) (rate * clock_rate( this ) * (1.0/1000000.0));
+	
+	return clocks;
+}
+
+// Gets address, given pointer to it in file header. If zero, returns rom_addr.
+addr_t get_addr( byte const in [] )
+{
+	addr_t addr = get_le16( in );
+	if ( addr == 0 )
+		addr = rom_addr;
+	return addr;
+}
+
+static blargg_err_t check_nsf_header( struct header_t* h )
+{
+	if ( !valid_tag( h ) )
+		return gme_wrong_file_type;
+	return 0;
+}
+
+blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size )
+{
+	// Unload
+	Info_unload( &this->info ); // TODO: extremely hacky!
+	this->m3u.size = 0;
+
+	this->voice_count = 0;
+	clear_track_vars( this );
+	
+	assert( offsetof (struct header_t,unused [4]) == header_size );
+	
+	if ( !memcmp( data, "NESM\x1A", 5 ) ) {
+		Nsf_disable_playlist( this, true );
+		
+		RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) );
+		return Nsf_post_load( this );
+	}
+	
+	blargg_err_t err = Info_load( &this->info, data, size, this );
+	Nsf_disable_playlist( this, false );
+	return err;
+}
+
+blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
+{
+	RETURN_ERR( check_nsf_header( &this->header ) );
+	
+	/* if ( header_.vers != 1 )
+		warning( "Unknown file version" ); */
+	
+	// set up data
+	addr_t load_addr = get_le16( this->header.load_addr );
+	
+	/* if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) )
+		warning( "Load address is too low" ); */
+		
+	Rom_set_addr( &this->rom, load_addr % this->rom.bank_size );
+	
+	/* if ( header_.vers != 1 )
+		warning( "Unknown file version" ); */
+		
+	set_play_period( this, play_period( &this->header ) );
+		
+	// sound and memory
+	blargg_err_t err = init_sound( this );
+	if ( err )
+		return err;
+	
+	// Post load
+	Sound_set_tempo( this, this->tempo );
+	
+	// Remute voices
+	Sound_mute_voices( this, this->mute_mask_ );
+
+	// Set track_count
+	this->track_count = this->header.track_count;
+	
+	// Change clock rate & setup buffer
+	this->clock_rate__ = (long) (clock_rate( &this->header ) + 0.5);
+	Buffer_clock_rate( &this->stereo_buf, this->clock_rate__ );
+	this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
+	return 0;
+}
+
+void Nsf_disable_playlist( struct Nsf_Emu* this, bool b )
+{
+	Info_disable_playlist( &this->info, b );
+	this->track_count = this->info.track_count;
+}
+
+void Nsf_clear_playlist( struct Nsf_Emu* this )
+{
+	Nsf_disable_playlist( this, true );
+}
+
+void write_bank( struct Nsf_Emu* this, int bank, int data )
+{
+	// Find bank in ROM
+	int offset = mask_addr( data * this->rom.bank_size, this->rom.mask );
+	/* if ( offset >= rom.size() )
+		warning( "invalid bank" ); */
+	void const* rom_data = Rom_at_addr( &this->rom, offset );
+	
+	#ifndef NSF_EMU_APU_ONLY
+		if ( bank < bank_count - fds_banks && fds_enabled( this ) )
+		{
+			// TODO: FDS bank switching is kind of hacky, might need to
+			// treat ROM as RAM so changes won't get lost when switching.
+			byte* out = sram( this );
+			if ( bank >= fds_banks )
+			{
+				out = fdsram( this );
+				bank -= fds_banks;
+			}
+			memcpy( &out [bank * this->rom.bank_size], rom_data, this->rom.bank_size );
+			return;
+		}
+	#endif
+	
+	if ( bank >= fds_banks )
+		Cpu_map_code( &this->cpu, (bank + 6) * this->rom.bank_size, this->rom.bank_size, rom_data, false );
+}
+
+void map_memory( struct Nsf_Emu* this )
+{
+	// Map standard things
+	Cpu_reset( &this->cpu, unmapped_code( this ) );
+	Cpu_map_code( &this->cpu, 0, 0x2000, this->low_ram, low_ram_size ); // mirrored four times
+	Cpu_map_code( &this->cpu, sram_addr, sram_size, sram( this ), 0 );
+	
+	// Determine initial banks
+	byte banks [bank_count];
+	static byte const zero_banks [sizeof this->header.banks] = { 0 };
+	if ( memcmp( this->header.banks, zero_banks, sizeof zero_banks ) )
+	{
+		banks [0] = this->header.banks [6];
+		banks [1] = this->header.banks [7];
+		memcpy( banks + fds_banks, this->header.banks, sizeof this->header.banks );
+	}
+	else
+	{
+		// No initial banks, so assign them based on load_addr
+		int i, first_bank = (get_addr( this->header.load_addr ) - sram_addr) / this->rom.bank_size;
+		unsigned total_banks = this->rom.size / this->rom.bank_size;
+		for ( i = bank_count; --i >= 0; )
+		{
+			int bank = i - first_bank;
+			if ( (unsigned) bank >= total_banks )
+				bank = 0;
+			banks [i] = bank;
+		}
+	}
+	
+	// Map banks
+	int i;
+	for ( i = (fds_enabled( this ) ? 0 : fds_banks); i < bank_count; ++i )
+		write_bank( this, i, banks [i] );
+	
+	// Map FDS RAM
+	if ( fds_enabled( this ) )
+		Cpu_map_code( &this->cpu, rom_addr, fdsram_size, fdsram( this ), 0 );
+}
+
+void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, struct Blip_Buffer* left, struct Blip_Buffer* right)
+{
+#if defined(ROCKBOX)
+	(void) left;
+	(void) right;
+#endif
+
+	if ( i < apu_osc_count )
+	{
+		Apu_osc_output( &this->apu, i, buf );
+		return;
+	}
+	i -= apu_osc_count;
+	
+	#ifndef NSF_EMU_APU_ONLY
+	{	
+		if ( vrc6_enabled( this ) && (i -= vrc6_osc_count) < 0 )
+		{
+			Vrc6_osc_output( &this->vrc6, i + vrc6_osc_count, buf );
+			return;
+		}
+		
+		if ( fme7_enabled( this ) && (i -= fme7_osc_count) < 0 )
+		{
+			Fme7_osc_output( &this->fme7, i + fme7_osc_count, buf );
+			return;
+		}
+		
+		if ( mmc5_enabled( this ) && (i -= mmc5_osc_count) < 0 )
+		{
+			Mmc5_set_output( &this->mmc5, i + mmc5_osc_count, buf );
+			return;
+		}
+		
+		if ( fds_enabled( this ) && (i -= fds_osc_count) < 0 )
+		{
+			Fds_set_output( &this->fds, i + fds_osc_count, buf );
+			return;
+		}
+		
+		if ( namco_enabled( this ) && (i -= namco_osc_count) < 0 )
+		{
+			Namco_osc_output( &this->namco, i + namco_osc_count, buf );
+			return;
+		}
+		
+		if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 )
+		{
+			Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf );
+			return;
+		}
+	}
+	#endif
+}
+
+// Emulation
+
+// Music Emu
+
+blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long rate )
+{
+	require( !this->sample_rate ); // sample rate can't be changed once set
+	Buffer_init( &this->stereo_buf );
+	RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
+	
+	// Set bass frequency
+	Buffer_bass_freq( &this->stereo_buf, 80 );
+	
+	this->sample_rate = rate;
+	return 0;
+}
+
+void Sound_mute_voice( struct Nsf_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 Nsf_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) )
+		{
+			set_voice( this, i, 0, 0, 0 );
+		}
+		else
+		{
+			struct channel_t ch = Buffer_channel( &this->stereo_buf );
+			assert( (ch.center && ch.left && ch.right) ||
+					(!ch.center && !ch.left && !ch.right) ); // all or nothing
+			set_voice( this, i, ch.center, ch.left, ch.right );
+		}
+	}
+}
+
+void Sound_set_tempo( struct Nsf_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;
+	
+	set_play_period( this, (int) (play_period( &this->header ) / t) );
+
+	Apu_set_tempo( &this->apu, t );
+	
+#ifndef NSF_EMU_APU_ONLY
+	if ( fds_enabled( this ) )
+		Fds_set_tempo( &this->fds, t );
+#endif
+}
+
+inline void push_byte( struct Nsf_Emu* this, int b )
+{
+	this->low_ram [0x100 + this->cpu.r.sp--] = b;
+}
+
+// Jumps to routine, given pointer to address in file header. Pushes idle_addr
+// as return address, NOT old PC.
+void jsr_then_stop( struct Nsf_Emu* this, byte const addr [] )
+{
+	this->cpu.r.pc = get_addr( addr );
+	push_byte( this, (idle_addr - 1) >> 8 );
+	push_byte( this, (idle_addr - 1) );
+}
+
+int cpu_read( struct Nsf_Emu* this, addr_t addr )
+{
+	#ifndef NSF_EMU_APU_ONLY
+	{
+		if ( namco_enabled( this ) && addr == namco_data_reg_addr )
+			return Namco_read_data( &this->namco );
+		
+		if ( fds_enabled( this ) && (unsigned) (addr - fds_io_addr) < fds_io_size )
+			return Fds_read( &this->fds, Cpu_time( &this->cpu ), addr );
+		
+		if ( mmc5_enabled( this ) ) {
+			int i = addr - 0x5C00;
+			if ( (unsigned) i < mmc5_exram_size )
+				return this->mmc5.exram [i];
+		
+			int m = addr - 0x5205;
+			if ( (unsigned) m < 2 )
+				return (this->mmc5_mul [0] * this->mmc5_mul [1]) >> (m * 8) & 0xFF;
+		}	
+	}
+	#endif
+	
+	/* Unmapped read */
+	return addr >> 8;
+}
+
+int unmapped_read( struct Nsf_Emu* this, addr_t addr )
+{
+	(void) this;
+	
+	switch ( addr )
+	{
+	case 0x2002:
+	case 0x4016:
+	case 0x4017:
+		return addr >> 8;
+	}
+
+	// Unmapped read
+	return addr >> 8;
+}
+
+void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
+{
+	#ifndef NSF_EMU_APU_ONLY
+	{
+		nes_time_t time = Cpu_time( &this->cpu );
+		if ( fds_enabled( this) && (unsigned) (addr - fds_io_addr) < fds_io_size )
+		{
+			Fds_write( &this->fds, time, addr, data );
+			return;
+		}
+		
+		if ( namco_enabled( this) )
+		{
+			if ( addr == namco_addr_reg_addr )
+			{
+				Namco_write_addr( &this->namco, data );
+				return;
+			}
+			
+			if ( addr == namco_data_reg_addr )
+			{
+				Namco_write_data( &this->namco, time, data );
+				return;
+			}
+		}
+		
+		if ( vrc6_enabled( this) )
+		{
+			int reg = addr & (vrc6_addr_step - 1);
+			int osc = (unsigned) (addr - vrc6_base_addr) / vrc6_addr_step;
+			if ( (unsigned) osc < vrc6_osc_count && (unsigned) reg < vrc6_reg_count )
+			{
+				Vrc6_write_osc( &this->vrc6, time, osc, reg, data );
+				return;
+			}
+		}
+		
+		if ( fme7_enabled( this) && addr >= fme7_latch_addr )
+		{
+			switch ( addr & fme7_addr_mask )
+			{
+			case fme7_latch_addr:
+				Fme7_write_latch( &this->fme7, data );
+				return;
+			
+			case fme7_data_addr:
+				Fme7_write_data( &this->fme7, time, data );
+				return;
+			}
+		}
+		
+		if ( mmc5_enabled( this) )
+		{
+			if ( (unsigned) (addr - mmc5_regs_addr) < mmc5_regs_size )
+			{
+				Mmc5_write_register( &this->mmc5, time, addr, data );
+				return;
+			}
+			
+			int m = addr - 0x5205;
+			if ( (unsigned) m < 2 )
+			{
+				this->mmc5_mul [m] = data;
+				return;
+			}
+			
+			int i = addr - 0x5C00;
+			if ( (unsigned) i < mmc5_exram_size )
+			{
+				this->mmc5.exram [i] = data;
+				return;
+			}
+		}
+		
+		if ( vrc7_enabled( this) )
+		{
+			if ( addr == 0x9010 )
+			{
+				Vrc7_write_reg( &this->vrc7, data );
+				return;
+			}
+			
+			if ( (unsigned) (addr - 0x9028) <= 0x08 )
+			{
+				Vrc7_write_data( &this->vrc7, time, data );
+				return;
+			}
+		}
+	}
+	#endif
+	
+	// Unmapped_write
+}
+
+void unmapped_write( struct Nsf_Emu* this, addr_t addr, int data )
+{
+	(void) data;
+	
+	switch ( addr )
+	{
+	case 0x8000: // some write to $8000 and $8001 repeatedly
+	case 0x8001:
+	case 0x4800: // probably namco sound mistakenly turned on in MCK
+	case 0xF800:
+	case 0xFFF8: // memory mapper?
+		return;
+	}
+	
+	if ( mmc5_enabled( this ) && addr == 0x5115 ) return;
+	
+	// FDS memory
+	if ( fds_enabled( this ) && (unsigned) (addr - 0x8000) < 0x6000 ) return;
+}
+
+void fill_buf( struct Nsf_Emu* this );
+blargg_err_t Nsf_start_track( struct Nsf_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];
+		track = e->track;
+	}
+	else track = Info_remap_track( &this->info, track );
+	
+	this->current_track = track;
+	Buffer_clear( &this->stereo_buf );
+	
+	#ifndef NSF_EMU_APU_ONLY
+		if ( mmc5_enabled( this ) )
+		{
+			this->mmc5_mul [0] = 0;
+			this->mmc5_mul [1] = 0;
+			memset( this->mmc5.exram, 0, mmc5_exram_size );
+		}
+
+		if ( fds_enabled( this )   ) Fds_reset( &this->fds );
+		if ( namco_enabled( this ) ) Namco_reset( &this->namco );
+		if ( vrc6_enabled( this )  ) Vrc6_reset( &this->vrc6 );
+		if ( fme7_enabled( this )  ) Fme7_reset( &this->fme7 );
+		if ( mmc5_enabled( this )  ) Apu_reset( &this->mmc5.apu, false, 0 );
+		if ( vrc7_enabled( this )  ) Vrc7_reset( &this->vrc7 );
+	#endif
+	
+	int speed_flags = 0;
+	#ifdef NSF_EMU_EXTRA_FLAGS
+		speed_flags = this->header.speed_flags;
+	#endif
+	
+	Apu_reset( &this->apu, pal_only( &this->header ), (speed_flags & 0x20) ? 0x3F : 0 );
+	Apu_write_register( &this->apu, 0, 0x4015, 0x0F );
+	Apu_write_register( &this->apu, 0, 0x4017, (speed_flags & 0x10) ? 0x80 : 0 );
+	
+	memset( unmapped_code( this ), halt_opcode, unmapped_size );
+	memset( this->low_ram, 0, low_ram_size );
+	memset( sram( this ), 0, sram_size );
+	
+	map_memory( this );
+	
+	// Arrange time of first call to play routine
+	this->play_extra = 0;
+	this->next_play  = this->play_period;
+	
+	this->play_delay = initial_play_delay;
+	this->saved_state.pc = idle_addr;
+	
+	// Setup for call to init routine
+	this->cpu.r.a  = track;
+	this->cpu.r.x  = pal_only( &this->header );
+	this->cpu.r.sp = 0xFF;
+	jsr_then_stop( this, this->header.init_addr );
+	/* if ( this->cpu.r.pc < get_addr( header.load_addr ) )
+		warning( "Init address < load address" ); */
+	
+	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;
+}
+
+void run_once( struct Nsf_Emu* this, nes_time_t end )
+{
+	// Emulate until next play call if possible
+	if ( run_cpu_until( this, min( this->next_play, end ) ) )
+	{
+		// Halt instruction encountered
+		
+		if ( this->cpu.r.pc != idle_addr )
+		{
+			// special_event( "illegal instruction" );
+			Cpu_set_time( &this->cpu, this->cpu.end_time );
+			return;
+		}
+
+		// Init/play routine returned
+		this->play_delay = 1; // play can now be called regularly
+		
+		if ( this->saved_state.pc == idle_addr )
+		{
+			// nothing to run
+			nes_time_t t = this->cpu.end_time;
+			if ( Cpu_time( &this->cpu ) < t )
+				Cpu_set_time( &this->cpu, t );
+		}
+		else
+		{
+			// continue init routine that was interrupted by play routine
+			this->cpu.r = this->saved_state;
+			this->saved_state.pc = idle_addr;
+		}
+	}
+	
+	if ( Cpu_time( &this->cpu ) >= this->next_play )
+	{
+		// Calculate time of next call to play routine
+		this->play_extra ^= 1; // extra clock every other call
+		this->next_play += this->play_period + this->play_extra;
+		
+		// Call routine if ready
+		if ( this->play_delay && !--this->play_delay )
+		{
+			// Save state if init routine is still running
+			if ( this->cpu.r.pc != idle_addr )
+			{
+				check( this->saved_state.pc == idle_addr );
+				this->saved_state = this->cpu.r;
+				// special_event( "play called during init" );
+			}
+			
+			jsr_then_stop( this, this->header.play_addr );
+		}
+	}
+}
+
+void run_until( struct Nsf_Emu* this, nes_time_t end )
+{
+	while ( Cpu_time( &this->cpu ) < end )
+		run_once( this, end );
+}
+
+void end_frame( struct Nsf_Emu* this, nes_time_t end )
+{
+	if ( Cpu_time( &this->cpu ) < end )
+		run_until( this, end );
+	Cpu_adjust_time( &this->cpu, -end );
+	
+	// Localize to new time frame
+	this->next_play -= end;
+	check( this->next_play >= 0 );
+	if ( this->next_play < 0 )
+		this->next_play = 0;
+	
+	Apu_end_frame( &this->apu, end );
+	
+	#ifndef NSF_EMU_APU_ONLY
+		if ( fds_enabled( this )   ) Fds_end_frame( &this->fds, end );
+		if ( fme7_enabled( this )  ) Fme7_end_frame( &this->fme7, end );
+		if ( mmc5_enabled( this )  ) Apu_end_frame( &this->mmc5.apu, end );
+		if ( namco_enabled( this ) ) Namco_end_frame( &this->namco, end );
+		if ( vrc6_enabled( this )  ) Vrc6_end_frame( &this->vrc6, end );
+		if ( vrc7_enabled( this )  ) Vrc7_end_frame( &this->vrc7, end );
+	#endif
+}
+
+// Tell/Seek
+
+blargg_long msec_to_samples( long sample_rate, blargg_long msec )
+{
+	blargg_long sec = msec / 1000;
+	msec -= sec * 1000;
+	return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
+}
+
+long Track_tell( struct Nsf_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 Nsf_Emu* this, long msec )
+{
+	blargg_long time = msec_to_samples( this->sample_rate, msec );
+	if ( time < this->out_time )
+		RETURN_ERR( Nsf_start_track( this, this->current_track ) );
+	return Track_skip( this, time - this->out_time );
+}
+
+blargg_err_t skip_( struct Nsf_Emu* this, long count ) ICODE_ATTR;
+blargg_err_t Track_skip( struct Nsf_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;
+}
+
+blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out ) ICODE_ATTR;
+blargg_err_t skip_( struct Nsf_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;
+}
+
+// Fading
+
+void Track_set_fade( struct Nsf_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( this->sample_rate, start_msec );
+}
+
+// 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 Nsf_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 Nsf_Emu* this, long count, sample_t* out ) ICODE_ATTR;
+void emu_play( struct Nsf_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 Nsf_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 Nsf_play( struct Nsf_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;
+}
+
+blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out )
+{
+	long remain = count;
+	while ( remain )
+	{
+		remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
+		if ( remain )
+		{
+			if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
+			{
+				this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
+
+				// Remute voices
+				Sound_mute_voices( this, this->mute_mask_ );
+			}
+			int msec = Buffer_length( &this->stereo_buf );
+			blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate__ / 1000 - 100;
+			RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
+			assert( clocks_emulated );
+			Buffer_end_frame( &this->stereo_buf, clocks_emulated );
+		}
+	}
+	return 0;
+}
+
+blargg_err_t run_clocks( struct Nsf_Emu* this, blip_time_t* duration, int msec )
+{
+#if defined(ROCKBOX)
+	(void) msec;
+#endif
+
+	end_frame( this, *duration );
+	return 0;
+}
diff --git a/apps/codecs/libgme/nsf_emu.h b/apps/codecs/libgme/nsf_emu.h
new file mode 100644
index 0000000..421425e
--- /dev/null
+++ b/apps/codecs/libgme/nsf_emu.h
@@ -0,0 +1,262 @@
+// Nintendo NES/Famicom NSF music file emulator
+
+// Game_Music_Emu 0.5.5
+#ifndef NSF_EMU_H
+#define NSF_EMU_H
+
+#include "rom_data.h"
+#include "multi_buffer.h"
+#include "nes_apu.h"
+#include "nes_cpu.h"
+#include "nsfe_info.h"
+#include "m3u_playlist.h"
+
+#ifndef NSF_EMU_APU_ONLY
+	#include "nes_namco_apu.h"
+	#include "nes_vrc6_apu.h"
+	#include "nes_fme7_apu.h"
+	#include "nes_fds_apu.h"
+	#include "nes_mmc5_apu.h"
+	#include "nes_vrc7_apu.h"
+#endif
+
+typedef short sample_t;
+
+// Sound chip flags
+enum {
+	vrc6_flag  = 1 << 0,
+	vrc7_flag  = 1 << 1,
+	fds_flag   = 1 << 2,
+	mmc5_flag  = 1 << 3,
+	namco_flag = 1 << 4,
+	fme7_flag  = 1 << 5
+};
+
+enum { fds_banks    = 2 };
+enum { bank_count   = fds_banks + 8 };
+
+enum { rom_begin = 0x8000 };
+enum { bank_select_addr = 0x5FF8 };
+enum { mem_size  = 0x10000 };	
+	
+// cpu sits here when waiting for next call to play routine
+enum { idle_addr = 0x5FF6 };
+enum { banks_addr = idle_addr };
+enum { badop_addr = bank_select_addr };
+
+enum { low_ram_size = 0x800 };
+enum { sram_size    = 0x2000 };
+enum { fdsram_size  = 0x6000 };
+enum { fdsram_offset = 0x2000 + page_size + 8 };
+enum { sram_addr = 0x6000 };
+enum { unmapped_size= page_size + 8 };
+
+enum { buf_size = 2048 };
+
+// NSF file header
+enum { header_size = 0x80 };
+struct header_t
+{
+	char tag [5];
+	byte vers;
+	byte track_count;
+	byte first_track;
+	byte load_addr [2];
+	byte init_addr [2];
+	byte play_addr [2];
+	char game [32];
+	char author [32];
+	char copyright [32];
+	byte ntsc_speed [2];
+	byte banks [8];
+	byte pal_speed [2];
+	byte speed_flags;
+	byte chip_flags;
+	byte unused [4];
+};
+
+struct Nsf_Emu {
+	// Play routine timing
+	nes_time_t next_play;
+	nes_time_t play_period;
+	int play_extra;
+	int play_delay;
+	struct registers_t saved_state; // of interrupted init routine
+	
+	int track_count;
+
+	// general
+	int max_initial_silence;
+	int voice_count;
+	int mute_mask_;
+	double tempo;
+	double gain;
+	
+	long sample_rate;
+	
+	// track-specific
+	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
+	volatile bool track_ended;
+	
+	// fading
+	blargg_long fade_start;
+	int fade_step;
+	
+	// silence detection
+	int silence_lookahead; // speed to run emulator when looking ahead for silence
+	bool ignore_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
+
+	double clock_rate_;
+	long clock_rate__;
+	unsigned buf_changed_count;
+
+	// M3u Playlist
+	struct M3u_Playlist m3u;
+
+	// Larger items at the end
+	#ifndef NSF_EMU_APU_ONLY
+		byte mmc5_mul [2];
+		
+		struct Nes_Fds_Apu   fds;
+		struct Nes_Mmc5_Apu  mmc5;
+		struct Nes_Namco_Apu namco;
+		struct Nes_Vrc6_Apu  vrc6;
+		struct Nes_Fme7_Apu  fme7;
+		struct Nes_Vrc7_Apu  vrc7;
+	#endif
+	
+	struct Nes_Cpu cpu;
+	struct Nes_Apu apu;
+	
+	// Header for currently loaded file
+	struct header_t header;
+	
+	struct Stereo_Buffer stereo_buf;
+	struct Rom_Data rom;
+	
+	// Extended nsf info
+	struct Nsfe_Info info;
+	
+	sample_t buf [buf_size];
+	byte high_ram[fdsram_size + fdsram_offset];
+	byte low_ram [low_ram_size];
+};
+
+// Basic functionality (see Gme_File.h for file loading/track info functions)
+
+void Nsf_init( struct Nsf_Emu* this );
+blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size );
+blargg_err_t Nsf_post_load( struct Nsf_Emu* this );
+
+// Set output sample rate. Must be called only once before loading file.
+blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long sample_rate );
+	
+// Start a track, where 0 is the first track. Also clears warning string.
+blargg_err_t Nsf_start_track( struct Nsf_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 Nsf_play( struct Nsf_Emu* this, long count, sample_t* buf );
+
+void Nsf_clear_playlist( struct Nsf_Emu* this );
+void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ); // use clear_playlist()
+
+// Track status/control
+
+// Number of milliseconds (1000 msec = 1 second) played since beginning of track
+long Track_tell( struct Nsf_Emu* this );
+
+// Seek to new time in track. Seeking backwards or far forward can take a while.
+blargg_err_t Track_seek( struct Nsf_Emu* this, long msec );
+
+// Skip n samples
+blargg_err_t Track_skip( struct Nsf_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 Nsf_Emu* this, long start_msec, long length_msec );
+
+// Get track length in milliseconds
+long Track_length( struct Nsf_Emu* this, int n );
+
+// 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 Nsf_Emu* this, double t );
+
+// Mute/unmute voice i, where voice 0 is first voice
+void Sound_mute_voice( struct Nsf_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 Nsf_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 Nsf_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 Nsf_Emu* this, blip_time_t* duration, int );
+
+void map_memory( struct Nsf_Emu* this ) ICODE_ATTR;
+void write_bank( struct Nsf_Emu* this, int index, int data ) ICODE_ATTR;
+int cpu_read( struct Nsf_Emu* this, addr_t ) ICODE_ATTR;
+void cpu_write( struct Nsf_Emu* this, addr_t, int ) ICODE_ATTR;
+void push_byte( struct Nsf_Emu* this, int ) ICODE_ATTR;
+addr_t get_addr( byte const [] ) ICODE_ATTR;
+bool run_cpu_until( struct Nsf_Emu* this, nes_time_t end ) ICODE_ATTR;
+
+// Sets clocks between calls to play routine to p + 1/2 clock
+static inline void set_play_period( struct Nsf_Emu* this, int p ) { this->play_period = p; }
+	
+// Time play routine will next be called
+static inline nes_time_t play_time( struct Nsf_Emu* this ) { return this->next_play; }
+	
+// Emulates to at least time t. Might emulate a few clocks extra.
+void run_until( struct Nsf_Emu* this, nes_time_t t ) ICODE_ATTR;
+	
+// Runs cpu to at least time t and returns false, or returns true
+// if it encounters illegal instruction (halt).
+bool run_cpu_until( struct Nsf_Emu* this, nes_time_t t ) ICODE_ATTR;
+	
+// cpu calls through to these to access memory (except instructions)
+int  read_mem(  struct Nsf_Emu* this, addr_t ) ICODE_ATTR;
+void write_mem( struct Nsf_Emu* this, addr_t, int ) ICODE_ATTR;
+	
+// Address of play routine
+static inline addr_t play_addr( struct Nsf_Emu* this ) { return get_addr( this->header.play_addr ); }
+	
+// Same as run_until, except emulation stops for any event (routine returned,
+// play routine called, illegal instruction).
+void run_once( struct Nsf_Emu* this, nes_time_t ) ICODE_ATTR;
+	
+// Reads byte as cpu would when executing code. Only works for RAM/ROM,
+// NOT I/O like sound chips.
+int  read_code( struct Nsf_Emu* this, addr_t addr ) ICODE_ATTR;
+
+static inline byte* fdsram( struct Nsf_Emu* this )          { return &this->high_ram [fdsram_offset]; }
+static inline byte* sram( struct Nsf_Emu* this )            { return this->high_ram; }
+static inline byte* unmapped_code( struct Nsf_Emu* this )   { return &this->high_ram [sram_size]; }
+
+#ifndef NSF_EMU_APU_ONLY
+static inline int fds_enabled( struct Nsf_Emu* this )   { return this->header.chip_flags & fds_flag;   }
+static inline int vrc6_enabled( struct Nsf_Emu* this )  { return this->header.chip_flags & vrc6_flag;  }
+static inline int vrc7_enabled( struct Nsf_Emu* this )  { return this->header.chip_flags & vrc7_flag;  }
+static inline int mmc5_enabled( struct Nsf_Emu* this )  { return this->header.chip_flags & mmc5_flag;  }
+static inline int namco_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & namco_flag; }
+static inline int fme7_enabled( struct Nsf_Emu* this )  { return this->header.chip_flags & fme7_flag;  }
+#endif
+	
+#endif
diff --git a/apps/codecs/libgme/nsfe_info.c b/apps/codecs/libgme/nsfe_info.c
new file mode 100644
index 0000000..d22b763
--- /dev/null
+++ b/apps/codecs/libgme/nsfe_info.c
@@ -0,0 +1,272 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+
+#include "nsf_emu.h"
+
+#include "blargg_endian.h"
+#include <string.h>
+
+/* Copyright (C) 2005-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"
+
+void Info_init( struct Nsfe_Info* this )
+{ 
+	this->playlist_disabled = false; 
+}
+
+void Info_unload( struct Nsfe_Info* this )
+{
+	memset(this->playlist, 0, 256);
+	memset(this->track_times, 0, 256 * sizeof(int32_t));
+	
+	this->playlist_size = 0;
+	this->track_times_size = 0;
+}
+
+// TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ?
+void Info_disable_playlist( struct Nsfe_Info* this, bool b )
+{
+	this->playlist_disabled = b;
+	this->track_count = this->playlist_size;
+	if ( !this->track_count || this->playlist_disabled )
+		this->track_count = this->actual_track_count_;
+}
+
+int Info_remap_track( struct Nsfe_Info* this, int track )
+{
+	if ( !this->playlist_disabled && (unsigned) track < (unsigned) this->playlist_size )
+		track = this->playlist [track];
+	return track;
+}
+
+const char eof_error [] = "Unexpected end of file";
+
+// Read n bytes from memory buffer
+static blargg_err_t in_read( void* dst, long bytes, void* data, long* offset, long size )
+{
+	if ((*offset + bytes) > size) return eof_error;
+	
+	memcpy(dst, (char*) data + *offset, bytes);
+	*offset += bytes;
+	return 0;
+}
+
+static blargg_err_t in_skip( long bytes, long *offset, long size )
+{
+	if ((*offset + bytes) > size) return eof_error;
+	
+	*offset += bytes;
+	return 0;
+}
+
+// Skip n bytes from memory buffer
+
+// Read multiple strings and separate into individual strings
+static int read_strs( void* data, long bytes, long* offset, long size,
+		const char* strs [4] )
+{
+	char* chars = (char*) data + *offset;
+	chars [bytes - 1] = 0; // in case last string doesn't have terminator
+	
+	if ( in_skip( bytes, offset, size) )
+		return -1;
+
+	int count = 0, i;
+	for ( i = 0; i < bytes; i++ )
+	{
+		strs [count] = &chars [i];
+		while ( i < bytes && chars [i] )
+			i++;
+		
+		count++;
+		if (count >= 4) 
+			break;
+	}
+	
+	return count;
+}
+
+struct nsfe_info_t
+{
+	byte load_addr [2];
+	byte init_addr [2];
+	byte play_addr [2];
+	byte speed_flags;
+	byte chip_flags;
+	byte track_count;
+	byte first_track;
+	byte unused [6];
+};
+
+blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Nsf_Emu* nsf_emu )
+{
+	long offset = 0;
+	int const nsfe_info_size = 16;
+	assert( offsetof (struct nsfe_info_t,unused [6]) == nsfe_info_size );
+	
+	// check header
+	byte signature [4];
+	blargg_err_t err = in_read( signature, sizeof signature, data, &offset, size );
+	if ( err )
+		return (err == eof_error ? gme_wrong_file_type : err);
+	if ( memcmp( signature, "NSFE", 4 ) ) {
+	}
+	
+	// free previous info
+	/* TODO: clear track_names */
+	memset(this->playlist, 0, 256);
+	memset(this->track_times, 0, 256 * sizeof(int32_t));
+	
+	this->playlist_size = 0;
+	this->track_times_size = 0;
+	
+	// default nsf header
+	static const struct header_t base_header =
+	{
+		{'N','E','S','M','\x1A'},// tag
+		1,                  // version
+		1, 1,               // track count, first track
+		{0,0},{0,0},{0,0},  // addresses
+		"","","",           // strings
+		{0x1A, 0x41},       // NTSC rate
+		{0,0,0,0,0,0,0,0},  // banks
+		{0x20, 0x4E},       // PAL rate
+		0, 0,               // flags
+		{0,0,0,0}           // unused
+	};
+	
+	memcpy( &nsf_emu->header, &base_header, sizeof base_header );
+	
+	// parse tags
+	int phase = 0;
+	while ( phase != 3 )
+	{
+		// read size and tag
+		byte block_header [2] [4];
+		RETURN_ERR( in_read( block_header, sizeof block_header, data, &offset, size ) );
+		
+		blargg_long chunk_size = get_le32( block_header [0] );
+		blargg_long tag  = get_le32( block_header [1] );
+		
+		switch ( tag )
+		{
+			case BLARGG_4CHAR('O','F','N','I'): {
+				check( phase == 0 );
+				if ( chunk_size < 8 )
+					return "Corrupt file";
+				
+				struct nsfe_info_t finfo;
+				finfo.track_count = 1;
+				finfo.first_track = 0;
+				
+				RETURN_ERR( in_read( &finfo, min( chunk_size, (blargg_long) nsfe_info_size ), 
+					(char*) data, &offset, size ) );
+				
+				if ( chunk_size > nsfe_info_size )
+					RETURN_ERR( in_skip( chunk_size - nsfe_info_size, &offset, size ) );
+					
+				phase = 1;
+				nsf_emu->header.speed_flags = finfo.speed_flags;
+				nsf_emu->header.chip_flags  = finfo.chip_flags;
+				nsf_emu->header.track_count = finfo.track_count;
+				this->actual_track_count_ = finfo.track_count;
+				nsf_emu->header.first_track = finfo.first_track;
+				memcpy( nsf_emu->header.load_addr, finfo.load_addr, 2 * 3 );
+				break;
+			}
+			
+			case BLARGG_4CHAR('K','N','A','B'):
+				if ( chunk_size > (int) sizeof nsf_emu->header.banks )
+					return "Corrupt file";
+				RETURN_ERR( in_read( nsf_emu->header.banks, chunk_size, data, &offset, size ) );
+				break;
+			
+			case BLARGG_4CHAR('h','t','u','a'): {
+				const char* strs [4];
+				int n = read_strs( data, chunk_size, &offset, size, strs );
+				if ( n < 0 ) 
+					return eof_error;
+				break;
+			}
+			
+			case BLARGG_4CHAR('e','m','i','t'):
+				this->track_times_size = chunk_size / 4;
+				RETURN_ERR( in_read( this->track_times, this->track_times_size * 4, data, &offset, size ) );
+				break;
+			
+			case BLARGG_4CHAR('l','b','l','t'):
+				RETURN_ERR( in_skip( chunk_size, &offset, size ) );
+				break;
+			
+			case BLARGG_4CHAR('t','s','l','p'):
+				this->playlist_size = chunk_size;
+				RETURN_ERR( in_read( &this->playlist [0], chunk_size, data, &offset, size ) );
+				break;
+			
+			case BLARGG_4CHAR('A','T','A','D'): {
+				check( phase == 1 );
+				phase = 2;
+				if ( !nsf_emu )
+				{
+					RETURN_ERR( in_skip( chunk_size, &offset, size ) );
+				}
+				else
+				{
+					// Avoid unexpected end of file
+					if ( (offset + chunk_size) > size )
+						return eof_error;
+						
+					RETURN_ERR( Rom_load( &nsf_emu->rom, (char*) data + offset, chunk_size, 0, 0, 0 ) );
+					RETURN_ERR( Nsf_post_load( nsf_emu ) );
+					offset += chunk_size;
+				}
+				break;
+			}
+			
+			case BLARGG_4CHAR('D','N','E','N'):
+				check( phase == 2 );
+				phase = 3;
+				break;
+			
+			default:
+				// tags that can be skipped start with a lowercase character
+				check( islower( (tag >> 24) & 0xFF ) );
+				RETURN_ERR( in_skip( chunk_size, &offset, size ) );
+				break;
+		}
+	}
+	
+	return 0;
+}
+
+long Track_length( struct Nsf_Emu* this, int n )
+{
+	long length = 0; 
+	if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
+		struct entry_t* entry = &this->m3u.entries [n];
+		length = entry->length;
+	} 
+	else if ( (this->info.playlist_size > 0) && (n < this->info.playlist_size) ) {
+		int remapped = Info_remap_track( &this->info, n );
+		if ( (unsigned) remapped < (unsigned) this->info.track_times_size )
+			length = (int32_t) get_le32( &this->info.track_times [remapped] );
+	}
+	else if( (unsigned) n < (unsigned) this->info.track_times_size )
+		length = (int32_t) get_le32( &this->info.track_times [n] );
+	
+	/* Length will be 2,30 minutes for one track songs,
+		and 1,45 minutes for multitrack songs */
+	if ( length <= 0 )
+		length = (this->track_count > 1 ? 105 : 150) * 1000;
+	
+	return length;
+}
diff --git a/apps/codecs/libgme/nsfe_info.h b/apps/codecs/libgme/nsfe_info.h
new file mode 100644
index 0000000..9dcde7b
--- /dev/null
+++ b/apps/codecs/libgme/nsfe_info.h
@@ -0,0 +1,30 @@
+// Nintendo NES/Famicom NSFE file info parser
+
+// Game_Music_Emu 0.5.5
+#ifndef NSFE_INFO_H
+#define NSFE_INFO_H
+
+#include "blargg_common.h"
+
+struct Nsf_Emu;
+
+// Allows reading info from NSFE file without creating emulator
+struct Nsfe_Info {
+	int playlist_size;
+	int track_times_size;
+	int track_count;
+	int actual_track_count_;
+	bool playlist_disabled;
+	
+	unsigned char playlist [256];
+	int32_t track_times [256];
+};
+
+void Info_init( struct Nsfe_Info* this );
+blargg_err_t Info_load( struct Nsfe_Info* this, void *data, long size, struct Nsf_Emu* );
+void Info_disable_playlist( struct Nsfe_Info* this, bool b );
+int Info_remap_track( struct Nsfe_Info* this, int i );
+void Info_unload( struct Nsfe_Info* this );
+
+
+#endif
diff --git a/apps/codecs/libgme/opl_apu.c b/apps/codecs/libgme/opl_apu.c
new file mode 100644
index 0000000..bde5e9e
--- /dev/null
+++ b/apps/codecs/libgme/opl_apu.c
@@ -0,0 +1,198 @@
+#include "opl_apu.h"

+

+#include "blargg_source.h"

+

+/* NOTE: Removed unused chips ~ gama */

+

+blargg_err_t Opl_init( struct Opl_Apu* this, long clock, long rate, blip_time_t period, enum opl_type_t type )

+{

+	Synth_init( &this->synth );

+

+	this->type_ = type;

+	this->clock_ = clock;

+	this->rate_ = rate;

+	this->period_ = period;

+	Opl_set_output( this, 0 );

+	Opl_volume( this, 1.0 );

+

+	switch (type)

+	{

+	case type_opll:

+	case type_msxmusic:

+	case type_smsfmunit:

+		OPLL_new ( &this->opll, clock, rate );

+    	OPLL_reset_patch( &this->opll, OPLL_2413_TONE );

+		break;

+	case type_vrc7:

+		OPLL_new ( &this->opll, clock, rate );

+		OPLL_reset_patch( &this->opll, OPLL_VRC7_TONE );

+		break;

+	case type_msxaudio:

+		OPL_init( &this->opl, this->opl_memory, sizeof this->opl_memory );

+		OPL_setSampleRate( &this->opl, rate, clock );

+		OPL_setInternalVolume(&this->opl, 1 << 13);

+		break;

+	}

+

+	Opl_reset( this );

+	return 0;

+}

+

+void Opl_shutdown( struct Opl_Apu* this )

+{

+	switch (this->type_)

+	{

+	case type_opll:

+	case type_msxmusic:

+	case type_smsfmunit:

+	case type_vrc7:

+		OPLL_delete( &this->opll );

+		break;

+	case type_msxaudio: break;

+	}

+}

+

+void Opl_reset( struct Opl_Apu* this )

+{

+	this->addr = 0;

+	this->next_time = 0;

+	this->last_amp = 0;

+

+	switch (this->type_)

+	{

+	case type_opll:

+	case type_msxmusic:

+	case type_smsfmunit:

+	case type_vrc7:

+		OPLL_reset( &this->opll );

+		OPLL_setMask( &this->opll, 0 );

+		break;

+	case type_msxaudio:

+		OPL_reset( &this->opl );

+		break;

+	}

+}

+

+static void run_until( struct Opl_Apu* this, blip_time_t end_time );

+void Opl_write_data( struct Opl_Apu* this, blip_time_t time, int data )

+{

+	run_until( this, time );

+	switch (this->type_)

+	{

+	case type_opll:

+	case type_msxmusic:

+	case type_smsfmunit:

+	case type_vrc7:

+		OPLL_writeIO( &this->opll, 0, this->addr );

+		OPLL_writeIO( &this->opll, 1, data );

+		break;

+	case type_msxaudio:

+		OPL_writeReg( &this->opl, this->addr, data );

+		break;

+	}

+}

+

+int Opl_read( struct Opl_Apu* this, blip_time_t time, int port )

+{

+	run_until( this, time );

+	switch (this->type_)

+	{

+	case type_opll:

+	case type_msxmusic:

+	case type_smsfmunit:

+	case type_vrc7:

+		return OPLL_read( &this->opll, port );

+	case type_msxaudio:

+		return OPL_readStatus( &this->opl );

+	}

+

+	return 0;

+}

+

+void Opl_end_frame( struct Opl_Apu* this, blip_time_t time )

+{

+	run_until( this, time );

+	this->next_time -= time;

+

+	if ( this->output_ )

+		Blip_set_modified( this->output_ );

+}

+

+static void run_until( struct Opl_Apu* this, blip_time_t end_time )

+{

+	if ( end_time > this->next_time )

+	{

+		blip_time_t time_delta = end_time - this->next_time;

+		blip_time_t time = this->next_time;

+		unsigned count = time_delta / this->period_ + 1;

+		switch (this->type_)

+		{

+		case type_opll:

+		case type_msxmusic:

+		case type_smsfmunit:

+		case type_vrc7:

+			{

+				OPLL* opll = &this->opll; // cache

+				struct Blip_Buffer* const output = this->output_;

+				while ( count > 0 )

+				{

+					unsigned todo = count;

+					if ( todo > 1024 ) todo = 1024;

+					short *buffer = OPLL_update_buffer(opll, todo);

+					

+					if ( output && buffer )

+					{

+						int last_amp = this->last_amp;

+						unsigned i;

+						for ( i = 0; i < todo; i++ )

+						{

+							int amp = buffer [i];

+							int delta = amp - last_amp;

+							if ( delta )

+							{

+								last_amp = amp;

+								Synth_offset_inline( &this->synth, time, delta, output );

+							}

+							time += this->period_;

+						}

+						this->last_amp = last_amp;

+					}

+					count -= todo;

+				}

+			}

+			break;

+		case type_msxaudio:

+			{

+				struct Y8950* opl = &this->opl;

+				struct Blip_Buffer* const output = this->output_;

+				while ( count > 0 )

+				{

+					unsigned todo = count;

+					if ( todo > 1024 ) todo = 1024;

+					int *buffer = OPL_updateBuffer(opl, todo);

+

+					if ( output && buffer )

+					{

+						int last_amp = this->last_amp;

+						unsigned i;

+						for ( i = 0; i < todo; i++ )

+						{

+							int amp = buffer [i];

+							int delta = amp - last_amp;

+							if ( delta )

+							{

+								last_amp = amp;

+								Synth_offset_inline( &this->synth, time, delta, output );

+							}

+							time += this->period_;

+						}

+						this->last_amp = last_amp;

+					}

+					count -= todo;

+				}

+			}

+			break;

+		}

+		this->next_time = time;

+	}

+}

diff --git a/apps/codecs/libgme/opl_apu.h b/apps/codecs/libgme/opl_apu.h
new file mode 100644
index 0000000..3f5a751
--- /dev/null
+++ b/apps/codecs/libgme/opl_apu.h
@@ -0,0 +1,62 @@
+#ifndef OPL_APU_H

+#define OPL_APU_H

+

+#include "blargg_common.h"

+#include "blargg_source.h"

+#include "blip_buffer.h"

+

+#include "emu8950.h"

+#include "emu2413.h"

+

+enum opl_type_t { type_opll = 0x10, type_msxmusic = 0x11, type_smsfmunit = 0x12,

+			type_vrc7 = 0x13, type_msxaudio = 0x21 };

+

+enum { opl_osc_count = 1 };

+

+struct Opl_Apu {

+	struct Blip_Buffer* output_;

+	enum opl_type_t type_;

+

+	blip_time_t next_time;

+	int last_amp;

+	int addr;

+	

+	long clock_;

+	long rate_;

+	blip_time_t period_;

+	

+	struct Blip_Synth synth;

+

+	// OPL chips

+	struct Y8950 opl;

+	OPLL opll;

+	

+	unsigned char regs[ 0x100 ];

+	unsigned char opl_memory[ 32768 ];

+};

+

+blargg_err_t Opl_init( struct Opl_Apu* this, long clock, long rate, blip_time_t period, enum opl_type_t type );

+	

+void Opl_reset( struct Opl_Apu* this );

+static inline void Opl_volume( struct Opl_Apu* this, double v ) { Synth_volume( &this->synth, 1.0 / (4096 * 6) * v ); }

+	

+static inline void Opl_osc_output( struct Opl_Apu* this, int i, struct Blip_Buffer* buf )

+{

+#if defined(ROCKBOX)

+	(void) i;

+#endif

+	assert( (unsigned) i < opl_osc_count );

+	this->output_ = buf;

+}

+

+static inline void Opl_set_output( struct Opl_Apu* this, struct Blip_Buffer* buf ) { Opl_osc_output( this, 0, buf ); }

+void Opl_end_frame( struct Opl_Apu* this, blip_time_t ) ICODE_ATTR;

+

+static inline void Opl_write_addr( struct Opl_Apu* this, int data ) { this->addr = data; }

+void Opl_write_data( struct Opl_Apu* this, blip_time_t, int data ) ICODE_ATTR;

+

+int Opl_read( struct Opl_Apu* this, blip_time_t, int port ) ICODE_ATTR;

+

+static inline bool Opl_supported( void ) { return true; }

+

+#endif

diff --git a/apps/codecs/libgme/opltables.h b/apps/codecs/libgme/opltables.h
new file mode 100644
index 0000000..1414f22
--- /dev/null
+++ b/apps/codecs/libgme/opltables.h
@@ -0,0 +1,242 @@
+#ifndef _OPLTABLES_H_

+#define _OPLTABLES_H_

+

+/* Precalculated emu8950 tables for use in Rockbox, 

+   Calculated for 44Khz sampling rate */

+

+static const short ar_adjust_coeff[] ICONST_ATTR = {

+ 	 255,  227,  210,  198,  189,  181,  175,  170,  165,  161,  157, 

+	 153,  150,  147,  144,  141,  139,  136,  134,  132,  130,  128, 

+	 126,  125,  123,  121,  120,  118,  117,  115,  114,  113,  112, 

+	 110,  109,  108,  107,  106,  105,  104,  103,  102,  101,  100, 

+	  99,   98,   97,   96,   95,   94,   94,   93,   92,   91,   91, 

+	  90,   89,   88,   88,   87,   86,   86,   85,   84,   84,   83, 

+	  82,   82,   81,   81,   80,   79,   79,   78,   78,   77,   77, 

+	  76,   76,   75,   75,   74,   74,   73,   73,   72,   72,   71, 

+	  71,   70,   70,   69,   69,   69,   68,   68,   67,   67,   66, 

+	  66,   66,   65,   65,   64,   64,   64,   63,   63,   62,   62, 

+	  62,   61,   61,   61,   60,   60,   60,   59,   59,   59,   58, 

+	  58,   58,   57,   57,   57,   56,   56,   56,   55,   55,   55, 

+	  54,   54,   54,   53,   53,   53,   53,   52,   52,   52,   51, 

+	  51,   51,   50,   50,   50,   50,   49,   49,   49,   49,   48, 

+	  48,   48,   48,   47,   47,   47,   46,   46,   46,   46,   45, 

+	  45,   45,   45,   44,   44,   44,   44,   44,   43,   43,   43, 

+	  43,   42,   42,   42,   42,   41,   41,   41,   41,   41,   40, 

+	  40,   40,   40,   39,   39,   39,   39,   39,   38,   38,   38, 

+	  38,   38,   37,   37,   37,   37,   37,   36,   36,   36,   36, 

+	  36,   35,   35,   35,   35,   35,   34,   34,   34,   34,   34, 

+	  33,   33,   33,   33,   33,   33,   32,   32,   32,   32,   32, 

+	  31,   31,   31,   31,   31,   31,   30,   30,   30,   30,   30, 

+	  30,   29,   29,   29,   29,   29,   29,   28,   28,   28,   28, 

+	  28,   28,   27,   27,   27,   27,   27,   27,   26,   26,   26, 

+	  26,   26,   26,   26,   25,   25,   25,   25,   25,   25,   25, 

+	  24,   24,   24,   24,   24,   24,   23,   23,   23,   23,   23, 

+	  23,   23,   22,   22,   22,   22,   22,   22,   22,   21,   21, 

+	  21,   21,   21,   21,   21,   21,   20,   20,   20,   20,   20, 

+	  20,   20,   19,   19,   19,   19,   19,   19,   19,   19,   18, 

+	  18,   18,   18,   18,   18,   18,   18,   17,   17,   17,   17, 

+	  17,   17,   17,   17,   16,   16,   16,   16,   16,   16,   16, 

+	  16,   15,   15,   15,   15,   15,   15,   15,   15,   14,   14, 

+	  14,   14,   14,   14,   14,   14,   14,   13,   13,   13,   13, 

+	  13,   13,   13,   13,   13,   12,   12,   12,   12,   12,   12, 

+	  12,   12,   12,   11,   11,   11,   11,   11,   11,   11,   11, 

+	  11,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10, 

+	   9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    8, 

+	   8,    8,    8,    8,    8,    8,    8,    8,    8,    7,    7, 

+	   7,    7,    7,    7,    7,    7,    7,    7,    6,    6,    6, 

+	   6,    6,    6,    6,    6,    6,    6,    5,    5,    5,    5, 

+	   5,    5,    5,    5,    5,    5,    5,    4,    4,    4,    4, 

+	   4,    4,    4,    4,    4,    4,    4,    3,    3,    3,    3, 

+	   3,    3,    3,    3,    3,    3,    3,    3,    2,    2,    2, 

+	   2,    2,    2,    2,    2,    2,    2,    2,    1,    1,    1, 

+	   1,    1,    1,    1,    1,    1,    1,    1,    1,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0

+};

+   

+static const short db2lin_coeff[] ICONST_ATTR = {

+	2047, 2003, 1960, 1918, 1877, 1837, 1798, 1759, 1722, 1685, 1649, 

+	1614, 1579, 1546, 1513, 1480, 1449, 1418, 1387, 1358, 1329, 1300, 

+	1273, 1245, 1219, 1193, 1167, 1142, 1118, 1094, 1071, 1048, 1025, 

+	1004,  982,  961,  941,  920,  901,  882,  863,  844,  826,  809, 

+	 791,  774,  758,  742,  726,  710,  695,  680,  666,  651,  638, 

+	 624,  611,  598,  585,  572,  560,  548,  536,  525,  514,  503, 

+	 492,  481,  471,  461,  451,  442,  432,  423,  414,  405,  396, 

+	 388,  380,  371,  364,  356,  348,  341,  333,  326,  319,  312, 

+	 306,  299,  293,  287,  280,  274,  269,  263,  257,  252,  246, 

+	 241,  236,  231,  226,  221,  216,  212,  207,  203,  198,  194, 

+	 190,  186,  182,  178,  174,  170,  167,  163,  160,  156,  153, 

+	 150,  147,  143,  140,  137,  134,  131,  129,  126,  123,  121, 

+	 118,  115,  113,  111,  108,  106,  104,  101,   99,   97,   95, 

+	  93,   91,   89,   87,   85,   83,   82,   80,   78,   76,   75, 

+	  73,   72,   70,   69,   67,   66,   64,   63,   61,   60,   59, 

+	  58,   56,   55,   54,   53,   52,   51,   49,   48,   47,   46, 

+	  45,   44,   43,   42,   42,   41,   40,   39,   38,   37,   36, 

+	  36,   35,   34,   33,   33,   32,   31,   31,   30,   29,   29, 

+	  28,   27,   27,   26,   26,   25,   25,   24,   23,   23,   22, 

+	  22,   21,   21,   21,   20,   20,   19,   19,   18,   18,   18, 

+	  17,   17,   16,   16,   16,   15,   15,   15,   14,   14,   14, 

+	  13,   13,   13,   13,   12,   12,   12,   12,   11,   11,   11, 

+	  11,   10,   10,   10,   10,    9,    9,    9,    9,    9,    8, 

+	   8,    8,    8,    8,    7,    7,    7,    7,    7,    7,    7, 

+	   6,    6,    6,    6,    6,    6,    6,    5,    5,    5,    5, 

+	   5,    5,    5,    5,    4,    4,    4,    4,    4,    4,    4, 

+	   4,    4,    4,    3,    3,    3,    3,    3,    3,    3,    3, 

+	   3,    3,    3,    3,    3,    3,    2,    2,    2,    2,    2, 

+	   2,    2,    2,    2,    2,    2,    2,    2,    2,    2,    2, 

+	   2,    2,    2,    1,    1,    1,    1,    1,    1,    1,    1, 

+	   1,    1,    1,    1,    1,    1,    1,    1,    1,    1,    1, 

+	   1,    1,    1,    1,    1,    1,    1,    1,    1,    1,    1, 

+	   1,    1,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+   	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+   	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+   	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+   	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+   	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+   	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+  	   0,    0,    0,    0,    0,    0

+};

+

+static const short sin_coeff[] ICONST_ATTR = {

+	 511,  235,  203,  185,  171,  161,  152,  145,  139,  134,  129, 

+ 	 124,  120,  117,  113,  110,  107,  104,  102,   99,   97,   95, 

+	  92,   90,   88,   87,   85,   83,   81,   80,   78,   77,   75, 

+	  74,   72,   71,   70,   69,   67,   66,   65,   64,   63,   62, 

+	  61,   60,   59,   58,   57,   56,   55,   54,   53,   52,   52, 

+	  51,   50,   49,   48,   48,   47,   46,   45,   45,   44,   43, 

+	  43,   42,   41,   41,   40,   39,   39,   38,   38,   37,   37, 

+	  36,   35,   35,   34,   34,   33,   33,   32,   32,   31,   31, 

+	  30,   30,   29,   29,   28,   28,   28,   27,   27,   26,   26, 

+	  25,   25,   25,   24,   24,   23,   23,   23,   22,   22,   22, 

+	  21,   21,   21,   20,   20,   20,   19,   19,   19,   18,   18, 

+	  18,   17,   17,   17,   16,   16,   16,   16,   15,   15,   15, 

+	  14,   14,   14,   14,   13,   13,   13,   13,   12,   12,   12, 

+	  12,   11,   11,   11,   11,   11,   10,   10,   10,   10,    9, 

+	   9,    9,    9,    9,    8,    8,    8,    8,    8,    7,    7, 

+	   7,    7,    7,    7,    6,    6,    6,    6,    6,    6,    5, 

+	   5,    5,    5,    5,    5,    5,    4,    4,    4,    4,    4, 

+	   4,    4,    4,    3,    3,    3,    3,    3,    3,    3,    3, 

+	   2,    2,    2,    2,    2,    2,    2,    2,    2,    2,    2, 

+	   1,    1,    1,    1,    1,    1,    1,    1,    1,    1,    1, 

+	   1,    1,    1,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0, 

+};

+

+static const short pm0_coeff[] ICONST_ATTR = {

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+ 	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  257,  257,  257,  257,  257,  257,  257,  257,  257, 

+	 257,  257,  257,  257,  257,  257,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  254, 

+	 254,  254,  254,  254,  254,  254,  254,  254,  254,  254,  254, 

+	 254,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255, 

+};

+

+static const short pm1_coeff[] ICONST_ATTR = {

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+ 	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  257, 

+	 257,  257,  257,  257,  257,  257,  257,  257,  257,  257,  257, 

+	 257,  257,  257,  257,  257,  257,  257,  257,  257,  257,  257, 

+	 257,  257,  257,  257,  257,  257,  257,  257,  257,  257,  257, 

+	 257,  258,  258,  258,  258,  258,  258,  258,  258,  258,  258, 

+	 258,  258,  258,  258,  258,  258,  258,  257,  257,  257,  257, 

+	 257,  257,  257,  257,  257,  257,  257,  257,  257,  257,  257, 

+	 257,  257,  257,  257,  257,  257,  257,  257,  257,  257,  257, 

+	 257,  257,  257,  257,  257,  257,  257,  257,  257,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  256,  256,  256, 

+	 256,  256,  256,  256,  256,  256,  256,  256,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  254,  254,  254,  254,  254, 

+	 254,  254,  254,  254,  254,  254,  254,  254,  254,  254,  254, 

+	 254,  254,  254,  254,  254,  254,  254,  254,  254,  254,  254, 

+	 254,  254,  254,  254,  254,  254,  254,  254,  254,  254,  253, 

+	 253,  253,  253,  253,  253,  253,  253,  253,  253,  253,  253, 

+	 253,  254,  254,  254,  254,  254,  254,  254,  254,  254,  254, 

+	 254,  254,  254,  254,  254,  254,  254,  254,  254,  254,  254, 

+	 254,  254,  254,  254,  254,  254,  254,  254,  254,  254,  254, 

+	 254,  254,  254,  254,  254,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255,  255,  255,  255,  255,  255,  255,  255,  255, 

+	 255,  255,  255, 

+};

+

+static const short am0_coeff[] ICONST_ATTR = {

+	   2,    2,    2,    2,    2,    2,    3,    3,    3,    3,    3, 

+	   3,    3,    3,    3,    3,    3,    3,    3,    3,    3,    3, 

+	   4,    4,    4,    4,    4,    4,    4,    4,    4,    4,    4, 

+	   4,    4,    4,    4,    4,    4,    4,    4,    4,    4,    4, 

+	   5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5, 

+	   5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5, 

+	   5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5, 

+	   5,    5,    5,    5,    5,    5,    5,    5,    4,    4,    4, 

+	   4,    4,    4,    4,    4,    4,    4,    4,    4,    4,    4, 

+	   4,    4,    4,    4,    4,    4,    4,    4,    3,    3,    3, 

+	   3,    3,    3,    3,    3,    3,    3,    3,    3,    3,    3, 

+	   3,    3,    2,    2,    2,    2,    2,    2,    2,    2,    2, 

+	   2,    2,    2,    2,    2,    2,    2,    1,    1,    1,    1, 

+	   1,    1,    1,    1,    1,    1,    1,    1,    1,    1,    1, 

+	   1,    1,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    1,    1, 

+	   1,    1,    1,    1,    1,    1,    1,    1,    1,    1,    1, 

+	   1,    1,    1,    1,    2,    2,    2,    2,    2,    2,    2, 

+	   2,    2,    2, 

+};

+

+static const short am1_coeff[] ICONST_ATTR = {

+	  12,   13,   13,   13,   14,   14,   14,   14,   15,   15,   15, 

+	  16,   16,   16,   17,   17,   17,   17,   18,   18,   18,   19, 

+	  19,   19,   19,   20,   20,   20,   20,   21,   21,   21,   21, 

+	  22,   22,   22,   22,   22,   23,   23,   23,   23,   23,   23, 

+	  24,   24,   24,   24,   24,   24,   24,   24,   25,   25,   25, 

+	  25,   25,   25,   25,   25,   25,   25,   25,   25,   25,   25, 

+	  25,   25,   25,   25,   25,   25,   25,   25,   25,   25,   25, 

+	  24,   24,   24,   24,   24,   24,   24,   24,   23,   23,   23, 

+	  23,   23,   23,   22,   22,   22,   22,   22,   21,   21,   21, 

+	  21,   20,   20,   20,   20,   19,   19,   19,   19,   18,   18, 

+	  18,   17,   17,   17,   17,   16,   16,   16,   15,   15,   15, 

+	  14,   14,   14,   14,   13,   13,   13,   12,   12,   12,   11, 

+	  11,   11,   10,   10,   10,    9,    9,    9,    9,    8,    8, 

+	   8,    7,    7,    7,    7,    6,    6,    6,    5,    5,    5, 

+	   5,    4,    4,    4,    4,    3,    3,    3,    3,    3,    2, 

+	   2,    2,    2,    2,    1,    1,    1,    1,    1,    1,    1, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+	   1,    1,    1,    1,    1,    1,    1,    2,    2,    2,    2, 

+	   2,    3,    3,    3,    3,    3,    4,    4,    4,    4,    5, 

+	   5,    5,    5,    6,    6,    6,    7,    7,    7,    7,    8, 

+	   8,    8,    9,    9,    9,    9,   10,   10,   10,   11,   11, 

+	  11,   12,   12, 

+};

+

+#endif

diff --git a/apps/codecs/libgme/resampler.c b/apps/codecs/libgme/resampler.c
new file mode 100644
index 0000000..bcd98f6
--- /dev/null
+++ b/apps/codecs/libgme/resampler.c
@@ -0,0 +1,320 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+
+#include "resampler.h"
+
+#include <stdlib.h>
+#include <string.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"
+
+// TODO: fix this. hack since resampler holds back some output.
+unsigned const resampler_extra = 34;
+
+enum { shift = 14 };
+int const unit = 1 << shift;
+
+blargg_err_t Resampler_setup( struct Resampler* this, double oversample, double rolloff, double gain )
+{
+	(void) rolloff;
+	
+	this->gain_ = (int)((1 << gain_bits) * gain);
+	this->step = (int) ( oversample * unit + 0.5);
+	this->rate_ = 1.0 / unit * this->step;
+	return 0;
+}
+
+blargg_err_t Resampler_reset( struct Resampler* this, int pairs )
+{
+	// expand allocations a bit
+	Resampler_resize( this, pairs );
+	this->resampler_size = this->oversamples_per_frame + (this->oversamples_per_frame >> 2);
+
+	this->buffer_size = this->resampler_size;
+	this->pos = 0;
+	this->write_pos = 0;
+	return 0;
+}
+
+void Resampler_resize( struct Resampler* this, int pairs )
+{
+	int new_sample_buf_size = pairs * 2;
+	if ( this->sample_buf_size != new_sample_buf_size )
+	{
+		this->sample_buf_size = new_sample_buf_size;
+		this->oversamples_per_frame = (int) (pairs * this->rate_) * 2 + 2;
+		Resampler_clear( this );
+	}
+}
+
+void mix_mono( struct Resampler* this, struct Stereo_Buffer* stereo_buf, dsample_t* out_ )
+{
+	int const bass = BLIP_READER_BASS( stereo_buf->bufs [0] );
+	BLIP_READER_BEGIN( sn, stereo_buf->bufs [0] );
+	
+	int count = this->sample_buf_size >> 1;
+	BLIP_READER_ADJ_( sn, count );
+	
+	typedef dsample_t stereo_dsample_t [2];
+	stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count;
+	stereo_dsample_t const* BLARGG_RESTRICT in =
+			(stereo_dsample_t const*) this->sample_buf + count;
+	int offset = -count;
+	int const gain = this->gain_;
+	do
+	{
+		int s = BLIP_READER_READ_RAW( sn ) >> (blip_sample_bits - 16);
+		BLIP_READER_NEXT_IDX_( sn, bass, offset );
+		
+		int l = (in [offset] [0] * gain >> gain_bits) + s;
+		int r = (in [offset] [1] * gain >> gain_bits) + s;
+		
+		BLIP_CLAMP( l, l );
+		out [offset] [0] = (blip_sample_t) l;
+		
+		BLIP_CLAMP( r, r );
+		out [offset] [1] = (blip_sample_t) r;
+	}
+	while ( ++offset );
+	
+	BLIP_READER_END( sn, stereo_buf->bufs [0] );
+}
+
+void mix_stereo( struct Resampler* this, struct Stereo_Buffer* stereo_buf, dsample_t* out_ )
+{
+	int const bass = BLIP_READER_BASS( stereo_buf->bufs [0] );
+	BLIP_READER_BEGIN( snc, stereo_buf->bufs [0] );
+	BLIP_READER_BEGIN( snl, stereo_buf->bufs [1] );
+	BLIP_READER_BEGIN( snr, stereo_buf->bufs [2] );
+	
+	int count = this->sample_buf_size >> 1;
+	BLIP_READER_ADJ_( snc, count );
+	BLIP_READER_ADJ_( snl, count );
+	BLIP_READER_ADJ_( snr, count );
+	
+	typedef dsample_t stereo_dsample_t [2];
+	stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count;
+	stereo_dsample_t const* BLARGG_RESTRICT in =
+			(stereo_dsample_t const*) this->sample_buf + count;
+	int offset = -count;
+	int const gain = this->gain_;
+	do
+	{
+		int sc = BLIP_READER_READ_RAW( snc ) >> (blip_sample_bits - 16);
+		int sl = BLIP_READER_READ_RAW( snl ) >> (blip_sample_bits - 16);
+		int sr = BLIP_READER_READ_RAW( snr ) >> (blip_sample_bits - 16);
+		BLIP_READER_NEXT_IDX_( snc, bass, offset );
+		BLIP_READER_NEXT_IDX_( snl, bass, offset );
+		BLIP_READER_NEXT_IDX_( snr, bass, offset );
+		
+		int l = (in [offset] [0] * gain >> gain_bits) + sl + sc;
+		int r = (in [offset] [1] * gain >> gain_bits) + sr + sc;
+		
+		BLIP_CLAMP( l, l );
+		out [offset] [0] = (blip_sample_t) l;
+		
+		BLIP_CLAMP( r, r );
+		out [offset] [1] = (blip_sample_t) r;
+	}
+	while ( ++offset );
+	
+	BLIP_READER_END( snc, stereo_buf->bufs [0] );
+	BLIP_READER_END( snl, stereo_buf->bufs [1] );
+	BLIP_READER_END( snr, stereo_buf->bufs [2] );
+}
+
+void mix_stereo_no_center( struct Resampler* this, struct Stereo_Buffer* stereo_buf, dsample_t* out_ )
+{
+	int const bass = BLIP_READER_BASS( stereo_buf->bufs [0] );
+	BLIP_READER_BEGIN( snl, stereo_buf->bufs [1] );
+	BLIP_READER_BEGIN( snr, stereo_buf->bufs [2] );
+	
+	int count = this->sample_buf_size >> 1;
+	BLIP_READER_ADJ_( snl, count );
+	BLIP_READER_ADJ_( snr, count );
+	
+	typedef dsample_t stereo_dsample_t [2];
+	stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count;
+	stereo_dsample_t const* BLARGG_RESTRICT in =
+			(stereo_dsample_t const*) this->sample_buf + count;
+	int offset = -count;
+	int const gain = this->gain_;
+	do
+	{
+		int sl = BLIP_READER_READ_RAW( snl ) >> (blip_sample_bits - 16);
+		int sr = BLIP_READER_READ_RAW( snr ) >> (blip_sample_bits - 16);
+		BLIP_READER_NEXT_IDX_( snl, bass, offset );
+		BLIP_READER_NEXT_IDX_( snr, bass, offset );
+		
+		int l = (in [offset] [0] * gain >> gain_bits) + sl;
+		int r = (in [offset] [1] * gain >> gain_bits) + sr;
+		
+		BLIP_CLAMP( l, l );
+		out [offset] [0] = (blip_sample_t) l;
+		
+		BLIP_CLAMP( r, r );
+		out [offset] [1] = (blip_sample_t) r;
+	}
+	while ( ++offset );
+	
+	BLIP_READER_END( snl, stereo_buf->bufs [1] );
+	BLIP_READER_END( snr, stereo_buf->bufs [2] );
+}
+
+dsample_t const* resample_( struct Resampler* this, dsample_t** out_,
+		dsample_t const* out_end, dsample_t const in [], int in_size )
+{
+	in_size -= write_offset;
+	if ( in_size > 0 )
+	{
+		dsample_t* BLIP_RESTRICT out = *out_;
+		dsample_t const* const in_end = in + in_size;
+		
+		int const step = this->step;
+		int       pos  = this->pos;
+		
+		// TODO: IIR filter, then linear resample
+		// TODO: detect skipped sample, allowing merging of IIR and resample?
+		
+		do
+		{
+			#define INTERP( i, out )\
+				out = (in [0 + i] * (unit - pos) + ((in [2 + i] + in [4 + i] + in [6 + i]) << shift) +\
+				in [8 + i] * pos) >> (shift + 2);
+			
+			int out_0;
+			INTERP( 0,                  out_0 )
+			INTERP( 1, out [0] = out_0; out [1] )
+			out += stereo;
+			
+			pos += step;
+			in += ((unsigned) pos >> shift) * stereo;
+			pos &= unit - 1;
+		}
+		while ( in < in_end && out < out_end );
+		
+		this->pos = pos;
+		*out_ = out;
+	}
+	return in;
+}
+
+inline int resample_wrapper( struct Resampler* this, dsample_t out [], int* out_size,
+		dsample_t const in [], int in_size )
+{
+	assert( Resampler_rate( this ) );
+	
+	dsample_t* out_ = out;
+	int result = resample_( this, &out_, out + *out_size, in, in_size ) - in;
+	assert( out_ <= out + *out_size );
+	assert( result <= in_size );
+	
+	*out_size = out_ - out;
+	return result;
+}
+
+int skip_input( struct Resampler* this, int count )
+{
+	this->write_pos -= count;
+	if ( this->write_pos < 0 ) // occurs when downsampling
+	{
+		count += this->write_pos;
+		this->write_pos = 0;
+	}
+	memmove( this->buf, &this->buf [count], this->write_pos * sizeof this->buf [0] );
+	return count;
+}
+
+void play_frame_( struct Resampler* this, struct Stereo_Buffer* stereo_buf, dsample_t* out )
+{
+	long pair_count = this->sample_buf_size >> 1;
+	blip_time_t blip_time = Blip_count_clocks( &stereo_buf->bufs [0], pair_count );
+	int sample_count = this->oversamples_per_frame - this->write_pos + resampler_extra;
+	
+	int new_count = this->callback( this->callback_data, blip_time, sample_count, &this->buf [this->write_pos] );
+	assert( new_count < resampler_size );
+	
+	Buffer_end_frame( stereo_buf, blip_time );
+	/* Blip_end_frame( &stereo_buf->bufs [0], blip_time ); */
+	assert( Blip_samples_avail( &stereo_buf->bufs [0] ) == pair_count * 2 );
+	
+	this->write_pos += new_count;
+	assert( (unsigned) this->write_pos <= this->buffer_size );
+	
+	new_count = this->sample_buf_size;
+	if ( new_count )
+		skip_input( this, resample_wrapper( this, this->sample_buf, &new_count, this->buf, this->write_pos ) );
+	assert( new_count == (long) this->sample_buf_size );
+	
+	int bufs_used = stereo_buf->stereo_added | stereo_buf->was_stereo;
+	if ( bufs_used <= 1 ) {
+		mix_mono( this, stereo_buf, out );
+		Blip_remove_samples( &stereo_buf->bufs [0], pair_count );
+		Blip_remove_silence( &stereo_buf->bufs [1], pair_count );
+		Blip_remove_silence( &stereo_buf->bufs [2], pair_count );
+	}
+	else if ( bufs_used & 1 ) {
+		mix_stereo( this, stereo_buf, out );
+		Blip_remove_samples( &stereo_buf->bufs [0], pair_count );
+		Blip_remove_samples( &stereo_buf->bufs [1], pair_count );
+		Blip_remove_samples( &stereo_buf->bufs [2], pair_count );
+	}
+	else {
+		mix_stereo_no_center( this, stereo_buf, out );
+		Blip_remove_silence( &stereo_buf->bufs [0], pair_count );
+		Blip_remove_samples( &stereo_buf->bufs [1], pair_count );
+		Blip_remove_samples( &stereo_buf->bufs [2], pair_count );
+	}
+	
+	// to do: this might miss opportunities for optimization
+	if ( !Blip_samples_avail( &stereo_buf->bufs [0] ) )
+	{
+		stereo_buf->was_stereo   = stereo_buf->stereo_added;
+		stereo_buf->stereo_added = 0;
+	}
+	
+	/* mix_mono( this, stereo_buf, out );
+	Blip_remove_samples( &stereo_buf->bufs [0], pair_count ); */
+}
+
+void Resampler_play( struct Resampler* this, long count, dsample_t* out, struct Stereo_Buffer* stereo_buf )
+{
+	// empty extra buffer
+	long remain = this->sample_buf_size - this->buf_pos;
+	if ( remain )
+	{
+		if ( remain > count )
+			remain = count;
+		count -= remain;
+		memcpy( out, &this->sample_buf [this->buf_pos], remain * sizeof *out );
+		out += remain;
+		this->buf_pos += remain;
+	}
+	
+	// entire frames
+	while ( count >= (long) this->sample_buf_size )
+	{
+		play_frame_( this, stereo_buf, out );
+		out += this->sample_buf_size;
+		count -= this->sample_buf_size;
+	}
+	
+	// extra
+	if ( count )
+	{
+		play_frame_( this, stereo_buf, this->sample_buf );
+		this->buf_pos = count;
+		memcpy( out, this->sample_buf, count * sizeof *out );
+		out += count;
+	}
+}
diff --git a/apps/codecs/libgme/resampler.h b/apps/codecs/libgme/resampler.h
new file mode 100644
index 0000000..f5e8c55
--- /dev/null
+++ b/apps/codecs/libgme/resampler.h
@@ -0,0 +1,68 @@
+// Combination of Downsampler and Blip_Buffer mixing. Used by Sega FM emulators.
+
+// Game_Music_Emu 0.5.5
+#ifndef RESAMPLER_H
+#define RESAMPLER_H
+
+#include "blargg_config.h"
+#include "multi_buffer.h"
+
+typedef short dsample_t;
+
+enum { stereo = 2 };
+enum { max_buf_size = 3960 };
+enum { max_resampler_size = 5942 };
+enum { write_offset = 8 * stereo };
+enum { gain_bits = 14 };
+
+struct Resampler {		
+	int (*callback)( void*, blip_time_t, int, dsample_t* );
+	void* callback_data;
+	
+	dsample_t sample_buf [max_buf_size];
+	int sample_buf_size;
+	int oversamples_per_frame;
+	int buf_pos;
+	int resampler_size;
+	int gain_;
+	
+	// Internal resampler
+	dsample_t buf [max_resampler_size];
+	int buffer_size;
+	
+	int write_pos;
+	double rate_;
+	
+	int pos;
+	int step;
+};
+
+static inline void Resampler_init( struct Resampler* this )
+{
+	this->pos = 0;
+	this->write_pos = 0;
+	this->rate_     = 0;
+}
+
+blargg_err_t Resampler_reset( struct Resampler* this, int max_pairs );
+void Resampler_resize( struct Resampler* this, int pairs_per_frame );
+	
+void Resampler_play( struct Resampler* this, long count, dsample_t* out, struct Stereo_Buffer* ) ICODE_ATTR;
+
+static inline void Resampler_set_callback(struct Resampler* this, int (*func)( void*, blip_time_t, int, dsample_t* ), void* user_data )
+{
+	this->callback = func;
+	this->callback_data = user_data;
+}
+
+blargg_err_t Resampler_setup( struct Resampler* this, double oversample, double rolloff, double gain );
+
+static inline void Resampler_clear( struct Resampler* this )
+{
+	this->buf_pos = this->sample_buf_size;
+
+	this->pos = 0;
+	this->write_pos = 0;
+}
+
+#endif
diff --git a/apps/codecs/libgme/rom_data.c b/apps/codecs/libgme/rom_data.c
new file mode 100644
index 0000000..5fe3115
--- /dev/null
+++ b/apps/codecs/libgme/rom_data.c
@@ -0,0 +1,68 @@
+// 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, const void* data, long size,
+		int header_size, void* header_out, int fill )
+{
+	long file_offset = this->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, this->rom_size );
+	memcpy( &this->unmapped [file_offset], this->file_data, 
+		this->file_size < this->pad_size ? this->file_size : this->pad_size );
+	
+	return 0;
+}
+
+void Rom_set_addr( struct Rom_Data* this, long addr )
+{
+	this->rom_addr = addr - this->bank_size - pad_extra;
+	
+	long rounded = (addr + this->file_size + this->bank_size - 1) / this->bank_size * this->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;
+}
diff --git a/apps/codecs/libgme/rom_data.h b/apps/codecs/libgme/rom_data.h
new file mode 100644
index 0000000..28b34f2
--- /dev/null
+++ b/apps/codecs/libgme/rom_data.h
@@ -0,0 +1,83 @@
+// 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 { pad_extra = 8 };
+enum { max_bank_size = 0x4000 };
+enum { max_pad_size = max_bank_size + pad_extra };
+enum { max_rom_size = 2 * max_pad_size };
+
+struct  Rom_Data {
+	byte* file_data;
+	blargg_ulong file_size;
+	
+	blargg_long rom_addr;
+	blargg_long bank_size;
+	blargg_long rom_size;
+	blargg_ulong pad_size;
+	blargg_long mask;
+	blargg_long size; // TODO: eliminate
+	blargg_long rsize_;
+	
+	// Unmapped space
+	byte unmapped [max_rom_size];
+};
+
+// Initialize rom
+static inline void Rom_init( struct Rom_Data* this, blargg_long bank_size )
+{
+	this->bank_size = bank_size;
+	this->pad_size = this->bank_size + pad_extra;
+	this->rom_size = 2 * this->pad_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, const 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_ - this->pad_size) )
+		offset = 0; // unmapped
+			
+	if ( offset < this->pad_size ) return &this->unmapped [offset];
+	else return &this->file_data [offset - this->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
diff --git a/apps/codecs/libgme/sgc_cpu.c b/apps/codecs/libgme/sgc_cpu.c
new file mode 100644
index 0000000..3bd2d15
--- /dev/null
+++ b/apps/codecs/libgme/sgc_cpu.c
@@ -0,0 +1,36 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/

+

+#include "sgc_emu.h"

+

+#include "blargg_endian.h"

+//#include "z80_cpu_log.h"

+

+/* Copyright (C) 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"

+

+#define OUT_PORT( addr, data ) cpu_out( this, TIME(), addr, data )

+#define IN_PORT( addr ) 0 // cpu in

+#define WRITE_MEM( addr, data ) cpu_write( this, addr, data )

+#define IDLE_ADDR               this->idle_addr

+#define RST_BASE                this->vectors_addr

+

+#define CPU_BEGIN \

+bool run_cpu( struct Sgc_Emu* this, cpu_time_t end_time )\

+{\

+	Sgc_Cpu* cpu = &this->cpu; \

+	Z80_set_end_time( cpu, end_time );

+

+	#include "z80_cpu_run.h"

+	

+	return warning;

+}

diff --git a/apps/codecs/libgme/sgc_emu.c b/apps/codecs/libgme/sgc_emu.c
new file mode 100644
index 0000000..9abfc00
--- /dev/null
+++ b/apps/codecs/libgme/sgc_emu.c
@@ -0,0 +1,673 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/

+

+#include "sgc_emu.h"

+

+/* Copyright (C) 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"

+

+int const osc_count = sms_osc_count + fm_apu_osc_count;

+

+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)

+

+const char gme_wrong_file_type [] = "Wrong file type for this emulator";

+

+void clear_track_vars( struct Sgc_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       = INT_MAX / 2 + 1;

+	this->fade_step        = 1;

+	this->silence_time     = 0;

+	this->silence_count    = 0;

+	this->buf_remain       = 0;

+	/* warning(); // clear warning */

+}

+

+void Sgc_init( struct Sgc_Emu* this )

+{

+	assert( offsetof (struct header_t,copyright [32]) == header_size );

+	

+	this->sample_rate = 0;

+	this->mute_mask_  = 0;

+	this->tempo       = 1.0;

+	this->gain        = 1.0;

+	this->voice_count = 0;

+	

+	// defaults

+	this->max_initial_silence = 2;

+	this->silence_lookahead   = 6;

+	this->ignore_silence      = false;

+

+	Sms_apu_init( &this->apu );

+	Fm_apu_create( &this->fm_apu );

+

+	Rom_init( &this->rom, 0x4000 );

+	Z80_init( &this->cpu );

+

+	Sound_set_gain( this, 1.2 );

+		

+	// Unload

+	clear_track_vars( this );

+}

+

+// Setup

+

+blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size )

+{

+	RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) );

+	

+	if ( !valid_tag( &this->header ) )

+		return gme_wrong_file_type;

+	

+	/* if ( header.vers != 1 )

+		warning( "Unknown file version" ); */

+	

+	/* if ( header.system > 2 )

+		warning( "Unknown system" ); */

+	

+	addr_t load_addr = get_le16( this->header.load_addr );

+	/* if ( load_addr < 0x400 )

+		set_warning( "Invalid load address" ); */

+	

+	Rom_set_addr( &this->rom, load_addr );

+	this->play_period = clock_rate( this ) / 60;

+	

+	if ( sega_mapping( this ) && Fm_apu_supported() )

+		RETURN_ERR( Fm_apu_init( &this->fm_apu, clock_rate( this ), clock_rate( this ) / 72 ) );

+	

+	this->m3u.size = 0;

+	this->track_count = this->header.song_count;

+	this->voice_count =  sega_mapping( this ) ? osc_count : sms_osc_count;

+	

+	Sms_apu_volume( &this->apu, this->gain );

+	Fm_apu_volume( &this->fm_apu, this->gain );

+

+	// Setup buffer

+	this->clock_rate_ = clock_rate( this );

+	Buffer_clock_rate( &this->stereo_buf, clock_rate( this ) );

+	this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );

+	Sound_set_tempo( this, this->tempo );

+

+	// Remute voices

+	Sound_mute_voices( this, this->mute_mask_ );

+	return 0;

+}

+

+void Sound_set_voice( struct Sgc_Emu* this, int i, struct Blip_Buffer* c, struct Blip_Buffer* l, struct Blip_Buffer* r )

+{

+	if ( i < sms_osc_count )

+		Sms_apu_set_output( &this->apu, i, c, l, r );

+	else

+		Fm_apu_set_output( &this->fm_apu, c );

+}

+

+blargg_err_t run_clocks( struct Sgc_Emu* this, blip_time_t* duration, int msec )

+{

+#if defined(ROCKBOX)

+	(void) msec;

+#endif

+

+	cpu_time_t t = *duration;

+	while ( Z80_time( &this->cpu ) < t )

+	{

+		cpu_time_t next = min( t, this->next_play );

+		if ( run_cpu( this, next ) )

+		{

+			/* warning( "Unsupported CPU instruction" ); */

+			Z80_set_time( &this->cpu, next );

+		}

+		

+		if ( this->cpu.r.pc == this->idle_addr )

+			Z80_set_time( &this->cpu, next );

+		

+		if ( Z80_time( &this->cpu ) >= this->next_play )

+		{

+			this->next_play += this->play_period;

+			if ( this->cpu.r.pc == this->idle_addr )

+				jsr( this, this->header.play_addr );

+		}

+	}

+	

+	this->next_play -= t;

+	check( this->next_play >= 0 );

+	Z80_adjust_time( &this->cpu, -t );

+	

+	Sms_apu_end_frame( &this->apu, t );

+	if ( sega_mapping( this ) && this->fm_accessed )

+	{

+		if ( Fm_apu_supported() )

+			Fm_apu_end_frame( &this->fm_apu, t );

+		/* else

+			warning( "FM sound not supported" ); */

+	}

+

+	return 0;

+}

+

+// Emulation

+

+void cpu_out( struct Sgc_Emu* this, cpu_time_t time, addr_t addr, int data )

+{

+	int port = addr & 0xFF;

+	

+	if ( sega_mapping( this ) )

+	{

+		switch ( port )

+		{

+		case 0x06:

+			Sms_apu_write_ggstereo( &this->apu, time, data );

+			return;

+		

+		case 0x7E:

+		case 0x7F:

+			Sms_apu_write_data( &this->apu, time, data ); /* dprintf( "$7E<-%02X\n", data ); */

+			return;

+		

+		case 0xF0:

+			this->fm_accessed = true;

+			if ( Fm_apu_supported() )

+				Fm_apu_write_addr( &this->fm_apu, data );//, dprintf( "$F0<-%02X\n", data );

+			return;

+		

+		case 0xF1:

+			this->fm_accessed = true;

+			if ( Fm_apu_supported() )

+				Fm_apu_write_data( &this->fm_apu, time, data );//, dprintf( "$F1<-%02X\n", data );

+			return;

+		}

+	}

+	else if ( port >= 0xE0 )

+	{

+		Sms_apu_write_data( &this->apu, time, data );

+		return;

+	}

+}

+

+void jsr( struct Sgc_Emu* this, byte addr [2] )

+{

+	*Z80_write( &this->cpu, --this->cpu.r.sp ) = this->idle_addr >> 8;

+	*Z80_write( &this->cpu, --this->cpu.r.sp ) = this->idle_addr & 0xFF;

+	this->cpu.r.pc = get_le16( addr );

+}

+

+void set_bank( struct Sgc_Emu* this, int bank, void const* data )

+{

+	//dprintf( "map bank %d to %p\n", bank, (byte*) data - rom.at_addr( 0 ) );

+	Z80_map_mem( &this->cpu, bank * this->rom.bank_size, this->rom.bank_size, this->unmapped_write, data );

+}

+

+void cpu_write( struct Sgc_Emu* this, addr_t addr, int data )

+{

+	if ( (addr ^ 0xFFFC) > 3 || !sega_mapping( this ) )

+	{

+		*Z80_write( &this->cpu, addr ) = data;

+		return;

+	}

+	

+	switch ( addr )

+	{

+	case 0xFFFC:

+		Z80_map_mem_rw( &this->cpu, 2 * this->rom.bank_size, this->rom.bank_size, this->ram2 );

+		if ( data & 0x08 )

+			break;

+		

+		this->bank2 = this->ram2;

+		// FALL THROUGH

+	

+	case 0xFFFF: {

+		bool rom_mapped = (Z80_read( &this->cpu, 2 * this->rom.bank_size ) == this->bank2);

+		this->bank2 = Rom_at_addr( &this->rom, data * this->rom.bank_size );

+		if ( rom_mapped )

+			set_bank( this, 2, this->bank2 );

+		break;

+	}

+		

+	case 0xFFFD:

+		set_bank( this, 0, Rom_at_addr( &this->rom, data * this->rom.bank_size ) );

+		break;

+	

+	case 0xFFFE:

+		set_bank( this, 1, Rom_at_addr( &this->rom, data * this->rom.bank_size ) );

+		break;

+	}

+}

+

+blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long rate )

+{

+	require( !this->sample_rate ); // sample rate can't be changed once set

+	Buffer_init( &this->stereo_buf );

+	Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 );

+

+	// Set buffer bass

+	Buffer_bass_freq( &this->stereo_buf, 80 );

+

+	this->sample_rate = rate;

+	return 0;

+}

+

+void Sound_mute_voice( struct Sgc_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 Sgc_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) )

+		{

+			Sound_set_voice( this, i, 0, 0, 0 );

+		}

+		else

+		{

+			struct channel_t ch = Buffer_channel( &this->stereo_buf );

+			assert( (ch.center && ch.left && ch.right) ||

+					(!ch.center && !ch.left && !ch.right) ); // all or nothing

+			Sound_set_voice( this, i, ch.center, ch.left, ch.right );

+		}

+	}

+}

+

+void Sound_set_tempo( struct Sgc_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->play_period = (int) (clock_rate( this ) / (this->header.rate ? 50 : 60) / t);

+}

+

+void fill_buf( struct Sgc_Emu* this ) ICODE_ATTR;

+blargg_err_t Sgc_start_track( struct Sgc_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];

+		track = e->track;

+	}

+	

+	this->current_track = track;

+	

+	if ( sega_mapping( this ) )

+	{

+		Sms_apu_reset( &this->apu, 0, 0 );

+		Fm_apu_reset( &this->fm_apu );

+		this->fm_accessed = false;

+	}

+	else

+	{

+		Sms_apu_reset( &this->apu, 0x0003, 15 );

+	}

+	

+	memset( this->ram , 0, sizeof this->ram );

+	memset( this->ram2, 0, sizeof this->ram2 );

+	memset( this->vectors, 0xFF, sizeof this->vectors );

+	Z80_reset( &this->cpu, this->unmapped_write, this->rom.unmapped );

+	

+	if ( sega_mapping( this ) )

+	{

+		this->vectors_addr = 0x10000 - page_size;

+		this->idle_addr = this->vectors_addr;

+		int i;

+		for ( i = 1; i < 8; ++i )

+		{

+			this->vectors [i*8 + 0] = 0xC3; // JP addr

+			this->vectors [i*8 + 1] = this->header.rst_addrs [i - 1] & 0xff;

+			this->vectors [i*8 + 2] = this->header.rst_addrs [i - 1] >> 8;

+		}

+		

+		Z80_map_mem_rw( &this->cpu, 0xC000, 0x2000, this->ram );

+		Z80_map_mem( &this->cpu, this->vectors_addr, page_size, this->unmapped_write, this->vectors );

+		

+		this->bank2 = NULL;

+		for ( i = 0; i < 4; ++i )

+			cpu_write( this, 0xFFFC + i, this->header.mapping [i] );

+	}

+	else

+	{

+		if ( !this->coleco_bios )

+			return "Coleco BIOS not set"; /* BLARGG_ERR( BLARGG_ERR_CALLER, "Coleco BIOS not set" ); */

+		

+		this->vectors_addr = 0;

+		Z80_map_mem( &this->cpu, 0, 0x2000, this->unmapped_write, this->coleco_bios );

+		int i;

+		for ( i = 0; i < 8; ++i )

+			Z80_map_mem_rw( &this->cpu, 0x6000 + i*0x400, 0x400, this->ram );

+		

+		this->idle_addr = 0x2000;

+		Z80_map_mem( &this->cpu, 0x2000, page_size, this->unmapped_write, this->vectors );

+		

+		for ( i = 0; i < 0x8000 / this->rom.bank_size; ++i )

+		{

+			int addr = 0x8000 + i*this->rom.bank_size;

+			Z80_map_mem( &this->cpu, addr, this->rom.bank_size, this->unmapped_write, Rom_at_addr( &this->rom, addr ) );

+		}

+	}

+	

+	this->cpu.r.sp  = get_le16( this->header.stack_ptr );

+	this->cpu.r.b.a = track;

+	this->next_play = this->play_period;

+

+	jsr( this, this->header.init_addr );

+

+	Buffer_clear( &this->stereo_buf );

+

+	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;

+}

+

+// Tell/Seek

+

+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 Sgc_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 Sgc_Emu* this, long msec )

+{

+	blargg_long time = msec_to_samples( msec, this->sample_rate );

+	if ( time < this->out_time )

+		RETURN_ERR( Sgc_start_track( this, this->current_track ) );

+	return Track_skip( this, time - this->out_time );

+}

+

+blargg_err_t skip_( struct Sgc_Emu* this, long count ) ICODE_ATTR;

+blargg_err_t Track_skip( struct Sgc_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;

+}

+

+blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out ) ICODE_ATTR;

+blargg_err_t skip_( struct Sgc_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;

+}

+

+// Fading

+

+void Track_set_fade( struct Sgc_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 Sgc_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 Sgc_Emu* this, long count, sample_t* out )

+{

+	check( this->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 Sgc_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 Sgc_play( struct Sgc_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 );

+		

+		// prints nifty graph of how far ahead we are when searching for silence

+		//debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );

+		

+		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;

+}

+

+blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out )

+{

+	long remain = count;

+	while ( remain )

+	{

+		remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );

+		if ( remain )

+		{

+			if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )

+			{

+				this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );

+				

+				// Remute voices

+				Sound_mute_voices( this, this->mute_mask_ );

+			}

+			int msec = Buffer_length( &this->stereo_buf );

+			blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;

+			RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );

+			assert( clocks_emulated );

+			Buffer_end_frame( &this->stereo_buf, clocks_emulated );

+		}

+	}

+	return 0;

+}

diff --git a/apps/codecs/libgme/sgc_emu.h b/apps/codecs/libgme/sgc_emu.h
new file mode 100644
index 0000000..957e743
--- /dev/null
+++ b/apps/codecs/libgme/sgc_emu.h
@@ -0,0 +1,199 @@
+// Sega/Game Gear/Coleco SGC music file emulator

+

+// Game_Music_Emu 0.6-pre

+#ifndef SGC_EMU_H

+#define SGC_EMU_H

+

+#include "blargg_common.h"

+#include "multi_buffer.h"

+

+#include "rom_data.h"

+#include "z80_cpu.h"

+#include "sms_fm_apu.h"

+#include "sms_apu.h"

+#include "m3u_playlist.h"

+

+typedef short sample_t;

+typedef struct Z80_Cpu Sgc_Cpu;

+

+enum { buf_size = 2048 };

+

+// SGC file header

+enum { header_size = 0xA0 };

+struct header_t

+{	

+	char tag       [4]; // "SGC\x1A"

+	byte vers;          // 0x01

+	byte rate;          // 0=NTSC 1=PAL

+	byte reserved1 [2];

+	byte load_addr [2];

+	byte init_addr [2];

+	byte play_addr [2];

+	byte stack_ptr [2];

+	byte reserved2 [2];

+	byte rst_addrs [7*2];

+	byte mapping   [4]; // Used by Sega only

+	byte first_song;    // Song to start playing first

+	byte song_count;

+	byte first_effect;

+	byte last_effect;

+	byte system;        // 0=Master System 1=Game Gear 2=Colecovision

+	byte reserved3 [23];

+	char game      [32]; // strings can be 32 chars, NOT terminated

+	char author    [32];

+	char copyright [32];

+};

+

+// True if header has valid file signature

+static inline bool valid_tag( struct header_t* h )

+{

+	return 0 == memcmp( h->tag, "SGC\x1A", 4 );	

+}		

+		

+static inline int effect_count( struct header_t* h ) { return h->last_effect ? h->last_effect - h->first_effect + 1 : 0; }

+

+

+struct Sgc_Emu {		

+	bool fm_accessed;

+

+	cpu_time_t play_period;

+	cpu_time_t next_play;

+	void const* bank2;      // ROM selected for bank 2, in case RAM is currently hiding it

+	addr_t vectors_addr;    // RST vectors start here

+	addr_t idle_addr;       // return address for init/play routines

+	void* coleco_bios;

+	

+	// general

+	int voice_count;

+	int mute_mask_;

+	double tempo;

+	double gain;

+	

+	long sample_rate;

+	

+	// track-specific

+	volatile bool track_ended;

+	int current_track;

+	int track_count;

+	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

+	bool ignore_silence;

+	int max_initial_silence;

+	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

+

+	long clock_rate_;

+	unsigned buf_changed_count;

+	

+	// M3u Playlist

+	struct M3u_Playlist m3u;

+	

+	sample_t buf [buf_size];

+	struct Stereo_Buffer stereo_buf;

+	

+	struct Sms_Apu apu;

+	struct Sms_Fm_Apu fm_apu;

+	

+	Sgc_Cpu cpu;

+	

+	// large items

+	struct header_t header;

+	struct Rom_Data rom;

+	byte vectors [page_size + page_padding];

+	byte ram [0x2000 + page_padding];

+	byte ram2 [0x4000 + page_padding];

+	byte unmapped_write [0x4000];

+};

+

+// Basic functionality (see Gme_File.h for file loading/track info functions)

+

+void Sgc_init( struct Sgc_Emu* this );

+

+blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size );

+	

+static inline int clock_rate( struct Sgc_Emu* this ) { return this->header.rate ? 3546893 : 3579545; }

+

+// 0x2000 bytes

+static inline void set_coleco_bios( struct Sgc_Emu* this, void* p ) { this->coleco_bios = p; }

+

+// Set output sample rate. Must be called only once before loading file.

+blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long sample_rate );

+

+// Start a track, where 0 is the first track. Also clears warning string.

+blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track );

+

+// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation

+// errors set warning string, and major errors also end track.

+blargg_err_t Sgc_play( struct Sgc_Emu* this, long count, sample_t* buf );

+

+// Track status/control

+

+// Number of milliseconds (1000 msec = 1 second) played since beginning of track

+long Track_tell( struct Sgc_Emu* this );

+

+// Seek to new time in track. Seeking backwards or far forward can take a while.

+blargg_err_t Track_seek( struct Sgc_Emu* this, long msec );

+

+// Skip n samples

+blargg_err_t Track_skip( struct Sgc_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 Sgc_Emu* this, long start_msec, long length_msec );

+

+// Get track length in milliseconds

+static inline long Track_get_length( struct Sgc_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;

+	} 

+

+	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 Sgc_Emu* this, double t );

+

+// Mute/unmute voice i, where voice 0 is first voice

+void Sound_mute_voice( struct Sgc_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 Sgc_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 Sgc_Emu* this, double g )

+{

+	assert( !this->sample_rate ); // you must set gain before setting sample rate

+	this->gain = g;

+}

+

+// True if Master System or Game Gear

+static inline bool sega_mapping( struct Sgc_Emu* this )

+{

+	return this->header.system <= 1;

+}

+

+// Emulation (You shouldn't touch these)

+

+bool run_cpu( struct Sgc_Emu* this, cpu_time_t end_time ) ICODE_ATTR;

+void cpu_out( struct Sgc_Emu* this, cpu_time_t time, addr_t addr, int data ) ICODE_ATTR;

+void cpu_write( struct Sgc_Emu* this, addr_t addr, int data ) ICODE_ATTR;

+void jsr( struct Sgc_Emu* this, byte addr [2] ) ICODE_ATTR;

+

+#endif

diff --git a/apps/codecs/libgme/sms_apu.c b/apps/codecs/libgme/sms_apu.c
new file mode 100644
index 0000000..4be63db
--- /dev/null
+++ b/apps/codecs/libgme/sms_apu.c
@@ -0,0 +1,310 @@
+// Sms_Snd_Emu 0.1.1. http://www.slack.net/~ant/
+
+#include "sms_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 noise_osc = 3;
+
+void Sms_apu_volume( struct Sms_Apu* this, double vol )
+{
+	vol *= 0.85 / sms_osc_count / 64;
+	Synth_volume( &this->synth, vol );
+}
+
+inline int calc_output( struct Sms_Apu* this, int i )
+{
+	int flags = this->ggstereo >> i;
+	return (flags >> 3 & 2) | (flags & 1);
+}
+
+void Sms_apu_set_output( struct Sms_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
+{
+#if defined(ROCKBOX)
+	(void) left;
+	(void) right;
+#endif
+	
+	// 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 < sms_osc_count ); // fails if you pass invalid osc index
+	
+	if ( center )
+	{
+		unsigned const divisor = 16384 * 16 * 2;
+		this->min_tone_period = ((unsigned) Blip_clock_rate( center ) + divisor/2) / divisor;
+	}
+	
+	if ( !center || !left || !right )
+	{
+		left  = center;
+		right = center;
+	}
+	
+	struct Osc* o = &this->oscs [i];
+	o->outputs [0] = NULL;
+	o->outputs [1] = right;
+	o->outputs [2] = left;
+	o->outputs [3] = center;
+	o->output = o->outputs [calc_output( this, i )];
+}
+
+static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci, int width )
+{
+	unsigned galois = 0;
+	while ( --width >= 0 )
+	{
+		galois = (galois << 1) | (fibonacci & 1);
+		fibonacci >>= 1;
+	}
+	return galois;
+}
+
+void Sms_apu_reset( struct Sms_Apu* this, unsigned feedback, int noise_width )
+{
+	this->last_time = 0;
+	this->latch     = 0;
+	this->ggstereo  = 0;
+	
+	// Calculate noise feedback values
+	if ( !feedback || !noise_width )
+	{
+		feedback    = 0x0009;
+		noise_width = 16;
+	}
+	this->looped_feedback = 1 << (noise_width - 1);
+	this->noise_feedback  = fibonacci_to_galois_lfsr( feedback, noise_width );
+	
+	// Reset oscs
+	int i;
+	for ( i = sms_osc_count; --i >= 0; )
+	{
+		struct Osc* o = &this->oscs [i];
+		o->output   =  NULL;
+		o->last_amp =  0;
+		o->delay    =  0;
+		o->phase    =  0;
+		o->period   =  0;
+		o->volume   = 15; // silent
+	}
+	
+	this->oscs [noise_osc].phase = 0x8000;
+	Sms_apu_write_ggstereo( this, 0, 0xFF );
+}
+
+void Sms_apu_init( struct Sms_Apu* this )
+{
+	this->min_tone_period = 7;
+	
+	Synth_init( &this->synth );
+	
+	// Clear outputs to NULL FIRST
+	this->ggstereo = 0;
+	
+	int i;
+	for ( i = sms_osc_count; --i >= 0; )
+		Sms_apu_set_output( this, i, NULL, NULL, NULL );
+	
+	Sms_apu_volume( this, 1.0 );
+	Sms_apu_reset( this, 0, 0 );
+}
+
+static void run_until( struct Sms_Apu* this, blip_time_t end_time )
+{
+	require( end_time >= this->last_time );
+	if ( end_time <= this->last_time )
+		return;
+	
+	// Synthesize each oscillator
+	int idx;
+	for ( idx = sms_osc_count; --idx >= 0; )
+	{
+		struct Osc* osc = &this->oscs [idx];
+		int vol  = 0;
+		int amp  = 0;
+		
+		// Determine what will be generated
+		struct Blip_Buffer* const out = osc->output;
+		if ( out )
+		{
+			// volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
+			static unsigned char const volumes [16] ICONST_ATTR = {
+				64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0
+			};
+			
+			vol = volumes [osc->volume];
+			amp = (osc->phase & 1) * vol;
+			
+			// Square freq above 16 kHz yields constant amplitude at half volume
+			if ( idx != noise_osc && osc->period < this->min_tone_period )
+			{
+				amp = vol >> 1;
+				vol = 0;
+			}
+			
+			// Update amplitude
+			int delta = amp - osc->last_amp;
+			if ( delta )
+			{
+				osc->last_amp = amp;
+				/* norm_synth.offset( last_time, delta, out ); */
+				Synth_offset( &this->synth, this->last_time, delta, out );
+				/* out->set_modified(); */
+				Blip_set_modified( out );
+			}
+		}
+		
+		// Generate wave
+		blip_time_t time = this->last_time + osc->delay;
+		if ( time < end_time )
+		{
+			// Calculate actual period
+			int period = osc->period;
+			if ( idx == noise_osc )
+			{
+				period = 0x20 << (period & 3);
+				if ( period == 0x100 )
+					period = this->oscs [2].period * 2;
+			}
+			period *= 0x10;
+			if ( !period )
+				period = 0x10;
+			
+			// Maintain phase when silent
+			int phase = osc->phase;
+			if ( !vol )
+			{
+				int count = (end_time - time + period - 1) / period;
+				time += count * period;
+				if ( idx != noise_osc ) // TODO: maintain noise LFSR phase?
+					phase ^= count & 1;
+			}
+			else
+			{
+				int delta = amp * 2 - vol;
+				
+				if ( idx != noise_osc )
+				{
+					// Square
+					do
+					{
+						delta = -delta;
+						/* norm_synth.offset( time, delta, out ); */
+						Synth_offset( &this->synth, time, delta, out );
+						time += period;
+					}
+					while ( time < end_time );
+					phase = (delta >= 0);
+				}
+				else
+				{
+					// Noise
+					unsigned const feedback = (osc->period & 4 ? this->noise_feedback : this->looped_feedback);
+					do
+					{
+						unsigned changed = phase + 1;
+						phase = ((phase & 1) * feedback) ^ (phase >> 1);
+						if ( changed & 2 ) // true if bits 0 and 1 differ
+						{
+							delta = -delta;
+							/* fast_synth.offset_inline( time, delta, out ); */
+							Synth_offset_inline( &this->synth, time, delta, out );
+						}
+						time += period;
+					}
+					while ( time < end_time );
+					check( phase );
+				}
+				osc->last_amp = (phase & 1) * vol;
+				Blip_set_modified( out );
+			}
+			osc->phase = phase;
+		}
+		osc->delay = time - end_time;
+	}
+	this->last_time = end_time;
+}
+
+void Sms_apu_write_ggstereo( struct Sms_Apu* this, blip_time_t time, int data )
+{
+	require( (unsigned) data <= 0xFF );
+	
+	run_until( this, time );
+	this->ggstereo = data;
+	
+	int i;
+	for ( i = sms_osc_count; --i >= 0; )
+	{
+		struct Osc* osc = &this->oscs [i];
+		
+		struct Blip_Buffer* old = osc->output;
+		osc->output = osc->outputs [calc_output( this, i )];
+		if ( osc->output != old )
+		{
+			int delta = -osc->last_amp;
+			if ( delta )
+			{
+				osc->last_amp = 0;
+				if ( old )
+				{
+					Blip_set_modified( old );
+					Synth_offset( &this->synth, this->last_time, delta, old );
+				}
+			}
+		}
+	}
+}
+
+void Sms_apu_write_data( struct Sms_Apu* this, blip_time_t time, int data )
+{
+	require( (unsigned) data <= 0xFF );
+	
+	run_until( this, time );
+	
+	if ( data & 0x80 )
+		this->latch = data;
+	
+	// We want the raw values written so our save state format can be
+	// as close to hardware as possible and unspecific to any emulator.
+	int idx = this->latch >> 5 & 3;
+	struct Osc* osc = &this->oscs [idx];
+	if ( this->latch & 0x10 )
+	{
+		osc->volume = data & 0x0F;
+	}
+	else
+	{
+		if ( idx == noise_osc )
+			osc->phase = 0x8000; // reset noise LFSR
+		
+		// Replace high 6 bits/low 4 bits of register with data
+		int lo = osc->period;
+		int hi = data << 4;
+		if ( idx == noise_osc || (data & 0x80) )
+		{
+			hi = lo;
+			lo = data;
+		}
+		osc->period = (hi & 0x3F0) | (lo & 0x00F);
+	}
+}
+
+void Sms_apu_end_frame( struct Sms_Apu* this, blip_time_t end_time )
+{
+	if ( end_time > this->last_time )
+		run_until( this, end_time );
+	
+	this->last_time -= end_time;
+	assert( this->last_time >= 0 );
+}
diff --git a/apps/codecs/libgme/sms_apu.h b/apps/codecs/libgme/sms_apu.h
new file mode 100644
index 0000000..25f4e74
--- /dev/null
+++ b/apps/codecs/libgme/sms_apu.h
@@ -0,0 +1,63 @@
+// Sega Master System SN76489 PSG sound chip emulator
+
+// Sms_Snd_Emu 0.1.2
+#ifndef SMS_APU_H
+#define SMS_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+// 0: Square 1, 1: Square 2, 2: Square 3, 3: Noise
+enum { sms_osc_count = 4 }; // 0 <= chan < osc_count
+	
+struct Osc
+{
+	struct Blip_Buffer* outputs [4]; // NULL, right, left, center
+	struct Blip_Buffer* output;
+	int          last_amp;
+	
+	int         volume;
+	int         period;
+	int         delay;
+	unsigned    phase;
+};
+
+struct Sms_Apu {
+	struct Osc     oscs [sms_osc_count];
+	int     ggstereo;
+	int     latch;
+	
+	blip_time_t last_time;
+	int         min_tone_period;
+	unsigned    noise_feedback;
+	unsigned    looped_feedback;
+	struct Blip_Synth synth;
+};
+
+// Basics
+
+void Sms_apu_init( struct Sms_Apu* this );
+
+// Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0,
+// output is mono.
+void Sms_apu_set_output( struct Sms_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right);
+
+// Emulates to time t, then writes data to Game Gear left/right assignment byte
+void Sms_apu_write_ggstereo( struct Sms_Apu* this, blip_time_t t, int data ) ICODE_ATTR;
+	
+// Emulates to time t, then writes data
+void Sms_apu_write_data( struct Sms_Apu* this, blip_time_t t, 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 Sms_apu_end_frame( struct Sms_Apu* this, blip_time_t t ) ICODE_ATTR;
+
+// More features
+
+// Resets sound chip and sets noise feedback bits and width
+void Sms_apu_reset( struct Sms_Apu* this, unsigned noise_feedback, int noise_width );
+
+// Sets overall volume, where 1.0 is normal
+void Sms_apu_volume( struct Sms_Apu* this, double vol );
+
+#endif
diff --git a/apps/codecs/libgme/sms_fm_apu.c b/apps/codecs/libgme/sms_fm_apu.c
new file mode 100644
index 0000000..b15fc56
--- /dev/null
+++ b/apps/codecs/libgme/sms_fm_apu.c
@@ -0,0 +1,82 @@
+#include "sms_fm_apu.h"

+

+#include "blargg_source.h"

+

+void Fm_apu_create( struct Sms_Fm_Apu* this )

+{ 

+	Synth_init( &this->synth );

+	Ym2413_init( &this->apu );

+}

+

+blargg_err_t Fm_apu_init( struct Sms_Fm_Apu* this, double clock_rate, double sample_rate )

+{

+	this->period_ = (blip_time_t) (clock_rate / sample_rate + 0.5);

+	CHECK_ALLOC( !Ym2413_set_rate( &this->apu, sample_rate, clock_rate ) );

+	

+	Fm_apu_set_output( this, 0 );

+	Fm_apu_volume( this, 1.0 );

+	Fm_apu_reset( this );

+	return 0;

+}

+

+void Fm_apu_reset( struct Sms_Fm_Apu* this )

+{

+	this->addr      = 0;

+	this->next_time = 0;

+	this->last_amp  = 0;

+	

+	Ym2413_reset( &this->apu );

+}

+

+void fm_run_until( struct Sms_Fm_Apu* this, blip_time_t end_time ) ICODE_ATTR;

+void Fm_apu_write_data( struct Sms_Fm_Apu* this, blip_time_t time, int data )

+{

+	if ( time > this->next_time )

+		fm_run_until( this, time );

+	

+	Ym2413_write( &this->apu, this->addr, data );

+}

+

+void fm_run_until( struct Sms_Fm_Apu* this, blip_time_t end_time )

+{

+	assert( end_time > this->next_time );

+	

+	struct Blip_Buffer* const output = this->output_;

+	if ( !output )

+	{

+		this->next_time = end_time;

+		return;

+	}

+	

+	blip_time_t time = this->next_time;

+	struct Ym2413_Emu* emu = &this->apu;

+	do

+	{

+		short samples [2];

+		Ym2413_run( emu, 1, samples );

+		int amp = (samples [0] + samples [1]) >> 1;

+		

+		int delta = amp - this->last_amp;

+		if ( delta )

+		{

+			this->last_amp = amp;

+			Synth_offset_inline( &this->synth, time, delta, output );

+		}

+		time += this->period_;

+	}

+	while ( time < end_time );

+	

+	this->next_time = time;

+}

+

+void Fm_apu_end_frame( struct Sms_Fm_Apu* this, blip_time_t time )

+{

+	if ( time > this->next_time )

+		fm_run_until( this, time );

+	

+	this->next_time -= time;

+	assert( this->next_time >= 0 );

+	

+	if ( this->output_ )

+		Blip_set_modified( this->output_ );

+}

diff --git a/apps/codecs/libgme/sms_fm_apu.h b/apps/codecs/libgme/sms_fm_apu.h
new file mode 100644
index 0000000..95e1f95
--- /dev/null
+++ b/apps/codecs/libgme/sms_fm_apu.h
@@ -0,0 +1,43 @@
+#ifndef SMS_FM_APU_H

+#define SMS_FM_APU_H

+

+#include "blargg_common.h"

+#include "blip_buffer.h"

+#include "ym2413_emu.h"

+

+enum { fm_apu_osc_count = 1 };

+

+struct Sms_Fm_Apu {

+	struct Blip_Buffer* output_;

+	blip_time_t next_time;

+	int last_amp;

+	int addr;

+	

+	int clock_;

+	int rate_;

+	blip_time_t period_;

+	

+	struct Blip_Synth synth;

+	struct Ym2413_Emu apu;

+};

+

+void Fm_apu_create( struct Sms_Fm_Apu* this );

+

+static inline bool Fm_apu_supported( void ) { return Ym2413_supported(); }

+blargg_err_t Fm_apu_init( struct Sms_Fm_Apu* this, double clock_rate, double sample_rate );

+	

+static inline void Fm_apu_set_output( struct Sms_Fm_Apu* this, struct Blip_Buffer* b )

+{

+	this->output_ = b;

+}

+

+static inline void Fm_apu_volume( struct Sms_Fm_Apu* this, double v ) { Synth_volume( &this->synth, 0.4 / 4096 * v ); }

+	

+void Fm_apu_reset( struct Sms_Fm_Apu* this );

+	

+static inline void Fm_apu_write_addr( struct Sms_Fm_Apu* this, int data )                 { this->addr = data; }

+void Fm_apu_write_data( struct Sms_Fm_Apu* this, blip_time_t, int data ) ICODE_ATTR;

+	

+void Fm_apu_end_frame( struct Sms_Fm_Apu* this, blip_time_t t ) ICODE_ATTR;

+

+#endif

diff --git a/apps/codecs/libgme/vgm_emu.c b/apps/codecs/libgme/vgm_emu.c
new file mode 100644
index 0000000..7fed4ef
--- /dev/null
+++ b/apps/codecs/libgme/vgm_emu.c
@@ -0,0 +1,1053 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+
+#include "vgm_emu.h"
+
+#include "blargg_endian.h"
+#include <string.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 */
+
+#include "blargg_source.h"
+
+const char* const gme_wrong_file_type = "Wrong file type for this emulator";
+
+double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
+double const rolloff = 0.990;
+double const oversample_factor = 1.5;
+
+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)
+
+// VGM commands (Spec v1.50)
+enum {
+	cmd_gg_stereo       = 0x4F,
+	cmd_psg             = 0x50,
+	cmd_ym2413          = 0x51,
+	cmd_ym2612_port0    = 0x52,
+	cmd_ym2612_port1    = 0x53,
+	cmd_ym2151          = 0x54,
+	cmd_delay           = 0x61,
+	cmd_delay_735       = 0x62,
+	cmd_delay_882       = 0x63,
+	cmd_byte_delay      = 0x64,
+	cmd_end             = 0x66,
+	cmd_data_block      = 0x67,
+	cmd_short_delay     = 0x70,
+	cmd_pcm_delay       = 0x80,
+	cmd_pcm_seek        = 0xE0,
+	
+	pcm_block_type      = 0x00,
+	ym2612_dac_port     = 0x2A,
+	ym2612_dac_pan_port = 0xB6
+};
+
+void clear_track_vars( struct Vgm_Emu* this )
+{
+	this->out_time         = 0;
+	this->emu_time         = 0;
+	this->emu_track_ended_ = true;
+	this->track_ended     = true;
+	this->fade_start       = INT_MAX / 2 + 1;
+	this->fade_step        = 1;
+	this->silence_time     = 0;
+	this->silence_count    = 0;
+	this->buf_remain       = 0;
+}
+
+int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, sample_t* buf );
+static int play_frame_( void* data, blip_time_t blip_time, int sample_count, short int* buf )
+{
+	return play_frame( (struct Vgm_Emu*) data, blip_time, sample_count, buf );
+} 
+
+void Vgm_init( struct Vgm_Emu* this )
+{
+	this->sample_rate = 0;
+	this->mute_mask_ = 0;
+	this->tempo = 1.0;
+	
+	// defaults
+	this->max_initial_silence = 2;
+	this->silence_lookahead = 1; // tracks should already be trimmed
+	this->ignore_silence     = false;
+
+	// Disable oversampling by default
+	this->disable_oversampling = true;
+	this->psg_rate   = 0;
+	
+	Sms_apu_init( &this->psg );
+	Synth_init( &this->pcm );
+	
+	Buffer_init( &this->buf );
+	Buffer_init( &this->stereo_buf );
+	this->blip_buf = &this->stereo_buf.bufs [0];
+
+	// Init fm chips	
+	Ym2413_init( &this->ym2413 );
+	Ym2612_init( &this->ym2612 );
+	
+	// Init resampler
+	Resampler_init( &this->resampler );
+	Resampler_set_callback( &this->resampler, play_frame_, this );
+
+	// Set sound gain, a value too high
+	// will cause saturation
+	Sound_set_gain(this, 1.0);
+	
+	// Unload
+	this->voice_count = 0;
+	clear_track_vars( this );	
+}
+
+// Track info
+
+static byte const* skip_gd3_str( byte const* in, byte const* end )
+{
+	while ( end - in >= 2 )
+	{
+		in += 2;
+		if ( !(in [-2] | in [-1]) )
+			break;
+	}
+	return in;
+}
+
+static byte const* get_gd3_str( byte const* in, byte const* end, char* field )
+{
+	byte const* mid = skip_gd3_str( in, end );
+	int i, len = (mid - in) / 2 - 1;
+	if ( field && len > 0 )
+	{
+		len = min( len, (int) gme_max_field );
+		field [len] = 0;
+		for ( i = 0; i < len; i++ )
+			field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8
+	}
+	return mid;
+}
+
+static byte const* get_gd3_pair( byte const* in, byte const* end, char* field )
+{
+	return skip_gd3_str( get_gd3_str( in, end, field ), end );
+}
+
+static void parse_gd3( byte const* in, byte const* end, struct track_info_t* out )
+{
+	in = get_gd3_pair( in, end, out->song );
+	in = get_gd3_pair( in, end, out->game );
+	in = get_gd3_pair( in, end, NULL ); // Skip system
+	in = get_gd3_pair( in, end, out->author );
+}
+
+int const gd3_header_size = 12;
+
+static long check_gd3_header( byte const* h, long remain )
+{
+	if ( remain < gd3_header_size ) return 0;
+	if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
+	if ( get_le32( h + 4 ) >= 0x200 ) return 0;
+	
+	long gd3_size = get_le32( h + 8 );
+	if ( gd3_size > remain - gd3_header_size )
+		gd3_size = remain - gd3_header_size;
+	return gd3_size;
+}
+
+byte const* gd3_data( struct Vgm_Emu* this, int* size )
+{
+	if ( size )
+		*size = 0;
+	
+	long gd3_offset = get_le32( header( this )->gd3_offset ) - 0x2C;
+	if ( gd3_offset < 0 )
+		return 0;
+	
+	byte const* gd3 = this->file_begin + header_size + gd3_offset;
+	long gd3_size = check_gd3_header( gd3, this->file_end - gd3 );
+	if ( !gd3_size )
+		return 0;
+	
+	if ( size )
+		*size = gd3_size + gd3_header_size;
+	
+	return gd3;
+}
+
+static void get_vgm_length( struct header_t const* h, struct track_info_t* out )
+{
+	long length = get_le32( h->track_duration ) * 10 / 441;
+	if ( length > 0 )
+	{
+		long loop = get_le32( h->loop_duration );
+		if ( loop > 0 && get_le32( h->loop_offset ) )
+		{
+			out->loop_length = loop * 10 / 441;
+			out->intro_length = length - out->loop_length;
+		}
+		else
+		{
+			out->length = length; // 1000 / 44100 (VGM files used 44100 as timebase)
+			out->intro_length = length; // make it clear that track is no longer than length
+			out->loop_length = 0;
+		}
+	}
+}
+
+blargg_err_t track_info( struct Vgm_Emu* this, struct track_info_t* out )
+{
+	memset(out, 0, sizeof out);
+	get_vgm_length( header( this ), out );
+	
+	int size;
+	byte const* gd3 = gd3_data( this, &size );
+	if ( gd3 )
+		parse_gd3( gd3 + gd3_header_size, gd3 + size, out );
+	
+	return 0;
+}
+
+static blargg_err_t check_vgm_header( struct header_t* h )
+{
+	if ( memcmp( h->tag, "Vgm ", 4 ) )
+		return gme_wrong_file_type;
+	return 0;
+}
+
+void set_voice( struct Vgm_Emu* this, int i, struct Blip_Buffer* c, struct Blip_Buffer* l, struct Blip_Buffer* r )
+{
+	if ( i < sms_osc_count ) {
+		int j;
+		for ( j = sms_osc_count; --j >= 0; )
+			Sms_apu_set_output( &this->psg, j, c, l, r );
+	}
+}
+
+blargg_err_t setup_fm( struct Vgm_Emu* this );
+blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_size, bool parse_info )
+{
+	// Unload
+	this->voice_count = 0;
+	clear_track_vars( this );
+	
+	// Clear info
+	memset( &this->info, 0, sizeof this->info );
+
+	assert( offsetof (struct header_t,unused2 [8]) == header_size );
+	
+	if ( new_size <= header_size )
+		return gme_wrong_file_type;
+			
+	// Reset data pointers
+	this->file_begin = new_data;
+	this->file_end = new_data + new_size;
+	
+	struct header_t* h = (struct header_t*) new_data;
+	RETURN_ERR( check_vgm_header( h ) );
+	check( get_le32( h.version ) <= 0x150 );
+	
+	// If this was VGZ file gd3 parse info
+	if ( parse_info ) {
+ 		track_info( this, &this->info );
+		
+		// If file was trimmed add an
+		// incomplete token to the game tag
+		if ( get_le32( h->data_size ) > (unsigned) new_size ) {
+			*((char *) this->file_end) = cmd_end;
+			strcat(this->info.game, "(Trimmed VGZ file)" );
+		}
+	}
+	
+	// Get loop
+	this->loop_begin = this->file_end;
+	
+	// If file was trimmed don't loop
+	if ( get_le32( h->loop_offset ) && get_le32( h->data_size ) <= (unsigned) new_size ) 
+		this->loop_begin = &new_data [get_le32( h->loop_offset ) + offsetof (struct header_t,loop_offset)];
+
+	// PSG rate
+	this->psg_rate = get_le32( h->psg_rate );
+	if ( !this->psg_rate )
+		this->psg_rate = 3579545;
+	
+	Buffer_clock_rate( &this->stereo_buf, this->psg_rate );
+	
+	// Disable FM
+	this->fm_rate = 0;
+	Ym2612_enable( &this->ym2612, false );
+	Ym2413_enable( &this->ym2413, false );
+	
+	Sound_set_tempo( this, 1 );
+	
+	this->voice_count = sms_osc_count;
+	
+	RETURN_ERR( setup_fm( this ) );
+	
+	// do after FM in case output buffer is changed
+	// setup buffer
+	this->clock_rate_ = this->psg_rate;
+	Buffer_clock_rate( &this->buf, this->psg_rate );
+	
+	// Setup bass
+	this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
+	
+	// Post load
+	Sound_set_tempo( this, this->tempo );
+	Sound_mute_voices( this, this->mute_mask_ );
+	
+	return 0;
+}
+
+void update_fm_rates( struct Vgm_Emu* this, int* ym2413_rate, int* ym2612_rate );
+blargg_err_t init_fm( struct Vgm_Emu* this, double* rate )
+{
+	int ym2612_rate = get_le32( header( this )->ym2612_rate );
+	int ym2413_rate = get_le32( header( this )->ym2413_rate );
+	if ( ym2413_rate && get_le32( header( this )->version ) < 0x110 )
+		update_fm_rates( this, &ym2413_rate, &ym2612_rate );
+	
+	if ( ym2612_rate )
+	{
+		if ( !*rate )
+			*rate = ym2612_rate / 144.0;
+		RETURN_ERR( Ym2612_set_rate( &this->ym2612, *rate, ym2612_rate ) );
+		Ym2612_enable( &this->ym2612, true );
+	}
+	else if ( ym2413_rate )
+	{
+		if ( !*rate )
+			*rate = ym2413_rate / 72.0;
+		int result = Ym2413_set_rate( &this->ym2413, *rate, ym2413_rate );
+		if ( result == 2 )
+			return "YM2413 FM sound not supported";
+		CHECK_ALLOC( !result );
+		Ym2413_enable( &this->ym2413, true );
+	}
+	
+	this->fm_rate = *rate;
+	
+	return 0;
+}
+
+blargg_err_t setup_fm( struct Vgm_Emu* this )
+{
+	double fm_rate = 0.0;
+	if ( !this->disable_oversampling )
+		this->fm_rate = this->sample_rate * oversample_factor;
+	RETURN_ERR( init_fm( this, &fm_rate ) );
+	
+	if ( uses_fm( this ) )
+	{
+		this->voice_count = 8;
+		RETURN_ERR( Resampler_setup( &this->resampler, fm_rate / this->sample_rate, rolloff, fm_gain * this->gain ) );
+		RETURN_ERR( Resampler_reset( &this->resampler, Buffer_length( &this->stereo_buf ) * this->sample_rate / 1000 ) );
+		Sms_apu_volume( &this->psg, 0.195 * fm_gain * this->gain );
+	}
+	else
+	{
+		Sms_apu_volume( &this->psg, this->gain );
+	}
+	
+	return 0;
+}
+
+// Emulation
+
+blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time );
+blargg_err_t run_clocks( struct Vgm_Emu* this, blip_time_t* time_io, int msec )
+{
+	*time_io = run( this, msec * this->vgm_rate / 1000 );
+	Sms_apu_end_frame( &this->psg, *time_io );
+	return 0;
+}
+
+
+
+blargg_err_t play_( struct Vgm_Emu* this, long count, sample_t* out )
+{
+	if ( !uses_fm( this ) ) {
+		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 - 100;
+				RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
+				assert( clocks_emulated );
+				Buffer_end_frame( &this->buf, clocks_emulated );
+			}
+		}
+		
+		return 0;
+	}
+		
+	Resampler_play( &this->resampler, count, out, &this->stereo_buf );
+	return 0;
+}
+
+// Vgm_Emu_impl
+
+inline int command_len( int command )
+{
+	static byte const lens [0x10] ICONST_ATTR = {
+	// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+	   1,1,1,2,2,3,1,1,1,1,3,3,4,4,5,5
+	};
+	int len = lens [command >> 4];
+	check( len != 1 );
+	return len;
+}
+	
+inline fm_time_t to_fm_time( struct Vgm_Emu* this, vgm_time_t t )
+{
+	return (t * this->fm_time_factor + this->fm_time_offset) >> fm_time_bits;
+}
+
+inline blip_time_t to_psg_time( struct Vgm_Emu* this, vgm_time_t t )
+{
+	return (t * this->blip_time_factor) >> blip_time_bits;
+}
+
+void write_pcm( struct Vgm_Emu* this, vgm_time_t vgm_time, int amp )
+{
+	if ( this->blip_buf )
+	{
+		check( amp >= 0 );
+		blip_time_t blip_time = to_psg_time( this, vgm_time );
+		int old = this->dac_amp;
+		int delta = amp - old;
+		this->dac_amp = amp;
+		Blip_set_modified( this->blip_buf );
+		if ( old >= 0 ) // first write is ignored, to avoid click
+			Synth_offset_inline( &this->pcm, blip_time, delta, this->blip_buf );
+		else
+			this->dac_amp |= this->dac_disabled;
+	}
+}
+
+blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
+{
+	vgm_time_t vgm_time = this->vgm_time; 
+	byte const* pos = this->pos;
+	if ( pos >= this->file_end )
+	{
+		this->emu_track_ended_ = true;
+		/* if ( pos > data_end )
+			warning( "Stream lacked end event" ); */
+	}
+	
+	while ( vgm_time < end_time && pos < this->file_end )
+	{
+		// TODO: be sure there are enough bytes left in stream for particular command
+		// so we don't read past end
+		switch ( *pos++ )
+		{
+		case cmd_end:
+			pos = this->loop_begin; // if not looped, loop_begin == data_end
+			break;
+		
+		case cmd_delay_735:
+			vgm_time += 735;
+			break;
+		
+		case cmd_delay_882:
+			vgm_time += 882;
+			break;
+		
+		case cmd_gg_stereo:
+			Sms_apu_write_ggstereo( &this->psg, to_psg_time( this, vgm_time ), *pos++ );
+			break;
+		
+		case cmd_psg:
+			Sms_apu_write_data( &this->psg, to_psg_time( this, vgm_time ), *pos++ );
+			break;
+		
+		case cmd_delay:
+			vgm_time += pos [1] * 0x100 + pos [0];
+			pos += 2;
+			break;
+		
+		case cmd_byte_delay:
+			vgm_time += *pos++;
+			break;
+		
+		case cmd_ym2413:
+			if ( Ym2413_run_until( &this->ym2413, to_fm_time( this, vgm_time ) ) )
+				Ym2413_write( &this->ym2413, pos [0], pos [1] );
+			pos += 2;
+			break;
+		
+		case cmd_ym2612_port0:
+			if ( pos [0] == ym2612_dac_port )
+			{
+				write_pcm( this, vgm_time, pos [1] );
+			}
+			else if ( Ym2612_run_until( &this->ym2612, to_fm_time( this, vgm_time ) ) )
+			{
+				if ( pos [0] == 0x2B )
+				{
+					this->dac_disabled = (pos [1] >> 7 & 1) - 1;
+					this->dac_amp |= this->dac_disabled;
+				}
+				Ym2612_write0( &this->ym2612, pos [0], pos [1] );
+			}
+			pos += 2;
+			break;
+		
+		case cmd_ym2612_port1:
+			if ( Ym2612_run_until( &this->ym2612, to_fm_time( this, vgm_time ) ) )
+			{
+				if ( pos [0] == ym2612_dac_pan_port )
+				{
+					struct Blip_Buffer* blip_buf = NULL;
+					switch ( pos [1] >> 6 )
+					{
+					case 0: blip_buf = NULL; break;
+					case 1: blip_buf = &this->stereo_buf.bufs [2]; break;
+					case 2: blip_buf = &this->stereo_buf.bufs [1]; break;
+					case 3: blip_buf = &this->stereo_buf.bufs [0]; break;
+					}
+					this->blip_buf = blip_buf;
+				}
+			
+				Ym2612_write1( &this->ym2612, pos [0], pos [1] );
+			}
+			pos += 2;
+			break;
+			
+		case cmd_data_block: {
+			check( *pos == cmd_end );
+			int type = pos [1];
+			long size = get_le32( pos + 2 );
+			pos += 6;
+			if ( type == pcm_block_type )
+				this->pcm_data = pos;
+			pos += size;
+			break;
+		}
+		
+		case cmd_pcm_seek:
+			this->pcm_pos = this->pcm_data + pos [3] * 0x1000000 + pos [2] * 0x10000 +
+					pos [1] * 0x100 + pos [0];
+			pos += 4;
+			break;
+		
+		default: {
+				int cmd = pos [-1];
+				switch ( cmd & 0xF0 )
+				{
+					case cmd_pcm_delay:
+						write_pcm( this, vgm_time, *this->pcm_pos++ );
+						vgm_time += cmd & 0x0F;
+						break;
+					
+					case cmd_short_delay:
+						vgm_time += (cmd & 0x0F) + 1;
+						break;
+					
+					case 0x50:
+						pos += 2;
+						break;
+					
+					default:
+						pos += command_len( cmd ) - 1;
+						/* warning( "Unknown stream event" ); */
+				}
+			}
+		}
+	}
+	vgm_time -= end_time;
+	this->pos = pos;
+	this->vgm_time = vgm_time;
+	
+	return to_psg_time( this, end_time );
+}
+
+int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, blip_sample_t out [] )
+{
+	// to do: timing is working mostly by luck
+	int min_pairs = (unsigned) sample_count / 2;
+	int vgm_time = (min_pairs << fm_time_bits) / this->fm_time_factor - 1;
+	assert( to_fm_time( this, vgm_time ) <= min_pairs );
+	int pairs;
+	while ( (pairs = to_fm_time( this, vgm_time )) < min_pairs )
+		vgm_time++;
+	//debug_printf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs );
+	
+	if ( Ym2612_enabled( &this->ym2612 ) )
+	{
+		Ym2612_begin_frame( &this->ym2612, out );
+		memset( out, 0, pairs * stereo * sizeof *out );
+	}
+	else if ( Ym2413_enabled( &this->ym2413 ) )
+	{
+		Ym2413_begin_frame( &this->ym2413, out );
+	}
+	
+	run( this, vgm_time );
+	Ym2612_run_until( &this->ym2612, pairs );
+	Ym2413_run_until( &this->ym2413, pairs );
+	
+	this->fm_time_offset = (vgm_time * this->fm_time_factor + this->fm_time_offset) - (pairs << fm_time_bits);
+	
+	Sms_apu_end_frame( &this->psg, blip_time );
+	
+	return pairs * stereo;
+}
+
+// Update pre-1.10 header FM rates by scanning commands
+void update_fm_rates( struct Vgm_Emu* this, int* ym2413_rate, int* ym2612_rate )
+{
+	byte const* p = this->file_begin + 0x40;
+	while ( p < this->file_end )
+	{
+		switch ( *p )
+		{
+		case cmd_end:
+			return;
+		
+		case cmd_psg:
+		case cmd_byte_delay:
+			p += 2;
+			break;
+		
+		case cmd_delay:
+			p += 3;
+			break;
+		
+		case cmd_data_block:
+			p += 7 + get_le32( p + 3 );
+			break;
+		
+		case cmd_ym2413:
+			*ym2612_rate = 0;
+			return;
+		
+		case cmd_ym2612_port0:
+		case cmd_ym2612_port1:
+			*ym2612_rate = *ym2413_rate;
+			*ym2413_rate = 0;
+			return;
+		
+		case cmd_ym2151:
+			*ym2413_rate = 0;
+			*ym2612_rate = 0;
+			return;
+		
+		default:
+			p += command_len( *p );
+		}
+	}
+}
+
+
+// Music Emu
+
+blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, long rate )
+{
+	require( !this->sample_rate ); // sample rate can't be changed once set
+	RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 30 ) );
+	RETURN_ERR( Buffer_set_sample_rate( &this->buf, rate, 1000 / 20 ) );
+	
+	// Set bass frequency
+	Buffer_bass_freq( &this->buf, 80 );
+	
+	this->sample_rate = rate;
+	return 0;
+}
+
+void Sound_mute_voice( struct Vgm_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 Vgm_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) )
+		{
+			set_voice( this, 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
+			set_voice( this, i, ch.center, ch.left, ch.right );
+		}
+	}
+	
+	// TODO: what was this for?
+	//core.pcm.output( &core.blip_buf );
+	
+	// TODO: silence PCM if FM isn't used?
+	if ( uses_fm( this ) )
+	{
+		for ( i = sms_osc_count; --i >= 0; )
+			Sms_apu_set_output( &this->psg, i, ( mask & 0x80 ) ? 0 : &this->stereo_buf.bufs [0], NULL, NULL );
+		if ( Ym2612_enabled( &this->ym2612 ) )
+		{
+			Synth_volume( &this->pcm, (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * this->gain );
+			Ym2612_mute_voices( &this->ym2612, mask );
+		}
+		
+		if ( Ym2413_enabled( &this->ym2413 ) )
+		{
+			int m = mask & 0x3F;
+			if ( mask & 0x20 )
+				m |= 0x01E0; // channels 5-8
+			if ( mask & 0x40 )
+				m |= 0x3E00;
+			Ym2413_mute_voices( &this->ym2413, m );
+		}
+	}
+}
+
+void Sound_set_tempo( struct Vgm_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;
+	
+	if ( this->file_begin )
+	{
+		this->vgm_rate = (long) (44100 * t + 0.5);
+		this->blip_time_factor = (int) ((double)
+				(1 << blip_time_bits) / this->vgm_rate * Blip_clock_rate( &this->stereo_buf.bufs [0] ) + 0.5);
+		//debug_printf( "blip_time_factor: %ld\n", blip_time_factor );
+		//debug_printf( "vgm_rate: %ld\n", vgm_rate );
+		// TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only)
+		//blip_time_factor = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 );
+		//vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 );
+		
+		this->fm_time_factor = 2 + (int) (this->fm_rate * (1 << fm_time_bits) / this->vgm_rate + 0.5);
+	}
+}
+
+void fill_buf( struct Vgm_Emu *this );
+blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
+{
+	clear_track_vars( this );
+	
+	Sms_apu_reset( &this->psg, get_le16( header( this )->noise_feedback ), header( this )->noise_width );
+	
+	this->blip_buf = &this->stereo_buf.bufs [0];
+	
+	this->dac_disabled = -1;
+	this->pos          = this->file_begin + header_size;
+	this->pcm_data     = this->pos;
+	this->pcm_pos      = this->pos;
+	this->dac_amp      = -1;
+	this->vgm_time     = 0;
+	if ( get_le32( header( this )->version ) >= 0x150 )
+	{
+		long data_offset = get_le32( header( this )->data_offset );
+		check( data_offset );
+		if ( data_offset )
+			this->pos += data_offset + offsetof (struct header_t,data_offset) - 0x40;
+	}
+	
+	if ( uses_fm( this ) )
+	{
+		if ( Ym2413_enabled( &this->ym2413 ) )
+			Ym2413_reset( &this->ym2413 );
+		
+		if ( Ym2612_enabled( &this->ym2612 ) )
+			Ym2612_reset( &this->ym2612 );
+		
+		Buffer_clear( &this->stereo_buf );
+		Resampler_clear( &this->resampler );
+	}
+	
+	this->fm_time_offset = 0;
+	
+	Buffer_clear( &this->buf );
+	
+	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;
+}
+
+// Tell/Seek
+
+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 Vgm_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 Vgm_Emu* this, long msec )
+{
+	blargg_long time = msec_to_samples( msec, this->sample_rate );
+	if ( time < this->out_time )
+		RETURN_ERR( Vgm_start_track( this ) );
+	return Track_skip( this, time - this->out_time );
+}
+
+blargg_err_t skip_( struct Vgm_Emu* this, long count );
+blargg_err_t Track_skip( struct Vgm_Emu* this, long count )
+{
+	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;
+		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;
+}
+
+blargg_err_t skip_( struct Vgm_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;
+}
+
+// Fading
+
+void Track_set_fade( struct Vgm_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 Vgm_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 Vgm_Emu* this, long count, sample_t* out )
+{
+	this->emu_time += count;
+	if ( !this->emu_track_ended_ ) {
+		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 Vgm_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 Vgm_play( struct Vgm_Emu* this, long out_count, sample_t* out )
+{
+	if ( this->track_ended )
+	{
+		memset( out, 0, out_count * sizeof *out );
+	}
+	else
+	{
+		require( out_count % stereo == 0 );
+		
+		assert( this->emu_time >= this->out_time );
+		
+		// prints nifty graph of how far ahead we are when searching for silence
+		//debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
+		
+		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;
+}
diff --git a/apps/codecs/libgme/vgm_emu.h b/apps/codecs/libgme/vgm_emu.h
new file mode 100644
index 0000000..deb64bc
--- /dev/null
+++ b/apps/codecs/libgme/vgm_emu.h
@@ -0,0 +1,211 @@
+// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator
+
+// Game_Music_Emu 0.5.5
+#ifndef VGM_EMU_H
+#define VGM_EMU_H
+
+#include "blargg_common.h"
+#include "blargg_source.h"
+#include "resampler.h"
+#include "multi_buffer.h"
+#include "ym2413_emu.h"
+#include "ym2612_emu.h"
+#include "sms_apu.h"
+
+typedef short sample_t;
+typedef int vgm_time_t;
+typedef int fm_time_t;
+
+enum { fm_time_bits = 12 };
+enum { blip_time_bits = 12 };
+enum { buf_size = 2048 };
+
+// VGM header format
+enum { header_size = 0x40 };
+struct header_t
+{
+	char tag [4];
+	byte data_size [4];
+	byte version [4];
+	byte psg_rate [4];
+	byte ym2413_rate [4];
+	byte gd3_offset [4];
+	byte track_duration [4];
+	byte loop_offset [4];
+	byte loop_duration [4];
+	byte frame_rate [4];
+	byte noise_feedback [2];
+	byte noise_width;
+	byte unused1;
+	byte ym2612_rate [4];
+	byte ym2151_rate [4];
+	byte data_offset [4];
+	byte unused2 [8];
+};
+
+enum { gme_max_field = 63 };
+struct track_info_t
+{
+	/* times in milliseconds; -1 if unknown */
+	long length;
+	long intro_length;
+	long loop_length;
+	
+	/* empty string if not available */
+	char game      [64];
+	char song      [96];
+	char author    [64];
+};
+
+// Emulates VGM music using SN76489/SN76496 PSG, YM2612, and YM2413 FM sound chips.
+// Supports custom sound buffer and frequency equalization when VGM uses just the PSG.
+// FM sound chips can be run at their proper rates, or slightly higher to reduce
+// aliasing on high notes. Currently YM2413 support requires that you supply a
+// YM2413 sound chip emulator. I can provide one I've modified to work with the library.
+struct Vgm_Emu {
+	double fm_rate;
+	long psg_rate;
+	long vgm_rate;
+	bool disable_oversampling;
+	
+	long fm_time_offset;
+	int fm_time_factor;
+
+	int blip_time_factor;
+	
+	byte const* file_begin;
+	byte const* file_end;
+	
+	vgm_time_t vgm_time;
+	byte const* loop_begin;
+	byte const* pos;
+	
+	byte const* pcm_data;
+	byte const* pcm_pos;
+	int dac_amp;
+	int dac_disabled; // -1 if disabled
+
+	struct Blip_Buffer* blip_buf;
+
+	// general
+	long clock_rate_;
+	unsigned buf_changed_count;
+	int max_initial_silence;
+	int voice_count;
+	int mute_mask_;
+	double tempo;
+	double gain;
+	
+	long sample_rate;
+	
+	// track-specific
+	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
+	volatile bool track_ended;
+	
+	// fading
+	blargg_long fade_start;
+	int fade_step;
+	
+	// silence detection
+	int silence_lookahead; // speed to run emulator when looking ahead for silence
+	bool ignore_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
+	
+	// larger items at the end
+	struct track_info_t info;
+	sample_t buf_ [buf_size];
+
+	struct Ym2612_Emu ym2612;
+	struct Ym2413_Emu ym2413;
+	
+	struct Sms_Apu psg;
+	struct Blip_Synth pcm;
+	struct Stereo_Buffer stereo_buf;
+	
+	struct Resampler resampler;
+	
+	struct Stereo_Buffer buf;
+};
+
+void Vgm_init( struct Vgm_Emu* this );
+
+// Disable running FM chips at higher than normal rate. Will result in slightly
+// more aliasing of high notes.
+static inline void Vgm_disable_oversampling( struct Vgm_Emu* this, bool disable ) { this->disable_oversampling = disable; }
+
+// Header for currently loaded file
+static inline struct header_t *header( struct Vgm_Emu* this ) { return (struct header_t*) this->file_begin; }
+	
+// Basic functionality (see Gme_File.h for file loading/track info functions)
+blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_size, bool parse_info );
+
+// True if any FM chips are used by file. Always false until init_fm()
+// is called.
+static inline bool uses_fm( struct Vgm_Emu* this ) { return Ym2612_enabled( &this->ym2612 ) || Ym2413_enabled( &this->ym2413 ); }
+
+// Set output sample rate. Must be called only once before loading file.
+blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, long sample_rate );
+	
+// Start a track, where 0 is the first track. Also clears warning string.
+blargg_err_t Vgm_start_track( struct Vgm_Emu* this );
+	
+// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
+// errors set warning string, and major errors also end track.
+blargg_err_t Vgm_play( struct Vgm_Emu* this, long count, sample_t* buf ) ICODE_ATTR;
+		
+// Track status/control
+
+// Number of milliseconds (1000 msec = 1 second) played since beginning of track
+long Track_tell( struct Vgm_Emu* this );
+	
+// Seek to new time in track. Seeking backwards or far forward can take a while.
+blargg_err_t Track_seek( struct Vgm_Emu* this, long msec );
+	
+// Skip n samples
+blargg_err_t Track_skip( struct Vgm_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 Vgm_Emu* this, long start_msec, long length_msec );
+
+// Get track length in milliseconds
+static inline long Track_get_length( struct Vgm_Emu* this )
+{
+	long length = this->info.length;
+	if ( length <= 0 )
+	{
+		length = this->info.intro_length + 2 * this->info.loop_length; // intro + 2 loops
+		if ( length <= 0 )
+			length = 150 * 1000; // 2.5 minutes
+	}
+	
+	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 Vgm_Emu* this, double t );
+	
+// Mute/unmute voice i, where voice 0 is first voice
+void Sound_mute_voice( struct Vgm_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 Vgm_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 Vgm_Emu* this, double g )
+{
+	assert( !this->sample_rate ); // you must set gain before setting sample rate
+	this->gain = g;
+}
+
+
+#endif
diff --git a/apps/codecs/libgme/vrc7tone.h b/apps/codecs/libgme/vrc7tone.h
new file mode 100644
index 0000000..a256c80
--- /dev/null
+++ b/apps/codecs/libgme/vrc7tone.h
@@ -0,0 +1,20 @@
+/* VRC7 TONES by okazaki@angel.ne.jp */

+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x33,0x01,0x09,0x0e,0x94,0x90,0x40,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x13,0x41,0x0f,0x0d,0xce,0xd3,0x43,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x12,0x1b,0x06,0xff,0xd2,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x61,0x61,0x1b,0x07,0xaf,0x63,0x20,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x22,0x21,0x1e,0x06,0xf0,0x76,0x08,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x66,0x21,0x15,0x00,0x93,0x94,0x20,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x61,0x1c,0x07,0x82,0x81,0x10,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x23,0x21,0x20,0x1f,0xc0,0x71,0x07,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x25,0x31,0x26,0x05,0x64,0x41,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x17,0x21,0x28,0x07,0xff,0x83,0x02,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x97,0x81,0x25,0x07,0xcf,0xc8,0x02,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x21,0x54,0x0f,0x80,0x7f,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x01,0x56,0x03,0xd3,0xb2,0x43,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x31,0x21,0x0c,0x03,0x82,0xc0,0x40,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x21,0x01,0x0c,0x03,0xd4,0xd3,0x40,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

diff --git a/apps/codecs/libgme/ym2413_emu.c b/apps/codecs/libgme/ym2413_emu.c
new file mode 100644
index 0000000..67870f3
--- /dev/null
+++ b/apps/codecs/libgme/ym2413_emu.c
@@ -0,0 +1,45 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/

+

+#include "ym2413_emu.h"

+

+void Ym2413_init( struct Ym2413_Emu* this )

+{

+	this->last_time = disabled_time; this->out = 0; 

+}

+

+int Ym2413_set_rate( struct Ym2413_Emu* this, double sample_rate, double clock_rate )

+{	

+	OPLL_new ( &this->opll, clock_rate, sample_rate );

+    OPLL_reset_patch( &this->opll, OPLL_2413_TONE );

+    

+	Ym2413_reset( this );

+	return 0;

+}

+

+void Ym2413_reset( struct Ym2413_Emu* this )

+{

+	OPLL_reset( &this->opll );

+	OPLL_setMask( &this->opll, 0 );

+}

+

+void Ym2413_write( struct Ym2413_Emu* this, int addr, int data )

+{

+	OPLL_writeIO( &this->opll, 0, addr );

+	OPLL_writeIO( &this->opll, 1, data );

+}

+

+void Ym2413_mute_voices( struct Ym2413_Emu* this, int mask )

+{

+	OPLL_setMask( &this->opll, mask );

+}

+

+void Ym2413_run( struct Ym2413_Emu* this, int pair_count, short* out )

+{

+	while ( pair_count-- )

+	{

+		int s = OPLL_calc( &this->opll ) << 1;

+		out [0] = s;

+		out [1] = s;

+		out += 2;

+	}

+}

diff --git a/apps/codecs/libgme/ym2413_emu.h b/apps/codecs/libgme/ym2413_emu.h
new file mode 100644
index 0000000..71369e9
--- /dev/null
+++ b/apps/codecs/libgme/ym2413_emu.h
@@ -0,0 +1,61 @@
+// YM2413 FM sound chip emulator interface

+

+// Game_Music_Emu 0.6-pre

+#ifndef YM2413_EMU_H

+#define YM2413_EMU_H

+

+#include "blargg_common.h"

+#include "emu2413.h"

+

+enum { out_chan_count = 2 }; // stereo

+enum { channel_count = 14 };

+enum { disabled_time = -1 };

+

+struct Ym2413_Emu  {

+	OPLL opll;

+		

+	// Impl

+	int last_time;

+	short* out;

+};

+

+void Ym2413_init( struct Ym2413_Emu* this );

+

+static inline bool Ym2413_supported( void ) { return true; }

+	

+// Sets output sample rate and chip clock rates, in Hz. Returns non-zero

+// if error.

+int Ym2413_set_rate( struct Ym2413_Emu* this, double sample_rate, double clock_rate );

+	

+// Resets to power-up state

+void Ym2413_reset( struct Ym2413_Emu* this );

+	

+// Mutes voice n if bit n (1 << n) of mask is set

+void Ym2413_mute_voices( struct Ym2413_Emu* this, int mask );

+	

+// Writes data to addr

+void Ym2413_write( struct Ym2413_Emu* this, int addr, int data ) ICODE_ATTR;

+	

+// Runs and writes pair_count*2 samples to output

+void Ym2413_run( struct Ym2413_Emu* this, int pair_count, short* out ) ICODE_ATTR;

+

+static inline void Ym2413_enable( struct Ym2413_Emu* this, bool b ) { this->last_time = b ? 0 : disabled_time; }

+static inline bool Ym2413_enabled( struct Ym2413_Emu* this ) { return this->last_time != disabled_time; }

+static inline void Ym2413_begin_frame( struct Ym2413_Emu* this, short* buf ) { this->out = buf; this->last_time = 0; }

+	

+static inline int Ym2413_run_until( struct Ym2413_Emu* this, int time )

+{

+	int count = time - this->last_time;

+	if ( count > 0 )

+	{

+		if ( this->last_time < 0 )

+			return false;

+		this->last_time = time;

+		short* p = this->out;

+		this->out += count * out_chan_count;

+		Ym2413_run( this, count, p );

+	}

+	return true;

+}

+

+#endif

diff --git a/apps/codecs/libgme/ym2612_emu.c b/apps/codecs/libgme/ym2612_emu.c
new file mode 100644
index 0000000..a2f32d3
--- /dev/null
+++ b/apps/codecs/libgme/ym2612_emu.c
@@ -0,0 +1,1359 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+// Based on Gens 2.10 ym2612.c
+
+#include "ym2612_emu.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <math.h>
+
+/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */
+/* Copyright (C) 2004-2007 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 */
+
+// This is mostly the original source in its C style and all.
+//
+// Somewhat optimized and simplified. Uses a template to generate the many
+// variants of Update_Chan. Rewrote header file. In need of full rewrite by
+// someone more familiar with FM sound and the YM2612. Has some inaccuracies
+// compared to the Sega Genesis sound, particularly being mixed at such a
+// high sample accuracy (the Genesis sounds like it has only 8 bit samples).
+// - Shay
+
+// Ported again to c by gama.
+// Not sure if performance is better than the original c version.
+
+#if !defined(ROCKBOX)
+	#define YM2612_CALCUL_TABLES
+#else
+	#include "ymtables.h"
+#endif
+
+const int output_bits = 14;
+
+static const unsigned char DT_DEF_TAB [4 * 32] ICONST_ATTR =
+{
+// FD = 0
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+// FD = 1
+  0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+  2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+
+// FD = 2
+  1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+  5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16,
+
+// FD = 3
+  2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+  8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22
+};
+
+static const unsigned char FKEY_TAB [16] ICONST_ATTR =
+{ 
+	0, 0, 0, 0,
+	0, 0, 0, 1,
+	2, 3, 3, 3,
+	3, 3, 3, 3
+};
+
+static const unsigned char LFO_AMS_TAB [4] ICONST_ATTR =
+{
+	31, 4, 1, 0
+};
+
+static const unsigned char LFO_FMS_TAB [8] ICONST_ATTR =
+{
+	LFO_FMS_BASE * 0, LFO_FMS_BASE * 1,
+	LFO_FMS_BASE * 2, LFO_FMS_BASE * 3,
+	LFO_FMS_BASE * 4, LFO_FMS_BASE * 6,
+	LFO_FMS_BASE * 12, LFO_FMS_BASE * 24
+};
+
+int in0, in1, in2, in3;            // current phase calculation
+// int en0, en1, en2, en3;            // current enveloppe calculation
+
+inline void set_seg( struct slot_t* s, int seg )
+{
+	s->env_xor = 0;
+	s->env_max = INT_MAX;
+	s->SEG = seg;
+	if ( seg & 4 )
+	{
+		s->env_xor = ENV_MASK;
+		s->env_max = ENV_MASK;
+	}
+}
+
+inline void YM2612_Special_Update(void) { }
+
+void KEY_ON( struct channel_* ch, struct tables_t *g, int nsl )
+{
+	struct slot_t *SL = &(ch->SLOT [nsl]);  // on recupere le bon pointeur de slot
+	
+	if (SL->Ecurp == RELEASE)       // la touche est-elle rel'chee ?
+	{
+		SL->Fcnt = 0;
+
+		// Fix Ecco 2 splash sound
+		
+		SL->Ecnt = (g->DECAY_TO_ATTACK [g->ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM;
+		SL->ChgEnM = ~0;
+
+//      SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK;
+//      SL->Ecnt = 0;
+
+		SL->Einc = SL->EincA;
+		SL->Ecmp = ENV_DECAY;
+		SL->Ecurp = ATTACK;
+	}
+}
+
+
+void KEY_OFF( struct channel_* ch, struct tables_t *g, int nsl )
+{
+	struct slot_t *SL = &(ch->SLOT [nsl]);  // on recupere le bon pointeur de slot
+	
+	if (SL->Ecurp != RELEASE)       // la touche est-elle appuyee ?
+	{
+		if (SL->Ecnt < ENV_DECAY)   // attack phase ?
+		{
+			SL->Ecnt = (g->ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY;
+		}
+
+		SL->Einc = SL->EincR;
+		SL->Ecmp = ENV_END;
+		SL->Ecurp = RELEASE;
+	}
+}
+
+
+int SLOT_SET( struct Ym2612_Impl* impl, int Adr, int data )
+{
+	int nch = Adr & 3;
+	if ( nch == 3 )
+		return 1;
+	
+	struct tables_t *g = &impl->g;
+	struct state_t *YM2612 = &impl->YM2612;
+	struct channel_* ch = &YM2612->CHANNEL [nch + (Adr & 0x100 ? 3 : 0)];
+	struct slot_t* sl = &ch->SLOT [(Adr >> 2) & 3];
+
+	switch ( Adr & 0xF0 )
+	{
+		case 0x30:
+			if ( (sl->MUL = (data & 0x0F)) != 0 ) sl->MUL <<= 1;
+			else sl->MUL = 1;
+
+			sl->DT = (int*) g->DT_TAB [(data >> 4) & 7];
+
+			ch->SLOT [0].Finc = -1;
+
+			break;
+
+		case 0x40:
+			sl->TL = data & 0x7F;
+
+			// SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound...
+			YM2612_Special_Update();
+
+#if ((ENV_HBITS - 7) < 0)
+			sl->TLL = sl->TL >> (7 - ENV_HBITS);
+#else
+			sl->TLL = sl->TL << (ENV_HBITS - 7);
+#endif
+
+			break;
+
+		case 0x50:
+			sl->KSR_S = 3 - (data >> 6);
+
+			ch->SLOT [0].Finc = -1;
+
+			if (data &= 0x1F) sl->AR = (int*) &g->AR_TAB [data << 1];
+			else sl->AR = (int*) &g->NULL_RATE [0];
+
+			sl->EincA = sl->AR [sl->KSR];
+			if (sl->Ecurp == ATTACK) sl->Einc = sl->EincA;
+			break;
+
+		case 0x60:
+			if ( (sl->AMSon = (data & 0x80)) != 0 ) sl->AMS = ch->AMS;
+			else sl->AMS = 31;
+
+			if (data &= 0x1F) sl->DR = (int*) &g->DR_TAB [data << 1];
+			else sl->DR = (int*) &g->NULL_RATE [0];
+
+			sl->EincD = sl->DR [sl->KSR];
+			if (sl->Ecurp == DECAY) sl->Einc = sl->EincD;
+			break;
+
+		case 0x70:
+			if (data &= 0x1F) sl->SR = (int*) &g->DR_TAB [data << 1];
+			else sl->SR = (int*) &g->NULL_RATE [0];
+
+			sl->EincS = sl->SR [sl->KSR];
+			if ((sl->Ecurp == SUBSTAIN) && (sl->Ecnt < ENV_END)) sl->Einc = sl->EincS;
+			break;
+
+		case 0x80:
+			sl->SLL = g->SL_TAB [data >> 4];
+
+			sl->RR = (int*) &g->DR_TAB [((data & 0xF) << 2) + 2];
+
+			sl->EincR = sl->RR [sl->KSR];
+			if ((sl->Ecurp == RELEASE) && (sl->Ecnt < ENV_END)) sl->Einc = sl->EincR;
+			break;
+
+		case 0x90:
+			// SSG-EG envelope shapes :
+			/*
+			   E  At Al H
+			  
+			   1  0  0  0  \\\\
+			   1  0  0  1  \___
+			   1  0  1  0  \/\/
+			   1  0  1  1  \
+			   1  1  0  0  ////
+			   1  1  0  1  /
+			   1  1  1  0  /\/\
+			   1  1  1  1  /___
+			  
+			   E  = SSG-EG enable
+			   At = Start negate
+			   Al = Altern
+			   H  = Hold */
+
+			set_seg( sl, (data & 8) ? (data & 0x0F) : 0 );
+			break;
+	}
+
+	return 0;
+}
+
+
+int CHANNEL_SET( struct state_t* YM2612, int Adr, int data )
+{
+	int num = Adr & 3;
+	if ( num == 3 )
+		return 1;
+	
+	struct channel_* ch = &YM2612->CHANNEL [num + (Adr & 0x100 ? 3 : 0)];
+	
+	switch ( Adr & 0xFC )
+	{
+		case 0xA0:
+			YM2612_Special_Update();
+
+			ch->FNUM [0] = (ch->FNUM [0] & 0x700) + data;
+			ch->KC [0] = (ch->FOCT [0] << 2) | FKEY_TAB [ch->FNUM [0] >> 7];
+
+			ch->SLOT [0].Finc = -1;
+			break;
+
+		case 0xA4:
+			YM2612_Special_Update();
+
+			ch->FNUM [0] = (ch->FNUM [0] & 0x0FF) + ((data & 0x07) << 8);
+			ch->FOCT [0] = (data & 0x38) >> 3;
+			ch->KC [0] = (ch->FOCT [0] << 2) | FKEY_TAB [ch->FNUM [0] >> 7];
+
+			ch->SLOT [0].Finc = -1;
+			break;
+
+		case 0xA8:
+			if ( Adr < 0x100 )
+			{
+				num++;
+
+				YM2612_Special_Update();
+
+				YM2612->CHANNEL [2].FNUM [num] = (YM2612->CHANNEL [2].FNUM [num] & 0x700) + data;
+				YM2612->CHANNEL [2].KC [num] = (YM2612->CHANNEL [2].FOCT [num] << 2) |
+						FKEY_TAB [YM2612->CHANNEL [2].FNUM [num] >> 7];
+
+				YM2612->CHANNEL [2].SLOT [0].Finc = -1;
+			}
+			break;
+
+		case 0xAC:
+			if ( Adr < 0x100 )
+			{
+				num++;
+
+				YM2612_Special_Update();
+
+				YM2612->CHANNEL [2].FNUM [num] = (YM2612->CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8);
+				YM2612->CHANNEL [2].FOCT [num] = (data & 0x38) >> 3;
+				YM2612->CHANNEL [2].KC [num] = (YM2612->CHANNEL [2].FOCT [num] << 2) |
+						FKEY_TAB [YM2612->CHANNEL [2].FNUM [num] >> 7];
+
+				YM2612->CHANNEL [2].SLOT [0].Finc = -1;
+			}
+			break;
+
+		case 0xB0:
+			if ( ch->ALGO != (data & 7) )
+			{
+				// Fix VectorMan 2 heli sound (level 1)
+				YM2612_Special_Update();
+
+				ch->ALGO = data & 7;
+				
+				ch->SLOT [0].ChgEnM = 0;
+				ch->SLOT [1].ChgEnM = 0;
+				ch->SLOT [2].ChgEnM = 0;
+				ch->SLOT [3].ChgEnM = 0;
+			}
+
+			ch->FB = 9 - ((data >> 3) & 7);                              // Real thing ?
+
+//          if (ch->FB = ((data >> 3) & 7)) ch->FB = 9 - ch->FB;       // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound...
+//          else ch->FB = 31;
+			break;
+
+		case 0xB4: {
+			YM2612_Special_Update();
+			
+			ch->LEFT = 0 - ((data >> 7) & 1);
+			ch->RIGHT = 0 - ((data >> 6) & 1);
+			
+			ch->AMS = LFO_AMS_TAB [(data >> 4) & 3];
+			ch->FMS = LFO_FMS_TAB [data & 7];
+			
+			int i;
+			for ( i = 0; i < 4; i++ )
+			{
+				struct slot_t* sl = &ch->SLOT [i];
+				sl->AMS = (sl->AMSon ? ch->AMS : 31);
+			}
+			break;
+		}
+	}
+	
+	return 0;
+}
+
+
+int YM_SET( struct Ym2612_Impl* impl, int Adr, int data )
+{
+	struct state_t* YM2612 = &impl->YM2612;
+	struct tables_t* g = &impl->g;
+	switch ( Adr )
+	{
+		case 0x22:
+			if (data & 8) // LFO enable
+			{
+				// Cool Spot music 1, LFO modified severals time which
+				// distord the sound, have to check that on a real genesis...
+
+				g->LFOinc = g->LFO_INC_TAB [data & 7];
+			}
+			else
+			{
+				g->LFOinc = g->LFOcnt = 0;
+			}
+			break;
+
+		case 0x24:
+			YM2612->TimerA = (YM2612->TimerA & 0x003) | (((int) data) << 2);
+
+			if (YM2612->TimerAL != (1024 - YM2612->TimerA) << 12)
+			{
+				YM2612->TimerAcnt = YM2612->TimerAL = (1024 - YM2612->TimerA) << 12;
+			}
+			break;
+
+		case 0x25:
+			YM2612->TimerA = (YM2612->TimerA & 0x3FC) | (data & 3);
+
+			if (YM2612->TimerAL != (1024 - YM2612->TimerA) << 12)
+			{
+				YM2612->TimerAcnt = YM2612->TimerAL = (1024 - YM2612->TimerA) << 12;
+			}
+			break;
+
+		case 0x26:
+			YM2612->TimerB = data;
+
+			if (YM2612->TimerBL != (256 - YM2612->TimerB) << (4 + 12))
+			{
+				YM2612->TimerBcnt = YM2612->TimerBL = (256 - YM2612->TimerB) << (4 + 12);
+			}
+			break;
+
+		case 0x27:
+			// Parametre divers
+			// b7 = CSM MODE
+			// b6 = 3 slot mode
+			// b5 = reset b
+			// b4 = reset a
+			// b3 = timer enable b
+			// b2 = timer enable a
+			// b1 = load b
+			// b0 = load a
+
+			if ((data ^ YM2612->Mode) & 0x40)
+			{
+				// We changed the channel 2 mode, so recalculate phase step
+				// This fix the punch sound in Street of Rage 2
+
+				YM2612_Special_Update();
+
+				YM2612->CHANNEL [2].SLOT [0].Finc = -1;      // recalculate phase step
+			}
+
+//          if ((data & 2) && (YM2612->Status & 2)) YM2612->TimerBcnt = YM2612->TimerBL;
+//          if ((data & 1) && (YM2612->Status & 1)) YM2612->TimerAcnt = YM2612->TimerAL;
+
+//          YM2612->Status &= (~data >> 4);                  // Reset du Status au cas ou c'est demande
+			YM2612->Status &= (~data >> 4) & (data >> 2);    // Reset Status
+
+			YM2612->Mode = data;
+			break;
+
+		case 0x28: {
+			int nch = data & 3;
+			if ( nch == 3 )
+				return 1;
+			if ( data & 4 )
+				nch += 3;
+			struct channel_* ch = &YM2612->CHANNEL [nch];
+
+			YM2612_Special_Update();
+
+			if (data & 0x10) KEY_ON(ch, g, S0);    // On appuie sur la touche pour le slot 1
+			else KEY_OFF(ch, g, S0);               // On rel'che la touche pour le slot 1
+			if (data & 0x20) KEY_ON(ch, g, S1);    // On appuie sur la touche pour le slot 3
+			else KEY_OFF(ch, g, S1);               // On rel'che la touche pour le slot 3
+			if (data & 0x40) KEY_ON(ch, g, S2);    // On appuie sur la touche pour le slot 2
+			else KEY_OFF(ch, g, S2);               // On rel'che la touche pour le slot 2
+			if (data & 0x80) KEY_ON(ch, g, S3);    // On appuie sur la touche pour le slot 4
+			else KEY_OFF(ch, g, S3);               // On rel'che la touche pour le slot 4
+			break;
+		}
+		
+		case 0x2B:
+			if (YM2612->DAC ^ (data & 0x80)) YM2612_Special_Update();
+
+			YM2612->DAC = data & 0x80;   // activation/desactivation du DAC
+			break;
+	}
+	
+	return 0;
+}
+
+#if defined(ROCKBOX)
+double fabs(double x)
+{
+	if (x < 0.0) return -x;
+	return x;
+}
+
+double ipow(double a,int b)
+{
+	if (b < 0) {
+		a = 1.0 / a;
+		b = -b;
+	}
+	double result = 1.0;
+	while(b) {
+		if (b & 1) result*=a;
+		a *= a;
+		b >>= 1;
+	}
+	return result; 
+}
+#endif
+
+void impl_reset( struct Ym2612_Impl* impl );
+void impl_set_rate( struct Ym2612_Impl* impl, double sample_rate, double clock_rate )
+{
+	assert( sample_rate );
+	assert( !clock_rate || clock_rate > sample_rate );
+	
+	int i;
+
+	// 144 = 12 * (prescale * 2) = 12 * 6 * 2
+	// prescale set to 6 by default
+	
+	double Frequence = (clock_rate ? clock_rate / sample_rate / 144.0 : 1.0);
+	if ( fabs( Frequence - 1.0 ) < 0.0000001 )
+		Frequence = 1.0;
+	impl->YM2612.TimerBase = (int) (Frequence * 4096.0);
+
+	// Tableau TL :
+	// [0     -  4095] = +output  [4095  - ...] = +output overflow (fill with 0)
+	// [12288 - 16383] = -output  [16384 - ...] = -output overflow (fill with 0)
+
+	for ( i = 0; i < TL_LENGHT; i++ )
+	{
+		if (i >= PG_CUT_OFF)    // YM2612 cut off sound after 78 dB (14 bits output ?)
+		{
+			impl->g.TL_TAB [TL_LENGHT + i] = impl->g.TL_TAB [i] = 0;
+		}
+		else
+		{
+			// Decibel -> Voltage
+		#ifdef YM2612_CALCUL_TABLES
+			impl->g.TL_TAB [i] = (int) (MAX_OUT / pow( 10.0, ENV_STEP / 20.0f * i ));
+		#else
+			impl->g.TL_TAB [i] = tl_coeff [i];
+		#endif
+			impl->g.TL_TAB [TL_LENGHT + i] = -impl->g.TL_TAB [i];
+		}
+	}
+	
+	// Tableau SIN :
+	// impl->g.SIN_TAB [x] [y] = sin(x) * y; 
+	// x = phase and y = volume
+
+	impl->g.SIN_TAB [0] = impl->g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF;
+
+	for ( i = 1; i <= SIN_LENGHT / 4; i++ )
+	{
+		// Sinus in dB
+	#ifdef YM2612_CALCUL_TABLES
+		double x = 20 * log10( 1 / sin( 2.0 * PI * i / SIN_LENGHT ) ); // convert to dB
+
+		int j = (int) (x / ENV_STEP);                       // Get TL range
+
+		if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF;
+	#else
+		int j = sindb_coeff [i-1];
+	#endif
+
+		impl->g.SIN_TAB [i] = impl->g.SIN_TAB [(SIN_LENGHT / 2) - i] = j;
+		impl->g.SIN_TAB [(SIN_LENGHT / 2) + i] = impl->g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j;
+	}
+
+	// Tableau LFO (LFO wav) :
+
+	for ( i = 0; i < LFO_LENGHT; i++ )
+	{
+	#ifdef YM2612_CALCUL_TABLES
+		double x = 1 + sin( 2.0 * PI * i * (1.0 / LFO_LENGHT) );    // Sinus
+		x *= 11.8 / ENV_STEP / 2;       // ajusted to MAX enveloppe modulation
+
+		impl->g.LFO_ENV_TAB [i] = (int) x;
+
+		x = sin( 2.0 * PI * i * (1.0 / LFO_LENGHT) );   // Sinus
+		x *= (1 << (LFO_HBITS - 1)) - 1;
+
+		impl->g.LFO_FREQ_TAB [i] = (int) x;
+	#else
+		impl->g.LFO_ENV_TAB [i] = lfo_env_coeff [i];
+		impl->g.LFO_FREQ_TAB [i] = lfo_freq_coeff [i];
+	#endif
+	}
+
+	// Tableau Enveloppe :
+	// impl->g.ENV_TAB [0] -> impl->g.ENV_TAB [ENV_LENGHT - 1]              = attack curve
+	// impl->g.ENV_TAB [ENV_LENGHT] -> impl->g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve
+
+	for ( i = 0; i < ENV_LENGHT; i++ )
+	{
+		// Attack curve (x^8 - music level 2 Vectorman 2)
+	#if defined(ROCKBOX)
+		double x = ipow( ((ENV_LENGHT - 1) - i) / (double) ENV_LENGHT, 8.0 );
+	#else
+		double x = pow( ((ENV_LENGHT - 1) - i) / (double) ENV_LENGHT, 8.0 );
+	#endif
+		x *= ENV_LENGHT;
+
+		impl->g.ENV_TAB [i] = (int) x;
+
+		// Decay curve (just linear)
+		impl->g.ENV_TAB [ENV_LENGHT + i] = i;
+	}
+	for ( i = 0; i < 8; i++ )
+		impl->g.ENV_TAB [i + ENV_LENGHT * 2] = 0;
+	
+	impl->g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1;      // for the stopped state
+	
+	// Tableau pour la conversion Attack -> Decay and Decay -> Attack
+	
+	int j = ENV_LENGHT - 1;
+	for ( i = 0; i < ENV_LENGHT; i++ )
+	{
+		while ( j && impl->g.ENV_TAB [j] < i )
+			j--;
+
+		impl->g.DECAY_TO_ATTACK [i] = j << ENV_LBITS;
+	}
+
+	// Tableau pour le Substain Level
+	
+	for ( i = 0; i < 15; i++ )
+	{
+		double x = i * 3 / ENV_STEP; // 3 and not 6 (Mickey Mania first music for test)
+
+		impl->g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY;
+	}
+
+	impl->g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off
+
+	// Tableau Frequency Step
+	{
+		// 0.5 because MUL = value * 2
+	#if SIN_LBITS + SIN_HBITS - (21 - 7) < 0
+		double const factor = 0.5 / (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)) * Frequence;
+	#else
+		double const factor = 0.5 * (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))) * Frequence;
+	#endif
+		for ( i = 0; i < 2048; i++ )
+			impl->g.FINC_TAB [i] = (unsigned) (i * factor);
+	}
+	
+	// Tableaux Attack & Decay Rate
+
+	for ( i = 0; i < 4; i++ )
+	{
+		impl->g.AR_TAB [i] = 0;
+		impl->g.DR_TAB [i] = 0;
+	}
+	
+	for ( i = 0; i < 60; i++ )
+	{
+		double x =
+				(1.0 + ((i & 3) * 0.25)) *      // bits 0-1 : x1.00, x1.25, x1.50, x1.75
+				(ENV_LENGHT << ENV_LBITS) *     // on ajuste pour le tableau impl->g.ENV_TAB
+				Frequence *
+				(1 << (i >> 2));                // bits 2-5 : shift bits (x2^0 - x2^15)     
+		
+		impl->g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE);
+		impl->g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE);
+	}
+	
+	for ( i = 64; i < 96; i++ )
+	{
+		impl->g.AR_TAB [i] = impl->g.AR_TAB [63];
+		impl->g.DR_TAB [i] = impl->g.DR_TAB [63];
+
+		impl->g.NULL_RATE [i - 64] = 0;
+	}
+	
+	for ( i = 96; i < 128; i++ )
+		impl->g.AR_TAB [i] = 0;
+	
+	// Tableau Detune
+	{
+	#if SIN_LBITS + SIN_HBITS - 21 < 0
+		double const factor = 1.0 / (1 << (21 - SIN_LBITS - SIN_HBITS)) * Frequence;
+	#else
+		double const factor =       (1 << (SIN_LBITS + SIN_HBITS - 21)) * Frequence;
+	#endif
+		for ( i = 0; i < 4; i++ )
+		{
+			int j;
+			for ( j = 0; j < 32; j++ )
+			{
+				double y = DT_DEF_TAB [(i << 5) + j] * factor;
+				
+				impl->g.DT_TAB [i + 0] [j] = (int)  y;
+				impl->g.DT_TAB [i + 4] [j] = (int) -y;
+			}
+		}
+	}
+	
+	// Tableau LFO
+	impl->g.LFO_INC_TAB [0] = (unsigned) (3.98 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	impl->g.LFO_INC_TAB [1] = (unsigned) (5.56 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	impl->g.LFO_INC_TAB [2] = (unsigned) (6.02 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	impl->g.LFO_INC_TAB [3] = (unsigned) (6.37 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	impl->g.LFO_INC_TAB [4] = (unsigned) (6.88 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	impl->g.LFO_INC_TAB [5] = (unsigned) (9.63 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	impl->g.LFO_INC_TAB [6] = (unsigned) (48.1 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	impl->g.LFO_INC_TAB [7] = (unsigned) (72.2 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+	
+	impl_reset( impl );
+}
+
+const char* Ym2612_set_rate( struct Ym2612_Emu* this, double sample_rate, double clock_rate )
+{
+// Only set rates if necessary
+#if defined(ROCKBOX)
+	static double last_sample_rate = 0.0, last_clock_rate = 0.0;
+	if (last_sample_rate == sample_rate && last_clock_rate == clock_rate) return 0;
+#endif
+	memset( &this->impl.YM2612, 0, sizeof this->impl.YM2612 );
+	impl_set_rate( &this->impl, sample_rate, clock_rate );
+	
+	return 0;
+}
+
+inline void write0( struct Ym2612_Impl* impl, int opn_addr, int data )
+{
+	assert( (unsigned) data <= 0xFF );
+	
+	if ( opn_addr < 0x30 )
+	{
+		impl->YM2612.REG [0] [opn_addr] = data;
+		YM_SET( impl, opn_addr, data );
+	}
+	else if ( impl->YM2612.REG [0] [opn_addr] != data )
+	{
+		impl->YM2612.REG [0] [opn_addr] = data;
+		
+		if ( opn_addr < 0xA0 )
+			SLOT_SET( impl, opn_addr, data );
+		else
+			CHANNEL_SET( &impl->YM2612, opn_addr, data );
+	}
+}
+
+inline void write1( struct Ym2612_Impl* impl, int opn_addr, int data )
+{
+	assert( (unsigned) data <= 0xFF );
+	
+	if ( opn_addr >= 0x30 && impl->YM2612.REG [1] [opn_addr] != data )
+	{
+		impl->YM2612.REG [1] [opn_addr] = data;
+
+		if ( opn_addr < 0xA0 )
+			SLOT_SET( impl, opn_addr + 0x100, data );
+		else
+			CHANNEL_SET( &impl->YM2612, opn_addr + 0x100, data );
+	}
+}
+
+void impl_reset( struct Ym2612_Impl* impl )
+{
+	impl->g.LFOcnt = 0;
+	impl->YM2612.TimerA = 0;
+	impl->YM2612.TimerAL = 0;
+	impl->YM2612.TimerAcnt = 0;
+	impl->YM2612.TimerB = 0;
+	impl->YM2612.TimerBL = 0;
+	impl->YM2612.TimerBcnt = 0;
+	impl->YM2612.DAC = 0;
+
+	impl->YM2612.Status = 0;
+
+	int i;
+	for ( i = 0; i < ym2612_channel_count; i++ )
+	{
+		struct channel_* ch = &impl->YM2612.CHANNEL [i];
+		
+		ch->LEFT = ~0;
+		ch->RIGHT = ~0;
+		ch->ALGO = 0;
+		ch->FB = 31;
+		ch->FMS = 0;
+		ch->AMS = 0;
+
+		int j;
+		for ( j = 0 ;j < 4 ; j++ )
+		{
+			ch->S0_OUT [j] = 0;
+			ch->FNUM [j] = 0;
+			ch->FOCT [j] = 0;
+			ch->KC [j] = 0;
+
+			ch->SLOT [j].Fcnt = 0;
+			ch->SLOT [j].Finc = 0;
+			ch->SLOT [j].Ecnt = ENV_END;     // Put it at the end of Decay phase...
+			ch->SLOT [j].Einc = 0;
+			ch->SLOT [j].Ecmp = 0;
+			ch->SLOT [j].Ecurp = RELEASE;
+
+			ch->SLOT [j].ChgEnM = 0;
+		}
+	}
+
+	for ( i = 0; i < 0x100; i++ )
+	{
+		impl->YM2612.REG [0] [i] = -1;
+		impl->YM2612.REG [1] [i] = -1;
+	}
+
+	for ( i = 0xB6; i >= 0xB4; i-- )
+	{
+		write0( impl, i, 0xC0 );
+		write1( impl, i, 0xC0 );
+	}
+
+	for ( i = 0xB2; i >= 0x22; i-- )
+	{
+		write0( impl, i, 0 );
+		write1( impl, i, 0 );
+	}
+	
+	write0( impl, 0x2A, 0x80 );
+}
+
+void Ym2612_reset( struct Ym2612_Emu* this )
+{
+	impl_reset( &this->impl );
+}
+
+void Ym2612_write0( struct Ym2612_Emu* this, int addr, int data )
+{
+	write0( &this->impl, addr, data );
+}
+
+void Ym2612_write1( struct Ym2612_Emu* this, int addr, int data )
+{
+	write1( &this->impl, addr, data );
+}
+
+void Ym2612_mute_voices( struct Ym2612_Emu* this, int mask ) { this->impl.mute_mask = mask; }
+
+static void update_envelope_( struct slot_t* sl )
+{
+	switch ( sl->Ecurp )
+	{
+	case 0:
+		// Env_Attack_Next
+		
+		// Verified with Gynoug even in HQ (explode SFX)
+		sl->Ecnt = ENV_DECAY;
+
+		sl->Einc = sl->EincD;
+		sl->Ecmp = sl->SLL;
+		sl->Ecurp = DECAY;
+		break;
+	
+	case 1:
+		// Env_Decay_Next
+		
+		// Verified with Gynoug even in HQ (explode SFX)
+		sl->Ecnt = sl->SLL;
+
+		sl->Einc = sl->EincS;
+		sl->Ecmp = ENV_END;
+		sl->Ecurp = SUBSTAIN;
+		break;
+	
+	case 2:
+		// Env_Substain_Next(slot_t *SL)
+		if (sl->SEG & 8)    // SSG envelope type
+		{
+			int release = sl->SEG & 1;
+			
+			if ( !release )
+			{
+				// re KEY ON
+
+				// sl->Fcnt = 0;
+				// sl->ChgEnM = ~0;
+
+				sl->Ecnt = 0;
+				sl->Einc = sl->EincA;
+				sl->Ecmp = ENV_DECAY;
+				sl->Ecurp = ATTACK;
+			}
+
+			set_seg( sl, (sl->SEG << 1) & 4 );
+			
+			if ( !release )
+				break;
+		}
+		// fall through
+	
+	case 3:
+		// Env_Release_Next
+		sl->Ecnt = ENV_END;
+		sl->Einc = 0;
+		sl->Ecmp = ENV_END + 1;
+		break;
+	
+	// default: no op
+	}
+}
+
+static inline void update_envelope( struct slot_t* sl )
+{
+	int ecmp = sl->Ecmp;
+	if ( (sl->Ecnt += sl->Einc) >= ecmp )
+		update_envelope_( sl );
+}
+
+
+typedef void (*ym2612_update_chan_t)( struct tables_t*, struct channel_*, short*, int );
+
+#define GET_CURRENT_PHASE \
+int in0 = ch->SLOT[S0].Fcnt; \
+int in1 = ch->SLOT[S1].Fcnt; \
+int in2 = ch->SLOT[S2].Fcnt; \
+int in3 = ch->SLOT[S3].Fcnt; \
+
+#define GET_CURRENT_LFO \
+int YM2612_LFOinc = g->LFOinc; \
+int YM2612_LFOcnt = g->LFOcnt + YM2612_LFOinc; 
+
+#define CALC_EN( x ) \
+	int temp##x = ENV_TAB [ch->SLOT [S##x].Ecnt >> ENV_LBITS] + ch->SLOT [S##x].TLL;  \
+	int en##x = ((temp##x ^ ch->SLOT [S##x].env_xor) + (env_LFO >> ch->SLOT [S##x].AMS)) & \
+ 			((temp##x - ch->SLOT [S##x].env_max) >> 31);
+				
+#define GET_ENV \
+int const env_LFO = g->LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK]; \
+short const* const ENV_TAB = g->ENV_TAB; \
+CALC_EN( 0 ) \
+CALC_EN( 1 ) \
+CALC_EN( 2 ) \
+CALC_EN( 3 ) \
+int const* const TL_TAB = g->TL_TAB;
+
+#define DO_FEEDBACK               \
+int CH_S0_OUT_0 = ch->S0_OUT [0]; \
+{                                 \
+	int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch->FB); \
+	CH_S0_OUT_1 = CH_S0_OUT_0; \
+	CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 ); \
+} \
+
+#define SINT( i, o ) (TL_TAB [g->SIN_TAB [(i)] + (o)])
+
+#define DO_LIMIT \
+CH_OUTd >>= MAX_OUT_BITS - output_bits + 2; \
+
+#define UPDATE_PHASE_CYCLE \
+unsigned freq_LFO = ((g->LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] * \
+			ch->FMS) >> (LFO_HBITS - 1 + 1)) + (1 << (LFO_FMS_LBITS - 1)); \
+YM2612_LFOcnt += YM2612_LFOinc; \
+in0 += (ch->SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); \
+in1 += (ch->SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); \
+in2 += (ch->SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); \
+in3 += (ch->SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+
+#define UPDATE_ENV \
+int t0 = buf [0] + (CH_OUTd & ch->LEFT); \
+int t1 = buf [1] + (CH_OUTd & ch->RIGHT); \
+update_envelope( &ch->SLOT [0] ); \
+update_envelope( &ch->SLOT [1] ); \
+update_envelope( &ch->SLOT [2] ); \
+update_envelope( &ch->SLOT [3] );
+
+#define DO_OUTPUT_0 \
+ch->S0_OUT [0] = CH_S0_OUT_0; \
+buf [0] = t0; \
+buf [1] = t1; \
+buf += 2; \
+
+#define DO_OUTPUT_1 \
+ch->S0_OUT [1] = CH_S0_OUT_1;
+
+#define UPDATE_PHASE \
+ch->SLOT [S0].Fcnt = in0; \
+ch->SLOT [S1].Fcnt = in1; \
+ch->SLOT [S2].Fcnt = in2; \
+ch->SLOT [S3].Fcnt = in3;
+
+void ym2612_update_chan0( struct tables_t* g, struct channel_* ch,
+		short* buf, int length )
+{
+	int not_end = ch->SLOT [S3].Ecnt - ENV_END;
+	int CH_S0_OUT_1 = ch->S0_OUT [1];
+	
+	GET_CURRENT_PHASE
+	GET_CURRENT_LFO
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		GET_ENV
+		DO_FEEDBACK
+		
+		int CH_OUTd;
+		int temp = in1 + CH_S0_OUT_1;
+		temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 );
+		temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+		CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+		
+		DO_LIMIT
+		UPDATE_PHASE_CYCLE
+		UPDATE_ENV
+		DO_OUTPUT_0
+	}
+	while ( --length );
+	DO_OUTPUT_1
+	UPDATE_PHASE
+}
+
+void ym2612_update_chan1( struct tables_t* g, struct channel_* ch,
+		short* buf, int length )
+{
+	int not_end = ch->SLOT [S3].Ecnt - ENV_END;
+	int CH_S0_OUT_1 = ch->S0_OUT [1];
+	
+	GET_CURRENT_PHASE
+	GET_CURRENT_LFO
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		GET_ENV
+		DO_FEEDBACK
+		
+		int CH_OUTd;
+		int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
+		temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+		CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+		
+		DO_LIMIT
+		UPDATE_PHASE_CYCLE
+		UPDATE_ENV
+		DO_OUTPUT_0
+	}
+	while ( --length );
+	DO_OUTPUT_1
+	UPDATE_PHASE
+}
+
+void ym2612_update_chan2( struct tables_t* g, struct channel_* ch,
+		short* buf, int length )
+{
+	int not_end = ch->SLOT [S3].Ecnt - ENV_END;
+	int CH_S0_OUT_1 = ch->S0_OUT [1];
+	
+	GET_CURRENT_PHASE
+	GET_CURRENT_LFO
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		GET_ENV
+		DO_FEEDBACK
+		
+		int CH_OUTd;
+		int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
+		temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+		CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+		
+		DO_LIMIT
+		UPDATE_PHASE_CYCLE
+		UPDATE_ENV
+		DO_OUTPUT_0
+	}
+	while ( --length );
+	DO_OUTPUT_1
+	UPDATE_PHASE
+}
+
+void ym2612_update_chan3( struct tables_t* g, struct channel_* ch,
+		short* buf, int length )
+{
+	int not_end = ch->SLOT [S3].Ecnt - ENV_END;
+	int CH_S0_OUT_1 = ch->S0_OUT [1];
+	
+	GET_CURRENT_PHASE
+	GET_CURRENT_LFO
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		GET_ENV
+		DO_FEEDBACK
+		
+		int CH_OUTd;
+		int temp = in1 + CH_S0_OUT_1;
+		temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) +
+				SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+		CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+		
+		DO_LIMIT
+		UPDATE_PHASE_CYCLE
+		UPDATE_ENV
+		DO_OUTPUT_0
+	}
+	while ( --length );
+	DO_OUTPUT_1
+	UPDATE_PHASE
+}
+
+void ym2612_update_chan4( struct tables_t* g, struct channel_* ch,
+		short* buf, int length )
+{
+	int not_end = ch->SLOT [S3].Ecnt - ENV_END;
+	not_end |= ch->SLOT [S1].Ecnt - ENV_END;
+	
+	int CH_S0_OUT_1 = ch->S0_OUT [1];
+	
+	GET_CURRENT_PHASE
+	GET_CURRENT_LFO
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		GET_ENV
+		DO_FEEDBACK
+		
+		int CH_OUTd;
+		int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+		CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) +
+				SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 );
+		
+		DO_LIMIT
+		UPDATE_PHASE_CYCLE
+		UPDATE_ENV
+		DO_OUTPUT_0
+	}
+	while ( --length );
+	DO_OUTPUT_1
+	UPDATE_PHASE
+}
+
+void ym2612_update_chan5( struct tables_t* g, struct channel_* ch,
+		short* buf, int length )
+{
+	int not_end = ch->SLOT [S3].Ecnt - ENV_END;
+	not_end |= ch->SLOT [S2].Ecnt - ENV_END;
+	not_end |= ch->SLOT [S1].Ecnt - ENV_END;
+	
+	int CH_S0_OUT_1 = ch->S0_OUT [1];
+	
+	GET_CURRENT_PHASE
+	GET_CURRENT_LFO
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		GET_ENV
+		DO_FEEDBACK
+		
+		int CH_OUTd;
+		int temp = CH_S0_OUT_1;
+		CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) +
+				SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) +
+				SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 );
+		
+		DO_LIMIT
+		UPDATE_PHASE_CYCLE
+		UPDATE_ENV
+		DO_OUTPUT_0
+	}
+	while ( --length );
+	DO_OUTPUT_1
+	UPDATE_PHASE
+}
+
+void ym2612_update_chan6( struct tables_t* g, struct channel_* ch,
+		short* buf, int length )
+{
+	int not_end = ch->SLOT [S3].Ecnt - ENV_END;
+	not_end |= ch->SLOT [S2].Ecnt - ENV_END;
+	not_end |= ch->SLOT [S1].Ecnt - ENV_END;
+	
+	int CH_S0_OUT_1 = ch->S0_OUT [1];
+	
+	GET_CURRENT_PHASE
+	GET_CURRENT_LFO
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		GET_ENV
+		DO_FEEDBACK
+		
+		int CH_OUTd;
+		CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
+				SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) +
+				SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+				
+		DO_LIMIT
+		UPDATE_PHASE_CYCLE
+		UPDATE_ENV
+		DO_OUTPUT_0
+	}
+	while ( --length );
+	DO_OUTPUT_1
+	UPDATE_PHASE
+}
+
+void ym2612_update_chan7( struct tables_t* g, struct channel_* ch,
+		short* buf, int length )
+{
+	int not_end = ch->SLOT [S3].Ecnt - ENV_END;
+	not_end |= ch->SLOT [S0].Ecnt - ENV_END;
+	not_end |= ch->SLOT [S2].Ecnt - ENV_END;
+	not_end |= ch->SLOT [S1].Ecnt - ENV_END;
+	
+	int CH_S0_OUT_1 = ch->S0_OUT [1];
+	
+	GET_CURRENT_PHASE
+	GET_CURRENT_LFO
+	
+	if ( !not_end )
+		return;
+	
+	do
+	{
+		GET_ENV
+		DO_FEEDBACK
+		
+		int CH_OUTd;
+		CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
+				SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) +
+				SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1;
+		
+		DO_LIMIT
+		UPDATE_PHASE_CYCLE
+		UPDATE_ENV
+		DO_OUTPUT_0
+	}
+	while ( --length );
+	DO_OUTPUT_1
+	UPDATE_PHASE
+}
+
+static void (*UPDATE_CHAN[8])(struct tables_t* g, struct channel_* ch,
+		short* buf, int length) =
+{
+	(void *)ym2612_update_chan0,
+	(void *)ym2612_update_chan1,
+	(void *)ym2612_update_chan2,
+	(void *)ym2612_update_chan3,
+	(void *)ym2612_update_chan4,
+	(void *)ym2612_update_chan5,
+	(void *)ym2612_update_chan6,
+	(void *)ym2612_update_chan7
+};
+
+void run_timer( struct Ym2612_Impl* impl, int length )
+{
+	int const step = 6;
+	int remain = length;
+	do
+	{
+		int n = step;
+		if ( n > remain )
+			n = remain;
+		remain -= n;
+		
+		int i = n * impl->YM2612.TimerBase;
+		if (impl->YM2612.Mode & 1)                            // Timer A ON ?
+		{
+	//      if ((impl->YM2612.TimerAcnt -= 14073) <= 0)       // 13879=NTSC (old: 14475=NTSC  14586=PAL)
+			if ((impl->YM2612.TimerAcnt -= i) <= 0)
+			{
+				// timer a overflow
+				
+				impl->YM2612.Status |= (impl->YM2612.Mode & 0x04) >> 2;
+				impl->YM2612.TimerAcnt += impl->YM2612.TimerAL;
+
+				if (impl->YM2612.Mode & 0x80)
+				{
+					KEY_ON( &impl->YM2612.CHANNEL [2], &impl->g, 0 );
+					KEY_ON( &impl->YM2612.CHANNEL [2], &impl->g, 1 );
+					KEY_ON( &impl->YM2612.CHANNEL [2], &impl->g, 2 );
+					KEY_ON( &impl->YM2612.CHANNEL [2], &impl->g, 3 );
+				}
+			}
+		}
+
+		if (impl->YM2612.Mode & 2)                            // Timer B ON ?
+		{
+	//      if ((impl->YM2612.TimerBcnt -= 14073) <= 0)       // 13879=NTSC (old: 14475=NTSC  14586=PAL)
+			if ((impl->YM2612.TimerBcnt -= i) <= 0)
+			{
+				// timer b overflow
+				impl->YM2612.Status |= (impl->YM2612.Mode & 0x08) >> 2;
+				impl->YM2612.TimerBcnt += impl->YM2612.TimerBL;
+			}
+		}
+	}
+	while ( remain > 0 );
+}
+
+void impl_run( struct Ym2612_Impl* impl, int pair_count, short out [] )
+{
+	if ( pair_count <= 0 )
+		return;
+	
+	if ( impl->YM2612.Mode & 3 )
+		run_timer( impl, pair_count );
+	
+	// Mise à jour des pas des compteurs-frequences s'ils ont ete modifies
+	
+	int chi;
+	for ( chi = 0; chi < ym2612_channel_count; chi++ )
+	{
+		struct channel_* ch = &impl->YM2612.CHANNEL [chi];
+		if ( ch->SLOT [0].Finc != -1 )
+			continue;
+		
+		int i2 = 0;
+		if ( chi == 2 && (impl->YM2612.Mode & 0x40) )
+			i2 = 2;
+		
+		int i;
+		for ( i = 0; i < 4; i++ )
+		{
+			// static int seq [4] = { 2, 1, 3, 0 };
+			// if ( i2 ) i2 = seq [i];
+			
+			struct slot_t* sl = &ch->SLOT [i];
+			int finc = impl->g.FINC_TAB [ch->FNUM [i2]] >> (7 - ch->FOCT [i2]);
+			int ksr = ch->KC [i2] >> sl->KSR_S;   // keycode attenuation
+			sl->Finc = (finc + sl->DT [ch->KC [i2]]) * sl->MUL;
+			if (sl->KSR != ksr)          // si le KSR a change alors
+			{                       // les differents taux pour l'enveloppe sont mis à jour
+				sl->KSR = ksr;
+
+				sl->EincA = sl->AR [ksr];
+				sl->EincD = sl->DR [ksr];
+				sl->EincS = sl->SR [ksr];
+				sl->EincR = sl->RR [ksr];
+
+				if (sl->Ecurp == ATTACK)
+				{
+					sl->Einc = sl->EincA;
+				}
+				else if (sl->Ecurp == DECAY)
+				{
+					sl->Einc = sl->EincD;
+				}
+				else if (sl->Ecnt < ENV_END)
+				{
+					if (sl->Ecurp == SUBSTAIN)
+						sl->Einc = sl->EincS;
+					else if (sl->Ecurp == RELEASE)
+						sl->Einc = sl->EincR;
+				}
+			}
+			
+			if ( i2 )
+				i2 = (i2 ^ 2) ^ (i2 >> 1);
+		}
+	}
+	
+	int i;
+	for ( i = 0; i < ym2612_channel_count; i++ )
+	{
+		if ( !(impl->mute_mask & (1 << i)) && (i != 5 || !impl->YM2612.DAC) )
+			UPDATE_CHAN [impl->YM2612.CHANNEL [i].ALGO]( &impl->g, &impl->YM2612.CHANNEL [i], out, pair_count );
+	}
+	
+	impl->g.LFOcnt += impl->g.LFOinc * pair_count;
+}
+
+void Ym2612_run( struct Ym2612_Emu* this, int pair_count, short out [] ) { impl_run( &this->impl, pair_count, out ); }
diff --git a/apps/codecs/libgme/ym2612_emu.h b/apps/codecs/libgme/ym2612_emu.h
new file mode 100644
index 0000000..19f0903
--- /dev/null
+++ b/apps/codecs/libgme/ym2612_emu.h
@@ -0,0 +1,237 @@
+// YM2612 FM sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef YM2612_EMU_H
+#define YM2612_EMU_H
+
+#include "blargg_common.h"
+
+enum { ym2612_out_chan_count = 2 }; // stereo
+enum { ym2612_channel_count = 6 };
+enum { ym2612_disabled_time = -1 };
+
+struct slot_t
+{
+	const int *DT;  // parametre detune
+	int MUL;    // parametre "multiple de frequence"
+	int TL;     // Total Level = volume lorsque l'enveloppe est au plus haut
+	int TLL;    // Total Level ajusted
+	int SLL;    // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression
+	int KSR_S;  // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe
+	int KSR;    // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer
+				// sur les differents parametres de l'enveloppe comme l'attaque, le decay ...  comme dans la realite !
+	int SEG;    // Type enveloppe SSG
+	int env_xor;
+	int env_max;
+
+	const int *AR;  // Attack Rate (table pointeur) = Taux d'attaque (AR [KSR])
+	const int *DR;  // Decay Rate (table pointeur) = Taux pour la regression (DR [KSR])
+	const int *SR;  // Sustin Rate (table pointeur) = Taux pour le maintien (SR [KSR])
+	const int *RR;  // Release Rate (table pointeur) = Taux pour le rel'chement (RR [KSR])
+	int Fcnt;   // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN [Finc >> 16])
+	int Finc;   // frequency step = pas d'incrementation du compteur-frequence
+				// plus le pas est grand, plus la frequence est aïgu (ou haute)
+	int Ecurp;  // Envelope current phase = cette variable permet de savoir dans quelle phase
+				// de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ...
+				// en fonction de la valeur de cette variable, on va appeler une fonction permettant
+				// de mettre à jour l'enveloppe courante.
+	int Ecnt;   // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe
+	int Einc;   // Envelope step courant
+	int Ecmp;   // Envelope counter limite pour la prochaine phase
+	int EincA;  // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque
+				// cette valeur est egal à AR [KSR]
+	int EincD;  // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression
+				// cette valeur est egal à DR [KSR]
+	int EincS;  // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue
+				// cette valeur est egal à SR [KSR]
+	int EincR;  // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement
+				// cette valeur est egal à RR [KSR]
+	int *OUTp;  // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree
+				// d'un autre ou carrement à la sortie de la voie
+	int INd;    // input data of the slot = donnees en entree du slot
+	int ChgEnM; // Change envelop mask.
+	int AMS;    // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO
+	int AMSon;  // AMS enable flag = drapeau d'activation de l'AMS
+};
+
+struct channel_
+{
+	int S0_OUT [4];         // anciennes sorties slot 0 (pour le feed back)
+	int LEFT;               // LEFT enable flag
+	int RIGHT;              // RIGHT enable flag
+	int ALGO;               // Algorythm = determine les connections entre les operateurs
+	int FB;                 // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree)
+	int FMS;                // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO
+	int AMS;                // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO
+	int FNUM [4];           // hauteur frequence de la voie (+ 3 pour le mode special)
+	int FOCT [4];           // octave de la voie (+ 3 pour le mode special)
+	int KC [4];             // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S)
+	struct slot_t SLOT [4];    // four slot.operators = les 4 slots de la voie
+	int FFlag;              // Frequency step recalculation flag
+};
+
+struct state_t
+{
+	int TimerBase;      // TimerBase calculation
+	int Status;         // YM2612 Status (timer overflow)
+	int TimerA;         // timerA limit = valeur jusqu'à laquelle le timer A doit compter
+	int TimerAL;
+	int TimerAcnt;      // timerA counter = valeur courante du Timer A
+	int TimerB;         // timerB limit = valeur jusqu'à laquelle le timer B doit compter
+	int TimerBL;
+	int TimerBcnt;      // timerB counter = valeur courante du Timer B
+	int Mode;           // Mode actuel des voie 3 et 6 (normal / special)
+	int DAC;            // DAC enabled flag
+	struct channel_ CHANNEL [ym2612_channel_count];  // Les 6 voies du YM2612
+	int REG [2] [0x100];    // Sauvegardes des valeurs de tout les registres, c'est facultatif
+						// cela nous rend le debuggage plus facile
+};
+
+#undef PI
+#define PI 3.14159265358979323846
+
+#define ATTACK    0
+#define DECAY     1
+#define SUBSTAIN  2
+#define RELEASE   3
+
+// SIN_LBITS <= 16
+// LFO_HBITS <= 16
+// (SIN_LBITS + SIN_HBITS) <= 26
+// (ENV_LBITS + ENV_HBITS) <= 28
+// (LFO_LBITS + LFO_HBITS) <= 28
+
+#define SIN_HBITS      12                               // Sinus phase counter int part
+#define SIN_LBITS      (26 - SIN_HBITS)                 // Sinus phase counter float part (best setting)
+
+#if (SIN_LBITS > 16)
+#define SIN_LBITS      16                               // Can't be greater than 16 bits
+#endif
+
+#define ENV_HBITS      12                               // Env phase counter int part
+#define ENV_LBITS      (28 - ENV_HBITS)                 // Env phase counter float part (best setting)
+
+#define LFO_HBITS      10                               // LFO phase counter int part
+#define LFO_LBITS      (28 - LFO_HBITS)                 // LFO phase counter float part (best setting)
+
+#define SIN_LENGHT     (1 << SIN_HBITS)
+#define ENV_LENGHT     (1 << ENV_HBITS)
+#define LFO_LENGHT     (1 << LFO_HBITS)
+
+#define TL_LENGHT      (ENV_LENGHT * 3)                 // Env + TL scaling + LFO
+
+#define SIN_MASK       (SIN_LENGHT - 1)
+#define ENV_MASK       (ENV_LENGHT - 1)
+#define LFO_MASK       (LFO_LENGHT - 1)
+
+#define ENV_STEP       (96.0 / ENV_LENGHT)              // ENV_MAX = 96 dB
+
+#define ENV_ATTACK     ((ENV_LENGHT * 0) << ENV_LBITS)
+#define ENV_DECAY      ((ENV_LENGHT * 1) << ENV_LBITS)
+#define ENV_END        ((ENV_LENGHT * 2) << ENV_LBITS)
+
+#define MAX_OUT_BITS   (SIN_HBITS + SIN_LBITS + 2)      // Modulation = -4 <--> +4
+#define MAX_OUT        ((1 << MAX_OUT_BITS) - 1)
+
+#define PG_CUT_OFF     ((int) (78.0 / ENV_STEP))
+//#define ENV_CUT_OFF    ((int) (68.0 / ENV_STEP))
+
+#define AR_RATE        399128
+#define DR_RATE        5514396
+
+//#define AR_RATE        426136
+//#define DR_RATE        (AR_RATE * 12)
+
+#define LFO_FMS_LBITS  9    // FIXED (LFO_FMS_BASE gives somethink as 1)
+#define LFO_FMS_BASE   ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS)))
+
+#define S0             0    // Stupid typo of the YM2612
+#define S1             2
+#define S2             1
+#define S3             3
+
+struct tables_t
+{
+	short SIN_TAB [SIN_LENGHT];                 // SINUS TABLE (offset into TL TABLE)
+	int LFOcnt;         // LFO counter = compteur-frequence pour le LFO
+	int LFOinc;         // LFO step counter = pas d'incrementation du compteur-frequence du LFO
+						// plus le pas est grand, plus la frequence est grande
+	unsigned int AR_TAB [128];                  // Attack rate table
+	unsigned int DR_TAB [96];                   // Decay rate table
+	unsigned int DT_TAB [8] [32];               // Detune table
+	unsigned int SL_TAB [16];                   // Substain level table
+	unsigned int NULL_RATE [32];                // Table for NULL rate
+	int LFO_INC_TAB [8];                        // LFO step table
+	
+	short ENV_TAB [2 * ENV_LENGHT + 8];         // ENV CURVE TABLE (attack & decay)
+	
+	short LFO_ENV_TAB [LFO_LENGHT];             // LFO AMS TABLE (adjusted for 11.8 dB)
+	short LFO_FREQ_TAB [LFO_LENGHT];            // LFO FMS TABLE
+	int TL_TAB [TL_LENGHT * 2];                 // TOTAL LEVEL TABLE (positif and minus)
+	unsigned int DECAY_TO_ATTACK [ENV_LENGHT];  // Conversion from decay to attack phase
+	unsigned int FINC_TAB [2048];               // Frequency step table
+};
+
+struct Ym2612_Impl
+{	
+	struct state_t YM2612;
+	int mute_mask;
+	struct tables_t g;
+};
+
+void impl_reset( struct Ym2612_Impl* impl );
+
+struct Ym2612_Emu  {
+	struct Ym2612_Impl impl;
+		
+	// Impl
+	int last_time;
+	int sample_rate;
+	int clock_rate;
+	short* out;
+};
+
+static inline void Ym2612_init( struct Ym2612_Emu* this_ )
+{ 
+	this_->last_time = ym2612_disabled_time; this_->out = 0;
+	this_->impl.mute_mask = 0;
+}
+	
+// Sets sample rate and chip clock rate, in Hz. Returns non-zero
+// if error. If clock_rate=0, uses sample_rate*144
+const char* Ym2612_set_rate( struct Ym2612_Emu* this_, double sample_rate, double clock_rate );
+	
+// Resets to power-up state
+void Ym2612_reset( struct Ym2612_Emu* this_ );
+	
+// Mutes voice n if bit n (1 << n) of mask is set
+void Ym2612_mute_voices( struct Ym2612_Emu* this_, int mask );
+	
+// Writes addr to register 0 then data to register 1
+void Ym2612_write0( struct Ym2612_Emu* this_, int addr, int data ) ICODE_ATTR;
+	
+// Writes addr to register 2 then data to register 3
+void Ym2612_write1( struct Ym2612_Emu* this_, int addr, int data ) ICODE_ATTR;
+	
+// Runs and adds pair_count*2 samples into current output buffer contents
+void Ym2612_run( struct Ym2612_Emu* this_, int pair_count, short* out ) ICODE_ATTR;
+
+static inline void Ym2612_enable( struct Ym2612_Emu* this_, bool b ) { this_->last_time = b ? 0 : ym2612_disabled_time; }
+static inline bool Ym2612_enabled( struct Ym2612_Emu* this_ ) { return this_->last_time != ym2612_disabled_time; }
+static inline void Ym2612_begin_frame( struct Ym2612_Emu* this_, short* buf ) { this_->out = buf; this_->last_time = 0; }
+		
+static inline int Ym2612_run_until( struct Ym2612_Emu* this_, int time )
+{
+	int count = time - this_->last_time;
+	if ( count > 0 )
+	{
+		if ( this_->last_time < 0 )
+			return false;
+		this_->last_time = time;
+		short* p = this_->out;
+		this_->out += count * ym2612_out_chan_count;
+		Ym2612_run( this_, count, p );
+	}
+	return true;
+}
+#endif
diff --git a/apps/codecs/libgme/ymdeltat.c b/apps/codecs/libgme/ymdeltat.c
new file mode 100644
index 0000000..ea0be59
--- /dev/null
+++ b/apps/codecs/libgme/ymdeltat.c
@@ -0,0 +1,655 @@
+/*

+**

+** File: ymdeltat.c

+**

+** YAMAHA DELTA-T adpcm sound emulation subroutine

+** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B)

+**

+** Base program is YM2610 emulator by Hiromitsu Shioya.

+** Written by Tatsuyuki Satoh

+** Improvements by Jarek Burczynski (bujar at mame dot net)

+**

+**

+** History:

+**

+** 03-08-2003 Jarek Burczynski:

+**  - fixed BRDY flag implementation.

+**

+** 24-07-2003 Jarek Burczynski, Frits Hilderink:

+**  - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset

+**

+** 22-07-2003 Jarek Burczynski, Frits Hilderink:

+**  - fixed external memory support

+**

+** 15-06-2003 Jarek Burczynski:

+**  - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08)

+**  - implemented support for the Limit address register

+**  - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM

+**  - implemented external memory access (read/write) via the ADPCM data reg reads/writes

+**    Thanks go to Frits Hilderink for the example code.

+**

+** 14-06-2003 Jarek Burczynski:

+**  - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO

+**  - modified EOS handling

+**

+** 05-04-2003 Jarek Burczynski:

+**  - implemented partial support for external/processor memory on sample replay

+**

+** 01-12-2002 Jarek Burczynski:

+**  - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi

+**  - renamed/removed some YM_DELTAT struct fields

+**

+** 28-12-2001 Acho A. Tang

+**  - added EOS status report on ADPCM playback.

+**

+** 05-08-2001 Jarek Burczynski:

+**  - now_step is initialized with 0 at the start of play.

+**

+** 12-06-2001 Jarek Burczynski:

+**  - corrected end of sample bug in YM_DELTAT_ADPCM_CALC.

+**    Checked on real YM2610 chip - address register is 24 bits wide.

+**    Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem.

+**

+** TO DO:

+**      Check size of the address register on the other chips....

+**

+** Version 0.72

+**

+** sound chips that have this unit:

+** YM2608   OPNA

+** YM2610/B OPNB

+** Y8950    MSX AUDIO

+**

+*/

+

+#include "ymdeltat.h"

+#define INLINE __inline

+#define logerror (void)

+

+#define YM_DELTAT_DELTA_MAX (24576)

+#define YM_DELTAT_DELTA_MIN (127)

+#define YM_DELTAT_DELTA_DEF (127)

+

+#define YM_DELTAT_DECODE_RANGE 32768

+#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE))

+#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1)

+

+

+/* Forecast to next Forecast (rate = *8) */

+/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */

+static const INT32 ym_deltat_decode_tableB1[16] ICONST_ATTR = {

+  1,   3,   5,   7,   9,  11,  13,  15,

+  -1,  -3,  -5,  -7,  -9, -11, -13, -15,

+};

+/* delta to next delta (rate= *64) */

+/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */

+static const INT32 ym_deltat_decode_tableB2[16] ICONST_ATTR = {

+  57,  57,  57,  57, 77, 102, 128, 153,

+  57,  57,  57,  57, 77, 102, 128, 153

+};

+

+#if 0

+void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT)

+{

+	logerror("BRDY_callback reached (flag set) !\n");

+

+	/* set BRDY bit in status register */

+	if(DELTAT->status_set_handler)

+		if(DELTAT->status_change_BRDY_bit)

+			(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+}

+#endif

+

+UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT)

+{

+	UINT8 v = 0;

+

+	/* external memory read */

+	if ( (DELTAT->portstate & 0xe0)==0x20 )

+	{

+		/* two dummy reads */

+		if (DELTAT->memread)

+		{

+			DELTAT->now_addr = DELTAT->start << 1;

+			DELTAT->memread--;

+			return 0;

+		}

+

+

+		if ( DELTAT->now_addr != (DELTAT->end<<1) )

+		{

+			v = DELTAT->memory[DELTAT->now_addr>>1];

+

+			/*logerror("YM Delta-T memory read  $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/

+

+			DELTAT->now_addr+=2; /* two nibbles at a time */

+

+			/* reset BRDY bit in status register, which means we are reading the memory now */

+			if(DELTAT->status_reset_handler)

+				if(DELTAT->status_change_BRDY_bit)

+					(DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+

+	/* setup a timer that will callback us in 10 master clock cycles for Y8950

+    * in the callback set the BRDY flag to 1 , which means we have another data ready.

+    * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.

+    */

+			/* set BRDY bit in status register */

+			if(DELTAT->status_set_handler)

+				if(DELTAT->status_change_BRDY_bit)

+					(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+		}

+		else

+		{

+			/* set EOS bit in status register */

+			if(DELTAT->status_set_handler)

+				if(DELTAT->status_change_EOS_bit)

+					(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);

+		}

+	}

+

+	return v;

+}

+

+

+/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */

+static const UINT8 dram_rightshift[4] ICONST_ATTR ={3,0,0,0};

+

+/* DELTA-T ADPCM write register */

+void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v)

+{

+	if(r>=0x10) return;

+	DELTAT->reg[r] = v; /* stock data */

+

+	switch( r )

+	{

+	case 0x00:

+/*

+START:

+    Accessing *external* memory is started when START bit (D7) is set to "1", so

+    you must set all conditions needed for recording/playback before starting.

+    If you access *CPU-managed* memory, recording/playback starts after

+    read/write of ADPCM data register $08.

+

+REC:

+    0 = ADPCM synthesis (playback)

+    1 = ADPCM analysis (record)

+

+MEMDATA:

+    0 = processor (*CPU-managed*) memory (means: using register $08)

+    1 = external memory (using start/end/limit registers to access memory: RAM or ROM)

+

+

+SPOFF:

+    controls output pin that should disable the speaker while ADPCM analysis

+

+RESET and REPEAT only work with external memory.

+

+

+some examples:

+value:   START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET   meaning:

+  C8     1      1    0       0       1      0 0 0       Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register

+  E8     1      1    1       0       1      0 0 0       Analysis (recording) from AUDIO to EXT.MEMORY,       sample rate in PRESCALER register

+  80     1      0    0       0       0      0 0 0       Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register

+  a0     1      0    1       0       0      0 0 0       Synthesis (playing) from EXT.MEMORY to AUDIO,        sample rate in DELTA-N register

+

+  60     0      1    1       0       0      0 0 0       External memory write via ADPCM data register $08

+  20     0      0    1       0       0      0 0 0       External memory read via ADPCM data register $08

+

+*/

+		/* handle emulation mode */

+		if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610)

+		{

+			v |= 0x20;		/*  YM2610 always uses external memory and doesn't even have memory flag bit. */

+		}

+

+		DELTAT->portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */

+

+		if( DELTAT->portstate&0x80 )/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */

+		{

+			/* set PCM BUSY bit */

+			DELTAT->PCM_BSY = 1;

+

+			/* start ADPCM */

+			DELTAT->now_step = 0;

+			DELTAT->acc      = 0;

+			DELTAT->prev_acc = 0;

+			DELTAT->adpcml   = 0;

+			DELTAT->adpcmd   = YM_DELTAT_DELTA_DEF;

+			DELTAT->now_data = 0;

+

+		}

+

+		if( DELTAT->portstate&0x20 ) /* do we access external memory? */

+		{

+			DELTAT->now_addr = DELTAT->start << 1;

+			DELTAT->memread = 2;	/* two dummy reads needed before accesing external memory via register $08*/

+

+			/* if yes, then let's check if ADPCM memory is mapped and big enough */

+			if(DELTAT->memory == 0)

+			{

+				logerror("YM Delta-T ADPCM rom not mapped\n");

+				DELTAT->portstate = 0x00;

+				DELTAT->PCM_BSY = 0;

+			}

+			else

+			{

+				if( DELTAT->end >= DELTAT->memory_size )	/* Check End in Range */

+				{

+					/* logerror("YM Delta-T ADPCM end out of range: $%08x\n", DELTAT->end); */

+					DELTAT->end = DELTAT->memory_size - 1;

+				}

+				if( DELTAT->start >= DELTAT->memory_size )	/* Check Start in Range */

+				{

+					/* logerror("YM Delta-T ADPCM start out of range: $%08x\n", DELTAT->start); */

+					DELTAT->portstate = 0x00;

+					DELTAT->PCM_BSY = 0;

+				}

+			}

+		}

+		else	/* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */

+		{

+			DELTAT->now_addr = 0;

+		}

+

+		if( DELTAT->portstate&0x01 )

+		{

+			DELTAT->portstate = 0x00;

+

+			/* clear PCM BUSY bit (in status register) */

+			DELTAT->PCM_BSY = 0;

+

+			/* set BRDY flag */

+			if(DELTAT->status_set_handler)

+				if(DELTAT->status_change_BRDY_bit)

+					(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+		}

+		break;

+	case 0x01:	/* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */

+		/* handle emulation mode */

+		if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610)

+		{

+			v |= 0x01;		/*  YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */

+		}

+

+		DELTAT->pan = &DELTAT->output_pointer[(v>>6)&0x03];

+		if ((DELTAT->control2 & 3) != (v & 3))

+		{

+			/*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */

+			if (DELTAT->DRAMportshift != dram_rightshift[v&3])

+			{

+				DELTAT->DRAMportshift = dram_rightshift[v&3];

+

+				/* final shift value depends on chip type and memory type selected:

+                        8 for YM2610 (ROM only),

+                        5 for ROM for Y8950 and YM2608,

+                        5 for x8bit DRAMs for Y8950 and YM2608,

+                        2 for x1bit DRAMs for Y8950 and YM2608.

+                */

+

+				/* refresh addresses */

+				DELTAT->start  = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift);

+				DELTAT->end    = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift);

+				DELTAT->end   += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1;

+				DELTAT->limit  = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift);

+			}

+		}

+		DELTAT->control2 = v;

+		break;

+	case 0x02:	/* Start Address L */

+	case 0x03:	/* Start Address H */

+		DELTAT->start  = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift);

+		/*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",DELTAT->reg[0x2], DELTAT->reg[0x3],DELTAT->start );*/

+		break;

+	case 0x04:	/* Stop Address L */

+	case 0x05:	/* Stop Address H */

+		DELTAT->end    = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift);

+		DELTAT->end   += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1;

+		/*logerror("DELTAT end  : 04=%2x 05=%2x addr=%8x\n",DELTAT->reg[0x4], DELTAT->reg[0x5],DELTAT->end   );*/

+		break;

+	case 0x06:	/* Prescale L (ADPCM and Record frq) */

+	case 0x07:	/* Prescale H */

+		break;

+	case 0x08:	/* ADPCM data */

+

+/*

+some examples:

+value:   START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET   meaning:

+  C8     1      1    0       0       1      0 0 0       Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register

+  E8     1      1    1       0       1      0 0 0       Analysis (recording) from AUDIO to EXT.MEMORY,       sample rate in PRESCALER register

+  80     1      0    0       0       0      0 0 0       Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register

+  a0     1      0    1       0       0      0 0 0       Synthesis (playing) from EXT.MEMORY to AUDIO,        sample rate in DELTA-N register

+

+  60     0      1    1       0       0      0 0 0       External memory write via ADPCM data register $08

+  20     0      0    1       0       0      0 0 0       External memory read via ADPCM data register $08

+

+*/

+

+		/* external memory write */

+		if ( (DELTAT->portstate & 0xe0)==0x60 )

+		{

+			if (DELTAT->memread)

+			{

+				DELTAT->now_addr = DELTAT->start << 1;

+				DELTAT->memread = 0;

+			}

+

+			/*logerror("YM Delta-T memory write $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/

+

+			if ( DELTAT->now_addr != (DELTAT->end<<1) )

+			{

+				DELTAT->memory[DELTAT->now_addr>>1] = v;

+			 	DELTAT->now_addr+=2; /* two nibbles at a time */

+

+				/* reset BRDY bit in status register, which means we are processing the write */

+				if(DELTAT->status_reset_handler)

+					if(DELTAT->status_change_BRDY_bit)

+						(DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+

+	/* setup a timer that will callback us in 10 master clock cycles for Y8950

+    * in the callback set the BRDY flag to 1 , which means we have written the data.

+    * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.

+    */

+				/* set BRDY bit in status register */

+				if(DELTAT->status_set_handler)

+					if(DELTAT->status_change_BRDY_bit)

+						(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+

+			}

+			else

+			{

+				/* set EOS bit in status register */

+				if(DELTAT->status_set_handler)

+					if(DELTAT->status_change_EOS_bit)

+						(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);

+			}

+

+			return;

+		}

+

+		/* ADPCM synthesis from CPU */

+		if ( (DELTAT->portstate & 0xe0)==0x80 )

+		{

+			DELTAT->CPU_data = v;

+

+			/* Reset BRDY bit in status register, which means we are full of data */

+			if(DELTAT->status_reset_handler)

+				if(DELTAT->status_change_BRDY_bit)

+					(DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+			return;

+		}

+

+	  break;

+	case 0x09:	/* DELTA-N L (ADPCM Playback Prescaler) */

+	case 0x0a:	/* DELTA-N H */

+		DELTAT->delta  = (DELTAT->reg[0xa]*0x0100 | DELTAT->reg[0x9]);

+		DELTAT->step     = (UINT32)( (double)(DELTAT->delta /* *(1<<(YM_DELTAT_SHIFT-16)) */ ) * (DELTAT->freqbase) );

+		/*logerror("DELTAT deltan:09=%2x 0a=%2x\n",DELTAT->reg[0x9], DELTAT->reg[0xa]);*/

+		break;

+	case 0x0b:	/* Output level control (volume, linear) */

+		{

+			INT32 oldvol = DELTAT->volume;

+			DELTAT->volume = (v&0xff) * (DELTAT->output_range/256) / YM_DELTAT_DECODE_RANGE;

+/*                              v     *     ((1<<16)>>8)        >>  15;

+*                       thus:   v     *     (1<<8)              >>  15;

+*                       thus: output_range must be (1 << (15+8)) at least

+*                               v     *     ((1<<23)>>8)        >>  15;

+*                               v     *     (1<<15)             >>  15;

+*/

+			/*logerror("DELTAT vol = %2x\n",v&0xff);*/

+			if( oldvol != 0 )

+			{

+				DELTAT->adpcml = (int)((double)DELTAT->adpcml / (double)oldvol * (double)DELTAT->volume);

+			}

+		}

+		break;

+	case 0x0c:	/* Limit Address L */

+	case 0x0d:	/* Limit Address H */

+		DELTAT->limit  = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift);

+		/*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",DELTAT->reg[0xc], DELTAT->reg[0xd],DELTAT->limit );*/

+		break;

+	}

+}

+

+void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode)

+{

+	DELTAT->now_addr  = 0;

+	DELTAT->now_step  = 0;

+	DELTAT->step      = 0;

+	DELTAT->start     = 0;

+	DELTAT->end       = 0;

+	DELTAT->limit     = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */

+	DELTAT->volume    = 0;

+	DELTAT->pan       = &DELTAT->output_pointer[pan];

+	DELTAT->acc       = 0;

+	DELTAT->prev_acc  = 0;

+	DELTAT->adpcmd    = 127;

+	DELTAT->adpcml    = 0;

+	DELTAT->emulation_mode = (UINT8)emulation_mode;

+	DELTAT->portstate = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x20 : 0;

+	DELTAT->control2  = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x01 : 0;	/* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */

+	DELTAT->DRAMportshift = dram_rightshift[DELTAT->control2 & 3];

+

+	/* The flag mask register disables the BRDY after the reset, however

+    ** as soon as the mask is enabled the flag needs to be set. */

+

+	/* set BRDY bit in status register */

+	if(DELTAT->status_set_handler)

+		if(DELTAT->status_change_BRDY_bit)

+			(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+}

+

+#if 0

+void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs)

+{

+	int r;

+

+	/* to keep adpcml */

+	DELTAT->volume = 0;

+	/* update */

+	for(r=1;r<16;r++)

+		YM_DELTAT_ADPCM_Write(DELTAT,r,regs[r]);

+	DELTAT->reg[0] = regs[0];

+

+	/* current rom data */

+	if (DELTAT->memory)

+		DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1) );

+

+}

+void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT)

+{

+#ifdef __STATE_H__

+	state_save_register_device_item(device, 0, DELTAT->portstate);

+	state_save_register_device_item(device, 0, DELTAT->now_addr);

+	state_save_register_device_item(device, 0, DELTAT->now_step);

+	state_save_register_device_item(device, 0, DELTAT->acc);

+	state_save_register_device_item(device, 0, DELTAT->prev_acc);

+	state_save_register_device_item(device, 0, DELTAT->adpcmd);

+	state_save_register_device_item(device, 0, DELTAT->adpcml);

+#endif

+}

+#endif

+

+

+#define YM_DELTAT_Limit(val,max,min)	\

+{										\

+	if ( val > max ) val = max;			\

+	else if ( val < min ) val = min;	\

+}

+

+static INLINE void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT)

+{

+	UINT32 step;

+	int data;

+

+	DELTAT->now_step += DELTAT->step;

+	if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )

+	{

+		step = DELTAT->now_step >> YM_DELTAT_SHIFT;

+		DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;

+		do{

+

+			if ( DELTAT->now_addr == (DELTAT->limit<<1) )

+				DELTAT->now_addr = 0;

+

+			if ( DELTAT->now_addr == (DELTAT->end<<1) ) {	/* 12-06-2001 JB: corrected comparison. Was > instead of == */

+				if( DELTAT->portstate&0x10 ){

+					/* repeat start */

+					DELTAT->now_addr = DELTAT->start<<1;

+					DELTAT->acc      = 0;

+					DELTAT->adpcmd   = YM_DELTAT_DELTA_DEF;

+					DELTAT->prev_acc = 0;

+				}else{

+					/* set EOS bit in status register */

+					if(DELTAT->status_set_handler)

+						if(DELTAT->status_change_EOS_bit)

+							(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);

+

+					/* clear PCM BUSY bit (reflected in status register) */

+					DELTAT->PCM_BSY = 0;

+

+					DELTAT->portstate = 0;

+					DELTAT->adpcml = 0;

+					DELTAT->prev_acc = 0;

+					return;

+				}

+			}

+

+			if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f;

+			else

+			{

+				DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1));

+				data = DELTAT->now_data >> 4;

+			}

+

+			DELTAT->now_addr++;

+			/* 12-06-2001 JB: */

+			/* YM2610 address register is 24 bits wide.*/

+			/* The "+1" is there because we use 1 bit more for nibble calculations.*/

+			/* WARNING: */

+			/* Side effect: we should take the size of the mapped ROM into account */

+			DELTAT->now_addr &= ( (1<<(24+1))-1);

+

+			/* store accumulator value */

+			DELTAT->prev_acc = DELTAT->acc;

+

+			/* Forecast to next Forecast */

+			DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);

+			YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);

+

+			/* delta to next delta */

+			DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;

+			YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );

+

+			/* ElSemi: Fix interpolator. */

+			/*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/

+

+		}while(--step);

+

+	}

+

+	/* ElSemi: Fix interpolator. */

+	DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);

+	DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);

+	DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;

+

+	/* output for work of output channels (outd[OPNxxxx])*/

+	*(DELTAT->pan) += DELTAT->adpcml;

+}

+

+

+

+static INLINE void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT)

+{

+	UINT32 step;

+	int data;

+

+	DELTAT->now_step += DELTAT->step;

+	if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )

+	{

+		step = DELTAT->now_step >> YM_DELTAT_SHIFT;

+		DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;

+		do{

+

+			if( DELTAT->now_addr&1 )

+			{

+				data = DELTAT->now_data & 0x0f;

+

+				DELTAT->now_data = DELTAT->CPU_data;

+

+				/* after we used CPU_data, we set BRDY bit in status register,

+                * which means we are ready to accept another byte of data */

+				if(DELTAT->status_set_handler)

+					if(DELTAT->status_change_BRDY_bit)

+						(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);

+			}

+			else

+			{

+				data = DELTAT->now_data >> 4;

+			}

+

+			DELTAT->now_addr++;

+

+			/* store accumulator value */

+			DELTAT->prev_acc = DELTAT->acc;

+

+			/* Forecast to next Forecast */

+			DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);

+			YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);

+

+			/* delta to next delta */

+			DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;

+			YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );

+

+

+		}while(--step);

+

+	}

+

+	/* ElSemi: Fix interpolator. */

+	DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);

+	DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);

+	DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;

+

+	/* output for work of output channels (outd[OPNxxxx])*/

+	*(DELTAT->pan) += DELTAT->adpcml;

+}

+

+

+

+/* ADPCM B (Delta-T control type) */

+void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT)

+{

+

+/*

+some examples:

+value:   START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET   meaning:

+  80     1      0    0       0       0      0 0 0       Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register

+  a0     1      0    1       0       0      0 0 0       Synthesis (playing) from EXT.MEMORY to AUDIO,        sample rate in DELTA-N register

+  C8     1      1    0       0       1      0 0 0       Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register

+  E8     1      1    1       0       1      0 0 0       Analysis (recording) from AUDIO to EXT.MEMORY,       sample rate in PRESCALER register

+

+  60     0      1    1       0       0      0 0 0       External memory write via ADPCM data register $08

+  20     0      0    1       0       0      0 0 0       External memory read via ADPCM data register $08

+

+*/

+

+	if ( (DELTAT->portstate & 0xe0)==0xa0 )

+	{

+		YM_DELTAT_synthesis_from_external_memory(DELTAT);

+		return;

+	}

+

+	if ( (DELTAT->portstate & 0xe0)==0x80 )

+	{

+		/* ADPCM synthesis from CPU-managed memory (from reg $08) */

+		YM_DELTAT_synthesis_from_CPU_memory(DELTAT);	/* change output based on data in ADPCM data reg ($08) */

+		return;

+	}

+

+//todo: ADPCM analysis

+//  if ( (DELTAT->portstate & 0xe0)==0xc0 )

+//  if ( (DELTAT->portstate & 0xe0)==0xe0 )

+

+	return;

+}

+

diff --git a/apps/codecs/libgme/ymdeltat.h b/apps/codecs/libgme/ymdeltat.h
new file mode 100644
index 0000000..01af499
--- /dev/null
+++ b/apps/codecs/libgme/ymdeltat.h
@@ -0,0 +1,100 @@
+#pragma once

+

+#ifndef __YMDELTAT_H__

+#define __YMDELTAT_H__

+

+#include "blargg_common.h"

+

+/* compiler dependence */

+#ifndef __OSDCOMM_H__

+#define __OSDCOMM_H__

+typedef unsigned char	UINT8;   /* unsigned  8bit */

+typedef unsigned short	UINT16;  /* unsigned 16bit */

+typedef unsigned int	UINT32;  /* unsigned 32bit */

+typedef signed char		INT8;    /* signed  8bit   */

+typedef signed short	INT16;   /* signed 16bit   */

+typedef signed int		INT32;   /* signed 32bit   */

+

+typedef INT32 stream_sample_t;

+

+#endif /* __OSDCOMM_H__ */

+

+#define YM_DELTAT_SHIFT    (16)

+

+#define YM_DELTAT_EMULATION_MODE_NORMAL	0

+#define YM_DELTAT_EMULATION_MODE_YM2610	1

+

+

+typedef void (*STATUS_CHANGE_HANDLER)(void *chip, UINT8 status_bits);

+

+

+/* DELTA-T (adpcm type B) struct */

+typedef struct deltat_adpcm_state {     /* AT: rearranged and tigntened structure */

+	UINT8	*memory;

+	INT32	*output_pointer;/* pointer of output pointers   */

+	INT32	*pan;			/* pan : &output_pointer[pan]   */

+	double	freqbase;

+#if 0

+	double	write_time;		/* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */

+	double	read_time;		/* Y8950: 8 cycles of main clock;  YM2608: 18 cycles of main clock */

+#endif

+	UINT32	memory_size;

+	int		output_range;

+	UINT32	now_addr;		/* current address      */

+	UINT32	now_step;		/* currect step         */

+	UINT32	step;			/* step                 */

+	UINT32	start;			/* start address        */

+	UINT32	limit;			/* limit address        */

+	UINT32	end;			/* end address          */

+	UINT32	delta;			/* delta scale          */

+	INT32	volume;			/* current volume       */

+	INT32	acc;			/* shift Measurement value*/

+	INT32	adpcmd;			/* next Forecast        */

+	INT32	adpcml;			/* current value        */

+	INT32	prev_acc;		/* leveling value       */

+	UINT8	now_data;		/* current rom data     */

+	UINT8	CPU_data;		/* current data from reg 08 */

+	UINT8	portstate;		/* port status          */

+	UINT8	control2;		/* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */

+	UINT8	portshift;		/* address bits shift-left:

+                            ** 8 for YM2610,

+                            ** 5 for Y8950 and YM2608 */

+

+	UINT8	DRAMportshift;	/* address bits shift-right:

+                            ** 0 for ROM and x8bit DRAMs,

+                            ** 3 for x1 DRAMs */

+

+	UINT8	memread;		/* needed for reading/writing external memory */

+

+	/* handlers and parameters for the status flags support */

+	STATUS_CHANGE_HANDLER	status_set_handler;

+	STATUS_CHANGE_HANDLER	status_reset_handler;

+

+	/* note that different chips have these flags on different

+    ** bits of the status register

+    */

+	void *	status_change_which_chip;	/* this chip id */

+	UINT8	status_change_EOS_bit;		/* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/

+	UINT8	status_change_BRDY_bit;		/* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */

+	UINT8	status_change_ZERO_bit;		/* 1 if silence lasts for more than 290 miliseconds on ADPCM recording */

+

+	/* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above,

+    ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608)

+    */

+	UINT8	PCM_BSY;		/* 1 when ADPCM is playing; Y8950/YM2608 only */

+

+	UINT8	reg[16];		/* adpcm registers      */

+	UINT8	emulation_mode;	/* which chip we're emulating */

+}YM_DELTAT;

+

+/*void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT);*/

+

+UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT) ICODE_ATTR;

+void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v) ICODE_ATTR;

+void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode);

+void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT) ICODE_ATTR;

+

+/*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs);

+void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT);*/

+

+#endif /* __YMDELTAT_H__ */

diff --git a/apps/codecs/libgme/ymtables.h b/apps/codecs/libgme/ymtables.h
new file mode 100644
index 0000000..4e8f62a
--- /dev/null
+++ b/apps/codecs/libgme/ymtables.h
@@ -0,0 +1,559 @@
+#ifndef _EMUTABLES_H_

+#define _EMUTABLES_H_

+

+/* Precompiled ym2612 tables for use in Rockbox */

+   

+static const int tl_coeff[] ICONST_ATTR = {

+	 268435455,  267712100,  266990695,  266271234,  265553712,  264838123,  264124462,  263412725,  262702906,  261994999,  261289000, 

+     260584903,  259882704,  259182396,  258483976,  257787438,  257092777,  256399988,  255709066,  255020006,  254332802,  253647450, 

+     252963945,  252282282,  251602456,  250924462,  250248294,  249573949,  248901421,  248230705,  247561797,  246894691,  246229383, 

+     245565867,  244904140,  244244195,  243586029,  242929637,  242275013,  241622154,  240971053,  240321708,  239674112,  239028261, 

+     238384150,  237741775,  237101131,  236462214,  235825018,  235189539,  234555773,  233923714,  233293359,  232664702,  232037740, 

+     231412466,  230788878,  230166970,  229546738,  228928178,  228311284,  227696052,  227082479,  226470558,  225860287,  225251660, 

+     224644674,  224039323,  223435603,  222833510,  222233039,  221634187,  221036948,  220441319,  219847295,  219254871,  218664044, 

+     218074809,  217487162,  216901098,  216316614,  215733704,  215152366,  214572594,  213994384,  213417732,  212842635,  212269087, 

+     211697084,  211126623,  210557699,  209990308,  209424446,  208860109,  208297293,  207735993,  207176206,  206617927,  206061153, 

+     205505879,  204952102,  204399816,  203849019,  203299706,  202751873,  202205517,  201660633,  201117217,  200575266,  200034774, 

+     199495740,  198958158,  198422024,  197887335,  197354088,  196822277,  196291899,  195762950,  195235427,  194709325,  194184641, 

+     193661370,  193139510,  192619056,  192100005,  191582352,  191066094,  190551228,  190037748,  189525653,  189014937,  188505598, 

+     187997631,  187491033,  186985800,  186481928,  185979414,  185478255,  184978446,  184479983,  183982864,  183487085,  182992641, 

+     182499530,  182007748,  181517291,  181028155,  180540338,  180053835,  179568643,  179084759,  178602178,  178120898,  177640915, 

+     177162225,  176684825,  176208712,  175733881,  175260330,  174788055,  174317053,  173847320,  173378853,  172911648,  172445702, 

+     171981012,  171517574,  171055385,  170594441,  170134740,  169676277,  169219049,  168763054,  168308287,  167854746,  167402427, 

+     166951327,  166501443,  166052770,  165605307,  165159050,  164713995,  164270139,  163827480,  163386013,  162945736,  162506646, 

+     162068738,  161632011,  161196460,  160762083,  160328877,  159896838,  159465963,  159036250,  158607694,  158180293,  157754044, 

+     157328943,  156904988,  156482176,  156060502,  155639965,  155220562,  154802288,  154385142,  153969119,  153554218,  153140435, 

+     152727766,  152316210,  151905763,  151496422,  151088184,  150681046,  150275005,  149870058,  149466203,  149063435,  148661753, 

+     148261154,  147861634,  147463190,  147065821,  146669522,  146274291,  145880125,  145487021,  145094976,  144703988,  144314054, 

+     143925170,  143537334,  143150543,  142764795,  142380086,  141996414,  141613775,  141232168,  140851589,  140472035,  140093505, 

+     139715994,  139339501,  138964022,  138589555,  138216097,  137843646,  137472198,  137101751,  136732302,  136363849,  135996388, 

+     135629918,  135264436,  134899938,  134536423,  134173887,  133812328,  133451743,  133092130,  132733486,  132375808,  132019095, 

+     131663342,  131308548,  130954711,  130601826,  130249893,  129898908,  129548869,  129199773,  128851618,  128504401,  128158119, 

+     127812771,  127468353,  127124864,  126782300,  126440659,  126099939,  125760137,  125421250,  125083277,  124746214,  124410060, 

+     124074812,  123740467,  123407023,  123074477,  122742828,  122412072,  122082208,  121753232,  121425143,  121097939,  120771615, 

+     120446172,  120121605,  119797912,  119475092,  119153142,  118832060,  118511843,  118192488,  117873994,  117556359,  117239579, 

+     116923653,  116608578,  116294353,  115980974,  115668439,  115356747,  115045894,  114735880,  114426700,  114118354,  113810839, 

+     113504152,  113198292,  112893256,  112589042,  112285648,  111983071,  111681310,  111380362,  111080225,  110780896,  110482375, 

+     110184657,  109887742,  109591627,  109296310,  109001789,  108708061,  108415125,  108122978,  107831619,  107541044,  107251253, 

+     106962243,  106674011,  106386556,  106099876,  105813968,  105528830,  105244461,  104960859,  104678020,  104395944,  104114628, 

+     103834069,  103554267,  103275219,  102996923,  102719377,  102442578,  102166526,  101891217,  101616650,  101342823,  101069734, 

+     100797381,  100525762,  100254875,   99984718,   99715288,   99446585,   99178606,   98911349,   98644812,   98378993,   98113891, 

+      97849503,   97585828,   97322863,   97060606,   96799057,   96538212,   96278070,   96018629,   95759887,   95501842,   95244493, 

+      94987837,   94731873,   94476599,   94222012,   93968112,   93714895,   93462361,   93210508,   92959333,   92708835,   92459012, 

+      92209863,   91961384,   91713575,   91466434,   91219959,   90974149,   90729000,   90484512,   90240683,   89997511,   89754994, 

+      89513131,   89271920,   89031358,   88791445,   88552178,   88313556,   88075578,   87838240,   87601542,   87365481,   87130057, 

+      86895267,   86661110,   86427584,   86194687,   85962418,   85730775,   85499756,   85269359,   85039583,   84810427,   84581888, 

+      84353965,   84126656,   83899959,   83673874,   83448397,   83223528,   82999266,   82775607,   82552551,   82330096,   82108241, 

+      81886984,   81666322,   81446256,   81226782,   81007900,   80789608,   80571904,   80354786,   80138254,   79922305,   79706938, 

+      79492151,   79277943,   79064313,   78851258,   78638777,   78426868,   78215531,   78004763,   77794564,   77584930,   77375862, 

+      77167357,   76959413,   76752031,   76545207,   76338940,   76133229,   75928072,   75723469,   75519416,   75315914,   75112960, 

+      74910552,   74708690,   74507373,   74306597,   74106363,   73906668,   73707512,   73508892,   73310807,   73113256,   72916237, 

+      72719749,   72523791,   72328361,   72133457,   71939079,   71745225,   71551892,   71359081,   71166789,   70975016,   70783759, 

+      70593018,   70402791,   70213076,   70023872,   69835179,   69646994,   69459315,   69272143,   69085475,   68899310,   68713647, 

+      68528484,   68343820,   68159653,   67975983,   67792808,   67610127,   67427937,   67246239,   67065030,   66884310,   66704076, 

+      66524328,   66345065,   66166285,   65987986,   65810168,   65632829,   65455968,   65279583,   65103674,   64928239,   64753277, 

+      64578786,   64404765,   64231213,   64058129,   63885511,   63713359,   63541670,   63370444,   63199679,   63029375,   62859529, 

+      62690141,   62521210,   62352734,   62184711,   62017142,   61850024,   61683357,   61517138,   61351368,   61186044,   61021166, 

+      60856731,   60692741,   60529192,   60366083,   60203414,   60041184,   59879391,   59718034,   59557111,   59396622,   59236566, 

+      59076941,   58917746,   58758980,   58600642,   58442730,   58285245,   58128183,   57971545,   57815329,   57659533,   57504158, 

+      57349201,   57194662,   57040539,   56886832,   56733539,   56580659,   56428190,   56276133,   56124486,   55973247,   55822415, 

+      55671990,   55521971,   55372355,   55223143,   55074333,   54925924,   54777915,   54630305,   54483092,   54336276,   54189856, 

+      54043830,   53898198,   53752959,   53608110,   53463652,   53319583,   53175903,   53032610,   52889702,   52747180,   52605042, 

+      52463287,   52321914,   52180922,   52040310,   51900076,   51760221,   51620743,   51481640,   51342912,   51204558,   51066577, 

+      50928968,   50791729,   50654860,   50518360,   50382228,   50246463,   50111064,   49976029,   49841359,   49707051,   49573105, 

+      49439520,   49306295,   49173429,   49040922,   48908771,   48776976,   48645537,   48514451,   48383719,   48253339,   48123311, 

+      47993633,   47864304,   47735324,   47606691,   47478405,   47350465,   47222869,   47095618,   46968709,   46842142,   46715916, 

+      46590031,   46464484,   46339276,   46214406,   46089871,   45965673,   45841809,   45718279,   45595082,   45472216,   45349682, 

+      45227478,   45105603,   44984057,   44862838,   44741946,   44621380,   44501139,   44381221,   44261627,   44142355,   44023404, 

+      43904774,   43786464,   43668472,   43550798,   43433442,   43316402,   43199677,   43083266,   42967170,   42851386,   42735914, 

+      42620753,   42505903,   42391362,   42277130,   42163206,   42049588,   41936277,   41823271,   41710570,   41598172,   41486077, 

+      41374285,   41262793,   41151602,   41040711,   40930118,   40819823,   40709826,   40600125,   40490720,   40381609,   40272793, 

+      40164269,   40056039,   39948099,   39840451,   39733093,   39626024,   39519243,   39412751,   39306545,   39200625,   39094991, 

+      38989642,   38884576,   38779794,   38675294,   38571075,   38467138,   38363480,   38260102,   38157002,   38054180,   37951635, 

+      37849367,   37747374,   37645656,   37544212,   37443042,   37342144,   37241518,   37141163,   37041078,   36941264,   36841718, 

+      36742440,   36643430,   36544687,   36446210,   36347998,   36250051,   36152368,   36054948,   35957790,   35860895,   35764260, 

+      35667886,   35571772,   35475916,   35380319,   35284980,   35189897,   35095071,   35000500,   34906184,   34812122,   34718314, 

+      34624758,   34531454,   34438402,   34345601,   34253050,   34160748,   34068695,   33976890,   33885332,   33794021,   33702956, 

+      33612137,   33521562,   33431231,   33341144,   33251299,   33161697,   33072336,   32983216,   32894336,   32805695,   32717294, 

+      32629130,   32541204,   32453515,   32366063,   32278846,   32191864,   32105116,   32018602,   31932322,   31846273,   31760457, 

+      31674872,   31589518,   31504393,   31419498,   31334832,   31250394,   31166183,   31082200,   30998442,   30914911,   30831604, 

+      30748522,   30665664,   30583029,   30500617,   30418426,   30336458,   30254710,   30173183,   30091875,   30010786,   29929916, 

+      29849263,   29768829,   29688610,   29608608,   29528822,   29449250,   29369893,   29290750,   29211820,   29133103,   29054598, 

+      28976304,   28898222,   28820350,   28742687,   28665234,   28587990,   28510954,   28434125,   28357503,   28281088,   28204879, 

+      28128875,   28053076,   27977482,   27902091,   27826903,   27751917,   27677134,   27602552,   27528172,   27453991,   27380011, 

+      27306230,   27232648,   27159264,   27086078,   27013089,   26940296,   26867700,   26795300,   26723094,   26651083,   26579267, 

+      26507643,   26436213,   26364975,   26293929,   26223075,   26152412,   26081939,   26011656,   25941562,   25871657,   25801940, 

+      25732412,   25663071,   25593916,   25524948,   25456166,   25387569,   25319157,   25250929,   25182886,   25115025,   25047348, 

+      24979852,   24912539,   24845407,   24778456,   24711686,   24645095,   24578684,   24512451,   24446397,   24380522,   24314823, 

+      24249302,   24183957,   24118789,   24053796,   23988978,   23924335,   23859866,   23795570,   23731448,   23667499,   23603722, 

+      23540117,   23476683,   23413421,   23350328,   23287406,   23224653,   23162070,   23099655,   23037408,   22975329,   22913417, 

+      22851673,   22790094,   22728681,   22667434,   22606352,   22545435,   22484682,   22424092,   22363666,   22303402,   22243301, 

+      22183362,   22123584,   22063968,   22004512,   21945216,   21886080,   21827104,   21768286,   21709627,   21651126,   21592783, 

+      21534597,   21476567,   21418694,   21360977,   21303416,   21246009,   21188758,   21131660,   21074717,   21017926,   20961289, 

+      20904805,   20848473,   20792292,   20736263,   20680385,   20624657,   20569080,   20513652,   20458374,   20403245,   20348264, 

+      20293432,   20238747,   20184209,   20129819,   20075575,   20021477,   19967525,   19913719,   19860057,   19806540,   19753167, 

+      19699938,   19646853,   19593910,   19541111,   19488453,   19435937,   19383563,   19331330,   19279238,   19227286,   19175474, 

+      19123802,   19072269,   19020875,   18969619,   18918502,   18867522,   18816680,   18765974,   18715405,   18664973,   18614676, 

+      18564515,   18514489,   18464598,   18414842,   18365219,   18315730,   18266375,   18217152,   18168062,   18119105,   18070279, 

+      18021585,   17973022,   17924590,   17876289,   17828118,   17780076,   17732164,   17684381,   17636727,   17589201,   17541803, 

+      17494533,   17447391,   17400375,   17353486,   17306724,   17260087,   17213577,   17167191,   17120930,   17074795,   17028783, 

+      16982896,   16937132,   16891491,   16845974,   16800579,   16755306,   16710155,   16665126,   16620219,   16575432,   16530766, 

+      16486221,   16441795,   16397490,   16353303,   16309236,   16265287,   16221457,   16177745,   16134151,   16090674,   16047314, 

+      16004072,   15960945,   15917935,   15875041,   15832263,   15789599,   15747051,   15704617,   15662298,   15620093,   15578001, 

+      15536023,   15494158,   15452406,   15410766,   15369239,   15327823,   15286519,   15245327,   15204245,   15163274,   15122414, 

+      15081663,   15041023,   15000491,   14960070,   14919757,   14879552,   14839456,   14799468,   14759588,   14719815,   14680150, 

+      14640591,   14601139,   14561793,   14522554,   14483420,   14444391,   14405468,   14366649,   14327935,   14289326,   14250820, 

+      14212418,   14174120,   14135925,   14097833,   14059843,   14021956,   13984171,   13946488,   13908906,   13871426,   13834047, 

+      13796768,   13759590,   13722512,   13685534,   13648655,   13611876,   13575196,   13538615,   13502132,   13465748,   13429462, 

+      13393273,   13357183,   13321189,   13285292,   13249492,   13213789,   13178182,   13142670,   13107255,   13071934,   13036709, 

+      13001579,   12966544,   12931603,   12896756,   12862003,   12827344,   12792778,   12758305,   12723925,   12689638,   12655443, 

+      12621341,   12587330,   12553411,   12519583,   12485846,   12452201,   12418646,   12385181,   12351807,   12318522,   12285327, 

+      12252222,   12219206,   12186279,   12153440,   12120690,   12088029,   12055455,   12022969,   11990571,   11958260,   11926036, 

+      11893899,   11861848,   11829884,   11798006,   11766214,   11734507,   11702886,   11671350,   11639900,   11608533,   11577252, 

+      11546055,   11514941,   11483912,   11452966,   11422104,   11391325,   11360628,   11330015,   11299484,   11269035,   11238668, 

+      11208384,   11178180,   11148058,   11118018,   11088058,   11058179,   11028380,   10998662,   10969024,   10939466,   10909987, 

+      10880588,   10851268,   10822027,   10792865,   10763781,   10734776,   10705849,   10677000,   10648228,   10619535,   10590918, 

+      10562379,   10533916,   10505530,   10477221,   10448988,   10420831,   10392750,   10364745,   10336815,   10308960,   10281180, 

+      10253476,   10225846,   10198290,   10170809,   10143401,   10116068,   10088808,   10061622,   10034509,   10007468,    9980501, 

+       9953607,    9926785,    9900035,    9873357,    9846752,    9820217,    9793755,    9767364,    9741043,    9714794,    9688616, 

+       9662508,    9636470,    9610503,    9584605,    9558778,    9533019,    9507331,    9481711,    9456161,    9430679,    9405266, 

+       9379922,    9354646,    9329438,    9304298,    9279225,    9254221,    9229283,    9204413,    9179610,    9154874,    9130204, 

+       9105601,    9081064,    9056593,    9032188,    9007849,    8983576,    8959368,    8935225,    8911147,    8887134,    8863186, 

+       8839302,    8815483,    8791728,    8768037,    8744409,    8720846,    8697346,    8673909,    8650535,    8627225,    8603977, 

+       8580792,    8557669,    8534608,    8511610,    8488674,    8465799,    8442987,    8420235,    8397545,    8374916,    8352348, 

+       8329841,    8307395,    8285009,    8262683,    8240418,    8218212,    8196067,    8173981,    8151954,    8129987,    8108079, 

+       8086230,    8064440,    8042709,    8021036,    7999422,    7977866,    7956368,    7934928,    7913545,    7892221,    7870954, 

+       7849744,    7828591,    7807495,    7786456,    7765474,    7744548,    7723679,    7702866,    7682109,    7661408,    7640763, 

+       7620173,    7599639,    7579160,    7558737,    7538368,    7518055,    7497796,    7477591,    7457441,    7437346,    7417304, 

+       7397317,    7377383,    7357503,    7337677,    7317904,    7298185,    7278518,    7258905,    7239344,    7219836,    7200381, 

+       7180978,    7161627,    7142329,    7123082,    7103888,    7084745,    7065654,    7046614,    7027625,    7008688,    6989802, 

+       6970966,    6952181,    6933447,    6914764,    6896130,    6877547,    6859014,    6840531,    6822098,    6803715,    6785381, 

+       6767096,    6748861,    6730675,    6712537,    6694449,    6676410,    6658419,    6640476,    6622582,    6604736,    6586938, 

+       6569188,    6551486,    6533832,    6516225,    6498666,    6481154,    6463689,    6446272,    6428901,    6411577,    6394299, 

+       6377069,    6359884,    6342746,    6325655,    6308609,    6291609,    6274655,    6257747,    6240884,    6224066,    6207294, 

+       6190568,    6173886,    6157249,    6140657,    6124110,    6107607,    6091149,    6074735,    6058365,    6042040,    6025758, 

+       6009521,    5993327,    5977177,    5961070,    5945007,    5928987,    5913010,    5897076,    5881185,    5865337,    5849532, 

+       5833769,    5818049,    5802371,    5786735,    5771141,    5755590,    5740080,    5724612,    5709186,    5693802,    5678459, 

+       5663157,    5647896,    5632677,    5617498,    5602361,    5587264,    5572208,    5557193,    5542218,    5527283,    5512389, 

+       5497534,    5482720,    5467946,    5453211,    5438517,    5423861,    5409246,    5394669,    5380132,    5365635,    5351176, 

+       5336756,    5322375,    5308033,    5293729,    5279464,    5265237,    5251049,    5236899,    5222787,    5208713,    5194677, 

+       5180679,    5166719,    5152796,    5138911,    5125063,    5111252,    5097479,    5083743,    5070044,    5056382,    5042756, 

+       5029167,    5015615,    5002100,    4988620,    4975178,    4961771,    4948400,    4935066,    4921767,    4908505,    4895278, 

+       4882086,    4868931,    4855810,    4842725,    4829676,    4816661,    4803682,    4790737,    4777827,    4764953,    4752112, 

+       4739307,    4726536,    4713799,    4701097,    4688429,    4675795,    4663195,    4650629,    4638097,    4625599,    4613134, 

+       4600703,    4588306,    4575941,    4563611,    4551313,    4539049,    4526817,    4514619,    4502453,    4490320,    4478220, 

+       4466153,    4454118,    4442115,    4430145,    4418207,    4406301,    4394428,    4382586,    4370776,    4358998,    4347252, 

+       4335538,    4323855,    4312203,    4300583,    4288994,    4277437,    4265910,    4254415,    4242950,    4231517,    4220114, 

+       4208742,    4197401,    4186090,    4174810,    4163560,    4152340,    4141151,    4129992,    4118863,    4107764,    4096694, 

+       4085655,    4074645,    4063665,    4052715,    4041794,    4030903,    4020041,    4009208,    3998404,    3987630,    3976884, 

+       3966168,    3955480,    3944821,    3934191,    3923590,    3913017,    3902472,    3891956,    3881469,    3871009,    3860578, 

+       3850175,    3839800,    3829453,    3819133,    3808842,    3798578,    3788342,    3778134,    3767953,    3757799,    3747673, 

+       3737574,    3727503,    3717458,    3707441,    3697450,    3687487,    3677550,    3667640,    3657757,    3647900,    3638070, 

+       3628267,    3618490,    3608739,    3599014,    3589316,    3579644,    3569998,    3560378,    3550783,    3541215,    3531673, 

+       3522156,    3512665,    3503199,    3493759,    3484344,    3474955,    3465591,    3456252,    3446939,    3437650,    3428387, 

+       3419148,    3409935,    3400746,    3391582,    3382443,    3373328,    3364238,    3355172,    3346131,    3337114,    3328122, 

+       3319153,    3310209,    3301289,    3292393,    3283521,    3274673,    3265849,    3257048,    3248271,    3239518,    3230789, 

+       3222083,    3213400,    3204741,    3196105,    3187493,    3178903,    3170337,    3161794,    3153274,    3144777,    3136302, 

+       3127851,    3119422,    3111016,    3102633,    3094272,    3085934,    3077619,    3069325,    3061054,    3052806,    3044579, 

+       3036375,    3028193,    3020033,    3011895,    3003779,    2995684,    2987612,    2979561,    2971532,    2963525,    2955539, 

+       2947575,    2939632,    2931710,    2923810,    2915931,    2908074,    2900237,    2892422,    2884628,    2876855,    2869102, 

+       2861371,    2853660,    2845971,    2838302,    2830653,    2823025,    2815418,    2807832,    2800265,    2792719,    2785194, 

+       2777689,    2770203,    2762739,    2755294,    2747869,    2740464,    2733080,    2725715,    2718370,    2711045,    2703739, 

+       2696453,    2689187,    2681941,    2674714,    2667506,    2660318,    2653149,    2646000,    2638870,    2631759,    2624667, 

+       2617594,    2610540,    2603506,    2596490,    2589493,    2582515,    2575556,    2568616,    2561694,    2554791,    2547907, 

+       2541041,    2534194,    2527365,    2520554,    2513762,    2506988,    2500233,    2493495,    2486776,    2480075,    2473392, 

+       2466727,    2460080,    2453450,    2446839,    2440246,    2433670,    2427112,    2420571,    2414049,    2407544,    2401056, 

+       2394586,    2388133,    2381698,    2375280,    2368879,    2362496,    2356130,    2349780,    2343448,    2337134,    2330836, 

+       2324555,    2318291,    2312044,    2305813,    2299600,    2293403,    2287223,    2281060,    2274913,    2268783,    2262669, 

+       2256572,    2250491,    2244427,    2238379,    2232347,    2226331,    2220332,    2214349,    2208382,    2202431,    2196496, 

+       2190577,    2184674,    2178787,    2172916,    2167060,    2161221,    2155397,    2149589,    2143796,    2138019,    2132258, 

+       2126512,    2120782,    2115067,    2109368,    2103683,    2098015,    2092361,    2086723,    2081100,    2075492,    2069899, 

+       2064321,    2058758,    2053211,    2047678,    2042160,    2036657,    2031169,    2025695,    2020237,    2014793,    2009364, 

+       2003949,    1998549,    1993163,    1987792,    1982436,    1977094,    1971766,    1966453,    1961154,    1955869,    1950599, 

+       1945342,    1940100,    1934872,    1929658,    1924458,    1919272,    1914101,    1908943,    1903799,    1898668,    1893552, 

+       1888450,    1883361,    1878286,    1873224,    1868176,    1863142,    1858122,    1853115,    1848121,    1843141,    1838174, 

+       1833221,    1828281,    1823354,    1818441,    1813540,    1808654,    1803780,    1798919,    1794072,    1789237,    1784416, 

+       1779607,    1774812,    1770029,    1765259,    1760502,    1755758,    1751027,    1746309,    1741603,    1736910,    1732229, 

+       1727561,    1722906,    1718263,    1713633,    1709015,    1704410,    1699817,    1695237,    1690669,    1686113,    1681569, 

+       1677038,    1672519,    1668012,    1663517,    1659034,    1654564,    1650105,    1645659,    1641224,    1636801,    1632391, 

+       1627992,    1623605,    1619230,    1614866,    1610515,    1606175,    1601847,    1597530,    1593225,    1588932,    1584650, 

+       1580380,    1576122,    1571874,    1567639,    1563414,    1559201,    1555000,    1550810,    1546631,    1542463,    1538306, 

+       1534161,    1530027,    1525904,    1521792,    1517691,    1513602,    1509523,    1505455,    1501399,    1497353,    1493318, 

+       1489294,    1485281,    1481278,    1477287,    1473306,    1469336,    1465376,    1461427,    1457489,    1453562,    1449645, 

+       1445738,    1441843,    1437957,    1434082,    1430218,    1426364,    1422520,    1418687,    1414864,    1411051,    1407249, 

+       1403457,    1399675,    1395903,    1392142,    1388390,    1384649,    1380918,    1377197,    1373486,    1369784,    1366093, 

+       1362412,    1358741,    1355079,    1351428,    1347786,    1344154,    1340532,    1336920,    1333317,    1329724,    1326141, 

+       1322567,    1319004,    1315449,    1311904,    1308369,    1304844,    1301327,    1297821,    1294323,    1290836,    1287357, 

+       1283888,    1280429,    1276978,    1273537,    1270105,    1266683,    1263269,    1259865,    1256470,    1253084,    1249708, 

+       1246340,    1242982,    1239632,    1236292,    1232960,    1229638,    1226324,    1223020,    1219724,    1216437,    1213159, 

+       1209890,    1206630,    1203378,    1200136,    1196902,    1193676,    1190460,    1187252,    1184052,    1180862,    1177680, 

+       1174506,    1171341,    1168185,    1165037,    1161897,    1158767,    1155644,    1152530,    1149424,    1146327,    1143238, 

+       1140157,    1137085,    1134021,    1130965,    1127917,    1124878,    1121846,    1118823,    1115809,    1112802,    1109803, 

+       1106813,    1103830,    1100855,    1097889,    1094931,    1091980,    1089037,    1086103,    1083176,    1080257,    1077346, 

+       1074443,    1071548,    1068660,    1065781,    1062909,    1060044,    1057188,    1054339,    1051498,    1048664,    1045839, 

+       1043020,    1040210,    1037407,    1034611,    1031823,    1029043,    1026270,    1023504,    1020746,    1017996,    1015252, 

+       1012517,    1009788,    1007067,    1004353,    1001647,     998948,     996256,     993571,     990894,     988224,     985561, 

+        982905,     980256,     977615,     974980,     972353,     969733,     967120,     964514,     961915,     959323,     956737, 

+        954159,     951588,     949024,     946467,     943916,     941373,     938836,     936306,     933783,     931267,     928757, 

+        926254,     923758,     921269,     918787,     916311,     913842,     911379,     908923,     906474,     904031,     901595, 

+        899166,     896743,     894326,     891916,     889513,     887116,     884725,     882341,     879963,     877592,     875227, 

+        872869,     870517,     868171,     865831,     863498,     861171,     858851,     856536,     854228,     851926,     849631, 

+        847341,     845058,     842781,     840510,     838245,     835986,     833733,     831487,     829246,     827011,     824783, 

+        822560,     820344,     818133,     815929,     813730,     811537,     809350,     807169,     804994,     802825,     800662, 

+        798504,     796352,     794206,     792066,     789932,     787803,     785680,     783563,     781452,     779346,     777246, 

+        775151,     773062,     770979,     768902,     766830,     764763,     762703,     760647,     758598,     756553,     754515, 

+        752482,     750454,     748432,     746415,     744403,     742397,     740397,     738402,     736412,     734428,     732448, 

+        730475,     728506,     726543,     724585,     722633,     720686,     718744,     716807,     714875,     712949,     711028, 

+        709112,     707201,     705295,     703394,     701499,     699609,     697723,     695843,     693968,     692098,     690233, 

+        688373,     686518,     684668,     682823,     680983,     679148,     677318,     675493,     673673,     671857,     670047, 

+        668241,     666441,     664645,     662854,     661067,     659286,     657510,     655738,     653971,     652208,     650451, 

+        648698,     646950,     645207,     643468,     641734,     640005,     638280,     636560,     634845,     633134,     631428, 

+        629727,     628030,     626337,     624650,     622966,     621288,     619613,     617944,     616279,     614618,     612962, 

+        611310,     609663,     608020,     606381,     604747,     603118,     601492,     599872,     598255,     596643,     595035, 

+        593432,     591833,     590238,     588647,     587061,     585479,     583901,     582328,     580759,     579194,     577633, 

+        576076,     574524,     572976,     571432,     569892,     568356,     566825,     565297,     563774,     562255,     560740, 

+        559229,     557722,     556219,     554720,     553225,     551734,     550248,     548765,     547286,     545811,     544341, 

+        542874,     541411,     539952,     538497,     537046,     535599,     534155,     532716,     531280,     529849,     528421, 

+        526997,     525577,     524161,     522748,     521340,     519935,     518534,     517136,     515743,     514353,     512967, 

+        511585,     510206,     508831,     507460,     506093,     504729,     503369,     502012,     500660,     499310,     497965, 

+        496623,     495285,     493950,     492619,     491292,     489968,     488648,     487331,     486018,     484708,     483402, 

+        482099,     480800,     479504,     478212,     476924,     475638,     474357,     473078,     471804,     470532,     469264, 

+        468000,     466739,     465481,     464227,     462976,     461728,     460484,     459243,     458005,     456771,     455540, 

+        454313,     453089,     451868,     450650,     449436,     448225,     447017,     445812,     444611,     443413,     442218, 

+        441026,     439838,     438653,     437470,     436292,     435116,     433943,     432774,     431608,     430445,     429285, 

+        428128,     426974,     425824,     424676,     423532,     422391,     421252,     420117,     418985,     417856,     416730, 

+        415607,     414487,     413370,     412256,     411146,     410038,     408933,     407831,     406732,     405636,     404543, 

+        403453,     402365,     401281,     400200,     399121,     398046,     396973,     395903,     394837,     393773,     392712, 

+        391653,     390598,     389545,     388496,     387449,     386405,     385363,     384325,     383289,     382257,     381226, 

+        380199,     379175,     378153,     377134,     376118,     375104,     374093,     373085,     372080,     371077,     370077, 

+        369080,     368085,     367094,     366104,     365118,     364134,     363153,     362174,     361198,     360225,     359254, 

+        358286,     357321,     356358,     355397,     354440,     353485,     352532,     351582,     350635,     349690,     348748, 

+        347808,     346871,     345936,     345004,     344074,     343147,     342222,     341300,     340380,     339463,     338548, 

+        337636,     336726,     335819,     334914,     334011,     333111,     332214,     331318,     330426,     329535,     328647, 

+        327762,     326878,     325997,     325119,     324243,     323369,     322498,     321629,     320762,     319898,     319036, 

+        318176,     317319,     316463,     315611,     314760,     313912,     313066,     312222,     311381,     310542,     309705, 

+        308871,     308038,     307208,     306380,     305555,     304731,     303910,     303091,     302275,     301460,     300648, 

+        299838,     299030,     298224,     297420,     296619,     295819,     295022,     294227,     293434,     292644,     291855, 

+        291069,     290284,     289502,     288722,     287944,     287168,     286394,     285622,     284853,     284085,     283320, 

+        282556,     281795,     281035,     280278,     279523,     278770,     278018,     277269,     276522,     275777,     275034, 

+        274293,     273553,     272816,     272081,     271348,     270617,     269888,     269160,     268435,     267712,     266990, 

+        266271,     265553,     264838,     264124,     263412,     262702,     261994,     261289,     260584,     259882,     259182, 

+        258483,     257787,     257092,     256399,     255709,     255020,     254332,     253647,     252963,     252282,     251602, 

+        250924,     250248,     249573,     248901,     248230,     247561,     246894,     246229,     245565,     244904,     244244, 

+        243586,     242929,     242275,     241622,     240971,     240321,     239674,     239028,     238384,     237741,     237101, 

+        236462,     235825,     235189,     234555,     233923,     233293,     232664,     232037,     231412,     230788,     230166, 

+        229546,     228928,     228311,     227696,     227082,     226470,     225860,     225251,     224644,     224039,     223435, 

+        222833,     222233,     221634,     221036,     220441,     219847,     219254,     218664,     218074,     217487,     216901, 

+        216316,     215733,     215152,     214572,     213994,     213417,     212842,     212269,     211697,     211126,     210557, 

+        209990,     209424,     208860,     208297,     207735,     207176,     206617,     206061,     205505,     204952,     204399, 

+        203849,     203299,     202751,     202205,     201660,     201117,     200575,     200034,     199495,     198958,     198422, 

+        197887,     197354,     196822,     196291,     195762,     195235,     194709,     194184,     193661,     193139,     192619, 

+        192100,     191582,     191066,     190551,     190037,     189525,     189014,     188505,     187997,     187491,     186985, 

+        186481,     185979,     185478,     184978,     184479,     183982,     183487,     182992,     182499,     182007,     181517, 

+        181028,     180540,     180053,     179568,     179084,     178602,     178120,     177640,     177162,     176684,     176208, 

+        175733,     175260,     174788,     174317,     173847,     173378,     172911,     172445,     171981,     171517,     171055, 

+        170594,     170134,     169676,     169219,     168763,     168308,     167854,     167402,     166951,     166501,     166052, 

+        165605,     165159,     164713,     164270,     163827,     163386,     162945,     162506,     162068,     161632,     161196, 

+        160762,     160328,     159896,     159465,     159036,     158607,     158180,     157754,     157328,     156904,     156482, 

+        156060,     155639,     155220,     154802,     154385,     153969,     153554,     153140,     152727,     152316,     151905, 

+        151496,     151088,     150681,     150275,     149870,     149466,     149063,     148661,     148261,     147861,     147463, 

+        147065,     146669,     146274,     145880,     145487,     145094,     144703,     144314,     143925,     143537,     143150, 

+        142764,     142380,     141996,     141613,     141232,     140851,     140472,     140093,     139715,     139339,     138964, 

+        138589,     138216,     137843,     137472,     137101,     136732,     136363,     135996,     135629,     135264,     134899, 

+        134536,     134173,     133812,     133451,     133092,     132733,     132375,     132019,     131663,     131308,     130954, 

+        130601,     130249,     129898,     129548,     129199,     128851,     128504,     128158,     127812,     127468,     127124, 

+        126782,     126440,     126099,     125760,     125421,     125083,     124746,     124410,     124074,     123740,     123407, 

+        123074,     122742,     122412,     122082,     121753,     121425,     121097,     120771,     120446,     120121,     119797, 

+        119475,     119153,     118832,     118511,     118192,     117873,     117556,     117239,     116923,     116608,     116294, 

+        115980,     115668,     115356,     115045,     114735,     114426,     114118,     113810,     113504,     113198,     112893, 

+        112589,     112285,     111983,     111681,     111380,     111080,     110780,     110482,     110184,     109887,     109591, 

+        109296,     109001,     108708,     108415,     108122,     107831,     107541,     107251,     106962,     106674,     106386, 

+        106099,     105813,     105528,     105244,     104960,     104678,     104395,     104114,     103834,     103554,     103275, 

+        102996,     102719,     102442,     102166,     101891,     101616,     101342,     101069,     100797,     100525,     100254, 

+         99984,      99715,      99446,      99178,      98911,      98644,      98378,      98113,      97849,      97585,      97322, 

+         97060,      96799,      96538,      96278,      96018,      95759,      95501,      95244,      94987,      94731,      94476, 

+         94222,      93968,      93714,      93462,      93210,      92959,      92708,      92459,      92209,      91961,      91713, 

+         91466,      91219,      90974,      90729,      90484,      90240,      89997,      89754,      89513,      89271,      89031, 

+         88791,      88552,      88313,      88075,      87838,      87601,      87365,      87130,      86895,      86661,      86427, 

+         86194,      85962,      85730,      85499,      85269,      85039,      84810,      84581,      84353,      84126,      83899, 

+         83673,      83448,      83223,      82999,      82775,      82552,      82330,      82108,      81886,      81666,      81446, 

+         81226,      81007,      80789,      80571,      80354,      80138,      79922,      79706,      79492,      79277,      79064, 

+         78851,      78638,      78426,      78215,      78004,      77794,      77584,      77375,      77167,      76959,      76752, 

+         76545,      76338,      76133,      75928,      75723,      75519,      75315,      75112,      74910,      74708,      74507, 

+         74306,      74106,      73906,      73707,      73508,      73310,      73113,      72916,      72719,      72523,      72328, 

+         72133,      71939,      71745,      71551,      71359,      71166,      70975,      70783,      70593,      70402,      70213, 

+         70023,      69835,      69646,      69459,      69272,      69085,      68899,      68713,      68528,      68343,      68159, 

+         67975,      67792,      67610,      67427,      67246,      67065,      66884,      66704,      66524,      66345,      66166, 

+         65987,      65810,      65632,      65455,      65279,      65103,      64928,      64753,      64578,      64404,      64231, 

+         64058,      63885,      63713,      63541,      63370,      63199,      63029,      62859,      62690,      62521,      62352, 

+         62184,      62017,      61850,      61683,      61517,      61351,      61186,      61021,      60856,      60692,      60529, 

+         60366,      60203,      60041,      59879,      59718,      59557,      59396,      59236,      59076,      58917,      58758, 

+         58600,      58442,      58285,      58128,      57971,      57815,      57659,      57504,      57349,      57194,      57040, 

+         56886,      56733,      56580,      56428,      56276,      56124,      55973,      55822,      55671,      55521,      55372, 

+         55223,      55074,      54925,      54777,      54630,      54483,      54336,      54189,      54043,      53898,      53752, 

+         53608,      53463,      53319,      53175,      53032,      52889,      52747,      52605,      52463,      52321,      52180, 

+         52040,      51900,      51760,      51620,      51481,      51342,      51204,      51066,      50928,      50791,      50654, 

+         50518,      50382,      50246,      50111,      49976,      49841,      49707,      49573,      49439,      49306,      49173, 

+         49040,      48908,      48776,      48645,      48514,      48383,      48253,      48123,      47993,      47864,      47735, 

+         47606,      47478,      47350,      47222,      47095,      46968,      46842,      46715,      46590,      46464,      46339, 

+         46214,      46089,      45965,      45841,      45718,      45595,      45472,      45349,      45227,      45105,      44984, 

+         44862,      44741,      44621,      44501,      44381,      44261,      44142,      44023,      43904,      43786,      43668, 

+         43550,      43433,      43316,      43199,      43083,      42967,      42851,      42735,      42620,      42505,      42391, 

+         42277,      42163,      42049,      41936,      41823,      41710,      41598,      41486,      41374,      41262,      41151, 

+         41040,      40930,      40819,      40709,      40600,      40490,      40381,      40272,      40164,      40056,      39948, 

+         39840,      39733,      39626,      39519,      39412,      39306,      39200,      39094,      38989,      38884,      38779, 

+         38675,      38571,      38467,      38363,      38260,      38157,      38054,      37951,      37849,      37747,      37645, 

+         37544,      37443,      37342,      37241,      37141,      37041,      36941,      36841,      36742,      36643,      36544, 

+         36446,      36347,      36250,      36152,      36054,      35957,      35860,      35764,      35667,      35571,      35475, 

+         35380,      35284,      35189,      35095,      35000,      34906,      34812,      34718,      34624,      34531,      34438, 

+         34345,      34253,      34160,      34068,      33976,      33885

+};

+

+static const short sindb_coeff[] ICONST_ATTR = {

+	2401, 2144, 1994, 1887, 1804, 1737, 1680, 1630, 1587, 1548, 1512, 1480, 1450, 

+    1423, 1397, 1373, 1351, 1330, 1310, 1291, 1273, 1255, 1239, 1223, 1208, 1194, 

+    1180, 1166, 1153, 1141, 1128, 1117, 1105, 1094, 1084, 1073, 1063, 1053, 1043, 

+    1034, 1025, 1016, 1007,  999,  990,  982,  974,  967,  959,  952,  944,  937, 

+     930,  923,  916,  910,  903,  897,  890,  884,  878,  872,  866,  860,  855, 

+     849,  843,  838,  832,  827,  822,  817,  812,  807,  802,  797,  792,  787, 

+     783,  778,  773,  769,  764,  760,  756,  751,  747,  743,  739,  734,  730, 

+     726,  722,  718,  715,  711,  707,  703,  699,  696,  692,  688,  685,  681, 

+     678,  674,  671,  667,  664,  661,  657,  654,  651,  648,  644,  641,  638, 

+     635,  632,  629,  626,  623,  620,  617,  614,  611,  608,  605,  602,  599, 

+     597,  594,  591,  588,  586,  583,  580,  578,  575,  572,  570,  567,  565, 

+     562,  560,  557,  555,  552,  550,  547,  545,  542,  540,  538,  535,  533, 

+     531,  528,  526,  524,  522,  519,  517,  515,  513,  510,  508,  506,  504, 

+     502,  500,  498,  495,  493,  491,  489,  487,  485,  483,  481,  479,  477, 

+     475,  473,  471,  469,  467,  465,  464,  462,  460,  458,  456,  454,  452, 

+     450,  449,  447,  445,  443,  441,  440,  438,  436,  434,  433,  431,  429, 

+     427,  426,  424,  422,  421,  419,  417,  416,  414,  412,  411,  409,  408, 

+     406,  404,  403,  401,  400,  398,  396,  395,  393,  392,  390,  389,  387, 

+     386,  384,  383,  381,  380,  378,  377,  375,  374,  372,  371,  370,  368, 

+     367,  365,  364,  362,  361,  360,  358,  357,  355,  354,  353,  351,  350, 

+     349,  347,  346,  345,  343,  342,  341,  339,  338,  337,  336,  334,  333, 

+     332,  330,  329,  328,  327,  325,  324,  323,  322,  320,  319,  318,  317, 

+     316,  314,  313,  312,  311,  310,  308,  307,  306,  305,  304,  303,  301, 

+     300,  299,  298,  297,  296,  295,  293,  292,  291,  290,  289,  288,  287, 

+     286,  285,  284,  282,  281,  280,  279,  278,  277,  276,  275,  274,  273, 

+     272,  271,  270,  269,  268,  267,  266,  265,  264,  263,  262,  261,  260, 

+     259,  258,  257,  256,  255,  254,  253,  252,  251,  250,  249,  248,  247, 

+     246,  245,  244,  243,  242,  241,  240,  240,  239,  238,  237,  236,  235, 

+     234,  233,  232,  231,  230,  230,  229,  228,  227,  226,  225,  224,  223, 

+     222,  222,  221,  220,  219,  218,  217,  216,  216,  215,  214,  213,  212, 

+     211,  211,  210,  209,  208,  207,  206,  206,  205,  204,  203,  202,  202, 

+     201,  200,  199,  198,  198,  197,  196,  195,  195,  194,  193,  192,  191, 

+     191,  190,  189,  188,  188,  187,  186,  185,  185,  184,  183,  182,  182, 

+     181,  180,  180,  179,  178,  177,  177,  176,  175,  174,  174,  173,  172, 

+     172,  171,  170,  170,  169,  168,  167,  167,  166,  165,  165,  164,  163, 

+     163,  162,  161,  161,  160,  159,  159,  158,  157,  157,  156,  155,  155, 

+     154,  153,  153,  152,  151,  151,  150,  150,  149,  148,  148,  147,  146, 

+     146,  145,  145,  144,  143,  143,  142,  141,  141,  140,  140,  139,  138, 

+     138,  137,  137,  136,  135,  135,  134,  134,  133,  133,  132,  131,  131, 

+     130,  130,  129,  129,  128,  127,  127,  126,  126,  125,  125,  124,  123, 

+     123,  122,  122,  121,  121,  120,  120,  119,  119,  118,  117,  117,  116, 

+     116,  115,  115,  114,  114,  113,  113,  112,  112,  111,  111,  110,  110, 

+     109,  109,  108,  108,  107,  107,  106,  106,  105,  105,  104,  104,  103, 

+     103,  102,  102,  101,  101,  100,  100,   99,   99,   98,   98,   97,   97, 

+      96,   96,   95,   95,   94,   94,   94,   93,   93,   92,   92,   91,   91, 

+      90,   90,   89,   89,   89,   88,   88,   87,   87,   86,   86,   85,   85, 

+      85,   84,   84,   83,   83,   82,   82,   82,   81,   81,   80,   80,   79, 

+      79,   79,   78,   78,   77,   77,   77,   76,   76,   75,   75,   75,   74, 

+      74,   73,   73,   73,   72,   72,   71,   71,   71,   70,   70,   69,   69, 

+      69,   68,   68,   68,   67,   67,   66,   66,   66,   65,   65,   65,   64, 

+      64,   63,   63,   63,   62,   62,   62,   61,   61,   61,   60,   60,   59, 

+      59,   59,   58,   58,   58,   57,   57,   57,   56,   56,   56,   55,   55, 

+      55,   54,   54,   54,   53,   53,   53,   52,   52,   52,   51,   51,   51, 

+      50,   50,   50,   49,   49,   49,   49,   48,   48,   48,   47,   47,   47, 

+      46,   46,   46,   45,   45,   45,   45,   44,   44,   44,   43,   43,   43, 

+      43,   42,   42,   42,   41,   41,   41,   40,   40,   40,   40,   39,   39, 

+      39,   39,   38,   38,   38,   37,   37,   37,   37,   36,   36,   36,   36, 

+      35,   35,   35,   35,   34,   34,   34,   34,   33,   33,   33,   32,   32, 

+      32,   32,   31,   31,   31,   31,   31,   30,   30,   30,   30,   29,   29, 

+      29,   29,   28,   28,   28,   28,   27,   27,   27,   27,   27,   26,   26, 

+      26,   26,   25,   25,   25,   25,   25,   24,   24,   24,   24,   23,   23, 

+      23,   23,   23,   22,   22,   22,   22,   22,   21,   21,   21,   21,   21, 

+      20,   20,   20,   20,   20,   19,   19,   19,   19,   19,   19,   18,   18, 

+      18,   18,   18,   17,   17,   17,   17,   17,   17,   16,   16,   16,   16, 

+      16,   15,   15,   15,   15,   15,   15,   14,   14,   14,   14,   14,   14, 

+      13,   13,   13,   13,   13,   13,   13,   12,   12,   12,   12,   12,   12, 

+      11,   11,   11,   11,   11,   11,   11,   10,   10,   10,   10,   10,   10, 

+      10,   10,    9,    9,    9,    9,    9,    9,    9,    8,    8,    8,    8, 

+       8,    8,    8,    8,    8,    7,    7,    7,    7,    7,    7,    7,    7, 

+       6,    6,    6,    6,    6,    6,    6,    6,    6,    6,    5,    5,    5, 

+       5,    5,    5,    5,    5,    5,    5,    4,    4,    4,    4,    4,    4, 

+       4,    4,    4,    4,    4,    3,    3,    3,    3,    3,    3,    3,    3, 

+       3,    3,    3,    3,    3,    2,    2,    2,    2,    2,    2,    2,    2, 

+       2,    2,    2,    2,    2,    2,    2,    1,    1,    1,    1,    1,    1, 

+       1,    1,    1,    1,    1,    1,    1,    1,    1,    1,    1,    1,    1, 

+       1,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+       0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+       0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+       0,    0,    0,    0,    0,    0,    0,    0,    0,    0

+};

+

+

+static const short lfo_freq_coeff[] ICONST_ATTR = {

+        0,    3,    6,    9,   12,   15,   18,   21,   25,   28,   31,   34,   37, 

+      40,   43,   46,   50,   53,   56,   59,   62,   65,   68,   71,   74,   78, 

+      81,   84,   87,   90,   93,   96,   99,  102,  105,  108,  111,  115,  118, 

+     121,  124,  127,  130,  133,  136,  139,  142,  145,  148,  151,  154,  157, 

+     160,  163,  166,  169,  172,  175,  178,  180,  183,  186,  189,  192,  195, 

+     198,  201,  204,  207,  209,  212,  215,  218,  221,  224,  226,  229,  232, 

+     235,  238,  240,  243,  246,  249,  251,  254,  257,  260,  262,  265,  268, 

+     270,  273,  276,  278,  281,  283,  286,  289,  291,  294,  296,  299,  301, 

+     304,  306,  309,  311,  314,  316,  319,  321,  324,  326,  328,  331,  333, 

+     336,  338,  340,  343,  345,  347,  350,  352,  354,  356,  359,  361,  363, 

+     365,  367,  370,  372,  374,  376,  378,  380,  382,  384,  386,  388,  391, 

+     393,  395,  396,  398,  400,  402,  404,  406,  408,  410,  412,  414,  415, 

+     417,  419,  421,  423,  424,  426,  428,  430,  431,  433,  435,  436,  438, 

+     439,  441,  443,  444,  446,  447,  449,  450,  452,  453,  455,  456,  457, 

+     459,  460,  461,  463,  464,  465,  467,  468,  469,  470,  472,  473,  474, 

+     475,  476,  477,  478,  480,  481,  482,  483,  484,  485,  486,  487,  488, 

+     488,  489,  490,  491,  492,  493,  494,  494,  495,  496,  497,  497,  498, 

+     499,  499,  500,  501,  501,  502,  502,  503,  504,  504,  504,  505,  505, 

+     506,  506,  507,  507,  507,  508,  508,  508,  509,  509,  509,  509,  510, 

+     510,  510,  510,  510,  510,  510,  510,  510,  510,  511,  510,  510,  510, 

+     510,  510,  510,  510,  510,  510,  510,  509,  509,  509,  509,  508,  508, 

+     508,  507,  507,  507,  506,  506,  505,  505,  504,  504,  504,  503,  502, 

+     502,  501,  501,  500,  499,  499,  498,  497,  497,  496,  495,  494,  494, 

+     493,  492,  491,  490,  489,  488,  488,  487,  486,  485,  484,  483,  482, 

+     481,  480,  478,  477,  476,  475,  474,  473,  472,  470,  469,  468,  467, 

+     465,  464,  463,  461,  460,  459,  457,  456,  455,  453,  452,  450,  449, 

+     447,  446,  444,  443,  441,  439,  438,  436,  435,  433,  431,  430,  428, 

+     426,  424,  423,  421,  419,  417,  415,  414,  412,  410,  408,  406,  404, 

+     402,  400,  398,  396,  395,  393,  391,  388,  386,  384,  382,  380,  378, 

+     376,  374,  372,  370,  367,  365,  363,  361,  359,  356,  354,  352,  350, 

+     347,  345,  343,  340,  338,  336,  333,  331,  328,  326,  324,  321,  319, 

+     316,  314,  311,  309,  306,  304,  301,  299,  296,  294,  291,  289,  286, 

+     283,  281,  278,  276,  273,  270,  268,  265,  262,  260,  257,  254,  251, 

+     249,  246,  243,  240,  238,  235,  232,  229,  226,  224,  221,  218,  215, 

+     212,  209,  207,  204,  201,  198,  195,  192,  189,  186,  183,  180,  178, 

+     175,  172,  169,  166,  163,  160,  157,  154,  151,  148,  145,  142,  139, 

+     136,  133,  130,  127,  124,  121,  118,  115,  111,  108,  105,  102,   99, 

+      96,   93,   90,   87,   84,   81,   78,   74,   71,   68,   65,   62,   59, 

+      56,   53,   50,   46,   43,   40,   37,   34,   31,   28,   25,   21,   18, 

+      15,   12,    9,    6,    3,    0,   -3,   -6,   -9,  -12,  -15,  -18,  -21, 

+     -25,  -28,  -31,  -34,  -37,  -40,  -43,  -46,  -50,  -53,  -56,  -59,  -62, 

+     -65,  -68,  -71,  -74,  -78,  -81,  -84,  -87,  -90,  -93,  -96,  -99, -102, 

+    -105, -108, -111, -115, -118, -121, -124, -127, -130, -133, -136, -139, -142, 

+    -145, -148, -151, -154, -157, -160, -163, -166, -169, -172, -175, -178, -180, 

+    -183, -186, -189, -192, -195, -198, -201, -204, -207, -209, -212, -215, -218, 

+    -221, -224, -226, -229, -232, -235, -238, -240, -243, -246, -249, -251, -254, 

+    -257, -260, -262, -265, -268, -270, -273, -276, -278, -281, -283, -286, -289, 

+    -291, -294, -296, -299, -301, -304, -306, -309, -311, -314, -316, -319, -321, 

+    -324, -326, -328, -331, -333, -336, -338, -340, -343, -345, -347, -350, -352, 

+    -354, -356, -359, -361, -363, -365, -367, -370, -372, -374, -376, -378, -380, 

+    -382, -384, -386, -388, -391, -393, -395, -396, -398, -400, -402, -404, -406, 

+    -408, -410, -412, -414, -415, -417, -419, -421, -423, -424, -426, -428, -430, 

+    -431, -433, -435, -436, -438, -439, -441, -443, -444, -446, -447, -449, -450, 

+    -452, -453, -455, -456, -457, -459, -460, -461, -463, -464, -465, -467, -468, 

+    -469, -470, -472, -473, -474, -475, -476, -477, -478, -480, -481, -482, -483, 

+    -484, -485, -486, -487, -488, -488, -489, -490, -491, -492, -493, -494, -494, 

+    -495, -496, -497, -497, -498, -499, -499, -500, -501, -501, -502, -502, -503, 

+    -504, -504, -504, -505, -505, -506, -506, -507, -507, -507, -508, -508, -508, 

+    -509, -509, -509, -509, -510, -510, -510, -510, -510, -510, -510, -510, -510, 

+    -510, -511, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -509, 

+    -509, -509, -509, -508, -508, -508, -507, -507, -507, -506, -506, -505, -505, 

+    -504, -504, -504, -503, -502, -502, -501, -501, -500, -499, -499, -498, -497, 

+    -497, -496, -495, -494, -494, -493, -492, -491, -490, -489, -488, -488, -487, 

+    -486, -485, -484, -483, -482, -481, -480, -478, -477, -476, -475, -474, -473, 

+    -472, -470, -469, -468, -467, -465, -464, -463, -461, -460, -459, -457, -456, 

+    -455, -453, -452, -450, -449, -447, -446, -444, -443, -441, -439, -438, -436, 

+    -435, -433, -431, -430, -428, -426, -424, -423, -421, -419, -417, -415, -414, 

+    -412, -410, -408, -406, -404, -402, -400, -398, -396, -395, -393, -391, -388, 

+    -386, -384, -382, -380, -378, -376, -374, -372, -370, -367, -365, -363, -361, 

+    -359, -356, -354, -352, -350, -347, -345, -343, -340, -338, -336, -333, -331, 

+    -328, -326, -324, -321, -319, -316, -314, -311, -309, -306, -304, -301, -299, 

+    -296, -294, -291, -289, -286, -283, -281, -278, -276, -273, -270, -268, -265, 

+    -262, -260, -257, -254, -251, -249, -246, -243, -240, -238, -235, -232, -229, 

+    -226, -224, -221, -218, -215, -212, -209, -207, -204, -201, -198, -195, -192, 

+    -189, -186, -183, -180, -178, -175, -172, -169, -166, -163, -160, -157, -154, 

+    -151, -148, -145, -142, -139, -136, -133, -130, -127, -124, -121, -118, -115, 

+    -111, -108, -105, -102,  -99,  -96,  -93,  -90,  -87,  -84,  -81,  -78,  -74, 

+     -71,  -68,  -65,  -62,  -59,  -56,  -53,  -50,  -46,  -43,  -40,  -37,  -34, 

+     -31,  -28,  -25,  -21,  -18,  -15,  -12,   -9,   -6,   -3

+};

+

+static const short lfo_env_coeff[] ICONST_ATTR = {

+     251,  253,  254,  256,  257,  259,  260,  262,  264,  265,  267,  268,  270, 

+     271,  273,  274,  276,  277,  279,  281,  282,  284,  285,  287,  288,  290, 

+     291,  293,  294,  296,  297,  299,  300,  302,  303,  305,  306,  308,  309, 

+     311,  312,  314,  315,  317,  318,  320,  321,  323,  324,  326,  327,  329, 

+     330,  332,  333,  335,  336,  337,  339,  340,  342,  343,  345,  346,  348, 

+     349,  350,  352,  353,  355,  356,  357,  359,  360,  362,  363,  364,  366, 

+     367,  369,  370,  371,  373,  374,  375,  377,  378,  379,  381,  382,  383, 

+     385,  386,  387,  389,  390,  391,  392,  394,  395,  396,  397,  399,  400, 

+     401,  402,  404,  405,  406,  407,  409,  410,  411,  412,  413,  414,  416, 

+     417,  418,  419,  420,  421,  423,  424,  425,  426,  427,  428,  429,  430, 

+     431,  432,  434,  435,  436,  437,  438,  439,  440,  441,  442,  443,  444, 

+     445,  446,  447,  448,  449,  450,  451,  452,  453,  453,  454,  455,  456, 

+     457,  458,  459,  460,  461,  461,  462,  463,  464,  465,  466,  466,  467, 

+     468,  469,  469,  470,  471,  472,  473,  473,  474,  475,  475,  476,  477, 

+     477,  478,  479,  479,  480,  481,  481,  482,  483,  483,  484,  484,  485, 

+     486,  486,  487,  487,  488,  488,  489,  489,  490,  490,  491,  491,  492, 

+     492,  493,  493,  493,  494,  494,  495,  495,  495,  496,  496,  497,  497, 

+     497,  498,  498,  498,  498,  499,  499,  499,  500,  500,  500,  500,  500, 

+     501,  501,  501,  501,  501,  502,  502,  502,  502,  502,  502,  502,  502, 

+     503,  503,  503,  503,  503,  503,  503,  503,  503,  503,  503,  503,  503, 

+     503,  503,  503,  503,  503,  503,  502,  502,  502,  502,  502,  502,  502, 

+     502,  501,  501,  501,  501,  501,  500,  500,  500,  500,  500,  499,  499, 

+     499,  498,  498,  498,  498,  497,  497,  497,  496,  496,  495,  495,  495, 

+     494,  494,  493,  493,  493,  492,  492,  491,  491,  490,  490,  489,  489, 

+     488,  488,  487,  487,  486,  486,  485,  484,  484,  483,  483,  482,  481, 

+     481,  480,  479,  479,  478,  477,  477,  476,  475,  475,  474,  473,  473, 

+     472,  471,  470,  469,  469,  468,  467,  466,  466,  465,  464,  463,  462, 

+     461,  461,  460,  459,  458,  457,  456,  455,  454,  453,  453,  452,  451, 

+     450,  449,  448,  447,  446,  445,  444,  443,  442,  441,  440,  439,  438, 

+     437,  436,  435,  434,  432,  431,  430,  429,  428,  427,  426,  425,  424, 

+     423,  421,  420,  419,  418,  417,  416,  414,  413,  412,  411,  410,  409, 

+     407,  406,  405,  404,  402,  401,  400,  399,  397,  396,  395,  394,  392, 

+     391,  390,  389,  387,  386,  385,  383,  382,  381,  379,  378,  377,  375, 

+     374,  373,  371,  370,  369,  367,  366,  364,  363,  362,  360,  359,  357, 

+     356,  355,  353,  352,  350,  349,  348,  346,  345,  343,  342,  340,  339, 

+     337,  336,  335,  333,  332,  330,  329,  327,  326,  324,  323,  321,  320, 

+     318,  317,  315,  314,  312,  311,  309,  308,  306,  305,  303,  302,  300, 

+     299,  297,  296,  294,  293,  291,  290,  288,  287,  285,  284,  282,  281, 

+     279,  277,  276,  274,  273,  271,  270,  268,  267,  265,  264,  262,  260, 

+     259,  257,  256,  254,  253,  251,  250,  248,  247,  245,  244,  242,  240, 

+     239,  237,  236,  234,  233,  231,  230,  228,  227,  225,  223,  222,  220, 

+     219,  217,  216,  214,  213,  211,  210,  208,  207,  205,  204,  202,  201, 

+     199,  198,  196,  195,  193,  192,  190,  189,  187,  186,  184,  183,  181, 

+     180,  178,  177,  175,  174,  172,  171,  169,  168,  166,  165,  164,  162, 

+     161,  159,  158,  156,  155,  153,  152,  151,  149,  148,  146,  145,  144, 

+     142,  141,  139,  138,  137,  135,  134,  133,  131,  130,  129,  127,  126, 

+     124,  123,  122,  120,  119,  118,  117,  115,  114,  113,  111,  110,  109, 

+     108,  106,  105,  104,  103,  101,  100,   99,   98,   96,   95,   94,   93, 

+      92,   90,   89,   88,   87,   86,   84,   83,   82,   81,   80,   79,   78, 

+      77,   75,   74,   73,   72,   71,   70,   69,   68,   67,   66,   65,   64, 

+      63,   62,   61,   60,   59,   58,   57,   56,   55,   54,   53,   52,   51, 

+      50,   49,   48,   47,   46,   45,   45,   44,   43,   42,   41,   40,   39, 

+      39,   38,   37,   36,   35,   35,   34,   33,   32,   31,   31,   30,   29, 

+      29,   28,   27,   26,   26,   25,   24,   24,   23,   22,   22,   21,   20, 

+      20,   19,   19,   18,   17,   17,   16,   16,   15,   15,   14,   14,   13, 

+      13,   12,   12,   11,   11,   10,   10,    9,    9,    9,    8,    8,    7, 

+       7,    7,    6,    6,    6,    5,    5,    5,    4,    4,    4,    3,    3, 

+       3,    3,    2,    2,    2,    2,    2,    1,    1,    1,    1,    1,    1, 

+       0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+       0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, 

+       0,    0,    0,    1,    1,    1,    1,    1,    1,    2,    2,    2,    2, 

+       2,    3,    3,    3,    3,    4,    4,    4,    5,    5,    5,    6,    6, 

+       6,    7,    7,    7,    8,    8,    9,    9,    9,   10,   10,   11,   11, 

+      12,   12,   13,   13,   14,   14,   15,   15,   16,   16,   17,   17,   18, 

+      19,   19,   20,   20,   21,   22,   22,   23,   24,   24,   25,   26,   26, 

+      27,   28,   29,   29,   30,   31,   31,   32,   33,   34,   35,   35,   36, 

+      37,   38,   39,   39,   40,   41,   42,   43,   44,   45,   45,   46,   47, 

+      48,   49,   50,   51,   52,   53,   54,   55,   56,   57,   58,   59,   60, 

+      61,   62,   63,   64,   65,   66,   67,   68,   69,   70,   71,   72,   73, 

+      74,   75,   77,   78,   79,   80,   81,   82,   83,   84,   86,   87,   88, 

+      89,   90,   92,   93,   94,   95,   96,   98,   99,  100,  101,  103,  104, 

+     105,  106,  108,  109,  110,  111,  113,  114,  115,  117,  118,  119,  120, 

+     122,  123,  124,  126,  127,  129,  130,  131,  133,  134,  135,  137,  138, 

+     139,  141,  142,  144,  145,  146,  148,  149,  151,  152,  153,  155,  156, 

+     158,  159,  161,  162,  164,  165,  166,  168,  169,  171,  172,  174,  175, 

+     177,  178,  180,  181,  183,  184,  186,  187,  189,  190,  192,  193,  195, 

+     196,  198,  199,  201,  202,  204,  205,  207,  208,  210,  211,  213,  214, 

+     216,  217,  219,  220,  222,  223,  225,  227,  228,  230,  231,  233,  234, 

+     236,  237,  239,  240,  242,  244,  245,  247,  248,  250

+};

+

+#endif

diff --git a/apps/codecs/libgme/z80_cpu.c b/apps/codecs/libgme/z80_cpu.c
new file mode 100644
index 0000000..9151350
--- /dev/null
+++ b/apps/codecs/libgme/z80_cpu.c
@@ -0,0 +1,85 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "z80_cpu.h"
+
+/* Copyright (C) 2006-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"
+
+// flags, named with hex value for clarity
+int const S80 = 0x80;
+int const Z40 = 0x40;
+int const F20 = 0x20;
+int const H10 = 0x10;
+int const F08 = 0x08;
+int const V04 = 0x04;
+int const P04 = 0x04;
+int const N02 = 0x02;
+int const C01 = 0x01;
+
+void Z80_init( struct Z80_Cpu* this ) 
+{
+	this->cpu_state = &this->cpu_state_;
+	
+	int i;
+	for ( i = 0x100; --i >= 0; )
+	{
+		int p, even = 1;
+		for ( p = i; p; p >>= 1 )
+			even ^= p;
+		int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04);
+		this->szpc [i] = n;
+		this->szpc [i + 0x100] = n | C01;
+	}
+	this->szpc [0x000] |= Z40;
+	this->szpc [0x100] |= Z40;
+}
+
+inline void set_page( struct Z80_Cpu* this, int i, void* write, void const* read )
+{
+	int offset = Z80_CPU_OFFSET( i * page_size );
+	byte      * write2 = STATIC_CAST(byte      *,write) - offset;
+	byte const* read2  = STATIC_CAST(byte const*,read ) - offset;
+	this->cpu_state_.write [i] = write2;
+	this->cpu_state_.read  [i] = read2;
+	this->cpu_state->write [i] = write2;
+	this->cpu_state->read  [i] = read2;
+}
+
+void Z80_reset( struct Z80_Cpu* this, void* unmapped_write, void const* unmapped_read )
+{
+	check( this->cpu_state == &this->cpu_state_ );
+	this->cpu_state = &this->cpu_state_;
+	this->cpu_state_.time = 0;
+	this->cpu_state_.base = 0;
+	this->end_time_   = 0;
+	
+	int i;
+	for ( i = 0; i < page_count + 1; i++ )
+		set_page( this, i, unmapped_write, unmapped_read );
+	
+	memset( &this->r, 0, sizeof this->r );
+}
+
+void Z80_map_mem( struct Z80_Cpu* this, addr_t start, int size, void* write, void const* read )
+{
+	// address range must begin and end on page boundaries
+	require( start % page_size == 0 );
+	require( size  % page_size == 0 );
+	require( start + size <= 0x10000 );
+	
+	int offset;
+	for ( offset = 0; offset < size; offset += page_size )
+		set_page( this, (start + offset) >> page_bits,
+				STATIC_CAST(char      *,write) + offset,
+				STATIC_CAST(char const*,read ) + offset );
+}
diff --git a/apps/codecs/libgme/z80_cpu.h b/apps/codecs/libgme/z80_cpu.h
new file mode 100644
index 0000000..15115b7
--- /dev/null
+++ b/apps/codecs/libgme/z80_cpu.h
@@ -0,0 +1,116 @@
+// Z80 CPU emulator

+

+// Game_Music_Emu 0.6-pre

+#ifndef Z80_CPU_H

+#define Z80_CPU_H

+

+#include "blargg_source.h"

+#include "blargg_endian.h"

+

+typedef int cpu_time_t;

+typedef int addr_t;

+

+enum { page_bits = 10 };

+enum { page_size = 1 << page_bits };

+enum { page_count = 0x10000 / page_size };

+

+// Can read this far past end of memory

+enum { cpu_padding = 0x100 };

+

+// Can read this many bytes past end of a page

+enum { page_padding = 4 };

+

+#ifdef BLARGG_BIG_ENDIAN

+	struct regs_t { byte b,c, d,e, h,l, flags,a; };

+#else

+	struct regs_t { byte c,b, e,d, l,h, a,flags; };

+#endif

+// BOOST_STATIC_ASSERT( sizeof (regs_t) == 8 );

+	

+struct pairs_t { uint16_t bc, de, hl, fa; };

+	

+// Registers are not updated until run() returns

+struct registers_t {

+	uint16_t pc;

+	uint16_t sp;

+	uint16_t ix;

+	uint16_t iy;

+	union {

+		struct regs_t b; //  b.b, b.c, b.d, b.e, b.h, b.l, b.flags, b.a

+		struct pairs_t w; // w.bc, w.de, w.hl. w.fa

+	};

+	union {

+		struct regs_t b;

+		struct pairs_t w;

+	} alt;

+	byte iff1;

+	byte iff2;

+	byte r;

+	byte i;

+	byte im;

+};

+

+struct cpu_state_t {

+	byte const* read  [page_count + 1];

+	byte      * write [page_count + 1];

+	cpu_time_t base;

+	cpu_time_t time;

+};

+

+struct Z80_Cpu {

+	byte szpc [0x200];

+	cpu_time_t end_time_;

+	

+	struct cpu_state_t* cpu_state; // points to cpu_state_ or a local copy within run()

+	struct cpu_state_t cpu_state_;

+

+	struct registers_t r;

+};

+

+void Z80_init( struct Z80_Cpu* this );

+

+// Clears registers and maps all pages to unmapped

+void Z80_reset( struct Z80_Cpu* this, void* unmapped_write, void const* unmapped_read );

+	

+// TODO: split mapping out of CPU

+	

+// Maps memory. Start and size must be multiple of page_size.

+void Z80_map_mem( struct Z80_Cpu* this, addr_t addr, int size, void* write, void const* read );

+	

+// Time of beginning of next instruction

+static inline cpu_time_t Z80_time( struct Z80_Cpu* this ) { return this->cpu_state->time + this->cpu_state->base; }

+	

+// Alter current time

+static inline void Z80_set_time( struct Z80_Cpu* this, cpu_time_t t ) { this->cpu_state->time = t - this->cpu_state->base; }

+static inline void Z80_adjust_time( struct Z80_Cpu* this, int delta ) { this->cpu_state->time += delta; }

+

+#ifdef BLARGG_NONPORTABLE

+	#define Z80_CPU_OFFSET( addr ) (addr)

+#else

+	#define Z80_CPU_OFFSET( addr ) ((addr) & (page_size - 1))

+#endif

+

+// Maps address to pointer to that byte

+static inline byte* Z80_write( struct Z80_Cpu* this, addr_t addr )

+{

+	return this->cpu_state->write [(unsigned) addr >> page_bits] + Z80_CPU_OFFSET( addr );

+}

+

+static inline byte const* Z80_read( struct Z80_Cpu* this, addr_t addr )

+{

+	return this->cpu_state->read [(unsigned) addr >> page_bits] + Z80_CPU_OFFSET( addr );

+}

+

+static inline void Z80_map_mem_rw( struct Z80_Cpu* this, addr_t addr, int size, void* p )

+{

+	Z80_map_mem( this, addr, size, p, p );

+}

+

+static inline void Z80_set_end_time( struct Z80_Cpu* this, cpu_time_t t )

+{

+	cpu_time_t delta = this->cpu_state->base - t;

+	this->cpu_state->base = t;

+	this->cpu_state->time += delta;

+}

+

+#endif

diff --git a/apps/codecs/libgme/z80_cpu_run.h b/apps/codecs/libgme/z80_cpu_run.h
new file mode 100644
index 0000000..18195ac
--- /dev/null
+++ b/apps/codecs/libgme/z80_cpu_run.h
@@ -0,0 +1,1696 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/

+

+// Last validated with zexall 2009.12.05.

+// Doesn't implement the R register or immediate interrupt after EI.

+// Address wrap-around isn't completely correct, but is prevented from crashing emulator.

+// 16-bit memory accesses are made directly to mapped memory, instead of using macro.

+

+#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.

+- Except where noted, time() and related functions will NOT work

+correctly inside a macro. TIME() is always correct, and between FLUSH_TIME() and

+CACHE_TIME() the normal time changing functions can be used.

+- Macros "returning" void may use a {} statement block. */

+

+	// 0 <= addr <= 0xFFFF + 0x100

+	// Optional; default uses whatever was set with map_mem()

+	int  READ_MEM(  addr_t );

+	void WRITE_MEM( addr_t, int data );

+	

+	// 0 <= port <= 0xFFFF (apparently upper 8 bits are output by hardware)

+	void OUT_PORT( int port, int data );

+	int  IN_PORT   int port );

+	

+	// Reference to Z80_Cpu object used for emulation

+	#define CPU cpu

+	

+// The following can be used within macros:

+	

+	// Current time

+	time_t TIME();

+	

+	// Allows use of time functions

+	void FLUSH_TIME();

+	

+	// Must be used before end of macro if FLUSH_TIME() was used earlier

+	void CACHE_TIME();

+

+// Configuration (optional; commented behavior if defined)

+	

+	// Optimizes as if map_mem( 0, 0x10000, FLAT_MEM, FLAT_MEM ) is always in effect

+	#define FLAT_MEM my_mem_array

+	

+	// If RST 7 ($FF) is encountered and PC = IDLE_ADDR, stops execution

+	#define IDLE_ADDR 0x1234

+	

+	// Expanded just before beginning of code, to help debugger

+	#define CPU_BEGIN void my_run_cpu() {

+	

+#endif

+

+/* Copyright (C) 2006-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 */

+

+#ifdef CPU_BEGIN

+	CPU_BEGIN

+#endif

+

+#define R cpu->r

+

+// flags, named with hex value for clarity

+int const S80 = 0x80;

+int const Z40 = 0x40;

+int const F20 = 0x20;

+int const H10 = 0x10;

+int const F08 = 0x08;

+int const V04 = 0x04;

+int const P04 = 0x04;

+int const N02 = 0x02;

+int const C01 = 0x01;

+

+#define SZ28P( n )  cpu->szpc [n]

+#define SZ28PC( n ) cpu->szpc [n]

+#define SZ28C( n )  (cpu->szpc [n] & ~P04)

+#define SZ28( n )   SZ28C( n )

+

+#define SET_R( n )  (void) (R.r = n)

+#define GET_R()     (R.r)

+

+// Time

+#define TIME()          (s_time + s.base)

+#define FLUSH_TIME()    {s.time = s_time;}

+#define CACHE_TIME()    {s_time = s.time;}

+

+// Memory

+#define RW_MEM( addr, rw )          RW_PAGE( addr, rw ) [RW_OFFSET( addr )]

+#define READ_CODE( addr )           RW_MEM( addr, read )

+

+#ifdef FLAT_MEM

+	#define RW_PAGE( addr, rw )     FLAT_MEM

+	#define RW_OFFSET( addr )       (addr)

+	#define INSTR( off, addr )      READ_CODE( addr )

+#else

+	#define RW_PAGE( addr, rw )     s.rw [(unsigned) (addr) >> page_bits]

+	#define RW_OFFSET( addr )       Z80_CPU_OFFSET( addr )

+	#define INSTR( off, addr )      instr [off]

+#endif

+

+#ifndef READ_MEM

+	#define READ_MEM( addr )        RW_MEM( addr, read )

+#endif

+	

+#ifndef WRITE_MEM

+	#define WRITE_MEM( addr, data ) (RW_MEM( addr, write ) = data)

+#endif

+

+#define READ_WORD( addr )           GET_LE16( &RW_MEM( addr, read ) )

+#define WRITE_WORD( addr, data )    SET_LE16( &RW_MEM( addr, write ), data )

+

+// 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 */

+

+// Misc

+#define CASE5( a, b, c, d, e          ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e

+#define CASE6( a, b, c, d, e, f       ) CASE5( a, b, c, d, e       ): case 0x##f

+#define CASE7( a, b, c, d, e, f, g    ) CASE6( a, b, c, d, e, f    ): case 0x##g

+#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h

+

+#ifdef BLARGG_BIG_ENDIAN

+	#define R8( n, offset ) ((r.r8_ - offset) [n]) 

+#elif BLARGG_LITTLE_ENDIAN

+	#define R8( n, offset ) ((r.r8_ - offset) [(n) ^ 1]) 

+#else

+	#error "Byte order of CPU must be known"

+#endif

+

+#define R16( n, shift, offset ) (r.r16_ [((unsigned) (n) >> shift) - (offset >> shift)])

+

+#define EX( x, y ) \

+	{\

+		int temp = x;\

+		x = y;\

+		y = temp;\

+	}

+		

+#define EXX( name ) \

+	EX( R.alt.name, r.name )

+		

+bool warning = false;

+{

+	struct cpu_state_t s;

+	#ifdef FLAT_MEM

+		s.base = cpu->cpu_state_.base;

+	#else

+		s = cpu->cpu_state_;

+	#endif

+	cpu->cpu_state = &s;

+	

+	

+	union r_t {

+		struct regs_t b;

+		struct pairs_t w;

+		byte r8_ [8]; // indexed

+		uint16_t r16_ [4];

+	} r;

+	r.b = R.b;

+	

+	cpu_time_t s_time = cpu->cpu_state_.time;

+	int pc = R.pc;

+	int sp = R.sp;

+	int ix = R.ix; // TODO: keep in memory for direct access?

+	int iy = R.iy;

+	int flags = R.b.flags;

+	

+	//goto loop; // confuses optimizer

+	s_time += 7;

+	pc -= 2;

+	

+call_not_taken:

+	s_time -= 7; 

+jp_not_taken:

+	pc += 2;

+loop:

+	

+	check( (unsigned) pc < 0x10000 + 1 ); // +1 so emulator can catch wrap-around

+	check( (unsigned) sp < 0x10000 );

+	check( (unsigned) flags < 0x100 );

+	check( (unsigned) ix < 0x10000 );

+	check( (unsigned) iy < 0x10000 );

+	

+	byte const* instr = RW_PAGE( pc, read );

+	

+	int opcode;

+	

+	if ( RW_OFFSET( ~0 ) == ~0 )

+	{

+		opcode = instr [RW_OFFSET( pc )];

+		pc++;

+		instr += RW_OFFSET( pc );

+	}

+	else

+	{

+		instr += RW_OFFSET( pc );

+		opcode = *instr++;

+		pc++;

+	}

+	

+	static byte const clock_table [256 * 2] = {

+	//   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F

+		 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0

+		 8,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1

+		 7,10,16, 6, 4, 4, 7, 4, 7,11,16, 6, 4, 4, 7, 4, // 2

+		 7,10,13, 6,11,11,10, 4, 7,11,13, 6, 4, 4, 7, 4, // 3

+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4

+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5

+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6

+		 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7

+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8

+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9

+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A

+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B

+		11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C

+		11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D

+		11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E

+		11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F

+		

+		// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8

+		//0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F

+		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,

+		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,

+		0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00,

+		0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,

+		0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,

+		0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,

+		0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,

+		0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00,

+		0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,

+		0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,

+		0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,

+		0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,

+		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,

+		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+		0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

+		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,

+	};

+	

+	if ( s_time >= 0 )

+		goto out_of_time;

+	s_time += clock_table [opcode];

+	

+	#ifdef Z80_CPU_LOG_H

+		//log_opcode( opcode, READ_CODE( pc ) );

+		z80_cpu_log( "log.txt", pc - 1, opcode, READ_CODE( pc ),

+				READ_CODE( pc + 1 ), READ_CODE( pc + 2 ) );

+		z80_log_regs( r.b.a, r.w.bc, r.w.de, r.w.hl, sp, ix, iy );

+	#endif

+	

+#define GET_ADDR()  GET_LE16( &INSTR( 0, pc ) )

+

+	int data;

+	data = INSTR( 0, pc );

+	

+	switch ( opcode )

+	{

+// Common

+

+	case 0x00: // NOP

+	CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc.

+		goto loop;

+	

+	case 0x08:{// EX AF,AF'

+		EXX( b.a );

+		EX( R.alt.b.flags, flags );

+		goto loop;

+	}

+	

+	case 0xD3: // OUT (imm),A

+		pc++;

+		OUT_PORT( (data + r.b.a * 0x100), r.b.a );

+		goto loop;

+		

+	case 0x2E: // LD L,imm

+		pc++;

+		r.b.l = data;

+		goto loop;

+	

+	case 0x3E: // LD A,imm

+		pc++;

+		r.b.a = data;

+		goto loop;

+	

+	case 0x3A:{// LD A,(addr)

+		int addr = GET_ADDR();

+		pc += 2;

+		r.b.a = READ_MEM( addr );

+		goto loop;

+	}

+	

+// Conditional

+

+#define ZERO    (flags & Z40)

+#define CARRY   (flags & C01)

+#define EVEN    (flags & P04)

+#define MINUS   (flags & S80)

+

+// JR

+// TODO: more efficient way to handle negative branch that wraps PC around

+#define JR_( cond, clocks ) {\

+	pc++;\

+	if ( !(cond) )\

+		goto loop;\

+	int offset = SBYTE( data );\

+	pc = WORD( pc + offset );\

+	s_time += clocks;\

+	goto loop;\

+}

+

+#define JR( cond ) JR_( cond, 5 )

+	

+	case 0x20: JR( !ZERO  ) // JR NZ,disp

+	case 0x28: JR(  ZERO  ) // JR Z,disp

+	case 0x30: JR( !CARRY ) // JR NC,disp

+	case 0x38: JR(  CARRY ) // JR C,disp

+	case 0x18: JR_( true,0) // JR disp

+

+	case 0x10:{// DJNZ disp

+		int temp = r.b.b - 1;

+		r.b.b = temp;

+		JR( temp )

+	}

+	

+// JP

+#define JP( cond ) \

+	if ( !(cond) )\

+		goto jp_not_taken;\

+	pc = GET_ADDR();\

+	goto loop;

+	

+	case 0xC2: JP( !ZERO  ) // JP NZ,addr

+	case 0xCA: JP(  ZERO  ) // JP Z,addr

+	case 0xD2: JP( !CARRY ) // JP NC,addr

+	case 0xDA: JP(  CARRY ) // JP C,addr

+	case 0xE2: JP( !EVEN  ) // JP PO,addr

+	case 0xEA: JP(  EVEN  ) // JP PE,addr

+	case 0xF2: JP( !MINUS ) // JP P,addr

+	case 0xFA: JP(  MINUS ) // JP M,addr

+	

+	case 0xC3: // JP addr

+		pc = GET_ADDR();

+		goto loop;

+	

+	case 0xE9: // JP HL

+		pc = r.w.hl;

+		goto loop;

+

+// RET

+#define RET( cond ) \

+	if ( cond )\

+		goto ret_taken;\

+	s_time -= 6;\

+	goto loop;

+	

+	case 0xC0: RET( !ZERO  ) // RET NZ

+	case 0xC8: RET(  ZERO  ) // RET Z

+	case 0xD0: RET( !CARRY ) // RET NC

+	case 0xD8: RET(  CARRY ) // RET C

+	case 0xE0: RET( !EVEN  ) // RET PO

+	case 0xE8: RET(  EVEN  ) // RET PE

+	case 0xF0: RET( !MINUS ) // RET P

+	case 0xF8: RET(  MINUS ) // RET M

+	

+	case 0xC9: // RET

+	ret_taken:

+		pc = READ_WORD( sp );

+		sp = WORD( sp + 2 );

+		goto loop;

+	

+// CALL

+#define CALL( cond ) \

+	if ( cond )\

+		goto call_taken;\

+	goto call_not_taken;

+

+	case 0xC4: CALL( !ZERO  ) // CALL NZ,addr

+	case 0xCC: CALL(  ZERO  ) // CALL Z,addr

+	case 0xD4: CALL( !CARRY ) // CALL NC,addr

+	case 0xDC: CALL(  CARRY ) // CALL C,addr

+	case 0xE4: CALL( !EVEN  ) // CALL PO,addr

+	case 0xEC: CALL(  EVEN  ) // CALL PE,addr

+	case 0xF4: CALL( !MINUS ) // CALL P,addr

+	case 0xFC: CALL(  MINUS ) // CALL M,addr

+	

+	case 0xCD:{// CALL addr

+	call_taken: {

+			int addr = pc + 2;

+			pc = GET_ADDR();

+			sp = WORD( sp - 2 );

+			WRITE_WORD( sp, addr );

+			goto loop;

+		}

+	}

+	

+	case 0xFF: // RST

+		#ifdef IDLE_ADDR

+			if ( pc == IDLE_ADDR + 1 )

+				goto hit_idle_addr;

+		#else

+			if ( pc > 0x10000 )

+			{

+				pc = WORD( pc - 1 );

+				s_time -= 11;

+				goto loop;

+			}

+		#endif

+	CASE7( C7, CF, D7, DF, E7, EF, F7 ):

+		data = pc;

+		pc = opcode & 0x38;

+		#ifdef RST_BASE

+			pc += RST_BASE;

+		#endif

+		goto push_data;

+

+// PUSH/POP

+	case 0xF5: // PUSH AF

+		data = r.b.a * 0x100u + flags;

+		goto push_data;

+	

+	case 0xC5: // PUSH BC

+	case 0xD5: // PUSH DE

+	case 0xE5: // PUSH HL

+		data = R16( opcode, 4, 0xC5 );

+	push_data:

+		sp = WORD( sp - 2 );

+		WRITE_WORD( sp, data );

+		goto loop;

+	

+	case 0xF1: // POP AF

+		flags = READ_MEM( sp );

+		r.b.a = READ_MEM( (sp + 1) );

+		sp = WORD( sp + 2 );

+		goto loop;

+	

+	case 0xC1: // POP BC

+	case 0xD1: // POP DE

+	case 0xE1: // POP HL

+		R16( opcode, 4, 0xC1 ) = READ_WORD( sp );

+		sp = WORD( sp + 2 );

+		goto loop;

+	

+// ADC/ADD/SBC/SUB

+	case 0x96: // SUB (HL)

+	case 0x86: // ADD (HL)

+		flags &= ~C01;

+	case 0x9E: // SBC (HL)

+	case 0x8E: // ADC (HL)

+		data = READ_MEM( r.w.hl );

+		goto adc_data;

+	

+	case 0xD6: // SUB A,imm

+	case 0xC6: // ADD imm

+		flags &= ~C01;

+	case 0xDE: // SBC A,imm

+	case 0xCE: // ADC imm

+		pc++;

+		goto adc_data;

+	

+	CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r

+	CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r

+		flags &= ~C01;

+	CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r

+	CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r

+		data = R8( opcode & 7, 0 );

+	adc_data: {

+		int result = data + (flags & C01);

+		data ^= r.b.a;

+		flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes

+		if ( flags )

+			result = -result;

+		result += r.b.a;

+		data ^= result;

+		flags +=(data & H10) +

+				((data + 0x80) >> 6 & V04) +

+				SZ28C( result & 0x1FF );

+		r.b.a = result;

+		goto loop;

+	}

+

+// CP

+	case 0xBE: // CP (HL)

+		data = READ_MEM( r.w.hl );

+		goto cp_data;

+	

+	case 0xFE: // CP imm

+		pc++;

+		goto cp_data;

+	

+	CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r

+		data = R8( opcode, 0xB8 );

+	cp_data: {

+		int result = r.b.a - data;

+		flags = N02 + (data & (F20 | F08)) + (result >> 8 & C01);

+		data ^= r.b.a;

+		flags +=(((result ^ r.b.a) & data) >> 5 & V04) +

+				(((data & H10) ^ result) & (S80 | H10));

+		if ( BYTE( result ) )

+			goto loop;

+		flags += Z40;

+		goto loop;

+	}

+	

+// ADD HL,r.w

+	

+	case 0x39: // ADD HL,SP

+		data = sp;

+		goto add_hl_data;

+	

+	case 0x09: // ADD HL,BC

+	case 0x19: // ADD HL,DE

+	case 0x29: // ADD HL,HL

+		data = R16( opcode, 4, 0x09 );

+	add_hl_data: {

+		int sum = r.w.hl + data;

+		data ^= r.w.hl;

+		r.w.hl = sum;

+		flags = (flags & (S80 | Z40 | V04)) +

+				(sum >> 16) +

+				(sum >> 8 & (F20 | F08)) +

+				((data ^ sum) >> 8 & H10);

+		goto loop;

+	}

+	

+	case 0x27:{// DAA

+		int a = r.b.a;

+		if ( a > 0x99 )

+			flags |= C01;

+		

+		int adjust = 0x60 * (flags & C01);

+		

+		if ( flags & H10 || (a & 0x0F) > 9 )

+			adjust += 0x06;

+		

+		if ( flags & N02 )

+			adjust = -adjust;

+		a += adjust;

+		

+		flags = (flags & (C01 | N02)) +

+				((r.b.a ^ a) & H10) +

+				SZ28P( BYTE( a ) );

+		r.b.a = a;

+		goto loop;

+	}

+	

+// INC/DEC

+	case 0x34: // INC (HL)

+		data = READ_MEM( r.w.hl ) + 1;

+		WRITE_MEM( r.w.hl, data );

+		goto inc_set_flags;

+	

+	CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r

+		data = ++R8( opcode >> 3, 0 );

+	inc_set_flags:

+		flags = (flags & C01) +

+				(((data & 0x0F) - 1) & H10) +

+				SZ28( BYTE( data ) );

+		if ( data != 0x80 )

+			goto loop;

+		flags += V04;

+		goto loop;

+	

+	case 0x35: // DEC (HL)

+		data = READ_MEM( r.w.hl ) - 1;

+		WRITE_MEM( r.w.hl, data );

+		goto dec_set_flags;

+	

+	CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r

+		data = --R8( opcode >> 3, 0 );

+	dec_set_flags:

+		flags = (flags & C01) + N02 +

+				(((data & 0x0F) + 1) & H10) +

+				SZ28( BYTE( data ) );

+		if ( data != 0x7F )

+			goto loop;

+		flags += V04;

+		goto loop;

+

+	case 0x03: // INC BC

+	case 0x13: // INC DE

+	case 0x23: // INC HL

+		R16( opcode, 4, 0x03 )++;

+		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( opcode, 4, 0x0B )--;

+		goto loop;

+	

+	case 0x3B: // DEC SP

+		sp = WORD( sp - 1 );

+		goto loop;

+	

+// AND

+	case 0xA6: // AND (HL)

+		data = READ_MEM( r.w.hl );

+		goto and_data;

+	

+	case 0xE6: // AND imm

+		pc++;

+		goto and_data;

+	

+	CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r

+		data = R8( opcode, 0xA0 );

+	and_data:

+		r.b.a &= data;

+		flags = SZ28P( r.b.a ) + H10;

+		goto loop;

+	

+// OR

+	case 0xB6: // OR (HL)

+		data = READ_MEM( r.w.hl );

+		goto or_data;

+	

+	case 0xF6: // OR imm

+		pc++;

+		goto or_data;

+	

+	CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r

+		data = R8( opcode, 0xB0 );

+	or_data:

+		r.b.a |= data;

+		flags = SZ28P( r.b.a );

+		goto loop;

+

+// XOR

+	case 0xAE: // XOR (HL)

+		data = READ_MEM( r.w.hl );

+		goto xor_data;

+	

+	case 0xEE: // XOR imm

+		pc++;

+		goto xor_data;

+	

+	CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r

+		data = R8( opcode, 0xA8 );

+	xor_data:

+		r.b.a ^= data;

+		flags = SZ28P( r.b.a );

+		goto loop;

+

+// LD

+	CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r

+		WRITE_MEM( r.w.hl, R8( opcode, 0x70 ) );

+		goto loop;

+	

+	CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r

+	CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r

+	CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r

+	CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r

+	CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r

+	CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r

+	CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r

+		R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 );

+		goto loop;

+	

+	CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm

+		R8( opcode >> 3, 0 ) = data;

+		pc++;

+		goto loop;

+	

+	case 0x36: // LD (HL),imm

+		pc++;

+		WRITE_MEM( r.w.hl, data );

+		goto loop;

+	

+	CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL)

+		R8( opcode >> 3, 8 ) = READ_MEM( r.w.hl );

+		goto loop;

+	

+	case 0x01: // LD r.w,imm

+	case 0x11:

+	case 0x21:

+		R16( opcode, 4, 0x01 ) = GET_ADDR();

+		pc += 2;

+		goto loop;

+	

+	case 0x31: // LD sp,imm

+		sp = GET_ADDR();

+		pc += 2;

+		goto loop;

+	

+	case 0x2A:{// LD HL,(addr)

+		int addr = GET_ADDR();

+		pc += 2;

+		r.w.hl = READ_WORD( addr );

+		goto loop;

+	}

+	

+	case 0x32:{// LD (addr),A

+		int addr = GET_ADDR();

+		pc += 2;

+		WRITE_MEM( addr, r.b.a );

+		goto loop;

+	}

+	

+	case 0x22:{// LD (addr),HL

+		int addr = GET_ADDR();

+		pc += 2;

+		WRITE_WORD( addr, r.w.hl );

+		goto loop;

+	}

+	

+	case 0x02: // LD (BC),A

+	case 0x12: // LD (DE),A

+		WRITE_MEM( R16( opcode, 4, 0x02 ), r.b.a );

+		goto loop;

+	

+	case 0x0A: // LD A,(BC)

+	case 0x1A: // LD A,(DE)

+		r.b.a = READ_MEM( R16( opcode, 4, 0x0A ) );

+		goto loop;

+	

+	case 0xF9: // LD SP,HL

+		sp = r.w.hl;

+		goto loop;

+	

+// Rotate

+	

+	case 0x07:{// RLCA

+		int temp = r.b.a;

+		temp = (temp << 1) + (temp >> 7);

+		flags = (flags & (S80 | Z40 | P04)) +

+				(temp & (F20 | F08 | C01));

+		r.b.a = temp;

+		goto loop;

+	}

+	

+	case 0x0F:{// RRCA

+		int temp = r.b.a;

+		flags = (flags & (S80 | Z40 | P04)) +

+				(temp & C01);

+		temp = (temp << 7) + (temp >> 1);

+		flags += temp & (F20 | F08);

+		r.b.a = temp;

+		goto loop;

+	}

+	

+	case 0x17:{// RLA

+		int temp = (r.b.a << 1) + (flags & C01);

+		flags = (flags & (S80 | Z40 | P04)) +

+				(temp & (F20 | F08)) +

+				(temp >> 8);

+		r.b.a = temp;

+		goto loop;

+	}

+	

+	case 0x1F:{// RRA

+		int temp = (flags << 7) + (r.b.a >> 1);

+		flags = (flags & (S80 | Z40 | P04)) +

+				(temp & (F20 | F08)) +

+				(r.b.a & C01);

+		r.b.a = temp;

+		goto loop;

+	}

+	

+// Misc

+	case 0x2F:{// CPL

+		int temp = ~r.b.a;

+		flags = (flags & (S80 | Z40 | P04 | C01)) +

+				(temp & (F20 | F08)) +

+				(H10 | N02);

+		r.b.a = temp;

+		goto loop;

+	}

+	

+	case 0x3F:{// CCF

+		flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) +

+				(flags << 4 & H10) +

+				(r.b.a & (F20 | F08));

+		goto loop;

+	}

+	

+	case 0x37: // SCF

+		flags = ((flags & (S80 | Z40 | P04)) | C01) +

+				(r.b.a & (F20 | F08));

+		goto loop;

+	

+	case 0xDB: // IN A,(imm)

+		pc++;

+		r.b.a = IN_PORT( (data + r.b.a * 0x100) );

+		goto loop;

+

+	case 0xE3:{// EX (SP),HL

+		int temp = READ_WORD( sp );

+		WRITE_WORD( sp, r.w.hl );

+		r.w.hl = temp;

+		goto loop;

+	}

+	

+	case 0xEB: // EX DE,HL

+		EX( r.w.hl, r.w.de );

+		goto loop;

+	

+	case 0xD9: // EXX DE,HL

+		EXX( w.bc );

+		EXX( w.de );

+		EXX( w.hl );

+		goto loop;

+	

+	case 0xF3: // DI

+		R.iff1 = 0;

+		R.iff2 = 0;

+		goto loop;

+	

+	case 0xFB: // EI

+		R.iff1 = 1;

+		R.iff2 = 1;

+		// TODO: delayed effect

+		goto loop;

+	

+	case 0x76: // HALT

+		goto halt;

+	

+//////////////////////////////////////// CB prefix

+	{

+	case 0xCB:

+		pc++;

+		switch ( data )

+		{

+	

+	// Rotate left

+		

+	#define RLC( read, write ) {\

+		int result = read;\

+		result = BYTE( result << 1 ) + (result >> 7);\

+		flags = SZ28P( result ) + (result & C01);\

+		write;\

+		goto loop;\

+	}

+		

+		case 0x06: // RLC (HL)

+			s_time += 7;

+			data = r.w.hl;

+		rlc_data_addr:

+			RLC( READ_MEM( data ), WRITE_MEM( data, result ) )

+		

+		CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r

+			byte* reg = &R8( data, 0 );

+			RLC( *reg, *reg = result )

+		}

+		

+	#define RL( read, write ) {\

+		int result = (read << 1) + (flags & C01);\

+		flags = SZ28PC( result );\

+		write;\

+		goto loop;\

+	}

+		

+		case 0x16: // RL (HL)

+			s_time += 7;

+			data = r.w.hl;

+		rl_data_addr:

+			RL( READ_MEM( data ), WRITE_MEM( data, result ) )

+		

+		CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r

+			byte* reg = &R8( data, 0x10 );

+			RL( *reg, *reg = result )

+		}

+		

+	#define SLA( read, low_bit, write ) {\

+		int result = (read << 1) + low_bit;\

+		flags = SZ28PC( result );\

+		write;\

+		goto loop;\

+	}

+		

+		case 0x26: // SLA (HL)

+			s_time += 7;

+			data = r.w.hl;

+		sla_data_addr:

+			SLA( READ_MEM( data ), 0, WRITE_MEM( data, result ) )

+		

+		CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r

+			byte* reg = &R8( data, 0x20 );

+			SLA( *reg, 0, *reg = result )

+		}

+		

+		case 0x36: // SLL (HL)

+			s_time += 7;

+			data = r.w.hl;

+		sll_data_addr:

+			SLA( READ_MEM( data ), 1, WRITE_MEM( data, result ) )

+		

+		CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r

+			byte* reg = &R8( data, 0x30 );

+			SLA( *reg, 1, *reg = result )

+		}

+		

+	// Rotate right

+		

+	#define RRC( read, write ) {\

+		int result = read;\

+		flags = result & C01;\

+		result = BYTE( result << 7 ) + (result >> 1);\

+		flags += SZ28P( result );\

+		write;\

+		goto loop;\

+	}

+		

+		case 0x0E: // RRC (HL)

+			s_time += 7;

+			data = r.w.hl;

+		rrc_data_addr:

+			RRC( READ_MEM( data ), WRITE_MEM( data, result ) )

+		

+		CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r

+			byte* reg = &R8( data, 0x08 );

+			RRC( *reg, *reg = result )

+		}

+		

+	#define RR( read, write ) {\

+		int result = read;\

+		int temp = result & C01;\

+		result = BYTE( flags << 7 ) + (result >> 1);\

+		flags = SZ28P( result ) + temp;\

+		write;\

+		goto loop;\

+	}

+		

+		case 0x1E: // RR (HL)

+			s_time += 7;

+			data = r.w.hl;

+		rr_data_addr:

+			RR( READ_MEM( data ), WRITE_MEM( data, result ) )

+		

+		CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r

+			byte* reg = &R8( data, 0x18 );

+			RR( *reg, *reg = result )

+		}

+		

+	#define SRA( read, write ) {\

+		int result = read;\

+		flags = result & C01;\

+		result = (result & 0x80) + (result >> 1);\

+		flags += SZ28P( result );\

+		write;\

+		goto loop;\

+	}

+		

+		case 0x2E: // SRA (HL)

+			data = r.w.hl;

+			s_time += 7;

+		sra_data_addr:

+			SRA( READ_MEM( data ), WRITE_MEM( data, result ) )

+		

+		CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r

+			byte* reg = &R8( data, 0x28 );

+			SRA( *reg, *reg = result )

+		}

+		

+	#define SRL( read, write ) {\

+		int result = read;\

+		flags = result & C01;\

+		result >>= 1;\

+		flags += SZ28P( result );\

+		write;\

+		goto loop;\

+	}

+		

+		case 0x3E: // SRL (HL)

+			s_time += 7;

+			data = r.w.hl;

+		srl_data_addr:

+			SRL( READ_MEM( data ), WRITE_MEM( data, result ) )

+		

+		CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r

+			byte* reg = &R8( data, 0x38 );

+			SRL( *reg, *reg = result )

+		}

+		

+	// BIT

+		{

+			int temp;

+		CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL)

+			s_time += 4;

+			temp = READ_MEM( r.w.hl );

+			flags &= C01;

+			goto bit_temp;

+		CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r

+		CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r

+		CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r

+		CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r

+		CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r

+		CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r

+		CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r

+		CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r

+			temp = R8( data & 7, 0 );

+			flags = (flags & C01) + (temp & (F20 | F08));

+		bit_temp:

+			temp = temp & (1 << (data >> 3 & 7));

+			flags += (temp & S80) + H10;

+			flags += (unsigned) --temp >> 8 & (Z40 | P04);

+			goto loop;

+		}

+		

+	// SET/RES

+		CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL)

+		CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL)

+			s_time += 7;

+			int temp = READ_MEM( r.w.hl );

+			int bit = 1 << (data >> 3 & 7);

+			temp |= bit; // SET

+			if ( !(data & 0x40) )

+				temp ^= bit; // RES

+			WRITE_MEM( r.w.hl, temp );

+			goto loop;

+		}

+		

+		CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r

+		CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r

+		CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r

+		CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r

+		CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r

+		CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r

+		CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r

+		CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r

+			R8( data & 7, 0 ) |= 1 << (data >> 3 & 7);

+			goto loop;

+		

+		CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r

+		CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r

+		CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r

+		CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r

+		CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r

+		CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r

+		CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r

+		CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r

+			R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7));

+			goto loop;

+		}

+		assert( false );

+	}

+

+#undef GET_ADDR

+#define GET_ADDR()  GET_LE16( &INSTR( 1, pc ) )

+

+//////////////////////////////////////// ED prefix

+	{

+	case 0xED:

+		pc++;

+		s_time += (clock_table + 256) [data] >> 4;

+		switch ( data )

+		{

+		{

+			int temp;

+		case 0x72: // SBC HL,SP

+		case 0x7A: // ADC HL,SP

+			temp = sp;

+			if ( 0 )

+		case 0x42: // SBC HL,BC

+		case 0x52: // SBC HL,DE

+		case 0x62: // SBC HL,HL

+		case 0x4A: // ADC HL,BC

+		case 0x5A: // ADC HL,DE

+		case 0x6A: // ADC HL,HL

+				temp = R16( data >> 3 & 6, 1, 0 );

+			int sum = temp + (flags & C01);

+			flags = ~data >> 2 & N02;

+			if ( flags )

+				sum = -sum;

+			sum += r.w.hl;

+			temp ^= r.w.hl;

+			temp ^= sum;

+			flags +=(sum >> 16 & C01) +

+					(temp >> 8 & H10) +

+					(sum >> 8 & (S80 | F20 | F08)) +

+					((temp + 0x8000) >> 14 & V04);

+			r.w.hl = sum;

+			if ( WORD( sum ) )

+				goto loop;

+			flags += Z40;

+			goto loop;

+		}

+		

+		CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C)

+			int temp = IN_PORT( r.w.bc );

+			R8( data >> 3, 8 ) = temp;

+			flags = (flags & C01) + SZ28P( temp );

+			goto loop;

+		}

+		

+		case 0x71: // OUT (C),0

+			r.b.flags = 0;

+		CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r

+			OUT_PORT( r.w.bc, R8( data >> 3, 8 ) );

+			goto loop;

+		

+		{

+			int temp;

+		case 0x73: // LD (ADDR),SP

+			temp = sp;

+			if ( 0 )

+		case 0x43: // LD (ADDR),BC

+		case 0x53: // LD (ADDR),DE

+				temp = R16( data, 4, 0x43 );

+			int addr = GET_ADDR();

+			pc += 2;

+			WRITE_WORD( addr, temp );

+			goto loop;

+		}

+		

+		case 0x4B: // LD BC,(ADDR)

+		case 0x5B:{// LD DE,(ADDR)

+			int addr = GET_ADDR();

+			pc += 2;

+			R16( data, 4, 0x4B ) = READ_WORD( addr );

+			goto loop;

+		}

+		

+		case 0x7B:{// LD SP,(ADDR)

+			int addr = GET_ADDR();

+			pc += 2;

+			sp = READ_WORD( addr );

+			goto loop;

+		}

+		

+		case 0x67:{// RRD

+			int temp = READ_MEM( r.w.hl );

+			WRITE_MEM( r.w.hl, ((r.b.a << 4) + (temp >> 4)) );

+			temp = (r.b.a & 0xF0) + (temp & 0x0F);

+			flags = (flags & C01) + SZ28P( temp );

+			r.b.a = temp;

+			goto loop;

+		}

+		

+		case 0x6F:{// RLD

+			int temp = READ_MEM( r.w.hl );

+			WRITE_MEM( r.w.hl, ((temp << 4) + (r.b.a & 0x0F)) );

+			temp = (r.b.a & 0xF0) + (temp >> 4);

+			flags = (flags & C01) + SZ28P( temp );

+			r.b.a = temp;

+			goto loop;

+		}

+		

+		CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG

+			opcode = 0x10; // flag to do SBC instead of ADC

+			flags &= ~C01;

+			data = r.b.a;

+			r.b.a = 0;

+			goto adc_data;

+		

+		{

+			int inc;

+		case 0xA9: // CPD

+		case 0xB9: // CPDR

+			inc = -1;

+			if ( 0 )

+		case 0xA1: // CPI

+		case 0xB1: // CPIR

+				inc = +1;

+			int addr = r.w.hl;

+			r.w.hl = addr + inc;

+			int temp = READ_MEM( addr );

+			

+			int result = r.b.a - temp;

+			flags = (flags & C01) + N02 +

+					((((temp ^ r.b.a) & H10) ^ result) & (S80 | H10));

+			

+			if ( !BYTE( result ) )

+				flags += Z40;

+			result -= (flags & H10) >> 4;

+			flags += result & F08;

+			flags += result << 4 & F20;

+			if ( !--r.w.bc )

+				goto loop;

+			

+			flags += V04;

+			if ( flags & Z40 || data < 0xB0 )

+				goto loop;

+			

+			pc -= 2;

+			s_time += 5;

+			goto loop;

+		}

+		

+		{

+			int inc;

+		case 0xA8: // LDD

+		case 0xB8: // LDDR

+			inc = -1;

+			if ( 0 )

+		case 0xA0: // LDI

+		case 0xB0: // LDIR

+				inc = +1;

+			int addr = r.w.hl;

+			r.w.hl = addr + inc;

+			int temp = READ_MEM( addr );

+			

+			addr = r.w.de;

+			r.w.de = addr + inc;

+			WRITE_MEM( addr, temp );

+			

+			temp += r.b.a;

+			flags = (flags & (S80 | Z40 | C01)) +

+					(temp & F08) + (temp << 4 & F20);

+			if ( !--r.w.bc )

+				goto loop;

+			

+			flags += V04;

+			if ( data < 0xB0 )

+				goto loop;

+			

+			pc -= 2;

+			s_time += 5;

+			goto loop;

+		}

+		

+		{

+			int inc;

+		case 0xAB: // OUTD

+		case 0xBB: // OTDR

+			inc = -1;

+			if ( 0 )

+		case 0xA3: // OUTI

+		case 0xB3: // OTIR

+				inc = +1;

+			int addr = r.w.hl;

+			r.w.hl = addr + inc;

+			int temp = READ_MEM( addr );

+			

+			int b = --r.b.b;

+			flags = (temp >> 6 & N02) + SZ28( b );

+			if ( b && data >= 0xB0 )

+			{

+				pc -= 2;

+				s_time += 5;

+			}

+			

+			OUT_PORT( r.w.bc, temp );

+			goto loop;

+		}

+		

+		{

+			int inc;

+		case 0xAA: // IND

+		case 0xBA: // INDR

+			inc = -1;

+			if ( 0 )

+		case 0xA2: // INI

+		case 0xB2: // INIR

+				inc = +1;

+			

+			int addr = r.w.hl;

+			r.w.hl = addr + inc;

+			

+			int temp = IN_PORT( r.w.bc );

+			

+			int b = --r.b.b;

+			flags = (temp >> 6 & N02) + SZ28( b );

+			if ( b && data >= 0xB0 )

+			{

+				pc -= 2;

+				s_time += 5;

+			}

+			

+			WRITE_MEM( addr, temp );

+			goto loop;

+		}

+		

+		case 0x47: // LD I,A

+			R.i = r.b.a;

+			goto loop;

+		

+		case 0x4F: // LD R,A

+			SET_R( r.b.a );

+			dprintf( "LD R,A not supported\n" );

+			warning = true;

+			goto loop;

+		

+		case 0x57: // LD A,I

+			r.b.a = R.i;

+			goto ld_ai_common;

+		

+		case 0x5F: // LD A,R

+			r.b.a = GET_R();

+			dprintf( "LD A,R not supported\n" );

+			warning = true;

+		ld_ai_common:

+			flags = (flags & C01) + SZ28( r.b.a ) + (R.iff2 << 2 & V04);

+			goto loop;

+		

+		CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN

+			R.iff1 = R.iff2;

+			goto ret_taken;

+		

+		case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0

+			R.im = 0;

+			goto loop;

+		

+		case 0x56: case 0x76: // IM 1

+			R.im = 1;

+			goto loop;

+		

+		case 0x5E: case 0x7E: // IM 2

+			R.im = 2;

+			goto loop;

+		

+		default:

+			dprintf( "Opcode $ED $%02X not supported\n", data );

+			warning = true;

+			goto loop;

+		}

+		assert( false );

+	}

+

+//////////////////////////////////////// DD/FD prefix

+	{

+	int ixy;

+	case 0xDD:

+		ixy = ix;

+		goto ix_prefix;

+	case 0xFD:

+		ixy = iy;

+	ix_prefix:

+		pc++;

+		int data2 = READ_CODE( pc );

+		s_time += (clock_table + 256) [data] & 0x0F;

+		switch ( data )

+		{

+	// TODO: more efficient way of avoid negative address

+	// TODO: avoid using this as argument to READ_MEM() since it is evaluated twice

+	#define IXY_DISP( ixy, disp )   WORD( (ixy ) + (disp))

+	

+	#define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in;

+	

+	// ADD/ADC/SUB/SBC

+	

+		case 0x96: // SUB (IXY+disp)

+		case 0x86: // ADD (IXY+disp)

+			flags &= ~C01;

+		case 0x9E: // SBC (IXY+disp)

+		case 0x8E: // ADC (IXY+disp)

+			pc++;

+			opcode = data;

+			data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );

+			goto adc_data;

+		

+		case 0x94: // SUB HXY

+		case 0x84: // ADD HXY

+			flags &= ~C01;

+		case 0x9C: // SBC HXY

+		case 0x8C: // ADC HXY

+			opcode = data;

+			data = ixy >> 8;

+			goto adc_data;

+		

+		case 0x95: // SUB LXY

+		case 0x85: // ADD LXY

+			flags &= ~C01;

+		case 0x9D: // SBC LXY

+		case 0x8D: // ADC LXY

+			opcode = data;

+			data = BYTE( ixy );

+			goto adc_data;

+		

+		{

+			int temp;

+		case 0x39: // ADD IXY,SP

+			temp = sp;

+			goto add_ixy_data;

+		

+		case 0x29: // ADD IXY,HL

+			temp = ixy;

+			goto add_ixy_data;

+		

+		case 0x09: // ADD IXY,BC

+		case 0x19: // ADD IXY,DE

+			temp = R16( data, 4, 0x09 );

+		add_ixy_data: {

+			int sum = ixy + temp;

+			temp ^= ixy;

+			ixy = WORD( sum );

+			flags = (flags & (S80 | Z40 | V04)) +

+					(sum >> 16) +

+					(sum >> 8 & (F20 | F08)) +

+					((temp ^ sum) >> 8 & H10);

+			goto set_ixy;

+		}

+		}

+	

+	// AND

+		case 0xA6: // AND (IXY+disp)

+			pc++;

+			data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );

+			goto and_data;

+		

+		case 0xA4: // AND HXY

+			data = ixy >> 8;

+			goto and_data;

+		

+		case 0xA5: // AND LXY

+			data = BYTE( ixy );

+			goto and_data;

+	

+	// OR

+		case 0xB6: // OR (IXY+disp)

+			pc++;

+			data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );

+			goto or_data;

+		

+		case 0xB4: // OR HXY

+			data = ixy >> 8;

+			goto or_data;

+		

+		case 0xB5: // OR LXY

+			data = BYTE( ixy );

+			goto or_data;

+	

+	// XOR

+		case 0xAE: // XOR (IXY+disp)

+			pc++;

+			data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );

+			goto xor_data;

+		

+		case 0xAC: // XOR HXY

+			data = ixy >> 8;

+			goto xor_data;

+		

+		case 0xAD: // XOR LXY

+			data = BYTE( ixy );

+			goto xor_data;

+	

+	// CP

+		case 0xBE: // CP (IXY+disp)

+			pc++;

+			data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) )  );

+			goto cp_data;

+		

+		case 0xBC: // CP HXY

+			data = ixy >> 8;

+			goto cp_data;

+		

+		case 0xBD: // CP LXY

+			data = BYTE( ixy );

+			goto cp_data;

+		

+	// LD

+		CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r

+			data = R8( data, 0x70 );

+			if ( 0 )

+		case 0x36: // LD (IXY+disp),imm

+				pc++, data = READ_CODE( pc );

+			pc++;

+			WRITE_MEM( IXY_DISP( ixy, SBYTE( data2 ) ), data );

+			goto loop;

+

+		CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY

+			R8( data >> 3, 8 ) = ixy >> 8;

+			goto loop;

+		

+		case 0x64: // LD HXY,HXY

+		case 0x6D: // LD LXY,LXY

+			goto loop;

+		

+		CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY

+			R8( data >> 3, 8 ) = ixy;

+			goto loop;

+		

+		CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp)

+			pc++;

+			R8( data >> 3, 8 ) = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );

+			goto loop;

+		

+		case 0x26: // LD HXY,imm

+			pc++;

+			goto ld_hxy_data;

+			

+		case 0x65: // LD HXY,LXY

+			data2 = BYTE( ixy );

+			goto ld_hxy_data;

+		

+		CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r

+			data2 = R8( data, 0x60 );

+		ld_hxy_data:

+			ixy = BYTE( ixy ) + (data2 << 8);

+			goto set_ixy;

+		

+		case 0x2E: // LD LXY,imm

+			pc++;

+			goto ld_lxy_data;

+			

+		case 0x6C: // LD LXY,HXY

+			data2 = ixy >> 8;

+			goto ld_lxy_data;

+		

+		CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r

+			data2 = R8( data, 0x68 );

+		ld_lxy_data:

+			ixy = (ixy & 0xFF00) + data2;

+		set_ixy:

+			if ( opcode == 0xDD )

+			{

+				ix = ixy;

+				goto loop;

+			}

+			iy = ixy;

+			goto loop;

+

+		case 0xF9: // LD SP,IXY

+			sp = ixy;

+			goto loop;

+	

+		case 0x22:{// LD (ADDR),IXY

+			int addr = GET_ADDR();

+			pc += 2;

+			WRITE_WORD( addr, ixy );

+			goto loop;

+		}

+		

+		case 0x21: // LD IXY,imm

+			ixy = GET_ADDR();

+			pc += 2;

+			goto set_ixy;

+		

+		case 0x2A:{// LD IXY,(addr)

+			int addr = GET_ADDR();

+			ixy = READ_WORD( addr );

+			pc += 2;

+			goto set_ixy;

+		}

+		

+	// DD/FD CB prefix

+		case 0xCB: {

+			data = IXY_DISP( ixy, SBYTE( data2 ) );

+			pc++;

+			data2 = READ_CODE( pc );

+			pc++;

+			switch ( data2 )

+			{

+			case 0x06: goto rlc_data_addr; // RLC (IXY)

+			case 0x16: goto rl_data_addr;  // RL (IXY)

+			case 0x26: goto sla_data_addr; // SLA (IXY)

+			case 0x36: goto sll_data_addr; // SLL (IXY)

+			case 0x0E: goto rrc_data_addr; // RRC (IXY)

+			case 0x1E: goto rr_data_addr;  // RR (IXY)

+			case 0x2E: goto sra_data_addr; // SRA (IXY)

+			case 0x3E: goto srl_data_addr; // SRL (IXY)

+			

+			CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp)

+				int temp = READ_MEM( data );

+				temp = temp & (1 << (data2 >> 3 & 7));

+				flags = (flags & C01) + H10 + (temp & S80);

+				flags += (unsigned) --temp >> 8 & (Z40 | P04);

+				goto loop;

+			}

+			

+			CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp)

+			CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp)

+				int temp = READ_MEM( data );

+				int bit = 1 << (data2 >> 3 & 7);

+				temp |= bit; // SET

+				if ( !(data2 & 0x40) )

+					temp ^= bit; // RES

+				WRITE_MEM( data, temp );

+				goto loop;

+			}

+			

+			default:

+				dprintf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 );

+				warning = true;

+				goto loop;

+			}

+			assert( false );

+		}

+		

+	// INC/DEC

+		case 0x23: // INC IXY

+			ixy = WORD( ixy + 1 );

+			goto set_ixy;

+		

+		case 0x2B: // DEC IXY

+			ixy = WORD( ixy - 1 );

+			goto set_ixy;

+		

+		case 0x34: // INC (IXY+disp)

+			ixy = IXY_DISP( ixy, SBYTE( data2 ) );

+			pc++;

+			data = READ_MEM( ixy ) + 1;

+			WRITE_MEM( ixy, data );

+			goto inc_set_flags;

+		

+		case 0x35: // DEC (IXY+disp)

+			ixy = IXY_DISP( ixy, SBYTE( data2 ) );

+			pc++;

+			data = READ_MEM( ixy ) - 1;

+			WRITE_MEM( ixy, data );

+			goto dec_set_flags;

+		

+		case 0x24: // INC HXY

+			ixy = WORD( ixy + 0x100 );

+			data = ixy >> 8;

+			goto inc_xy_common;

+		

+		case 0x2C: // INC LXY

+			data = BYTE( ixy + 1 );

+			ixy = (ixy & 0xFF00) + data;

+		inc_xy_common:

+			if ( opcode == 0xDD )

+			{

+				ix = ixy;

+				goto inc_set_flags;

+			}

+			iy = ixy;

+			goto inc_set_flags;

+		

+		case 0x25: // DEC HXY

+			ixy = WORD( ixy - 0x100 );

+			data = ixy >> 8;

+			goto dec_xy_common;

+		

+		case 0x2D: // DEC LXY

+			data = BYTE( ixy - 1 );

+			ixy = (ixy & 0xFF00) + data;

+		dec_xy_common:

+			if ( opcode == 0xDD )

+			{

+				ix = ixy;

+				goto dec_set_flags;

+			}

+			iy = ixy;

+			goto dec_set_flags;

+		

+	// PUSH/POP

+		case 0xE5: // PUSH IXY

+			data = ixy;

+			goto push_data;

+		

+		case 0xE1:{// POP IXY

+			ixy = READ_WORD( sp );

+			sp = WORD( sp + 2 );

+			goto set_ixy;

+		}

+	

+	// Misc

+		

+		case 0xE9: // JP (IXY)

+			pc = ixy;

+			goto loop;

+		

+		case 0xE3:{// EX (SP),IXY

+			int temp = READ_WORD( sp );

+			WRITE_WORD( sp, ixy );

+			ixy = temp;

+			goto set_ixy;

+		}

+		

+		default:

+			dprintf( "Unnecessary DD/FD prefix encountered\n" );

+			warning = true;

+			pc--;

+			goto loop;

+		}

+		assert( false );

+	}

+	

+	}

+	dprintf( "Unhandled main opcode: $%02X\n", opcode );

+	assert( false );

+	

+#ifdef IDLE_ADDR

+hit_idle_addr:

+	s_time -= 11;

+	goto out_of_time;

+#endif

+halt:

+	s_time &= 3; // increment by multiple of 4

+out_of_time:

+	pc--;

+	

+	r.b.flags = flags;

+	R.ix     = ix;

+	R.iy     = iy;

+	R.sp     = sp;

+	R.pc     = pc;

+	R.b      = r.b;

+	

+	cpu->cpu_state_.base = s.base;

+	cpu->cpu_state_.time = s_time;

+	cpu->cpu_state = &cpu->cpu_state_;

+}

diff --git a/apps/codecs/nsf.c b/apps/codecs/nsf.c
index d626d52..a556f75 100644
--- a/apps/codecs/nsf.c
+++ b/apps/codecs/nsf.c
@@ -1,4378 +1,68 @@
-/***************************************************************************
- *             __________               __   ___.
- *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
- *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
- *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
- *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
- *                     \/            \/     \/    \/            \/
- *
- * Copyright (C) 2006 Adam Gashlin (hcs)
- * Copyright (C) 2004 Disch
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
 
-/*
- * This is a perversion of Disch's excellent NotSoFatso.
- */
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
 
-#include "codeclib.h"
-#include "inttypes.h"
-#include "system.h"
+#define GME_NSF_TYPE
+
+#include <codecs/lib/codeclib.h>
+#include "libgme/nsf_emu.h" 
 
 CODEC_HEADER
 
-#if   (CONFIG_CPU == MCF5250)
-#define ICODE_INSTEAD_OF_INLINE
-/* Enough IRAM to move additional data and code to it. */
-#define IBSS_ATTR_NSF_LARGE_IRAM    IBSS_ATTR
-#define ICONST_ATTR_NSF_LARGE_IRAM  ICONST_ATTR
-
-#elif (CONFIG_CPU == PP5022) || (CONFIG_CPU == PP5024)
-#define ICODE_INSTEAD_OF_INLINE
-/* Enough IRAM to move additional data and code to it. */
-#define IBSS_ATTR_NSF_LARGE_IRAM    IBSS_ATTR
-#define ICONST_ATTR_NSF_LARGE_IRAM  ICONST_ATTR
-
-#elif defined(CPU_S5L870X)
-#define ICODE_INSTEAD_OF_INLINE
-/* Very large IRAM. Move even more data to it. */
-#define IBSS_ATTR_NSF_LARGE_IRAM    IBSS_ATTR
-#define ICONST_ATTR_NSF_LARGE_IRAM  ICONST_ATTR
-
-#else
-#define ICODE_INSTEAD_OF_INLINE
-/* Not enough IRAM available. */
-#define IBSS_ATTR_NSF_LARGE_IRAM
-#define ICONST_ATTR_NSF_LARGE_IRAM
-#endif
-
 /* Maximum number of bytes to process in one iteration */
-#define WAV_CHUNK_SIZE (1024*2)
+#define CHUNK_SIZE (1024*2)
 
-static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR MEM_ALIGN_ATTR;
-
-#define ZEROMEMORY(addr,size) memset(addr,0,size)
-
-/* simple profiling with USEC_TIMER
-
-#define NSF_PROFILE
-
-*/
-
-#ifdef NSF_PROFILE
-
-#define CREATE_TIMER(name) static uint32_t nsf_timer_##name##_start,\
-    nsf_timer_##name##_total
-#define ENTER_TIMER(name) nsf_timer_##name##_start=USEC_TIMER
-#define EXIT_TIMER(name) nsf_timer_##name##_total+=\
-    (USEC_TIMER-nsf_timer_##name##_start)
-#define READ_TIMER(name) (nsf_timer_##name##_total)
-#define RESET_TIMER(name) nsf_timer_##name##_total=0
-
-#define PRINT_TIMER_PCT(bname,tname,nstr) ci->fdprintf(
-    logfd,"%10ld ",READ_TIMER(bname));\
-    ci->fdprintf(logfd,"(%3d%%) " nstr "\t",\
-    ((uint64_t)READ_TIMER(bname))*100/READ_TIMER(tname))
-
-CREATE_TIMER(total);
-CREATE_TIMER(cpu);
-CREATE_TIMER(apu);
-CREATE_TIMER(squares);
-CREATE_TIMER(tnd);
-CREATE_TIMER(tnd_enter);
-CREATE_TIMER(tnd_tri);
-CREATE_TIMER(tnd_noise);
-CREATE_TIMER(tnd_dmc);
-CREATE_TIMER(fds);
-CREATE_TIMER(frame);
-CREATE_TIMER(mix);
-
-void reset_profile_timers(void) {
-    RESET_TIMER(total);
-    RESET_TIMER(cpu);
-    RESET_TIMER(apu);
-    RESET_TIMER(squares);
-    RESET_TIMER(tnd);
-    RESET_TIMER(tnd_enter);
-    RESET_TIMER(tnd_tri);
-    RESET_TIMER(tnd_noise);
-    RESET_TIMER(tnd_dmc);
-    RESET_TIMER(fds);
-    RESET_TIMER(frame);
-    RESET_TIMER(mix);
-}
-
-int logfd=-1;
-
-void print_timers(char * path, int track) {
-    logfd = ci->open("/nsflog.txt",O_WRONLY|O_CREAT|O_APPEND, 0666);
-    ci->fdprintf(logfd,"%s[%d]:\t",path,track);
-    ci->fdprintf(logfd,"%10ld total\t",READ_TIMER(total));
-    PRINT_TIMER_PCT(cpu,total,"CPU");
-    PRINT_TIMER_PCT(apu,total,"APU");
-    ci->fdprintf(logfd,"\n\t");
-    PRINT_TIMER_PCT(squares,apu,"squares");
-    PRINT_TIMER_PCT(frame,apu,"frame");
-    PRINT_TIMER_PCT(mix,apu,"mix");
-    PRINT_TIMER_PCT(fds,apu,"FDS");
-    PRINT_TIMER_PCT(tnd,apu,"tnd");
-    ci->fdprintf(logfd,"\n\t\t");
-    PRINT_TIMER_PCT(tnd_enter,tnd,"enter");
-    PRINT_TIMER_PCT(tnd_tri,tnd,"triangle");
-    PRINT_TIMER_PCT(tnd_noise,tnd,"noise");
-    PRINT_TIMER_PCT(tnd_dmc,tnd,"DMC");
-    ci->fdprintf(logfd,"\n");
-
-    ci->close(logfd);
-    logfd=-1;
-}
-
-#else
-
-#define CREATE_TIMER(name)
-#define ENTER_TIMER(name)
-#define EXIT_TIMER(name)
-#define READ_TIMER(name)
-#define RESET_TIMER(name)
-#define print_timers(path,track)
-#define reset_profile_timers()
-
-#endif
-
-/* proper handling of multibyte values */
-#ifdef ROCKBOX_LITTLE_ENDIAN
-union TWIN
-{
-    uint16_t                        W;
-    struct{ uint8_t l; uint8_t h; } B;
-};
-
-union QUAD
-{
-    uint32_t                                    D;
-    struct{ uint8_t l; uint8_t h; uint16_t w; } B;
-};
-#else
-
-union TWIN
-{
-    uint16_t                        W;
-    struct{ uint8_t h; uint8_t l; } B;
-};
-
-union QUAD
-{
-    uint32_t                                    D;
-    struct{uint16_t w; uint8_t h; uint8_t l; }  B;
-};
-
-#endif
-
-#define NTSC_FREQUENCY           1789772.727273f
-#define PAL_FREQUENCY            1652097.692308f
-#define NTSC_NMIRATE                  60.098814f
-#define PAL_NMIRATE                   50.006982f
-
-#define NES_FREQUENCY           21477270
-#define NTSC_FRAME_COUNTER_FREQ (NTSC_FREQUENCY / (NES_FREQUENCY / 89490.0f))
-#define PAL_FRAME_COUNTER_FREQ  (PAL_FREQUENCY / (NES_FREQUENCY / 89490.0f))
-
-/****************** tables */
-static const int32_t ModulationTable[8] ICONST_ATTR = {0,1,2,4,0,-4,-2,-1};
-static const uint16_t  DMC_FREQ_TABLE[2][0x10] ICONST_ATTR_NSF_LARGE_IRAM = {
-    /* NTSC */
-    {0x1AC,0x17C,0x154,0x140,0x11E,0x0FE,0x0E2,0x0D6,0x0BE,0x0A0,0x08E,0x080,
-    0x06A,0x054,0x048,0x036},
-    /* PAL */
-    {0x18C,0x160,0x13A,0x128,0x108,0x0EA,0x0D0,0x0C6,0x0B0,0x094,0x082,0x076,
-    0x062,0x04E,0x042,0x032}
-};
-
-static const uint8_t DUTY_CYCLE_TABLE[4] ICONST_ATTR_NSF_LARGE_IRAM = {
-    2,4,8,12
-};
-
-static const uint8_t LENGTH_COUNTER_TABLE[0x20] ICONST_ATTR_NSF_LARGE_IRAM = {
-    0x0A,0xFE,0x14,0x02,0x28,0x04,0x50,0x06,0xA0,0x08,0x3C,0x0A,0x0E,0x0C,0x1A,
-    0x0E,0x0C,0x10,0x18,0x12,0x30,0x14,0x60,0x16,0xC0,0x18,0x48,0x1A,0x10,0x1C,
-    0x20,0x1E
-};
-
-static const uint16_t NOISE_FREQ_TABLE[0x10] ICONST_ATTR_NSF_LARGE_IRAM = {
-    0x004,0x008,0x010,0x020,0x040,0x060,0x080,0x0A0,0x0CA,0x0FE,0x17C,0x1FC,
-    0x2FA,0x3F8,0x7F2,0xFE4
-};
-
-/****************** NSF loading ******************/
-
-/* file format structs (both are little endian) */
-
-struct NESM_HEADER
-{
-    uint32_t        nHeader;
-    uint8_t         nHeaderExtra;
-    uint8_t         nVersion;
-    uint8_t         nTrackCount;
-    uint8_t         nInitialTrack;
-    uint16_t        nLoadAddress;
-    uint16_t        nInitAddress;
-    uint16_t        nPlayAddress;
-    uint8_t         szGameTitle[32];
-    uint8_t         szArtist[32];
-    uint8_t         szCopyright[32];
-    uint16_t        nSpeedNTSC;
-    uint8_t         nBankSwitch[8];
-    uint16_t        nSpeedPAL;
-    uint8_t         nNTSC_PAL;
-    uint8_t         nExtraChip;
-    uint8_t         nExpansion[4];
-};
-
-struct NSFE_INFOCHUNK
-{
-    uint16_t        nLoadAddress;
-    uint16_t        nInitAddress;
-    uint16_t        nPlayAddress;
-    uint8_t         nIsPal;
-    uint8_t         nExt;
-    uint8_t         nTrackCount;
-    uint8_t         nStartingTrack;
-};
-
-static int32_t     LoadFile(uint8_t *,size_t);
-
-static int32_t     LoadFile_NESM(uint8_t *,size_t);
-static int32_t     LoadFile_NSFE(uint8_t *,size_t);
-
-/* NSF file info */
-
-/* basic NSF info */
-static int32_t     bIsExtended=0;      /* 0 = NSF, 1 = NSFE */
-static uint8_t     nIsPal=0;           /* 0 = NTSC, 1 = PAL, 2,3 = mixed
-                                          NTSC/PAL (interpretted as NTSC) */
-static int32_t     nfileLoadAddress=0; /* The address to which the NSF code is
-                                          loaded */
-static int32_t     nfileInitAddress=0; /* The address of the Init routine
-                                          (called at track change) */
-static int32_t     nfilePlayAddress=0; /* The address of the Play routine
-                                          (called several times a second) */
-static uint8_t     nChipExtensions=0;  /* Bitwise representation of the
-                                          external chips used by this NSF.  */
-    
-/* old NESM speed stuff (blarg) */
-static int32_t     nNTSC_PlaySpeed=0;
-static int32_t     nPAL_PlaySpeed=0;
-
-/* track info */
-/* The number of tracks in the NSF (1 = 1 track, 5 = 5 tracks, etc) */
-static int32_t     nTrackCount=0;
-/* The initial track (ZERO BASED:  0 = 1st track, 4 = 5th track, etc) */
-static int32_t     nInitialTrack=0;
-
-/* nsf data */
-static uint8_t*    pDataBuffer=0;      /* the buffer containing NSF code. */
-static int32_t     nDataBufferSize=0;  /* the size of the above buffer. */
-
-/* playlist */
-static uint8_t     nPlaylist[256];     /* Each entry is the zero based index of
-                                          the song to play */
-static int32_t     nPlaylistSize=0;    /* number of tracks in the playlist */
-
-/* track time / fade */
-static int32_t     nTrackTime[256];    /* track times -1 if no track times
-                                          specified */
-static int32_t     nTrackFade[256];    /* track fade times -1 if none are
-                                          specified */
-
-/* string info */
-static uint8_t     szGameTitle[0x101];
-static uint8_t     szArtist[0x101];
-static uint8_t     szCopyright[0x101];
-static uint8_t     szRipper[0x101];
-
-/* bankswitching info */
-static uint8_t     nBankswitch[8]={0}; /* The initial bankswitching registers
-                                          needed for some NSFs. If the NSF does
-                                          not use bankswitching, these values
-                                          will all be zero */
-
-static int32_t     LoadFile(uint8_t * inbuffer, size_t size)
-{
-    if(!inbuffer) return -1;
-
-    int32_t ret = -1;
-
-    if(!memcmp(inbuffer,"NESM",4)) ret = LoadFile_NESM(inbuffer,size);
-    if(!memcmp(inbuffer,"NSFE",4)) ret = LoadFile_NSFE(inbuffer,size);
-
-    /*
-     * Snake's revenge puts '00' for the initial track,
-     * which (after subtracting 1) makes it 256 or -1 (bad!)
-     * This prevents that crap
-     */
-    if(nInitialTrack >= nTrackCount)
-        nInitialTrack = 0;
-    if(nInitialTrack < 0)
-        nInitialTrack = 0;
-
-    /* if there's no tracks... this is a crap NSF */
-    if(nTrackCount < 1)
-    {
-        return -1;
-    }
-
-    return ret;
-}
-
-static int32_t LoadFile_NESM(uint8_t* inbuffer, size_t size)
-{
-    uint8_t ignoreversion=1;
-    uint8_t needdata=1;
-
-    /* read the info */
-    struct NESM_HEADER hdr;
-    
-    memcpy(&hdr,inbuffer,sizeof(hdr));
-
-    /* confirm the header */
-    if(memcmp("NESM",&(hdr.nHeader),4))         return -1;
-    if(hdr.nHeaderExtra != 0x1A)                return -1;
-    /* stupid NSFs claim to be above version 1  >_> */
-    if((!ignoreversion) && (hdr.nVersion != 1)) return -1;
-
-    /* 
-     * NESM is generally easier to work with (but limited!)
-     * just move the data over from NESM_HEADER over to our member data
-     */
-
-    bIsExtended =               0;
-    nIsPal =                    hdr.nNTSC_PAL & 0x03;
-    nPAL_PlaySpeed =            letoh16(hdr.nSpeedPAL);
-    nNTSC_PlaySpeed =           letoh16(hdr.nSpeedNTSC);
-    nfileLoadAddress =          letoh16(hdr.nLoadAddress);
-    nfileInitAddress =          letoh16(hdr.nInitAddress);
-    nfilePlayAddress =          letoh16(hdr.nPlayAddress);
-    nChipExtensions =           hdr.nExtraChip;
-
-
-    nTrackCount =               hdr.nTrackCount;
-    nInitialTrack =             hdr.nInitialTrack - 1;
-
-    memcpy(nBankswitch,hdr.nBankSwitch,8);
-
-    memcpy(szGameTitle,hdr.szGameTitle,32);
-    memcpy(szArtist   ,hdr.szArtist   ,32);
-    memcpy(szCopyright,hdr.szCopyright,32);
-
-    /* read the NSF data */
-    if(needdata)
-    {
-        pDataBuffer=inbuffer+0x80;
-        nDataBufferSize=size-0x80;
-    }
-
-    /* if we got this far... it was a successful read */
-    return 0;
-}
-
-static int32_t LoadFile_NSFE(uint8_t* inbuffer, size_t size)
-{
-    /* the vars we'll be using */
-    uint32_t nChunkType;
-    int32_t  nChunkSize;
-    int32_t  nChunkUsed;
-    int32_t i;
-    uint8_t *  nDataPos = 0;
-    uint8_t bInfoFound = 0;
-    uint8_t bEndFound = 0;
-    uint8_t bBankFound = 0;
-    nPlaylistSize=-1;
-
-    struct NSFE_INFOCHUNK   info;
-    ZEROMEMORY(&info,sizeof(struct NSFE_INFOCHUNK));
-    ZEROMEMORY(nBankswitch,8);
-    info.nTrackCount = 1;       /* default values */
-    
-    if (size < 8) return -1;    /* must have at least NSFE,NEND */
-
-    /* confirm the header! */
-    memcpy(&nChunkType,inbuffer,4);
-    inbuffer+=4;
-    if(memcmp(&nChunkType,"NSFE",4))            return -1;
-
-    for (i=0;i<256;i++) {
-        nTrackTime[i]=-1;
-        nTrackFade[i]=-1;
-    }
-
-    /* begin reading chunks */
-    while(!bEndFound)
-    {
-        memcpy(&nChunkSize,inbuffer,4);
-        nChunkSize=letoh32(nChunkSize);
-        inbuffer+=4;
-        memcpy(&nChunkType,inbuffer,4);
-        inbuffer+=4;
-
-        if(!memcmp(&nChunkType,"INFO",4)) {
-            /* only one info chunk permitted */
-            if(bInfoFound)                      return -1;
-            if(nChunkSize < 8)                  return -1;  /* minimum size */
-
-            bInfoFound = 1;
-            nChunkUsed = MIN((int32_t)sizeof(struct NSFE_INFOCHUNK),
-                             nChunkSize);
-
-            memcpy(&info,inbuffer,nChunkUsed);
-            inbuffer+=nChunkSize;
-
-            bIsExtended =           1;
-            nIsPal =                info.nIsPal & 3;
-            nfileLoadAddress =      letoh16(info.nLoadAddress);
-            nfileInitAddress =      letoh16(info.nInitAddress);
-            nfilePlayAddress =      letoh16(info.nPlayAddress);
-            nChipExtensions =       info.nExt;
-            nTrackCount =           info.nTrackCount;
-            nInitialTrack =         info.nStartingTrack;
-
-            nPAL_PlaySpeed =        (uint16_t)(1000000 / PAL_NMIRATE);
-            nNTSC_PlaySpeed =       (uint16_t)(1000000 / NTSC_NMIRATE);
-        } else if (!memcmp(&nChunkType,"DATA",4)) {
-            if(!bInfoFound)                     return -1;
-            if(nDataPos)                        return -1;
-            if(nChunkSize < 1)                  return -1;
-
-            nDataBufferSize = nChunkSize;
-            nDataPos = inbuffer;
-
-            inbuffer+=nChunkSize;
-        } else if (!memcmp(&nChunkType,"NEND",4)) {
-            bEndFound = 1;
-        } else if (!memcmp(&nChunkType,"time",4)) {
-            if(!bInfoFound)                     return -1;
-            for (nChunkUsed=0; nChunkUsed < MIN(nChunkSize / 4,nTrackCount);
-                 nChunkUsed++,inbuffer+=4) {
-                nTrackTime[nChunkUsed]=
-                    ((uint32_t)inbuffer[0])|
-                    ((uint32_t)inbuffer[1]<<8)|
-                    ((uint32_t)inbuffer[2]<<16)|
-                    ((uint32_t)inbuffer[3]<<24);
-            }
-
-            inbuffer+=nChunkSize-(nChunkUsed*4);
-
-            /* negative signals to use default time */
-            for(; nChunkUsed < nTrackCount; nChunkUsed++)
-                nTrackTime[nChunkUsed] = -1;
-        } else if (!memcmp(&nChunkType,"fade",4)) {
-            if(!bInfoFound)                     return -1;
-            for (nChunkUsed=0; nChunkUsed < MIN(nChunkSize / 4,nTrackCount);
-                 nChunkUsed++,inbuffer+=4) {
-                nTrackFade[nChunkUsed]=
-                    ((uint32_t)inbuffer[0])|
-                    ((uint32_t)inbuffer[1]<<8)|
-                    ((uint32_t)inbuffer[2]<<16)|
-                    ((uint32_t)inbuffer[3]<<24);
-            }
-
-            inbuffer+=nChunkSize-(nChunkUsed*4);
-
-            /* negative signals to use default time */
-            for(; nChunkUsed < nTrackCount; nChunkUsed++)
-                nTrackFade[nChunkUsed] = -1;
-        } else if (!memcmp(&nChunkType,"BANK",4)) {
-            if(bBankFound)                      return -1;
-
-            bBankFound = 1;
-            nChunkUsed = MIN(8,nChunkSize);
-            memcpy(nBankswitch,inbuffer,nChunkUsed);
-
-            inbuffer+=nChunkSize;
-        } else if (!memcmp(&nChunkType,"plst",4)) {
-
-            nPlaylistSize = nChunkSize;
-            if(nPlaylistSize >= 1) {
-
-                memcpy(nPlaylist,inbuffer,nChunkSize);
-                inbuffer+=nChunkSize;
-            }
-        } else if (!memcmp(&nChunkType,"auth",4)) {
-            uint8_t*        ptr;
-
-            ptr = inbuffer;
-
-            uint8_t*    ar[4] = {szGameTitle,szArtist,szCopyright,szRipper};
-            int32_t     i;
-            for(i = 0; (ptr-inbuffer)<nChunkSize && i < 4; i++)
-            {
-                nChunkUsed = strlen(ptr) + 1;
-                memcpy(ar[i],ptr,nChunkUsed);
-                ptr += nChunkUsed;
-            }
-            inbuffer+=nChunkSize;
-        } else if (!memcmp(&nChunkType,"tlbl",4)) {
-            /* we unfortunately can't use these anyway */
-            inbuffer+=nChunkSize;
-        } else { /* unknown chunk */
-            nChunkType = letoh32(nChunkType)>>24;  /* check the first byte */
-            /* chunk is vital... don't continue */
-            if((nChunkType >= 'A') && (nChunkType <= 'Z'))
-                return -1;
-            /* otherwise, just skip it */
-            inbuffer+=nChunkSize;
-        }       /* end if series */
-    }           /* end while */
-
-    /*
-     * if we exited the while loop without a 'return', we must have hit an NEND
-     *  chunk if this is the case, the file was layed out as it was expected.
-     *  now.. make sure we found both an info chunk, AND a data chunk... since
-     *  these are minimum requirements for a valid NSFE file
-     */
-
-    if(!bInfoFound)         return -1;
-    if(!nDataPos)           return -1;
-
-    /* if both those chunks existed, this file is valid.
-       Load the data if it's needed */
-
-    pDataBuffer=nDataPos;
-
-    /* return success! */
-    return 0;
-}
-
-
-/****************** Audio Device Structures ******************/
-
-struct FDSWave
-{
-    /*  Envelope Unit   */
-    uint8_t     bEnvelopeEnable;
-    uint8_t     nEnvelopeSpeed;
-
-    /*  Volume Envelope */
-    uint8_t     nVolEnv_Mode;
-    uint8_t     nVolEnv_Decay;
-    uint8_t     nVolEnv_Gain;
-    int32_t     nVolEnv_Timer;
-    int32_t     nVolEnv_Count;
-    uint8_t     nVolume;
-    uint8_t     bVolEnv_On;
-
-    /*  Sweep Envenlope */
-    uint8_t     nSweep_Mode;
-    uint8_t     nSweep_Decay;
-    int32_t     nSweep_Timer;
-    int32_t     nSweep_Count;
-    uint8_t     nSweep_Gain;
-    uint8_t     bSweepEnv_On;
-
-    /*  Effector / LFO / Modulation Unit    */
-    int32_t     nSweepBias;
-    uint8_t     bLFO_Enabled;
-    union TWIN  nLFO_Freq;
-    /*float       fLFO_Timer;*/
-    /*float       fLFO_Count;*/
-    int32_t    nLFO_Timer; /* -17.14*/
-    int32_t    nLFO_Count; /* -17.14*/
-    uint8_t     nLFO_Addr;
-    uint8_t     nLFO_Table[0x40];
-    uint8_t     bLFO_On;
-
-    /*  Main Output     */
-    uint8_t     nMainVolume;
-    uint8_t     bEnabled;
-    union TWIN  nFreq;
-    /*float       fFreqCount;*/
-    int32_t    nFreqCount; /* -17.14 */
-    uint8_t     nMainAddr;
-    uint8_t     nWaveTable[0x40];
-    uint8_t     bWaveWrite;
-    uint8_t     bMain_On;
-
-    /*  Output and Downsampling */
-    int32_t     nMixL;
-    
-    /*  Pop Reducer             */
-    uint8_t     bPopReducer;
-    uint8_t     nPopOutput;
-    int32_t     nPopCount;
-    
-};
-static int16_t     FDS_nOutputTable_L[4][0x21][0x40] IBSS_ATTR_NSF_LARGE_IRAM MEM_ALIGN_ATTR;
-
-struct FME07Wave
-{
-    /* Frequency Control */
-    union TWIN  nFreqTimer;
-    int32_t     nFreqCount;
-
-    /* Channel Disabling */
-    uint8_t     bChannelEnabled;
-
-    /* Volume */
-    uint8_t     nVolume;
-
-    /* Duty Cycle */
-    uint8_t     nDutyCount;
-
-    /* Output and Downsampling */
-    int32_t     nMixL;
-};
-
-static int16_t     FME07_nOutputTable_L[0x10] IDATA_ATTR MEM_ALIGN_ATTR;
-
-struct N106Wave
-{
-    /*  All Channel Stuff */
-
-    uint8_t     nActiveChannels;
-    uint8_t     bAutoIncrement;
-    uint8_t     nCurrentAddress;
-    uint8_t     nRAM[0x100];      /* internal memory for registers/wave data */
-    int32_t     nFrequencyLookupTable[8]; /* lookup tbl for freq conversions */
-
-    /*
-     *  Individual channel stuff
-     */
-    /*  Wavelength / Frequency */
-    union QUAD  nFreqReg[8];
-    int32_t     nFreqTimer[8];
-    int32_t     nFreqCount[8];
-
-    /*  Wave data length / remaining */
-    uint8_t     nWaveSize[8];
-    uint8_t     nWaveRemaining[8];
-
-    /*  Wave data position */
-    uint8_t     nWavePosStart[8];
-    uint8_t     nWavePos[8];
-    uint8_t     nOutput[8];
-
-    /*  Volume */
-    uint8_t     nVolume[8];
-
-    /*  Pop Reducer */
-    uint8_t     nPreVolume[8];
-    uint8_t     nPopCheck[8];
-
-    /* Mixing */
-    int32_t     nMixL[8];
-};
-
-static int16_t     N106_nOutputTable_L[0x10][0x10] IBSS_ATTR_NSF_LARGE_IRAM MEM_ALIGN_ATTR;
-
-struct VRC6PulseWave
-{
-
-    /* Frequency Control */
-    union TWIN  nFreqTimer;
-    int32_t     nFreqCount;
-
-    /* Flags */
-    uint8_t     bChannelEnabled;
-    uint8_t     bDigitized;
-
-    /* Volume */
-    uint8_t     nVolume;
-
-    /* Duty Cycle */
-    uint8_t     nDutyCycle;
-    uint8_t     nDutyCount;
-
-    /* Output and Downsampling */
-    int32_t     nMixL;
-    
-};
-
-static int16_t     VRC6Pulse_nOutputTable_L[0x10] IDATA_ATTR MEM_ALIGN_ATTR;
-
-struct VRC6SawWave
-{
-
-    /* Frequency Control */
-    union TWIN  nFreqTimer;
-    int32_t     nFreqCount;
-
-    /* Flags */
-    uint8_t     bChannelEnabled;
-
-    /* Phase Accumulator */
-    uint8_t     nAccumRate;
-    uint8_t     nAccum;
-    uint8_t     nAccumStep;
-
-    /* Output and Downsampling */
-    int32_t     nMixL;
-    
-};
-
-static int16_t     VRC6Saw_nOutputTable_L[0x20] IDATA_ATTR MEM_ALIGN_ATTR;
-
-struct Wave_Squares
-{
-
-    /* Programmable Timer */
-    union TWIN  nFreqTimer[2];
-    int32_t     nFreqCount[2];
-
-    /* Length Counter */
-    uint8_t     nLengthCount[2];
-    uint8_t     bLengthEnabled[2];
-    uint8_t     bChannelEnabled[2];
-
-    /* Volume / Decay */
-    uint8_t     nVolume[2];
-    uint8_t     nDecayVolume[2];
-    uint8_t     bDecayEnable[2];
-    uint8_t     bDecayLoop[2];
-    uint8_t     nDecayTimer[2];
-    uint8_t     nDecayCount[2];
-
-    /* Sweep Unit */
-    uint8_t     bSweepEnable[2];
-    uint8_t     bSweepMode[2];
-    uint8_t     bSweepForceSilence[2];
-    uint8_t     nSweepTimer[2];
-    uint8_t     nSweepCount[2];
-    uint8_t     nSweepShift[2];
-
-    /* Duty Cycle */
-    uint8_t     nDutyCount[2];
-    uint8_t     nDutyCycle[2];
-
-    /* Output and Downsampling */
-    int32_t         nMixL;
-};
-
-static int16_t     Squares_nOutputTable_L[0x10][0x10] IDATA_ATTR MEM_ALIGN_ATTR;
-
-struct Wave_TND
-{
-
-    /*
-     * Triangle
-     */
-
-    /* Programmable Timer */
-    union TWIN  nTriFreqTimer;
-    int32_t     nTriFreqCount;
-
-    /* Length Counter */
-    uint8_t     nTriLengthCount;
-    uint8_t     bTriLengthEnabled;
-    uint8_t     bTriChannelEnabled;
-
-    /* Linear Counter */
-    uint8_t     nTriLinearCount;
-    uint8_t     nTriLinearLoad;
-    uint8_t     bTriLinearHalt;
-    uint8_t     bTriLinearControl;
-
-    /* Tri-Step Generator / Output */
-    uint8_t     nTriStep;
-    uint8_t     nTriOutput;
-
-    /*
-     * Noise
-     */
-
-    /* Programmable Timer */
-    uint16_t    nNoiseFreqTimer;
-    int32_t     nNoiseFreqCount;
-
-    /* Length Counter */
-    uint8_t     nNoiseLengthCount;
-    uint8_t     bNoiseLengthEnabled;
-    uint8_t     bNoiseChannelEnabled;
-
-    /* Volume / Decay */
-    uint8_t     nNoiseVolume;
-    uint8_t     nNoiseDecayVolume;
-    uint8_t     bNoiseDecayEnable;
-    uint8_t     bNoiseDecayLoop;
-    uint8_t     nNoiseDecayTimer;
-    uint8_t     nNoiseDecayCount;
-
-    /* Random Number Generator */
-    uint16_t    nNoiseRandomShift;
-    uint8_t     bNoiseRandomMode;           /* 1 = 32k, 6 = 93-bit */
-    uint8_t     bNoiseRandomOut;
-
-    /*
-     * DMC
-     */
-
-    /* Play Mode */
-    uint8_t     bDMCLoop;
-    uint8_t     bDMCIRQEnabled;
-    uint8_t     bDMCIRQPending;
-
-    /* Address / DMA */
-    uint8_t     nDMCDMABank_Load;
-    uint16_t    nDMCDMAAddr_Load;
-    uint8_t     nDMCDMABank;
-    uint16_t    nDMCDMAAddr;
-    uint8_t*    pDMCDMAPtr[8];
-
-    /* Length / Input */
-    uint16_t    nDMCLength;
-    uint16_t    nDMCBytesRemaining;
-    uint8_t     nDMCDelta;
-    uint8_t     nDMCDeltaBit;
-    uint8_t     bDMCDeltaSilent;
-    uint8_t     nDMCSampleBuffer;
-    uint8_t     bDMCSampleBufferEmpty;
-
-    /* Frequency */
-    uint16_t    nDMCFreqTimer;
-    int32_t     nDMCFreqCount;
-
-    /* Output */
-    uint8_t     bDMCActive;
-    uint8_t     nDMCOutput;
-
-    int32_t     nMixL;
-};
-
-/* channels */
-static struct Wave_Squares mWave_Squares IDATA_ATTR; /* Square channels 1 and 2 */
-static struct Wave_TND     mWave_TND IDATA_ATTR;     /* Triangle/Noise/DMC channels */
-static struct VRC6PulseWave    mWave_VRC6Pulse[2] IDATA_ATTR;
-static struct VRC6SawWave  mWave_VRC6Saw IDATA_ATTR;
-static struct N106Wave     mWave_N106 IDATA_ATTR;
-static struct FDSWave      mWave_FDS IDATA_ATTR;
-static struct FME07Wave    mWave_FME07[3] IDATA_ATTR; /* FME-07's 3 pulse channels */
-
-
-/****************** MMC5 ******************/
-/* will include MMC5 sound channels some day,
-   currently only multiply is supported */
-
-/****************** N106 (Disch loves this chip) ******************/
-
-#ifdef ICODE_INSTEAD_OF_INLINE
-static void Wave_N106_DoTicks(const int32_t ticks) ICODE_ATTR;
-static void Wave_N106_DoTicks(const int32_t ticks)
-#else
-static inline void Wave_N106_DoTicks(const int32_t ticks);
-static inline void Wave_N106_DoTicks(const int32_t ticks)
-#endif
-{
-    register int32_t i;
-
-    for(i = (7 - mWave_N106.nActiveChannels); i < 8; i++)
-    {
-        if(!mWave_N106.nFreqReg[i].D)
-        {
-            /* written frequency of zero will cause divide by zero error
-               makes me wonder if the formula was supposed to be Reg+1 */
-            mWave_N106.nVolume[i] = mWave_N106.nPreVolume[i];
-            continue;
-        }
-
-        {
-            mWave_N106.nMixL[i] = 
-                N106_nOutputTable_L[mWave_N106.nVolume[i]]
-                                      [mWave_N106.nOutput[i]];
-            
-            if(mWave_N106.nFreqTimer[i] < 0)
-                mWave_N106.nFreqTimer[i] =
-                 (mWave_N106.nFrequencyLookupTable[mWave_N106.nActiveChannels] /
-                 mWave_N106.nFreqReg[i].D);
-            if(mWave_N106.nFreqCount[i] > mWave_N106.nFreqTimer[i])
-                mWave_N106.nFreqCount[i] = mWave_N106.nFreqTimer[i];
-
-            mWave_N106.nFreqCount[i] -= ticks << 8;
-            while(mWave_N106.nFreqCount[i] <= 0)
-            {
-                mWave_N106.nFreqCount[i] += mWave_N106.nFreqTimer[i];
-                if(mWave_N106.nWaveRemaining[i])
-                {
-                    mWave_N106.nWaveRemaining[i]--;
-                    mWave_N106.nWavePos[i]++;
-                }
-                if(!mWave_N106.nWaveRemaining[i])
-                {
-                    mWave_N106.nWaveRemaining[i] = mWave_N106.nWaveSize[i];
-                    mWave_N106.nWavePos[i] = mWave_N106.nWavePosStart[i];
-                    if(mWave_N106.nVolume[i] != mWave_N106.nPreVolume[i])
-                    {
-                        if(++mWave_N106.nPopCheck[i] >= 2)
-                        {
-                            mWave_N106.nPopCheck[i] = 0;
-                            mWave_N106.nVolume[i] = mWave_N106.nPreVolume[i];
-                        }
-                    }
-                }
-
-                mWave_N106.nOutput[i] =
-                    mWave_N106.nRAM[mWave_N106.nWavePos[i]];
-                    
-                if(!mWave_N106.nOutput[i])
-                {
-                    mWave_N106.nPopCheck[i] = 0;
-                    mWave_N106.nVolume[i] = mWave_N106.nPreVolume[i];
-                }
-                    
-            }
-        }
-    }
-}
-/****************** VRC6 ******************/
-
-#ifdef ICODE_INSTEAD_OF_INLINE
-static void Wave_VRC6_DoTicks(const int32_t ticks) ICODE_ATTR;
-static void Wave_VRC6_DoTicks(const int32_t ticks)
-#else
-static inline void Wave_VRC6_DoTicks(const int32_t ticks);
-static inline void Wave_VRC6_DoTicks(const int32_t ticks)
-#endif
-{
-    register int32_t i;
-
-    for(i = 0; i < 2; i++) {
-
-        if(mWave_VRC6Pulse[i].bChannelEnabled) {
-
-            mWave_VRC6Pulse[i].nFreqCount -= ticks;
-
-            if(mWave_VRC6Pulse[i].nDutyCount <=
-               mWave_VRC6Pulse[i].nDutyCycle)
-            {
-                mWave_VRC6Pulse[i].nMixL =
-                    VRC6Pulse_nOutputTable_L[mWave_VRC6Pulse[i].nVolume];
-            }
-            else
-                mWave_VRC6Pulse[i].nMixL = 0;
-
-            while(mWave_VRC6Pulse[i].nFreqCount <= 0) {
-                mWave_VRC6Pulse[i].nFreqCount +=
-                    mWave_VRC6Pulse[i].nFreqTimer.W + 1;
-
-                if(!mWave_VRC6Pulse[i].bDigitized)
-                    mWave_VRC6Pulse[i].nDutyCount =
-                        (mWave_VRC6Pulse[i].nDutyCount + 1) & 0x0F;
-            }
-        }
-    }
-
-    if(mWave_VRC6Saw.bChannelEnabled) {
-
-        mWave_VRC6Saw.nFreqCount -= ticks;
-
-        mWave_VRC6Saw.nMixL =
-            VRC6Saw_nOutputTable_L[mWave_VRC6Saw.nAccum >> 3];
-
-        while(mWave_VRC6Saw.nFreqCount <= 0) {
-
-            mWave_VRC6Saw.nFreqCount += mWave_VRC6Saw.nFreqTimer.W + 1;
-
-            mWave_VRC6Saw.nAccumStep++;
-            if(mWave_VRC6Saw.nAccumStep == 14)
-            {
-                mWave_VRC6Saw.nAccumStep = 0;
-                mWave_VRC6Saw.nAccum = 0;
-            }
-            else if(!(mWave_VRC6Saw.nAccumStep & 1))
-                mWave_VRC6Saw.nAccum += mWave_VRC6Saw.nAccumRate;
-        }
-    }
-}
-
-/****************** Square waves ******************/
-
-/* decay */
-#ifdef ICODE_INSTEAD_OF_INLINE
-static void Wave_Squares_ClockMajor(void) ICODE_ATTR;
-static void Wave_Squares_ClockMajor()
-#else
-static inline void Wave_Squares_ClockMajor(void);
-static inline void Wave_Squares_ClockMajor()
-#endif
-{
-    if(mWave_Squares.nDecayCount[0])
-        mWave_Squares.nDecayCount[0]--;
-    else
-    {
-        mWave_Squares.nDecayCount[0] = mWave_Squares.nDecayTimer[0];
-        if(mWave_Squares.nDecayVolume[0])
-            mWave_Squares.nDecayVolume[0]--;
-        else
-        {
-            if(mWave_Squares.bDecayLoop[0])
-                mWave_Squares.nDecayVolume[0] = 0x0F;
-        }
-
-        if(mWave_Squares.bDecayEnable[0])
-            mWave_Squares.nVolume[0] = mWave_Squares.nDecayVolume[0];
-    }
-        
-    if(mWave_Squares.nDecayCount[1])
-        mWave_Squares.nDecayCount[1]--;
-    else
-        {
-        mWave_Squares.nDecayCount[1] = mWave_Squares.nDecayTimer[1];
-        if(mWave_Squares.nDecayVolume[1])
-            mWave_Squares.nDecayVolume[1]--;
-        else
-        {
-            if(mWave_Squares.bDecayLoop[1])
-                mWave_Squares.nDecayVolume[1] = 0x0F;
-        }
-
-        if(mWave_Squares.bDecayEnable[1])
-            mWave_Squares.nVolume[1] = mWave_Squares.nDecayVolume[1];
-    }
-        
-}
-
-
-#ifdef ICODE_INSTEAD_OF_INLINE
-static void Wave_Squares_CheckSweepForcedSilence(const int32_t i) ICODE_ATTR;
-static void Wave_Squares_CheckSweepForcedSilence(const int32_t i)
-#else
-static inline void Wave_Squares_CheckSweepForcedSilence(const int32_t i);
-static inline void Wave_Squares_CheckSweepForcedSilence(const int32_t i)
-#endif
-{
-    if(mWave_Squares.nFreqTimer[i].W < 8) {
-        mWave_Squares.bSweepForceSilence[i] = 1; return;
-    }
-    if(!mWave_Squares.bSweepMode[i] &&
-       (( mWave_Squares.nFreqTimer[i].W +
-          (mWave_Squares.nFreqTimer[i].W >> mWave_Squares.nSweepShift[i]))
-       >= 0x0800)) { mWave_Squares.bSweepForceSilence[i] = 1; return; }
-
-    mWave_Squares.bSweepForceSilence[i] = 0;
-}
-
-/* sweep / length */
-#ifdef ICODE_INSTEAD_OF_INLINE
-static void Wave_Squares_ClockMinor(void) ICODE_ATTR;
-static void Wave_Squares_ClockMinor()
-#else
-static inline void Wave_Squares_ClockMinor(void);
-static inline void Wave_Squares_ClockMinor()
-#endif
-{
-/* unrolled a little loop
-   static int i = 0;
-  for(i = 0; i < 2; i++)
-  {
-*/
-    if(mWave_Squares.bLengthEnabled[0] && mWave_Squares.nLengthCount[0])
-            mWave_Squares.nLengthCount[0]--;
-
-    if(!mWave_Squares.bSweepEnable[0] || !mWave_Squares.nLengthCount[0] ||
-        mWave_Squares.bSweepForceSilence[0] || !mWave_Squares.nSweepShift[0])
-        goto other_square;
-
-    if(mWave_Squares.nSweepCount[0])
-        mWave_Squares.nSweepCount[0]--;
-    else
-    {
-        mWave_Squares.nSweepCount[0] = mWave_Squares.nSweepTimer[0];
-        if(mWave_Squares.bSweepMode[0])  mWave_Squares.nFreqTimer[0].W -=
-            (mWave_Squares.nFreqTimer[0].W >> mWave_Squares.nSweepShift[0])+1;
-        else mWave_Squares.nFreqTimer[0].W +=
-            (mWave_Squares.nFreqTimer[0].W >> mWave_Squares.nSweepShift[0]);
-
-        Wave_Squares_CheckSweepForcedSilence(0);
-    }
-        
-    /* */
-other_square:
-    if(mWave_Squares.bLengthEnabled[1] && mWave_Squares.nLengthCount[1])
-        mWave_Squares.nLengthCount[1]--;
-
-    if(!mWave_Squares.bSweepEnable[1] || !mWave_Squares.nLengthCount[1] ||
-        mWave_Squares.bSweepForceSilence[1] || !mWave_Squares.nSweepShift[1])
-        return;
-
-    if(mWave_Squares.nSweepCount[1])
-        mWave_Squares.nSweepCount[1]--;
-    else
-    {
-        mWave_Squares.nSweepCount[1] = mWave_Squares.nSweepTimer[1];
-        if(mWave_Squares.bSweepMode[1])  mWave_Squares.nFreqTimer[1].W -=
-            (mWave_Squares.nFreqTimer[1].W >> mWave_Squares.nSweepShift[1]);
-        else mWave_Squares.nFreqTimer[1].W +=
-            (mWave_Squares.nFreqTimer[1].W >> mWave_Squares.nSweepShift[1]);
-
-        Wave_Squares_CheckSweepForcedSilence(1);
-    }
-}
-
-/****************** Triangle/noise/DMC ******************/
-
-/* decay (noise), linear (tri) */
-
-#ifdef ICODE_INSTEAD_OF_INLINE
-static void Wave_TND_ClockMajor(void) ICODE_ATTR;
-static void Wave_TND_ClockMajor()
-#else
-static inline void Wave_TND_ClockMajor(void);
-static inline void Wave_TND_ClockMajor()
-#endif
-{
-    /* noise's decay */
-    if(mWave_TND.nNoiseDecayCount)
-        mWave_TND.nNoiseDecayCount--;
-    else
-    {
-        mWave_TND.nNoiseDecayCount = mWave_TND.nNoiseDecayTimer;
-        if(mWave_TND.nNoiseDecayVolume)
-            mWave_TND.nNoiseDecayVolume--;
-        else
-        {
-            if(mWave_TND.bNoiseDecayLoop)
-                mWave_TND.nNoiseDecayVolume = 0x0F;
-        }
-
-        if(mWave_TND.bNoiseDecayEnable)
-            mWave_TND.nNoiseVolume = mWave_TND.nNoiseDecayVolume;
-    }
-
-    /* triangle's linear */
-    if(mWave_TND.bTriLinearHalt)
-        mWave_TND.nTriLinearCount = mWave_TND.nTriLinearLoad;
-    else if(mWave_TND.nTriLinearCount)
-        mWave_TND.nTriLinearCount--;
-
-    if(!mWave_TND.bTriLinearControl)
-        mWave_TND.bTriLinearHalt = 0;
-}
-
-/* length */
-
-#ifdef ICODE_INSTEAD_OF_INLINE
-static void Wave_TND_ClockMinor(void) ICODE_ATTR;
-static void Wave_TND_ClockMinor()
-#else
-static inline void Wave_TND_ClockMinor(void);
-static inline void Wave_TND_ClockMinor()
-#endif
-{
-    if(mWave_TND.bNoiseLengthEnabled && mWave_TND.nNoiseLengthCount)
-        mWave_TND.nNoiseLengthCount--;
-        
-    if(mWave_TND.bTriLengthEnabled && mWave_TND.nTriLengthCount)
-        mWave_TND.nTriLengthCount--;
-}
-
-/*#undef this*/
-
-/****************** NSF Core ******************/
-
-/* start globals */
-
-/*
- *  Memory
- */
-/* RAM:      0x0000 - 0x07FF */
-static uint8_t     pRAM[0x800] IBSS_ATTR_NSF_LARGE_IRAM MEM_ALIGN_ATTR;
-/* SRAM:     0x6000 - 0x7FFF (non-FDS only) */
-static uint8_t     pSRAM[0x2000] IBSS_ATTR_NSF_LARGE_IRAM MEM_ALIGN_ATTR;
-/* ExRAM:    0x5C00 - 0x5FF5 (MMC5 only)
- * Also holds NSF player code (at 0x5000 - 0x500F) */
-static uint8_t     pExRAM[0x1000] IBSS_ATTR_NSF_LARGE_IRAM MEM_ALIGN_ATTR;
-/* Full ROM buffer */
-static uint8_t*    pROM_Full IDATA_ATTR;
-
-static uint16_t    main_nOutputTable_L[0x8000] MEM_ALIGN_ATTR;
-
-static uint8_t*    pROM[10] IDATA_ATTR;/* ROM banks (point to areas in pROM_Full) */
-                                       /* 0x8000 - 0xFFFF */
-                                       /* also includes 0x6000 - 0x7FFF (FDS only) */
-static uint8_t*        pStack;         /* the stack (points to areas in pRAM) */
-                                       /* 0x0100 - 0x01FF */
-
-static int32_t         nROMSize;       /* size of this ROM file in bytes */
-static int32_t         nROMBankCount;  /* max number of 4k banks */
-static int32_t         nROMMaxSize;    /* size of allocated pROM_Full buffer */
-
-/*
- *  Memory Proc Pointers
- */
- 
-typedef uint8_t ( *ReadProc)(uint16_t);
-typedef void ( *WriteProc)(uint16_t,uint8_t);
-static ReadProc    ReadMemory[0x10] IDATA_ATTR MEM_ALIGN_ATTR;
-static WriteProc   WriteMemory[0x10] IDATA_ATTR MEM_ALIGN_ATTR;
-
-/*
- *  6502 Registers / Mode
- */
-
-static uint8_t     regA IDATA_ATTR;        /* Accumulator */
-static uint8_t     regX IDATA_ATTR;        /* X-Index */
-static uint8_t     regY IDATA_ATTR;        /* Y-Index */
-static uint8_t     regP IDATA_ATTR;        /* Processor Status */
-static uint8_t     regSP IDATA_ATTR;       /* Stack Pointer */
-static uint16_t    regPC IDATA_ATTR;       /* Program Counter */
-
-static uint8_t     bPALMode IDATA_ATTR;/* 1 if in PAL emulation mode, 0 if in NTSC */
-static uint8_t     bCPUJammed IDATA_ATTR;  /* 0 = not jammed.  1 = really
-                                              jammed. 2 = 'fake' jammed */
-                                  /* fake jam caused by the NSF code to signal
-                                   * the end of the play/init routine */
-
-/* Multiplication Register, for MMC5 chip only (5205+5206) */
-static uint8_t     nMultIn_Low;
-static uint8_t     nMultIn_High;
-
-/*
- *  NSF Preparation Information
- */
-
-static uint8_t     nBankswitchInitValues[10];  /* banks to swap to on tune init */
-static uint16_t    nPlayAddress;               /* Play routine address */
-static uint16_t    nInitAddress;               /* Init routine address */
-
-static uint8_t     nExternalSound;             /* external sound chips */
-static uint8_t     nCurTrack;
-
-static float       fNSFPlaybackSpeed;
-
-/*
- *  pAPU
- */
-
-static uint8_t     nFrameCounter;      /* Frame Sequence Counter */
-static uint8_t     nFrameCounterMax;   /* Frame Sequence Counter Size
-                                          (3 or 4 depending on $4017.7) */
-static uint8_t     bFrameIRQEnabled;   /* TRUE if frame IRQs are enabled */
-static uint8_t     bFrameIRQPending;   /* TRUE if the frame sequencer is
-                                          holding down an IRQ */
-
-static uint8_t         nFME07_Address;
-
-/*
- *  Timing and Counters
- */
-/* fixed point -15.16 */
-
-static int32_t     nTicksUntilNextFrame;
-static int32_t     nTicksPerPlay;
-static int32_t     nTicksUntilNextPlay;
-static int32_t     nTicksPerSample;
-static int32_t     nTicksUntilNextSample;
-
-static uint32_t    nCPUCycle IDATA_ATTR;
-static uint32_t    nAPUCycle IDATA_ATTR;
-
-   
-static uint32_t    nTotalPlays; /* number of times the play subroutine has been
-                                   called (for tracking output time) */
-/*
- *  Silence Tracker
- */
-static int32_t     nSilentSamples;
-static int32_t     nSilentSampleMax;
-static int32_t     nSilenceTrackMS;
-static uint8_t     bNoSilenceIfTime;
-static uint8_t     bTimeNotDefault;
-
-/*
- *  Sound output options
- */
-static const int32_t       nSampleRate=44100;
-
-/*
- *  Volume/fading/filter tracking
- */
-
-static uint32_t        nStartFade; /* play call to start fading out */
-static uint32_t        nEndFade;   /* play call to stop fading out (song is over) */
-static uint8_t         bFade;      /* are we fading? */
-static float           fFadeVolume;
-static float           fFadeChange;
-
-/*
- *  Designated Output Buffer
- */
-static uint8_t*        pOutput IDATA_ATTR;
-
-static const uint8_t   bDMCPopReducer=1;
-static uint8_t         nDMCPop_Prev IDATA_ATTR = 0;
-static uint8_t         bDMCPop_Skip IDATA_ATTR = 0;
-static uint8_t         bDMCPop_SamePlay IDATA_ATTR = 0;
-
-static const uint8_t   nForce4017Write=0;
-static const uint8_t   bN106PopReducer=0;
-static const uint8_t   bIgnore4011Writes=0;
-    
-static const uint8_t   bIgnoreBRK=0;
-static const uint8_t   bIgnoreIllegalOps=0;
-static const uint8_t   bNoWaitForReturn=0;
-static const uint8_t   bPALPreference=0;
-static const uint8_t   bCleanAXY=0;
-static const uint8_t   bResetDuty=0;
-
-/*
- *  Sound Filter
- */
-
-static int64_t     nFilterAccL IDATA_ATTR;
-static int64_t     nHighPass IDATA_ATTR;
-
-static int32_t     nHighPassBase IDATA_ATTR;
-
-static uint8_t     bHighPassEnabled IDATA_ATTR;
-
-/* end globals */
-
-#define CLOCK_MAJOR() { Wave_Squares_ClockMajor(); Wave_TND_ClockMajor(); }
-#define CLOCK_MINOR() { Wave_Squares_ClockMinor(); Wave_TND_ClockMinor(); }
-
-#define EXTSOUND_VRC6           0x01
-#define EXTSOUND_VRC7           0x02
-#define EXTSOUND_FDS            0x04
-#define EXTSOUND_MMC5           0x08
-#define EXTSOUND_N106           0x10
-#define EXTSOUND_FME07          0x20
-
-#define SILENCE_THRESHOLD       3
-
-/*
- *  prototypes
- */
- 
-static uint32_t Emulate6502(uint32_t runto) ICODE_ATTR;
-static void EmulateAPU(uint8_t bBurnCPUCycles) ICODE_ATTR;
-
-static int     NSFCore_Initialize(void); /* 1 = initialized ok,
-                           0 = couldn't initialize (memory allocation error) */
-
-/*
- *  Song Loading
- */
-static int     LoadNSF(int32_t);   /* grab data from an existing file
-                                      1 = loaded ok, 0 = error loading */
-
-/*
- *  Track Control
- */
-static void    SetTrack(uint8_t track);  /* Change tracks */
-
-/*
- *  Getting Samples
- */
-/* fill a buffer with samples */
-static int32_t     GetSamples(uint8_t* buffer, int32_t buffersize);
-
-/*
- *  Playback options
- */
-/* Set desired playback options (0 = bad options couldn't be set) */
-static int     SetPlaybackOptions(int32_t samplerate);
-/* Speed throttling (0 = uses NSF specified speed) */
-static void    SetPlaybackSpeed(float playspersec);
-
-static float   GetPlaybackSpeed(void);
-/* rockbox: not used
-float   GetMasterVolume(void); */
-
-/* rockbox: not used */
-#if 0
-/*
- *  Seeking
- */
-/* gets the number of 'play' routine calls executed */
-float   GetPlayCalls(void);
-
-/* gets the output time (based on the given play rate,
-   if basedplayspersec is zero, current playback speed is used */
-uint32_t    GetWrittenTime(float basedplayspersec);
-/* sets the number of 'plays' routines executed (for precise seeking) */
-void    SetPlayCalls(float plays);
-/* sets the written time (approx. seeking) */
-void    SetWrittenTime(uint32_t ms,float basedplays);
-#endif
-
-/*
- *  Fading
- */
-
-/* rockbox: not used
-void    StopFade(void); */      /* stops all fading (plays indefinitely) */
-static uint8_t SongCompleted(void);    /* song has faded out (samples have
-                                          stopped being generated) */
-/* parameters are play calls */
-static void    SetFade(int32_t fadestart,int32_t fadestop,uint8_t bNotDefault);
-static void    SetFadeTime(uint32_t fadestart,uint32_t fadestop,
-                           float basedplays, uint8_t bNotDefault);
-                          /* parameters are in milliseconds */
-
-/*
- *  Internal Functions
- */
-static void    RebuildOutputTables(void);
-static void    RecalculateFade(void);  /* called when fade status is changed. */
-static void    RecalcFilter(void);
-static void    RecalcSilenceTracker(void);
-
-static void    WriteMemory_VRC6(uint16_t a,uint8_t v) ICODE_ATTR;
-static void    WriteMemory_MMC5(uint16_t a,uint8_t v) ICODE_ATTR;
-static void    WriteMemory_N106(uint16_t a,uint8_t v) ICODE_ATTR;
-static void    WriteMemory_FME07(uint16_t a,uint8_t v) ICODE_ATTR;
-
-/*
- *  Memory Read/Write routines
- */
-
-static uint8_t     ReadMemory_RAM(uint16_t a) ICODE_ATTR;
-static uint8_t     ReadMemory_ExRAM(uint16_t a) ICODE_ATTR;
-static uint8_t     ReadMemory_SRAM(uint16_t a) ICODE_ATTR;
-static uint8_t     ReadMemory_pAPU(uint16_t a) ICODE_ATTR;
-static uint8_t     ReadMemory_ROM(uint16_t a) ICODE_ATTR;
-static uint8_t     ReadMemory_Default(uint16_t a) ICODE_ATTR;
-
-static uint8_t     ReadMemory_N106(uint16_t a) ICODE_ATTR;
-
-static void        WriteMemory_RAM(uint16_t a,uint8_t v) ICODE_ATTR;
-static void        WriteMemory_ExRAM(uint16_t a,uint8_t v) ICODE_ATTR;
-static void        WriteMemory_SRAM(uint16_t a,uint8_t v) ICODE_ATTR;
-static void        WriteMemory_pAPU(uint16_t a,uint8_t v) ICODE_ATTR;
-static void        WriteMemory_FDSRAM(uint16_t a,uint8_t v) ICODE_ATTR;
-static void        WriteMemory_Default(uint16_t a,uint8_t v) ICODE_ATTR;
-
-static uint8_t         ReadMemory_RAM(uint16_t a)      { return pRAM[a & 0x07FF]; }
-static uint8_t         ReadMemory_ExRAM(uint16_t a)    { return pExRAM[a & 0x0FFF]; }
-static uint8_t         ReadMemory_SRAM(uint16_t a)     { return pSRAM[a & 0x1FFF]; }
-static uint8_t         ReadMemory_ROM(uint16_t a)
-    { return pROM[(a >> 12) - 6][a & 0x0FFF]; }
-static uint8_t         ReadMemory_Default(uint16_t a)  { return (a >> 8); }
-
-static void        WriteMemory_RAM(uint16_t a,uint8_t v)
-    { pRAM[a & 0x07FF] = v; }
-static void        WriteMemory_ExRAM(uint16_t a,uint8_t v);
-static void        WriteMemory_SRAM(uint16_t a,uint8_t v)
-    { pSRAM[a & 0x1FFF] = v; }
-static void        WriteMemory_FDSRAM(uint16_t a,uint8_t v)
-    { pROM[(a >> 12) - 6][a & 0x0FFF] = v; }
-static void        WriteMemory_Default(uint16_t a,uint8_t v)   { (void)a; (void)v; }
-
-
-/* Read Memory Procs */
-
-static uint8_t  ReadMemory_pAPU(uint16_t a)
-{
-    EmulateAPU(1);
-
-    if(a == 0x4015)
-    {
-        uint8_t ret = 0;
-        if(mWave_Squares.nLengthCount[0])       ret |= 0x01;
-        if(mWave_Squares.nLengthCount[1])       ret |= 0x02;
-        if(mWave_TND.nTriLengthCount)           ret |= 0x04;
-        if(mWave_TND.nNoiseLengthCount)         ret |= 0x08;
-        if(mWave_TND.nDMCBytesRemaining)        ret |= 0x10;
-
-        if(bFrameIRQPending)            ret |= 0x40;
-        if(mWave_TND.bDMCIRQPending)            ret |= 0x80;
-
-        bFrameIRQPending = 0;
-        return ret;
-    }
-
-    if(!(nExternalSound & EXTSOUND_FDS))        return 0x40;
-    if(bPALMode)                                return 0x40;
-
-    if((a >= 0x4040) && (a <= 0x407F))
-        return mWave_FDS.nWaveTable[a & 0x3F] | 0x40;
-    if(a == 0x4090)
-        return (mWave_FDS.nVolEnv_Gain & 0x3F) | 0x40;
-    if(a == 0x4092)
-        return (mWave_FDS.nSweep_Gain & 0x3F) | 0x40;
-
-    return 0x40;
-}
-
-static uint8_t  ReadMemory_N106(uint16_t a)
-{
-    if(a != 0x4800)
-        return ReadMemory_pAPU(a);
-
-    uint8_t ret = mWave_N106.nRAM[(mWave_N106.nCurrentAddress << 1)] |
-        (mWave_N106.nRAM[(mWave_N106.nCurrentAddress << 1) + 1] << 4);
-    if(mWave_N106.bAutoIncrement)
-        mWave_N106.nCurrentAddress = (mWave_N106.nCurrentAddress + 1) & 0x7F;
-
-    return ret;
-}
-
-
-/* Write Memory Procs */
-
-static void  WriteMemory_ExRAM(uint16_t a,uint8_t v)
-{
-    if(a < 0x5FF6)              /* Invalid */
-        return;
-
-    a -= 0x5FF6;
-
-    /* Swap out banks */
-
-    EmulateAPU(1);
-    /* stop it from swapping to a bank that doesn't exist */
-    if(v >= nROMBankCount)
-        v = 0;
-
-    pROM[a] = pROM_Full + (v << 12);
-
-    /* Update the DMC's DMA pointer, as well */
-    if(a >= 2)
-        mWave_TND.pDMCDMAPtr[a - 2] = pROM[a];
-}
-
-static void  WriteMemory_pAPU(uint16_t a,uint8_t v)
-{
-    EmulateAPU(1);
-    switch(a)
-    {
-    /* Square 1 */
-    case 0x4000:
-        mWave_Squares.nDutyCycle[0] = DUTY_CYCLE_TABLE[v >> 6];
-        mWave_Squares.bLengthEnabled[0] =
-            !(mWave_Squares.bDecayLoop[0] = (v & 0x20));
-        mWave_Squares.bDecayEnable[0] = !(v & 0x10);
-        mWave_Squares.nDecayTimer[0] = (v & 0x0F);
-
-        if(!mWave_Squares.bDecayEnable[0])
-            mWave_Squares.nVolume[0] = mWave_Squares.nDecayTimer[0];
-        break;
-
-    case 0x4001:
-        mWave_Squares.bSweepEnable[0] = (v & 0x80);
-        mWave_Squares.nSweepTimer[0] = (v & 0x70) >> 4;
-        mWave_Squares.bSweepMode[0] = v & 0x08;
-        mWave_Squares.nSweepShift[0] = v & 0x07;
-        Wave_Squares_CheckSweepForcedSilence(0);
-        break;
-        
-    case 0x4002:
-        mWave_Squares.nFreqTimer[0].B.l = v;
-        Wave_Squares_CheckSweepForcedSilence(0);
-        break;
-        
-    case 0x4003:
-        mWave_Squares.nFreqTimer[0].B.h = v & 0x07;
-        Wave_Squares_CheckSweepForcedSilence(0);
-
-        mWave_Squares.nDecayVolume[0] = 0x0F;
-
-        if(mWave_Squares.bChannelEnabled[0])
-            mWave_Squares.nLengthCount[0] = LENGTH_COUNTER_TABLE[v >> 3];
-
-        if(bResetDuty)
-            mWave_Squares.nDutyCount[0] = 0;
-        break;
-        
-
-    /* Square 2 */
-    case 0x4004:
-        mWave_Squares.nDutyCycle[1] = DUTY_CYCLE_TABLE[v >> 6];
-        mWave_Squares.bLengthEnabled[1] =
-            !(mWave_Squares.bDecayLoop[1] = (v & 0x20));
-        mWave_Squares.bDecayEnable[1] = !(v & 0x10);
-        mWave_Squares.nDecayTimer[1] = (v & 0x0F);
-
-        if(!mWave_Squares.bDecayEnable[1])
-            mWave_Squares.nVolume[1] = mWave_Squares.nDecayTimer[1];
-        break;
-
-    case 0x4005:
-        mWave_Squares.bSweepEnable[1] = (v & 0x80);
-        mWave_Squares.nSweepTimer[1] = (v & 0x70) >> 4;
-        mWave_Squares.bSweepMode[1] = v & 0x08;
-        mWave_Squares.nSweepShift[1] = v & 0x07;
-        Wave_Squares_CheckSweepForcedSilence(1);
-        break;
-        
-    case 0x4006:
-        mWave_Squares.nFreqTimer[1].B.l = v;
-        Wave_Squares_CheckSweepForcedSilence(1);
-        break;
-        
-    case 0x4007:
-        mWave_Squares.nFreqTimer[1].B.h = v & 0x07;
-        Wave_Squares_CheckSweepForcedSilence(1);
-
-        mWave_Squares.nDecayVolume[1] = 0x0F;
-
-        if(mWave_Squares.bChannelEnabled[1])
-            mWave_Squares.nLengthCount[1] = LENGTH_COUNTER_TABLE[v >> 3];
-
-        if(bResetDuty)
-            mWave_Squares.nDutyCount[1] = 0;
-        break;
-
-        
-    /* Triangle */
-    case 0x4008:
-        mWave_TND.nTriLinearLoad = v & 0x7F;
-        mWave_TND.bTriLinearControl = v & 0x80;
-        mWave_TND.bTriLengthEnabled = !(v & 0x80);
-        break;
-
-    case 0x400A:
-        mWave_TND.nTriFreqTimer.B.l = v;
-        break;
-
-    case 0x400B:
-        mWave_TND.nTriFreqTimer.B.h = v & 0x07;
-        mWave_TND.bTriLinearHalt = 1;
-        
-        if(mWave_TND.bTriChannelEnabled)
-            mWave_TND.nTriLengthCount = LENGTH_COUNTER_TABLE[v >> 3];
-        break;
-
-    /* Noise */
-    case 0x400C:
-        mWave_TND.bNoiseLengthEnabled =
-            !(mWave_TND.bNoiseDecayLoop = (v & 0x20));
-        mWave_TND.bNoiseDecayEnable = !(v & 0x10);
-        mWave_TND.nNoiseDecayTimer = (v & 0x0F);
-
-        if(mWave_TND.bNoiseDecayEnable)
-            mWave_TND.nNoiseVolume = mWave_TND.nNoiseDecayVolume;
-        else
-            mWave_TND.nNoiseVolume = mWave_TND.nNoiseDecayTimer;
-        break;
-
-    case 0x400E:
-        mWave_TND.nNoiseFreqTimer = NOISE_FREQ_TABLE[v & 0x0F];
-        mWave_TND.bNoiseRandomMode = (v & 0x80) ? 6 : 1;
-        break;
-
-    case 0x400F:
-        if(mWave_TND.bNoiseChannelEnabled)
-            mWave_TND.nNoiseLengthCount = LENGTH_COUNTER_TABLE[v >> 3];
-
-        mWave_TND.nNoiseDecayVolume = 0x0F;
-        if(mWave_TND.bNoiseDecayEnable)
-            mWave_TND.nNoiseVolume = 0x0F;
-        break;
-
-    /* DMC */
-    case 0x4010:
-        mWave_TND.bDMCLoop = v & 0x40;
-        mWave_TND.bDMCIRQEnabled = v & 0x80;
-        /* IRQ can't be pending if disabled */
-        if(!mWave_TND.bDMCIRQEnabled)
-            mWave_TND.bDMCIRQPending = 0;
-
-        mWave_TND.nDMCFreqTimer = DMC_FREQ_TABLE[bPALMode][v & 0x0F];
-        break;
-
-    case 0x4011:
-        if(bIgnore4011Writes)
-            break;
-        v &= 0x7F;
-        if(bDMCPopReducer)
-        {
-            if(bDMCPop_SamePlay)
-                mWave_TND.nDMCOutput = v;
-            else
-            {
-                if(bDMCPop_Skip)
-                {
-                    bDMCPop_Skip = 0;
-                    break;
-                }
-                if(nDMCPop_Prev == v) break;
-                if(mWave_TND.nDMCOutput == v) break;
-                mWave_TND.nDMCOutput = nDMCPop_Prev;
-                nDMCPop_Prev = v;
-                bDMCPop_SamePlay = 1;
-            }
-        }
-        else
-            mWave_TND.nDMCOutput = v;
-        break;
-
-    case 0x4012:
-        mWave_TND.nDMCDMABank_Load = (v >> 6) | 0x04;
-        mWave_TND.nDMCDMAAddr_Load = (v << 6) & 0x0FFF;
-        break;
-
-    case 0x4013:
-        mWave_TND.nDMCLength = (v << 4) + 1;
-        break;
-
-    /* All / General Purpose */
-    case 0x4015:
-        mWave_TND.bDMCIRQPending = 0;
-
-        if(v & 0x01){   mWave_Squares.bChannelEnabled[0] =  1;  }
-        else        {   mWave_Squares.bChannelEnabled[0] =
-                        mWave_Squares.nLengthCount[0] =     0;  }
-        if(v & 0x02){   mWave_Squares.bChannelEnabled[1] =  1;  }
-        else        {   mWave_Squares.bChannelEnabled[1] =
-                        mWave_Squares.nLengthCount[1] =     0;  }
-        if(v & 0x04){   mWave_TND.bTriChannelEnabled =      1;  }
-        else        {   mWave_TND.bTriChannelEnabled =
-                        mWave_TND.nTriLengthCount =         0;  }
-        if(v & 0x08){   mWave_TND.bNoiseChannelEnabled =    1;  }
-        else        {   mWave_TND.bNoiseChannelEnabled =
-                        mWave_TND.nNoiseLengthCount =       0;  }
-
-        if(v & 0x10)
-        {
-            if(!mWave_TND.nDMCBytesRemaining)
-            {
-                bDMCPop_Skip = 1;
-                mWave_TND.nDMCDMAAddr = mWave_TND.nDMCDMAAddr_Load;
-                mWave_TND.nDMCDMABank = mWave_TND.nDMCDMABank_Load;
-                mWave_TND.nDMCBytesRemaining = mWave_TND.nDMCLength;
-                mWave_TND.bDMCActive = 1;
-            }
-        }
-        else
-            mWave_TND.nDMCBytesRemaining = 0;
-        break;
-
-    case 0x4017:
-        bFrameIRQEnabled = !(v & 0x40);
-        bFrameIRQPending = 0;
-        nFrameCounter = 0;
-        nFrameCounterMax = (v & 0x80) ? 4 : 3;
-        nTicksUntilNextFrame =
-            (bPALMode ? PAL_FRAME_COUNTER_FREQ : NTSC_FRAME_COUNTER_FREQ)
-            * 0x10000;
-
-        CLOCK_MAJOR();
-        if(v & 0x80) CLOCK_MINOR();
-        break;
-    }
-
-    if(!(nExternalSound & EXTSOUND_FDS))        return;
-    if(bPALMode)                                return;
-
-    /* FDS Sound registers */
-
-    if(a < 0x4040)      return;
-
-    /* wave table */
-    if(a <= 0x407F)
-    {
-        if(mWave_FDS.bWaveWrite)
-            mWave_FDS.nWaveTable[a - 0x4040] = v;
-    }
-    else
-    {
-        switch(a)
-        {
-        case 0x4080:
-            mWave_FDS.nVolEnv_Mode = (v >> 6);
-            if(v & 0x80)
-            {
-                mWave_FDS.nVolEnv_Gain = v & 0x3F;
-                if(!mWave_FDS.nMainAddr)
-                {
-                    if(mWave_FDS.nVolEnv_Gain < 0x20)
-                        mWave_FDS.nVolume = mWave_FDS.nVolEnv_Gain;
-                    else mWave_FDS.nVolume = 0x20;
-                }
-            }
-            mWave_FDS.nVolEnv_Decay = v & 0x3F;
-            mWave_FDS.nVolEnv_Timer =
-                ((mWave_FDS.nVolEnv_Decay + 1) * mWave_FDS.nEnvelopeSpeed * 8);
-
-            mWave_FDS.bVolEnv_On = mWave_FDS.bEnvelopeEnable &&
-                mWave_FDS.nEnvelopeSpeed && !(v & 0x80);
-            break;
-
-        case 0x4082:
-            mWave_FDS.nFreq.B.l = v;
-            mWave_FDS.bMain_On = mWave_FDS.nFreq.W && mWave_FDS.bEnabled &&
-                !mWave_FDS.bWaveWrite;
-            break;
-
-        case 0x4083:
-            mWave_FDS.bEnabled =        !(v & 0x80);
-            mWave_FDS.bEnvelopeEnable = !(v & 0x40);
-            if(v & 0x80)
-            {
-                if(mWave_FDS.nVolEnv_Gain < 0x20)
-                    mWave_FDS.nVolume = mWave_FDS.nVolEnv_Gain;
-                else mWave_FDS.nVolume = 0x20;
-            }
-            mWave_FDS.nFreq.B.h = v & 0x0F;
-            mWave_FDS.bMain_On = mWave_FDS.nFreq.W && mWave_FDS.bEnabled &&
-                !mWave_FDS.bWaveWrite;
-
-            mWave_FDS.bVolEnv_On = mWave_FDS.bEnvelopeEnable &&
-                mWave_FDS.nEnvelopeSpeed && !(mWave_FDS.nVolEnv_Mode & 2);
-            mWave_FDS.bSweepEnv_On = mWave_FDS.bEnvelopeEnable &&
-                mWave_FDS.nEnvelopeSpeed && !(mWave_FDS.nSweep_Mode & 2);
-            break;
-
-
-        case 0x4084:
-            mWave_FDS.nSweep_Mode = v >> 6;
-            if(v & 0x80)
-                mWave_FDS.nSweep_Gain = v & 0x3F;
-            mWave_FDS.nSweep_Decay = v & 0x3F;
-            mWave_FDS.nSweep_Timer =
-                ((mWave_FDS.nSweep_Decay + 1) * mWave_FDS.nEnvelopeSpeed * 8);
-            mWave_FDS.bSweepEnv_On =
-                mWave_FDS.bEnvelopeEnable && mWave_FDS.nEnvelopeSpeed &&
-                !(v & 0x80);
-            break;
-
-
-        case 0x4085:
-            if(v & 0x40)    mWave_FDS.nSweepBias = (v & 0x3F) - 0x40;
-            else            mWave_FDS.nSweepBias = v & 0x3F;
-            mWave_FDS.nLFO_Addr = 0;
-            break;
-
-
-        case 0x4086:
-            mWave_FDS.nLFO_Freq.B.l = v;
-            mWave_FDS.bLFO_On =
-                mWave_FDS.bLFO_Enabled && mWave_FDS.nLFO_Freq.W;
-            if(mWave_FDS.nLFO_Freq.W)
-                mWave_FDS.nLFO_Timer = (0x10000<<14) / mWave_FDS.nLFO_Freq.W;
-            break;
-
-        case 0x4087:
-            mWave_FDS.bLFO_Enabled = !(v & 0x80);
-            mWave_FDS.nLFO_Freq.B.h = v & 0x0F;
-            mWave_FDS.bLFO_On =
-                mWave_FDS.bLFO_Enabled && mWave_FDS.nLFO_Freq.W;
-            if(mWave_FDS.nLFO_Freq.W)
-                mWave_FDS.nLFO_Timer = (0x10000<<14) / mWave_FDS.nLFO_Freq.W;
-            break;
-
-        case 0x4088:
-            if(mWave_FDS.bLFO_Enabled)  break;
-            register int32_t i;
-            for(i = 0; i < 62; i++)
-                mWave_FDS.nLFO_Table[i] = mWave_FDS.nLFO_Table[i + 2];
-            mWave_FDS.nLFO_Table[62] = mWave_FDS.nLFO_Table[63] = v & 7;
-            break;
-
-        case 0x4089:
-            mWave_FDS.nMainVolume = v & 3;
-            mWave_FDS.bWaveWrite = v & 0x80;
-            mWave_FDS.bMain_On = mWave_FDS.nFreq.W && mWave_FDS.bEnabled &&
-                !mWave_FDS.bWaveWrite;
-            break;
-
-        case 0x408A:
-            mWave_FDS.nEnvelopeSpeed = v;
-            mWave_FDS.bVolEnv_On =
-                mWave_FDS.bEnvelopeEnable &&
-                mWave_FDS.nEnvelopeSpeed && !(mWave_FDS.nVolEnv_Mode & 2);
-            mWave_FDS.bSweepEnv_On =
-                mWave_FDS.bEnvelopeEnable &&
-                mWave_FDS.nEnvelopeSpeed && !(mWave_FDS.nSweep_Mode & 2);
-            break;
-        }
-    }
-}
-
-static void  WriteMemory_VRC6(uint16_t a,uint8_t v)
-{
-    EmulateAPU(1);
-
-    if((a < 0xA000) && (nExternalSound & EXTSOUND_VRC7)) return;
-    else if(nExternalSound & EXTSOUND_FDS)
-        WriteMemory_FDSRAM(a,v);
-
-    switch(a)
-    {
-    /* Pulse 1 */
-    case 0x9000:
-        mWave_VRC6Pulse[0].nVolume = v & 0x0F;
-        mWave_VRC6Pulse[0].nDutyCycle = (v >> 4) & 0x07;
-        mWave_VRC6Pulse[0].bDigitized = v & 0x80;
-        if(mWave_VRC6Pulse[0].bDigitized)
-            mWave_VRC6Pulse[0].nDutyCount = 0;
-        break;
-
-    case 0x9001:
-        mWave_VRC6Pulse[0].nFreqTimer.B.l = v;
-        break;
-
-    case 0x9002:
-        mWave_VRC6Pulse[0].nFreqTimer.B.h = v & 0x0F;
-        mWave_VRC6Pulse[0].bChannelEnabled = v & 0x80;
-        break;
-        
-
-    /* Pulse 2 */
-    case 0xA000:
-        mWave_VRC6Pulse[1].nVolume = v & 0x0F;
-        mWave_VRC6Pulse[1].nDutyCycle = (v >> 4) & 0x07;
-        mWave_VRC6Pulse[1].bDigitized = v & 0x80;
-        if(mWave_VRC6Pulse[1].bDigitized)
-            mWave_VRC6Pulse[1].nDutyCount = 0;
-        break;
-
-    case 0xA001:
-        mWave_VRC6Pulse[1].nFreqTimer.B.l = v;
-        break;
-
-    case 0xA002:
-        mWave_VRC6Pulse[1].nFreqTimer.B.h = v & 0x0F;
-        mWave_VRC6Pulse[1].bChannelEnabled = v & 0x80;
-        break;
-        
-    /* Sawtooth */
-    case 0xB000:
-        mWave_VRC6Saw.nAccumRate = (v & 0x3F);
-        break;
-
-    case 0xB001:
-        mWave_VRC6Saw.nFreqTimer.B.l = v;
-        break;
-
-    case 0xB002:
-        mWave_VRC6Saw.nFreqTimer.B.h = v & 0x0F;
-        mWave_VRC6Saw.bChannelEnabled = v & 0x80;
-        break;
-    }
-}
-
-static void  WriteMemory_MMC5(uint16_t a,uint8_t v)
-{
-    if((a <= 0x5015) && !bPALMode)
-    {
-        /* no audio emulation */
-        return;
-    }
-
-    if(a == 0x5205)
-    {
-        nMultIn_Low = v;
-        goto multiply;
-    }
-    if(a == 0x5206)
-    {
-        nMultIn_High = v;
-multiply:
-        a = nMultIn_Low * nMultIn_High;
-        pExRAM[0x205] = a & 0xFF;
-        pExRAM[0x206] = a >> 8;
-        return;
-    }
-
-    if(a < 0x5C00) return;
-
-    pExRAM[a & 0x0FFF] = v;
-    if(a >= 0x5FF6)
-        WriteMemory_ExRAM(a,v);
-}
-
-static void  WriteMemory_N106(uint16_t a,uint8_t v)
-{
-    if(a < 0x4800)
-    {
-        WriteMemory_pAPU(a,v);
-        return;
-    }
-
-    if(a == 0xF800)
-    {
-        mWave_N106.nCurrentAddress = v & 0x7F;
-        mWave_N106.bAutoIncrement = (v & 0x80);
-        return;
-    }
-
-    if(a == 0x4800)
-    {
-        EmulateAPU(1);
-        mWave_N106.nRAM[mWave_N106.nCurrentAddress << 1] = v & 0x0F;
-        mWave_N106.nRAM[(mWave_N106.nCurrentAddress << 1) + 1] = v >> 4;
-        a = mWave_N106.nCurrentAddress;
-        if(mWave_N106.bAutoIncrement)
-            mWave_N106.nCurrentAddress =
-                (mWave_N106.nCurrentAddress + 1) & 0x7F;
-
-#define N106REGWRITE(ch,r0,r1,r2,r3,r4)                         \
-    case r0:    if(mWave_N106.nFreqReg[ch].B.l == v) break;     \
-                mWave_N106.nFreqReg[ch].B.l = v;                \
-                mWave_N106.nFreqTimer[ch] = -1;              \
-                break;                                          \
-    case r1:    if(mWave_N106.nFreqReg[ch].B.h == v) break;     \
-                mWave_N106.nFreqReg[ch].B.h = v;                \
-                mWave_N106.nFreqTimer[ch] = -1;              \
-                break;                                          \
-    case r2:    if(mWave_N106.nFreqReg[ch].B.w != (v & 3)){     \
-                    mWave_N106.nFreqReg[ch].B.w = v & 0x03;     \
-                    mWave_N106.nFreqTimer[ch] = -1;}         \
-                mWave_N106.nWaveSize[ch] = 0x20 - (v & 0x1C);   \
-                break;                                          \
-    case r3:    mWave_N106.nWavePosStart[ch] = v;               \
-                break;                                          \
-    case r4:    mWave_N106.nPreVolume[ch] = v & 0x0F;           \
-                if(!bN106PopReducer)                            \
-                    mWave_N106.nVolume[ch] = v & 0x0F
-
-        switch(a)
-        {
-            N106REGWRITE(0,0x40,0x42,0x44,0x46,0x47); break;
-            N106REGWRITE(1,0x48,0x4A,0x4C,0x4E,0x4F); break;
-            N106REGWRITE(2,0x50,0x52,0x54,0x56,0x57); break;
-            N106REGWRITE(3,0x58,0x5A,0x5C,0x5E,0x5F); break;
-            N106REGWRITE(4,0x60,0x62,0x64,0x66,0x67); break;
-            N106REGWRITE(5,0x68,0x6A,0x6C,0x6E,0x6F); break;
-            N106REGWRITE(6,0x70,0x72,0x74,0x76,0x77); break;
-            N106REGWRITE(7,0x78,0x7A,0x7C,0x7E,0x7F);
-                v = (v >> 4) & 7;
-                if(mWave_N106.nActiveChannels == v) break;
-                mWave_N106.nActiveChannels = v;
-                mWave_N106.nFreqTimer[0] = -1;
-                mWave_N106.nFreqTimer[1] = -1;
-                mWave_N106.nFreqTimer[2] = -1;
-                mWave_N106.nFreqTimer[3] = -1;
-                mWave_N106.nFreqTimer[4] = -1;
-                mWave_N106.nFreqTimer[5] = -1;
-                mWave_N106.nFreqTimer[6] = -1;
-                mWave_N106.nFreqTimer[7] = -1;
-                break;
-        }
-#undef N106REGWRITE
-    }
-}
-
-static void WriteMemory_FME07(uint16_t a,uint8_t v)
-{
-    if((a < 0xD000) && (nExternalSound & EXTSOUND_FDS))
-        WriteMemory_FDSRAM(a,v);
-
-    if(a == 0xC000)
-        nFME07_Address = v;
-    if(a == 0xE000)
-    {
-        switch(nFME07_Address)
-        {
-        case 0x00:  mWave_FME07[0].nFreqTimer.B.l = v;          break;
-        case 0x01:  mWave_FME07[0].nFreqTimer.B.h = v & 0x0F;   break;
-        case 0x02:  mWave_FME07[1].nFreqTimer.B.l = v;          break;
-        case 0x03:  mWave_FME07[1].nFreqTimer.B.h = v & 0x0F;   break;
-        case 0x04:  mWave_FME07[2].nFreqTimer.B.l = v;          break;
-        case 0x05:  mWave_FME07[2].nFreqTimer.B.h = v & 0x0F;   break;
-        case 0x07:
-            mWave_FME07[0].bChannelEnabled = !(v & 0x01);
-            mWave_FME07[1].bChannelEnabled = !(v & 0x02);
-            mWave_FME07[2].bChannelEnabled = !(v & 0x03);
-            break;
-        case 0x08:  mWave_FME07[0].nVolume = v & 0x0F; break;
-        case 0x09:  mWave_FME07[1].nVolume = v & 0x0F; break;
-        case 0x0A:  mWave_FME07[2].nVolume = v & 0x0F; break;
-        }
-    }
-}
-
-/*
- * Emulate APU
- */
-
-static int32_t fulltick;
-static void EmulateAPU(uint8_t bBurnCPUCycles)
-{
-    int32_t tick;
-    int64_t diff;
-    
-    int32_t tnd_out;
-    int square_out1;
-    int square_out2;
-    
-    ENTER_TIMER(apu);
-    
-    fulltick += (signed)(nCPUCycle - nAPUCycle);
-
-    int32_t burned;
-    int32_t mixL;
-
-    if(bFade && nSilentSampleMax && (nSilentSamples >= nSilentSampleMax))
-        fulltick = 0;
-
-    while(fulltick>0)
-    {
-        tick = (nTicksUntilNextSample+0xffff)>>16;
-
-        fulltick -= tick;
-
-        /*
-         * Sample Generation
-         */
-
-        ENTER_TIMER(squares);
-        /* Square generation */
-
-        mWave_Squares.nFreqCount[0] -= tick;
-        mWave_Squares.nFreqCount[1] -= tick;
-
-        if((mWave_Squares.nDutyCount[0] < mWave_Squares.nDutyCycle[0]) &&
-            mWave_Squares.nLengthCount[0] &&
-            !mWave_Squares.bSweepForceSilence[0])
-            square_out1 = mWave_Squares.nVolume[0];
-        else
-            square_out1 = 0;
-
-        if((mWave_Squares.nDutyCount[1] < mWave_Squares.nDutyCycle[1]) &&
-            mWave_Squares.nLengthCount[1] &&
-            !mWave_Squares.bSweepForceSilence[1])
-            square_out2 = mWave_Squares.nVolume[1];
-        else
-            square_out2 = 0;
-
-        mWave_Squares.nMixL = Squares_nOutputTable_L[square_out1][square_out2];
-
-        if(mWave_Squares.nFreqCount[0]<=0)
-        {
-            int cycles =
-                (-mWave_Squares.nFreqCount[0])/
-                (mWave_Squares.nFreqTimer[0].W + 1) + 1;
-            mWave_Squares.nFreqCount[0] =
-                (mWave_Squares.nFreqTimer[0].W + 1)-
-                (-mWave_Squares.nFreqCount[0])%
-                (mWave_Squares.nFreqTimer[0].W + 1);
-            mWave_Squares.nDutyCount[0] =
-                (mWave_Squares.nDutyCount[0]+cycles)%0x10;
-        }
-        if(mWave_Squares.nFreqCount[1]<=0)
-        {
-            int cycles =
-                (-mWave_Squares.nFreqCount[1])/
-                (mWave_Squares.nFreqTimer[1].W + 1) + 1;
-            mWave_Squares.nFreqCount[1] = 
-                (mWave_Squares.nFreqTimer[1].W + 1)-
-                (-mWave_Squares.nFreqCount[1])%
-                (mWave_Squares.nFreqTimer[1].W + 1);
-            mWave_Squares.nDutyCount[1] = (mWave_Squares.nDutyCount[1]+cycles)%
-                0x10;
-        }
-        /* end of Square generation */
-        EXIT_TIMER(squares);
-        ENTER_TIMER(tnd);
-        
-        ENTER_TIMER(tnd_enter);
-    
-        burned=0;
-    
-        /* TND generation */
-    
-        if(mWave_TND.nNoiseFreqTimer) mWave_TND.nNoiseFreqCount -= tick;
-            
-        if(mWave_TND.nTriFreqTimer.W > 8)
-            mWave_TND.nTriFreqCount -= tick;
-
-        tnd_out = mWave_TND.nTriOutput << 11;
-
-        if(mWave_TND.bNoiseRandomOut && mWave_TND.nNoiseLengthCount)
-            tnd_out |= mWave_TND.nNoiseVolume << 7;
-
-        tnd_out |= mWave_TND.nDMCOutput;
-
-        mWave_TND.nMixL = main_nOutputTable_L[tnd_out];
-
-        EXIT_TIMER(tnd_enter);
-    
-        ENTER_TIMER(tnd_tri);
-    
-        /* Tri */
-
-        if(mWave_TND.nTriFreqCount<=0)
-        {
-            if(mWave_TND.nTriLengthCount && mWave_TND.nTriLinearCount)
-            {
-                do mWave_TND.nTriStep++;
-                while ((mWave_TND.nTriFreqCount +=
-                    mWave_TND.nTriFreqTimer.W + 1) <= 0);
-                mWave_TND.nTriStep &= 0x1F;
-
-                if(mWave_TND.nTriStep & 0x10)
-                    mWave_TND.nTriOutput = mWave_TND.nTriStep ^ 0x1F;
-                else mWave_TND.nTriOutput = mWave_TND.nTriStep;
-            } else mWave_TND.nTriFreqCount=mWave_TND.nTriFreqTimer.W+1;
-        }
-
-        EXIT_TIMER(tnd_tri);
-    
-        ENTER_TIMER(tnd_noise);
-    
-        /* Noise */
-
-        if(mWave_TND.nNoiseFreqTimer &&
-           mWave_TND.nNoiseVolume && mWave_TND.nNoiseFreqCount<=0)
-        {
-            mWave_TND.nNoiseFreqCount = mWave_TND.nNoiseFreqTimer;
-            mWave_TND.nNoiseRandomShift <<= 1;
-            mWave_TND.bNoiseRandomOut = (((mWave_TND.nNoiseRandomShift <<
-                mWave_TND.bNoiseRandomMode) ^
-                mWave_TND.nNoiseRandomShift) & 0x8000 ) ? 1 : 0;
-            if(mWave_TND.bNoiseRandomOut)
-                mWave_TND.nNoiseRandomShift |= 0x01;
-        }
-    
-        EXIT_TIMER(tnd_noise);
-    
-        ENTER_TIMER(tnd_dmc);
-
-        /* DMC */
-        if(mWave_TND.bDMCActive)
-        {
-            mWave_TND.nDMCFreqCount -= tick;
-            while (mWave_TND.nDMCFreqCount <= 0) {
-                if (!mWave_TND.bDMCActive) {
-                    mWave_TND.nDMCFreqCount = mWave_TND.nDMCFreqTimer;
-                    break;
-                }
-
-                mWave_TND.nDMCFreqCount += mWave_TND.nDMCFreqTimer;
-
-                if(mWave_TND.bDMCSampleBufferEmpty &&
-                   mWave_TND.nDMCBytesRemaining)
-                {
-                    burned += 4;        /* 4 cycle burn! */
-                    mWave_TND.nDMCSampleBuffer =
-                        mWave_TND.pDMCDMAPtr[mWave_TND.nDMCDMABank]
-                                            [mWave_TND.nDMCDMAAddr];
-                    mWave_TND.nDMCDMAAddr++;
-                    if(mWave_TND.nDMCDMAAddr & 0x1000)
-                    {
-                        mWave_TND.nDMCDMAAddr &= 0x0FFF;
-                        mWave_TND.nDMCDMABank =
-                            (mWave_TND.nDMCDMABank + 1) & 0x07;
-                    }
-
-                    mWave_TND.bDMCSampleBufferEmpty = 0;
-                    mWave_TND.nDMCBytesRemaining--;
-                    if(!mWave_TND.nDMCBytesRemaining)
-                    {
-                        if(mWave_TND.bDMCLoop)
-                        {
-                            mWave_TND.nDMCDMABank = mWave_TND.nDMCDMABank_Load;
-                            mWave_TND.nDMCDMAAddr = mWave_TND.nDMCDMAAddr_Load;
-                            mWave_TND.nDMCBytesRemaining =mWave_TND.nDMCLength;
-                        }
-                        else if(mWave_TND.bDMCIRQEnabled)
-                            mWave_TND.bDMCIRQPending = 1;
-                    }
-                }
-
-                if(!mWave_TND.nDMCDeltaBit)
-                {
-                    mWave_TND.nDMCDeltaBit = 8;
-                    mWave_TND.bDMCDeltaSilent =mWave_TND.bDMCSampleBufferEmpty;
-                    mWave_TND.nDMCDelta = mWave_TND.nDMCSampleBuffer;
-                    mWave_TND.bDMCSampleBufferEmpty = 1;
-                }
-                
-                if(mWave_TND.nDMCDeltaBit) {
-                    mWave_TND.nDMCDeltaBit--;
-                    if(!mWave_TND.bDMCDeltaSilent)
-                    {
-                        if(mWave_TND.nDMCDelta & 0x01)
-                        {
-                            if(mWave_TND.nDMCOutput < 0x7E)
-                                mWave_TND.nDMCOutput += 2;
-                        }
-                        else if(mWave_TND.nDMCOutput > 1)
-                            mWave_TND.nDMCOutput -= 2;
-                    }
-                    mWave_TND.nDMCDelta >>= 1;
-                }
-
-                if(!mWave_TND.nDMCBytesRemaining &&
-                    mWave_TND.bDMCSampleBufferEmpty &&
-                    mWave_TND.bDMCDeltaSilent)
-                    mWave_TND.bDMCActive = mWave_TND.nDMCDeltaBit = 0;
-            }
-        }
-    
-        EXIT_TIMER(tnd_dmc);
-   
-        /* end of TND generation */
-        EXIT_TIMER(tnd);
-
-        if(nExternalSound && !bPALMode)
-        {
-            if(nExternalSound & EXTSOUND_VRC6)
-                Wave_VRC6_DoTicks(tick);
-            if(nExternalSound & EXTSOUND_N106)
-                Wave_N106_DoTicks(tick);
-            if(nExternalSound & EXTSOUND_FME07)
-            {
-                if (mWave_FME07[0].bChannelEnabled &&
-                    mWave_FME07[0].nFreqTimer.W) {
-                    mWave_FME07[0].nFreqCount -= tick;
-
-                    if(mWave_FME07[0].nDutyCount < 16)
-                    {
-                        mWave_FME07[0].nMixL =
-                            FME07_nOutputTable_L[mWave_FME07[0].nVolume];
-                    } else mWave_FME07[0].nMixL = 0;
-                    while(mWave_FME07[0].nFreqCount <= 0) {
-                        mWave_FME07[0].nFreqCount +=
-                            mWave_FME07[0].nFreqTimer.W;
-
-                        mWave_FME07[0].nDutyCount=
-                            (mWave_FME07[0].nDutyCount+1)&0x1f;
-                    }
-                }
-
-                if (mWave_FME07[1].bChannelEnabled &&
-                    mWave_FME07[1].nFreqTimer.W) {
-                    mWave_FME07[1].nFreqCount -= tick;
-
-                    if(mWave_FME07[1].nDutyCount < 16)
-                    {
-                        mWave_FME07[1].nMixL =
-                            FME07_nOutputTable_L[mWave_FME07[1].nVolume];
-                    } else mWave_FME07[1].nMixL = 0;
-                    while(mWave_FME07[1].nFreqCount <= 0) {
-                        mWave_FME07[1].nFreqCount +=
-                            mWave_FME07[1].nFreqTimer.W;
-
-                        mWave_FME07[1].nDutyCount=
-                            (mWave_FME07[1].nDutyCount+1)&0x1f;
-                    }
-                }
-
-                if (mWave_FME07[2].bChannelEnabled &&
-                    mWave_FME07[2].nFreqTimer.W) {
-                    mWave_FME07[2].nFreqCount -= tick;
-
-                    if(mWave_FME07[2].nDutyCount < 16)
-                    {
-                        mWave_FME07[2].nMixL =
-                            FME07_nOutputTable_L[mWave_FME07[2].nVolume];
-                    } else mWave_FME07[2].nMixL = 0;
-                    while(mWave_FME07[2].nFreqCount <= 0) {
-                        mWave_FME07[2].nFreqCount +=
-                            mWave_FME07[2].nFreqTimer.W;
-
-                        mWave_FME07[2].nDutyCount=
-                            (mWave_FME07[2].nDutyCount+1)&0x1f;
-                    }
-                }
-
-            } /* end FME07 */
-            ENTER_TIMER(fds);
-            if(nExternalSound & EXTSOUND_FDS) {
-
-                /*  Volume Envelope Unit    */
-                if(mWave_FDS.bVolEnv_On)
-                {
-                    mWave_FDS.nVolEnv_Count -= tick;
-                    while(mWave_FDS.nVolEnv_Count <= 0)
-                    {
-                        mWave_FDS.nVolEnv_Count += mWave_FDS.nVolEnv_Timer;
-                        if(mWave_FDS.nVolEnv_Mode) {
-                            if(mWave_FDS.nVolEnv_Gain < 0x20)
-                                mWave_FDS.nVolEnv_Gain++;
-                            }
-                        else {
-                            if(mWave_FDS.nVolEnv_Gain)
-                                mWave_FDS.nVolEnv_Gain--;
-                        }
-                    }
-                }
-    
-                /*  Sweep Envelope Unit */
-                if(mWave_FDS.bSweepEnv_On)
-                {
-                    mWave_FDS.nSweep_Count -= tick;
-                    while(mWave_FDS.nSweep_Count <= 0)
-                    {
-                        mWave_FDS.nSweep_Count += mWave_FDS.nSweep_Timer;
-                        if(mWave_FDS.nSweep_Mode)    {
-                            if(mWave_FDS.nSweep_Gain < 0x20)
-                                mWave_FDS.nSweep_Gain++;
-                        } else {
-                            if(mWave_FDS.nSweep_Gain) mWave_FDS.nSweep_Gain--;
-                        }
-                    }
-                }
-            
-                /*  Effector / LFO      */
-                int32_t     subfreq = 0;
-                if(mWave_FDS.bLFO_On)
-                {
-                    mWave_FDS.nLFO_Count -= tick<<14;
-                    while(mWave_FDS.nLFO_Count <= 0)
-                    {
-                        mWave_FDS.nLFO_Count += mWave_FDS.nLFO_Timer;
-                        if(mWave_FDS.nLFO_Table[mWave_FDS.nLFO_Addr] == 4)
-                            mWave_FDS.nSweepBias = 0;
-                        else 
-                            mWave_FDS.nSweepBias +=
-                                ModulationTable[ 
-                                    mWave_FDS.nLFO_Table[mWave_FDS.nLFO_Addr]
-                                ];
-                        mWave_FDS.nLFO_Addr = (mWave_FDS.nLFO_Addr + 1) & 0x3F;
-                    }
-            
-                    while(mWave_FDS.nSweepBias >  63)
-                        mWave_FDS.nSweepBias -= 128;
-                    while(mWave_FDS.nSweepBias < -64)
-                        mWave_FDS.nSweepBias += 128;
-            
-                    register int32_t temp =
-                        mWave_FDS.nSweepBias * mWave_FDS.nSweep_Gain;
-                    if(temp & 0x0F)
-                    {
-                        temp /= 16;
-                        if(mWave_FDS.nSweepBias < 0) temp--;
-                        else                temp += 2;
-                    }
-                    else
-                        temp /= 16;
-            
-                    if(temp > 193)  temp -= 258;
-                    if(temp < -64)  temp += 256;
-            
-                    subfreq = mWave_FDS.nFreq.W * temp / 64;
-                }
-            
-                /*  Main Unit       */
-                if(mWave_FDS.bMain_On)
-                {
-                    mWave_FDS.nMixL =
-                        FDS_nOutputTable_L[mWave_FDS.nMainVolume]
-                                          [mWave_FDS.nVolume]
-                                 [mWave_FDS.nWaveTable[mWave_FDS.nMainAddr] ];
-            
-                    if((subfreq + mWave_FDS.nFreq.W) > 0)
-                    {
-                        int32_t freq = (0x10000<<14) / (subfreq + mWave_FDS.nFreq.W);
-            
-                        mWave_FDS.nFreqCount -= tick<<14;
-                        while(mWave_FDS.nFreqCount <= 0)
-                        {
-                            mWave_FDS.nFreqCount += freq;
-            
-                            mWave_FDS.nMainAddr =
-                                (mWave_FDS.nMainAddr + 1) & 0x3F;
-                            mWave_FDS.nPopOutput =
-                                mWave_FDS.nWaveTable[mWave_FDS.nMainAddr];
-                            if(!mWave_FDS.nMainAddr)
-                            {
-                                if(mWave_FDS.nVolEnv_Gain < 0x20)
-                                    mWave_FDS.nVolume = mWave_FDS.nVolEnv_Gain;
-                                else mWave_FDS.nVolume = 0x20;
-                            }
-                        }
-                    }
-                    else
-                        mWave_FDS.nFreqCount = mWave_FDS.nLFO_Count;
-                }
-                else if(mWave_FDS.bPopReducer && mWave_FDS.nPopOutput)
-                {
-                    mWave_FDS.nMixL = FDS_nOutputTable_L[mWave_FDS.nMainVolume]
-                                                        [mWave_FDS.nVolume]
-                                                        [mWave_FDS.nPopOutput];
-            
-                    mWave_FDS.nPopCount -= tick;
-                    while(mWave_FDS.nPopCount <= 0)
-                    {
-                        mWave_FDS.nPopCount += 500;
-                        mWave_FDS.nPopOutput--;
-                        if(!mWave_FDS.nPopOutput)
-                            mWave_FDS.nMainAddr = 0;
-                    }
-                } /* end FDS */
-            }
-            EXIT_TIMER(fds);
-        } /* end while fulltick */
-
-        if(bBurnCPUCycles)
-        {
-            nCPUCycle += burned;
-            fulltick += burned;
-        }
-        
-        /* Frame Sequencer */
-
-        ENTER_TIMER(frame);
-        nTicksUntilNextFrame -= tick<<16;
-        while(nTicksUntilNextFrame <= 0)
-        {
-            nTicksUntilNextFrame +=
-                (bPALMode ? PAL_FRAME_COUNTER_FREQ : NTSC_FRAME_COUNTER_FREQ) *
-                0x10000;
-            nFrameCounter++;
-            if(nFrameCounter > nFrameCounterMax)
-                nFrameCounter = 0;
-
-            if(nFrameCounterMax == 4)
-            {
-                if(nFrameCounter < 4)
-                {
-                    CLOCK_MAJOR();
-                    if(!(nFrameCounter & 1))
-                        CLOCK_MINOR();
-                }
-            }
-            else
-            {
-                CLOCK_MAJOR();
-                if(nFrameCounter & 1)
-                    CLOCK_MINOR();
-
-                if((nFrameCounter == 3) && bFrameIRQEnabled)
-                    bFrameIRQPending = 1;
-            }
-        }
-        EXIT_TIMER(frame);
-
-        ENTER_TIMER(mix);
-        nTicksUntilNextSample -= tick<<16;
-        if(nTicksUntilNextSample <= 0)
-        {
-            nTicksUntilNextSample += nTicksPerSample;
-            
-            mixL = mWave_Squares.nMixL;
-            mixL += mWave_TND.nMixL;
-
-            if(nExternalSound && !bPALMode)
-            {
-                if(nExternalSound & EXTSOUND_VRC6)
-                {
-                    mixL += (mWave_VRC6Pulse[0].nMixL);
-                    mixL += (mWave_VRC6Pulse[1].nMixL);
-                    mixL += (mWave_VRC6Saw.nMixL);
-                }
-                if(nExternalSound & EXTSOUND_N106) {
-                    mixL += (mWave_N106.nMixL[0]);
-                    mixL += (mWave_N106.nMixL[1]);
-                    mixL += (mWave_N106.nMixL[2]);
-                    mixL += (mWave_N106.nMixL[3]);
-                    mixL += (mWave_N106.nMixL[4]);
-                    mixL += (mWave_N106.nMixL[5]);
-                    mixL += (mWave_N106.nMixL[6]);
-                    mixL += (mWave_N106.nMixL[7]);
-                }
-                if(nExternalSound & EXTSOUND_FME07)
-                {
-                    mixL += (mWave_FME07[0].nMixL);
-                    mixL += (mWave_FME07[1].nMixL);
-                    mixL += (mWave_FME07[2].nMixL);
-                }
-                if(nExternalSound & EXTSOUND_FDS)
-                    mixL += mWave_FDS.nMixL;
-            }
-
-            /*  Filter  */
-            diff = ((int64_t)mixL << 25) - nFilterAccL;
-            nFilterAccL += (diff * nHighPass) >> 16;
-            mixL = (int32_t)(diff >> 23);
-            /*  End Filter  */
-                
-            if(bFade && (fFadeVolume < 1))
-                mixL = (int32_t)(mixL * fFadeVolume);
-
-            if(mixL < -32768)   mixL = -32768;
-            if(mixL >  32767)   mixL =  32767;
-
-            *((uint16_t*)pOutput) = (uint16_t)mixL;
-            pOutput += 2;
-        }
-        
-    }
-    EXIT_TIMER(mix);
-
-    nAPUCycle = nCPUCycle;
-    
-    EXIT_TIMER(apu);
-}
-
-
-/*
- *  Initialize
- *
- *      Initializes Memory
- */
-
-static int NSFCore_Initialize()
-{
-    int32_t i;
-    /* clear globals */
-    /* why, yes, this was easier when they were in a struct */
-
-    /*
-     *  Memory
-     */
-
-    ZEROMEMORY(pRAM,0x800);
-    ZEROMEMORY(pSRAM,0x2000);
-    ZEROMEMORY(pExRAM,0x1000);
-    pROM_Full=0;
-
-    ZEROMEMORY(pROM,10);
-    pStack=0;
-
-    nROMSize=0;
-    nROMBankCount=0;
-    nROMMaxSize=0;
-
-    /*
-     *  Memory Proc Pointers
-     */
-
-    ZEROMEMORY(ReadMemory,sizeof(ReadProc)*0x10);
-    ZEROMEMORY(WriteMemory,sizeof(WriteProc)*0x10);
-    
-    /*
-     *  6502 Registers / Mode
-     */
-
-    regA=0;
-    regX=0;
-    regY=0;
-    regP=0;
-    regSP=0;
-    regPC=0;
-
-    bPALMode=0;
-    bCPUJammed=0;
-
-    nMultIn_Low=0;
-    nMultIn_High=0;
-
-    /*
-     *  NSF Preparation Information
-     */
-
-    ZEROMEMORY(nBankswitchInitValues,10);
-    nPlayAddress=0;
-    nInitAddress=0;
-
-    nExternalSound=0;
-    nCurTrack=0;
-
-    fNSFPlaybackSpeed=0;
-
-    /*
-     * pAPU
-     */
-
-    nFrameCounter=0;
-    nFrameCounterMax=0;
-    bFrameIRQEnabled=0;
-    bFrameIRQPending=0;
-
-    /*
-     *  Timing and Counters
-     */
-    nTicksUntilNextFrame=0;
-
-    nTicksPerPlay=0;
-    nTicksUntilNextPlay=0;
-
-    nTicksPerSample=0;
-    nTicksUntilNextSample=0;
-
-    nCPUCycle=0;
-    nAPUCycle=0;
-    nTotalPlays=0;
-
-    /*
-     * Silence Tracker
-     */
-    nSilentSamples=0;
-    nSilentSampleMax=0;
-    nSilenceTrackMS=0;
-    bNoSilenceIfTime=0;
-    bTimeNotDefault=0;
-
-    /*
-     * Volume/fading/filter tracking
-     */
-
-    nStartFade=0;
-    nEndFade=0;
-    bFade=0;
-    fFadeVolume=0;
-    fFadeChange=0;
-
-    pOutput=0;
-
-    nDMCPop_Prev=0;
-    bDMCPop_Skip=0;
-    bDMCPop_SamePlay=0;
-
-    /*
-     * Sound Filter
-     */
-
-    nFilterAccL=0;
-    nHighPass=0;
-
-    nHighPassBase=0;
-
-    bHighPassEnabled=0;
-
-    /* channels */
-    
-    ZEROMEMORY(&mWave_Squares,sizeof(struct Wave_Squares));
-    ZEROMEMORY(&mWave_TND,sizeof(struct Wave_TND));
-    ZEROMEMORY(mWave_VRC6Pulse,sizeof(struct VRC6PulseWave)*2);
-    ZEROMEMORY(&mWave_VRC6Saw,sizeof(struct VRC6SawWave));
-    ZEROMEMORY(&mWave_N106,sizeof(struct N106Wave));
-    ZEROMEMORY(mWave_FME07,sizeof(struct FME07Wave)*3);
-    ZEROMEMORY(&mWave_FDS,sizeof(struct FDSWave));
-    
-    /* end clear globals */
-
-    // Default filter bases
-    nHighPassBase = 150;
-
-    bHighPassEnabled = 1;
-
-    mWave_TND.nNoiseRandomShift =   1;
-    for(i = 0; i < 8; i++)
-        mWave_TND.pDMCDMAPtr[i] = pROM[i + 2];
-
-
-    SetPlaybackOptions(nSampleRate);
-
-    for(i = 0; i < 8; i++)
-        mWave_N106.nFrequencyLookupTable[i] =
-            ((((i + 1) * 45 * 0x40000) / (float)NES_FREQUENCY) *
-            (float)NTSC_FREQUENCY) * 256.0;
-
-    ZEROMEMORY(pRAM,0x800);
-    ZEROMEMORY(pSRAM,0x2000);
-    ZEROMEMORY(pExRAM,0x1000);
-    pStack = pRAM + 0x100;
-    return 1;
-}
-
-/*
- *  LoadNSF
- */
-
-static int LoadNSF(int32_t datasize)
-{
-    if(!pDataBuffer)                return 0;
-
-    int32_t i;
-
-    nExternalSound = nChipExtensions;
-    if(nIsPal & 2)
-        bPALMode = bPALPreference;
-    else
-        bPALMode = nIsPal & 1;
-
-    SetPlaybackOptions(nSampleRate);
-    
-    int32_t neededsize = datasize + (nfileLoadAddress & 0x0FFF);
-    if(neededsize & 0x0FFF)     neededsize += 0x1000 - (neededsize & 0x0FFF);
-    if(neededsize < 0x1000)     neededsize = 0x1000;
-
-    uint8_t specialload = 0;
-    
-    for(i = 0; (i < 8) && (!nBankswitch[i]); i++);
-    if(i < 8)       /* uses bankswitching */
-    {
-        memcpy(&nBankswitchInitValues[2],nBankswitch,8);
-        nBankswitchInitValues[0] = nBankswitch[6];
-        nBankswitchInitValues[1] = nBankswitch[7];
-        if(nExternalSound & EXTSOUND_FDS)
-        {
-            if(!(nBankswitchInitValues[0] || nBankswitchInitValues[1]))
-            {
-                /*
-                 * FDS sound with '00' specified for both $6000 and $7000 banks.
-                 * point this to an area of fresh RAM (sort of hackish solution
-                 * for those FDS tunes that don't quite follow the nsf specs.
-                 */
-                nBankswitchInitValues[0] = (uint8_t)(neededsize >> 12);
-                nBankswitchInitValues[1] = (uint8_t)(neededsize >> 12) + 1;
-                neededsize += 0x2000;
-            }
-        }
-    }
-    else            /* doesn't use bankswitching */
-    {
-        if(nExternalSound & EXTSOUND_FDS)
-        {
-            /* bad load address */
-            if(nfileLoadAddress < 0x6000)       return 0;
-
-            if(neededsize < 0xA000)
-                neededsize = 0xA000;
-            specialload = 1;
-            for(i = 0; i < 10; i++)
-                nBankswitchInitValues[i] = (uint8_t)i;
-        }
-        else
-        {
-            /* bad load address */
-            if(nfileLoadAddress < 0x8000)       return 0;
-
-            int32_t j = (nfileLoadAddress >> 12) - 6;
-            for(i = 0; i < j; i++)
-                nBankswitchInitValues[i] = 0;
-            for(j = 0; i < 10; i++, j++)
-                nBankswitchInitValues[i] = (uint8_t)j;
-        }
-    }
-
-    nROMSize = neededsize;
-    nROMBankCount = neededsize >> 12;
-
-    if(specialload)
-        pROM_Full = pDataBuffer-(nfileLoadAddress-0x6000);
-    else
-        pROM_Full = pDataBuffer-(nfileLoadAddress&0x0FFF);
-
-    ZEROMEMORY(pRAM,0x0800);
-    ZEROMEMORY(pExRAM,0x1000);
-    ZEROMEMORY(pSRAM,0x2000);
-
-    nExternalSound = nChipExtensions;
-    fNSFPlaybackSpeed = (bPALMode ? PAL_NMIRATE : NTSC_NMIRATE);
-    
-    SetPlaybackSpeed(0);
-
-    nPlayAddress = nfilePlayAddress;
-    nInitAddress = nfileInitAddress;
-
-    pExRAM[0x00] = 0x20;                        /* JSR */
-    pExRAM[0x01] = nInitAddress&0xff;           /* Init Address */
-    pExRAM[0x02] = (nInitAddress>>8)&0xff;
-    pExRAM[0x03] = 0xF2;                        /* JAM */
-    pExRAM[0x04] = 0x20;                        /* JSR */
-    pExRAM[0x05] = nPlayAddress&0xff;           /* Play Address */
-    pExRAM[0x06] = (nPlayAddress>>8)&0xff;
-    pExRAM[0x07] = 0x4C;                        /* JMP */
-    pExRAM[0x08] = 0x03;/* $5003  (JAM right before the JSR to play address) */
-    pExRAM[0x09] = 0x50;
-
-    regA = regX = regY = 0;
-    regP = 0x04;            /* I_FLAG */
-    regSP = 0xFF;
-
-    nFilterAccL = 0;
-
-    /*  Reset Read/Write Procs          */
-    
-    ReadMemory[0] = ReadMemory[1] = ReadMemory_RAM;
-    ReadMemory[2] = ReadMemory[3] = ReadMemory_Default;
-    ReadMemory[4] =                 ReadMemory_pAPU;
-    ReadMemory[5] =                 ReadMemory_ExRAM;
-    ReadMemory[6] = ReadMemory[7] = ReadMemory_SRAM;
-
-    WriteMemory[0] = WriteMemory[1] =   WriteMemory_RAM;
-    WriteMemory[2] = WriteMemory[3] =   WriteMemory_Default;
-    WriteMemory[4] =                    WriteMemory_pAPU;
-    WriteMemory[5] =                    WriteMemory_ExRAM;
-    WriteMemory[6] = WriteMemory[7] =   WriteMemory_SRAM;
-
-    for(i = 8; i < 16; i++)
-    {
-        ReadMemory[i] = ReadMemory_ROM;
-        WriteMemory[i] = WriteMemory_Default;
-    }
-
-    if(nExternalSound & EXTSOUND_FDS)
-    {
-        WriteMemory[0x06] = WriteMemory_FDSRAM;
-        WriteMemory[0x07] = WriteMemory_FDSRAM;
-        WriteMemory[0x08] = WriteMemory_FDSRAM;
-        WriteMemory[0x09] = WriteMemory_FDSRAM;
-        WriteMemory[0x0A] = WriteMemory_FDSRAM;
-        WriteMemory[0x0B] = WriteMemory_FDSRAM;
-        WriteMemory[0x0C] = WriteMemory_FDSRAM;
-        WriteMemory[0x0D] = WriteMemory_FDSRAM;
-        ReadMemory[0x06] = ReadMemory_ROM;
-        ReadMemory[0x07] = ReadMemory_ROM;
-    }
-
-    if(!bPALMode)   /* no expansion sound available on a PAL system */
-    {
-        if(nExternalSound & EXTSOUND_VRC6)
-        {
-            /* if both VRC6+VRC7... it MUST go to WriteMemory_VRC6
-             * or register writes will be lost (WriteMemory_VRC6 calls
-             * WriteMemory_VRC7 if needed) */
-            WriteMemory[0x09] = WriteMemory_VRC6;   
-            WriteMemory[0x0A] = WriteMemory_VRC6;   
-            WriteMemory[0x0B] = WriteMemory_VRC6;   
-        }
-        if(nExternalSound & EXTSOUND_N106)
-        {
-            WriteMemory[0x04] = WriteMemory_N106;
-            ReadMemory[0x04] = ReadMemory_N106;
-            WriteMemory[0x0F] = WriteMemory_N106;
-        }
-        if(nExternalSound & EXTSOUND_FME07)
-        {
-            WriteMemory[0x0C] = WriteMemory_FME07;
-            WriteMemory[0x0E] = WriteMemory_FME07;
-        }
-    }
-    
-    /* MMC5 still has a multiplication reg that needs to be available on
-       PAL tunes */
-    if(nExternalSound & EXTSOUND_MMC5)
-        WriteMemory[0x05] = WriteMemory_MMC5;
-
-    return 1;
-}
-
-/*
- *  SetTrack
- */
-
-static void SetTrack(uint8_t track)
-{
-    int32_t i;
-    
-    nCurTrack = track;
-
-    regPC = 0x5000;
-    regA = track;
-    regX = bPALMode;
-    regY = bCleanAXY ? 0 : 0xCD;
-    regSP = 0xFF;
-    if(bCleanAXY)
-        regP = 0x04;
-    bCPUJammed = 0;
-
-    nCPUCycle = nAPUCycle = 0;
-    nDMCPop_Prev = 0;
-    bDMCPop_Skip = 0;
-
-    for(i = 0x4000; i < 0x400F; i++)
-        WriteMemory_pAPU(i,0);
-    WriteMemory_pAPU(0x4010,0);
-    WriteMemory_pAPU(0x4012,0);
-    WriteMemory_pAPU(0x4013,0);
-    WriteMemory_pAPU(0x4014,0);
-    WriteMemory_pAPU(0x4015,0);
-    WriteMemory_pAPU(0x4015,0x0F);
-    WriteMemory_pAPU(0x4017,0);
-
-    for(i = 0; i < 10; i++)
-        WriteMemory_ExRAM(0x5FF6 + i,nBankswitchInitValues[i]);
-
-    ZEROMEMORY(pRAM,0x0800);
-    ZEROMEMORY(pSRAM,0x2000);
-    ZEROMEMORY(&pExRAM[0x10],0x0FF0);
-    bFade = 0;
-
-
-    nTicksUntilNextSample = nTicksPerSample;
-    nTicksUntilNextFrame =
-        (bPALMode ? PAL_FRAME_COUNTER_FREQ : NTSC_FRAME_COUNTER_FREQ)*0x10000;
-    nTicksUntilNextPlay = nTicksPerPlay;
-    nTotalPlays = 0;
-    
-    /*  Clear mixing vals   */
-    mWave_Squares.nMixL = 0;
-    mWave_TND.nMixL = 0;
-    mWave_VRC6Pulse[0].nMixL = 0;
-    mWave_VRC6Pulse[1].nMixL = 0;
-    mWave_VRC6Saw.nMixL = 0;
-
-    /*  Reset Tri/Noise/DMC */
-    mWave_TND.nTriStep = mWave_TND.nTriOutput = 0;
-    mWave_TND.nDMCOutput = 0;
-    mWave_TND.bNoiseRandomOut = 0;
-    mWave_Squares.nDutyCount[0] = mWave_Squares.nDutyCount[1] = 0;
-    mWave_TND.bDMCActive = 0;
-    mWave_TND.nDMCBytesRemaining = 0;
-    mWave_TND.bDMCSampleBufferEmpty = 1;
-    mWave_TND.bDMCDeltaSilent = 1;
-
-    /*  Reset VRC6  */
-    mWave_VRC6Pulse[0].nVolume = 0;
-    mWave_VRC6Pulse[1].nVolume = 0;
-    mWave_VRC6Saw.nAccumRate = 0;
-
-    /*  Reset N106  */
-    ZEROMEMORY(mWave_N106.nRAM,0x100);
-    ZEROMEMORY(mWave_N106.nVolume,8);
-    ZEROMEMORY(mWave_N106.nOutput,8);
-    ZEROMEMORY(mWave_N106.nMixL,32);
-
-    /*  Reset FME-07    */
-    mWave_FME07[0].nVolume = 0;
-    mWave_FME07[1].nVolume = 0;
-    mWave_FME07[2].nVolume = 0;
-
-    /*  Clear FDS crap      */
-
-    mWave_FDS.bEnvelopeEnable = 0;
-    mWave_FDS.nEnvelopeSpeed = 0xFF;
-    mWave_FDS.nVolEnv_Mode = 2;
-    mWave_FDS.nVolEnv_Decay = 0;
-    mWave_FDS.nVolEnv_Gain = 0;
-    mWave_FDS.nVolume = 0;
-    mWave_FDS.bVolEnv_On = 0;
-    mWave_FDS.nSweep_Mode = 2;
-    mWave_FDS.nSweep_Decay = 0;
-    mWave_FDS.nSweep_Gain = 0;
-    mWave_FDS.bSweepEnv_On = 0;
-    mWave_FDS.nSweepBias = 0;
-    mWave_FDS.bLFO_Enabled = 0;
-    mWave_FDS.nLFO_Freq.W = 0;
-/*    mWave_FDS.fLFO_Timer = 0;
-    mWave_FDS.fLFO_Count = 0;*/
-    mWave_FDS.nLFO_Timer = 0;
-    mWave_FDS.nLFO_Count = 0;
-    mWave_FDS.nLFO_Addr = 0;
-    mWave_FDS.bLFO_On = 0;
-    mWave_FDS.nMainVolume = 0;
-    mWave_FDS.bEnabled = 0;
-    mWave_FDS.nFreq.W = 0;
-/*    mWave_FDS.fFreqCount = 0;*/
-    mWave_FDS.nFreqCount = 0;
-    mWave_FDS.nMainAddr = 0;
-    mWave_FDS.bWaveWrite = 0;
-    mWave_FDS.bMain_On = 0;
-    mWave_FDS.nMixL = 0;
-    ZEROMEMORY(mWave_FDS.nWaveTable,0x40);
-    ZEROMEMORY(mWave_FDS.nLFO_Table,0x40);
-
-    mWave_FDS.nSweep_Count = mWave_FDS.nSweep_Timer =
-        ((mWave_FDS.nSweep_Decay + 1) * mWave_FDS.nEnvelopeSpeed * 8);
-    mWave_FDS.nVolEnv_Count = mWave_FDS.nVolEnv_Timer =
-        ((mWave_FDS.nVolEnv_Decay + 1) * mWave_FDS.nEnvelopeSpeed * 8);
-
-    nSilentSamples = 0;
-
-    nFilterAccL = 0;
-
-    nSilentSamples = 0;
-
-    fulltick=0;
-}
-
-/*
- *  SetPlaybackOptions
- */
-
-static int SetPlaybackOptions(int32_t samplerate)
-{
-    if(samplerate < 2000)                   return 0;
-    if(samplerate > 96000)                  return 0;
-
-    nTicksPerSample =
-        (bPALMode ? PAL_FREQUENCY : NTSC_FREQUENCY) / samplerate * 0x10000;
-    nTicksUntilNextSample = nTicksPerSample;
-
-    RecalcFilter();
-    RecalcSilenceTracker();
-
-    return 1;
-}
-
-/*
- *  SetPlaybackSpeed
- */
-
-static void SetPlaybackSpeed(float playspersec)
-{
-    if(playspersec < 1)
-    {
-        playspersec = fNSFPlaybackSpeed;
-    }
-
-    nTicksPerPlay = nTicksUntilNextPlay =
-        (bPALMode ? PAL_FREQUENCY : NTSC_FREQUENCY) / playspersec * 0x10000;
-}
-
-/*
-*   GetPlaybackSpeed
-*/
-
-static float GetPlaybackSpeed()
-{
-    if(nTicksPerPlay <= 0)  return 0;
-    return ((bPALMode ? PAL_FREQUENCY : NTSC_FREQUENCY) / (nTicksPerPlay>>16));
-}
-
-/*
- *  RecalcFilter
- */
-
-static void RecalcFilter()
-{
-    if(!nSampleRate) return;
-
-    nHighPass = ((int64_t)nHighPassBase << 16) / nSampleRate;
-
-    if(nHighPass > (1<<16)) nHighPass = 1<<16;
-}
-
-/*
- *  RecalcSilenceTracker
- */
-
-static void RecalcSilenceTracker()
-{
-    if(nSilenceTrackMS <= 0 || !nSampleRate ||
-       (bNoSilenceIfTime && bTimeNotDefault))
-    {
-        nSilentSampleMax = 0;
-        return;
-    }
-
-    nSilentSampleMax = nSilenceTrackMS * nSampleRate / 500;
-    nSilentSampleMax /= 2;
-}
-
-static void RebuildOutputTables(void) {
-    int32_t i,j;
-    float l[3];
-    int32_t temp;
-    float ftemp;
-    
-    /* tnd */
-    for(i = 0; i < 3; i++)
-    {
-        l[i] = 255;
-    }
-
-    for(i = 0; i < 0x8000; i++)
-    {
-        ftemp = (l[0] * (i >> 11)) / 2097885;
-        ftemp += (l[1] * ((i >> 7) & 0x0F)) / 3121455;
-        ftemp += (l[2] * (i & 0x7F)) / 5772690;
-
-        if(!ftemp)
-            main_nOutputTable_L[i] = 0;
-        else
-            main_nOutputTable_L[i] =
-                (int16_t)(2396850 / ((1.0f / ftemp) + 100));
-    }
-    
-    /* squares */
-    for(i = 0; i < 2; i++)
-    {
-        l[i] = 255;
-    }
-
-    for(j = 0; j < 0x10; j++)
-    {
-        for(i = 0; i < 0x10; i++)
-        {
-            temp = (int32_t)(l[0] * j);
-            temp += (int32_t)(l[1] * i);
-
-            if(!temp)
-                Squares_nOutputTable_L[j][i] = 0;
-            else
-                Squares_nOutputTable_L[j][i] = 1438200 / ((2072640 / temp) + 100);
-        }
-    }
-
-    /* VRC6 Pulse 1,2 */
-    for(i = 0; i < 0x10; i++)
-    {
-        VRC6Pulse_nOutputTable_L[i] =
-            1875 * i / 0x0F;
-    }
-    /* VRC6 Saw */
-    for(i = 0; i < 0x20; i++)
-    {
-        VRC6Saw_nOutputTable_L[i] = 3750 * i / 0x1F;
-    }
-
-    /* N106 channels */
-    /* this amplitude is just a guess */
-
-    for(i = 0; i < 0x10; i++)
-    {
-        for(j = 0; j < 0x10; j++)
-        {
-            N106_nOutputTable_L[i][j] = (3000 * i * j) / 0xE1;
-        }
-    }
-    
-    /* FME-07 Square A,B,C */
-    FME07_nOutputTable_L[15] = 3000;
-    FME07_nOutputTable_L[0] = 0;
-    for(i = 14; i > 0; i--)
-    {
-        FME07_nOutputTable_L[i] = FME07_nOutputTable_L[i + 1] * 80 / 100;
-    }
-
-    /*
-     *  FDS
-     */
-    /*  this base volume (4000) is just a guess to what sounds right.
-     *  Given the number of steps available in an FDS wave... it seems like
-     *  it should be much much more... but then it's TOO loud.
-     */
-    for(i = 0; i < 0x21; i++)
-    {
-        for(j = 0; j < 0x40; j++)
-        {
-            FDS_nOutputTable_L[0][i][j] =
-                (4000 * i * j * 30) / (0x21 * 0x40 * 30);
-            FDS_nOutputTable_L[1][i][j] =
-                (4000 * i * j * 20) / (0x21 * 0x40 * 30);
-            FDS_nOutputTable_L[2][i][j] =
-                (4000 * i * j * 15) / (0x21 * 0x40 * 30);
-            FDS_nOutputTable_L[3][i][j] =
-                (4000 * i * j * 12) / (0x21 * 0x40 * 30);
-        }
-    }
-}
-
-/* rockbox: not used */
-#if 0
-/*
- *  GetPlayCalls
- */
-
-float GetPlayCalls()
-{
-    if(!nTicksPerPlay)  return 0;
-
-    return ((float)nTotalPlays) +
-        (1.0f - (nTicksUntilNextPlay*1.0f / nTicksPerPlay));
-}
-
-/*
- *  GetWrittenTime
- */
-uint32_t GetWrittenTime(float basedplayspersec /* = 0 */)
-{
-    if(basedplayspersec <= 0)
-        basedplayspersec = GetPlaybackSpeed();
-
-    if(basedplayspersec <= 0)
-        return 0;
-
-    return (uint32_t)((GetPlayCalls() * 1000) / basedplayspersec);
-}
-
-/*
- *  StopFade
- */
-void StopFade()
-{
-    bFade = 0;
-    fFadeVolume = 1;
-}
-#endif
-
-/*
- *  SongCompleted
- */
-
-static uint8_t SongCompleted()
-{
-    if(!bFade)                      return 0;
-    if(nTotalPlays >= nEndFade)     return 1;
-    if(nSilentSampleMax)            return (nSilentSamples >= nSilentSampleMax);
-
-    return 0;
-}
-
-/*
- *  SetFade
- */
-
-static void SetFade(int32_t fadestart,int32_t fadestop,
-             uint8_t bNotDefault) /* play routine calls */
-{
-    if(fadestart < 0)   fadestart = 0;
-    if(fadestop < fadestart) fadestop = fadestart;
-
-    nStartFade = (uint32_t)fadestart;
-    nEndFade = (uint32_t)fadestop;
-    bFade = 1;
-    bTimeNotDefault = bNotDefault;
-
-    RecalcSilenceTracker();
-    RecalculateFade();
-}
-
-/*
- *  SetFadeTime
- */
-
-static void SetFadeTime(uint32_t fadestart,uint32_t fadestop,float basedplays,
-                 uint8_t bNotDefault) /* time in MS */
-{
-    if(basedplays <= 0)
-        basedplays = GetPlaybackSpeed();
-    if(basedplays <= 0)
-        return;
-
-    SetFade((int32_t)(fadestart * basedplays / 1000),
-           (int32_t)(fadestop * basedplays / 1000),bNotDefault);
-}
-
-/*
- *  RecalculateFade
- */
-
-static void RecalculateFade()
-{
-    if(!bFade)  return;
-
-    /* make it hit silence a little before the song ends...
-       otherwise we're not really fading OUT, we're just fading umm...
-       quieter =P */
-    int32_t temp = (int32_t)(GetPlaybackSpeed() / 4);
-
-    if(nEndFade <= nStartFade)
-    {
-        nEndFade = nStartFade;
-        fFadeChange = 1.0f;
-    }
-    else if((nEndFade - temp) <= nStartFade)
-        fFadeChange = 1.0f;
-    else
-        fFadeChange = 1.0f / (nEndFade - nStartFade - temp);
-
-    if(nTotalPlays < nStartFade)
-        fFadeVolume = 1.0f;
-    else if(nTotalPlays >= nEndFade)
-        fFadeVolume = 0.0f;
-    else
-    {
-        fFadeVolume = 1.0f - ( (nTotalPlays - nStartFade + 1) * fFadeChange );
-        if(fFadeVolume < 0)
-            fFadeVolume = 0;
-    }
-
-}
-
-static int32_t GetSamples(uint8_t* buffer,int32_t buffersize)
-{
-    if(!buffer)                             return 0;
-    if(buffersize < 16)                     return 0;
-    if(bFade && (nTotalPlays >= nEndFade))  return 0;
-    
-    pOutput = buffer;
-    uint32_t runtocycle =
-        (uint32_t)((buffersize / 2) * nTicksPerSample / 0x10000);
-    nCPUCycle = nAPUCycle = 0;
-    uint32_t tick;
-
-    while(1)
-    {
-        /*tick = (uint32_t)ceil(fTicksUntilNextPlay);*/
-        tick = (nTicksUntilNextPlay+0xffff)>>16;
-        if((tick + nCPUCycle) > runtocycle)
-            tick = runtocycle - nCPUCycle;
-
-        if(bCPUJammed)
-        {
-            nCPUCycle += tick;
-            EmulateAPU(0);
-        }
-        else
-        {
-            tick = Emulate6502(tick + nCPUCycle);
-            EmulateAPU(1);
-        }
-
-        nTicksUntilNextPlay -= tick<<16;
-        if(nTicksUntilNextPlay <= 0)
-        {
-            nTicksUntilNextPlay += nTicksPerPlay;
-            if((bCPUJammed == 2) || bNoWaitForReturn)
-            {
-                regX = regY = regA = (bCleanAXY ? 0 : 0xCD);
-                regPC = 0x5004;
-                nTotalPlays++;
-                bDMCPop_SamePlay = 0;
-                bCPUJammed = 0;
-                if(nForce4017Write == 1)    WriteMemory_pAPU(0x4017,0x00);
-                if(nForce4017Write == 2)    WriteMemory_pAPU(0x4017,0x80);
-            }
-            
-            if(bFade && (nTotalPlays >= nStartFade))
-            {
-                fFadeVolume -= fFadeChange;
-                if(fFadeVolume < 0)
-                    fFadeVolume = 0;
-                if(nTotalPlays >= nEndFade)
-                    break;
-            }
-        }
-
-        if(nCPUCycle >= runtocycle)
-            break;
-    }
-
-    nCPUCycle = nAPUCycle = 0;
-
-    if(nSilentSampleMax && bFade)
-    {
-        int16_t* tempbuf = (int16_t*)buffer;
-        while( ((uint8_t*)tempbuf) < pOutput)
-        {
-            if( (*tempbuf < -SILENCE_THRESHOLD) ||
-                (*tempbuf > SILENCE_THRESHOLD) )
-                nSilentSamples = 0;
-            else
-            {
-                if(++nSilentSamples >= nSilentSampleMax)
-                    return (int32_t)( ((uint8_t*)tempbuf) - buffer);
-            }
-            tempbuf++;
-        }
-    }
-
-    return (int32_t)(pOutput - buffer);
-}
-
-/****************** 6502 emulation ******************/
-
-/*  Memory reading/writing and other defines */
-
-/* reads zero page memory */
-#define     Zp(a)           pRAM[a]
-/* reads zero page memory in word form */
-#define     ZpWord(a)       (Zp(a) | (Zp((uint8_t)(a + 1)) << 8))
-/* reads memory */
-#define     Rd(a)           ((ReadMemory[((uint16_t)(a)) >> 12])(a))
-/* reads memory in word form */
-#define     RdWord(a)       (Rd(a) | (Rd(a + 1) << 8))
-/* writes memory */
-#define     Wr(a,v)         (WriteMemory[((uint16_t)(a)) >> 12])(a,v)
-/* writes zero paged memory */
-#define     WrZ(a,v)        pRAM[a] = v
-/* pushes a value onto the stack */
-#define     PUSH(v)         pStack[SP--] = v
-/* pulls a value from the stack */
-#define     PULL(v)         v = pStack[++SP]
-
-/*  Addressing Modes */
-
-/* first set - gets the value that's being addressed */
-/*Immediate*/
-#define Ad_VlIm()   val = Rd(PC.W); PC.W++
-/*Zero Page*/
-#define Ad_VlZp()   final.W = Rd(PC.W); val = Zp(final.W); PC.W++
-/*Zero Page, X*/
-#define Ad_VlZx()   front.W = final.W = Rd(PC.W); final.B.l += X;           \
-                    val = Zp(final.B.l); PC.W++
-/*Zero Page, Y*/
-#define Ad_VlZy()   front.W = final.W = Rd(PC.W); final.B.l += Y;           \
-                    val = Zp(final.B.l); PC.W++
-/*Absolute*/
-#define Ad_VlAb()   final.W = RdWord(PC.W); val = Rd(final.W); PC.W += 2
-/*Absolute, X [uses extra cycle if crossed page]*/
-#define Ad_VlAx()   front.W = final.W = RdWord(PC.W); final.W += X; PC.W += 2;\
-                    if(front.B.h != final.B.h) nCPUCycle++; val = Rd(final.W)
-/*Absolute, X [uses extra cycle if crossed page]*/
-#define Ad_VlAy()   front.W = final.W = RdWord(PC.W); final.W += Y; PC.W += 2;\
-                    if(front.B.h != final.B.h) nCPUCycle++; val = Rd(final.W)
-/*(Indirect, X)*/
-#define Ad_VlIx()   front.W = final.W = Rd(PC.W); final.B.l += X; PC.W++;   \
-                    final.W = ZpWord(final.B.l); val = Rd(final.W)
-/*(Indirect), Y [uses extra cycle if crossed page]*/
-#define Ad_VlIy()   val = Rd(PC.W); front.W = final.W = ZpWord(val); PC.W++;\
-                    final.W += Y; if(final.B.h != front.B.h) nCPUCycle++;    \
-                    front.W = val; val = Rd(final.W)
-
-/* second set - gets the ADDRESS that the mode is referring to (for operators
- *              that write to memory) note that AbsoluteX, AbsoluteY, and
- *              IndirectY modes do NOT check for page boundary crossing here
- *              since that extra cycle isn't added for operators that write to
- *              memory (it only applies to ones that only read from memory.. in
- *              which case the 1st set should be used)
- */
-/*Zero Page*/
-#define Ad_AdZp()   final.W = Rd(PC.W); PC.W++
-/*Zero Page, X*/
-#define Ad_AdZx()   final.W = front.W = Rd(PC.W); final.B.l += X; PC.W++
-/*Zero Page, Y*/
-#define Ad_AdZy()   final.W = front.W = Rd(PC.W); final.B.l += Y; PC.W++
-/*Absolute*/
-#define Ad_AdAb()   final.W = RdWord(PC.W); PC.W += 2
-/*Absolute, X*/
-#define Ad_AdAx()   front.W = final.W = RdWord(PC.W); PC.W += 2;            \
-                    final.W += X
-/*Absolute, Y*/
-#define Ad_AdAy()   front.W = final.W = RdWord(PC.W); PC.W += 2;            \
-                    final.W += Y
-/*(Indirect, X)*/
-#define Ad_AdIx()   front.W = final.W = Rd(PC.W); PC.W++; final.B.l += X;   \
-                    final.W = ZpWord(final.B.l)
-/*(Indirect), Y*/
-#define Ad_AdIy()   front.W = Rd(PC.W); final.W = ZpWord(front.W) + Y;      \
-                    PC.W++
-
-/* third set - reads memory, performs the desired operation on the value, then
- * writes back to memory
- *       used for operators that directly change memory (ASL, INC, DEC, etc)
- */
-/*Zero Page*/
-#define MRW_Zp(cmd) Ad_AdZp(); val = Zp(final.W); cmd(val); WrZ(final.W,val)
-/*Zero Page, X*/
-#define MRW_Zx(cmd) Ad_AdZx(); val = Zp(final.W); cmd(val); WrZ(final.W,val)
-/*Zero Page, Y*/
-#define MRW_Zy(cmd) Ad_AdZy(); val = Zp(final.W); cmd(val); WrZ(final.W,val)
-/*Absolute*/
-#define MRW_Ab(cmd) Ad_AdAb(); val = Rd(final.W); cmd(val); Wr(final.W,val)
-/*Absolute, X*/
-#define MRW_Ax(cmd) Ad_AdAx(); val = Rd(final.W); cmd(val); Wr(final.W,val)
-/*Absolute, Y*/
-#define MRW_Ay(cmd) Ad_AdAy(); val = Rd(final.W); cmd(val); Wr(final.W,val)
-/*(Indirect, X)*/
-#define MRW_Ix(cmd) Ad_AdIx(); val = Rd(final.W); cmd(val); Wr(final.W,val)
-/*(Indirect), Y*/
-#define MRW_Iy(cmd) Ad_AdIy(); val = Rd(final.W); cmd(val); Wr(final.W,val)
-
-/* Relative modes are special in that they're only used by branch commands
- *  this macro handles the jump, and should only be called if the branch
- *  condition was true if the branch condition was false, the PC must be
- *  incremented
- */
-
-#define RelJmp(cond)    val = Rd(PC.W); PC.W++; final.W = PC.W + (int8_t)(val);\
-                        if(cond) {\
-                        nCPUCycle += ((final.B.h != PC.B.h) ? 2 : 1);\
-                        PC.W = final.W; }
-
-/* Status Flags */
-
-#define     C_FLAG      0x01    /* carry flag */
-#define     Z_FLAG      0x02    /* zero flag */
-#define     I_FLAG      0x04    /* mask interrupt flag */
-#define     D_FLAG      0x08    /* decimal flag (decimal mode is unsupported on
-                                   NES) */
-#define     B_FLAG      0x10    /* break flag (not really in the status register
-                                   It's value in ST is never used.  When ST is
-                                   put in memory (by an interrupt or PHP), this
-                                   flag is set only if BRK was called)
-                                   ** also when PHP is called due to a bug */
-#define     R_FLAG      0x20    /* reserved flag (not really in the register.
-                                   It's value is never used.
-                                   Whenever ST is put in memory,
-                                   this flag is always set) */
-#define     V_FLAG      0x40    /* overflow flag */
-#define     N_FLAG      0x80    /* sign flag */
-
-
-/*  Lookup Tables */
-
-/* the number of CPU cycles used for each instruction */
-static const uint8_t CPU_Cycles[0x100] ICONST_ATTR_NSF_LARGE_IRAM = {
-7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6,
-2,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7,
-6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6,
-2,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7,
-6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6,
-2,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7,
-6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6,
-2,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7,
-2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
-2,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5,
-2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
-2,5,0,5,4,4,4,4,2,4,2,4,4,4,4,4,
-2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
-2,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7,
-2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
-2,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7     };
-
-/* the status of the NZ flags for the given value */
-static const uint8_t NZTable[0x100] ICONST_ATTR_NSF_LARGE_IRAM = {
-Z_FLAG,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,
-N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG };
-
-/* A quick macro for working with the above table */
-#define UpdateNZ(v) ST = (ST & ~(N_FLAG|Z_FLAG)) | NZTable[v]
-
-
-/*
- *  Opcodes
- *
- *      These opcodes perform the action with the given value (changing that
- *  value if necessary).  Registers and flags associated with the operation
- *  are changed accordingly.  There are a few exceptions which will be noted
- *  when they arise
- */
-
-
-/*  ADC
-        Adds the value to the accumulator with carry
-        Changes:  A, NVZC
-        - Decimal mode not supported on the NES
-        - Due to a bug, NVZ flags are not altered if the Decimal flag is on
-          --(taken out)-- */
-#define ADC()                                                           \
-    tw.W = A + val + (ST & C_FLAG);                                     \
-    ST = (ST & (I_FLAG|D_FLAG)) | tw.B.h | NZTable[tw.B.l] |            \
-        ( (0x80 & ~(A ^ val) & (A ^ tw.B.l)) ? V_FLAG : 0 );            \
-    A = tw.B.l
-
-/*  AND
-        Combines the value with the accumulator using a bitwise AND operation
-        Changes:  A, NZ     */
-#define AND()                                                           \
-    A &= val;                                                           \
-    UpdateNZ(A)
-
-/*  ASL
-        Left shifts the value 1 bit.  The bit that gets shifted out goes to
-        the carry flag.
-        Changes:  value, NZC        */
-#define ASL(value)                                                      \
-    tw.W = value << 1;                                                  \
-    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | tw.B.h | NZTable[tw.B.l];     \
-    value = tw.B.l
-
-/*  BIT
-        Compares memory with the accumulator with an AND operation, but changes
-        neither.
-        The two high bits of memory get transferred to the status reg
-        Z is set if the AND operation yielded zero, otherwise it's cleared
-        Changes:  NVZ               */
-#define BIT()                                                           \
-    ST = (ST & ~(N_FLAG|V_FLAG|Z_FLAG)) | (val & (N_FLAG|V_FLAG)) |     \
-            ((A & val) ? 0 : Z_FLAG)
-
-/*  CMP, CPX, CPY
-        Compares memory with the given register with a subtraction operation.
-        Flags are set accordingly depending on the result:
-        Reg < Memory:  Z=0, C=0
-        Reg = Memory:  Z=1, C=1
-        Reg > Memory:  Z=0, C=1
-        N is set according to the result of the subtraction operation
-        Changes:  NZC
-
-        NOTE -- CMP, CPX, CPY all share this same routine, so the desired
-                register (A, X, or Y respectively) must be given when calling
-                this macro... as well as the memory to compare it with. */
-#define CMP(reg)                                                        \
-    tw.W = reg - val;                                                   \
-    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | (tw.B.h ? 0 : C_FLAG) |       \
-            NZTable[tw.B.l]
-
-/*  DEC, DEX, DEY
-        Decriments a value by one.
-        Changes:  value, NZ             */
-#define DEC(value)                                                      \
-    value--;                                                            \
-    UpdateNZ(value)
-
-/*  EOR
-        Combines a value with the accumulator using a bitwise exclusive-OR
-        operation
-        Changes:  A, NZ                 */
-#define EOR()                                                           \
-    A ^= val;                                                           \
-    UpdateNZ(A)
-
-/*  INC, INX, INY
-        Incriments a value by one.
-        Changes:  value, NZ             */
-#define INC(value)                                                      \
-    value++;                                                            \
-    UpdateNZ(value)
-
-/*  LSR
-        Shifts value one bit to the right.  Bit that gets shifted out goes to
-        the Carry flag.
-        Changes:  value, NZC            */
-#define LSR(value)                                                      \
-    tw.W = value >> 1;                                                  \
-    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | NZTable[tw.B.l] |             \
-        (value & 0x01);                                                 \
-    value = tw.B.l
-
-/*  ORA
-        Combines a value with the accumulator using a bitwise inclusive-OR
-        operation
-        Changes:  A, NZ                 */
-#define ORA()                                                           \
-    A |= val;                                                           \
-    UpdateNZ(A)
-
-/*  ROL
-        Rotates a value one bit to the left:
-        C <-   7<-6<-5<-4<-3<-2<-1<-0    <- C
-        Changes:  value, NZC            */
-#define ROL(value)                                                      \
-    tw.W = (value << 1) | (ST & 0x01);                                  \
-    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | NZTable[tw.B.l] | tw.B.h;     \
-    value = tw.B.l
-
-/*  ROR
-        Rotates a value one bit to the right:
-        C ->   7->6->5->4->3->2->1->0   -> C
-        Changes:  value, NZC            */
-#define ROR(value)                                                      \
-    tw.W = (value >> 1) | (ST << 7);                                    \
-    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | NZTable[tw.B.l] |             \
-        (value & 0x01);                                                 \
-    value = tw.B.l
-
-/*  SBC
-        Subtracts a value from the accumulator with borrow (inverted carry)
-        Changes:  A, NVZC
-        - Decimal mode not supported on the NES
-        - Due to a bug, NVZ flags are not altered if the Decimal flag is on
-           --(taken out)-- */
-#define SBC()                                                               \
-    tw.W = A - val - ((ST & C_FLAG) ? 0 : 1);                               \
-    ST = (ST & (I_FLAG|D_FLAG)) | (tw.B.h ? 0 : C_FLAG) | NZTable[tw.B.l] | \
-                    (((A ^ val) & (A ^ tw.B.l) & 0x80) ? V_FLAG : 0);       \
-    A = tw.B.l
-
-/*  Undocumented Opcodes
- *
- *      These opcodes are not included in the official specifications.  However,
- *  some of the unused opcode values perform operations which have since been
- *  documented.
- */
-
-
-/*  ASO
-        Left shifts a value, then ORs the result with the accumulator
-        Changes:  value, A, NZC                                         */
-#define ASO(value)                                                      \
-    tw.W = value << 1;                                                  \
-    A |= tw.B.l;                                                        \
-    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | NZTable[A] | tw.B.h;          \
-    value = tw.B.l
-
-/*  RLA
-        Roll memory left 1 bit, then AND the result with the accumulator
-        Changes:  value, A, NZC                                         */
-#define RLA(value)                                                      \
-    tw.W = (value << 1) | (ST & 0x01);                                  \
-    A &= tw.B.l;                                                        \
-    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | NZTable[A] | tw.B.h;          \
-    value = tw.B.l
-
-/*  LSE
-        Right shifts a value one bit, then EORs the result with the accumulator
-        Changes:  value, A, NZC                                         */
-#define LSE(value)                                                      \
-    tw.W = value >> 1;                                                  \
-    A ^= tw.B.l;                                                        \
-    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | NZTable[A] | (value & 0x01);  \
-    value = tw.B.l
-
-/*  RRA
-        Roll memory right one bit, then ADC the result
-        Changes:  value, A, NVZC                                        */
-#define RRA(value)                                                      \
-    tw.W = (value >> 1) | (ST << 7);                                    \
-    ST = (ST & ~C_FLAG) | (value & 0x01);                               \
-    value = tw.B.l;                                                     \
-    ADC()
-
-/*  AXS
-        ANDs the contents of the X and A registers and stores the result
-        int memory.
-        Changes:  value  [DOES NOT CHANGE X, A, or any flags]           */
-#define AXS(value)                                                      \
-    value = A & X
-
-/*  DCM
-        Decriments a value and compares it with the A register.
-        Changes:  value, NZC                                            */
-#define DCM(value)                                                          \
-    value--;                                                                \
-    CMP(A)
-
-/*  INS
-        Incriments a value then SBCs it
-        Changes:  value, A, NVZC                                        */
-#define INS(value)                                                      \
-    value++;                                                            \
-    SBC()
-
-/*  AXA     */
-#define AXA(value)                                                      \
-    value = A & X & (Rd(PC.W - 1) + 1)
-
-
-/* The 6502 emulation function! */
-
-static uint8_t val;
-static uint8_t op;
-
-static uint32_t Emulate6502(uint32_t runto)
-{
-    /* If the CPU is jammed... don't bother */
-    if(bCPUJammed == 1)
-        return 0;
-
-    register union TWIN tw;     /* used in calculations */
-    register uint8_t    ST = regP;
-    register union TWIN PC;
-    uint8_t         SP = regSP;
-    register uint8_t    A = regA;
-    register uint8_t    X = regX;
-    register uint8_t    Y = regY;
-    union TWIN          front;
-    union TWIN          final;
-    PC.W = regPC;
-
-    uint32_t ret = nCPUCycle;
-    
-    ENTER_TIMER(cpu);
-    
-    /*  Start the loop */
-    
-    while(nCPUCycle < runto)
-    {
-        op = Rd(PC.W);
-        PC.W++;
-
-        nCPUCycle += CPU_Cycles[op];
-        switch(op)
-        {
-            /* Documented Opcodes first */
-            
-        /*  Flag setting/clearing */
-        case 0x18:  ST &= ~C_FLAG;  break;      /* CLC  */
-        case 0x38:  ST |=  C_FLAG;  break;      /* SEC  */
-        case 0x58:  ST &= ~I_FLAG;  break;      /* CLI  */
-        case 0x78:  ST |=  I_FLAG;  break;      /* SEI  */
-        case 0xB8:  ST &= ~V_FLAG;  break;      /* CLV  */
-        case 0xD8:  ST &= ~D_FLAG;  break;      /* CLD  */
-        case 0xF8:  ST |=  D_FLAG;  break;      /* SED  */
-
-        /* Branch commands */
-        case 0x10:  RelJmp(!(ST & N_FLAG)); break;  /* BPL  */
-        case 0x30:  RelJmp( (ST & N_FLAG)); break;  /* BMI  */
-        case 0x50:  RelJmp(!(ST & V_FLAG)); break;  /* BVC  */
-        case 0x70:  RelJmp( (ST & V_FLAG)); break;  /* BVS  */
-        case 0x90:  RelJmp(!(ST & C_FLAG)); break;  /* BCC  */
-        case 0xB0:  RelJmp( (ST & C_FLAG)); break;  /* BCS  */
-        case 0xD0:  RelJmp(!(ST & Z_FLAG)); break;  /* BNE  */
-        case 0xF0:  RelJmp( (ST & Z_FLAG)); break;  /* BEQ  */
-
-        /* Direct stack alteration commands (push/pull commands) */
-        case 0x08:  PUSH(ST | R_FLAG | B_FLAG); break;  /* PHP  */
-        case 0x28:  PULL(ST);                   break;  /* PLP  */
-        case 0x48:  PUSH(A);                    break;  /* PHA  */
-        case 0x68:  PULL(A); UpdateNZ(A);       break;  /* PLA  */
-
-        /* Register Transfers */
-        case 0x8A:  A = X;  UpdateNZ(A);    break;  /* TXA  */
-        case 0x98:  A = Y;  UpdateNZ(A);    break;  /* TYA  */
-        case 0x9A:  SP = X;                 break;  /* TXS  */
-        case 0xA8:  Y = A;  UpdateNZ(A);    break;  /* TAY  */
-        case 0xAA:  X = A;  UpdateNZ(A);    break;  /* TAX  */
-        case 0xBA:  X = SP; UpdateNZ(X);    break;  /* TSX  */
-
-        /*  Other commands */
-
-        /* ADC  */
-        case 0x61:  Ad_VlIx();  ADC();  break;
-        case 0x65:  Ad_VlZp();  ADC();  break;
-        case 0x69:  Ad_VlIm();  ADC();  break;
-        case 0x6D:  Ad_VlAb();  ADC();  break;
-        case 0x71:  Ad_VlIy();  ADC();  break;
-        case 0x75:  Ad_VlZx();  ADC();  break;
-        case 0x79:  Ad_VlAy();  ADC();  break;
-        case 0x7D:  Ad_VlAx();  ADC();  break;
-
-        /* AND  */
-        case 0x21:  Ad_VlIx();  AND();  break;
-        case 0x25:  Ad_VlZp();  AND();  break;
-        case 0x29:  Ad_VlIm();  AND();  break;
-        case 0x2D:  Ad_VlAb();  AND();  break;
-        case 0x31:  Ad_VlIy();  AND();  break;
-        case 0x35:  Ad_VlZx();  AND();  break;
-        case 0x39:  Ad_VlAy();  AND();  break;
-        case 0x3D:  Ad_VlAx();  AND();  break;
-
-        /* ASL  */
-        case 0x0A:  ASL(A);             break;
-        case 0x06:  MRW_Zp(ASL);        break;
-        case 0x0E:  MRW_Ab(ASL);        break;
-        case 0x16:  MRW_Zx(ASL);        break;
-        case 0x1E:  MRW_Ax(ASL);        break;
-
-        /* BIT  */
-        case 0x24:  Ad_VlZp();  BIT();  break;
-        case 0x2C:  Ad_VlAb();  BIT();  break;
-
-        /* BRK  */
-        case 0x00:
-            if(bIgnoreBRK)
-                break;
-            PC.W++;                     /*BRK has a padding byte*/
-            PUSH(PC.B.h);               /*push high byte of the return address*/
-            PUSH(PC.B.l);               /*push low byte of return address*/
-            PUSH(ST | R_FLAG | B_FLAG); /*push processor status with R|B flags*/
-            ST |= I_FLAG;               /*mask interrupts*/
-            PC.W = RdWord(0xFFFE);      /*read the IRQ vector and jump to it*/
-
-            /* extra check to make sure we didn't hit an infinite BRK loop */
-            if(!Rd(PC.W))                   /* next command will be BRK */
-            {
-                /* the CPU will endlessly loop...
-                   just jam it to ease processing power */
-                bCPUJammed = 1;
-                goto jammed;
-            }
-            break;
-
-        /* CMP  */
-        case 0xC1:  Ad_VlIx();  CMP(A); break;
-        case 0xC5:  Ad_VlZp();  CMP(A); break;
-        case 0xC9:  Ad_VlIm();  CMP(A); break;
-        case 0xCD:  Ad_VlAb();  CMP(A); break;
-        case 0xD1:  Ad_VlIy();  CMP(A); break;
-        case 0xD5:  Ad_VlZx();  CMP(A); break;
-        case 0xD9:  Ad_VlAy();  CMP(A); break;
-        case 0xDD:  Ad_VlAx();  CMP(A); break;
-
-        /* CPX  */
-        case 0xE0:  Ad_VlIm();  CMP(X); break;
-        case 0xE4:  Ad_VlZp();  CMP(X); break;
-        case 0xEC:  Ad_VlAb();  CMP(X); break;
-
-        /* CPY  */
-        case 0xC0:  Ad_VlIm();  CMP(Y); break;
-        case 0xC4:  Ad_VlZp();  CMP(Y); break;
-        case 0xCC:  Ad_VlAb();  CMP(Y); break;
-
-        /* DEC  */
-        case 0xCA:  DEC(X);             break;      /* DEX  */
-        case 0x88:  DEC(Y);             break;      /* DEY  */
-        case 0xC6:  MRW_Zp(DEC);        break;
-        case 0xCE:  MRW_Ab(DEC);        break;
-        case 0xD6:  MRW_Zx(DEC);        break;
-        case 0xDE:  MRW_Ax(DEC);        break;
-
-        /* EOR  */
-        case 0x41:  Ad_VlIx();  EOR();  break;
-        case 0x45:  Ad_VlZp();  EOR();  break;
-        case 0x49:  Ad_VlIm();  EOR();  break;
-        case 0x4D:  Ad_VlAb();  EOR();  break;
-        case 0x51:  Ad_VlIy();  EOR();  break;
-        case 0x55:  Ad_VlZx();  EOR();  break;
-        case 0x59:  Ad_VlAy();  EOR();  break;
-        case 0x5D:  Ad_VlAx();  EOR();  break;
-
-        /* INC  */
-        case 0xE8:  INC(X);             break;      /* INX  */
-        case 0xC8:  INC(Y);             break;      /* INY  */
-        case 0xE6:  MRW_Zp(INC);        break;
-        case 0xEE:  MRW_Ab(INC);        break;
-        case 0xF6:  MRW_Zx(INC);        break;
-        case 0xFE:  MRW_Ax(INC);        break;
-
-        /* JMP  */
-        /* Absolute JMP */
-        case 0x4C:  final.W = RdWord(PC.W);  PC.W = final.W; val = 0;   break;
-        /* Indirect JMP -- must take caution:
-           Indirection at 01FF will read from 01FF and 0100 (not 0200) */
-        case 0x6C:  front.W = final.W = RdWord(PC.W);
-                    PC.B.l = Rd(final.W); final.B.l++;
-                    PC.B.h = Rd(final.W); final.W = PC.W;
-                    break;      
-        /* JSR  */
-        case 0x20:
-            val = 0;
-            final.W = RdWord(PC.W);
-            PC.W++;         /* JSR only increments the return address by one.
-                               It's incremented again upon RTS */
-            PUSH(PC.B.h);   /* push high byte of return address */
-            PUSH(PC.B.l);   /* push low byte of return address */
-            PC.W = final.W;
-            break;
-
-        /* LDA  */
-        case 0xA1:  Ad_VlIx(); A = val; UpdateNZ(A);    break;
-        case 0xA5:  Ad_VlZp(); A = val; UpdateNZ(A);    break;
-        case 0xA9:  Ad_VlIm(); A = val; UpdateNZ(A);    break;
-        case 0xAD:  Ad_VlAb(); A = val; UpdateNZ(A);    break;
-        case 0xB1:  Ad_VlIy(); A = val; UpdateNZ(A);    break;
-        case 0xB5:  Ad_VlZx(); A = val; UpdateNZ(A);    break;
-        case 0xB9:  Ad_VlAy(); A = val; UpdateNZ(A);    break;
-        case 0xBD:  Ad_VlAx(); A = val; UpdateNZ(A);    break;
-
-        /* LDX  */
-        case 0xA2:  Ad_VlIm(); X = val; UpdateNZ(X);    break;
-        case 0xA6:  Ad_VlZp(); X = val; UpdateNZ(X);    break;
-        case 0xAE:  Ad_VlAb(); X = val; UpdateNZ(X);    break;
-        case 0xB6:  Ad_VlZy(); X = val; UpdateNZ(X);    break;
-        case 0xBE:  Ad_VlAy(); X = val; UpdateNZ(X);    break;
-
-        /* LDY  */
-        case 0xA0:  Ad_VlIm(); Y = val; UpdateNZ(Y);    break;
-        case 0xA4:  Ad_VlZp(); Y = val; UpdateNZ(Y);    break;
-        case 0xAC:  Ad_VlAb(); Y = val; UpdateNZ(Y);    break;
-        case 0xB4:  Ad_VlZx(); Y = val; UpdateNZ(Y);    break;
-        case 0xBC:  Ad_VlAx(); Y = val; UpdateNZ(Y);    break;
-
-        /* LSR  */
-        case 0x4A:  LSR(A);             break;
-        case 0x46:  MRW_Zp(LSR);        break;
-        case 0x4E:  MRW_Ab(LSR);        break;
-        case 0x56:  MRW_Zx(LSR);        break;
-        case 0x5E:  MRW_Ax(LSR);        break;
-
-        /* NOP  */
-        case 0xEA:
-
-        /* --- Undocumented ---
-            These opcodes perform the same action as NOP    */
-        case 0x1A:  case 0x3A:  case 0x5A:
-        case 0x7A:  case 0xDA:  case 0xFA:      break;
-
-        /* ORA  */
-        case 0x01:  Ad_VlIx();  ORA();  break;
-        case 0x05:  Ad_VlZp();  ORA();  break;
-        case 0x09:  Ad_VlIm();  ORA();  break;
-        case 0x0D:  Ad_VlAb();  ORA();  break;
-        case 0x11:  Ad_VlIy();  ORA();  break;
-        case 0x15:  Ad_VlZx();  ORA();  break;
-        case 0x19:  Ad_VlAy();  ORA();  break;
-        case 0x1D:  Ad_VlAx();  ORA();  break;
-
-        /* ROL  */
-        case 0x2A:  ROL(A);             break;
-        case 0x26:  MRW_Zp(ROL);        break;
-        case 0x2E:  MRW_Ab(ROL);        break;
-        case 0x36:  MRW_Zx(ROL);        break;
-        case 0x3E:  MRW_Ax(ROL);        break;
-
-        /* ROR  */
-        case 0x6A:  ROR(A);             break;
-        case 0x66:  MRW_Zp(ROR);        break;
-        case 0x6E:  MRW_Ab(ROR);        break;
-        case 0x76:  MRW_Zx(ROR);        break;
-        case 0x7E:  MRW_Ax(ROR);        break;
-
-        /* RTI  */
-        case 0x40:
-            PULL(ST);                   /*pull processor status*/
-            PULL(PC.B.l);               /*pull low byte of return address*/
-            PULL(PC.B.h);               /*pull high byte of return address*/
-            break;
-
-        /* RTS  */
-        case 0x60:
-            PULL(PC.B.l);
-            PULL(PC.B.h);
-            PC.W++; /* the return address is one less of what it needs */
-            break;
-
-        /* SBC  */
-        case 0xE1:  Ad_VlIx();  SBC();  break;
-        case 0xE5:  Ad_VlZp();  SBC();  break;
-        /* - Undocumented -  EB performs the same operation as SBC immediate */
-        case 0xEB:
-        case 0xE9:  Ad_VlIm();  SBC();  break;
-        case 0xED:  Ad_VlAb();  SBC();  break;
-        case 0xF1:  Ad_VlIy();  SBC();  break;
-        case 0xF5:  Ad_VlZx();  SBC();  break;
-        case 0xF9:  Ad_VlAy();  SBC();  break;
-        case 0xFD:  Ad_VlAx();  SBC();  break;
-
-        /* STA  */
-        case 0x81:  Ad_AdIx(); val = A; Wr(final.W,A);  break;
-        case 0x85:  Ad_AdZp(); val = A; WrZ(final.W,A); break;
-        case 0x8D:  Ad_AdAb(); val = A; Wr(final.W,A);  break;
-        case 0x91:  Ad_AdIy(); val = A; Wr(final.W,A);  break;
-        case 0x95:  Ad_AdZx(); val = A; WrZ(final.W,A); break;
-        case 0x99:  Ad_AdAy(); val = A; Wr(final.W,A);  break;
-        case 0x9D:  Ad_AdAx(); val = A; Wr(final.W,A);  break;
-
-        /* STX  */
-        case 0x86:  Ad_AdZp(); val = X; WrZ(final.W,X); break;
-        case 0x8E:  Ad_AdAb(); val = X; Wr(final.W,X);  break;
-        case 0x96:  Ad_AdZy(); val = X; WrZ(final.W,X); break;
-
-        /* STY  */
-        case 0x84:  Ad_AdZp(); val = Y; WrZ(final.W,Y); break;
-        case 0x8C:  Ad_AdAb(); val = Y; Wr(final.W,Y);  break;
-        case 0x94:  Ad_AdZx(); val = Y; WrZ(final.W,Y); break;
-
-        /*  Undocumented Opcodes */
-        /* ASO  */
-        case 0x03:  if(bIgnoreIllegalOps) break;    MRW_Ix(ASO);    break;
-        case 0x07:  if(bIgnoreIllegalOps) break;    MRW_Zp(ASO);    break;
-        case 0x0F:  if(bIgnoreIllegalOps) break;    MRW_Ab(ASO);    break;
-        case 0x13:  if(bIgnoreIllegalOps) break;    MRW_Iy(ASO);    break;
-        case 0x17:  if(bIgnoreIllegalOps) break;    MRW_Zx(ASO);    break;
-        case 0x1B:  if(bIgnoreIllegalOps) break;    MRW_Ay(ASO);    break;
-        case 0x1F:  if(bIgnoreIllegalOps) break;    MRW_Ax(ASO);    break;
-
-        /* RLA  */
-        case 0x23:  if(bIgnoreIllegalOps) break;    MRW_Ix(RLA);    break;
-        case 0x27:  if(bIgnoreIllegalOps) break;    MRW_Zp(RLA);    break;
-        case 0x2F:  if(bIgnoreIllegalOps) break;    MRW_Ab(RLA);    break;
-        case 0x33:  if(bIgnoreIllegalOps) break;    MRW_Iy(RLA);    break;
-        case 0x37:  if(bIgnoreIllegalOps) break;    MRW_Zx(RLA);    break;
-        case 0x3B:  if(bIgnoreIllegalOps) break;    MRW_Ay(RLA);    break;
-        case 0x3F:  if(bIgnoreIllegalOps) break;    MRW_Ax(RLA);    break;
-
-        /* LSE  */
-        case 0x43:  if(bIgnoreIllegalOps) break;    MRW_Ix(LSE);    break;
-        case 0x47:  if(bIgnoreIllegalOps) break;    MRW_Zp(LSE);    break;
-        case 0x4F:  if(bIgnoreIllegalOps) break;    MRW_Ab(LSE);    break;
-        case 0x53:  if(bIgnoreIllegalOps) break;    MRW_Iy(LSE);    break;
-        case 0x57:  if(bIgnoreIllegalOps) break;    MRW_Zx(LSE);    break;
-        case 0x5B:  if(bIgnoreIllegalOps) break;    MRW_Ay(LSE);    break;
-        case 0x5F:  if(bIgnoreIllegalOps) break;    MRW_Ax(LSE);    break;
-
-        /* RRA  */
-        case 0x63:  if(bIgnoreIllegalOps) break;    MRW_Ix(RRA);    break;
-        case 0x67:  if(bIgnoreIllegalOps) break;    MRW_Zp(RRA);    break;
-        case 0x6F:  if(bIgnoreIllegalOps) break;    MRW_Ab(RRA);    break;
-        case 0x73:  if(bIgnoreIllegalOps) break;    MRW_Iy(RRA);    break;
-        case 0x77:  if(bIgnoreIllegalOps) break;    MRW_Zx(RRA);    break;
-        case 0x7B:  if(bIgnoreIllegalOps) break;    MRW_Ay(RRA);    break;
-        case 0x7F:  if(bIgnoreIllegalOps) break;    MRW_Ax(RRA);    break;
-
-        /* AXS  */
-        case 0x83:  if(bIgnoreIllegalOps) break;    MRW_Ix(AXS);    break;
-        case 0x87:  if(bIgnoreIllegalOps) break;    MRW_Zp(AXS);    break;
-        case 0x8F:  if(bIgnoreIllegalOps) break;    MRW_Ab(AXS);    break;
-        case 0x97:  if(bIgnoreIllegalOps) break;    MRW_Zy(AXS);    break;
-
-        /* LAX  */
-        case 0xA3:  if(bIgnoreIllegalOps) break;
-            Ad_VlIx();  X = A = val; UpdateNZ(A);   break;
-        case 0xA7:  if(bIgnoreIllegalOps) break;
-            Ad_VlZp();  X = A = val; UpdateNZ(A);   break;
-        case 0xAF:  if(bIgnoreIllegalOps) break;
-            Ad_VlAb();  X = A = val; UpdateNZ(A);   break;
-        case 0xB3:  if(bIgnoreIllegalOps) break;
-            Ad_VlIy();  X = A = val; UpdateNZ(A);   break;
-        case 0xB7:  if(bIgnoreIllegalOps) break;
-            Ad_VlZy();  X = A = val; UpdateNZ(A);   break;
-        case 0xBF:  if(bIgnoreIllegalOps) break;
-            Ad_VlAy();  X = A = val; UpdateNZ(A);   break;
-
-        /* DCM  */
-        case 0xC3:  if(bIgnoreIllegalOps) break;    MRW_Ix(DCM);    break;
-        case 0xC7:  if(bIgnoreIllegalOps) break;    MRW_Zp(DCM);    break;
-        case 0xCF:  if(bIgnoreIllegalOps) break;    MRW_Ab(DCM);    break;
-        case 0xD3:  if(bIgnoreIllegalOps) break;    MRW_Iy(DCM);    break;
-        case 0xD7:  if(bIgnoreIllegalOps) break;    MRW_Zx(DCM);    break;
-        case 0xDB:  if(bIgnoreIllegalOps) break;    MRW_Ay(DCM);    break;
-        case 0xDF:  if(bIgnoreIllegalOps) break;    MRW_Ax(DCM);    break;
-
-        /* INS  */
-        case 0xE3:  if(bIgnoreIllegalOps) break;    MRW_Ix(INS);    break;
-        case 0xE7:  if(bIgnoreIllegalOps) break;    MRW_Zp(INS);    break;
-        case 0xEF:  if(bIgnoreIllegalOps) break;    MRW_Ab(INS);    break;
-        case 0xF3:  if(bIgnoreIllegalOps) break;    MRW_Iy(INS);    break;
-        case 0xF7:  if(bIgnoreIllegalOps) break;    MRW_Zx(INS);    break;
-        case 0xFB:  if(bIgnoreIllegalOps) break;    MRW_Ay(INS);    break;
-        case 0xFF:  if(bIgnoreIllegalOps) break;    MRW_Ax(INS);    break;
-
-        /* ALR
-                AND Accumulator with memory and LSR the result  */
-        case 0x4B:  if(bIgnoreIllegalOps) break;
-                    Ad_VlIm();  A &= val;   LSR(A); break;
-
-        /* ARR
-                ANDs memory with the Accumulator and RORs the result    */
-        case 0x6B:  if(bIgnoreIllegalOps) break;
-                    Ad_VlIm();  A &= val;   ROR(A); break;
-
-        /* XAA
-                Transfers X -> A, then ANDs A with memory               */
-        case 0x8B:  if(bIgnoreIllegalOps) break;
-                    Ad_VlIm();  A = X & val; UpdateNZ(A);   break;
-
-        /* OAL
-                OR the Accumulator with #EE, AND Accumulator with Memory,
-                Transfer A -> X   */
-        case 0xAB:  if(bIgnoreIllegalOps) break;
-                    Ad_VlIm();  X = (A &= (val | 0xEE));
-                    UpdateNZ(A);    break;
-
-        /* SAX
-                ANDs A and X registers (does not change A), subtracts memory
-                from result (CMP style, not SBC style) result is stored in X */
-        case 0xCB:  if(bIgnoreIllegalOps) break;
-                Ad_VlIm();  tw.W = (X & A) - val; X = tw.B.l;
-                    ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) | NZTable[X] |
-                        (tw.B.h ? C_FLAG : 0);   break;
-        /* SKB
-                Skip Byte... or DOP - Double No-Op
-                These bytes do nothing, but take a parameter (which can be
-                ignored) */
-        case 0x04:  case 0x14:  case 0x34:  case 0x44:  case 0x54:  case 0x64:
-        case 0x80:  case 0x82:  case 0x89:  case 0xC2:  case 0xD4:  case 0xE2:
-        case 0xF4:
-            if(bIgnoreIllegalOps) break;
-            PC.W++;     /* skip unused byte */
-            break;
-
-        /* SKW
-                Swip Word... or TOP - Tripple No-Op
-            These bytes are the same as SKB, only they take a 2 byte parameter.
-            This can be ignored in some cases, but the read needs to be
-            performed in a some cases because an extra clock cycle may be used
-            in the process     */
-        /* Absolute address... no need for operator */
-        case 0x0C:
-            if(bIgnoreIllegalOps) break;
-            PC.W += 2;  break;
-        /* Absolute X address... may cross page, have to perform the read */
-        case 0x1C:  case 0x3C:  case 0x5C:  case 0x7C:  case 0xDC:  case 0xFC:
-            if(bIgnoreIllegalOps) break;
-            Ad_VlAx(); break;
-
-        /* HLT / JAM
-                Jams up CPU operation           */
-        case 0x02:  case 0x12:  case 0x22:  case 0x32:  case 0x42:  case 0x52:
-        case 0x62:  case 0x72:  case 0x92:  case 0xB2:  case 0xD2:  case 0xF2:
-            /*it's not -really- jammed... only the NSF code has ended*/
-            if(PC.W == 0x5004)  bCPUJammed = 2;
-            else
-            {
-                if(bIgnoreIllegalOps) break;
-                bCPUJammed = 1;
-            }
-            goto jammed;
-
-        /* TAS  */
-        case 0x9B:
-            if(bIgnoreIllegalOps) break;
-            Ad_AdAy();
-            SP = A & X & (Rd(PC.W - 1) + 1);
-            Wr(final.W,SP);
-            break;
-
-        /* SAY  */
-        case 0x9C:
-            if(bIgnoreIllegalOps) break;
-            Ad_AdAx();
-            Y &= (Rd(PC.W - 1) + 1);
-            Wr(final.W,Y);
-            break;
-
-        /* XAS  */
-        case 0x9E:
-            if(bIgnoreIllegalOps) break;
-            Ad_AdAy();
-            X &= (Rd(PC.W - 1) + 1);
-            Wr(final.W,X);
-            break;
-
-        /* AXA  */
-        case 0x93:  if(bIgnoreIllegalOps) break;    MRW_Iy(AXA);    break;
-        case 0x9F:  if(bIgnoreIllegalOps) break;    MRW_Ay(AXA);    break;
-
-        /* ANC  */
-        case 0x0B:  case 0x2B:
-            if(bIgnoreIllegalOps) break;
-            Ad_VlIm();
-            A &= val;
-            ST = (ST & ~(N_FLAG|Z_FLAG|C_FLAG)) |
-                NZTable[A] | ((A & 0x80) ? C_FLAG : 0);
-            break;
-
-        /* LAS  */
-        case 0xBB:
-            if(bIgnoreIllegalOps) break;
-            Ad_VlAy();
-            X = A = (SP &= val);
-            UpdateNZ(A);
-            break;
-        }
-    }
-
-jammed:
-    regPC = PC.W;
-    regA = A;
-    regX = X;
-    regY = Y;
-    regSP = SP;
-    regP = ST;
-    
-    EXIT_TIMER(cpu);
-    
-    return (nCPUCycle - ret);
-}
+static int16_t samples[CHUNK_SIZE] IBSS_ATTR;
+static struct Nsf_Emu nsf_emu IDATA_ATTR CACHEALIGN_ATTR;
 
 /****************** rockbox interface ******************/
 
-/** Operational info **/
-static int track = 0;
-static char last_path[MAX_PATH];
-static int dontresettrack = 0;
-static bool repeat_one = false;
-
-static void set_codec_track(int t, int d) {
-    int track,fade,def=0;
-    SetTrack(t);
+static void set_codec_track(int t, int multitrack) {
+    Nsf_start_track(&nsf_emu, t); 
 
     /* for REPEAT_ONE we disable track limits */
-    if (!repeat_one) {
-        if (!bIsExtended || nTrackTime[t]==-1) {track=60*2*1000; def=1;}
-        else track=nTrackTime[t];
-        if (!bIsExtended || nTrackFade[t]==-1) fade=5*1000;
-        else fade=nTrackFade[t];
-        nSilenceTrackMS=5000;
-        SetFadeTime(track,track+fade, fNSFPlaybackSpeed,def);
+    if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+        Track_set_fade(&nsf_emu, Track_length( &nsf_emu, t ) - 4000, 4000);
     }
-    ci->set_elapsed(d*1000); /* d is track no to display */
+    if (multitrack) ci->set_elapsed(t*1000); /* t is track no to display */
+    else ci->set_elapsed(0);
 }
 
 /* this is the codec entry point */
 enum codec_status codec_main(enum codec_entry_call_reason reason)
 {
     if (reason == CODEC_LOAD) {
-        /* we only render 16 bits, 44.1KHz, Mono */
+        /* we only render 16 bits */
         ci->configure(DSP_SET_SAMPLE_DEPTH, 16);
-        ci->configure(DSP_SET_FREQUENCY, 44100);
-        ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
 
-        RebuildOutputTables();
+        /* 44 Khz, Interleaved stereo */
+        ci->configure(DSP_SET_FREQUENCY, 44100);
+        ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
+
+        Nsf_init(&nsf_emu);
+        Nsf_set_sample_rate(&nsf_emu, 44100);
     }
 
     return CODEC_OK;
 }
-    
+
 /* this is called for each file to process */
 enum codec_status codec_run(void)
 {
-    int written;
+    blargg_err_t err;
     uint8_t *buf;
     size_t n;
-    int endofstream; /* end of stream flag */
-    int usingplaylist = 0;
+    int track, is_multitrack;
+    uint32_t elapsed_time;
     intptr_t param;
-    
+
+    track = is_multitrack = 0;
+    elapsed_time = 0;
+
     DEBUGF("NSF: next_track\n");
     if (codec_init()) {
         return CODEC_ERROR;
-    }
-    DEBUGF("NSF: after init\n");
-    
+    }  
+
     codec_set_replaygain(ci->id3);
         
     /* Read the entire file */
@@ -4383,100 +73,63 @@
         DEBUGF("NSF: file load failed\n");
         return CODEC_ERROR;
     }
-
-    repeat_one = ci->global_settings->repeat_mode == REPEAT_ONE;
-    
-init_nsf:    
-    if(!NSFCore_Initialize()) {
-        DEBUGF("NSF: NSFCore_Initialize failed\n"); return CODEC_ERROR;}
-
-    if(LoadFile(buf,ci->filesize)) {
-        DEBUGF("NSF: LoadFile failed\n"); return CODEC_ERROR;}
-    if(!SetPlaybackOptions(44100)) {
-        DEBUGF("NSF: SetPlaybackOptions failed\n"); return CODEC_ERROR;}
-    if(!LoadNSF(nDataBufferSize)) {
-        DEBUGF("NSF: LoadNSF failed\n"); return CODEC_ERROR;}
-
-    if (!dontresettrack||strcmp(ci->id3->path,last_path)) {
-        /* if this is the first time we're seeing this file, or if we haven't
-           been asked to preserve the track number, default to the proper
-           initial track */
-        if (bIsExtended && !repeat_one && nPlaylistSize>0) {
-            /* decide to use the playlist */
-            usingplaylist=1;
-            track=0;
-            set_codec_track(nPlaylist[0],0);
-        } else {
-            /* simply use the initial track */
-            track=nInitialTrack;
-            set_codec_track(track,track);
-        }
-    } else {
-        /* if we've already been running this file assume track is set
-           already */
-        if (usingplaylist) set_codec_track(nPlaylist[track],track);
-        else set_codec_track(track,track);
+   
+    if ((err = Nsf_load(&nsf_emu, buf, ci->filesize))) {
+        DEBUGF("NSF: Nsf_load failed (%s)\n", err);
+        return CODEC_ERROR;
     }
-    strcpy(last_path,ci->id3->path);
+
+    /* Update internal track count */
+    if (nsf_emu.m3u.size > 0)
+        nsf_emu.track_count = nsf_emu.m3u.size;
+
+    if (nsf_emu.track_count > 1) is_multitrack = 1;
+
+next_track:
+    set_codec_track(track, is_multitrack);
 
     /* The main decoder loop */
-    
-    endofstream = 0;
-    
-    reset_profile_timers();
-    
-    while (!endofstream) {
+    while (1) {
         enum codec_command_action action = ci->get_command(&param);
 
         if (action == CODEC_ACTION_HALT)
             break;
 
         if (action == CODEC_ACTION_SEEK_TIME) {
-            track=param/1000;
-            if (usingplaylist) {
-                if (track>=nPlaylistSize) break;
-            } else {
-                if (track>=nTrackCount) break;
+            if (is_multitrack) {
+                track = param/1000;
+                ci->seek_complete();
+                if (track >= nsf_emu.track_count) break;
+                goto next_track;
             }
-            dontresettrack=1;
+
+            ci->set_elapsed(param);
+            elapsed_time = param;
+            Track_seek(&nsf_emu, param);
             ci->seek_complete();
-            goto init_nsf;
-        }
-
-        ENTER_TIMER(total);
-        written=GetSamples((uint8_t*)samples,WAV_CHUNK_SIZE/2);
-        EXIT_TIMER(total);
-        
-        if (!written || SongCompleted()) {
-            print_timers(last_path,track);
-            reset_profile_timers();
             
-            track++;
-            if (usingplaylist) {
-               if (track>=nPlaylistSize) break;
-            } else {
-               if (track>=nTrackCount) break;
+            /* Set fade again */
+            if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+                Track_set_fade(&nsf_emu, Track_length( &nsf_emu, track ), 4000);
             }
-            dontresettrack=1;
-            goto init_nsf;
         }
 
-        ci->pcmbuf_insert(samples, NULL, written >> 1);
-    }
-    
-    print_timers(last_path,track);
+        /* Generate audio buffer */
+        err = Nsf_play(&nsf_emu, CHUNK_SIZE, samples);
+        if (err || nsf_emu.track_ended) {
+            track++;
+            if (track >= nsf_emu.track_count) break;
+            goto next_track;
+        }
 
-    if (repeat_one) {
-        /* in repeat one mode just advance to the next track */
-        track++;
-        if (track>=nTrackCount) track=0;
-        dontresettrack=1;
-        /* at this point we can't tell if another file has been selected */
-    } else {
-        /* otherwise do a proper load of the next file */
-        dontresettrack=0;
-        last_path[0]='\0';
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+
+        /* Set elapsed time for one track files */
+        if (is_multitrack == 0) {
+            elapsed_time += (CHUNK_SIZE / 2) / 44.1;
+            ci->set_elapsed(elapsed_time);
+        }
     }
-    
+
     return CODEC_OK;
 }
diff --git a/apps/codecs/sgc.c b/apps/codecs/sgc.c
new file mode 100644
index 0000000..e5f0299
--- /dev/null
+++ b/apps/codecs/sgc.c
@@ -0,0 +1,123 @@
+
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+
+#include <codecs/lib/codeclib.h>
+#include "libgme/sgc_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 Sgc_Emu sgc_emu IDATA_ATTR CACHEALIGN_ATTR;
+
+/* Coleco Bios */
+/* Colecovision not supported yet
+static char coleco_bios[0x2000];
+*/
+
+/****************** rockbox interface ******************/
+
+static void set_codec_track(int t) {
+    Sgc_start_track(&sgc_emu, t); 
+
+    /* for REPEAT_ONE we disable track limits */
+    if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+        Track_set_fade(&sgc_emu, Track_get_length( &sgc_emu, t ), 4000);
+    }
+    ci->set_elapsed(t*1000); /* t is track no to display */
+}
+
+/* this is the codec entry point */
+enum codec_status codec_main(enum codec_entry_call_reason reason)
+{
+    if (reason == CODEC_LOAD) {
+        /* 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);
+
+        Sgc_init(&sgc_emu);
+        Sgc_set_sample_rate(&sgc_emu, 44100);
+
+        /* set coleco bios, should be named coleco_bios.rom */
+        /* Colecovision not supported yet
+        int fd = ci->open("/coleco_bios.rom", O_RDONLY);
+        if ( fd >= 0 ) {
+            ci->read(fd, coleco_bios, 0x2000);
+            ci->close(fd);
+            set_coleco_bios( &sgc_emu, coleco_bios );
+        }
+        */
+    }
+
+    return CODEC_OK;
+}
+
+/* this is called for each file to process */
+enum codec_status codec_run(void)
+{
+    blargg_err_t err;
+    uint8_t *buf;
+    size_t n;
+    intptr_t param;
+    int track = 0;
+
+    DEBUGF("SGC: next_track\n");
+    if (codec_init()) {
+        return CODEC_ERROR;
+    }
+
+    codec_set_replaygain(ci->id3);
+
+    /* Read the entire file */
+    DEBUGF("SGC: request file\n");
+    ci->seek_buffer(0);
+    buf = ci->request_buffer(&n, ci->filesize);
+    if (!buf || n < (size_t)ci->filesize) {
+        DEBUGF("SGC: file load failed\n");
+        return CODEC_ERROR;
+    }
+   
+    if ((err = Sgc_load_mem(&sgc_emu, buf, ci->filesize))) {
+        DEBUGF("SGC: Sgc_load failed (%s)\n", err);
+        return CODEC_ERROR;
+    }
+
+    /* Update internal track count */
+    if (sgc_emu.m3u.size > 0)
+        sgc_emu.track_count = sgc_emu.m3u.size;
+
+next_track:
+    set_codec_track(track);
+
+    /* The main decoder loop */
+    while (1) {
+        enum codec_command_action action = ci->get_command(&param);
+
+        if (action == CODEC_ACTION_HALT)
+            break;
+
+        if (action == CODEC_ACTION_SEEK_TIME) {
+            track = param/1000;
+            ci->seek_complete();
+            if (track >= sgc_emu.track_count) break;
+            goto next_track;
+        }
+
+        /* Generate audio buffer */
+        err = Sgc_play(&sgc_emu, CHUNK_SIZE, samples);
+        if (err || sgc_emu.track_ended) {
+            track++;
+            if (track >= sgc_emu.track_count) break;
+            goto next_track;
+        }
+
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+    }
+
+    return CODEC_OK;
+}
diff --git a/apps/codecs/vgm.c b/apps/codecs/vgm.c
new file mode 100644
index 0000000..89bfd19
--- /dev/null
+++ b/apps/codecs/vgm.c
@@ -0,0 +1,142 @@
+
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+/* Inflate code taken from WikiViewer plugin by Adam Gashlin */
+
+#include <codecs/lib/codeclib.h>
+
+#include "libgme/blargg_endian.h"
+#include "libgme/vgm_emu.h" 
+#include "libgme/inflate/mallocer.h"
+#include "libgme/inflate/inflate.h"
+
+CODEC_HEADER
+
+/* Maximum number of bytes to process in one iteration */
+#define CHUNK_SIZE (1024*4)
+#define MAINMEMBUF 0
+
+static int16_t samples[CHUNK_SIZE] IBSS_ATTR;
+static struct Vgm_Emu vgm_emu IDATA_ATTR CACHEALIGN_ATTR;
+
+static void *inflatebuf;              /* heap for gunzip */
+static char *songbuf;               /* destination for uncompressed song */
+static uint32_t songbuflen=0;  /* size of the song buffer */
+static uint32_t songlen=0;       /* used size of the song buffer */
+
+/****************** rockbox interface ******************/
+
+/* this is the codec entry point */
+enum codec_status codec_main(enum codec_entry_call_reason reason)
+{
+    if (reason == CODEC_LOAD) {
+        /* we only render 16 bits */
+        ci->configure(DSP_SET_SAMPLE_DEPTH, 16);
+
+        /* 32 Khz, Interleaved stereo */
+        ci->configure(DSP_SET_FREQUENCY, 44100);
+        ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
+
+        Vgm_init(&vgm_emu);
+        Vgm_set_sample_rate(&vgm_emu, 44100);
+    }
+
+    return CODEC_OK;
+}
+
+/* this is called for each file to process */
+enum codec_status codec_run(void)
+{
+    blargg_err_t err;
+    uint8_t *buf;
+    size_t n;
+    intptr_t param;
+
+    uint32_t elapsed_time = 0;
+
+    DEBUGF("VGM: next_track\n");
+    if (codec_init()) {
+        return CODEC_ERROR;
+    }
+
+    codec_set_replaygain(ci->id3);
+
+    /* Read the entire file */
+    DEBUGF("VGM: request file\n");
+    ci->seek_buffer(0);
+    buf = ci->request_buffer(&n, ci->filesize);
+    if (!buf) {
+        DEBUGF("VGM: file load failed\n");
+        return CODEC_ERROR;
+    }
+    
+    /* If couldn't get the whole buffer
+        will trim file and put and 'end_command'
+        at the end*/
+    if (n < (size_t)ci->filesize) {
+        DEBUGF("VGM: file was trimmed\n");
+    }
+    
+    /* If is gzipped decompress it */
+    if ( get_le16( buf ) == 0x8b1f ) {
+        wpw_init_mempool(MAINMEMBUF);
+        inflatebuf=wpw_malloc(MAINMEMBUF,0x13500);
+            
+        /* Will use available remaining memory 
+            as output buffer */
+        songbuflen=wpw_available(MAINMEMBUF);
+        songbuf=wpw_malloc(MAINMEMBUF,songbuflen);
+
+        songlen=decompress(buf,n,songbuf,songbuflen,0,inflatebuf);  
+
+        if ((err = Vgm_load_mem(&vgm_emu, songbuf, songlen, true))) {
+            DEBUGF("VGM: Vgm_load_mem failed (%s)\n", err);
+            return CODEC_ERROR;
+        }
+
+        /* Since metadata parser doesn't support VGZ 
+             will set song length here */
+        ci->id3->length = Track_get_length( &vgm_emu );
+    }
+    else if ((err = Vgm_load_mem(&vgm_emu, buf, n, false))) {
+        DEBUGF("VGM: Vgm_load failed_mem (%s)\n", err);
+        return CODEC_ERROR;
+    }
+
+    Vgm_start_track(&vgm_emu); 
+
+    /* for REPEAT_ONE we disable track limits */
+    if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+        Track_set_fade(&vgm_emu, ci->id3->length - 4000, 4000);
+    }
+    
+    ci->set_elapsed(0);
+
+    /* The main decoder loop */
+    while (1) {
+        enum codec_command_action action = ci->get_command(&param);
+
+        if (action == CODEC_ACTION_HALT)
+            break;
+
+        if (action == CODEC_ACTION_SEEK_TIME) {
+            ci->set_elapsed(param);
+            elapsed_time = param;
+            Track_seek(&vgm_emu, param);
+            ci->seek_complete();
+            
+            /* Set fade again in case we seek to start of song */
+            Track_set_fade(&vgm_emu, ci->id3->length - 4000, 4000);
+        }
+
+        /* Generate audio buffer */
+        err = Vgm_play(&vgm_emu, CHUNK_SIZE, samples);
+        if (err || vgm_emu.track_ended) break;
+
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+
+        elapsed_time += (CHUNK_SIZE / 2) / 44.1;
+        ci->set_elapsed(elapsed_time);
+    }
+
+    return CODEC_OK;
+}
diff --git a/apps/filetypes.c b/apps/filetypes.c
index 779337e..17a16db 100644
--- a/apps/filetypes.c
+++ b/apps/filetypes.c
@@ -112,6 +112,13 @@
     { "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 },
+    { "ay", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+    { "gbs", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+    { "hes", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+    { "sgc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+    { "vgm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+    { "vgz", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+    { "kss", 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 },
diff --git a/apps/metadata.c b/apps/metadata.c
index cbb5b42..8b1101e 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -216,6 +216,24 @@
     /* Advanced Audio Coding High Efficiency in M4A container */
     [AFMT_MP4_AAC_HE] =
         AFMT_ENTRY("AAC-HE","aac",  NULL,       get_mp4_metadata,   "mp4\0"),
+    /* AY (ZX Spectrum, Amstrad CPC Sound Format) */
+    [AFMT_AY] = 
+        AFMT_ENTRY("AY",    "ay",  NULL, get_ay_metadata,           "ay\0"),
+    /* GBS (Game Boy Sound Format) */
+    [AFMT_GBS] =
+        AFMT_ENTRY("GBS",   "gbs",  NULL,       get_gbs_metadata,   "gbs\0"),
+    /* HES (Hudson Entertainment System Sound Format) */
+    [AFMT_HES] =
+        AFMT_ENTRY("HES",   "hes",  NULL,       get_hes_metadata,   "hes\0"),
+    /* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */
+    [AFMT_SGC] =
+        AFMT_ENTRY("SGC", "sgc", NULL, get_sgc_metadata,     "sgc\0"),
+    /* VGM (Video Game Music Format) */
+    [AFMT_VGM] =
+        AFMT_ENTRY("VGM", "vgm", NULL, get_vgm_metadata,   "vgm\0vgz\0"),
+    /* KSS (MSX computer KSS Music File) */
+    [AFMT_KSS] =
+        AFMT_ENTRY("KSS", "kss", NULL, get_kss_metadata,   "kss\0"),
 #endif
 };
 
@@ -299,6 +317,12 @@
     case AFMT_SID:
     case AFMT_MOD:
     case AFMT_SAP:
+    case AFMT_AY:
+    case AFMT_GBS:
+    case AFMT_HES:
+    case AFMT_SGC:
+    case AFMT_VGM:
+    case AFMT_KSS:
         /* Type must be allocated and loaded in its entirety onto
            the buffer */
         return TYPE_ATOMIC_AUDIO;
diff --git a/apps/metadata.h b/apps/metadata.h
index d016359..3c5efb7 100644
--- a/apps/metadata.h
+++ b/apps/metadata.h
@@ -87,6 +87,12 @@
     AFMT_WMAVOICE,     /* WMA Voice in ASF */
     AFMT_MPC_SV8,      /* Musepack SV8 */
     AFMT_MP4_AAC_HE,   /* Advanced Audio Coding (AAC-HE) in M4A container */
+    AFMT_AY,             /* AY (ZX Spectrum, Amstrad CPC Sound Format) */
+    AFMT_GBS,          /* GBS (Game Boy Sound Format) */
+    AFMT_HES,          /* HES (Hudson Entertainment System Sound Format) */
+    AFMT_SGC,          /* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */
+    AFMT_VGM,         /* VGM (Video Game Music Format) */
+    AFMT_KSS,          /* KSS (MSX computer KSS Music File) */
 #endif
 
     /* add new formats at any index above this line to have a sensible order -
diff --git a/apps/metadata/ay.c b/apps/metadata/ay.c
new file mode 100644
index 0000000..8b737a7
--- /dev/null
+++ b/apps/metadata/ay.c
@@ -0,0 +1,148 @@
+#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"
+
+/* Taken from blargg's Game_Music_Emu library */
+
+typedef unsigned char byte;
+
+/* AY file header */
+enum { header_size = 0x14 };
+struct header_t
+{
+    byte tag[8];
+    byte vers;
+    byte player;
+    byte unused[2];
+    byte author[2];
+    byte comment[2];
+    byte max_track;
+    byte first_track;
+    byte track_info[2];
+};
+
+struct file_t {
+	struct header_t const* header;
+	byte const* tracks;
+	byte const* end;    /* end of file data */
+};
+
+static int get_be16( const void *a )
+{
+    return get_short_be( (void*) a );
+}
+
+/* Given pointer to 2-byte offset of data, returns pointer to data, or NULL if
+ * offset is 0 or there is less than min_size bytes of data available. */
+static byte const* get_data( struct file_t const* file, byte const ptr [], int min_size )
+{
+    int offset = (int16_t) get_be16( ptr );
+    int pos  = ptr - (byte const*) file->header;
+    int size = file->end - (byte const*) file->header;
+    int limit = size - min_size;
+    if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit )
+        return NULL;
+    return ptr + offset;
+}
+
+static const char *parse_header( byte const in [], int size, struct file_t* out )
+{
+    if ( size < header_size )
+        return "wrong file type";
+
+    out->header = (struct header_t const*) in;
+    out->end    = in + size;
+    struct header_t const* h = (struct header_t const*) in;
+    if ( memcmp( h->tag, "ZXAYEMUL", 8 ) )
+        return "wrong file type";
+
+    out->tracks = get_data( out, h->track_info, (h->max_track + 1) * 4 );
+    if ( !out->tracks )
+        return "missing track data";
+
+    return 0;
+}
+
+static void copy_ay_fields( struct file_t const* file, struct mp3entry* id3, int track )
+{
+    int track_count = file->header->max_track + 1;
+
+    /* calculate track length based on number of subtracks */
+    if (track_count > 1) {
+        id3->length = file->header->max_track * 1000;
+    } else {
+        byte const* track_info = get_data( file, file->tracks + track * 4 + 2, 6 );
+        if (track_info)
+            id3->length = get_be16( track_info + 4 ) * (1000 / 50); /* frames to msec */
+        else id3->length = 120 * 1000;
+    }
+    
+    if ( id3->length <= 0 )
+        id3->length = 120 * 1000;  /* 2 minutes */ 
+
+    /* If meta info was found in the m3u skip next step */
+    if (id3->title && id3->title[0]) return;
+
+    /* If file has more than one track will
+        use file name as title */
+    char * tmp;
+    if (track_count <= 1) {
+        tmp = (char *) get_data( file, file->tracks + track * 4, 1 );
+        if ( tmp ) id3->title = tmp;
+    }
+
+    /* Author */
+    tmp = (char *) get_data( file, file->header->author, 1 );
+    if (tmp) id3->artist = tmp;
+    
+    /* Comment */
+    tmp = (char *) get_data( file, file->header->comment, 1 );
+    if (tmp) id3->comment = tmp;
+}
+
+bool parse_ay_header(int fd, struct mp3entry *id3)
+{
+    /* Use the trackname part of the id3 structure as a temporary buffer */
+    unsigned char* buf = (unsigned char *)id3->id3v2buf;
+    struct file_t file;
+    int read_bytes;
+
+    lseek(fd, 0, SEEK_SET);
+    if ((read_bytes = read(fd, buf, ID3V2_BUF_SIZE)) < header_size)
+        return false;
+
+    buf [ID3V2_BUF_SIZE] = '\0';
+    if ( parse_header( buf, read_bytes, &file ) ) 
+        return false;
+
+    copy_ay_fields( &file, id3, 0 );
+    return true;
+}
+
+bool get_ay_metadata(int fd, struct mp3entry* id3)
+{
+    char ay_type[8];
+    if ((lseek(fd, 0, SEEK_SET) < 0) ||
+         read(fd, ay_type, 8) < 8)
+        return false;
+
+    id3->vbr = false;
+    id3->filesize = filesize(fd);
+
+    id3->bitrate = 706;
+    id3->frequency = 44100;
+  
+    /* Make sure this is a ZX Ay file */
+    if (memcmp( ay_type, "ZXAYEMUL", 8 ) != 0)
+        return false;
+
+    return parse_ay_header(fd, id3);
+}
diff --git a/apps/metadata/gbs.c b/apps/metadata/gbs.c
new file mode 100644
index 0000000..796db59
--- /dev/null
+++ b/apps/metadata/gbs.c
@@ -0,0 +1,65 @@
+#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 parse_gbs_header(int fd, struct mp3entry* id3)
+{
+    /* Use the trackname part of the id3 structure as a temporary buffer */
+    unsigned char* buf = (unsigned char *)id3->path;
+    lseek(fd, 0, SEEK_SET);
+    if (read(fd, buf, 112) < 112)
+        return false;
+
+    /* Calculate track length with number of subtracks */
+    id3->length = buf[4] * 1000;
+
+    /* If meta info was found in the m3u skip next step */
+    if (id3->title && id3->title[0]) return true;
+    
+    char *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;
+
+    return true;
+}
+
+bool get_gbs_metadata(int fd, struct mp3entry* id3)
+{
+    char gbs_type[3];
+    if ((lseek(fd, 0, SEEK_SET) < 0) ||
+         (read(fd, gbs_type, 3) < 3))
+        return false;
+
+    id3->vbr = false;
+    id3->filesize = filesize(fd);
+    /* we only render 16 bits, 44.1KHz, Stereo */
+    id3->bitrate = 706;
+    id3->frequency = 44100;
+
+    /* Check for GBS magic */
+    if (memcmp( gbs_type, "GBS", 3 ) != 0)
+        return false;
+
+    return parse_gbs_header(fd, id3);
+}
diff --git a/apps/metadata/hes.c b/apps/metadata/hes.c
new file mode 100644
index 0000000..6d99d52
--- /dev/null
+++ b/apps/metadata/hes.c
@@ -0,0 +1,39 @@
+#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"
+#include "plugin.h"
+
+bool get_hes_metadata(int fd, struct mp3entry* id3)
+{
+    /* Use the id3v2 buffer part of the id3 structure as a temporary buffer */
+    unsigned char* buf = (unsigned char *)id3->id3v2buf;
+    int read_bytes;
+
+    if ((lseek(fd, 0, SEEK_SET) < 0) 
+         || ((read_bytes = read(fd, buf, 4)) < 4))
+        return false;
+
+    /* Verify this is a HES file */
+    if (memcmp(buf,"HESM",4) != 0)
+        return false;
+
+    id3->vbr = false;
+    id3->filesize = filesize(fd);
+    /* we only render 16 bits, 44.1KHz, Stereo */
+    id3->bitrate = 706;
+    id3->frequency = 44100;
+
+    /* Set default track count (length)*/
+    id3->length = 255 * 1000;
+
+    return true;
+}
+
diff --git a/apps/metadata/kss.c b/apps/metadata/kss.c
new file mode 100644
index 0000000..ecb59b8
--- /dev/null
+++ b/apps/metadata/kss.c
@@ -0,0 +1,53 @@
+#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 parse_kss_header(int fd, struct mp3entry* id3)
+{
+    /* Use the trackname part of the id3 structure as a temporary buffer */
+    unsigned char* buf = (unsigned char *)id3->path;
+
+     lseek(fd, 0, SEEK_SET);
+     if (read(fd, buf, 0x20) < 0x20)
+        return false;
+
+    /* calculate track length with number of tracks */
+    id3->length = 0;
+    if (buf[14] == 0x10) {
+        id3->length = (get_short_le((void *)(buf + 26)) + 1) * 1000;
+    }
+    
+    if (id3->length <= 0)
+        id3->length = 255 * 1000; /* 255 tracks */
+
+    return true;
+}
+
+
+bool get_kss_metadata(int fd, struct mp3entry* id3)
+{
+   uint32_t kss_type;
+    if ((lseek(fd, 0, SEEK_SET) < 0) ||
+         read_uint32be(fd, &kss_type) != (int)sizeof(kss_type))
+        return false;
+
+    id3->vbr = false;
+    id3->filesize = filesize(fd);
+    /* we only render 16 bits, 44.1KHz, Stereo */
+    id3->bitrate = 706;
+    id3->frequency = 44100;
+  
+    /* Make sure this is an SGC file */
+    if (kss_type != FOURCC('K','S','C','C') && kss_type != FOURCC('K','S','S','X'))
+        return false;
+
+    return parse_kss_header(fd, id3);
+}
diff --git a/apps/metadata/metadata_common.c b/apps/metadata/metadata_common.c
index 6c420d9..1ad89d1 100644
--- a/apps/metadata/metadata_common.c
+++ b/apps/metadata/metadata_common.c
@@ -178,6 +178,14 @@
     return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
 }
 
+/* Read an unaligned 16-bit little endian short from buffer. */
+unsigned short get_short_be(void* buf)
+{
+    unsigned char* p = (unsigned char*) buf;
+
+    return (p[0] << 8) | p[1];
+}
+
 /* Read an unaligned 32-bit little endian long from buffer. */
 long get_slong(void* buf)
 {
diff --git a/apps/metadata/metadata_common.h b/apps/metadata/metadata_common.h
index b2c76af..a48c2a4 100644
--- a/apps/metadata/metadata_common.h
+++ b/apps/metadata/metadata_common.h
@@ -62,6 +62,7 @@
 unsigned long get_long_le(void* buf);
 unsigned short get_short_le(void* buf);
 unsigned long get_long_be(void* buf);
+unsigned short get_short_be(void* buf);
 long get_slong(void* buf);
 unsigned long get_itunes_int32(char* value, int count);
 long parse_tag(const char* name, char* value, struct mp3entry* id3,
diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h
index 7797b47..adb7a82 100644
--- a/apps/metadata/metadata_parsers.h
+++ b/apps/metadata/metadata_parsers.h
@@ -47,3 +47,9 @@
 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_ay_metadata(int fd, struct mp3entry* id3);
+bool get_gbs_metadata(int fd, struct mp3entry* id3);
+bool get_hes_metadata(int fd, struct mp3entry* id3);
+bool get_sgc_metadata(int fd, struct mp3entry* id3);
+bool get_vgm_metadata(int fd, struct mp3entry* id3);
+bool get_kss_metadata(int fd, struct mp3entry* id3);
diff --git a/apps/metadata/nsf.c b/apps/metadata/nsf.c
index 29fd847..2fa6f36 100644
--- a/apps/metadata/nsf.c
+++ b/apps/metadata/nsf.c
@@ -11,6 +11,9 @@
 #include "rbunicode.h"
 #include "string-extra.h"
 
+/* NOTE: This file was modified to work properly with the new nsf codec based
+    on Game_Music_Emu */
+
 struct NESM_HEADER
 {
     uint32_t        nHeader;
@@ -66,7 +69,7 @@
 
      /* default values */
     info.nTrackCount = 1;
-    id3->length = 2*1000*60;
+    id3->length = 150 * 1000;
     
     /* begin reading chunks */
     while (!(chunks_found & CHUNK_NEND))
@@ -210,6 +213,10 @@
     if (track_count | playlist_count)
         id3->length = MAX(track_count, playlist_count)*1000;
 
+    /* Single subtrack files will be treated differently
+        by gme's nsf codec */
+    if (id3->length <= 1000) id3->length = 150 * 1000;
+
     /*
      * if we exited the while loop without a 'return', we must have hit an NEND
      *  chunk if this is the case, the file was layed out as it was expected.
@@ -230,7 +237,7 @@
         return false;
 
     /* Length */
-    id3->length = hdr.nTrackCount*1000;
+    id3->length = (hdr.nTrackCount > 1 ? hdr.nTrackCount : 150) * 1000;
 
     /* Title */
     id3->title = p;
@@ -250,7 +257,6 @@
 bool get_nsf_metadata(int fd, struct mp3entry* id3)
 {
     uint32_t nsf_type;
-      
     if (lseek(fd, 0, SEEK_SET) < 0 ||
         read_uint32be(fd, &nsf_type) != (int)sizeof(nsf_type))
         return false;
diff --git a/apps/metadata/sgc.c b/apps/metadata/sgc.c
new file mode 100644
index 0000000..9e16de2
--- /dev/null
+++ b/apps/metadata/sgc.c
@@ -0,0 +1,67 @@
+#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 parse_sgc_header(int fd, struct mp3entry* id3)
+{
+    /* Use the trackname part of the id3 structure as a temporary buffer */
+    unsigned char* buf = (unsigned char *)id3->path;
+
+     lseek(fd, 0, SEEK_SET);
+     if (read(fd, buf, 0xA0) < 0xA0)
+        return false;
+
+    /* calculate track length with number of tracks */
+    id3->length = buf[37] * 1000;
+
+    /* If meta info was found in the m3u skip next step */
+    if (id3->title && id3->title[0]) return true;
+
+    char *p = id3->id3v2buf;
+
+    /* Some metadata entries have 32 bytes length */
+    /* Game */
+    memcpy(p, &buf[64], 32); *(p + 33) = '\0';
+    id3->title = p;
+    p += strlen(p)+1;
+
+    /* Artist */
+    memcpy(p, &buf[96], 32); *(p + 33) = '\0';
+    id3->artist = p;
+    p += strlen(p)+1;
+
+    /* Copyright */
+    memcpy(p, &buf[128], 32); *(p + 33) = '\0';
+    id3->album = p;
+    p += strlen(p)+1;
+    return true;
+}
+
+
+bool get_sgc_metadata(int fd, struct mp3entry* id3)
+{
+   uint32_t sgc_type;
+    if ((lseek(fd, 0, SEEK_SET) < 0) ||
+         read_uint32be(fd, &sgc_type) != (int)sizeof(sgc_type))
+        return false;
+
+    id3->vbr = false;
+    id3->filesize = filesize(fd);
+    /* we only render 16 bits, 44.1KHz, Stereo */
+    id3->bitrate = 706;
+    id3->frequency = 44100;
+  
+    /* Make sure this is an SGC file */
+    if (sgc_type != FOURCC('S','G','C',0x1A))
+        return false;
+
+    return parse_sgc_header(fd, id3);
+}
diff --git a/apps/metadata/vgm.c b/apps/metadata/vgm.c
new file mode 100644
index 0000000..9ea95b3
--- /dev/null
+++ b/apps/metadata/vgm.c
@@ -0,0 +1,195 @@
+#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"
+
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+
+typedef unsigned char byte;
+
+enum { header_size = 0x40 };
+enum { max_field = 64 };
+
+struct header_t
+{
+    char tag [4]; 
+    byte data_size [4];
+    byte version [4];
+    byte psg_rate [4];
+    byte ym2413_rate [4];
+    byte gd3_offset [4];
+    byte track_duration [4];
+    byte loop_offset [4];
+    byte loop_duration [4];
+    byte frame_rate [4];
+    byte noise_feedback [2];
+    byte noise_width;
+    byte unused1;
+    byte ym2612_rate [4];
+    byte ym2151_rate [4];
+    byte data_offset [4];
+    byte unused2 [8];
+};
+
+static byte const* skip_gd3_str( byte const* in, byte const* end )
+{
+    while ( end - in >= 2 )
+    {
+        in += 2;
+        if ( !(in [-2] | in [-1]) )
+            break;
+    }
+    return in;
+}
+
+static byte const* get_gd3_str( byte const* in, byte const* end, char* field )
+{
+    byte const* mid = skip_gd3_str( in, end );
+    int len = (mid - in) / 2 - 1;
+    if ( field && len > 0 )
+    {
+        len = len < (int) max_field ? len : (int) max_field;
+
+        field [len] = 0;
+        /* Conver to utf8 */
+        utf16LEdecode( in, field, len );
+        
+        /* Copy string back to id3v2buf */
+        strcpy( (char*) in, field );
+    }
+    return mid;
+}
+
+static byte const* get_gd3_pair( byte const* in, byte const* end, char* field )
+{
+    return skip_gd3_str( get_gd3_str( in, end, field ), end );
+}
+
+static void parse_gd3( byte const* in, byte const* end, struct mp3entry* id3 )
+{
+    char* p = id3->path;
+    id3->title = (char *) in;
+    in = get_gd3_pair( in, end, p ); /* Song */
+
+    id3->album = (char *) in;
+    in = get_gd3_pair( in, end, p ); /* Game */
+
+    in = get_gd3_pair( in, end, NULL ); /* System */
+
+    id3->artist = (char *) in;
+    in = get_gd3_pair( in, end, p ); /* Author */
+
+#if MEMORYSIZE > 2
+    in = get_gd3_str ( in, end, NULL  ); /* Copyright */
+    in = get_gd3_pair( in, end, NULL ); /* Dumper */
+
+    id3->comment = (char *) in;
+    in = get_gd3_str ( in, end, p ); /* Comment */
+#endif
+}
+
+int const gd3_header_size = 12;
+
+static long check_gd3_header( byte* h, long remain )
+{
+    if ( remain < gd3_header_size ) return 0;
+    if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
+    if ( get_long_le( h + 4 ) >= 0x200 ) return 0;
+
+    long gd3_size = get_long_le( h + 8 );
+    if ( gd3_size > remain - gd3_header_size )
+        gd3_size = remain - gd3_header_size;
+
+    return gd3_size;
+}
+
+static void get_vgm_length( struct header_t* h, struct mp3entry* id3 )
+{
+    long length = get_long_le( h->track_duration ) * 10 / 441;
+    if ( length > 0 )
+    {
+        long loop_length = 0, intro_length = 0;
+        long loop = get_long_le( h->loop_duration );
+        if ( loop > 0 && get_long_le( h->loop_offset ) )
+        {
+            loop_length = loop * 10 / 441;
+            intro_length = length - loop_length;
+        }
+        else
+        {
+            intro_length = length; /* make it clear that track is no longer than length */
+            loop_length = 0;
+        }
+        
+         id3->length = intro_length + 2 * loop_length; /* intro + 2 loops */
+         return;
+    }
+
+    id3->length = 150 * 1000; /* 2.5 minutes */
+}
+
+bool get_vgm_metadata(int fd, struct mp3entry* id3)
+{
+    /* Use the id3v2 part of the id3 structure as a temporary buffer */
+    unsigned char* buf = (unsigned char *)id3->id3v2buf;
+    int read_bytes;
+
+    memset(buf, 0, ID3V2_BUF_SIZE);
+    if ((lseek(fd, 0, SEEK_SET) < 0) 
+         || ((read_bytes = read(fd, buf, header_size)) < header_size))
+    {
+        return false;
+    }
+
+    id3->vbr = false;
+    id3->filesize = filesize(fd);
+
+    id3->bitrate = 706;
+    id3->frequency = 44100;
+
+    /* If file is gzipped, will get metadata later */
+    if (memcmp(buf, "Vgm ", 4))
+    {
+        /* We must set a default song length here because
+            the codec can't do it anymore */
+        id3->length = 150 * 1000; /* 2.5 minutes */
+        return true;
+    }
+
+    /* Get song length from header */
+    struct header_t* header = (struct header_t*) buf;
+    get_vgm_length( header, id3 );
+
+    long gd3_offset = get_long_le( header->gd3_offset ) - 0x2C;
+    
+    /* No gd3 tag found */
+    if ( gd3_offset < 0 )
+        return true;
+
+    /*  Seek to gd3 offset and read as 
+         many bytes posible */
+    gd3_offset = id3->filesize - (header_size + gd3_offset);
+    if ((lseek(fd, -gd3_offset, SEEK_END) < 0) 
+         || ((read_bytes = read(fd, buf, ID3V2_BUF_SIZE)) <= 0))
+        return true;
+
+    byte* gd3 = buf;
+    long gd3_size = check_gd3_header( gd3, read_bytes );
+
+    /* GD3 tag is zero */
+    if ( gd3_size == 0 )
+        return true;
+
+    /* Finally, parse gd3 tag */
+    if ( gd3 )
+        parse_gd3( gd3 + gd3_header_size, gd3 + read_bytes, id3 );
+
+    return true;
+}
diff --git a/docs/CREDITS b/docs/CREDITS
index 0156961..d382527 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -599,6 +599,7 @@
 Seheon Ryu
 Asier Arsuaga
 Vencislav Atanasov
+Mauricio Garrido
 
 The libmad team
 The wavpack team
diff --git a/manual/appendix/file_formats.tex b/manual/appendix/file_formats.tex
index 4e5d96a..66fe397 100644
--- a/manual/appendix/file_formats.tex
+++ b/manual/appendix/file_formats.tex
@@ -209,6 +209,29 @@
     Synthetic music Mobile Application Format
         & \fname{.mmf} 
         & PCM/ADPCM only \\
+    Game Boy Sound Format
+        & \fname{.gbs}
+        & Progress bar and seek use subtracks instead of seconds.\\
+    AY Sound Chip Music
+        & \fname{.ay}
+        & Progress bar and seek use subtracks instead of seconds for
+          multitrack files.\\
+    Hudson Entertainment System Sound Format
+        & \fname{.hes}
+        & Progress bar and seek use subtracks instead of seconds.\\
+    MSX Konami Sound System
+        & \fname{.kss}
+        & Progress bar and seek use subtracks instead of seconds.\\
+    SMS/GG/CV Sound Format
+        & \fname{.sgc}
+        & Supports Sega Master System and Game Gear Sound Format. 
+          Progress bar and seek use subtracks instead of seconds.\\
+    Video Game Music Format
+        & \fname{.vgm}
+        & \\
+    Gzipped Video Game Music Format
+        & \fname{.vgz}
+        & \\
     MOD
         & \fname{.mod} 
         & \\
@@ -227,7 +250,7 @@
   \end{rbtabular}
   
   \subsection{Codec featureset}
-  \begin{rbtabular}{.90\textwidth}{lXXX}%
+  \begin{rbtabular}{.95\textwidth}{lXXX}%
   {\textbf{Format} & \textbf{Seek} & \textbf{Resume} & \textbf{Gapless}}{}{}
     ATSC A/52 (AC3)                             & x & x &   \\
     ADX                                         & x &   &   \\
@@ -252,6 +275,13 @@
     Wavpack                                     & x & x & x \\
     Atari Sound Format                          & x &   &   \\
     Synthetic music Mobile Application Format   & x & x &   \\
+    Game Boy Sound Format                       & x &   &   \\
+    AY Sound Chip Music                         & x &   &   \\
+    Hudson Entertainment System Sound Format    & x &   &   \\
+    MSX Konami Sound System                     & x &   &   \\
+    SMS/GG/CV Sound Format                      & x &   &   \\
+    Video Game Music Format                     & x & x &   \\
+    Gzipped Video Game Music Format             & x & x &   \\
     MOD                                         & x &   &   \\
     NES Sound Format                            & x &   &   \\
     Atari SAP                                   & x &   &   \\
@@ -259,8 +289,10 @@
     SPC700                                      & x &   &   \\
   \end{rbtabular}
   
-  \note{The seek implementations of NES Sound Format and Sound Interface Device 
-  use subtracks instead of seconds, whereas each subtrack equals a second.}
+  \note{The seek implementations of NES Sound Format, Sound Interface Device,
+  Game Boy Sound Format, AY Sound Chip Music, Hudson Entertainment System Sound,
+  Format, MSX Konami Sound System and SMS/GG/CV Sound Format use subtracks
+  instead of seconds, whereas each subtrack equals a second.}
   
   \section{\label{ref:SupportedMetadata}Supported metadata tags}
     Rockbox supports different metadata formats. In general those tag formats
@@ -281,13 +313,14 @@
       MP4               & \fname{.m4a}, \fname{.m4b}, \fname{.mp4} \\
       ASF               & \fname{.wma}, \fname{.wmv}, \fname{.asf} \\
       Codec specific    & \fname{.mmf}, \fname{.mod}, \fname{.nsf}, \fname{.nsfe},
-                          \fname{.sap}, \fname{.sid}, \fname{.spc} \\
+                          \fname{.sap}, \fname{.sid}, \fname{.spc}, \fname{.gbs},
+                          \fname{.ay}, \fname{.kss}, \fname{.sgc}, \fname{.vgm} \\
       None              & \fname{.a52}, \fname{.ac3}, \fname{.adx}, \fname{.oma},
                           \fname{.aa3}, \fname{.aif}, \fname{.aiff}, \fname{.au},
                           \fname{.snd}, \fname{.shn}, \fname{.vox}, \fname{.w64},
                           \fname{.wav}, \fname{.cmc}, \fname{.cm3}, \fname{.cmr},
                           \fname{.cms}, \fname{.dmc}, \fname{.dlt}, \fname{.mpt},
-                          \fname{.mpd} \\
+                          \fname{.mpd}, \fname{.hes}, \fname{.vgz} \\
     \end{rbtabular}
     
     \subsection{Featureset for generic metadata tags}
@@ -322,17 +355,21 @@
      Replaygain             & \fname{.mpc}\\
      Title                  & \fname{.tta}, \fname{.spc}, \fname{.mmf}, \fname{.sid}, 
                               \fname{.rm}, \fname{.ra}, \fname{.rmvb}, \fname{.nsf}, 
-                              \fname{.nsfe}, \fname{.mod}, \fname{.sap} \\
+                              \fname{.nsfe}, \fname{.mod}, \fname{.sap}, \fname{.gbs},
+                              \fname{.ay}, \fname{.sgc}, \fname{.vgm} \\
      Artist                 & \fname{.tta}, \fname{.spc}, \fname{.mmf}, \fname{.sid}, 
                               \fname{.rm}, \fname{.ra}, \fname{.rmvb}, \fname{.nsf}, 
-                              \fname{.nsfe}, \fname{.sap} \\
-     Album                  & \fname{.spc}, \fname{.sid}, \fname{.nsf}, \fname{.nsfe} \\
+                              \fname{.nsfe}, \fname{.sap}, \fname{.gbs}, \fname{.ay},
+                              \fname{.sgc}, \fname{.vgm} \\
+     Album                  & \fname{.spc}, \fname{.sid}, \fname{.nsf}, \fname{.nsfe},
+                              \fname{.gbs}, \fname{.ay}, \fname{.sgc}, \fname{.vgm} \\
      Genre                  & \fname{.tta}, \fname{.spc}, \fname{.sap} \\
      Disc                   & \fname{.tta} \\
      Track                  & \fname{.tta} \\
      Year                   & \fname{.spc}, \fname{.sid}, \fname{.sap} \\
      Composer               & \fname{.mmf} \\
-     Comment                & \fname{.spc}, \fname{.rm}, \fname{.ra}, \fname{.rmvb} \\
+     Comment                & \fname{.spc}, \fname{.rm}, \fname{.ra}, \fname{.rmvb},
+                              \fname{.vgm} \\
      Albumartist            & None \\
      Grouping               & None \\
     \end{rbtabular}