diff --git a/source/ngc/fceuload.c b/source/ngc/fceuload.c index cce6c2d..035dd65 100644 --- a/source/ngc/fceuload.c +++ b/source/ngc/fceuload.c @@ -95,7 +95,7 @@ int GCMemROM(int method, int size) FCEUI_Sound(SAMPLERATE); FCEUI_SetSoundVolume(100); // 0-100 FCEUI_SetSoundQuality(0); // 0 - low, 1 - high - FCEUI_SetLowPass(0); + FCEUI_SetLowPass(1); InitialisePads(); diff --git a/source/ngc/gcaudio.c b/source/ngc/gcaudio.c index d17dcb5..3ac278b 100644 --- a/source/ngc/gcaudio.c +++ b/source/ngc/gcaudio.c @@ -13,31 +13,56 @@ #include #define SAMPLERATE 48000 -unsigned char audiobuffer[2][64 * 1024] ATTRIBUTE_ALIGN(32); -/*** Allow for up to 1 full second ***/ + +static u8 ConfigRequested = 0; +static u8 soundbuffer[2][3840] ATTRIBUTE_ALIGN(32); +static u8 mixbuffer[16000]; +static int mixhead = 0; +static int mixtail = 0; +static int whichab = 0; +int IsPlaying = 0; + +static int mixercollect( u8 *outbuffer, int len ) +{ + u32 *dst = (u32 *)outbuffer; + u32 *src = (u32 *)mixbuffer; + int done = 0; + + /*** Always clear output buffer ***/ + memset(outbuffer, 0, len); + + while ( ( mixtail != mixhead ) && ( done < len ) ) + { + *dst++ = src[mixtail++]; + if (mixtail == 4000) mixtail = 0; + done += 4; + } + + /*** Realign to 32 bytes for DMA ***/ + mixtail -= ((done&0x1f) >> 2); + if (mixtail < 0) mixtail += 4000; + done &= ~0x1f; + if (!done) return len >> 1; + return done; +} /**************************************************************************** * AudioSwitchBuffers * * Manages which buffer is played next ****************************************************************************/ -static int isWriting = 0; /*** Bool for buffer writes ***/ -static int buffSize[2]; /*** Hold size of current buffer ***/ -static int whichab = 0; /*** Which Audio Buffer is in use ***/ -static int isPlaying; /*** Is Playing ***/ -static void AudioSwitchBuffers() +void AudioSwitchBuffers() { - if ( buffSize[whichab] ) - { - AUDIO_StopDMA(); - AUDIO_InitDMA((u32)audiobuffer[whichab], buffSize[whichab]); - DCFlushRange(audiobuffer[whichab], buffSize[whichab]); - AUDIO_StartDMA(); - isPlaying = 0; - } - - whichab ^= 1; - buffSize[whichab] = 0; + if ( !ConfigRequested ) + { + int len = mixercollect( soundbuffer[whichab], 3840 ); + DCFlushRange(soundbuffer[whichab], len); + AUDIO_InitDMA((u32)soundbuffer[whichab], len); + AUDIO_StartDMA(); + whichab ^= 1; + IsPlaying = 1; + } + else IsPlaying = 0; } void InitialiseSound() @@ -45,29 +70,15 @@ void InitialiseSound() AUDIO_Init(NULL); /*** Start audio subsystem ***/ AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); AUDIO_RegisterDMACallback( AudioSwitchBuffers ); - memset(audiobuffer, 0, (64 * 1024 * 2)); - buffSize[0] = buffSize[1] = 0; -} - -void StartAudio() -{ - AUDIO_StartDMA(); + memset(soundbuffer, 0, 3840*2); + memset(mixbuffer, 0, 16000); } void StopAudio() { - AUDIO_StopDMA(); - buffSize[0] = buffSize[1] = 0; -} - -static inline unsigned short FLIP16(unsigned short b) -{ - return((b<<8)|((b>>8)&0xFF)); -} - -static inline u32 FLIP32(u32 b) -{ - return( (b<<24) | ((b>>8)&0xFF00) | ((b<<8)&0xFF0000) | ((b>>24)&0xFF) ); + AUDIO_StopDMA(); + ConfigRequested = 1; + IsPlaying = 0; } /**************************************************************************** @@ -75,43 +86,24 @@ static inline u32 FLIP32(u32 b) * * PlaySound will simply mix to get it right ****************************************************************************/ -#define AUDIOBUFFER ((50 * SAMPLERATE) / 1000 ) << 4 -static int isPlaying = 0; -static short MBuffer[ 8 * 96000 / 50 ]; - -void PlaySound( unsigned int *Buffer, int count ) +void PlaySound( int *Buffer, int count ) { - int P; - unsigned short *s = (unsigned short *)&MBuffer[0]; - unsigned int *d = (unsigned int *)&audiobuffer[whichab][buffSize[whichab]]; - unsigned int c; - int ms; + int i; + s16 sample; + u32 *dst = (u32 *)mixbuffer; - isWriting = 1; + for( i = 0; i < count; i++ ) + { + sample = Buffer[i] & 0xffff; + dst[mixhead++] = sample | ( sample << 16); + if (mixhead == 4000) mixhead = 0; + } - for ( P = 0; P < count; P++ ) - { - MBuffer[P] = Buffer[P]; - } - - /*** Now do Mono - Stereo Conversion ***/ - ms = count; - do - { - c = 0xffff & *s++; - *d++ = (c | (c<<16)); - } while(--ms); - - buffSize[whichab] += ( count << 2 ); - /*** This is the kicker for the entire audio loop ***/ - if ( isPlaying == 0 ) - { - if ( buffSize[whichab] > AUDIOBUFFER ) - { - isPlaying = 1; - AudioSwitchBuffers(); - } - } - - isWriting = 0; + /* Restart Sound Processing if stopped */ + //return; + if (IsPlaying == 0) + { + ConfigRequested = 0; + AudioSwitchBuffers (); + } } diff --git a/source/ngc/gcaudio.h b/source/ngc/gcaudio.h index 4610951..a657aa4 100644 --- a/source/ngc/gcaudio.h +++ b/source/ngc/gcaudio.h @@ -11,4 +11,4 @@ void InitialiseSound(); void StopAudio(); -void PlaySound( void *Buf, int samples ); +void PlaySound( int *Buffer, int samples ); diff --git a/source/ngc/gcvideo.c b/source/ngc/gcvideo.c index dcc7511..27c6cf0 100644 --- a/source/ngc/gcvideo.c +++ b/source/ngc/gcvideo.c @@ -10,6 +10,7 @@ ****************************************************************************/ #include +#include #include #include #include @@ -52,11 +53,57 @@ void CheesyScale(unsigned char *XBuf); int whichfb = 0; int copynow = GX_FALSE; +/**************************************************************************** + * VideoThreading + ***************************************************************************/ +#define TSTACK 16384 +lwpq_t videoblankqueue; +lwp_t vbthread; +static unsigned char vbstack[TSTACK]; + +/**************************************************************************** + * vbgetback + * + * This callback enables the emulator to keep running while waiting for a + * vertical blank. + * + * Putting LWP to good use :) + ***************************************************************************/ +static void * +vbgetback (void *arg) +{ + while (1) + { + VIDEO_WaitVSync (); /**< Wait for video vertical blank */ + LWP_SuspendThread (vbthread); + } + + return NULL; +} + +/**************************************************************************** + * InitVideoThread + * + * libOGC provides a nice wrapper for LWP access. + * This function sets up a new local queue and attaches the thread to it. + ***************************************************************************/ +void +InitVideoThread () +{ + /*** Initialise a new queue ***/ + LWP_InitQueue (&videoblankqueue); + + /*** Create the thread on this queue ***/ + LWP_CreateThread (&vbthread, vbgetback, NULL, vbstack, TSTACK, 80); +} + /**************************************************************************** * GX Chip Copy to XFB ****************************************************************************/ -static void copy_to_xfb() { - if (copynow == GX_TRUE) { +static void copy_to_xfb() +{ + if (copynow == GX_TRUE) + { GX_CopyDisp(xfb[whichfb],GX_TRUE); GX_Flush(); copynow = GX_FALSE; @@ -90,7 +137,8 @@ static void copy_to_xfb() { /**************************************************************************** * Initialise the GX ****************************************************************************/ -void StartGX() { +void StartGX() +{ /*** Clear out FIFO area ***/ memset(&gp_fifo, 0, DEFAULT_FIFO_SIZE); @@ -149,7 +197,8 @@ void StartGX() { * * Using the texture map draw with quads ****************************************************************************/ -void GXDraw(unsigned char *XBuf) { +void GXDraw(unsigned char *XBuf) +{ float gs = 1.0; float gt = 1.0; int width, height,t,xb; @@ -236,7 +285,8 @@ UpdatePadsCB () * * Helps keep the rendering at 2x sweet ****************************************************************************/ -void initDisplay() { +void initDisplay() +{ /*** Start VIDEO Subsystem ***/ VIDEO_Init(); @@ -258,18 +308,24 @@ void initDisplay() { xfb[1] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); VIDEO_SetNextFramebuffer(xfb[0]); + VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK); + VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK); + VIDEO_SetNextFramebuffer (xfb[0]); + VIDEO_SetBlack(FALSE); VIDEO_Flush(); VIDEO_WaitVSync(); - if(vmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); + if(vmode->viTVMode&VI_NON_INTERLACE) + VIDEO_WaitVSync(); + VIDEO_SetPostRetraceCallback((VIRetraceCallback)UpdatePadsCB); - /*** Setup a console - guard against spurious printf ***/ VIDEO_SetPreRetraceCallback((VIRetraceCallback)copy_to_xfb); - VIDEO_SetNextFramebuffer(xfb[0]); PAD_Init(); StartGX(); + + InitVideoThread (); } /**************************************************************************** @@ -279,12 +335,19 @@ void initDisplay() { ****************************************************************************/ #define NESWIDTH 256 #define NESHEIGHT 240 -void RenderFrame(char *XBuf, int style) { + +void RenderFrame(char *XBuf, int style) +{ int gcdispOffset = 32; /*** Offset to centre on screen ***/ int w,h; int c,i; + // Ensure previous vb has complete + while ((LWP_ThreadIsSuspended (vbthread) == 0) || (copynow == GX_TRUE)) + usleep (50); + whichfb ^= 1; + switch(style) { case 0 : VIDEO_ClearFrameBuffer(vmode, xfb[whichfb], COLOR_BLACK); @@ -313,10 +376,12 @@ void RenderFrame(char *XBuf, int style) { break; } - /*** Now resync with VSync ***/ - VIDEO_SetNextFramebuffer(xfb[whichfb]); - VIDEO_Flush(); - VIDEO_WaitVSync(); + VIDEO_SetNextFramebuffer(xfb[whichfb]); + VIDEO_Flush(); + copynow = GX_TRUE; + + // Return to caller, don't waste time waiting for vb + LWP_ResumeThread (vbthread); } /****************************************************************************