1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Andree Buschmann 2011-08-07 20:01:04 +00:00
parent 93c6f1329a
commit acb0917556
149 changed files with 33133 additions and 4431 deletions

View file

@ -227,6 +227,12 @@ metadata/smaf.c
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

View file

@ -33,6 +33,12 @@ vox.c
wav64.c
tta.c
wmapro.c
ay.c
gbs.c
hes.c
sgc.c
vgm.c
kss.c
#ifdef HAVE_RECORDING

137
apps/codecs/ay.c Normal file
View file

@ -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;
}

View file

@ -43,6 +43,13 @@ include $(APPSDIR)/codecs/librm/librm.make
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)/au.codec : $(CODECDIR)/libpcm.a
$(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

108
apps/codecs/gbs.c Normal file
View file

@ -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;
}

108
apps/codecs/hes.c Normal file
View file

@ -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;
}

113
apps/codecs/kss.c Normal file
View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,6 @@
ay_apu.c
ay_cpu.c
ay_emu.c
blip_buffer.c
multi_buffer.c
z80_cpu.c

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

413
apps/codecs/libgme/ay_apu.c Normal file
View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

783
apps/codecs/libgme/ay_emu.c Normal file
View file

@ -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;
}

172
apps/codecs/libgme/ay_emu.h Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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

1958
apps/codecs/libgme/emu2413.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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

1198
apps/codecs/libgme/emu8950.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

410
apps/codecs/libgme/gb_apu.c Normal file
View file

@ -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;
}

View file

@ -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

View file

@ -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 );
}

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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

View file

@ -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"
}

View file

@ -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;
}

View file

@ -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

18
apps/codecs/libgme/gme.h Normal file
View file

@ -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

View file

@ -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 */

View file

@ -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 );
}

View file

@ -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

View file

@ -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 );
}

View file

@ -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

1321
apps/codecs/libgme/hes_cpu.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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

View file

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

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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 );
}

View file

@ -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

View file

@ -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 $@

View file

@ -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 $@

View file

@ -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 $@

View file

@ -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 $@

View file

@ -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 $@

View file

@ -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 $@

View file

@ -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 $@

View file

@ -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

View file

@ -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

View file

@ -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] );
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

Some files were not shown because too many files have changed in this diff Show more