/* Vag streaming into SPU2 -- from sony sample -- matt may 2001 */ #include #include #include #include #include #include #include #include #include #include #include #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 (¶m); 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 */