void Inventory::Load( PlayerState* pPlayerState ) { ReloadItems(); m_pPlayerState = pPlayerState; m_iLastSeenCombo = 0; // don't load battle sounds if they're not going to be used switch( GAMESTATE->m_PlayMode ) { case PLAY_MODE_BATTLE: { m_soundAcquireItem.Load( THEME->GetPathS("Inventory","aquire item") ); for( unsigned i=0; i<g_Items.size(); i++ ) { RageSound* pSound = new RageSound; pSound->Load( THEME->GetPathS("Inventory",ssprintf("use item %u",i+1)) ); m_vpSoundUseItem.push_back( pSound ); } m_soundItemEnding.Load( THEME->GetPathS("Inventory","item ending") ); break; } default: break; } }
void RageSoundManager::Update(float delta) { FlushPosMapQueue(); /* Scan the owned_sounds list for sounds that are no longer playing, and delete them. */ g_SoundManMutex.Lock(); /* lock for access to owned_sounds */ set<RageSound *> ToDelete; for( set<RageSound *>::iterator it = owned_sounds.begin(); it != owned_sounds.end(); ++it ) { RageSound *pSound = *it; if( pSound->IsPlaying() ) continue; LOG->Trace("XXX: deleting '%s'", pSound->GetLoadedFilePath().c_str()); ToDelete.insert( pSound ); } for( set<RageSound *>::iterator it = ToDelete.begin(); it != ToDelete.end(); ++it ) owned_sounds.erase( *it ); g_SoundManMutex.Unlock(); /* finished with owned_sounds */ /* Be sure to release g_SoundManMutex before deleting sounds. */ for( set<RageSound *>::iterator it = ToDelete.begin(); it != ToDelete.end(); ++it ) delete *it; if( driver != NULL ) driver->Update(delta); }
/* Don't hold the lock when we don't have to. We call this function from other * threads, to avoid stalling the gameplay thread. */ void RageSoundManager::PlayOnce( CString sPath ) { /* We want this to start quickly, so don't try to prebuffer it. */ RageSound *snd = new RageSound; snd->Load(sPath, false); snd->Play(); /* We're responsible for freeing it. Add it to owned_sounds *after* we start * playing, so RageSoundManager::Update doesn't free it before we actually start * it. */ DeleteSoundWhenFinished( snd ); }
void RageSound::PlayCopy(bool is_action, const RageSoundParams *pParams) const { if(is_action && PREFSMAN->m_MuteActions) { return; } RageSound *pSound = new RageSound( *this ); if( pParams ) pSound->SetParams( *pParams ); pSound->StartPlaying(); pSound->DeleteSelfWhenFinishedPlaying(); }
RageSound *RageSoundManager::PlayCopyOfSound( RageSound &snd, const RageSoundParams *params ) { RageSound *pSound = new RageSound(snd); DeleteSoundWhenFinished( pSound ); if( params ) pSound->SetParams( *params ); // Move to the start position. pSound->SetPositionSeconds( pSound->GetParams().m_StartSecond ); pSound->StartPlaying(); return pSound; }
bool RandomSample::LoadSound( CString sSoundFilePath ) { LOG->Trace( "RandomSample::LoadSound( %s )", sSoundFilePath.c_str() ); RageSound *pSS = new RageSound; if( !pSS->Load(sSoundFilePath) ) { LOG->Trace( "Error loading \"%s\": %s", sSoundFilePath.c_str(), pSS->GetError().c_str() ); delete pSS; return false; } m_pSamples.push_back( pSS ); return true; }
/* This is only called by RageSoundManager::Update. */ void RageSoundManager::FlushPosMapQueue() { queued_pos_map_t p; /* We don't need to lock to access pos_map_queue. */ while( pos_map_queue.read( &p, 1 ) ) { RageSound *pSound = GetSoundByID( p.ID ); /* If we can't find the ID, the sound was probably deleted before we got here. */ if( pSound == NULL ) { // LOG->Trace("ignored unknown (stale?) commit ID %i", p.ID); continue; } pSound->CommitPlayingPosition( p.frameno, p.pos, p.got_frames ); } }
static void StartMusic( MusicToPlay &ToPlay ) { CHECKPOINT; LockMutex L( *g_Mutex ); CHECKPOINT; if( g_Playing->m_Music->IsPlaying() && !g_Playing->m_Music->GetLoadedFilePath().CompareNoCase(ToPlay.file) ) return; CHECKPOINT; if( ToPlay.file.empty() ) { /* StopPlaying() can take a while, so don't hold the lock while we stop the sound. * Be sure to leave the rest of g_Playing in place. */ RageSound *pOldSound = g_Playing->m_Music; g_Playing->m_Music = new RageSound; L.Unlock(); /* We're not allowed to delete the sound in a separate thread, because * RageSoundManager::FlushPosMapQueue might be running. Stop the sound, * and give it to RageSoundManager to delete. */ SOUNDMAN->DeleteSound( pOldSound ); return; } CHECKPOINT; /* Unlock, load the sound here, and relock. Loading may take a while if we're * reading from CD and we have to seek far, which can throw off the timing below. */ MusicPlaying *NewMusic; { g_Mutex->Unlock(); RageSound *pSound = new RageSound; pSound->Load( ToPlay.file, false ); g_Mutex->Lock(); NewMusic = new MusicPlaying( pSound ); } NewMusic->m_Timing = g_Playing->m_Timing; CHECKPOINT; /* See if we can find timing data, if it's not already loaded. */ if( !ToPlay.HasTiming && IsAFile(ToPlay.timing_file) ) { LOG->Trace("Found '%s'", ToPlay.timing_file.c_str()); if( SMLoader::LoadTimingFromFile( ToPlay.timing_file, ToPlay.timing_data ) ) ToPlay.HasTiming = true; } CHECKPOINT; if( ToPlay.HasTiming ) NewMusic->m_NewTiming = ToPlay.timing_data; if( ToPlay.align_beat && ToPlay.HasTiming && ToPlay.force_loop && ToPlay.length_sec != -1 ) { /* Extend the loop period so it always starts and ends on the same fractional * beat. That is, if it starts on beat 1.5, and ends on beat 10.2, extend it * to end on beat 10.5. This way, effects always loop cleanly. */ float fStartBeat = NewMusic->m_NewTiming.GetBeatFromElapsedTime( ToPlay.start_sec ); float fEndSec = ToPlay.start_sec + ToPlay.length_sec; float fEndBeat = NewMusic->m_NewTiming.GetBeatFromElapsedTime( fEndSec ); const float fStartBeatFraction = fmodfp( fStartBeat, 1 ); const float fEndBeatFraction = fmodfp( fEndBeat, 1 ); float fBeatDifference = fStartBeatFraction - fEndBeatFraction; if( fBeatDifference < 0 ) fBeatDifference += 1.0f; /* unwrap */ fEndBeat += fBeatDifference; const float fRealEndSec = NewMusic->m_NewTiming.GetElapsedTimeFromBeat( fEndBeat ); const float fNewLengthSec = fRealEndSec - ToPlay.start_sec; /* Extend the fade_len, so the added time is faded out. */ ToPlay.fade_len += fNewLengthSec - ToPlay.length_sec; ToPlay.length_sec = fNewLengthSec; } CHECKPOINT; bool StartImmediately = false; if( !ToPlay.HasTiming ) { /* This song has no real timing data. The offset is arbitrary. Change it so * the beat will line up to where we are now, so we don't have to delay. */ float fDestBeat = fmodfp( GAMESTATE->m_fSongBeat, 1 ); CHECKPOINT_M(ssprintf("%f",GAMESTATE->m_fSongBeat)); CHECKPOINT_M(ssprintf("%p",NewMusic)); float fTime = NewMusic->m_NewTiming.GetElapsedTimeFromBeat( fDestBeat ); NewMusic->m_NewTiming.m_fBeat0OffsetInSeconds = fTime; StartImmediately = true; } CHECKPOINT; /* If we have an active timer, try to start on the next update. Otherwise, * start now. */ if( !g_Playing->m_HasTiming && !g_UpdatingTimer ) StartImmediately = true; if( !ToPlay.align_beat ) StartImmediately = true; CHECKPOINT; RageTimer when; /* zero */ if( !StartImmediately ) { /* GetPlayLatency returns the minimum time until a sound starts. That's * common when starting a precached sound, but our sound isn't, so it'll * probably take a little longer. Nudge the latency up. */ const float PresumedLatency = SOUND->GetPlayLatency() + 0.040f; const float fCurSecond = GAMESTATE->m_fMusicSeconds + PresumedLatency; const float fCurBeat = g_Playing->m_Timing.GetBeatFromElapsedTime( fCurSecond ); const float fCurBeatFraction = fmodfp( fCurBeat,1 ); /* The beat that the new sound will start on. */ const float fStartBeat = NewMusic->m_NewTiming.GetBeatFromElapsedTime( ToPlay.start_sec ); float fStartBeatFraction = fmodfp( fStartBeat, 1 ); if( fStartBeatFraction < fCurBeatFraction ) fStartBeatFraction += 1.0f; /* unwrap */ const float fCurBeatToStartOn = truncf(fCurBeat) + fStartBeatFraction; const float fSecondToStartOn = g_Playing->m_Timing.GetElapsedTimeFromBeat( fCurBeatToStartOn ); const float fMaximumDistance = 2; const float fDistance = min( fSecondToStartOn - fCurSecond, fMaximumDistance ); when = GAMESTATE->m_LastBeatUpdate + PresumedLatency + fDistance; } CHECKPOINT; /* Important: don't hold the mutex while we load and seek the actual sound. */ L.Unlock(); { NewMusic->m_HasTiming = ToPlay.HasTiming; if( ToPlay.HasTiming ) NewMusic->m_NewTiming = ToPlay.timing_data; NewMusic->m_TimingDelayed = true; // NewMusic->m_Music->Load( ToPlay.file, false ); RageSoundParams p; p.m_StartSecond = ToPlay.start_sec; p.m_LengthSeconds = ToPlay.length_sec; p.m_FadeLength = ToPlay.fade_len; p.StartTime = when; if( ToPlay.force_loop ) p.StopMode = RageSoundParams::M_LOOP; NewMusic->m_Music->SetParams( p ); NewMusic->m_Music->SetPositionSeconds( p.m_StartSecond ); NewMusic->m_Music->StartPlaying(); } CHECKPOINT; LockMut( *g_Mutex ); delete g_Playing; g_Playing = NewMusic; CHECKPOINT; }
void ScreenTestSound::Input( const InputEventPlus &input ) { if( input.type != IET_FIRST_PRESS ) return; // ignore switch( input.DeviceI.device ) { case DEVICE_KEYBOARD: switch( input.DeviceI.button ) { case '1': case '2': case '3': case '4': case '5': selected = input.DeviceI.button - '0'-1; break; case 'p': { /* We want to be able to read the position of copied sounds; if we let * RageSound copy itself, then the copy will be owned by RageSoundManager * and we won't be allowed to touch it. Copy it ourself. */ RageSound *pCopy = new RageSound( s[selected].s ); m_sSoundCopies[selected].push_back( pCopy ); pCopy->Play(); break; } case 's': for( int i = 0; i < nsounds; ++i ) { /* Stop copied sounds. */ vector<RageSound *> &snds = m_sSoundCopies[i]; for( unsigned j = 0; j < snds.size(); ++j ) snds[j]->Stop(); } break; case 'l': { RageSoundParams p = s[selected].s.GetParams(); p.StopMode = RageSoundParams::M_LOOP; s[selected].s.SetParams( p ); } break; case 'a': { RageSoundParams p = s[selected].s.GetParams(); p.StopMode = RageSoundParams::M_STOP; s[selected].s.SetParams( p ); } break; case 'c': { RageSoundParams p = s[selected].s.GetParams(); p.StopMode = RageSoundParams::M_CONTINUE; s[selected].s.SetParams( p ); } break; /* case KEY_LEFT: obj.SetX(obj.GetX() - 10); break; case KEY_RIGHT: obj.SetX(obj.GetX() + 10); break; case KEY_UP: obj.SetY(obj.GetY() - 10); break; case KEY_DOWN: obj.SetY(obj.GetY() + 10); break; */ } } }