diff --git a/stepmania/src/RageSound.cpp b/stepmania/src/RageSound.cpp index 8fb99650d2..7e7cbaaec6 100644 --- a/stepmania/src/RageSound.cpp +++ b/stepmania/src/RageSound.cpp @@ -44,10 +44,6 @@ const int internal_buffer_size = 1024*16; /* The amount of data to read from SDL_sound at once. */ const unsigned read_block_size = 1024; -/* The number of frames we should keep pos_map data for. This being too high - * is mostly harmless; the data is small. */ -const int pos_map_backlog_frames = 100000; - RageSoundParams::RageSoundParams(): StartTime( RageZeroTimer ) { @@ -197,7 +193,7 @@ void RageSound::Update(float delta) LockMut(SOUNDMAN->lock); /* Erase old pos_map data. */ - CleanPosMap( pos_map ); +// CleanPosMap( pos_map ); } /* Return the number of bytes available in the input buffer. */ @@ -514,21 +510,7 @@ bool RageSound::GetDataToPlay( int16_t *buffer, int size, int &sound_frame, int /* Indicate that a block of audio data has been written to the device. */ void RageSound::CommitPlayingPosition( int64_t frameno, int pos, int got_frames ) { - LockMut(SOUNDMAN->lock); - - if( pos_map.size() ) - { - /* Optimization: If the last entry lines up with this new entry, just merge them. */ - pos_map_t &last = pos_map.back(); - if( last.frameno+last.frames == frameno && - last.position+last.frames == pos ) - { - last.frames += got_frames; - return; - } - } - - pos_map.push_back( pos_map_t( frameno, pos, got_frames ) ); + pos_map.Insert( frameno, pos, got_frames ); } /* Called by the mixer: return a block of sound data. @@ -610,7 +592,7 @@ void RageSound::StopPlaying() playing = false; playing_thread = 0; - pos_map.clear(); + pos_map.Clear(); } /* This is similar to StopPlaying, except it's called by sound drivers when we're done @@ -631,7 +613,7 @@ void RageSound::SoundIsFinishedPlaying() playing = false; playing_thread = 0; - pos_map.clear(); + pos_map.Clear(); } RageSound *RageSound::Play( const RageSoundParams *params ) @@ -660,77 +642,6 @@ float RageSound::GetLengthSeconds() return len / 1000.f; /* ms -> secs */ } -int64_t RageSound::SearchPosMap( const deque &pos_map, int64_t cur_frame, bool *approximate ) -{ - /* cur_frame is probably in pos_map. Search to figure out what position - * it maps to. */ - int64_t closest_position = 0, closest_position_dist = INT_MAX; - int closest_block = 0; /* print only */ - for( unsigned i = 0; i < pos_map.size(); ++i ) - { - if( cur_frame >= pos_map[i].frameno && - cur_frame < pos_map[i].frameno+pos_map[i].frames ) - { - /* cur_frame lies in this block; it's an exact match. Figure - * out the exact position. */ - int64_t diff = pos_map[i].position - pos_map[i].frameno; - return cur_frame + diff; - } - - /* See if the current position is close to the beginning of this block. */ - int64_t dist = llabs( pos_map[i].frameno - cur_frame ); - if( dist < closest_position_dist ) - { - closest_position_dist = dist; - closest_block = i; - closest_position = pos_map[i].position - dist; - } - - /* See if the current position is close to the end of this block. */ - dist = llabs( pos_map[i].frameno + pos_map[i].frames - cur_frame ); - if( dist < closest_position_dist ) - { - closest_position_dist = dist; - closest_position = pos_map[i].position + pos_map[i].frames + dist; - } - } - - /* The frame is out of the range of data we've actually sent. - * Return the closest position. - * - * There are three cases when this happens: - * 1. After the first GetPCM call, but before it actually gets heard. - * 2. After GetPCM returns EOF and the sound has flushed, but before - * SoundStopped has been called. - * 3. Underflow; we'll be given a larger frame number than we know about. - */ - /* XXX: %lli normally, %I64i in Windows */ - LOG->Trace( "Approximate sound time: driver frame %lli, pos_map frame %lli (dist %lli), closest position is %lli", - cur_frame, pos_map[closest_block].frameno, closest_position_dist, closest_position ); - - if( approximate ) - *approximate = true; - return closest_position; -} - -void RageSound::CleanPosMap( deque &pos_map ) -{ - LockMut( SOUNDMAN->lock ); - - /* Determine the number of frames of data we have. */ - int64_t total_frames = 0; - for( unsigned i = 0; i < pos_map.size(); ++i ) - total_frames += pos_map[i].frames; - - /* Remove the oldest entry so long we'll stil have enough data. Don't delete every - * frame, so we'll always have some data to extrapolate from. */ - while( pos_map.size() > 1 && total_frames - pos_map.front().frames > pos_map_backlog_frames ) - { - total_frames -= pos_map.front().frames; - pos_map.pop_front(); - } -} - /* Get the position in frames. */ int64_t RageSound::GetPositionSecondsInternal( bool *approximate ) const { @@ -745,7 +656,7 @@ int64_t RageSound::GetPositionSecondsInternal( bool *approximate ) const /* If we don't yet have any position data, GetPCM hasn't yet been called at all, * so guess what we think the real time is. */ - if(pos_map.empty()) + if( pos_map.IsEmpty() ) { LOG->Trace("no data yet; %i", stopped_position); if( approximate ) @@ -759,7 +670,7 @@ int64_t RageSound::GetPositionSecondsInternal( bool *approximate ) const /* Before using pos_map, flush any incoming positions. */ SOUNDMAN->FlushPosMapQueue(); - return SearchPosMap( pos_map, cur_frame, approximate ); + return pos_map.Search( cur_frame, approximate ); } /* diff --git a/stepmania/src/RageSound.h b/stepmania/src/RageSound.h index 2ed638d428..e37d6a90e5 100644 --- a/stepmania/src/RageSound.h +++ b/stepmania/src/RageSound.h @@ -4,6 +4,7 @@ #include #include "RageTimer.h" #include "RageUtil_CircularBuffer.h" +#include "RageSoundPosMap.h" class SoundReader; @@ -122,25 +123,8 @@ private: CircBuf databuf; int FillBuf(int bytes); - /* Sound blocks we've sent out recently through GetPCM. We keep track - * of each block for the last four calls of GetPCM. */ - struct pos_map_t - { - /* Frame number from the POV of the sound driver: */ - int64_t frameno; - - /* Actual sound position within the sample: */ - int64_t position; - - /* The number of frames in this block: */ - int64_t frames; - - pos_map_t() { frameno=0; position=0; frames=0; } - pos_map_t( int64_t frame, int pos, int cnt ) { frameno=frame; position=pos; frames=cnt; } - }; - deque pos_map; - static int64_t SearchPosMap( const deque &pos_map, int64_t cur_frames, bool *approximate ); - static void CleanPosMap( deque &pos_map ); + /* We keep track of sound blocks we've sent out recently through GetDataToPlay. */ + pos_map_queue pos_map; CString m_sFilePath; diff --git a/stepmania/src/RageSoundPosMap.cpp b/stepmania/src/RageSoundPosMap.cpp new file mode 100644 index 0000000000..dd91e3f842 --- /dev/null +++ b/stepmania/src/RageSoundPosMap.cpp @@ -0,0 +1,153 @@ +#include "global.h" + +#include "RageSoundPosMap.h" +#include "RageLog.h" +#include "RageUtil.h" + +/* The number of frames we should keep pos_map data for. This being too high + * is mostly harmless; the data is small. */ +const int pos_map_backlog_frames = 100000; + +pos_map_queue::pos_map_queue(): m_Mutex("pos_map_queue") +{ +} + + +pos_map_queue::pos_map_queue( const pos_map_queue &cpy ): m_Mutex("pos_map_queue") +{ + *this = cpy; +} + +pos_map_queue &pos_map_queue::operator=( const pos_map_queue &cpy ) +{ + /* Hack: to prevent deadlock, always lock the lesser pointer first. */ + if( this < &cpy ) + { + m_Mutex.Lock(); + cpy.m_Mutex.Lock(); + } else { + cpy.m_Mutex.Lock(); + m_Mutex.Lock(); + } + + m_Queue = cpy.m_Queue; + + /* Unlock order doesn't matter. */ + m_Mutex.Unlock(); + cpy.m_Mutex.Unlock(); + return *this; +} + +void pos_map_queue::Insert( int64_t frameno, int pos, int got_frames ) +{ + LockMut(m_Mutex); + + if( m_Queue.size() ) + { + /* Optimization: If the last entry lines up with this new entry, just merge them. */ + pos_map_t &last = m_Queue.back(); + if( last.frameno+last.frames == frameno && + last.position+last.frames == pos ) + { + last.frames += got_frames; + return; + } + } + + m_Queue.push_back( pos_map_t( frameno, pos, got_frames ) ); + + Cleanup(); +} + +void pos_map_queue::Cleanup() +{ + LockMut(m_Mutex); + + /* Determine the number of frames of data we have. */ + int64_t total_frames = 0; + for( unsigned i = 0; i < m_Queue.size(); ++i ) + total_frames += m_Queue[i].frames; + + /* Remove the oldest entry so long we'll stil have enough data. Don't delete every + * frame, so we'll always have some data to extrapolate from. */ + while( m_Queue.size() > 1 && total_frames - m_Queue.front().frames > pos_map_backlog_frames ) + { + total_frames -= m_Queue.front().frames; + m_Queue.pop_front(); + } +} + +int64_t pos_map_queue::Search( int64_t frame, bool *approximate ) const +{ + LockMut(m_Mutex); + + if( IsEmpty() ) + { + if( approximate ) + *approximate = true; + return 0; + } + + /* frame is probably in pos_map. Search to figure out what position + * it maps to. */ + int64_t closest_position = 0, closest_position_dist = INT_MAX; + int closest_block = 0; /* print only */ + for( unsigned i = 0; i < m_Queue.size(); ++i ) + { + if( frame >= m_Queue[i].frameno && + frame < m_Queue[i].frameno+m_Queue[i].frames ) + { + /* frame lies in this block; it's an exact match. Figure + * out the exact position. */ + int64_t diff = m_Queue[i].position - m_Queue[i].frameno; + return frame + diff; + } + + /* See if the current position is close to the beginning of this block. */ + int64_t dist = llabs( m_Queue[i].frameno - frame ); + if( dist < closest_position_dist ) + { + closest_position_dist = dist; + closest_block = i; + closest_position = m_Queue[i].position - dist; + } + + /* See if the current position is close to the end of this block. */ + dist = llabs( m_Queue[i].frameno + m_Queue[i].frames - frame ); + if( dist < closest_position_dist ) + { + closest_position_dist = dist; + closest_position = m_Queue[i].position + m_Queue[i].frames + dist; + } + } + + /* The frame is out of the range of data we've actually sent. + * Return the closest position. + * + * There are three cases when this happens: + * 1. After the first GetPCM call, but before it actually gets heard. + * 2. After GetPCM returns EOF and the sound has flushed, but before + * SoundStopped has been called. + * 3. Underflow; we'll be given a larger frame number than we know about. + */ + /* XXX: %lli normally, %I64i in Windows */ + LOG->Trace( "Approximate sound time: driver frame %lli, m_Queue frame %lli (dist %lli), closest position is %lli", + frame, m_Queue[closest_block].frameno, closest_position_dist, closest_position ); + + if( approximate ) + *approximate = true; + return closest_position; +} + +void pos_map_queue::Clear() +{ + LockMut(m_Mutex); + m_Queue.clear(); +} + +bool pos_map_queue::IsEmpty() const +{ + LockMut(m_Mutex); + return m_Queue.empty(); +} + diff --git a/stepmania/src/RageSoundPosMap.h b/stepmania/src/RageSoundPosMap.h new file mode 100644 index 0000000000..90d40703b8 --- /dev/null +++ b/stepmania/src/RageSoundPosMap.h @@ -0,0 +1,48 @@ +#ifndef RAGE_SOUND_POS_MAP_H +#define RAGE_SOUND_POS_MAP_H + +#include "RageThreads.h" +#include + +struct pos_map_t +{ + /* Frame number from the POV of the sound driver: */ + int64_t frameno; + + /* Actual sound position within the sample: */ + int64_t position; + + /* The number of frames in this block: */ + int64_t frames; + + pos_map_t() { frameno=0; position=0; frames=0; } + pos_map_t( int64_t frame, int pos, int cnt ) { frameno=frame; position=pos; frames=cnt; } +}; + +/* This class maps one range of frames to another. */ +class pos_map_queue +{ + deque m_Queue; + mutable RageMutex m_Mutex; + + void Cleanup(); + +public: + pos_map_queue(); + pos_map_queue( const pos_map_queue &cpy ); + pos_map_queue &operator=( const pos_map_queue &cpy ); + + /* Insert a mapping from frameno to position, containing pos got_frames. */ + void Insert( int64_t frameno, int position, int got_frames ); + + /* Return the position for the given frameno. */ + int64_t Search( int64_t frameno, bool *approximate ) const; + + /* Erase all mappings. */ + void Clear(); + + bool IsEmpty() const; +}; + + +#endif