Files
itgmania212121/stepmania/src/RageSound.cpp
T

686 lines
18 KiB
C++
Raw Normal View History

2002-12-13 07:44:34 +00:00
/*
2005-01-20 01:49:04 +00:00
* Handle loading and decoding of sounds.
*
2002-12-13 07:44:34 +00:00
* For small files, pre-decode the entire file into a regular buffer. We
* might want to play many samples at once, and we don't want to have to decode
* 5-10 mp3s simultaneously during play.
*
* For larger files, decode them on the fly. These are usually music, and there's
* usually only one of those playing at a time. When we get updates, decode data
* at the same rate we're playing it. If we don't do this, and we're being read
* in large chunks, we're forced to decode in larger chunks as well, which can
* cause framerate problems.
*
2002-12-27 23:15:39 +00:00
* Error handling:
* Decoding errors (eg. CRC failures) will be recovered from when possible.
*
* When they can't be recovered, the sound will stop (unless loop or !autostop)
* and the error will be available in GetError().
*
* Seeking past the end of the file will throw a warning and rewind.
2002-12-13 07:44:34 +00:00
*/
2003-02-16 04:01:45 +00:00
#include "global.h"
2002-12-13 07:44:34 +00:00
#include "RageSound.h"
#include "RageSoundManager.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "PrefsManager.h"
2004-10-25 01:53:19 +00:00
#include "RageSoundUtil.h"
#include "RageSoundReader_Extend.h"
2006-12-10 00:43:51 +00:00
#include "RageSoundReader_Pan.h"
#include "RageSoundReader_PitchChange.h"
2003-03-15 23:34:45 +00:00
#include "RageSoundReader_Preload.h"
#include "RageSoundReader_Resample_Good.h"
2003-09-13 06:08:51 +00:00
#include "RageSoundReader_FileReader.h"
2006-12-22 21:46:03 +00:00
#include "RageSoundReader_ThreadedBuffer.h"
2002-12-13 07:44:34 +00:00
2005-12-23 05:31:27 +00:00
static const int channels = 2;
static const int framesize = 2 * channels; /* 16-bit */
2005-06-08 04:47:03 +00:00
#define samplerate() m_pSource->GetSampleRate()
2002-12-13 07:44:34 +00:00
2004-02-28 00:14:16 +00:00
RageSoundParams::RageSoundParams():
2005-09-23 00:12:32 +00:00
m_StartTime( RageZeroTimer )
2004-02-28 00:14:16 +00:00
{
m_StartSecond = 0;
m_LengthSeconds = -1;
2004-02-28 00:14:16 +00:00
m_FadeLength = 0;
2005-10-02 08:35:46 +00:00
m_Volume = 1.0f;
m_fPitch = 1.0f;
m_fSpeed = 1.0f;
StopMode = M_AUTO;
m_bIsCriticalSound = false;
2004-02-28 00:14:16 +00:00
}
RageSoundLoadParams::RageSoundLoadParams()
{
m_bSupportRateChanging = false;
2006-12-10 00:43:51 +00:00
m_bSupportPan = false;
}
RageSound::RageSound():
m_Mutex( "RageSound" )
2002-12-13 07:44:34 +00:00
{
2005-06-08 04:47:03 +00:00
ASSERT( SOUNDMAN );
2002-12-13 07:44:34 +00:00
2005-06-08 04:47:03 +00:00
m_pSource = NULL;
m_iStreamFrame = 0;
m_iStoppedSourceFrame = 0;
2005-06-08 04:47:03 +00:00
m_iMaxDriverFrame = 0;
m_bPlaying = false;
2002-12-13 07:44:34 +00:00
}
RageSound::~RageSound()
{
Unload();
}
2005-06-08 04:47:03 +00:00
RageSound::RageSound( const RageSound &cpy ):
RageSoundBase( cpy ),
m_Mutex( "RageSound" )
2002-12-13 07:44:34 +00:00
{
ASSERT(SOUNDMAN);
2005-06-08 04:47:03 +00:00
m_pSource = NULL;
2002-12-13 07:44:34 +00:00
*this = cpy;
}
RageSound &RageSound::operator=( const RageSound &cpy )
{
LockMut(cpy.m_Mutex);
2004-02-28 01:02:06 +00:00
m_Param = cpy.m_Param;
m_iStreamFrame = cpy.m_iStreamFrame;
m_iStoppedSourceFrame = cpy.m_iStoppedSourceFrame;
2005-06-08 04:47:03 +00:00
m_iMaxDriverFrame = 0;
m_bPlaying = false;
delete m_pSource;
if( cpy.m_pSource )
m_pSource = cpy.m_pSource->Copy();
2004-10-23 17:43:49 +00:00
else
2005-06-08 04:47:03 +00:00
m_pSource = NULL;
2002-12-13 07:44:34 +00:00
2002-12-22 08:28:21 +00:00
m_sFilePath = cpy.m_sFilePath;
return *this;
2002-12-13 07:44:34 +00:00
}
void RageSound::Unload()
{
LockMut(m_Mutex);
2002-12-13 07:44:34 +00:00
if(IsPlaying())
2002-12-22 08:13:33 +00:00
StopPlaying();
2002-12-13 07:44:34 +00:00
2005-06-08 04:47:03 +00:00
delete m_pSource;
m_pSource = NULL;
2002-12-27 23:15:39 +00:00
2002-12-13 07:44:34 +00:00
m_sFilePath = "";
}
2004-12-04 21:20:02 +00:00
bool RageSound::IsLoaded() const
2004-12-04 21:13:29 +00:00
{
2005-06-08 04:47:03 +00:00
return m_pSource != NULL;
2004-12-04 21:13:29 +00:00
}
2006-11-29 02:20:10 +00:00
class RageSoundReader_Silence: public RageSoundReader
2004-06-17 01:22:47 +00:00
{
public:
int GetLength() const { return 0; }
int GetLength_Fast() const { return 0; }
int SetPosition( int iFrame ) { return 1; }
2006-12-21 23:41:02 +00:00
int Read( char *buf, int iFrames ) { return RageSoundReader::END_OF_FILE; }
2006-11-29 02:20:10 +00:00
RageSoundReader *Copy() const { return new RageSoundReader_Silence; }
2004-08-21 07:01:54 +00:00
int GetSampleRate() const { return 44100; }
2006-12-10 09:04:58 +00:00
unsigned GetNumChannels() const { return 1; }
int GetNextSourceFrame() const { return 0; }
float GetStreamToSourceRatio() const { return 1.0f; }
2006-12-23 10:04:49 +00:00
RString GetError() const { return ""; }
2004-06-17 01:22:47 +00:00
};
2006-01-22 01:00:06 +00:00
bool RageSound::Load( RString sSoundFilePath )
2002-12-13 07:44:34 +00:00
{
2005-03-16 04:10:10 +00:00
/* Automatically determine whether to precache */
/* TODO: Hook this up to a pref? */
return Load( sSoundFilePath, false );
}
2002-12-13 07:44:34 +00:00
bool RageSound::Load( RString sSoundFilePath, bool bPrecache, const RageSoundLoadParams *pParams )
2005-03-16 04:10:10 +00:00
{
2005-06-08 04:47:03 +00:00
LOG->Trace( "RageSound::LoadSound( '%s', %d )", sSoundFilePath.c_str(), bPrecache );
2003-01-21 02:44:35 +00:00
if( pParams == NULL )
{
static const RageSoundLoadParams Defaults;
pParams = &Defaults;
}
2005-11-23 18:51:00 +00:00
/* If this sound is already preloaded and held by SOUNDMAN, just make a copy
* of that. Since RageSoundReader_Preload is refcounted, this is cheap. */
2006-11-29 02:20:10 +00:00
RageSoundReader *pSound = SOUNDMAN->GetLoadedSound( sSoundFilePath );
if( pSound == NULL )
2004-06-17 01:22:47 +00:00
{
2006-01-22 01:00:06 +00:00
RString error;
pSound = RageSoundReader_FileReader::OpenFile( sSoundFilePath, error );
2005-07-18 19:09:31 +00:00
if( pSound == NULL )
{
LOG->Warn( "RageSound::Load: error opening sound \"%s\": %s",
sSoundFilePath.c_str(), error.c_str() );
pSound = new RageSoundReader_Silence;
}
}
else
{
/* The sound we were given from SOUNDMAN is already preloaded. */
bPrecache = false;
2003-04-23 19:11:59 +00:00
}
LoadSoundReader( pSound );
2004-11-14 04:29:33 +00:00
/* Try to precache. Do this after calling LoadSoundReader() to put the
2005-06-08 04:47:03 +00:00
* sound in this->m_pSource, so we preload after resampling. */
if( bPrecache )
2005-07-18 19:09:31 +00:00
{
if( RageSoundReader_Preload::PreloadSound(m_pSource) )
{
/* We've preloaded the sound. Pass it to SOUNDMAN, for reuse. */
SOUNDMAN->AddLoadedSound( sSoundFilePath, (RageSoundReader_Preload *) m_pSource );
}
else
{
bPrecache = false;
}
2005-07-18 19:09:31 +00:00
}
2004-11-14 04:29:33 +00:00
m_pSource = new RageSoundReader_Extend( m_pSource );
2006-12-22 21:46:03 +00:00
if( !bPrecache )
m_pSource = new RageSoundReader_ThreadedBuffer( m_pSource );
if( pParams->m_bSupportRateChanging )
{
RageSoundReader_PitchChange *pRate = new RageSoundReader_PitchChange( m_pSource );
m_pSource = pRate;
}
2006-12-10 00:43:51 +00:00
if( pParams->m_bSupportPan )
m_pSource = new RageSoundReader_Pan( m_pSource );
m_sFilePath = sSoundFilePath;
2004-04-20 01:55:23 +00:00
m_Mutex.SetName( ssprintf("RageSound (%s)", Basename(sSoundFilePath).c_str() ) );
2002-12-27 23:15:39 +00:00
return true;
2002-12-13 07:44:34 +00:00
}
2006-11-29 02:20:10 +00:00
void RageSound::LoadSoundReader( RageSoundReader *pSound )
{
Unload();
2006-12-23 04:07:46 +00:00
m_iStreamFrame = m_iStoppedSourceFrame = 0;
2006-12-13 09:15:29 +00:00
const int iNeededRate = SOUNDMAN->GetDriverSampleRate();
bool bSupportRateChange = false;
if( iNeededRate != pSound->GetSampleRate() || bSupportRateChange )
{
RageSoundReader_Resample_Good *Resample = new RageSoundReader_Resample_Good( pSound, iNeededRate );
pSound = Resample;
}
m_pSource = pSound;
2002-12-13 07:44:34 +00:00
}
/*
* Retrieve audio data, for mixing. At the time of this call, the frameno at which the
2004-03-18 03:30:36 +00:00
* sound will be played doesn't have to be known. Once committed, and the frameno
* is known, call CommitPCMData.
2004-03-18 03:30:36 +00:00
*
* RageSound::GetDataToPlay and RageSound::FillBuf are the main threaded API. These
* need to execute without blocking other threads from calling eg. GetPositionSeconds,
* since they may take some time to run.
2004-03-18 03:30:36 +00:00
*
2006-12-22 21:43:22 +00:00
* On underrun, if no data was read, returns WOULD_BLOCK. On end of file, if no
* data was read, returns END_OF_FILE. If any data is read, it is returned; these
* conditions are masked and will be seen on the next call. Otherwise, the requested
* number of frames will always be returned.
2004-03-18 03:30:36 +00:00
*/
2006-12-22 21:24:19 +00:00
int RageSound::GetDataToPlay( int16_t *pBuffer, int iFrames, int64_t &iStreamFrame, int &iFramesStored )
2002-12-13 07:44:34 +00:00
{
/* We only update m_iStreamFrame; only take a shared lock, so we don't block the main thread. */
// LockMut(m_Mutex);
2002-12-13 07:44:34 +00:00
2005-06-08 04:47:03 +00:00
ASSERT_M( m_bPlaying, ssprintf("%p", this) );
2006-12-23 04:13:18 +00:00
ASSERT( m_pSource );
2004-01-19 20:37:14 +00:00
2005-06-08 04:47:03 +00:00
iFramesStored = 0;
iStreamFrame = m_iStreamFrame;
2002-12-13 07:44:34 +00:00
while( iFrames > 0 )
2002-12-13 07:44:34 +00:00
{
2006-12-23 04:13:18 +00:00
float fRate = 1.0f;
int iSourceFrame;
/* Read data from our source. */
char *pDest = (char *) pBuffer;
int iGotFrames = m_pSource->RetriedRead( pDest + (iFramesStored * framesize), iFrames, &iSourceFrame, &fRate );
if( iGotFrames == RageSoundReader::ERROR )
2006-12-23 04:16:21 +00:00
{
m_sError = m_pSource->GetError();
LOG->Warn( "Decoding %s failed: %s", GetLoadedFilePath().c_str(), m_sError.c_str() );
}
2006-12-23 04:13:18 +00:00
if( iGotFrames < 0 )
2006-12-23 04:13:18 +00:00
{
if( !iFramesStored )
return iGotFrames;
else
break;
}
m_Mutex.Lock();
m_StreamToSourceMap.Insert( m_iStreamFrame, iGotFrames, iSourceFrame, fRate );
m_Mutex.Unlock();
m_iStreamFrame += iGotFrames;
2002-12-13 07:44:34 +00:00
iFramesStored += iGotFrames;
iFrames -= iGotFrames;
2004-03-18 03:30:36 +00:00
}
2006-12-23 04:13:18 +00:00
if( m_pSource->GetNumChannels() == 1 )
RageSoundUtil::ConvertMonoToStereoInPlace( pBuffer, iFramesStored );
2006-12-22 21:24:19 +00:00
return iFramesStored;
2004-03-18 03:30:36 +00:00
}
/* Indicate that a block of audio data has been written to the device. */
2006-12-23 04:27:41 +00:00
void RageSound::CommitPlayingPosition( int64_t iHardwareFrame, int64_t iStreamFrame, int iGotFrames )
2004-03-18 03:30:36 +00:00
{
2004-04-16 22:28:01 +00:00
m_Mutex.Lock();
2006-12-23 04:27:41 +00:00
m_HardwareToStreamMap.Insert( iHardwareFrame, iGotFrames, iStreamFrame );
2004-04-16 22:28:01 +00:00
m_Mutex.Unlock();
2004-03-18 03:30:36 +00:00
}
2005-11-23 18:31:29 +00:00
/* Start playing from the current position. */
2002-12-22 08:13:33 +00:00
void RageSound::StartPlaying()
2002-12-13 07:44:34 +00:00
{
2005-06-08 04:47:03 +00:00
ASSERT( !m_bPlaying );
2002-12-13 07:44:34 +00:00
2005-09-23 00:12:32 +00:00
/* If m_StartTime is in the past, then we probably set a start time but took too
2004-01-12 01:02:18 +00:00
* long loading. We don't want that; log it, since it can be unobvious. */
2005-09-23 00:12:32 +00:00
if( !m_Param.m_StartTime.IsZero() && m_Param.m_StartTime.Ago() > 0 )
2004-01-12 01:02:18 +00:00
LOG->Trace("Sound \"%s\" has a start time %f seconds in the past",
2005-09-23 00:12:32 +00:00
GetLoadedFilePath().c_str(), m_Param.m_StartTime.Ago() );
2004-01-12 01:02:18 +00:00
2002-12-27 23:15:39 +00:00
/* Tell the sound manager to start mixing us. */
// LOG->Trace("set playing true for %p (StartPlaying) (%s)", this, this->GetLoadedFilePath().c_str());
2005-06-08 04:47:03 +00:00
m_bPlaying = true;
if( !m_Param.m_bIsCriticalSound && SOUNDMAN->GetPlayOnlyCriticalSounds() )
m_Param.m_Volume = 0;
ApplyParams();
SOUNDMAN->StartMixing( this );
// LOG->Trace("StartPlaying %p finished (%s)", this, this->GetLoadedFilePath().c_str());
2002-12-13 07:44:34 +00:00
}
2002-12-22 08:13:33 +00:00
void RageSound::StopPlaying()
2002-12-13 07:44:34 +00:00
{
2005-06-08 04:47:03 +00:00
if( !m_bPlaying )
2003-02-10 21:43:50 +00:00
return;
m_iStoppedSourceFrame = (int) GetPositionSecondsInternal();
2003-01-25 09:01:42 +00:00
/* Tell the sound driver to stop mixing this sound. */
2002-12-13 07:44:34 +00:00
SOUNDMAN->StopMixing(this);
2004-01-15 01:17:47 +00:00
/* Lock the mutex after calling UnregisterPlayingSound. We must not make driver
* calls with our mutex locked (driver mutex < sound mutex). Nobody else will
* see our sound as not playing until we set playing = false. */
m_Mutex.Lock();
// LOG->Trace("set playing false for %p (StopPlaying) (%s)", this, this->GetLoadedFilePath().c_str());
2005-06-08 04:47:03 +00:00
m_bPlaying = false;
2005-06-08 04:47:03 +00:00
m_iMaxDriverFrame = 0;
m_HardwareToStreamMap.Clear();
m_StreamToSourceMap.Clear();
// LOG->Trace("StopPlaying %p finished (%s)", this, this->GetLoadedFilePath().c_str());
m_Mutex.Unlock();
2002-12-13 07:44:34 +00:00
}
/* This is similar to StopPlaying, except it's called by sound drivers when we're done
* playing, rather than by users to as us to stop. (The only difference is that this
* doesn't call SOUNDMAN->StopMixing; there's no reason to tell the sound driver to
* stop mixing, since they're the one telling us we're done.)
*
* This is only called from the main thread. */
void RageSound::SoundIsFinishedPlaying()
{
2005-06-08 04:47:03 +00:00
if( !m_bPlaying )
return;
m_Mutex.Lock();
m_iStoppedSourceFrame = (int) GetPositionSecondsInternal();
// LOG->Trace("set playing false for %p (SoundIsFinishedPlaying) (%s)", this, this->GetLoadedFilePath().c_str());
2005-06-08 04:47:03 +00:00
m_bPlaying = false;
m_iMaxDriverFrame = 0;
m_HardwareToStreamMap.Clear();
m_StreamToSourceMap.Clear();
// LOG->Trace("SoundIsFinishedPlaying %p finished (%s)", this, this->GetLoadedFilePath().c_str());
m_Mutex.Unlock();
}
2005-06-08 04:47:03 +00:00
RageSound *RageSound::Play( const RageSoundParams *pParams )
2002-12-22 08:13:33 +00:00
{
2005-06-08 04:47:03 +00:00
if( m_pSource == NULL )
2005-06-08 04:16:03 +00:00
{
LOG->Warn( "RageSound::Play: sound not loaded" );
return NULL;
}
return SOUNDMAN->PlaySound( *this, pParams );
2002-12-22 08:13:33 +00:00
}
void RageSound::Stop()
{
StopPlaying();
2002-12-22 08:13:33 +00:00
}
bool RageSound::Pause( bool bPause )
{
2005-06-08 04:47:03 +00:00
if( m_pSource == NULL )
2005-06-08 04:16:03 +00:00
{
LOG->Warn( "RageSound::Pause: sound not loaded" );
return false;
}
return SOUNDMAN->Pause( this, bPause );
}
2002-12-22 08:13:33 +00:00
2002-12-13 07:44:34 +00:00
float RageSound::GetLengthSeconds()
{
2005-06-08 04:47:03 +00:00
if( m_pSource == NULL )
2005-06-08 04:16:03 +00:00
{
LOG->Warn( "RageSound::GetLengthSeconds: sound not loaded" );
return -1;
}
2005-06-08 04:47:03 +00:00
int iLength = m_pSource->GetLength();
2002-12-13 07:44:34 +00:00
2005-06-08 04:47:03 +00:00
if( iLength < 0 )
{
2005-11-23 18:51:00 +00:00
LOG->Warn( "GetLengthSeconds failed on %s: %s", GetLoadedFilePath().c_str(), m_pSource->GetError().c_str() );
return -1;
2002-12-13 07:44:34 +00:00
}
2005-06-08 04:47:03 +00:00
return iLength / 1000.f; /* ms -> secs */
2002-12-13 07:44:34 +00:00
}
2004-02-20 04:16:12 +00:00
/* Get the position in frames. */
int64_t RageSound::GetPositionSecondsInternal( bool *bApproximate, RageTimer *pTimer ) const
2004-01-18 02:07:58 +00:00
{
2005-06-08 04:47:03 +00:00
LockMut( m_Mutex );
2004-01-18 02:07:58 +00:00
2005-06-08 04:47:03 +00:00
if( bApproximate )
*bApproximate = false;
2004-01-18 02:07:58 +00:00
/* If we're not playing, just report the static position. */
if( !IsPlaying() )
return m_iStoppedSourceFrame;
2004-01-18 02:07:58 +00:00
2006-12-13 18:38:07 +00:00
/* If we don't yet have any position data, CommitPlayingPosition hasn't yet been called at all,
2004-01-18 02:07:58 +00:00
* so guess what we think the real time is. */
if( m_HardwareToStreamMap.IsEmpty() || m_StreamToSourceMap.IsEmpty() )
2004-01-18 02:07:58 +00:00
{
// LOG->Trace( "no data yet; %i", m_iStoppedSourceFrame );
2005-06-08 04:47:03 +00:00
if( bApproximate )
*bApproximate = true;
return m_iStoppedSourceFrame;
2004-01-18 02:07:58 +00:00
}
/* Get our current hardware position. */
int64_t iCurrentHardwareFrame = SOUNDMAN->GetPosition( pTimer );
2004-01-18 02:07:58 +00:00
/* It's sometimes possible for the hardware position to move backwards, usually
* on underrun. We can try to prevent this in each driver, but it's an obscure
* error, so let's clamp the result here instead. Be sure to reset this on stop,
* since the position may reset. */
if( iCurrentHardwareFrame < m_iMaxDriverFrame )
2004-08-24 23:51:43 +00:00
{
/* Clamp the output to one per second, so one underruns don't cascade due to
* output spam. */
static RageTimer last(RageZeroTimer);
if( last.IsZero() || last.Ago() > 1.0f )
{
LOG->Trace( "Sound %s: driver returned a lesser position (%i < %i)",
this->GetLoadedFilePath().c_str(), (int) iCurrentHardwareFrame, (int) m_iMaxDriverFrame );
2004-08-24 23:51:43 +00:00
last.Touch();
}
}
m_iMaxDriverFrame = iCurrentHardwareFrame = max( iCurrentHardwareFrame, m_iMaxDriverFrame );
bool bApprox;
int64_t iStreamFrame = m_HardwareToStreamMap.Search( iCurrentHardwareFrame, &bApprox );
if( bApproximate && bApprox )
*bApproximate = true;
int64_t iSourceFrame = m_StreamToSourceMap.Search( iStreamFrame, &bApprox );
if( bApproximate && bApprox )
*bApproximate = true;
return iSourceFrame;
2004-01-18 02:07:58 +00:00
}
2004-01-18 04:04:37 +00:00
/*
* If non-NULL, approximate is set to true if the returned time is approximated because of
* underrun, the sound not having started (after Play()) or finished (after EOF) yet.
*
* If non-NULL, Timestamp is set to the real clock time associated with the returned sound
* position. We might take a variable amount of time before grabbing the timestamp (to
* lock SOUNDMAN); we might lose the scheduler after grabbing it, when releasing SOUNDMAN.
*/
2005-06-08 04:47:03 +00:00
float RageSound::GetPositionSeconds( bool *bApproximate, RageTimer *pTimestamp ) const
{
2005-06-08 04:47:03 +00:00
LockMut( m_Mutex );
2004-01-18 04:01:52 +00:00
const int64_t iPositionFrames = GetPositionSecondsInternal( bApproximate, pTimestamp );
return iPositionFrames / float(samplerate());
}
2002-12-27 23:15:39 +00:00
bool RageSound::SetPositionSeconds( float fSeconds )
2002-12-13 07:44:34 +00:00
{
2005-06-08 04:47:03 +00:00
if( m_pSource == NULL )
2005-06-08 04:16:03 +00:00
{
LOG->Warn( "RageSound::SetPositionSeconds(%f): sound not loaded", fSeconds );
return false;
}
return SetPositionFrames( int(fSeconds * samplerate()) );
2002-12-22 08:13:33 +00:00
}
2003-04-24 23:51:05 +00:00
/* This is always the desired sample rate of the current driver. */
int RageSound::GetSampleRate() const
{
2005-06-08 04:47:03 +00:00
if( m_pSource == NULL )
2005-06-08 04:16:03 +00:00
{
LOG->Warn( "RageSound::GetSampleRate(): sound not loaded" );
return 44100;
}
2005-06-08 04:47:03 +00:00
return m_pSource->GetSampleRate();
2003-04-24 23:51:05 +00:00
}
2005-06-08 04:47:03 +00:00
bool RageSound::SetPositionFrames( int iFrames )
2002-12-22 08:13:33 +00:00
{
2005-06-08 04:47:03 +00:00
LockMut( m_Mutex );
2002-12-27 23:15:39 +00:00
2005-06-08 04:47:03 +00:00
if( m_pSource == NULL )
2005-06-08 04:16:03 +00:00
{
2005-06-19 00:21:48 +00:00
LOG->Warn( "RageSound::SetPositionFrames(%d): sound not loaded", iFrames );
2005-06-08 04:16:03 +00:00
return false;
}
2006-12-22 21:44:36 +00:00
int iRet = m_pSource->SetPosition( iFrames );
2005-10-23 07:04:55 +00:00
if( iRet == -1 )
2002-12-27 23:15:39 +00:00
{
2006-12-23 04:16:21 +00:00
m_sError = m_pSource->GetError();
LOG->Warn( "SetPositionFrames: seek %s failed: %s", GetLoadedFilePath().c_str(), m_sError.c_str() );
2002-12-27 23:15:39 +00:00
}
2006-12-23 04:23:19 +00:00
else if( iRet == 0 )
2002-12-27 23:15:39 +00:00
{
/* Seeked past EOF. */
LOG->Warn( "SetPositionFrames: %i samples is beyond EOF in %s",
iFrames, GetLoadedFilePath().c_str() );
2006-12-23 04:23:19 +00:00
}
else
{
m_iStoppedSourceFrame = iFrames;
2002-12-27 23:15:39 +00:00
}
2006-12-23 04:23:19 +00:00
return iRet == 1;
2002-12-13 07:44:34 +00:00
}
2005-10-02 08:35:46 +00:00
float RageSound::GetAbsoluteVolume() const
{
float f = m_Param.m_Volume;
f *= SOUNDMAN->GetMixVolume();
2005-10-02 08:35:46 +00:00
return f;
}
2004-02-28 01:02:06 +00:00
float RageSound::GetPlaybackRate() const
2004-02-28 00:14:16 +00:00
{
return m_Param.m_fSpeed;
2004-02-28 00:14:16 +00:00
}
RageTimer RageSound::GetStartTime() const
2004-02-28 00:14:16 +00:00
{
2005-09-23 00:12:32 +00:00
return m_Param.m_StartTime;
2004-02-28 00:14:16 +00:00
}
void RageSound::SetParams( const RageSoundParams &p )
2004-02-28 00:14:16 +00:00
{
m_Param = p;
ApplyParams();
2004-02-28 00:14:16 +00:00
}
void RageSound::ApplyParams()
{
if( m_pSource == NULL )
return;
m_pSource->SetProperty( "Pitch", m_Param.m_fPitch );
m_pSource->SetProperty( "Speed", m_Param.m_fSpeed );
m_pSource->SetProperty( "StartSecond", m_Param.m_StartSecond );
m_pSource->SetProperty( "LengthSeconds", m_Param.m_LengthSeconds );
m_pSource->SetProperty( "FadeSeconds", m_Param.m_FadeLength );
switch( GetStopMode() )
{
case RageSoundParams::M_LOOP:
m_pSource->SetProperty( "Loop", 1.0f );
break;
case RageSoundParams::M_STOP:
m_pSource->SetProperty( "Stop", 1.0f );
break;
case RageSoundParams::M_CONTINUE:
m_pSource->SetProperty( "Continue", 1.0f );
break;
}
}
2006-12-09 23:54:33 +00:00
bool RageSound::SetProperty( const RString &sProperty, float fValue )
{
return m_pSource->SetProperty( sProperty, fValue );
}
RageSoundParams::StopMode_t RageSound::GetStopMode() const
2004-02-28 00:14:16 +00:00
{
if( m_Param.StopMode != RageSoundParams::M_AUTO )
return m_Param.StopMode;
2005-12-21 08:27:00 +00:00
if( m_sFilePath.find("loop") != string::npos )
return RageSoundParams::M_LOOP;
else
return RageSoundParams::M_STOP;
2003-02-10 21:43:50 +00:00
}
2006-12-04 22:31:25 +00:00
// lua start
#include "LuaBinding.h"
class LunaRageSound: public Luna<RageSound>
{
public:
static int pitch( T* p, lua_State *L )
{
RageSoundParams params( p->GetParams() );
params.m_fPitch = FArg(1);
p->SetParams( params );
return 0;
}
static int speed( T* p, lua_State *L )
{
RageSoundParams params( p->GetParams() );
params.m_fSpeed = FArg(1);
p->SetParams( params );
return 0;
}
2006-12-10 00:36:27 +00:00
static int SetProperty( T* p, lua_State *L )
{
LuaHelpers::Push( L, p->SetProperty(SArg(1), FArg(2)) );
return 1;
}
2006-12-04 22:31:25 +00:00
LunaRageSound()
{
ADD_METHOD( pitch );
ADD_METHOD( speed );
2006-12-10 00:36:27 +00:00
ADD_METHOD( SetProperty );
2006-12-04 22:31:25 +00:00
}
};
LUA_REGISTER_CLASS( RageSound )
// lua end
2003-03-15 23:34:45 +00:00
/*
2004-05-06 02:40:33 +00:00
* Copyright (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/