add sound support (PSP), improve performance (PSP), fix sound buffer underrun

This commit is contained in:
hissorii 2013-08-31 00:11:38 +09:00
parent 2063f37a76
commit fc7aa486d5
11 changed files with 94 additions and 157 deletions

View File

@ -1,4 +1,4 @@
VERSION=0.01
VERSION=0.02
#if defined(NO_SOUND)
EXTRA_DEFINES+= -DNOSOUND

View File

@ -167,10 +167,10 @@ void FASTCALL OPM_Write(DWORD adr, BYTE data)
}
void OPM_Update(short *buffer, int length, BYTE *pbsp, BYTE *pbep)
void OPM_Update(short *buffer, int length, int rate)
{
if ( (!juliet_YM2151IsEnable())||(!Config.SoundROMEO) )
if ( opm ) opm->Mix((FM::Sample*)buffer, length, pbsp, pbep);
if ( opm ) opm->Mix((FM::Sample*)buffer, length, rate);
}

View File

@ -4,7 +4,7 @@
int OPM_Init(int clock, int rate);
void OPM_Cleanup(void);
void OPM_Reset(void);
void OPM_Update(short *buffer, int length, BYTE *pbsp, BYTE *pbep);
void OPM_Update(short *buffer, int length, int rate);
void FASTCALL OPM_Write(DWORD r, BYTE v);
BYTE FASTCALL OPM_Read(WORD a);
void FASTCALL OPM_Timer(DWORD step);

View File

@ -457,7 +457,7 @@ inline void OPM::MixSubL(int activech, ISample** idest)
// ---------------------------------------------------------------------------
// ¹çÀ® (stereo)
//
void OPM::Mix(Sample* buffer, int nsamples, BYTE* pbsp, BYTE* pbep)
void OPM::Mix(Sample* buffer, int nsamples, int rate)
{
#define IStoSample(s) ((Limit(s, 0xffff, -0x10000) * fmvolume) >> 14)
//#define IStoSample(s) ((s * fmvolume) >> 14)
@ -488,11 +488,7 @@ void OPM::Mix(Sample* buffer, int nsamples, BYTE* pbsp, BYTE* pbep)
idest[6] = &ibuf[pan[6]];
idest[7] = &ibuf[pan[7]];
for (i = 0, dest = buffer; i < nsamples; i++, dest += 2) {
if ((BYTE *)dest >= pbep) {
dest = (Sample *)pbsp;
}
for (i = 0, dest = buffer; i < nsamples; i++) {
ibuf[1] = ibuf[2] = ibuf[3] = 0;
if (activech & 0xaaaa)
LFO(), MixSubL(activech, idest);
@ -501,6 +497,18 @@ void OPM::Mix(Sample* buffer, int nsamples, BYTE* pbsp, BYTE* pbep)
StoreSample(dest[0], IStoSample(ibuf[1] + ibuf[3]));
StoreSample(dest[1], IStoSample(ibuf[2] + ibuf[3]));
// PSP°Ê³°¤Ïrate¤Ï0
if (rate == 0 || rate == 44100) {
dest += 2;
} else if (rate == 22050) {
dest[2] = dest[0];
dest[3] = dest[1];
dest += 4;
} else if (rate == 11025) {
dest[2] = dest[4] = dest[6] = dest[0];
dest[3] = dest[5] = dest[7] = dest[1];
dest += 8;
}
}
}
#undef IStoSample

View File

@ -88,7 +88,7 @@ namespace FM
uint GetReg(uint addr);
uint ReadStatus() { return status & 0x03; }
void Mix(Sample* buffer, int nsamples, BYTE* pbsp, BYTE* pbep);
void Mix(Sample* buffer, int nsamples, int rate);
void SetVolume(int db);
void SetChannelMask(uint mask);

View File

@ -1,6 +1,6 @@
ポータブルX68000エミュレータ
PX68K ( Portable (x)keropi PRO-68K )
2013/08/29
2013/08/31
いろいろなデバイスでX68000エミュレータが動作したら楽しいな、
と思い立ち、このPX68Kを作りはじめました。
@ -25,7 +25,7 @@ PX68K
(詳細はfmgen/readme.txtを参照)
PX68Kへの実装にあたり、以下の変更を行っています
・Sample型をint32からint16に変更
OPM::Mix()をリングバッファ対応に変更
PSP用に11k, 22kから44kデータ作成ロジックを追加
0. 注意事項
@ -115,13 +115,13 @@ PX68K
5. サウンド出力
・PSPについては未実装です。
・Android/Unixは対応していますが、サンプリング周波数は22050Hz固定です。
今のところconfigに書いた値は反映されません。UI実装時に考えます。
現在ADPCMとOPMのみ対応済みで、マーキュリーユニット、MIDIについては
未対応です。
・PSPは今のところサンプリング周波数が11025Hz固定です。
7. その他
7.1 X68000のメインメモリ
@ -154,6 +154,11 @@ PX68K
2013/08/22 Android版Ver0.01リリース
2013/08/29 サウンド出力の追加 (Unix, Android)
2013/08/29 Android版Ver0.02リリース
2013/08/30 プチイズの原因となる、サウンドバッファのunderrunが
発生しないよう修正 (common)
2013/08/30 CPUクロックを222MHzから333Mhzに変更 (PSP)
2013/08/30 サウンド出力の追加 (PSP)
2013/08/31 PSP版Ver0.02リリース
9. 免責

View File

@ -35,18 +35,11 @@
#include <android/log.h>
#endif
short playing = FALSE;
#define PCMBUF_SIZE 2*2*48000*2
#define PCMBUF_SIZE 2*2*48000
short playing = FALSE;
BYTE pcmbuffer[PCMBUF_SIZE];
BYTE *pbsp = pcmbuffer;
BYTE *pbrp = pcmbuffer, *pbwp = pcmbuffer;
BYTE *pbep = &pcmbuffer[PCMBUF_SIZE];
DWORD ratebase1000 = 22;
DWORD ratebase = 22050;
long DSound_PreCounter = 0;
BYTE sdlsndbuf[PCMBUF_SIZE];
int inlen = 0;
int audio_fd = -1;
@ -77,23 +70,30 @@ DSound_Init(unsigned long rate, unsigned long buflen)
return TRUE;
}
printf("pbsp 0x%x, pbep 0x%x\n", pbsp, pbep);
ratebase1000 = rate / 1000;
ratebase = rate;
//printf("pbsp 0x%x, pbep 0x%x\n", pbsp, pbep);
// Linuxは2倍(SDL1.2)、Android(SDL2.0)は4倍のlenでcallbackされた。
// この値を小さくした方が音の遅延は少なくなるが負荷があがる
samples = 2048;
samples = 1024;
// ??????????????????????????????
bzero(&fmt, sizeof(fmt));
#ifdef PSP
// PSPは常に44kを要求するので、rateが22Kの場合はデータを2倍にする
// r0, l0, r1, l1, ... -> r0, l0, r0, l0, r1, l1, r1, l1, ...
fmt.freq = 44100;
#else
fmt.freq = rate;
#endif
fmt.format = AUDIO_S16SYS;
fmt.channels = 2;
fmt.samples = samples;
fmt.callback = sdlaudio_callback;
fmt.userdata = NULL;
#ifdef PSP
fmt.userdata = rate;
#else
fmt.userdata = 0;
#endif
audio_fd = SDL_OpenAudio(&fmt, NULL);
if (audio_fd < 0) {
SDL_Quit();
@ -143,146 +143,59 @@ DSound_Cleanup(void)
// ??????????????
// ---------------------------------------------------------------------------
void FASTCALL
DSound_Send0(long clock)
static void FASTCALL
DSound_Send(long length, int rate)
{
int length = 0;
if (audio_fd >= 0) {
DSound_PreCounter += (ratebase * clock);
while (DSound_PreCounter >= 10000000L) {
length++;
DSound_PreCounter -= 10000000L;
}
if (length == 0) {
return;
}
SDL_LockAudio();
inlen += length * 4;
ADPCM_Update((short *)pbwp, length, pbsp, pbep);
OPM_Update((short *)pbwp, length, pbsp, pbep);
ADPCM_Update((short *)pcmbuffer, length, rate);
OPM_Update((short *)pcmbuffer, length, rate);
#ifndef NO_MERCURY
//Mcry_Update((short *)pcmbufp, length);
#endif
pbwp += length * sizeof(WORD) * 2;
if (pbwp >= pbep) {
pbwp = pbsp;
}
SDL_UnlockAudio();
}
}
static void
sdlaudio_callback(void *userdata, unsigned char *stream, int len)
{
int lena, lenb, datalen;
BYTE *buf, *pbrp0;
static DWORD bef;
DWORD now;
static int under = 0;
SDL_LockAudio();
int rate;
now = timeGetTime();
//PSP以外はrateは0
rate = (int)userdata;
#ifdef PSP
DSound_Send(len / (44100 / rate) / 4, rate);
#else
DSound_Send(len / 4, rate);
#endif
#ifdef ANDROID
//__android_log_print(ANDROID_LOG_DEBUG,"Tag","tdiff %4d : pbrp = 0x%x, pbwp = 0x%x : len %d", now - bef, pbrp, pbwp, len);
#else
//printf("tdiff %4d : pbrp = 0x%x, pbwp = 0x%x : ", now - bef, pbrp, pbwp);
//printf("tdiff %4d : pbrp = 0x%x, pbwp = 0x%x : len %d", now - bef, pbrp, pbwp, len);
#endif
pbrp0 = pbrp;
if (pbrp <= pbwp) {
// pcmbuffer
// +---------+-------------+----------+
// | |/////////////| |
// +---------+-------------+----------+
// A A<--datalen-->A A
// | | | |
// pbsp pbrp pbwp pbep
datalen = pbwp - pbrp;
if (datalen >= len) {
buf = pbrp;
pbrp += len;
//printf("TYPEA: ");
} else {
memcpy(sdlsndbuf, pbrp, datalen);
// xxx underrun : 不足分はとりあえず0で埋める
memset(&sdlsndbuf[datalen], 0, len - datalen);
buf = sdlsndbuf;
pbrp += datalen;
//printf("TYPEB: ");
}
} else {
// pcmbuffer
// +---------+-------------+----------+
// |/////////| |//////////|
// +------+--+-------------+----------+
// <-lenb-> A <---lena--->
// A | A A
// | | | |
// pbsp pbwp pbrp pbep
lena = pbep - pbrp;
if (lena >= len) {
buf = pbrp;
pbrp += len;
//printf("***** TYPEC: ");
} else {
lenb = len - lena;
if (pbwp - pbsp >= lenb) {
memcpy(sdlsndbuf, pbrp, lena);
memcpy(&sdlsndbuf[lena], pbsp, lenb);
buf = sdlsndbuf;
pbrp = pbsp + lenb;
//printf("********** TYPED: ");
} else {
memcpy(sdlsndbuf, pbrp, lena);
memcpy(&sdlsndbuf[lena], pbsp, pbwp - pbsp);
// xxx underrun : 不足分はとりあえず0で埋める
memset(&sdlsndbuf[lena + pbwp - pbsp], 0, lenb - (pbwp - pbsp));
buf = sdlsndbuf;
pbrp = pbwp;
//printf("*************** TYPEE: ");
}
}
}
under = under + inlen - len;
//printf("inlen %4d under %4d\n", inlen, under);
inlen = 0;
#if SDL_VERSION_ATLEAST(2, 0, 0)
// SDL2.0ではstream bufferのクリアが必要
memset(stream, 0, len);
#endif
SDL_MixAudio(stream, buf, len, SDL_MIX_MAXVOLUME);
//printf("pbrp0 0x%x, pbrp 0x%x\n", pbrp0, pbrp);
SDL_MixAudio(stream, pcmbuffer, len, SDL_MIX_MAXVOLUME);
#if 0
// ADPCMがバッファ書き込み -> OPMがバッファにデータをMix なので
// ADPCMを無効にする場合はリングバッファのクリアが必要
if (pbrp >= pbrp0) {
memset(pbrp0, 0, pbrp - pbrp0);
} else {
memset(pbsp, 0, pbrp - pbsp);
memset(pbrp0, 0, pbep - pbrp0);
}
// ADPCMを無効にする場合はリングバッファのクリアが必要なので
// この#if 0を有効にする
memset(pcmbuffer, 0, len);
#endif
bef = now;
SDL_UnlockAudio();
}
#else /* NOSOUND */
int
DSound_Init(unsigned long rate, unsigned long buflen)
{
return FALSE;
}
@ -303,9 +216,4 @@ DSound_Cleanup(void)
return TRUE;
}
void FASTCALL
DSound_Send0(long clock)
{
}
#endif /* !NOSOUND */

View File

@ -8,7 +8,6 @@ int DSound_Cleanup(void);
void DSound_Play(void);
void DSound_Stop(void);
void FASTCALL DSound_Send0(long clock);
void DS_SetVolumeOPM(long vol);
void DS_SetVolumeADPCM(long vol);

View File

@ -436,7 +436,7 @@ void WinX68k_Exec(void)
if ( clk_count>=clk_next ) {
//OPM_RomeoOut(Config.BufferSize*5);
MIDI_DelayOut((Config.MIDIAutoDelay)?(Config.BufferSize*5):Config.MIDIDelay);
//MIDI_DelayOut((Config.MIDIAutoDelay)?(Config.BufferSize*5):Config.MIDIDelay);
MFP_TimerA();
if ( (MFP[MFP_AER]&0x40)&&(vline==CRTC_IntLine) )
MFP_Int(1);
@ -470,7 +470,6 @@ void WinX68k_Exec(void)
MouseIntCnt = 0;
SCC_IntCheck();
}
DSound_Send0(clk_line); // Bufferがでかいとき用
vline++;
clk_next = (clk_total*(vline+1))/VLINE_TOTAL;
@ -513,6 +512,7 @@ void WinX68k_Exec(void)
#ifdef PSP
#include <pspmoduleinfo.h>
#include <psppower.h>
PSP_HEAP_SIZE_KB(-1024);
@ -525,6 +525,10 @@ int main(int argc, char *argv[])
SDL_Event ev;
int sdlaudio = -1;
#ifdef PSP
scePowerSetClockFrequency(333, 333, 166);
#endif
#ifdef ANDROID
__android_log_write(ANDROID_LOG_DEBUG,"Tag","555");
#endif
@ -537,9 +541,12 @@ int main(int argc, char *argv[])
LoadConfig();
// xxx PSPで音がおかしくなるのを調べる
#ifndef PSP
Config.SampleRate = 22050;
#else
Config.SampleRate = 11025;
FrameRate = 5;
NoWaitMode = 0;
#endif
#ifndef NOSOUND
@ -700,6 +707,7 @@ int main(int argc, char *argv[])
WinDraw_HideSplash();
}
}
#ifndef PSP
while (SDL_PollEvent(&ev)) {
switch (ev.type) {
case SDL_QUIT:
@ -714,6 +722,7 @@ int main(int argc, char *argv[])
break;
}
}
#endif
}
end_loop:

View File

@ -114,7 +114,7 @@ void FASTCALL ADPCM_PreUpdate(DWORD clock)
// -----------------------------------------------------------------------
// DSoundが指定してくる分だけバッファにデータを書き出す
// -----------------------------------------------------------------------
void FASTCALL ADPCM_Update(signed short *buffer, DWORD length, BYTE *pbsp, BYTE *pbep)
void FASTCALL ADPCM_Update(signed short *buffer, DWORD length, int rate)
{
int outs;
signed int outl, outr;
@ -122,11 +122,7 @@ void FASTCALL ADPCM_Update(signed short *buffer, DWORD length, BYTE *pbsp, BYTE
if ( length<=0 ) return;
while ( length ) {
int tmp;
if (buffer >= pbep) {
buffer = pbsp;
}
int tmpl, tmpr;
if ( (ADPCM_WrPtr==ADPCM_RdPtr)&&(!(DMA[3].CCR&0x40)) ) DMA_Exec(3);
if ( ADPCM_WrPtr!=ADPCM_RdPtr ) {
@ -172,12 +168,24 @@ void FASTCALL ADPCM_Update(signed short *buffer, DWORD length, BYTE *pbsp, BYTE
OutsIpL[3] = outs;
#if 1
tmp = INTERPOLATE(OutsIpR, 0);
if ( tmp>32767 ) tmp = 32767; else if ( tmp<(-32768) ) tmp = -32768;
*(buffer++) = (short)tmp;
tmp = INTERPOLATE(OutsIpL, 0);
if ( tmp>32767 ) tmp = 32767; else if ( tmp<(-32768) ) tmp = -32768;
*(buffer++) = (short)tmp;
tmpr = INTERPOLATE(OutsIpR, 0);
if ( tmpr>32767 ) tmpr = 32767; else if ( tmpr<(-32768) ) tmpr = -32768;
*(buffer++) = (short)tmpr;
tmpl = INTERPOLATE(OutsIpL, 0);
if ( tmpl>32767 ) tmpl = 32767; else if ( tmpl<(-32768) ) tmpl = -32768;
*(buffer++) = (short)tmpl;
// PSP°Ê³°¤Ïrate¤Ï0
if (rate == 22050) {
*(buffer++) = (short)tmpr;
*(buffer++) = (short)tmpl;
} else if (rate == 11025) {
*(buffer++) = (short)tmpr;
*(buffer++) = (short)tmpl;
*(buffer++) = (short)tmpr;
*(buffer++) = (short)tmpl;
*(buffer++) = (short)tmpr;
*(buffer++) = (short)tmpl;
}
#else
*(buffer++) = (short)OutsIpR[3];
*(buffer++) = (short)OutsIpL[3];

View File

@ -5,7 +5,7 @@ extern BYTE ADPCM_Clock;
extern DWORD ADPCM_ClockRate;
void FASTCALL ADPCM_PreUpdate(DWORD clock);
void FASTCALL ADPCM_Update(signed short *buffer, DWORD length, BYTE *pbsp, BYTE *pbep);
void FASTCALL ADPCM_Update(signed short *buffer, DWORD length, int rate);
void FASTCALL ADPCM_Write(DWORD adr, BYTE data);
BYTE FASTCALL ADPCM_Read(DWORD adr);