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:
parent
93c6f1329a
commit
acb0917556
149 changed files with 33133 additions and 4431 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
137
apps/codecs/ay.c
Normal 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(¶m);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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
108
apps/codecs/gbs.c
Normal 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(¶m);
|
||||
|
||||
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
108
apps/codecs/hes.c
Normal 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(¶m);
|
||||
|
||||
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
113
apps/codecs/kss.c
Normal 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(¶m);
|
||||
|
||||
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;
|
||||
}
|
||||
20
apps/codecs/libgme/2413tone.h
Normal file
20
apps/codecs/libgme/2413tone.h
Normal 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
|
||||
20
apps/codecs/libgme/281btone.h
Normal file
20
apps/codecs/libgme/281btone.h
Normal 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
|
||||
6
apps/codecs/libgme/AYSOURCES
Normal file
6
apps/codecs/libgme/AYSOURCES
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
ay_apu.c
|
||||
ay_cpu.c
|
||||
ay_emu.c
|
||||
blip_buffer.c
|
||||
multi_buffer.c
|
||||
z80_cpu.c
|
||||
8
apps/codecs/libgme/GBSSOURCES
Normal file
8
apps/codecs/libgme/GBSSOURCES
Normal 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
|
||||
7
apps/codecs/libgme/HESSOURCES
Normal file
7
apps/codecs/libgme/HESSOURCES
Normal 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
|
||||
14
apps/codecs/libgme/KSSSOURCES
Normal file
14
apps/codecs/libgme/KSSSOURCES
Normal 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
|
||||
15
apps/codecs/libgme/NSFSOURCES
Normal file
15
apps/codecs/libgme/NSFSOURCES
Normal 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
|
||||
10
apps/codecs/libgme/SGCSOURCES
Normal file
10
apps/codecs/libgme/SGCSOURCES
Normal 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
|
||||
12
apps/codecs/libgme/VGMSOURCES
Normal file
12
apps/codecs/libgme/VGMSOURCES
Normal 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
413
apps/codecs/libgme/ay_apu.c
Normal 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;
|
||||
}
|
||||
79
apps/codecs/libgme/ay_apu.h
Normal file
79
apps/codecs/libgme/ay_apu.h
Normal 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
|
||||
59
apps/codecs/libgme/ay_cpu.c
Normal file
59
apps/codecs/libgme/ay_cpu.c
Normal 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
783
apps/codecs/libgme/ay_emu.c
Normal 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
172
apps/codecs/libgme/ay_emu.h
Normal 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
|
||||
159
apps/codecs/libgme/blargg_common.h
Normal file
159
apps/codecs/libgme/blargg_common.h
Normal 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
|
||||
42
apps/codecs/libgme/blargg_config.h
Normal file
42
apps/codecs/libgme/blargg_config.h
Normal 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
|
||||
147
apps/codecs/libgme/blargg_endian.h
Normal file
147
apps/codecs/libgme/blargg_endian.h
Normal 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
|
||||
71
apps/codecs/libgme/blargg_source.h
Normal file
71
apps/codecs/libgme/blargg_source.h
Normal 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
|
||||
285
apps/codecs/libgme/blip_buffer.c
Normal file
285
apps/codecs/libgme/blip_buffer.c
Normal 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);
|
||||
}
|
||||
279
apps/codecs/libgme/blip_buffer.h
Normal file
279
apps/codecs/libgme/blip_buffer.h
Normal 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
1958
apps/codecs/libgme/emu2413.c
Normal file
File diff suppressed because it is too large
Load diff
164
apps/codecs/libgme/emu2413.h
Normal file
164
apps/codecs/libgme/emu2413.h
Normal 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
1198
apps/codecs/libgme/emu8950.c
Normal file
File diff suppressed because it is too large
Load diff
248
apps/codecs/libgme/emu8950.h
Normal file
248
apps/codecs/libgme/emu8950.h
Normal 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
|
||||
298
apps/codecs/libgme/emuadpcm.c
Normal file
298
apps/codecs/libgme/emuadpcm.c
Normal 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;
|
||||
}
|
||||
52
apps/codecs/libgme/emuadpcm.h
Normal file
52
apps/codecs/libgme/emuadpcm.h
Normal 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
|
||||
170
apps/codecs/libgme/emutables.h
Normal file
170
apps/codecs/libgme/emutables.h
Normal 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
|
||||
41
apps/codecs/libgme/emutypes.h
Normal file
41
apps/codecs/libgme/emutypes.h
Normal 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
410
apps/codecs/libgme/gb_apu.c
Normal 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;
|
||||
}
|
||||
85
apps/codecs/libgme/gb_apu.h
Normal file
85
apps/codecs/libgme/gb_apu.h
Normal 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
|
||||
53
apps/codecs/libgme/gb_cpu.c
Normal file
53
apps/codecs/libgme/gb_cpu.c
Normal 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 );
|
||||
}
|
||||
80
apps/codecs/libgme/gb_cpu.h
Normal file
80
apps/codecs/libgme/gb_cpu.h
Normal 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
|
||||
1187
apps/codecs/libgme/gb_cpu_run.h
Normal file
1187
apps/codecs/libgme/gb_cpu_run.h
Normal file
File diff suppressed because it is too large
Load diff
787
apps/codecs/libgme/gb_oscs.c
Normal file
787
apps/codecs/libgme/gb_oscs.c
Normal 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;
|
||||
}
|
||||
198
apps/codecs/libgme/gb_oscs.h
Normal file
198
apps/codecs/libgme/gb_oscs.h
Normal 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
|
||||
120
apps/codecs/libgme/gbs_cpu.c
Normal file
120
apps/codecs/libgme/gbs_cpu.c
Normal 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"
|
||||
}
|
||||
631
apps/codecs/libgme/gbs_emu.c
Normal file
631
apps/codecs/libgme/gbs_emu.c
Normal 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;
|
||||
}
|
||||
204
apps/codecs/libgme/gbs_emu.h
Normal file
204
apps/codecs/libgme/gbs_emu.h
Normal 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
18
apps/codecs/libgme/gme.h
Normal 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
|
||||
21
apps/codecs/libgme/gme_types.h
Normal file
21
apps/codecs/libgme/gme_types.h
Normal 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 */
|
||||
315
apps/codecs/libgme/hes_apu.c
Normal file
315
apps/codecs/libgme/hes_apu.c
Normal 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 );
|
||||
}
|
||||
55
apps/codecs/libgme/hes_apu.h
Normal file
55
apps/codecs/libgme/hes_apu.h
Normal 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
|
||||
297
apps/codecs/libgme/hes_apu_adpcm.c
Normal file
297
apps/codecs/libgme/hes_apu_adpcm.c
Normal 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 );
|
||||
}
|
||||
89
apps/codecs/libgme/hes_apu_adpcm.h
Normal file
89
apps/codecs/libgme/hes_apu_adpcm.h
Normal 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
1321
apps/codecs/libgme/hes_cpu.c
Normal file
File diff suppressed because it is too large
Load diff
95
apps/codecs/libgme/hes_cpu.h
Normal file
95
apps/codecs/libgme/hes_cpu.h
Normal 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
|
||||
72
apps/codecs/libgme/hes_cpu_io.h
Normal file
72
apps/codecs/libgme/hes_cpu_io.h
Normal 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 )
|
||||
877
apps/codecs/libgme/hes_emu.c
Normal file
877
apps/codecs/libgme/hes_emu.c
Normal 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;
|
||||
}
|
||||
229
apps/codecs/libgme/hes_emu.h
Normal file
229
apps/codecs/libgme/hes_emu.h
Normal 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
|
||||
147
apps/codecs/libgme/inflate/bbfuncs.c
Normal file
147
apps/codecs/libgme/inflate/bbfuncs.c
Normal 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);
|
||||
}
|
||||
}
|
||||
33
apps/codecs/libgme/inflate/bbfuncs.h
Normal file
33
apps/codecs/libgme/inflate/bbfuncs.h
Normal 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
|
||||
1159
apps/codecs/libgme/inflate/inflate.c
Normal file
1159
apps/codecs/libgme/inflate/inflate.c
Normal file
File diff suppressed because it is too large
Load diff
30
apps/codecs/libgme/inflate/inflate.h
Normal file
30
apps/codecs/libgme/inflate/inflate.h
Normal 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
|
||||
86
apps/codecs/libgme/inflate/mallocer.c
Normal file
86
apps/codecs/libgme/inflate/mallocer.c
Normal 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);
|
||||
}
|
||||
16
apps/codecs/libgme/inflate/mallocer.h
Normal file
16
apps/codecs/libgme/inflate/mallocer.h
Normal 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);
|
||||
16
apps/codecs/libgme/inflate/mbreader.c
Normal file
16
apps/codecs/libgme/inflate/mbreader.c
Normal 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;
|
||||
}
|
||||
15
apps/codecs/libgme/inflate/mbreader.h
Normal file
15
apps/codecs/libgme/inflate/mbreader.h
Normal 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
|
||||
35
apps/codecs/libgme/kss_cpu.c
Normal file
35
apps/codecs/libgme/kss_cpu.c
Normal 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;
|
||||
}
|
||||
883
apps/codecs/libgme/kss_emu.c
Normal file
883
apps/codecs/libgme/kss_emu.c
Normal 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;
|
||||
}
|
||||
|
||||
228
apps/codecs/libgme/kss_emu.h
Normal file
228
apps/codecs/libgme/kss_emu.h
Normal 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
|
||||
166
apps/codecs/libgme/kss_scc_apu.c
Normal file
166
apps/codecs/libgme/kss_scc_apu.c
Normal 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 );
|
||||
}
|
||||
51
apps/codecs/libgme/kss_scc_apu.h
Normal file
51
apps/codecs/libgme/kss_scc_apu.h
Normal 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
|
||||
21
apps/codecs/libgme/libay.make
Normal file
21
apps/codecs/libgme/libay.make
Normal 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 $@
|
||||
21
apps/codecs/libgme/libgbs.make
Normal file
21
apps/codecs/libgme/libgbs.make
Normal 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 $@
|
||||
21
apps/codecs/libgme/libhes.make
Normal file
21
apps/codecs/libgme/libhes.make
Normal 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 $@
|
||||
21
apps/codecs/libgme/libkss.make
Normal file
21
apps/codecs/libgme/libkss.make
Normal 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 $@
|
||||
21
apps/codecs/libgme/libnsf.make
Normal file
21
apps/codecs/libgme/libnsf.make
Normal 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 $@
|
||||
21
apps/codecs/libgme/libsgc.make
Normal file
21
apps/codecs/libgme/libsgc.make
Normal 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 $@
|
||||
21
apps/codecs/libgme/libvgm.make
Normal file
21
apps/codecs/libgme/libvgm.make
Normal 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 $@
|
||||
31
apps/codecs/libgme/m3u_playlist.h
Normal file
31
apps/codecs/libgme/m3u_playlist.h
Normal 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
|
||||
36
apps/codecs/libgme/msxtypes.h
Normal file
36
apps/codecs/libgme/msxtypes.h
Normal 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
|
||||
226
apps/codecs/libgme/multi_buffer.c
Normal file
226
apps/codecs/libgme/multi_buffer.c
Normal 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] );
|
||||
}
|
||||
72
apps/codecs/libgme/multi_buffer.h
Normal file
72
apps/codecs/libgme/multi_buffer.h
Normal 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
|
||||
393
apps/codecs/libgme/nes_apu.c
Normal file
393
apps/codecs/libgme/nes_apu.c
Normal 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;
|
||||
}
|
||||
134
apps/codecs/libgme/nes_apu.h
Normal file
134
apps/codecs/libgme/nes_apu.h
Normal 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
|
||||
62
apps/codecs/libgme/nes_cpu.c
Normal file
62
apps/codecs/libgme/nes_cpu.c
Normal 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();
|
||||
}
|
||||
106
apps/codecs/libgme/nes_cpu.h
Normal file
106
apps/codecs/libgme/nes_cpu.h
Normal 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
|
||||
94
apps/codecs/libgme/nes_cpu_io.h
Normal file
94
apps/codecs/libgme/nes_cpu_io.h
Normal 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 )
|
||||
1122
apps/codecs/libgme/nes_cpu_run.h
Normal file
1122
apps/codecs/libgme/nes_cpu_run.h
Normal file
File diff suppressed because it is too large
Load diff
291
apps/codecs/libgme/nes_fds_apu.c
Normal file
291
apps/codecs/libgme/nes_fds_apu.c
Normal 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;
|
||||
}
|
||||
116
apps/codecs/libgme/nes_fds_apu.h
Normal file
116
apps/codecs/libgme/nes_fds_apu.h
Normal 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
|
||||
135
apps/codecs/libgme/nes_fme7_apu.c
Normal file
135
apps/codecs/libgme/nes_fme7_apu.c
Normal 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;
|
||||
}
|
||||
|
||||
90
apps/codecs/libgme/nes_fme7_apu.h
Normal file
90
apps/codecs/libgme/nes_fme7_apu.h
Normal 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
|
||||
61
apps/codecs/libgme/nes_mmc5_apu.h
Normal file
61
apps/codecs/libgme/nes_mmc5_apu.h
Normal 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
|
||||
133
apps/codecs/libgme/nes_namco_apu.c
Normal file
133
apps/codecs/libgme/nes_namco_apu.c
Normal 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;
|
||||
}
|
||||
|
||||
71
apps/codecs/libgme/nes_namco_apu.h
Normal file
71
apps/codecs/libgme/nes_namco_apu.h
Normal 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
|
||||
583
apps/codecs/libgme/nes_oscs.c
Normal file
583
apps/codecs/libgme/nes_oscs.c
Normal 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;
|
||||
}
|
||||
|
||||
165
apps/codecs/libgme/nes_oscs.h
Normal file
165
apps/codecs/libgme/nes_oscs.h
Normal 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
|
||||
191
apps/codecs/libgme/nes_vrc6_apu.c
Normal file
191
apps/codecs/libgme/nes_vrc6_apu.c
Normal 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;
|
||||
}
|
||||
|
||||
62
apps/codecs/libgme/nes_vrc6_apu.h
Normal file
62
apps/codecs/libgme/nes_vrc6_apu.h
Normal 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
|
||||
89
apps/codecs/libgme/nes_vrc7_apu.c
Normal file
89
apps/codecs/libgme/nes_vrc7_apu.c
Normal 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;
|
||||
}
|
||||
52
apps/codecs/libgme/nes_vrc7_apu.h
Normal file
52
apps/codecs/libgme/nes_vrc7_apu.h
Normal 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
|
||||
115
apps/codecs/libgme/nsf_cpu.c
Normal file
115
apps/codecs/libgme/nsf_cpu.c
Normal 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
Loading…
Add table
Add a link
Reference in a new issue