thug/Code/Gel/Music/Ngps/Pcm/pcm_com.c
2016-02-14 08:39:12 +11:00

1822 lines
50 KiB
C

/* Vag streaming into SPU2 -- from sony sample -- matt may 2001 */
#include <kernel.h>
#include <sys/types.h>
#include <sys/file.h>
#include <stdio.h>
#include <string.h>
#include <sif.h>
#include <sifcmd.h>
#include <sifrpc.h>
#include <libsd.h>
#include <sdrcmd.h>
#include <thread.h>
#include "pcm.h"
#include "pcmiop.h"
// ================================================================
#define STREAM_PITCH( x ) ( ( ( x ) * 22050 ) / 48000 )
#define CHECK_TRANSFER_INTEGRITY 0
#define TEST_PLAY_TIMING 0
#define SHOW_STREAM_INFO 0
#define SHOW_STATE 0
#define SHOW_SMALL_STREAM_STATE 0
#define SHOW_SMALL_MUSIC_STATE 0
#define SHOW_ACTION 0
#define SHOW_SMALL_ACTION 0
#define DBUGPRINTF 0
#define SHOW_TIMING 0
#define SHOW_STOP 0
#define SHOW_SMALL_COMMANDS 0
#define PRINT_EXCESSIVE_WAIT 0
#if SHOW_STREAM_INFO
#define ShowStreamInfo( x ) _ShowStreamInfo( x )
#else
#define ShowStreamInfo( x )
#endif
#if SHOW_SMALL_COMMANDS
#define ShowSmallCommand printf
#else
#define ShowSmallCommand( x )
#endif
#if SHOW_STOP
#define ShowStop printf
#else
#define ShowStop( x )
#endif
#if SHOW_TIMING
#define ShowTime printf
#else
#define ShowTime DoNothing
void DoNothing( char* text, ... ) { }
#endif
#if SHOW_ACTION
#define ShowAction printf
#else
#define ShowAction( x )
#endif
#if SHOW_SMALL_STREAM_STATE
#define SmallShowStreamState printf( " %d", whichStream ), printf
#else
#define SmallShowStreamState( x )
#endif
#if SHOW_SMALL_MUSIC_STATE
#define SmallShowMusicState printf
#else
#define SmallShowMusicState( x )
#endif
#if SHOW_SMALL_ACTION
#define SmallShowAction printf
#else
#define SmallShowAction( x )
#endif
#if SHOW_STATE
#define ShowState printf
#else
#define ShowState( x )
#endif
#if DBUGPRINTF
#define Dbug_Printf( x ) printf( x )
#else
#define Dbug_Printf( x )
#endif
// the program running on the EE side will check to see if any of the buffers need to be filled:
volatile unsigned int gEECommand = 0;
#define TRANS_DMA_CH_MUSIC 1
#define TRANS_DMA_CH_STREAM 0
#define _1st 0
#define _2nd 1
#define TRUE 1
#define FALSE 0
unsigned int VCount=0;
unsigned int gThid = 0;
unsigned int gSem = 0;
unsigned int gpIopBuf; // IOP SMEM
struct StreamInfo gStreamInfo[ NUM_STREAMS ];
struct StreamInfo gMusicInfo;
volatile int gUsingStreamDMA = 0;
volatile int gSPUUsingStreamDMA = 0;
// Done on the EE now
//volatile unsigned int gStreamVolPercent = 100;
// Timing test code
#if TEST_PLAY_TIMING
static int test_timing_request_id = 2;
static int test_timing_send_timing_result = FALSE;
static int test_timing_hit_point = FALSE;
#endif // TEST_PLAY_TIMING
u_int GetNAX(int core,int ch);
int OutsideMusicSPULoadBuffer();
int OutsideStreamSPULoadBuffer(int whichStream);
int DownloadMusic(int whichChannel);
int DownloadStream(int whichStream);
void IncMusicBuffer();
void IncStreamBuffer(int whichStream);
void StartMusic();
void StartStream(int whichStream);
int RefreshStreams( int status );
void SendStatus();
static int _AdpcmDmaIntMusic( int, void* );
static int _AdpcmDmaIntStream( int, void* );
#define _L 0
#define _R 1
#define _Lch(x) ((x >> 16) & 0xffff)
#define _Rch(x) (x & 0xffff)
// Timer structure.
typedef struct TimerCtx {
int thread_id;
int timer_id;
int count;
} TimerCtx;
TimerCtx gTimer;
#define ONE_HSCAN 1000*1000/60 // Approx 60 frames per second
/**********************************************************************************
IRQ Interrupt - called, say, once per frame
***********************************************************************************/
int TimerFunc( void *common)
{
TimerCtx *tc = (TimerCtx *) common;
VCount++;
iSignalSema(gSem); // Signal StreamADPCM to be called
return (tc->count);
}
/**********************************************************************************
SetTimer - IRQ Interrupt
***********************************************************************************/
int SetTimer( TimerCtx* timer )
{
struct SysClock clock;
int timer_id;
USec2SysClock (ONE_HSCAN, & clock);
timer->count = clock.low; /* within 32-bit */
// Use sysclock timer
if ((timer_id = AllocHardTimer (TC_SYSCLOCK, 32, 1)) <= 0) {
printf ("Can NOT allocate hard timer ...\n");
return (-1);
}
timer->timer_id = timer_id;
if (SetTimerHandler (timer_id, timer->count,
(void *)TimerFunc, (void *) timer) != KE_OK) {
printf ("Can NOT set timeup timer handler ...\n");
return (-1);
}
if (SetupHardTimer (timer_id, TC_SYSCLOCK, TM_NO_GATE, 1) != KE_OK) {
printf ("Can NOT setup hard timer ...\n");
return (-1);
}
return 0;
}
/**********************************************************************************
startTimer - IRQ Interrupt
***********************************************************************************/
int StartTimer( TimerCtx* timer )
{
if (StartHardTimer (timer->timer_id) != KE_OK)
{
printf ("Can NOT start hard timer ...\n");
return (-1);
}
return 0;
}
// EzADPCM_SDINIT
void AdpcmSdInit( void )
{
sceSdInit (0);
// Disk media: DVD
// Output format: PCM
// Copy guard: normal (one generation recordable / default)
sceSdSetCoreAttr (SD_C_SPDIF_MODE, (SD_SPDIF_MEDIA_DVD |
SD_SPDIF_OUT_PCM |
SD_SPDIF_COPY_NORMAL));
return;
}
void AdpcmSetUpVoice( int core, int vnum, int pSpuBuf )
{
sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_VOLL, 0 );
sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_VOLR, 0 );
sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_PITCH, core == MUSIC_CORE ? DEFAULT_PITCH : STREAM_PITCH( DEFAULT_PITCH ) );
// sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_ADSR1, 0x000f );
// sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_ADSR2, 0x1fc0 );
sceSdSetAddr ( core | ( vnum << 1 ) | SD_VA_SSA, pSpuBuf );
return;
}
// EzADPCM_INIT
int AdpcmInit( int pIopBuf )
{
int i;
if ( gEECommand & PCMSTATUS_INITIALIZED )
{
return ( 0 );
}
if ( gSem == 0 )
{
struct SemaParam sem;
sem.initCount = 0;
sem.maxCount = 1;
sem.attr = AT_THFIFO;
gSem = CreateSema (&sem);
}
if (gThid == 0)
{
struct ThreadParam param;
param.attr = TH_C;
param.entry = RefreshStreams;
param.initPriority = BASE_priority-3;
param.stackSize = 0x800; // was 800
param.option = 0;
gThid = CreateThread (&param);
printf( "EzADPCM: create thread ID= %d\n", gThid );
StartThread (gThid, (u_long) NULL);
}
sceSdSetTransIntrHandler( TRANS_DMA_CH_MUSIC, ( sceSdTransIntrHandler ) _AdpcmDmaIntMusic, ( void * ) &gSem );
sceSdSetTransIntrHandler( TRANS_DMA_CH_STREAM, ( sceSdTransIntrHandler ) _AdpcmDmaIntStream, ( void * ) &gSem );
SetTimer(&gTimer); // Setup IRQ interrupt
StartTimer(&gTimer); // Start IRQ interrupt (calls TimerFunc)
AdpcmSetUpVoice( MUSIC_CORE, MUSIC_L_VOICE, MUSIC_L_SPU_BUF_LOC );
AdpcmSetUpVoice( MUSIC_CORE, MUSIC_R_VOICE, MUSIC_R_SPU_BUF_LOC );
for ( i = 0; i < NUM_STREAMS; i++ )
{
AdpcmSetUpVoice( STREAM_CORE, STREAM_VOICE( i ), STREAM_SPU_BUF_LOC( i ) );
}
memset( gStreamInfo, 0, sizeof( struct StreamInfo ) * NUM_STREAMS );
memset( &gMusicInfo, 0, sizeof( struct StreamInfo ) );
gpIopBuf = pIopBuf;
gEECommand |= PCMSTATUS_INITIALIZED;
//printf( "PCM Irx iop buf %d\n", gpIopBuf );
//Dbug_Printf( "PCM irx is initialized\n" );
return gThid;
}
// EzADPCM_QUIT
void AdpcmQuit( void )
{
DeleteThread (gThid);
gThid = 0;
#if 0
DeleteSema (gSem);
gSem = 0;
#endif
return;
}
// EzADPCM_PRELOADMUSIC
// EzADPCM_PLAYMUSIC
int AdpcmPlayMusic( int size, int preload_only )
{
extern void AdpcmSetMusicVolumeDirect( unsigned int );
if ( gMusicInfo.status != PCM_STATUS_IDLE )
{
ShowAction( "NOTE NOTE NOTE NOTE Can't play music -- music isn't in Idle state.\n" );
return -1;
}
AdpcmSetMusicVolumeDirect( gMusicInfo.volume );
if (preload_only)
{
gMusicInfo.m_preloadMode = TRUE;
}
else
{
gMusicInfo.m_preloadMode = FALSE;
}
gMusicInfo.status = PCM_STATUS_PRELOAD;
gMusicInfo.loadState = MUSIC_LOAD_STATE_IDLE;
gMusicInfo.size = size;
gMusicInfo.m_iopBufLoaded[0] = TRUE;
gMusicInfo.m_iopBufLoaded[1] = FALSE;
gEECommand &= ~PCMSTATUS_NEED_MUSIC_BUFFER_0;
if (size > MUSIC_HALF_IOP_BUFFER_SIZE)
{
gEECommand |= PCMSTATUS_NEED_MUSIC_BUFFER_1;
}
gEECommand |= PCMSTATUS_MUSIC_PLAYING;
ShowAction( "Starting music\n" );
SmallShowAction( "-mb" );
return 0;
}
void AdpcmSetStreamVolume( unsigned int vol, int whichStream );
// EzADPCM_PRELOADMUSIC
// EzADPCM_PLAYSTREAM
int AdpcmPlayStream( int size, int whichStream, unsigned int vol, unsigned int pitch, int preload_only )
{
if ( gStreamInfo[ whichStream ].status != PCM_STATUS_IDLE )
{
printf( "trying to play stream when stream already playing!\n" );
return -1;
}
if (preload_only)
{
gStreamInfo[ whichStream ].m_preloadMode = TRUE;
}
else
{
gStreamInfo[ whichStream ].m_preloadMode = FALSE;
AdpcmSetStreamVolume( vol, whichStream );
gStreamInfo[ whichStream ].pitch = STREAM_PITCH( pitch );
}
// have to make sure it's okay to use the stream DMA first...
gStreamInfo[ whichStream ].status = PCM_STATUS_PRELOAD;
gStreamInfo[ whichStream ].size = size;
gStreamInfo[ whichStream ].m_iopBufLoaded[0] = TRUE;
gStreamInfo[ whichStream ].m_iopBufLoaded[1] = FALSE;
gEECommand &= ~( PCMSTATUS_NEED_STREAM0_BUFFER_0 << whichStream );
if (size > STREAM_HALF_IOP_BUFFER_SIZE)
{
gEECommand |= PCMSTATUS_NEED_STREAM0_BUFFER_1 << whichStream;
}
gStreamInfo[ whichStream ].loadState = STREAM_LOAD_STATE_IDLE;
gEECommand |= ( PCMSTATUS_STREAM_PLAYING( whichStream ) );
ShowAction( "Starting stream\n" );
SmallShowAction( "-sb" );
return 0;
}
// EzADPCM_PLAYPRELOADEDMUSIC
int AdpcmPlayPreloadedMusic()
{
// Tell update loop it is OK to play
gMusicInfo.m_preloadMode = FALSE;
SignalSema(gSem); // Signal RefreshStreams() to be called
ShowAction( "Playing preloaded music\n" );
SmallShowAction( "-ppm" );
return 0;
}
// EzADPCM_PLAYPRELOADEDSTREAM
int AdpcmPlayPreloadedStream(int whichStream, unsigned int vol, unsigned int pitch)
{
AdpcmSetStreamVolume( vol, whichStream );
gStreamInfo[ whichStream ].pitch = STREAM_PITCH( pitch );
// Tell update loop it is OK to play
gStreamInfo[ whichStream ].m_preloadMode = FALSE;
SignalSema(gSem); // Signal RefreshStreams() to be called
ShowAction( "Playing preloaded stream\n" );
SmallShowAction( "-pps" );
return 0;
}
/* internal */
void _AdpcmSetMusicVoiceMute( void )
{
sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1) | SD_VP_VOLL, 0);
sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1) | SD_VP_VOLR, 0);
sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1) | SD_VP_VOLL, 0);
sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1) | SD_VP_VOLR, 0);
return;
}
/* internal */
void _AdpcmSetStreamVoiceMute( int whichVoice )
{
sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichVoice ) << 1) | SD_VP_VOLL, 0);
sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichVoice ) << 1) | SD_VP_VOLR, 0);
return;
}
void StopMusic( void )
{
sceSdSetCoreAttr( MUSIC_CORE | SD_C_IRQ_ENABLE, 0 );
_AdpcmSetMusicVoiceMute( );
sceSdSetSwitch( MUSIC_CORE | SD_S_KOFF, ( 1 << MUSIC_R_VOICE ) | ( 1 << MUSIC_L_VOICE ) );
ShowAction( "Stopping music\n" );
SmallShowAction( "-!sm!" );
gMusicInfo.stop = 0;
gMusicInfo.loadState = MUSIC_LOAD_STATE_IDLE;
//gMusicInfo.m_iopBufLoaded[0] = FALSE; // This should not be necessary
//gMusicInfo.m_iopBufLoaded[1] = FALSE;
gEECommand &= ~( PCMSTATUS_MUSIC_PLAYING | PCMSTATUS_MUSIC_READY | PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 );
gMusicInfo.status = PCM_STATUS_IDLE;
}
void StopStream( int whichStream )
{
sceSdSetCoreAttr( STREAM_CORE | SD_C_IRQ_ENABLE, 0 );
_AdpcmSetStreamVoiceMute( whichStream );
sceSdSetSwitch( STREAM_CORE | SD_S_KOFF, ( 1 << STREAM_VOICE( whichStream ) ) );
ShowAction( "Stopping stream\n" );
SmallShowAction( "-!ss!" );
gStreamInfo[ whichStream ].stop = 0;
gStreamInfo[ whichStream ].loadState = STREAM_LOAD_STATE_IDLE;
//gStreamInfo[ whichStream ].m_iopBufLoaded[0] = FALSE; // This should not be necessary
//gStreamInfo[ whichStream ].m_iopBufLoaded[1] = FALSE;
gEECommand &= ~( ( PCMSTATUS_STREAM0_PLAYING | PCMSTATUS_STREAM0_READY | PCMSTATUS_NEED_STREAM0_BUFFER_0 | PCMSTATUS_NEED_STREAM0_BUFFER_1 ) << whichStream );
gStreamInfo[ whichStream ].status = PCM_STATUS_IDLE;
}
#define NUM_WAIT_CLICKS 10000
// EzADPCM_STOPMUSIC
int AdpcmStopMusic( void )
{
int i;
int j = 0;
if ( gMusicInfo.status != PCM_STATUS_IDLE )
{
ShowStop( "stopmusic" );
gMusicInfo.stop++;
SignalSema(gSem); // Signal RefreshStreams() to be called
// Why the hell are we waiting???? This is VERY lame!
while ( gMusicInfo.status != PCM_STATUS_IDLE )
{
for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
;
if ( j++ > NUM_WAIT_CLICKS )
{
j = 0;
printf( "...Waiting for music stop...\n" );
}
}
ShowStop( "\n" );
return ( 1 );
}
return ( 0 );
}
// EzADPCM_STOPSTREAM
int AdpcmStopStream( int whichStream )
{
int i;
int j = 0;
if ( gStreamInfo[ whichStream ].status != PCM_STATUS_IDLE )
{
ShowStop( "Stop stream" );
gStreamInfo[ whichStream ].stop++;
SignalSema(gSem); // Signal RefreshStreams() to be called
// Why the hell are we waiting???? This is VERY lame!
while ( gStreamInfo[ whichStream ].status != PCM_STATUS_IDLE )
{
for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
;
if ( j++ > NUM_WAIT_CLICKS )
{
printf( "...Waiting for stream %d stop...\n", whichStream );
j = 0;
}
}
ShowStop( "\n" );
SmallShowAction( "-ss" );
ShowAction( "Stopped stream\n" );
return ( 1 );
}
return ( 0 );
}
// EzADPCM_SETMUSICVOL
void AdpcmSetMusicVolume( unsigned int vol )
{
gMusicInfo.volume = vol;
gMusicInfo.volumeSet = 1;
return;
}
// EzADPCM_SETMUSICVOLDIRECT
void AdpcmSetMusicVolumeDirect( unsigned int vol )
{
gMusicInfo.volume = vol;
sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_VOLL, vol );
sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_VOLR, 0 );
sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_VOLL, 0 );
sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_VOLR, vol );
return;
}
// Done on the EE now
//void AdpcmSetStreamGlobVol( unsigned int volPercent )
//{
// gStreamVolPercent = volPercent;
//}
#define PERCENT_MULT ( ( 1 << 16 ) / 100 )
#define PERCENT( x, y ) ( ( ( x ) * ( y ) * PERCENT_MULT ) >> 16 )
// EzADPCM_SETSTREAMVOL
void AdpcmSetStreamVolume( unsigned int vol, int whichStream )
{
gStreamInfo[ whichStream ].volume = vol;
gStreamInfo[ whichStream ].volumeSet = 1;
return;
}
// EzADPCM_SETSTREAMVOLDIRECT
void AdpcmSetStreamVolumeDirect( unsigned int vol, int whichStream )
{
//sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_VOLL, PERCENT( gStreamVolPercent, _Lch( vol ) ) );
//sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_VOLR, PERCENT( gStreamVolPercent, _Rch( vol ) ) );
sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_VOLL, _Lch( vol ) );
sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_VOLR, _Rch( vol ) );
return;
}
// Shouldn't need these unless debugging or something --
// Instead just get gEECommand each frame and act accordingly.
// EzADPCM_GETMUSICSTATUS
unsigned int AdpcmGetMusicStatus( void )
{
return gMusicInfo.status;
}
// EzADPCM_GETSTREAMSTATUS
/*unsigned int AdpcmGetStreamStatus( int whichStream )
{
return gStreamInfo[ whichStream ].status;
} */
static volatile unsigned int sEELastCommand = 0xFFFFFFFF;
// EzADPCM_GETSTATUS
unsigned int AdpcmGetStatus( void )
{
unsigned int temp;
temp = gEECommand;
gEECommand &= ~( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 |
PCMSTATUS_NEED_STREAM0_BUFFER_0 | PCMSTATUS_NEED_STREAM0_BUFFER_1 |
PCMSTATUS_NEED_STREAM1_BUFFER_0 | PCMSTATUS_NEED_STREAM1_BUFFER_1 |
PCMSTATUS_NEED_STREAM2_BUFFER_0 | PCMSTATUS_NEED_STREAM2_BUFFER_1 );
sEELastCommand = gEECommand;
#if SHOW_SMALL_COMMANDS
if ( temp & ( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 |
PCMSTATUS_NEED_STREAM0_BUFFER_0 | PCMSTATUS_NEED_STREAM0_BUFFER_1 |
PCMSTATUS_NEED_STREAM1_BUFFER_0 | PCMSTATUS_NEED_STREAM1_BUFFER_1 |
PCMSTATUS_NEED_STREAM2_BUFFER_0 | PCMSTATUS_NEED_STREAM2_BUFFER_1 ) )
{
ShowSmallCommand( " <_L_>" );
}
#endif
return temp;
}
int AdpcmHasStatusChanged( void )
{
return sEELastCommand != gEECommand;
}
// ================================================================
#define _ADPCM_MARK_START 0x04
#define _ADPCM_MARK_LOOP 0x02
#define _ADPCM_MARK_END 0x01
#define _AdpcmSetMarkFINAL(a,s) { \
*( ( unsigned int * ) ( ( a ) + ( s ) - 16 ) ) = 0; \
*( ( unsigned int * ) ( ( a ) + ( s ) - 12 ) ) = 0; \
*( ( unsigned int * ) ( ( a ) + ( s ) - 8 ) ) = 0; \
*( ( unsigned int * ) ( ( a ) + ( s ) - 4 ) ) = 0; \
*((unsigned char *)((a)+(s)-0x0f)) = (_ADPCM_MARK_LOOP | _ADPCM_MARK_START | _ADPCM_MARK_END); \
FlushDcache (); }
// Start of loop - note that a (address is either the start or mid address of the IOP buffer. s is half the buffer size)
#define _AdpcmSetMarkSTART(a,s) { \
*((unsigned char *)((a)+1)) = \
(_ADPCM_MARK_LOOP | _ADPCM_MARK_START); \
*((unsigned char *)((a)+0x10+1)) = \
_ADPCM_MARK_LOOP; \
*((unsigned char *)((a)+(s)-0x0f)) = \
_ADPCM_MARK_LOOP; \
FlushDcache (); }
// End of loop - note that a (address is either the start or mid address of the IOP buffer. s is half the buffer size)
#define _AdpcmSetMarkEND(a,s) { \
*((unsigned char *)((a)+1)) = \
_ADPCM_MARK_LOOP; \
*((unsigned char *)((a)+0x10+1)) = \
_ADPCM_MARK_LOOP; \
*((unsigned char *)((a)+(s)-0x0f)) = \
(_ADPCM_MARK_LOOP | _ADPCM_MARK_END); \
FlushDcache (); }
// Preload Start marker - note that a (address is the start address of the IOP buffer. s is buffer size)
#define _AdpcmSetMarkSTARTpre(a,s) { \
*((unsigned char *)((a)+1)) = \
(_ADPCM_MARK_LOOP | _ADPCM_MARK_START); \
*((unsigned char *)((a)+0x10+1)) = \
_ADPCM_MARK_LOOP; \
FlushDcache (); }
// Preload End marker - note that a (address is the start address of the IOP buffer. s is buffer size)
#define _AdpcmSetMarkENDpre(a,s) { \
*((unsigned char *)((a)+(s)-0xf)) = \
(_ADPCM_MARK_LOOP | _ADPCM_MARK_END); \
FlushDcache (); }
/* internal */
static int _AdpcmDmaIntMusic(int ch, void *common) // DMA Interrupt -- when transfering data to SPU2 from IOP
{
//Kprintf("Got music IRQ for channel %d\n", ch);
if ( gMusicInfo.status != PCM_STATUS_IDLE )
{
gMusicInfo.m_spuTransDone = TRUE;
return 1;
}
return 0;
}
/* internal */
static int _AdpcmDmaIntStream(int ch, void *common) // DMA Interrupt -- when transfering data to SPU2 from IOP
{
if ( gUsingStreamDMA )
{
int whichStream = gUsingStreamDMA - 1;
//Kprintf("Received Stream DMA 0 callback\n");
if ( gStreamInfo[ whichStream ].status != PCM_STATUS_IDLE )
{
gStreamInfo[ whichStream ].m_spuTransDone = TRUE;
gUsingStreamDMA = 0;
return 1;
}
}
else if (gSPUUsingStreamDMA)
{
gSPUUsingStreamDMA = 0;
//Kprintf("Received SPU DMA 0 callback\n");
}
else
{
//Kprintf("Received outside DMA 0 callback\n");
}
return 0;
}
void UpdateMusic( void )
{
struct StreamInfo *pInfo;
pInfo = &gMusicInfo;
switch ( pInfo->status )
{
case PCM_STATUS_IDLE:
break;
case PCM_STATUS_PRELOAD:
switch ( pInfo->loadState )
{
case ( MUSIC_LOAD_STATE_IDLE ):
{
ShowState( "music preload l\n" );
SmallShowMusicState( "-MPL" );
pInfo->m_spuBufSide = _1st;
pInfo->m_iopBufIndex = _1st;
pInfo->m_iopOffset = 0;
pInfo->remaining = pInfo->size;
// preload left:
if (DownloadMusic(_L))
{
pInfo->loadState++;
}
break;
}
case ( MUSIC_LOAD_STATE_PRELOADING_L ):
{
// Check that transfer is done
if (!pInfo->m_spuTransDone)
{
break;
}
if ( pInfo->stop ) // Don't stop until we know the first CD stream and DMA to SPU have finished
{
StopMusic( );
break;
}
ShowState( "music preload r\n" );
SmallShowMusicState( "-MPR" );
// preload right:
DownloadMusic(_R);
pInfo->loadState++;
break;
}
case ( MUSIC_LOAD_STATE_PRELOADING_R ):
{
// Check that transfer is done
if (!pInfo->m_spuTransDone)
{
break;
}
if ( pInfo->stop ) // Don't stop until we the DMA to SPU has finished
{
StopMusic( );
break;
}
// This will let the EE know that we are ready to play
gEECommand |= PCMSTATUS_MUSIC_READY;
// Now check if we are in a preload_only mode
if (pInfo->m_preloadMode)
{
// We need to wait until we get a message from the EE to start
break;
}
ShowState( "Music starting -- waiting for IRQ\n" );
SmallShowMusicState( " -MSIRQ" );
pInfo->status = PCM_STATUS_RUNNING;
IncMusicBuffer();
// Play music
StartMusic();
#if TEST_PLAY_TIMING
if (test_timing_send_timing_result)
{
test_timing_hit_point = TRUE;
}
#endif // TEST_PLAY_TIMING
pInfo->loadState++;
break;
}
}
break;
case PCM_STATUS_RUNNING:
pInfo->m_spuCurAddr = GetNAX( MUSIC_CORE, MUSIC_L_VOICE );
switch ( pInfo->loadState )
{
case ( MUSIC_LOAD_STATE_WAITING_FOR_REFRESH ):
{
if ( pInfo->stop )
{
//pInfo->status = PCM_STATUS_READY_TO_STOP;
StopMusic( );
return;
}
// Wait for SPU buffer cross
if (!OutsideMusicSPULoadBuffer())
{
break;
}
ShowState( "Music Running -- Wait For Last Load\n" );
SmallShowStreamState( "-MRWLL" );
// Change state and go immediately into it
pInfo->loadState++;
}
case ( MUSIC_LOAD_STATE_WAITING_FOR_LAST_LOAD ):
{
if ( pInfo->stop )
{
StopMusic( );
break;
}
// After we got this far, we've crossed the SPU buffer and we
// know we need to download. But if the new IOP buffer isn't
// loaded and download before we cross SPU buffers AGAIN, then
// we know the stream has failed. Time to panic!
if (!OutsideMusicSPULoadBuffer())
{
if (!(gEECommand & PCMSTATUS_PANIC))
{
// Print only once
Kprintf("ERROR: Music did not load data in time. In SPU buffer %d and pos %x\n", pInfo->m_spuBufSide, pInfo->m_spuCurAddr - MUSIC_L_SPU_BUF_LOC);
}
gEECommand |= PCMSTATUS_PANIC;
break;
}
ShowState( "Music Running -- Loading L\n" );
SmallShowMusicState( "-MRLL" );
// load in left side:
if (DownloadMusic(_L))
{
pInfo->loadState++;
}
break;
}
case ( MUSIC_LOAD_STATE_LOADING_L ):
{
// Check that transfer is done
if (!pInfo->m_spuTransDone)
{
break;
}
//Kprintf( "Music Running -- Loading R at offset %x and address %x\n", pInfo->m_iopOffset, gpIopBuf + MUSIC_R_IOP_OFFSET + pInfo->m_iopOffset );
ShowState( "Music Running -- Loading R\n" );
SmallShowMusicState( "-MRLR" );
// load in right side:
DownloadMusic(_R);
pInfo->loadState++;
break;
}
case ( MUSIC_LOAD_STATE_LOADING_R ):
{
// Check that transfer is done
if (!pInfo->m_spuTransDone)
{
break;
}
ShowState( "Music Running -- Waiting for IRQ\n" );
SmallShowMusicState( "-MRIRQ" );
IncMusicBuffer();
pInfo->loadState = MUSIC_LOAD_STATE_WAITING_FOR_REFRESH;
break;
}
default:
printf( "Unknown music loading state %d\n", pInfo->loadState );
break;
}
break;
case PCM_STATUS_TERMINATE:
pInfo->m_spuCurAddr = GetNAX( MUSIC_CORE, MUSIC_L_VOICE );
if ( !pInfo->stop && ( ( pInfo->m_spuCurAddr > pInfo->m_spuEndAddr ) || ( pInfo->m_spuCurAddr <= pInfo->m_spuEndAddr - 16 ) ) )
{
break;
}
ShowState( "Music at end\n" );
case PCM_STATUS_READY_TO_STOP:
ShowState( "Music terminate\n" );
SmallShowMusicState( "-MT!!!" );
StopMusic( );
break;
default:
printf( "unknown music status in pcm irx!!!\n" );
break;
}
}
void UpdateStream( int whichStream )
{
struct StreamInfo *pInfo;
pInfo = &gStreamInfo[ whichStream ];
switch ( pInfo->status )
{
case PCM_STATUS_IDLE:
break;
case PCM_STATUS_PRELOAD:
switch ( pInfo->loadState )
{
case ( STREAM_LOAD_STATE_IDLE ):
{
ShowState( "stream preload l\n" );
SmallShowStreamState( "-SPL" );
pInfo->m_spuBufSide = _1st;
pInfo->m_iopBufIndex = _1st;
pInfo->m_iopOffset = 0;
pInfo->remaining = pInfo->size;
if (DownloadStream(whichStream))
{
pInfo->loadState++;
}
break;
}
case ( STREAM_LOAD_STATE_PRELOADING ):
{
ShowState( "Stream starting -- waiting for IRQ\n" );
SmallShowStreamState( "-SSIRQ" );
// Check that transfer is done
if (!pInfo->m_spuTransDone)
{
break;
}
if ( pInfo->stop ) // Don't stop until we know the first CD stream and DMA to SPU have finished
{
StopStream( whichStream );
break;
}
// This will let the EE know that we are ready to play
gEECommand |= PCMSTATUS_STREAM_READY(whichStream);
// Now check if we are in a preload_only mode
if (pInfo->m_preloadMode)
{
// We need to wait until we get a message from the EE to start
break;
}
pInfo->status = PCM_STATUS_RUNNING;
IncStreamBuffer(whichStream);
// Play the stream
StartStream(whichStream);
//Kprintf("Started playing stream %d; paused = %d; volume = %x\n", whichStream, gStreamInfo[ whichStream ].paused, pInfo->volume);
pInfo->loadState++;
break;
}
}
break;
case PCM_STATUS_RUNNING:
pInfo->m_spuCurAddr = GetNAX( STREAM_CORE, STREAM_VOICE( whichStream ) );
switch ( pInfo->loadState )
{
case ( STREAM_LOAD_STATE_WAITING_FOR_REFRESH ):
{
if ( pInfo->stop )
{
//pInfo->status = PCM_STATUS_READY_TO_STOP;
StopStream( whichStream );
return;
}
// Wait for SPU buffer cross
if (!OutsideStreamSPULoadBuffer(whichStream))
{
break;
}
ShowState( "Stream Running -- Wait For Last Load\n" );
SmallShowStreamState( "-SRWLL" );
// Inc state and start it directly
pInfo->loadState++;
}
case ( STREAM_LOAD_STATE_WAITING_FOR_LAST_LOAD ):
{
if ( pInfo->stop )
{
StopStream( whichStream );
break;
}
// After we got this far, we've crossed the SPU buffer and we
// know we need to download. But if the new IOP buffer isn't
// loaded and download before we cross SPU buffers AGAIN, then
// we know the stream has failed. Time to panic!
if (!OutsideStreamSPULoadBuffer(whichStream))
{
if (!(gEECommand & PCMSTATUS_PANIC))
{
// Print only once
Kprintf("ERROR: Stream %d did not load data in time. In SPU buffer %d and pos %x\n", whichStream, pInfo->m_spuBufSide, pInfo->m_spuCurAddr - STREAM_SPU_BUF_LOC( whichStream ));
}
gEECommand |= PCMSTATUS_PANIC;
break;
}
ShowState( "Stream Running -- Loading\n" );
SmallShowStreamState( "-SRL" );
if (DownloadStream(whichStream))
{
pInfo->loadState++;
}
break;
}
case ( STREAM_LOAD_STATE_LOADING ):
{
// Check that transfer is done
if (!pInfo->m_spuTransDone)
{
break;
}
ShowState( "Stream Running -- Waiting for IRQ\n" );
SmallShowStreamState( "-SRIRQ" );
IncStreamBuffer(whichStream);
pInfo->loadState = STREAM_LOAD_STATE_WAITING_FOR_REFRESH;
break;
}
default:
printf( "Unknown Stream loading state %d\n", pInfo->loadState );
break;
}
break;
case PCM_STATUS_TERMINATE:
pInfo->m_spuCurAddr = GetNAX( STREAM_CORE, STREAM_VOICE( whichStream ) );
if ( !pInfo->stop && ( ( pInfo->m_spuCurAddr > pInfo->m_spuEndAddr ) || ( pInfo->m_spuCurAddr <= pInfo->m_spuEndAddr - 16 ) ) )
{
break;
}
case PCM_STATUS_READY_TO_STOP:
if ( gUsingStreamDMA != (whichStream + 1) )
{
ShowState( "Stream terminate\n" );
SmallShowStreamState( "-ST!!!" );
StopStream( whichStream );
}
break;
default:
printf( "unknown stream status in pcm irx!!!\n" );
break;
}
}
// Reserve Stream DMA channel
int LockDMA( int whichStream )
{
if ( gSPUUsingStreamDMA )
{
SmallShowStreamState( " -|spu_no" );
//Kprintf("******* Can't lock DMA for Stream %d because of SPU\n", whichStream);
return FALSE;
}
if ( gUsingStreamDMA )
{
SmallShowStreamState( " -|no" );
//Kprintf("******* Can't lock DMA for Stream %d\n", whichStream);
return FALSE;
}
else
{
SmallShowStreamState( " -|ok" );
gUsingStreamDMA = 1 + whichStream;
//Kprintf("Locked DMA for Stream %d\n", whichStream);
return TRUE;
}
}
/**********************************************************************************
GetNAX
Get current NAX (Address where channel is playing in SPU RAM)
Code must check the NAX 3 times to decide which result is correct.
***********************************************************************************/
u_int GetNAX(int core,int ch)
{
u_int pos,pos2,pos3;
ch <<= 1;
while(1)
{
pos=sceSdGetAddr(core|ch|SD_VA_NAX);
pos2=sceSdGetAddr(core|ch|SD_VA_NAX);
pos3=sceSdGetAddr(core|ch|SD_VA_NAX);
if (pos == pos2)
return(pos);
else if (pos2 == pos3)
return(pos2);
}
}
#if CHECK_TRANSFER_INTEGRITY
void CheckTransfer( int dmaCh )
{
if ( !sceSdVoiceTransStatus( dmaCh, SD_TRANS_STATUS_CHECK ) )
{
int i;
printf( "waiting for voicetrans" );
while ( !sceSdVoiceTransStatus( dmaCh, SD_TRANS_STATUS_CHECK ) )
{
printf( "." );
for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
;
}
printf( "\n" );
}
}
#endif
// Checks to see if current SPU playing address is outside the loading buffer
int OutsideMusicSPULoadBuffer()
{
struct StreamInfo *pInfo;
pInfo = &gMusicInfo;
if ( pInfo->m_spuBufSide == _1st )
{
if ( pInfo->m_spuCurAddr > ( MUSIC_L_SPU_BUF_LOC + SB_BUF_HALF ) )
{
#if CHECK_TRANSFER_INTEGRITY
CheckTransfer( TRANS_DMA_CH_MUSIC );
#endif
return TRUE;
//Kprintf("music position past end for SPU buffer %d\n", pInfo->bufSide);
}
}
else
{
if ( pInfo->m_spuCurAddr < ( MUSIC_L_SPU_BUF_LOC + SB_BUF_HALF ) )
{
#if CHECK_TRANSFER_INTEGRITY
CheckTransfer( TRANS_DMA_CH_MUSIC );
#endif
return TRUE;
//Kprintf("music position past end for SPU buffer %d\n", pInfo->bufSide);
}
}
return FALSE;
}
// Checks to see if current SPU playing address is outside the loading buffer
int OutsideStreamSPULoadBuffer(int whichStream)
{
struct StreamInfo *pInfo;
pInfo = &gStreamInfo[ whichStream ];
if ( pInfo->m_spuBufSide == _1st )
{
if ( pInfo->m_spuCurAddr > ( STREAM_SPU_BUF_LOC( whichStream ) + SB_BUF_HALF ) )
{
#if CHECK_TRANSFER_INTEGRITY
CheckTransfer( TRANS_DMA_CH_STREAM );
#endif
return TRUE;
}
}
else
{
if ( pInfo->m_spuCurAddr < ( STREAM_SPU_BUF_LOC( whichStream ) + SB_BUF_HALF ) )
{
#if CHECK_TRANSFER_INTEGRITY
CheckTransfer( TRANS_DMA_CH_STREAM );
#endif
return TRUE;
}
}
return FALSE;
}
// Downloads music data from the IOP to a SPU buffer
int DownloadMusic(int whichChannel)
{
int music_iop_offset;
int music_spu_addr;
struct StreamInfo *pInfo;
pInfo = &gMusicInfo;
if (whichChannel == _L)
{
//int iopBufferIndex = (pInfo->m_iopOffset < MUSIC_HALF_IOP_BUFFER_SIZE) ? 0 : 1;
if (!pInfo->m_iopBufLoaded[pInfo->m_iopBufIndex])
{
//Kprintf("****** ERROR: Music buffer #%d not loaded.\n", pInfo->m_iopBufIndex);
return FALSE;
}
pInfo->m_spuTransSize = SB_BUF_HALF;
pInfo->remaining -= SB_BUF_HALF;
if ( pInfo->remaining < 0 )
{
pInfo->m_spuTransSize += pInfo->remaining;
}
music_iop_offset = MUSIC_L_IOP_OFFSET;
music_spu_addr = MUSIC_L_SPU_BUF_LOC;
}
else
{
music_iop_offset = MUSIC_R_IOP_OFFSET;
music_spu_addr = MUSIC_R_SPU_BUF_LOC;
}
// load in a side:
if ( pInfo->m_spuBufSide == _1st )
{
_AdpcmSetMarkSTART( gpIopBuf + music_iop_offset + pInfo->m_iopOffset, pInfo->m_spuTransSize );
}
if ( pInfo->remaining <= 0 )
{
_AdpcmSetMarkFINAL( gpIopBuf + music_iop_offset + pInfo->m_iopOffset, pInfo->m_spuTransSize );
pInfo->m_spuEndAddr = MUSIC_L_SPU_BUF_LOC + pInfo->m_spuTransSize + SB_BUF_HALF * pInfo->m_spuBufSide;
}
else if ( pInfo->m_spuBufSide == _2nd )
{
_AdpcmSetMarkEND( gpIopBuf + music_iop_offset + pInfo->m_iopOffset, pInfo->m_spuTransSize );
}
pInfo->m_spuTransDone = FALSE;
sceSdVoiceTrans( TRANS_DMA_CH_MUSIC, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
(unsigned char *) gpIopBuf + music_iop_offset + pInfo->m_iopOffset,
( music_spu_addr + SB_BUF_HALF * pInfo->m_spuBufSide ), pInfo->m_spuTransSize );
//Kprintf("Downloading music channel %d from IOP %x to SPU %x of size %d\n", whichChannel, gpIopBuf + music_iop_offset + pInfo->m_iopOffset,
// ( music_spu_addr + SB_BUF_HALF * pInfo->m_spuBufSide ), pInfo->m_spuTransSize );
return TRUE;
}
// Downloads audio stream data from the IOP to a SPU buffer
int DownloadStream(int whichStream)
{
struct StreamInfo *pInfo;
pInfo = &gStreamInfo[ whichStream ];
//iopBufferIndex = (pInfo->m_iopOffset < STREAM_HALF_IOP_BUFFER_SIZE) ? 0 : 1;
if (!pInfo->m_iopBufLoaded[pInfo->m_iopBufIndex])
{
//Kprintf("****** ERROR: Stream %d buffer #%d not loaded.\n", whichStream, pInfo->m_iopBufIndex);
return FALSE;
}
if (!LockDMA(whichStream))
{
return FALSE;
}
pInfo->m_spuTransSize = SB_BUF_HALF;
pInfo->remaining -= SB_BUF_HALF;
if ( pInfo->remaining < 0 )
{
pInfo->m_spuTransSize += pInfo->remaining;
}
//Kprintf( "Stream: IOP offset %d, transfer %d, remaining %d\n", pInfo->m_iopOffset, pInfo->m_spuTransSize, pInfo->remaining);
// load in left side:
if ( pInfo->m_spuBufSide == _1st )
{
_AdpcmSetMarkSTART( gpIopBuf + STREAM_IOP_OFFSET( whichStream ) + pInfo->m_iopOffset, pInfo->m_spuTransSize );
}
if ( pInfo->remaining <= 0 )
{
_AdpcmSetMarkFINAL( gpIopBuf + STREAM_IOP_OFFSET( whichStream ) + pInfo->m_iopOffset, pInfo->m_spuTransSize );
pInfo->m_spuEndAddr = STREAM_SPU_BUF_LOC( whichStream ) + pInfo->m_spuTransSize + SB_BUF_HALF * pInfo->m_spuBufSide;
}
else if ( pInfo->m_spuBufSide == _2nd )
{
_AdpcmSetMarkEND( gpIopBuf + STREAM_IOP_OFFSET( whichStream ) + pInfo->m_iopOffset, pInfo->m_spuTransSize );
}
pInfo->m_spuTransDone = FALSE;
sceSdVoiceTrans( TRANS_DMA_CH_STREAM, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
(unsigned char *) gpIopBuf + STREAM_IOP_OFFSET( whichStream ) + pInfo->m_iopOffset,
( STREAM_SPU_BUF_LOC( whichStream ) + SB_BUF_HALF * pInfo->m_spuBufSide ), pInfo->m_spuTransSize );
//Kprintf("Downloaded Stream %d buffer #%d\n", whichStream, iopBufferIndex);
return TRUE;
}
void IncMusicBuffer()
{
struct StreamInfo *pInfo;
pInfo = &gMusicInfo;
pInfo->m_iopOffset += SB_BUF_HALF;
pInfo->m_spuBufSide = ( pInfo->m_spuBufSide == _1st ) ? _2nd : _1st;
if ( pInfo->remaining <= 0 )
{
// the vag data up to the end should contain all zeros,
// so it doesn't matter that the interrupt won't happen until the end.
// stop the music when it gets to the end:
pInfo->status = PCM_STATUS_TERMINATE;
ShowState( "Music last buffer\n" );
}
else
{
if ( (pInfo->m_iopBufIndex == _1st) && (pInfo->m_iopOffset >= MUSIC_HALF_IOP_BUFFER_SIZE) )
{
//Dbug_Printf( "music move over\n" );
pInfo->m_iopOffset += MUSIC_HALF_IOP_BUFFER_SIZE; // move over by half the size since L/R load in together...
pInfo->m_iopBufIndex = _2nd;
if ( pInfo->remaining > MUSIC_HALF_IOP_BUFFER_SIZE )
{
gEECommand |= PCMSTATUS_NEED_MUSIC_BUFFER_0;
pInfo->m_iopBufLoaded[0] = FALSE;
}
}
else if ( (pInfo->m_iopBufIndex == _2nd) && ( pInfo->m_iopOffset >= ( MUSIC_IOP_BUFFER_SIZE + MUSIC_HALF_IOP_BUFFER_SIZE ) ) )
{
//Dbug_Printf( "music move back\n" );
pInfo->m_iopOffset = 0;
pInfo->m_iopBufIndex = _1st;
if ( pInfo->remaining > MUSIC_HALF_IOP_BUFFER_SIZE )
{
gEECommand |= PCMSTATUS_NEED_MUSIC_BUFFER_1;
pInfo->m_iopBufLoaded[1] = FALSE;
}
}
}
}
void IncStreamBuffer(int whichStream)
{
struct StreamInfo *pInfo;
pInfo = &gStreamInfo[ whichStream ];
pInfo->m_iopOffset += SB_BUF_HALF;
pInfo->m_spuBufSide = ( pInfo->m_spuBufSide == _1st ) ? _2nd : _1st;
if ( pInfo->remaining <= 0 )
{
// the vag data up to the end should contain all zeros,
// so it doesn't matter that the interrupt won't happen until the end.
// stop the stream when it gets to the end:
pInfo->status = PCM_STATUS_TERMINATE;
}
else
{
// Must check if it has crossed from one IOP buffer to the other
if ( (pInfo->m_iopBufIndex == _1st) && (pInfo->m_iopOffset >= STREAM_HALF_IOP_BUFFER_SIZE) )
{
SmallShowStreamState( "-SMO" );
pInfo->m_iopBufIndex = _2nd;
if ( pInfo->remaining > STREAM_HALF_IOP_BUFFER_SIZE )
{
gEECommand |= ( PCMSTATUS_NEED_STREAM0_BUFFER_0 << whichStream );
pInfo->m_iopBufLoaded[0] = FALSE;
//Kprintf("Asking for Stream %d buffer #%d\n", whichStream, 0);
}
}
else if ( (pInfo->m_iopBufIndex == _2nd) && pInfo->m_iopOffset >= STREAM_IOP_BUFFER_SIZE )
{
SmallShowStreamState( "-SMB" );
pInfo->m_iopOffset -= STREAM_IOP_BUFFER_SIZE;
pInfo->m_iopBufIndex = _1st;
if ( pInfo->remaining > STREAM_HALF_IOP_BUFFER_SIZE )
{
gEECommand |= ( PCMSTATUS_NEED_STREAM0_BUFFER_1 << whichStream );
pInfo->m_iopBufLoaded[1] = FALSE;
//Kprintf("Asking for Stream %d buffer #%d\n", whichStream, 1);
}
}
}
}
// Start the music that is already preloaded
void StartMusic()
{
AdpcmSetMusicVolumeDirect( gMusicInfo.volume );
sceSdSetSwitch( MUSIC_CORE | SD_S_KON, ( ( 1 << MUSIC_R_VOICE ) | ( 1 << MUSIC_L_VOICE ) ) );
}
// Start the stream that is already preloaded
void StartStream(int whichStream)
{
AdpcmSetStreamVolumeDirect( gStreamInfo[ whichStream ].volume, whichStream );
// Here's the bug! This was causing a recently paused stream to un-pause...
if ( !gStreamInfo[ whichStream ].paused )
{
sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_PITCH, gStreamInfo[ whichStream ].pitch );
}
sceSdSetSwitch( STREAM_CORE | SD_S_KON, 1 << STREAM_VOICE( whichStream ) );
//Kprintf("Stream %d using SPU memory (%6x-%6x)\n", whichStream, STREAM_SPU_BUF_LOC( whichStream ), STREAM_SPU_BUF_LOC( whichStream ) + SB_BUF_SIZE - 1);
}
/* internal */
int RefreshStreams( int status )
{
int i;
while ( 1 )
{
WaitSema(gSem);
// Update everything
for ( i = 0; i < NUM_STREAMS; i++ )
{
UpdateStream( i );
}
UpdateMusic( );
if ( gMusicInfo.volumeSet )
{
gMusicInfo.volumeSet = 0;
AdpcmSetMusicVolumeDirect( gMusicInfo.volume );
}
for ( i = 0; i < NUM_STREAMS; i++ )
{
if ( gStreamInfo[ i ].volumeSet )
{
gStreamInfo[ i ].volumeSet = 0;
AdpcmSetStreamVolumeDirect( gStreamInfo[ i ].volume, i );
}
}
SendStatus();
}
return 0;
}
void _ShowStreamInfo( struct StreamInfo *pInfo )
{
printf( "\nStream Info:\n" );
printf( "paused %d\n", pInfo->paused );
printf( "m_spuEndAddr %d\n", pInfo->m_spuEndAddr );
printf( "m_spuBufSide %d\n", pInfo->m_spuBufSide );
printf( "m_spuTransSize %d\n", pInfo->m_spuTransSize );
printf( "loadState %d\n", pInfo->loadState );
printf( "volume %d\n", pInfo->volume );
printf( "volumeSet %d\n", pInfo->volumeSet );
printf( "stop %d\n", pInfo->stop );
printf( "status %d\n", pInfo->status );
printf( "size %d\n", pInfo->size );
printf( "remaining %d\n", pInfo->remaining );
printf( "m_iopOffset %d\n", pInfo->m_iopOffset );
printf( "pitch %d\n", pInfo->pitch );
printf( "eecom %d\n", gEECommand );
}
void PauseStream( int whichStream, int pause )
{
ShowStreamInfo( &gStreamInfo[ whichStream ] );
if ( pause )
{
if ( !gStreamInfo[ whichStream ].paused )
{
sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_PITCH, 0 );
gStreamInfo[ whichStream ].paused = 1;
ShowAction( "Pausing stream\n" );
SmallShowAction( "-ps" );
}
}
else
{
if ( gStreamInfo[ whichStream ].paused )
{
sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_PITCH, gStreamInfo[ whichStream ].pitch );
gStreamInfo[ whichStream ].paused = 0;
ShowAction( "Unpausing stream\n" );
SmallShowAction( "-ups" );
}
}
}
volatile int ret = 0;
#define U_DATA( x ) ( *( ( unsigned int * ) data_ + x ) )
#define DATA( x ) ( *( ( int * ) data_ + x ) )
static void *dispatch( unsigned int command, void *data_, int size )
{
int ch;
// printf( "size %d", size );
// printf("# dispatch [%04x] %x, %x, %x, %x\n", command, *((int*) data_ + 0), *((int*) data_ + 1), *((int*) data_ + 2), *((int*) data_ + 3));
ch = command & EzADPCM_CH_MASK;
switch ( command & EzADPCM_COMMAND_MASK )
{
case EzADPCM_INIT:
ret = AdpcmInit( DATA( 0 ) ); // iop buffer pointer
break;
case EzADPCM_QUIT:
AdpcmQuit( );
break;
case EzADPCM_PRELOADMUSIC:
ret = AdpcmPlayMusic( DATA( 0 ), TRUE ); // size of the entire PCM data in the file
break;
case EzADPCM_PRELOADSTREAM:
ret = AdpcmPlayStream( DATA( 0 ), ch, 0, 0, TRUE); // size of the entire PCM data in the file
break;
case EzADPCM_PLAYPRELOADEDMUSIC:
ret = AdpcmPlayPreloadedMusic();
break;
case EzADPCM_PLAYPRELOADEDSTREAM:
ret = AdpcmPlayPreloadedStream(ch, DATA(0), DATA(1));
break;
case EzADPCM_PLAYMUSIC:
ret = AdpcmPlayMusic( DATA( 0 ), FALSE ); // size of the entire PCM data in the file
break;
case EzADPCM_PLAYSTREAM:
ret = AdpcmPlayStream( DATA( 0 ), ch, DATA( 1 ), DATA( 2 ), FALSE ); // size of the entire PCM data in the file
break;
case EzADPCM_PAUSEMUSIC:
if ( DATA( 0 ) )
{
if ( !gMusicInfo.paused )
{
sceSdSetParam( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_PITCH, 0 );
sceSdSetParam( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_PITCH, 0 );
gMusicInfo.paused = 1;
ShowAction( "Pausing music\n" );
SmallShowAction( "-pm" );
}
}
else
{
if ( gMusicInfo.paused )
{
sceSdSetParam( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_PITCH, DEFAULT_PITCH );
sceSdSetParam( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_PITCH, DEFAULT_PITCH );
gMusicInfo.paused = 0;
ShowAction( "Unpausing music\n" );
SmallShowAction( "-upm" );
}
}
break;
case EzADPCM_PAUSESTREAMS:
{
int whichStream;
for ( whichStream = 0; whichStream < NUM_STREAMS; whichStream++ )
{
PauseStream( whichStream, DATA( 0 ) );
}
break;
}
case EzADPCM_PAUSESTREAM:
PauseStream( ch, DATA( 0 ) );
break;
case EzADPCM_STOPMUSIC:
ret = AdpcmStopMusic( );
break;
case EzADPCM_STOPSTREAMS:
{
int i;
for ( i = 0; i < NUM_STREAMS; i++ )
{
ret = AdpcmStopStream( i );
}
break;
}
case EzADPCM_STOPSTREAM:
if ( NUM_STREAMS > DATA( 0 ) )
ret = AdpcmStopStream( DATA( 0 ) );
break;
case EzADPCM_SETMUSICVOL:
AdpcmSetMusicVolume( U_DATA( 0 ) );
break;
case EzADPCM_SETSTREAMVOLANDPITCH:
sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( ch ) << 1 ) | SD_VP_PITCH, ( U_DATA( 1 ) / 2 ) );
// Adjust for pitch --> 22,050 hz when standard is 48000
gStreamInfo[ ch ].pitch = STREAM_PITCH( U_DATA( 1 ) );
// intentional fall through:
case EzADPCM_SETSTREAMVOL:
AdpcmSetStreamVolume( U_DATA( 0 ), ch );
break;
#if 0
case EzADPCM_SETSTREAMGLOBVOL:
AdpcmSetStreamGlobVol( U_DATA( 0 ) );
break;
case EzADPCM_GETMUSICSTATUS:
ret = AdpcmGetMusicStatus( );
break;
#endif
case EzADPCM_SDINIT:
AdpcmSdInit( );
break;
default:
ERROR (("EzADPCM driver error: unknown command %d \n", DATA( 0 ) ) );
break;
}
//printf( "! return value = %x \n", ret );
return (void*)(&ret);
}
// SifCmd variables
//static sceSifCmdData cmdbuffer[NUM_COMMAND_HANDLERS];
unsigned int gCmdSem;
// Note these can be changed in an interrupt
#define NUM_STREAM_REQUESTS (10)
static volatile SStreamRequestPacket StreamRequestArray[NUM_STREAM_REQUESTS];
static volatile int FirstStreamRequest; // Interrupt only reads this value.
static volatile int FreeStreamRequest; // This is the main variable that changes in the interrupt
static SSifCmdStreamResultPacket StreamResult;
void request_callback(void *p, void *q);
void load_status_callback(void *p, void *q);
int sce_adpcm_loop( void )
{
int oldStat;
int last_cmd_id = -1;
// Create semaphore to signal command came in
struct SemaParam sem;
sem.initCount = 0;
sem.maxCount = 1;
sem.attr = AT_THFIFO;
gCmdSem = CreateSema (&sem);
// Init the stream queue
FirstStreamRequest = 0;
FreeStreamRequest = 0;
sceSifInitRpc(0);
// set local buffer & functions
CpuSuspendIntr(&oldStat);
// SIFCMD
// No longer need to call sceSifSetCmdBuffer() since we share it with fileio.irx
//sceSifSetCmdBuffer( &cmdbuffer[0], NUM_COMMAND_HANDLERS);
sceSifAddCmdHandler(STREAM_REQUEST_COMMAND, (void *) request_callback, NULL );
sceSifAddCmdHandler(STREAM_LOAD_STATUS_COMMAND, (void *) load_status_callback, NULL );
CpuResumeIntr(oldStat);
// The loop
while (1) {
//printf("waiting for pcm command semaphore\n");
WaitSema(gCmdSem); // Get signal from callback
//printf("got pcm command semaphore\n");
// Note that FreeStreamRequest can change in the interrupt at any time
// Also, FirstStreamRequest is examined in the interrupt, but just to make sure we don't overflow the buffer
//Dbg_Assert(FreeStreamRequest != FirstStreamRequest);
while (FreeStreamRequest != FirstStreamRequest)
{
int *p_ret;
volatile SStreamRequestPacket *p_request = &(StreamRequestArray[FirstStreamRequest]);
#if TEST_PLAY_TIMING
if (p_request->m_request.m_command == EzADPCM_PLAYPRELOADEDMUSIC)
{
test_timing_request_id = p_request->m_request_id;
test_timing_send_timing_result = TRUE;
test_timing_hit_point = FALSE;
}
#endif // TEST_PLAY_TIMING
//printf("EzPcm: got request id %d with command %x\n", p_request->m_request_id, p_request->m_request.m_command);
p_ret = dispatch(p_request->m_request.m_command, (void *) p_request->m_request.m_param, 0);
// Send the result back
if (last_cmd_id >= 0) // Wait for previous send to complete (it should already be done, though)
{
while(sceSifDmaStat(last_cmd_id) >= 0)
printf("Waiting for PCM DMA\n");
}
// Gotta copy the id into SStreamRequest
StreamResult.m_header.opt = p_request->m_request_id; // Copy id
StreamResult.m_return_value = *p_ret;
StreamResult.m_status_flags = AdpcmGetStatus();
last_cmd_id = sceSifSendCmd(STREAM_RESULT_COMMAND, &StreamResult, sizeof(StreamResult), 0, 0, 0);
// Increment request index; Note that interrupt can look at this
FirstStreamRequest = (FirstStreamRequest + 1) % NUM_STREAM_REQUESTS;
}
}
return 0;
}
void request_callback(void *p, void *q)
{
SSifCmdStreamRequestPacket *h = (SSifCmdStreamRequestPacket *) p;
// Check to make sure we can add
int nextFreeReq = (FreeStreamRequest + 1) % NUM_STREAM_REQUESTS;
if (nextFreeReq == FirstStreamRequest)
{
// We can't allow a request to be ignored. We must abort
//Dbg_Assert(0);
}
// Copy the request into the reuest queue
StreamRequestArray[FreeStreamRequest].m_request = h->m_request;
StreamRequestArray[FreeStreamRequest].m_request_id = h->m_header.opt;
FreeStreamRequest = nextFreeReq;
// And wake up the dispatch thread
iSignalSema(gCmdSem);
return;
}
void load_status_callback(void *p, void *q)
{
SSifCmdStreamLoadStatusPacket *h = (SSifCmdStreamLoadStatusPacket *) p;
// Mark buffer as loaded
if (h->m_stream_num == -1)
{
//Kprintf("Music buffer #%d finished loading.\n", h->m_buffer_num);
gMusicInfo.m_iopBufLoaded[h->m_buffer_num] = TRUE;
}
else
{
//Kprintf("Stream %d buffer #%d finished loading.\n", h->m_stream_num, h->m_buffer_num);
gStreamInfo[h->m_stream_num].m_iopBufLoaded[h->m_buffer_num] = TRUE;
}
}
void SendStatus()
{
static SSifCmdStreamStatusPacket StreamStatus;
#if TEST_PLAY_TIMING
if (test_timing_send_timing_result && test_timing_hit_point)
{
sEELastCommand = 0xFFFFFFFF;
StreamStatus.m_header.opt = test_timing_request_id;
test_timing_send_timing_result = FALSE;
}
else
{
StreamStatus.m_header.opt = 0;
}
#endif // TEST_PLAY_TIMING
// Only send updates when it has changed
if (AdpcmHasStatusChanged())
{
StreamStatus.m_status_flags = AdpcmGetStatus();
sceSifSendCmd(STREAM_STATUS_COMMAND, &StreamStatus, sizeof(StreamStatus), 0, 0, 0);
}
}
/* ----------------------------------------------------------------
* End on File
* ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */