int PicoRegionOverride = 0; // override the region detection 0: Auto, 1: Japan NTSC, 2: Japan PAL, 4: US, 8: Europe\r
int PicoAutoRgnOrder = 0;\r
int emustatus = 0;\r
-void (*PicoWriteSound)(void) = 0; // called once per frame at the best time to send sound buffer (PsndOut) to hardware\r
+void (*PicoWriteSound)(int len) = 0; // called once per frame at the best time to send sound buffer (PsndOut) to hardware\r
\r
struct PicoSRAM SRam;\r
int z80startCycle = 0, z80stopCycle = 0; // in 68k cycles\r
// to be called on 224 or line_sample scanlines only\r
static __inline void getSamples(int y)\r
{\r
+ static int curr_pos = 0;\r
+\r
if(y == 224) {\r
//dprintf("sta%i: %i [%i]", (emustatus & 2), emustatus, y);\r
if(emustatus & 2)\r
- sound_render(PsndLen/2, PsndLen-PsndLen/2);\r
- else sound_render(0, PsndLen);\r
+ curr_pos += sound_render(curr_pos, PsndLen-PsndLen/2);\r
+ else curr_pos = sound_render(0, PsndLen);\r
if (emustatus&1) emustatus|=2; else emustatus&=~2;\r
- if (PicoWriteSound) PicoWriteSound();\r
+ if (PicoWriteSound) PicoWriteSound(curr_pos);\r
// clear sound buffer\r
sound_clear();\r
- //memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1));\r
}\r
else if(emustatus & 3) {\r
emustatus|= 2;\r
emustatus&=~1;\r
- sound_render(0, PsndLen/2);\r
+ curr_pos = sound_render(0, PsndLen/2);\r
}\r
}\r
\r
\r
// here we render sound if ym2612 is disabled\r
if(!(PicoOpt&1) && PsndOut) {\r
- sound_render(0, PsndLen);\r
- if(PicoWriteSound) PicoWriteSound();\r
+ int len = sound_render(0, PsndLen);\r
+ if(PicoWriteSound) PicoWriteSound(len);\r
// clear sound buffer\r
- memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1));\r
+ sound_clear();\r
}\r
\r
// render screen\r
int PicoFrame(void);\r
extern int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU\r
extern int (*PicoCram)(int cram); // Callback to convert colour ram 0000bbb0 ggg0rrr0\r
-extern void (*PicoWriteSound)(void); // called once per frame at the best time to send sound buffer (PsndOut) to hardware\r
+extern void (*PicoWriteSound)(int len); // called once per frame at the best time to send sound buffer (PsndOut) to hardware\r
\r
int PicoFrameMCD(void);\r
\r
extern int PsndRate,PsndLen;\r
extern short *PsndOut;\r
void sound_reset();\r
-void sound_rerate();\r
+void sound_rerate(int preserve_state);\r
void z80_pack(unsigned char *data);\r
void z80_unpack(unsigned char *data);\r
void z80_reset();\r
int SekResetS68k(void);\r
int SekInterruptS68k(int irq);\r
\r
+// sound/sound.c\r
+extern int PsndLen_exc_cnt;\r
+extern int PsndLen_exc_add;\r
+\r
// VideoPort.c\r
void PicoVideoWrite(unsigned int a,unsigned short d);\r
unsigned int PicoVideoRead(unsigned int a);\r
gfx_cd_update();
}
-// to be called on 224 or line_sample scanlines only
-static __inline void getSamples(int y)
-{
- if(y == 224) {
- //dprintf("sta%i: %i [%i]", (emustatus & 2), emustatus, y);
- if(emustatus & 2)
- sound_render(PsndLen/2, PsndLen-PsndLen/2);
- else sound_render(0, PsndLen);
- if (emustatus&1) emustatus|=2; else emustatus&=~2;
- if (PicoWriteSound) PicoWriteSound();
- // clear sound buffer
- sound_clear();
- //memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1));
- }
- else if(emustatus & 3) {
- emustatus|= 2;
- emustatus&=~1;
- sound_render(0, PsndLen/2);
- }
-}
-
static int PicoFrameHintsMCD(void)
{
sound_timers_and_dac(y);
// get samples from sound chips
- if(y == 32 && PsndOut)
- emustatus &= ~1;
- else if((y == 224 || y == line_sample) && PsndOut)
- getSamples(y);
+ if (y == 224 && PsndOut) {
+ int len = sound_render(0, PsndLen);
+ if (PicoWriteSound) PicoWriteSound(len);
+ // clear sound buffer
+ sound_clear();
+ }
// Run scanline:
//dprintf("m68k starting exec @ %06x", SekPc);
@ limit
@ reg=int_sample, lr=1, r3=tmp, kills flags
.macro Limit reg
- add r3, lr, \reg, asr #16
+ add r3, lr, \reg, asr #15
bics r3, r3, #1 @ in non-overflow conditions r3 is 0 or 1
movne \reg, #0x8000
- submi \reg, \reg, #1
+ subpl \reg, \reg, #1
.endm
@ mov r3, #0x8000
@ subpl r3, r3, #1
- add r3, lr, \reg, asr #16
+ add r3, lr, \reg, asr #15
bics r3, r3, #1 @ in non-overflow conditions r3 is 0 or 1
moveq \reg, \reg, lsl #16
movne \reg, #0x80000000
- submi \reg, \reg, #0x00010000
+ subpl \reg, \reg, #0x00010000
.endm
@ check if dest is word aligned
tst r0, #2
beq m32_16_mo_no_unalw
- ldrsh r5, [r0], #2
+ ldrsh r5, [r0]
ldr r4, [r1], #4
sub r2, r2, #1
add r4, r4, r5
m32_16_mo_no_unal2:
tst r2, #1
ldmeqfd sp!, {r4-r8,pc}
- ldrsh r5, [r0], #2
+ ldrsh r5, [r0]
ldr r4, [r1], #4
add r4, r4, r5
Limit r4
// for Pico\r
int PsndRate=0;\r
int PsndLen=0; // number of mono samples, multiply by 2 for stereo\r
+int PsndLen_exc_add=0; // this is for non-integer sample counts per line, eg. 22050/60\r
+int PsndLen_exc_cnt=0;\r
short *PsndOut=NULL; // PCM data buffer\r
\r
// from ym2612.c\r
\r
if(PsndLen <= lines) {\r
// shrinking algo\r
- dac_cnt = 0;//lines - PsndLen;\r
+ dac_cnt = -PsndLen;\r
len=1; pos=0;\r
dac_info[225] = 1;\r
\r
}\r
} else {\r
// stretching\r
- dac_cnt = PsndLen/2;\r
+ dac_cnt = PsndLen;\r
pos=0;\r
for(i = 225; i != 224; i++) {\r
if (i >= lines) i = 0;\r
}\r
// last sample\r
for(len = 0, i = pos; i < PsndLen; i++) len++;\r
+ if (PsndLen_exc_add) len++;\r
dac_info[224] = (pos<<4)|len;\r
}\r
-// dprintf("rate is %i, len %i", PsndRate, PsndLen);\r
-// for(i=0; i < lines; i++)\r
-// dprintf("%03i : %03i : %i", i, dac_info[i]>>4, dac_info[i]&0xf);\r
-//exit(8);\r
+ //for(i=len=0; i < lines; i++) {\r
+ // printf("%03i : %03i : %i\n", i, dac_info[i]>>4, dac_info[i]&0xf);\r
+ // len+=dac_info[i]&0xf;\r
+ //}\r
+ //printf("rate is %i, len %f\n", PsndRate, (double)PsndRate/(Pico.m.pal ? 50.0 : 60.0));\r
+ //printf("len total: %i, last pos: %i\n", len, pos);\r
+ //exit(8);\r
}\r
\r
\r
extern int z80stopCycle;\r
void *ym2612_regs;\r
\r
- // init even if we are not going to use them, just in case we ever enable it\r
- YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);\r
// also clear the internal registers+addr line\r
ym2612_regs = YM2612GetRegs();\r
memset(ym2612_regs, 0, 0x200+4);\r
-\r
- SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);\r
-\r
- // calculate PsndLen\r
- PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);\r
-\r
- // recalculate dac info\r
- dac_recalculate();\r
z80stopCycle = 0;\r
+\r
+ sound_rerate(0);\r
}\r
\r
\r
// to be called after changing sound rate or chips\r
-void sound_rerate()\r
+void sound_rerate(int preserve_state)\r
{\r
unsigned int state[28];\r
+ int target_fps = Pico.m.pal ? 50 : 60;\r
\r
- // not all rates are supported in MCD mode due to mp3 decoder\r
+ // not all rates are supported in MCD mode due to mp3 decoder limitations\r
if (PicoMCD & 1) {\r
if (PsndRate != 11025 && PsndRate != 22050 && PsndRate != 44100) PsndRate = 22050;\r
- if (!(PicoOpt & 8)) PicoOpt |= 8;\r
+ PicoOpt |= 8; // force stereo\r
}\r
\r
- if ((PicoMCD & 1) && Pico_mcd->m.audio_track) Pico_mcd->m.audio_offset = mp3_get_offset();\r
+ if (preserve_state) {\r
+ if ((PicoMCD & 1) && Pico_mcd->m.audio_track)\r
+ Pico_mcd->m.audio_offset = mp3_get_offset();\r
+ }\r
YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);\r
- // feed it back it's own registers, just like after loading state\r
- YM2612PicoStateLoad();\r
- if ((PicoMCD & 1) && Pico_mcd->m.audio_track)\r
- mp3_start_play(Pico_mcd->TOC.Tracks[Pico_mcd->m.audio_track].F, Pico_mcd->m.audio_offset);\r
+ if (preserve_state) {\r
+ // feed it back it's own registers, just like after loading state\r
+ YM2612PicoStateLoad();\r
+ if ((PicoMCD & 1) && Pico_mcd->m.audio_track)\r
+ mp3_start_play(Pico_mcd->TOC.Tracks[Pico_mcd->m.audio_track].F, Pico_mcd->m.audio_offset);\r
+ }\r
\r
- memcpy(state, sn76496_regs, 28*4); // remember old state\r
+ if (preserve_state) memcpy(state, sn76496_regs, 28*4); // remember old state\r
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);\r
- memcpy(sn76496_regs, state, 28*4); // restore old state\r
+ if (preserve_state) memcpy(sn76496_regs, state, 28*4); // restore old state\r
\r
// calculate PsndLen\r
- PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);\r
+ PsndLen=PsndRate / target_fps;\r
+ PsndLen_exc_add=((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
+ PsndLen_exc_cnt=0;\r
\r
// recalculate dac info\r
dac_recalculate();\r
\r
if (PicoMCD & 1)\r
pcm_set_rate(PsndRate);\r
+\r
+ // clear all buffers\r
+ memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);\r
+ if (PsndOut)\r
+ sound_clear();\r
}\r
\r
\r
d[0] = dout;\r
if (len > 1) {\r
d[2] = dout;\r
- if (len > 2) {\r
+ if (len > 2)\r
d[4] = dout;\r
- if (len > 3)\r
- d[6] = dout;\r
- }\r
}\r
} else {\r
short *d = PsndOut + pos;\r
d[0] = dout;\r
if (len > 1) {\r
d[1] = dout;\r
- if (len > 2) {\r
+ if (len > 2)\r
d[2] = dout;\r
- if (len > 3)\r
- d[3] = dout;\r
- }\r
}\r
}\r
}\r
\r
void sound_clear(void)\r
{\r
- if (PicoOpt & 8) memset32((int *) PsndOut, 0, PsndLen);\r
- else memset(PsndOut, 0, PsndLen<<1);\r
-// memset(PsndBuffer, 0, (PicoOpt & 8) ? (PsndLen<<3) : (PsndLen<<2));\r
+ int len = PsndLen;\r
+ if (PsndLen_exc_add) len++;\r
+ if (PicoOpt & 8) memset32((int *) PsndOut, 0, len); // clear both channels at once\r
+ else memset(PsndOut, 0, len<<1);\r
}\r
\r
\r
int do_pcm = (PicoMCD&1) && (PicoOpt&0x400) && (Pico_mcd->pcm.control & 0x80) && Pico_mcd->pcm.enabled;\r
offset <<= stereo;\r
\r
+ if (offset == 0) { // should happen once per frame\r
+ // compensate for float part of PsndLen\r
+ PsndLen_exc_cnt += PsndLen_exc_add;\r
+ if (PsndLen_exc_cnt >= 0x10000) {\r
+ PsndLen_exc_cnt -= 0x10000;\r
+ length++;\r
+ }\r
+ }\r
+\r
// PSG\r
if (PicoOpt & 2)\r
SN76496Update(PsndOut+offset, length, stereo);\r
if (PicoOpt & 1)\r
YM2612UpdateOne(buf32, length, stereo, 1);\r
\r
+ // CD: PCM sound\r
if (do_pcm)\r
pcm_update(buf32, length, stereo);\r
\r
- // CDDA audio\r
+ // CD: CDDA audio\r
// if ((PicoMCD & 1) && (PicoOpt & 0x800))\r
// mp3_update(PsndBuffer+offset, length, stereo);\r
\r
mix_32_to_16l_stereo(PsndOut+offset, buf32, length);\r
else mix_32_to_16_mono (PsndOut+offset, buf32, length);\r
\r
- return 0;\r
+ return length;\r
}\r
\r
\r
}\r
writebuff_ptr = 0;\r
\r
+ /* predict sample counter for next frame */\r
+ if (PsndLen_exc_add) {\r
+ if (PsndLen_exc_cnt + PsndLen_exc_add >= 0x10000) length = PsndLen + 1;\r
+ else length = PsndLen;\r
+ }\r
+\r
/* give 940 ym job */\r
shared_ctl->writebuffsel ^= 1;\r
shared_ctl->length = length;\r
static _940_ctl_t *shared_ctl = (_940_ctl_t *) 0x00200000;\r
static unsigned char *mp3_data = (unsigned char *) 0x01000000;\r
YM2612 *ym2612_940;\r
-int *mix_buffer;\r
\r
// from init.s\r
void wait_irq(void);\r
\r
void Main940(int startvector, int pc_at_irq)\r
{\r
+ int mix_buffer = shared_data->mix_buffer;\r
ym2612_940 = &shared_data->ym2612;\r
- mix_buffer = shared_data->mix_buffer;\r
\r
// debug\r
shared_ctl->vstarts[startvector]++;\r
\r
extern int crashed_940;\r
\r
+static short sndBuffer[2*44100/50];\r
static char noticeMsg[64]; // notice msg to draw\r
static struct timeval noticeMsgTime = { 0, 0 }; // when started showing\r
static int reset_timing, osd_fps_x;\r
prevEvents = (allActions[0] | allActions[1]) >> 16;\r
}\r
\r
-static int snd_excess_add = 0, snd_excess_cnt = 0; // hack\r
\r
-static void updateSound(void)\r
+static void updateSound(int len)\r
{\r
- int len = (PicoOpt&8)?PsndLen*2:PsndLen;\r
-\r
- snd_excess_cnt += snd_excess_add;\r
- if (snd_excess_cnt >= 0x10000) {\r
- snd_excess_cnt -= 0x10000;\r
- if (PicoOpt&8) {\r
- PsndOut[len] = PsndOut[len-2];\r
- PsndOut[len+1] = PsndOut[len-1];\r
- len+=2;\r
- } else {\r
- PsndOut[len] = PsndOut[len-1];\r
- len++;\r
- }\r
- }\r
+ if (PicoOpt&8) len<<=1;\r
\r
gp2x_sound_write(PsndOut, len<<1);\r
}\r
\r
// prepare sound stuff\r
if(currentConfig.EmuOpt & 4) {\r
+ int snd_excess_add;\r
if(PsndRate != PsndRate_old || (PicoOpt&0x20b) != (PicoOpt_old&0x20b) || Pico.m.pal != pal_old || crashed_940) {\r
/* if 940 is turned off, we need it to be put back to sleep */\r
if (!(PicoOpt&0x200) && ((PicoOpt^PicoOpt_old)&0x200)) {\r
Reset940(1, 2);\r
Pause940(1);\r
}\r
- sound_rerate();\r
+ sound_rerate(1);\r
}\r
//excess_samples = PsndRate - PsndLen*target_fps;\r
- snd_excess_cnt = 0;\r
snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n", PsndRate, PsndLen, snd_excess_add, (PicoOpt&8)>>3, Pico.m.pal);\r
gp2x_start_sound(PsndRate, 16, (PicoOpt&8)>>3);\r
gp2x_sound_volume(currentConfig.volume, currentConfig.volume);\r
PicoWriteSound = updateSound;\r
- PsndOut = calloc((PicoOpt&8) ? (PsndLen*4+4) : (PsndLen*2+2), 1);\r
+ memset(sndBuffer, 0, sizeof(sndBuffer));\r
+ PsndOut = sndBuffer;\r
PsndRate_old = PsndRate;\r
PsndLen_real = PsndLen;\r
PicoOpt_old = PicoOpt;\r
emu_SaveLoadGame(0, 1);\r
SRam.changed = 0;\r
}\r
-\r
- if (PsndOut != 0) {\r
- free(PsndOut);\r
- PsndOut = 0;\r
- }\r
}\r
\r
\r