// Streaming music off the cd... #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // temp for debugging ghost voice: #include #include #include // temp for stream sim Rnd( ) call: #include #include "p_music.h" #define TEST_MOTHERFUCKING_CD 0 // Doubt this even works now #define TEST_PLAY_TIMING 0 namespace Pcm { #define MAX_VOL ( 0x3fff ) #define NORMAL_VOL ( 0x3fff >> 1 ) #define MUSIC_PRIORITY ( DEFAULT_ASYNC_PRIORITY - 20 ) #define STREAM_PRIORITY ( DEFAULT_ASYNC_PRIORITY - 10 ) // The lower the number, the higher the priority int gIopBuffer = 0; int gNonAllignedIopBuffer = 0; int gPcmStatus = 0; // All the Wad file info for the music and audio streams SWadInfo gWadInfo[ 2 ]; CFileStreamInfo gStreamList [ NUM_FILE_STREAMS ] = { { FILE_STREAM_MUSIC, LOAD_STATE_DONE, PLAY_STATE_DONE, NULL, 0, 0, 0 }, // Music stream { FILE_STREAM_STREAM0, LOAD_STATE_DONE, PLAY_STATE_DONE, NULL, 0, 0, 0 }, // Audio stream 0 { FILE_STREAM_STREAM1, LOAD_STATE_DONE, PLAY_STATE_DONE, NULL, 0, 0, 0 }, // Audio stream 1 { FILE_STREAM_STREAM2, LOAD_STATE_DONE, PLAY_STATE_DONE, NULL, 0, 0, 0 } // Audio stream 2 }; CFileStreamInfo * gpMusicInfo = &gStreamList[FILE_STREAM_MUSIC]; CFileStreamInfo * gpStreamInfo[ NUM_STREAMS ] = { &gStreamList[FILE_STREAM_STREAM0], &gStreamList[FILE_STREAM_STREAM1], &gStreamList[FILE_STREAM_STREAM2] }; unsigned int gStreamVolume = 100; // Communication to RPC -- ezpcm.irx: // EzPcm is the function that the EE uses to call the IRX program EzPcm.irx. // EzPcm.irx is a separate code module that runs on the IOP chip (the playstation one chip). // The EzPcm module is build from the pcm* files in music\ngps\pcm. // The files in the music\ngps\bgm folder were used to build a different irx that is // no longer used for streaming music, so don't worry about the BGM files. #define MAX_QUEUED_REQUESTS (10) // SifCmd packets static SSifCmdStreamRequestPacket s_cmd_requests[MAX_QUEUED_REQUESTS] __attribute__ ((aligned(16))); static SSifCmdStreamLoadStatusPacket s_cmd_load_status __attribute__ ((aligned(16))); static uint32 s_cmd_request_dma_ids[MAX_QUEUED_REQUESTS] = { 0 }; static uint32 s_cmd_load_status_last_id = 0; static int s_cmd_request_free_index = 0; static int s_request_id = 0; static volatile bool s_request_done; static volatile int s_request_result; static volatile int s_request_result_id; // These are set by sifcmds sent directly from the IOP volatile int sSentStatus = 0; volatile bool sNewStatus = false; // Timing test code #if TEST_PLAY_TIMING static volatile uint32 test_timing_start; static volatile uint32 test_timing_end; static int test_timing_request_id = -1; #endif // TEST_PLAY_TIMING void result_callback(void *p, void *q) { SSifCmdStreamResultPacket *h = (SSifCmdStreamResultPacket *) p; s_request_result = h->m_return_value; s_request_result_id = h->m_header.opt; sSentStatus |= h->m_status_flags; sNewStatus = true; s_request_done = true; //scePrintf("*************** EzPcm() result ID %d result = %d\n", s_request_result_id, s_request_result); #if TEST_PLAY_TIMING if (s_request_result_id == test_timing_request_id) { test_timing_end = Tmr::GetTimeInUSeconds(); scePrintf("EzPcm() turnaround time Frame %d Time %d (%x - %x)\n", Tmr::GetVblanks(), (test_timing_end - test_timing_start), test_timing_end, test_timing_start); } #endif // TEST_PLAY_TIMING ExitHandler(); } void status_callback(void *p, void *q) { SSifCmdStreamStatusPacket *h = (SSifCmdStreamStatusPacket *) p; sSentStatus |= h->m_status_flags; sNewStatus = true; #if TEST_PLAY_TIMING if ((int) h->m_header.opt == test_timing_request_id) { test_timing_end = Tmr::GetTimeInUSeconds(); scePrintf("EzPcm() final turnaround time Frame %d Time %d (%x - %x)\n", Tmr::GetVblanks(), (test_timing_end - test_timing_start), test_timing_end, test_timing_start); } #endif // TEST_PLAY_TIMING ExitHandler(); } bool get_status(int & status) { bool new_stat; // Disable interrupts just in case we get a sifcmd callback DI(); status = sSentStatus; new_stat = sNewStatus; sSentStatus = 0; sNewStatus = false; EI(); return new_stat; } // prototype: void EzPcm( int command, int data, int data2 = 0, int data3 = 0 ); // IOP void EzPcm( int command, int data, int data2, int data3 ) { //scePrintf("Doing EzPcm request id %d with command %x\n", s_request_id, command); #if TEST_PLAY_TIMING if (command == EzADPCM_PLAYPRELOADEDMUSIC) { test_timing_start = Tmr::GetTimeInUSeconds(); test_timing_request_id = s_request_id; Dbg_Message("Doing EzPcm request id %d with command %x", test_timing_request_id, command); } #endif // TEST_PLAY_TIMING int nextFreeIndex = (s_cmd_request_free_index + 1) % MAX_QUEUED_REQUESTS; SSifCmdStreamRequestPacket *p_packet = &s_cmd_requests[s_cmd_request_free_index]; // Make sure this request packet is not still in the Sif DMA queue if (s_cmd_request_dma_ids[s_cmd_request_free_index]) { while(sceSifDmaStat(s_cmd_request_dma_ids[s_cmd_request_free_index]) >= 0) ; } p_packet->m_header.opt = s_request_id++; p_packet->m_request.m_command = command; p_packet->m_request.m_param[0] = data; p_packet->m_request.m_param[1] = data2; p_packet->m_request.m_param[2] = data3; s_request_done = false; // Callback will change it to true s_cmd_request_dma_ids[s_cmd_request_free_index] = sceSifSendCmd(STREAM_REQUEST_COMMAND, p_packet, sizeof(*p_packet), 0, 0, 0); s_cmd_request_free_index = nextFreeIndex; //Dbg_MsgAssert(request_result_id == (request_id -1), ("Result id (%d) differs from request id (%d)", request_result_id, request_id - 1)); } // Tell IOP that the buffer is done loading void send_load_done(EFileStreamType streamType, int buffer_num, int bytes_loaded) { Dbg_Assert((buffer_num >= 0) && (buffer_num <= 1)); // Wait for last send to complete if (s_cmd_load_status_last_id) { while(sceSifDmaStat(s_cmd_load_status_last_id) >= 0) ; } s_cmd_load_status.m_stream_num = streamType - 1; s_cmd_load_status.m_buffer_num = buffer_num; s_cmd_load_status.m_bytes_loaded = bytes_loaded; //FlushCache(0); s_cmd_load_status_last_id = sceSifSendCmd(STREAM_LOAD_STATUS_COMMAND, &s_cmd_load_status, sizeof(s_cmd_load_status), 0, 0, 0); //scePrintf("Audio stream %d buffer #%d finished loading\n", streamType, buffer_num); } ////////////////////////////////////////////////////////////////////////////////////////////////// #include void InitCDPlease( void ) { if (Config::CD()) { // already done since we're building in CD mode... return; } #if TEST_MOTHERFUCKING_CD static bool initializedAndShit = false; if ( !initializedAndShit ) { sceCdInit(SCECdINIT); // this may oughtta be changed to SCDCdDVD... // Actually, a smart thing to do would be to have this defined somewhere // and switched between SCECdCD or SCDCdDVD depending on the build? //sceCdMmode(SCECdCD); sceCdMmode(SCECdDVD); initializedAndShit = true; } #endif } static bool CheckForCDErrors( void ) { int cdErr; cdErr = sceCdGetError( ); switch ( cdErr ) { case ( SCECdErNO ): // no error break; case ( SCECdErTRMOPN ): // cover opened during playback case ( SCECdErREAD ): // read error return ( true ); default: printf( "cd err %d\n", cdErr ); break; } return ( false ); } /////////////////////////////////////////////////////////////////////////////////////// // CFileStreamInfo // // Preloads the SPU audio buffers (because this can take a few frames) void CFileStreamInfo::preload_spu_buffers() { // Check if it is music or normal audio if (IsMusicStream()) { // got initial music buffers loaded - start preloading SPU buffers EzPcm( EzADPCM_PRELOADMUSIC, m_fileSize / 2 ); // because there are two channels L and R } else { // got initial stream buffers loaded - start preloading SPU buffers EzPcm( EzADPCM_PRELOADSTREAM | GetAudioStreamIndex(), m_fileSize ); } } // Starts playing a preloaded audio stream void CFileStreamInfo::start_preloaded_audio() { gPcmStatus &= ~PCMSTATUS_LOAD_AUDIO( m_streamType ); gPcmStatus |= PCMSTATUS_AUDIO_PLAYING( m_streamType ); // Check if it is music or normal audio if (IsMusicStream()) { // got initial music buffers loaded on the SPU - start it playing: EzPcm( EzADPCM_PLAYPRELOADEDMUSIC, 0 ); } else { // got initial stream buffers loaded on the SPU - start it playing: EzPcm( EzADPCM_PLAYPRELOADEDSTREAM | GetAudioStreamIndex(), m_volume, m_pitch ); } } // Starts playing the audio stream void CFileStreamInfo::start_audio() { gPcmStatus &= ~PCMSTATUS_LOAD_AUDIO( m_streamType ); gPcmStatus |= PCMSTATUS_AUDIO_PLAYING( m_streamType ); // Check if it is music or normal audio if (IsMusicStream()) { // got initial music buffers loaded - start it playing: EzPcm( EzADPCM_PLAYMUSIC, m_fileSize / 2 ); // because there are two channels L and R } else { // got initial stream buffers loaded - start it playing: EzPcm( EzADPCM_PLAYSTREAM | GetAudioStreamIndex(), m_fileSize, m_volume, m_pitch ); } } // Starts the load of an audio stream chunk void CFileStreamInfo::load_chunk(int buffer) { if ( !mp_fileHandle ) { Dbg_MsgAssert( 0,( "s'it, yo... no file opened for stream index %d", GetAudioStreamIndex() )); return; } // Check if it is music or normal audio if (IsMusicStream()) { void *p_iop_addr = (void *) (gIopBuffer + ( MUSIC_IOP_BUFFER_SIZE * buffer )); mp_fileHandle->Read( p_iop_addr, 1, MUSIC_IOP_BUFFER_SIZE ); //scePrintf("Reading music buffer %d from disk to IOP buffer %x of size %d\n", buffer, p_iop_addr, MUSIC_IOP_BUFFER_SIZE); m_offset += MUSIC_IOP_BUFFER_SIZE; } else { int whichStream = GetAudioStreamIndex(); void *p_iop_addr = (void *) (gIopBuffer + STREAM_IOP_OFFSET( whichStream ) + ( STREAM_HALF_IOP_BUFFER_SIZE * buffer )); mp_fileHandle->Read( p_iop_addr, 1, STREAM_HALF_IOP_BUFFER_SIZE ); m_offset += STREAM_HALF_IOP_BUFFER_SIZE; } } // Checks to see if the IOP module is requesting another stream chunk ELoadState CFileStreamInfo::check_for_load_requests() { ELoadState state = LOAD_STATE_IDLE; if ( gPcmStatus & PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) ) { load_chunk( 0 ); state = LOAD_STATE_LOADING0; gPcmStatus &= ~PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ); } else if ( gPcmStatus & PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) ) { load_chunk( 1 ); state = LOAD_STATE_LOADING1; gPcmStatus &= ~PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ); } return state; } // Checks to see if loading of stream chunk is done // errno will be non-zero if we were busy AND there was an error bool CFileStreamInfo::is_chunk_load_done(int & errno) { Dbg_Assert(mp_fileHandle); // Clear error var errno = 0; // Check for delay errors // Garrett: We should only be concerned if we get a request for the buffer we are currently loading. The // only problem to this is the fact that the IOP doesn't check to see if a buffer is loaded at all. We // must add this so we can get rid of the conservative check of making sure we don't get ANY new load requests. // if ( gPcmStatus & ( PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) | PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) ) ) int load_flag = (m_loadState == LOAD_STATE_LOADING0) ? PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) : PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ); if ( gPcmStatus & load_flag ) { int cur_buffer_load = (int) (m_loadState - LOAD_STATE_LOADING0); Dbg_Message("Load State: %d", m_loadState); Dbg_MsgAssert( 0, ( "If repeatable, tell Garrett that audio #%d buffer #%d didn't load on time. Status: %x", m_streamType, cur_buffer_load, gPcmStatus )); printf( "If repeatable, tell Garrett audio #%d buffer #%d didn't load on time -- must increase IOP streaming buf size. Status %x", m_streamType, cur_buffer_load, gPcmStatus ); errno = (IsMusicStream()) ? -1 : m_streamType; } if (!mp_fileHandle->IsBusy()) { int cur_buffer_load = (int) (m_loadState - LOAD_STATE_LOADING0); // Tell IOP that we are done send_load_done(m_streamType, cur_buffer_load, IsMusicStream() ? MUSIC_IOP_BUFFER_SIZE : STREAM_HALF_IOP_BUFFER_SIZE); return true; } // Return that we aren't done return false; } // returns true and closes fine if we read the whole file bool CFileStreamInfo::file_finished() { if ( m_offset < m_fileSize ) { return false; } else { Dbg_Assert(mp_fileHandle); // It shouldn't be closed yet File::CAsyncFileLoader::sClose( mp_fileHandle ); mp_fileHandle = NULL; return true; } } // Starts an audio stream bool CFileStreamInfo::StartStream(File::SHedFile *pHedFile, bool play) { // make sure CD isn't being used by another system: // Garrett: This should go away File::StopStreaming( ); // Make sure these load flags aren't set if ( gPcmStatus & ( PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) | PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) ) ) { Dbg_Message("Load State: %d", m_loadState); Dbg_MsgAssert( 0, ( "Start Stream on audio #%d still has load buffer flag. Status: %x", m_streamType, gPcmStatus )); printf( "Start Stream on audio #%d still has load buffer flag. Status %x", m_streamType, gPcmStatus ); } // Find correct wad filename char *p_wadName; unsigned int wadLsn; int priority; if (IsMusicStream()) { wadLsn = gWadInfo[MUSIC_CHANNEL].m_lsn; p_wadName = gWadInfo[MUSIC_CHANNEL].m_fileName; priority = MUSIC_PRIORITY; } else { wadLsn = gWadInfo[EXTRA_CHANNEL].m_lsn; p_wadName = gWadInfo[EXTRA_CHANNEL].m_fileName; priority = STREAM_PRIORITY; } // load Audio wad: if ( mp_fileHandle ) { Dbg_Message( "What? Audio file opened still: %d", mp_fileHandle ); Dbg_MsgAssert( 0,( "What? Stream file still (or already) opened." )); return false; } if (Config::CD() || TEST_MOTHERFUCKING_CD) { mp_fileHandle = File::CPs2AsyncFileLoader::sRawOpen(wadLsn, false, priority); } else { if (pHedFile->HasNoWad()) { char local_file[256]; strcpy(local_file, pHedFile->p_filename); if (IsMusicStream()) { strcat(local_file, ".ivg"); } else { strcat(local_file, ".vag"); } mp_fileHandle = File::CAsyncFileLoader::sOpen(&local_file[1], false, priority); } else { mp_fileHandle = File::CAsyncFileLoader::sOpen(p_wadName, false, priority); } } if ( !mp_fileHandle ) { Dbg_MsgAssert( 0,( "Play Audio: couldn't find audio wad file %s", p_wadName )); printf( "play audio failed to find audio wad file %s\n", p_wadName ); return false; } m_offset = 0; mp_fileHandle->SetDestination(File::MEM_IOP); if (pHedFile->HasNoWad()) { // .ivg files don't have the VAG header if (!IsMusicStream()) mp_fileHandle->Seek( SIZE_OF_VAGHEADER, SEEK_SET ); } else { mp_fileHandle->Seek( pHedFile->Offset, SEEK_SET ); } m_fileSize = pHedFile->GetFileSize(); m_loadState = LOAD_STATE_LOADING0; m_playState = (play) ? PLAY_STATE_START : PLAY_STATE_PRELOAD_EE; // Start loading the data, but don't play it yet load_chunk(0); // Change the state gPcmStatus |= PCMSTATUS_LOAD_AUDIO(m_streamType); return true; } // Plays a preloaded audio stream bool CFileStreamInfo::PlayStream() { switch (m_playState) { case PLAY_STATE_DONE: case PLAY_STATE_PLAYING: return false; case PLAY_STATE_PAUSED: // Not implemented yet Dbg_MsgAssert(0, ("Playing a paused stream not implemented yet")); return false; case PLAY_STATE_PRELOAD_EE: Dbg_MsgAssert(0, ("Starting a preloaded stream that isn't finished preloading")); return false; case PLAY_STATE_PRELOAD_IOP: case PLAY_STATE_STOP: case PLAY_STATE_START: switch (m_loadState) { case LOAD_STATE_DONE: case LOAD_STATE_IDLE: case LOAD_STATE_LOADING1: // Since it may already be loading the next section m_playState = PLAY_STATE_PLAYING; //start_audio(); start_preloaded_audio(); break; case LOAD_STATE_LOADING0: m_playState = PLAY_STATE_START; Dbg_MsgAssert(0, ("Starting a preloaded stream that isn't finished preloading. Load state: %d", m_loadState)); break; } break; } return true; } // Stops an audio stream bool CFileStreamInfo::StopStream() { int status = IsMusicStream() ? PCMAudio_GetMusicStatus( ) : PCMAudio_GetStreamStatus( GetAudioStreamIndex() ); // Check states if ( status == PCM_STATUS_PLAYING ) { if (IsMusicStream()) { EzPcm( EzADPCM_STOPMUSIC, 0 ); } else { EzPcm( EzADPCM_STOPSTREAM, GetAudioStreamIndex() ); } } // Close async file if ( mp_fileHandle ) { m_loadState = LOAD_STATE_DONE; File::CAsyncFileLoader::sClose( mp_fileHandle ); mp_fileHandle = NULL; } // Clear flags PCMAudio_UpdateStatus(); m_playState = PLAY_STATE_DONE; gPcmStatus &= ~( PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) | PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) | PCMSTATUS_LOAD_AUDIO( m_streamType ) | PCMSTATUS_AUDIO_PLAYING( m_streamType ) | PCMSTATUS_AUDIO_READY(m_streamType) ); return true; } // Update: executed on each stream every frame // returns error number on error, 0 otherwise... int CFileStreamInfo::Update() { int errno = 0; switch (m_loadState) { case LOAD_STATE_IDLE: // Make sure IOP didn't drop out (would probably be a state problem) //Dbg_MsgAssert(gPcmStatus & PCMSTATUS_AUDIO_PLAYING( m_streamType ), ("Generic stream %d not playing while in LOAD_STATE_IDLE", m_streamType)); m_loadState = check_for_load_requests(); break; case LOAD_STATE_LOADING0: case LOAD_STATE_LOADING1: // Make sure IOP didn't drop out (would probably be a state problem) //Dbg_MsgAssert(gPcmStatus & PCMSTATUS_AUDIO_PLAYING( m_streamType ), ("Generic stream %d not playing while in LOAD_STATE_LOADING", m_streamType)); if (!is_chunk_load_done(errno)) { if (errno != 0) { return errno; } break; // still busy loading } // Check if it needs starting if (m_playState == PLAY_STATE_START) { m_playState = PLAY_STATE_PLAYING; start_audio(); } else if (m_playState == PLAY_STATE_PRELOAD_EE) { m_playState = PLAY_STATE_PRELOAD_IOP; preload_spu_buffers(); } // Change state and possibly close file if (file_finished()) { m_loadState = LOAD_STATE_DONE; } else { m_loadState = LOAD_STATE_IDLE; } break; case LOAD_STATE_DONE: // Just wait for the audio to finish playing if ( !(gPcmStatus & PCMSTATUS_AUDIO_PLAYING( m_streamType )) ) { m_playState = PLAY_STATE_DONE; // Clear out any old sent load request flags gPcmStatus &= ~( PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) | PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) ); } break; } return errno; } #define DEBUG_LOAD_FLAGS 0 #if DEBUG_LOAD_FLAGS int old_load_state[NUM_FILE_STREAMS]; int old_play_state[NUM_FILE_STREAMS]; #endif // Combine the IOP sent status into gPcmStatus void PCMAudio_UpdateStatus() { int new_status; if (get_status(new_status)) { for ( int i = 0; i < NUM_FILE_STREAMS; i++ ) { gPcmStatus &= ~( PCMSTATUS_AUDIO_PLAYING( i ) | PCMSTATUS_AUDIO_READY( i ) ); } gPcmStatus |= new_status; } } // returns error number on error, 0 otherwise... int PCMAudio_Update( void ) { int i; #if DEBUG_LOAD_FLAGS for (int i = 0; i < NUM_FILE_STREAMS; i++) { old_load_state[i] = gStreamList[ i ].m_loadState; old_play_state[i] = gStreamList[ i ].m_playState; } #endif // Clear the flags PCMAudio_UpdateStatus(); static bool print_panic = true; // Using this now until we can clear the PANIC flag on the IOP if ((gPcmStatus & PCMSTATUS_PANIC) && print_panic) { print_panic = false; scePrintf("ezpcm.irx module set PANIC flag: Look at its last error message.\n"); //Dbg_MsgAssert(0, ("ezpcm.irx module set PANIC flag: Look at its last error message.")); } // Make sure there aren't any CD errors if (0 && (Config::CD() || TEST_MOTHERFUCKING_CD) && CheckForCDErrors( )) { return ( -1 ); } // Go through each stream and update for (i = 0; i < NUM_FILE_STREAMS; i++) { CFileStreamInfo *pInfo = &gStreamList[ i ]; int ret_val = pInfo->Update(); if (ret_val != 0) { return ret_val; } } #if 0 // If file loads go out of sync over a busy network, keep it from crashing: if ( gMusicInfo.mp_fileHandle && !( gPcmStatus & PCMSTATUS_MUSIC_PLAYING ) ) { File::CAsyncFileLoader::sClose( gMusicInfo.mp_fileHandle ); gMusicInfo.mp_fileHandle = NULL; Dbg_Message( "Symptom of a busy network: Closing music file." ); } for ( i = 0; i < NUM_STREAMS; i++ ) { if ( gStreamInfo[ i ].mp_fileHandle && !( gPcmStatus & (PCMSTATUS_STREAM_PLAYING( i ) | PCMSTATUS_LOAD_STREAM( i ))) ) { File::CAsyncFileLoader::sClose( gStreamInfo[i].mp_fileHandle ); gStreamInfo[ i ].mp_fileHandle = NULL; Dbg_Message( "Symptom of a busy network: Closing stream file." ); } } #endif #if 0 Dbg_MsgAssert(!(gPcmStatus & ( 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 )), ("Skipped a buffer load: status %x", gPcmStatus)); #else if (gPcmStatus & ( 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 )) { Dbg_Message("************Skipped a buffer load: status %x", gPcmStatus); #if DEBUG_LOAD_FLAGS for (int i = 0; i < NUM_FILE_STREAMS; i++) { Dbg_Message("Audio Stream %d: load state = %d", i, gStreamList[ i ].m_loadState); Dbg_Message("Audio Stream %d: play state = %d", i, gStreamList[ i ].m_playState); Dbg_Message("Audio Stream %d: old load state = %d", i, old_load_state[i]); Dbg_Message("Audio Stream %d: old play state = %d", i, old_play_state[i]); } #endif } #endif return ( 0 ); } /* ------------ Initialize rpc -- Just call once at the very beginning! ------------ */ void PCMAudio_Init( void ) { Dbg_MsgAssert(NUM_FILE_STREAMS == (NUM_STREAMS + 1), ("Number of file slots allocated for audio streams (%d) different than number of IOP audio streams (%d)", NUM_FILE_STREAMS - 1, NUM_STREAMS)); // Check to make sure stream array is filled for (int i = 0; i < NUM_STREAMS; i++) { Dbg_Assert(gpStreamInfo[i]); } DI(); printf( "Setting up Sif Cmd with EzPCM Streaming IRX...\n" ); // No longer need to call sceSifSetCmdBuffer() since we share it with async filesys // static sceSifCmdData cmdbuffer[NUM_COMMAND_HANDLERS]; // sceSifSetCmdBuffer( &cmdbuffer[0], NUM_COMMAND_HANDLERS); sceSifAddCmdHandler(STREAM_RESULT_COMMAND, result_callback, NULL ); sceSifAddCmdHandler(STREAM_STATUS_COMMAND, status_callback, NULL ); EI(); // if CD system hasn't been initialized, // but you want to test music from CD, // this will do it... InitCDPlease( ); Dbg_MsgAssert( !gIopBuffer,( "What the fuck - buffer already exists?" )); FlushCache( 0 ); PCMAudio_GetIopMemory( ); EzPcm( EzADPCM_INIT, gIopBuffer ); //EzPcm( EzADPCM_SETSTREAMGLOBVOL, gStreamVolume ); } // end of PCMAudio_Init( ) int PCMAudio_GetIopMemory( void ) { if ( !gNonAllignedIopBuffer ) { // streaming buffers: gNonAllignedIopBuffer = ( int )sceSifAllocIopHeap( TOTAL_IOP_BUFFER_SIZE_NEEDED + ALLIGN_REQUIREMENT ); gIopBuffer = gNonAllignedIopBuffer + ALLIGN_REQUIREMENT - ( gNonAllignedIopBuffer & ( ALLIGN_REQUIREMENT - 1 ) ); if ( !gNonAllignedIopBuffer ) { Dbg_MsgAssert( 0,( "Failed to allocate IOP memory - %d k\n", TOTAL_IOP_BUFFER_SIZE_NEEDED / 1024 )); return 0; } } return ( gIopBuffer ); } void PCMAudio_SetStreamGlobalVolume( unsigned int volume ) { gStreamVolume = volume; //if ( gPcmStatus ) // make sure we've initialized PCM stuff: //{ // EzPcm( EzADPCM_SETSTREAMGLOBVOL, gStreamVolume ); //} } bool PCMAudio_SetMusicVolume( float volume ) { int vol = ( int )PERCENT( volume, MAX_VOL ); if ( vol > MAX_VOL ) vol = MAX_VOL; EzPcm( EzADPCM_SETMUSICVOL, ( vol << 16 ) | vol ); return true; }// end of PCMAudio_SetMusicVolume( ) /*int PCMAudio_SetStreamVolumeAndPitch( float volumeL, float volumeR, float fPitch, int whichStream ) { Dbg_Message( "pitch %d", pitch ); return ( EzPcm( EzADPCM_SETSTREAMVOLANDPITCH | whichStream, ( ( volL << 16 ) | volR ), pitch ) ); }// end of PCMAudio_SetVolumeAndPitch( ) */ // Should check before calling this to make sure the sound is playing and has the uniqueID of the sound you are // trying to adjust: bool PCMAudio_SetStreamVolume( float volumeL, float volumeR, int whichStream ) { int volL = ( int )PERCENT( volumeL, gStreamVolume ); volL = ( int )PERCENT( volL, NORMAL_VOL ); if ( volL > MAX_VOL ) volL = MAX_VOL; else if ( volL < -MAX_VOL ) volL = -MAX_VOL; int volR; if ( volumeR == volumeL ) { volR = volL; } else { volR = ( int )PERCENT( volumeR, gStreamVolume ); volR = ( int )PERCENT( volR, NORMAL_VOL ); if ( volR > MAX_VOL ) volR = MAX_VOL; else if ( volR < -MAX_VOL ) volR = -MAX_VOL; } // for phasing effects (of sounds behind you), set the volume // to 0x7fff if ( volL < 0 ) { volL = 0x7fff + volL; } if ( volR < 0 ) // Just in case we start phase shifting the right side instead { volR = 0x7fff + volR; } gpStreamInfo[ whichStream ]->m_volume = ( ( (volL & 0x7FFF) << 16 ) | (volR & 0x7FFF) ); if (gpStreamInfo[ whichStream ]->m_playState == PLAY_STATE_PLAYING) { EzPcm( EzADPCM_SETSTREAMVOL | whichStream, gpStreamInfo[ whichStream ]->m_volume); } return true; }// end of PCMAudio_SetVolume( ) // Currently, this only works when the stream isn't playing bool PCMAudio_SetStreamPitch( float fPitch, int whichStream ) { if ( fPitch > 100.0f ) { fPitch = 100.0f; } int pitch; pitch = ( int ) PERCENT( fPitch, ( float )DEFAULT_PITCH ); if ( pitch == 0 ) { Dbg_Message( "Trying to set stream to zero pitch." ); return ( false ); } if (gpStreamInfo[ whichStream ]->m_playState == PLAY_STATE_PLAYING) { Dbg_MsgAssert(0, ("Can't change pitch of stream %d while it is playing", whichStream)); return false; } else { gpStreamInfo[ whichStream ]->m_pitch = pitch; return true; } } bool PCMAudio_PlayMusicTrack( const char *filename ) { if ( PCMAudio_GetMusicStatus( ) != PCM_STATUS_FREE ) { Dbg_MsgAssert( 0 , ( "Playing new track without stopping the first track." ) ); return ( false ); } if (!(Config::CD() || TEST_MOTHERFUCKING_CD)) { int testMusic = 0; testMusic = Script::GetInteger( 0x47ac7ba5 ); // checksum 'testMusicFromHost' if ( !testMusic ) { return ( false ); } } // make sure CD is not in use by another system: File::StopStreaming( ); if ( !gWadInfo[MUSIC_CHANNEL].mp_hed ) { Dbg_Message( "Music header not loaded, can't play track %s", filename ); return ( false ); } File::SHedFile *pHed = FindFileInHed( filename, gWadInfo[MUSIC_CHANNEL].mp_hed ); if ( !pHed ) { Dbg_Message( "Track %d not found in music header.", filename ); return ( false ); } // Start the actual stream return gpMusicInfo->StartStream(pHed, true); } // end of PCMAudio_PlayMusicTrack( ) bool PCMAudio_PlayMusicStream( uint32 checksum ) { if ( PCMAudio_GetMusicStatus( ) != PCM_STATUS_FREE ) { Dbg_MsgAssert( 0 , ( "Playing music stream without stopping the last music track." ) ); return ( false ); } if (!(Config::CD() || TEST_MOTHERFUCKING_CD)) { int testMusic = 0; testMusic = Script::GetInteger( 0x47ac7ba5 ); // checksum 'testMusicFromHost' if ( !testMusic ) { return ( false ); } } // make sure CD is not in use by another system: File::StopStreaming( ); if ( !gWadInfo[MUSIC_CHANNEL].mp_hed ) { Dbg_Message( "Music header not loaded, can't play music stream %s", Script::FindChecksumName(checksum) ); return ( false ); } File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gWadInfo[MUSIC_CHANNEL].mp_hed ); if ( !pHed ) { Dbg_Message( "Music Stream %s not found in music header.", Script::FindChecksumName(checksum) ); return ( false ); } // Start the actual stream return gpMusicInfo->StartStream(pHed, true); } bool PCMAudio_PlayStream( uint32 checksum, int whichStream, float volumeL, float volumeR, float fPitch ) { Dbg_MsgAssert( whichStream < NUM_STREAMS, ( "Stream is out of range" ) ); if ( PCMAudio_GetStreamStatus( whichStream ) != PCM_STATUS_FREE ) { Dbg_Message("Stream %d not free: status is %x", whichStream, PCMAudio_GetStreamStatus( whichStream )); return ( false ); } if (!(Config::CD() || TEST_MOTHERFUCKING_CD)) { int testStreams = 0; testStreams = Script::GetInteger( 0x62df9442 ); // checksum 'testStreamsFromHost' if ( !testStreams ) { return ( false ); } } //Dbg_Message("Playing stream %x on %d", checksum, whichStream); // load from vag wad: if ( !gWadInfo[EXTRA_CHANNEL].mp_hed ) { //Dbg_Message( "Stream header not loaded, can't play track %s", filename ); return ( false ); } File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gWadInfo[EXTRA_CHANNEL].mp_hed ); if ( !pHed ) { Dbg_Message( "Stream %s not found in stream header.", Script::FindChecksumName(checksum) ); return ( false ); } // Set pitch and volume PCMAudio_SetStreamPitch(fPitch, whichStream); PCMAudio_SetStreamVolume(volumeL, volumeR, whichStream); // Start the actual stream return gpStreamInfo[ whichStream ]->StartStream(pHed, true); } // end of PCMAudio_PlayStream( ) void PCMAudio_StopMusic( bool waitPlease ) { gpMusicInfo->StopStream(); } void PCMAudio_StopStream( int whichStream, bool waitPlease ) { gpStreamInfo[ whichStream ]->StopStream(); } void PCMAudio_StopStreams( void ) { int i; for ( i = 0; i < NUM_STREAMS; i++ ) { PCMAudio_StopStream( i, true ); } } // Get a stream loaded into a buffer, but don't play yet bool PCMAudio_PreLoadStream( uint32 checksum, int whichStream ) { Dbg_MsgAssert( whichStream < NUM_STREAMS, ( "Stream is out of range" ) ); if ( PCMAudio_GetStreamStatus( whichStream ) != PCM_STATUS_FREE ) { return ( false ); } if (!(Config::CD() || TEST_MOTHERFUCKING_CD)) { int testStreams = 0; testStreams = Script::GetInteger( 0x62df9442 ); // checksum 'testStreamsFromHost' if ( !testStreams ) { return ( false ); } } // load from vag wad: if ( !gWadInfo[EXTRA_CHANNEL].mp_hed ) { //Dbg_Message( "Stream header not loaded, can't play track %s", filename ); return ( false ); } File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gWadInfo[EXTRA_CHANNEL].mp_hed ); if ( !pHed ) { //Dbg_Message( "Track %d not found in stream header.", filename ); return ( false ); } // Start the actual stream return gpStreamInfo[ whichStream ]->StartStream(pHed, false); } // Returns true if preload done. Assumes that caller is calling this on a preloaded, but not yet played, stream. // The results are meaningless otherwise. bool PCMAudio_PreLoadStreamDone( int whichStream ) { Dbg_MsgAssert( whichStream < NUM_STREAMS, ( "Stream is out of range" ) ); Dbg_MsgAssert( (gpStreamInfo[whichStream]->m_playState == PLAY_STATE_PRELOAD_EE) || (gpStreamInfo[whichStream]->m_playState == PLAY_STATE_PRELOAD_IOP), ( "PreLoadStreamDone(): This stream on channel %d is either playing or wasn't preloaded. Load state %d. Play State %d. IOP flags %x", whichStream, gpStreamInfo[whichStream]->m_loadState, gpStreamInfo[whichStream]->m_playState, gPcmStatus ) ); //return gpStreamInfo[whichStream]->m_loadState != LOAD_STATE_LOADING0; if (gPcmStatus & PCMSTATUS_STREAM_READY(whichStream)) { Dbg_Message("PCMAudio_PreLoadStreamDone true for stream channel %d. Load state %d. Play State %d. IOP flags %x", whichStream, gpStreamInfo[whichStream]->m_loadState, gpStreamInfo[whichStream]->m_playState, gPcmStatus); } return (gPcmStatus & PCMSTATUS_STREAM_READY(whichStream)) && (gpStreamInfo[whichStream]->m_playState == PLAY_STATE_PRELOAD_IOP); } // Tells a preloaded stream to start playing. Must call PCMAudio_PreLoadStreamDone() first to guarantee that // it starts immediately. bool PCMAudio_StartPreLoadedStream( int whichStream, float volumeL, float volumeR, float pitch ) { Dbg_MsgAssert( whichStream < NUM_STREAMS, ( "Stream is out of range" ) ); // Set pitch and volume PCMAudio_SetStreamPitch(pitch, whichStream); PCMAudio_SetStreamVolume(volumeL, volumeR, whichStream); // Start playing the actual stream return gpStreamInfo[ whichStream ]->PlayStream(); } bool PCMAudio_PreLoadMusicStream( uint32 checksum ) { if ( PCMAudio_GetMusicStatus( ) != PCM_STATUS_FREE ) { Dbg_MsgAssert( 0 , ( "Preloading music stream without stopping the last music track." ) ); return ( false ); } if (!(Config::CD() || TEST_MOTHERFUCKING_CD)) { int testMusic = 0; testMusic = Script::GetInteger( 0x47ac7ba5 ); // checksum 'testMusicFromHost' if ( !testMusic ) { return ( false ); } } // load from vag wad: if ( !gWadInfo[MUSIC_CHANNEL].mp_hed ) { //Dbg_Message( "Stream header not loaded, can't play track %s", filename ); return ( false ); } File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gWadInfo[MUSIC_CHANNEL].mp_hed ); if ( !pHed ) { Dbg_Message( "Music Stream %s not found in music header.", Script::FindChecksumName(checksum) ); return ( false ); } // Start the actual stream return gpMusicInfo->StartStream(pHed, false); } bool PCMAudio_PreLoadMusicStreamDone( void ) { Dbg_MsgAssert( (gpMusicInfo->m_playState == PLAY_STATE_PRELOAD_EE) || (gpMusicInfo->m_playState == PLAY_STATE_PRELOAD_IOP), ( "PreLoadMusicStreamDone(): This stream is either playing or wasn't preloaded." ) ); //return gpMusicInfo->m_loadState != LOAD_STATE_LOADING0; return (gPcmStatus & PCMSTATUS_MUSIC_READY) && (gpMusicInfo->m_playState == PLAY_STATE_PRELOAD_IOP); } bool PCMAudio_StartPreLoadedMusicStream( void ) { // Start playing the actual stream return gpMusicInfo->PlayStream(); } int PCMAudio_GetMusicStatus( void ) { if ( gPcmStatus & PCMSTATUS_LOAD_MUSIC ) { return ( PCM_STATUS_LOADING ); } else if ( gPcmStatus & PCMSTATUS_MUSIC_PLAYING ) { return ( PCM_STATUS_PLAYING ); } else if ( gpMusicInfo->m_playState == PLAY_STATE_STOP) { return ( PCM_STATUS_STOPPED ); } else if ( gpMusicInfo->m_playState == PLAY_STATE_PLAYING) // EE thinks it is playing, but not IOP { return ( PCM_STATUS_END ); } else { //Dbg_Message("Music status is free %x", gPcmStatus); return ( PCM_STATUS_FREE ); } Dbg_MsgAssert( 0,( "Sell your stock in GCC." )); return ( 0 ); } int PCMAudio_GetStreamStatus( int whichStream ) { if ( (whichStream < 0) || (whichStream >= NUM_STREAMS) ) { Dbg_MsgAssert( 0, ( "Checking stream status on stream %d, past valid range ( 0 to %d ).", whichStream, NUM_STREAMS - 1 ) ); return ( false ); } if ( gPcmStatus & PCMSTATUS_LOAD_STREAM( whichStream ) ) { return ( PCM_STATUS_LOADING ); } else if ( gPcmStatus & PCMSTATUS_STREAM_PLAYING( whichStream ) ) { return ( PCM_STATUS_PLAYING ); } else if ( gpStreamInfo[whichStream]->m_playState == PLAY_STATE_STOP) { return ( PCM_STATUS_STOPPED ); } else if ( gpStreamInfo[whichStream]->m_playState == PLAY_STATE_PLAYING) // EE thinks it is playing, but not IOP { return ( PCM_STATUS_END ); } else { return ( PCM_STATUS_FREE ); } Dbg_MsgAssert( 0,( "Sell your stock in GCC." )); return ( 0 ); } void PCMAudio_Pause( bool pause, int ch ) { if ( ch == MUSIC_CHANNEL ) { EzPcm( EzADPCM_PAUSEMUSIC, pause ); } else { EzPcm( EzADPCM_PAUSESTREAMS, pause ); } } // end of PCMAudio_Pause( ) void PCMAudio_PauseStream( bool pause, int whichStream ) { EzPcm( EzADPCM_PAUSESTREAM | whichStream, pause ); } unsigned int GetCDLocation( const char *pWadName ) { InitCDPlease( ); File::StopStreaming( ); sceCdSync( 0 ); char tempFilename[ 255 ]; sprintf( tempFilename, "\\%s%s.WAD;1", Config::GetDirectory(), pWadName ); int i; for ( i = strlen( tempFilename ) - 1; i > 0; i-- ) { if ( tempFilename[ i ] >= 'a' && tempFilename[ i ] <= 'z' ) { tempFilename[ i ] += 'A' - 'a'; } } sceCdlFILE fileInfo; int retVal = 0; if ( !( retVal = sceCdSearchFile( &fileInfo, tempFilename ) ) ) { printf( "Wad %s not found -- Err %d\n", tempFilename, retVal ); return ( 0 ); } return ( fileInfo.lsn ); } bool PCMAudio_TrackExists( const char *pTrackName, int ch ) { if ( !gWadInfo[ch].mp_hed ) { return ( false ); } if ( !FindFileInHed( pTrackName, gWadInfo[ch].mp_hed ) ) { Dbg_Message( "Audio file %s not found in header file.", pTrackName ); return ( false ); } return ( true ); } bool PCMAudio_LoadMusicHeader( const char *nameOfFile ) { bool no_wad = false; if (!(Config::CD() || TEST_MOTHERFUCKING_CD)) { no_wad = true; } return gWadInfo[MUSIC_CHANNEL].LoadHeader(nameOfFile, no_wad); } bool PCMAudio_LoadStreamHeader( const char *nameOfFile ) { bool no_wad = false; if (!(Config::CD() || TEST_MOTHERFUCKING_CD)) { int testStreams = 0; testStreams = Script::GetInteger( 0x62df9442 ); // checksum 'testStreamsFromHost' if ( !testStreams ) { return ( false ); } no_wad = true; } return gWadInfo[EXTRA_CHANNEL].LoadHeader(nameOfFile, no_wad); } uint32 PCMAudio_FindNameFromChecksum( uint32 checksum, int ch ) { File::SHed *pHed; pHed = gWadInfo[ch].mp_hed; if ( !pHed ) { return ( NULL ); } File::SHedFile* pHedFile = File::FindFileInHedUsingChecksum( checksum, pHed ); if( pHedFile ) { return pHedFile->Checksum; } return NULL; } /////////////////////////////////////////////////////////////////////////////////////// // SWadInfo // bool SWadInfo::LoadHeader(const char *nameOfFile, bool no_wad, bool assertOnError) { if ( mp_hed ) { Mem::Free( mp_hed ); mp_hed = NULL; } mp_hed = File::LoadHed( nameOfFile, TEST_MOTHERFUCKING_CD, no_wad ); if ( !mp_hed ) { Dbg_Message( "Couldn't find audio header %s", nameOfFile ); Dbg_MsgAssert( !assertOnError, ( "Couldn't find audio header %s", nameOfFile )); return false; } if (Config::CD() || TEST_MOTHERFUCKING_CD) { Dbg_MsgAssert(!no_wad, ("Can't use a no-wad version of a hed file on the CD")); m_lsn = GetCDLocation( nameOfFile ); if ( !m_lsn ) { Mem::Free( mp_hed ); mp_hed = NULL; Dbg_Message( "Couldn't find audio wad %s", nameOfFile ); Dbg_MsgAssert( !assertOnError,( "Couldn't find audio wad %s", nameOfFile )); return false; } } else { sprintf( m_fileName, "host:%s.WAD", nameOfFile ); } return (bool) mp_hed; } } // namespace PCM