From 0b529a49608371c52dc3381b592caa4b8ce35530 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sun, 10 Dec 2006 07:08:17 +0000 Subject: [PATCH] RageSoundReader::Read() returns sound data which may be at a different rate than the source it's reading from; one second of returned data may correspond to two seconds in the source material. GetStreamToSourceRatio returns the ratio of returned data in the next Read() call to source data. This is propagated upwards in the filter tree, so rate changes by a speed changer in the middle of the tree will be reflected in the final GetStreamToSourceRatio(). This means that whenever the ratio changes, Read() stops returning data; it returns whatever it has, so the caller has an opportunity to call GetStreamToSourceRatio again and notice the change. These semantics can be annoying to implement in some cases, where only the processing of Read() may notice a ratio change. Read() may want to return 0, to say "something changed, call GetStreamToSourceRatio again", but 0 means EOF. Add RageSoundReader::END_OF_FILE. 0 is now no longer a special case; it means "there's more data, I just didn't return any this time". This is functionally equivalent to errno EINTR. --- stepmania/src/RageSound.cpp | 23 +++++--- stepmania/src/RageSoundReader.h | 6 +++ .../src/RageSoundReader_ChannelSplit.cpp | 17 +++--- stepmania/src/RageSoundReader_MP3.cpp | 4 +- stepmania/src/RageSoundReader_Pan.cpp | 6 +++ stepmania/src/RageSoundReader_Preload.cpp | 2 + .../src/RageSoundReader_Resample_Good.cpp | 5 +- stepmania/src/RageSoundReader_SpeedChange.cpp | 53 +++++++++++-------- stepmania/src/RageSoundReader_SpeedChange.h | 4 +- stepmania/src/RageSoundReader_Vorbisfile.cpp | 5 +- stepmania/src/RageSoundReader_WAV.cpp | 12 ++++- 11 files changed, 91 insertions(+), 46 deletions(-) diff --git a/stepmania/src/RageSound.cpp b/stepmania/src/RageSound.cpp index b1e4bba23a..0b8264d3f3 100644 --- a/stepmania/src/RageSound.cpp +++ b/stepmania/src/RageSound.cpp @@ -288,18 +288,29 @@ int RageSound::GetData( char *pBuffer, int iFrames ) fRate = m_pSource->GetStreamToSourceRatio(); int iNewSourceFrame = m_pSource->GetNextSourceFrame(); - iGotFrames = m_pSource->Read( pBuffer, iFrames ); - if( iGotFrames == -1 ) + while( iGotFrames == 0 ) { - Fail( m_pSource->GetError() ); + iGotFrames = m_pSource->Read( pBuffer, iFrames ); + if( iGotFrames == RageSoundReader::ERROR ) + { + Fail( m_pSource->GetError() ); - /* Pretend we got EOF. */ - return 0; + /* Pretend we got EOF. */ + return 0; + } + + if( iGotFrames == RageSoundReader::END_OF_FILE ) + { + iGotFrames = 0; + break; + } + + ASSERT_M( iGotFrames >= 0, ssprintf("%i", iGotFrames) ); // unhandled error condition } /* If we didn't get any data, don't update iSourceFrame, so we just keep * extrapolating if we're in M_CONTINUE. */ - if( iGotFrames != 0 ) + if( iGotFrames == 0 ) iSourceFrame = iNewSourceFrame; if( m_pSource->GetNumChannels() == 1 ) diff --git a/stepmania/src/RageSoundReader.h b/stepmania/src/RageSoundReader.h index 8b6812eb88..d917eee213 100644 --- a/stepmania/src/RageSoundReader.h +++ b/stepmania/src/RageSoundReader.h @@ -18,6 +18,12 @@ public: virtual bool IsStreamingFromDisk() const = 0; virtual bool SetProperty( const RString &sProperty, float fValue ) { return false; } + /* Return values for Read(). */ + enum { + ERROR = -1, + END_OF_FILE = -2 + }; + /* GetNextSourceFrame() provides the source frame associated with the next frame * that will be read via Read(). GetStreamToSourceRatio() returns the ratio * for extrapolating the source frames of the remainder of the block. These diff --git a/stepmania/src/RageSoundReader_ChannelSplit.cpp b/stepmania/src/RageSoundReader_ChannelSplit.cpp index 09006732d5..900da120d1 100644 --- a/stepmania/src/RageSoundReader_ChannelSplit.cpp +++ b/stepmania/src/RageSoundReader_ChannelSplit.cpp @@ -48,7 +48,7 @@ public: } /* Request that m_sBuffer contain frames [iStartFrame,iStartFrame+iFrames). */ - bool ReadBuffer(); + int ReadBuffer(); int m_iRefCount; RageSoundReader *m_pSource; @@ -111,10 +111,11 @@ bool RageSoundReader_Split::SetProperty( const RString &sProperty, float fValue int RageSoundReader_Split::Read( char *pBuf, int iFrames ) { m_iRequestFrames = iFrames; - if( !m_pImpl->ReadBuffer() ) + int iRet = m_pImpl->ReadBuffer(); + if( iRet < 0 ) { this->SetError( m_pImpl->m_pSource->GetError() ); - return -1; + return iRet; } int iBytesAvailable = m_pImpl->m_sBuffer.size(); @@ -153,7 +154,7 @@ int RageSoundReader_Split::Read( char *pBuf, int iFrames ) return iFramesAvailable; } -bool RageSoundSplitterImpl::ReadBuffer() +int RageSoundSplitterImpl::ReadBuffer() { /* Discard any bytes that are no longer requested by any sound. */ int iMinFrameRequested = INT_MAX; @@ -182,21 +183,21 @@ bool RageSoundSplitterImpl::ReadBuffer() int iFramesToRead = iMaxFrameRequested - (m_iBufferPositionFrames + iFramesBuffered); if( iFramesToRead <= 0 ) - return true; // requested data already buffered + return 1; // requested data already buffered int iBytesToRead = iFramesToRead * sizeof(int16_t) * m_pSource->GetNumChannels(); int iOldSizeBytes = m_sBuffer.size(); m_sBuffer.resize( iOldSizeBytes + iBytesToRead ); int iGotFrames = m_pSource->Read( &m_sBuffer[0] + iOldSizeBytes, iFramesToRead ); - if( iGotFrames == -1 ) + if( iGotFrames < 0 ) { m_sBuffer.resize( iOldSizeBytes ); - return false; + return iGotFrames; } int iGotBytes = iGotFrames * sizeof(int16_t) * m_pSource->GetNumChannels(); m_sBuffer.resize( iOldSizeBytes + iGotBytes ); - return true; + return 1; } void RageSoundReader_Split::AddSourceChannelToSound( int iFromChannel, int iToChannel ) diff --git a/stepmania/src/RageSoundReader_MP3.cpp b/stepmania/src/RageSoundReader_MP3.cpp index c321fbd398..166bb9aba2 100644 --- a/stepmania/src/RageSoundReader_MP3.cpp +++ b/stepmania/src/RageSoundReader_MP3.cpp @@ -765,9 +765,9 @@ int RageSoundReader_MP3::Read( char *buf, int iFrames ) /* Decode more from the MP3 stream. */ int ret = do_mad_frame_decode(); if( ret == 0 ) - return iFramesWritten; + return END_OF_FILE; if( ret == -1 ) - return -1; + return ERROR; synth_output(); } diff --git a/stepmania/src/RageSoundReader_Pan.cpp b/stepmania/src/RageSoundReader_Pan.cpp index e37a204317..fb3ea50604 100644 --- a/stepmania/src/RageSoundReader_Pan.cpp +++ b/stepmania/src/RageSoundReader_Pan.cpp @@ -17,6 +17,12 @@ int RageSoundReader_Pan::Read( char *pBuf, int iFrames ) iFrames /= 2; iFrames = m_pSource->Read( pBuf, iFrames ); + if( iFrames < 0 ) + { + this->SetError( m_pSource->GetError() ); + return iFrames; + } + int iSamples = iFrames * m_pSource->GetNumChannels(); int16_t *pSampleBuf = (int16_t *) pBuf; diff --git a/stepmania/src/RageSoundReader_Preload.cpp b/stepmania/src/RageSoundReader_Preload.cpp index 17abd46c83..b70f93a511 100644 --- a/stepmania/src/RageSoundReader_Preload.cpp +++ b/stepmania/src/RageSoundReader_Preload.cpp @@ -126,6 +126,8 @@ int RageSoundReader_Preload::Read( char *pBuffer, int iFrames ) const int iFramesAvail = iSizeFrames - m_iPosition; iFrames = min( iFrames, iFramesAvail ); + if( iFrames == 0 ) + return END_OF_FILE; memcpy( pBuffer, m_Buffer->data() + (m_iPosition * samplesize), iFrames * samplesize ); m_iPosition += iFrames; diff --git a/stepmania/src/RageSoundReader_Resample_Good.cpp b/stepmania/src/RageSoundReader_Resample_Good.cpp index dab5e13bce..1a30d5b675 100644 --- a/stepmania/src/RageSoundReader_Resample_Good.cpp +++ b/stepmania/src/RageSoundReader_Resample_Good.cpp @@ -726,6 +726,7 @@ int RageSoundReader_Resample_Good::Read( char *pBuf_, int iFrames ) return iFramesRead; } + // XXX: errors return m_pSource->Read( pBuf_, iFrames ); } @@ -735,10 +736,10 @@ int RageSoundReader_Resample_Good::Read( char *pBuf_, int iFrames ) ASSERT( pTmpBuf ); int iFramesIn = m_pSource->Read( (char *) pTmpBuf, iFramesNeeded ); - if( iFramesIn == -1 ) + if( iFramesIn < 0 ) { SetError( m_pSource->GetError() ); - return -1; + return iFramesIn; } const int iSamplesIn = iFramesIn * iChannels; diff --git a/stepmania/src/RageSoundReader_SpeedChange.cpp b/stepmania/src/RageSoundReader_SpeedChange.cpp index 3b99855810..6397ddf24d 100644 --- a/stepmania/src/RageSoundReader_SpeedChange.cpp +++ b/stepmania/src/RageSoundReader_SpeedChange.cpp @@ -85,7 +85,7 @@ static int FindClosestMatch( const int16_t *pBuffer, int iBufferSize, const int1 return iBestOffset; } -void RageSoundReader_SpeedChange::FillData( int iMaxFrames ) +int RageSoundReader_SpeedChange::FillData( int iMaxFrames ) { /* XXX: If the rate or source frame offset changes in the source, we should stop * at that point, so the changes propagate upward. That's tricky, since we want @@ -97,13 +97,16 @@ void RageSoundReader_SpeedChange::FillData( int iMaxFrames ) int iFramesToRead = iMaxFrames - m_iDataBufferAvailFrames; int iBytesToRead = iFramesToRead * m_Channels.size() * sizeof(int16_t); if( iBytesToRead <= 0 ) - return; + return m_iDataBufferAvailFrames; int16_t *pTempBuffer = (int16_t *) alloca( iBytesToRead ); int iGotFrames = m_pSource->Read( (char *) pTempBuffer, iFramesToRead ); - // if( iGotFrames == -1 ) XXX - if( !iGotFrames ) - return; + if( iGotFrames < 0 ) + { + if( iGotFrames == END_OF_FILE && m_iDataBufferAvailFrames ) + return m_iDataBufferAvailFrames; + return iGotFrames; + } for( size_t i = 0; i < m_Channels.size(); ++i ) { @@ -124,6 +127,7 @@ void RageSoundReader_SpeedChange::FillData( int iMaxFrames ) m_iDataBufferAvailFrames += iGotFrames; } + return m_iDataBufferAvailFrames; } void RageSoundReader_SpeedChange::EraseData( int iFramesToDelete ) @@ -145,15 +149,10 @@ void RageSoundReader_SpeedChange::EraseData( int iFramesToDelete ) } } -bool RageSoundReader_SpeedChange::Step() +int RageSoundReader_SpeedChange::Step() { if( m_iDataBufferAvailFrames == 0 ) - { - FillData( GetWindowSizeFrames() ); - if( m_iDataBufferAvailFrames == 0 ) - return false; - return true; - } + return FillData( GetWindowSizeFrames() ); /* If m_iPos is non-zero, we just finished playing a previous block, so advance forward. */ if( m_iPos ) @@ -186,15 +185,21 @@ bool RageSoundReader_SpeedChange::Step() EraseData( iToDelete ); /* Fill as much data as we might need to do the search and use the result. */ - int iMaxPositionNeeded = m_iUncorrelatedPos + GetToleranceFrames() + GetWindowSizeFrames(); - for( size_t i = 0; i < m_Channels.size(); ++i ) - iMaxPositionNeeded = max( iMaxPositionNeeded, m_Channels[i].m_iCorrelatedPos + GetWindowSizeFrames() ); - FillData( iMaxPositionNeeded ); - if( iMaxPositionNeeded > m_iDataBufferAvailFrames ) { - /* We're at EOF. Flush the remaining data, if any. */ - m_iUncorrelatedPos = m_Channels[0].m_iCorrelatedPos; - return true; + int iMaxPositionNeeded = m_iUncorrelatedPos + GetToleranceFrames() + GetWindowSizeFrames(); + for( size_t i = 0; i < m_Channels.size(); ++i ) + iMaxPositionNeeded = max( iMaxPositionNeeded, m_Channels[i].m_iCorrelatedPos + GetWindowSizeFrames() ); + + int iGot = FillData( iMaxPositionNeeded ); + if( iGot < 0 ) + return iGot; + + if( iMaxPositionNeeded > m_iDataBufferAvailFrames ) + { + /* We're at EOF. Flush the remaining data, if any. */ + m_iUncorrelatedPos = m_Channels[0].m_iCorrelatedPos; + return m_iDataBufferAvailFrames; + } } /* Starting at our preferred position (m_iUncorrelatedPos), within GetToleranceFrames(), @@ -214,7 +219,7 @@ bool RageSoundReader_SpeedChange::Step() c.m_iCorrelatedPos = iBest + m_iUncorrelatedPos; ASSERT( m_Channels[i].m_iCorrelatedPos + GetWindowSizeFrames() <= m_iDataBufferAvailFrames ); } - return true; + return m_iDataBufferAvailFrames; } int RageSoundReader_SpeedChange::GetCursorAvail() const @@ -247,9 +252,11 @@ int RageSoundReader_SpeedChange::Read( char *buf, int iFrames ) if( iCursorAvail == 0 ) { - Step(); + int iRet = Step(); + if( iRet < 0 ) + return iRet; if( !GetCursorAvail() ) - return 0; // EOF + return END_OF_FILE; continue; } diff --git a/stepmania/src/RageSoundReader_SpeedChange.h b/stepmania/src/RageSoundReader_SpeedChange.h index 4ca9f39848..68d8e14600 100644 --- a/stepmania/src/RageSoundReader_SpeedChange.h +++ b/stepmania/src/RageSoundReader_SpeedChange.h @@ -25,9 +25,9 @@ public: bool NextReadWillStep() const { return GetCursorAvail() == 0; } protected: - void FillData( int iMax ); + int FillData( int iMax ); void EraseData( int iToDelete ); - bool Step(); + int Step(); void Reset(); /* Get the ratio last set by SetSpeedRatio. */ diff --git a/stepmania/src/RageSoundReader_Vorbisfile.cpp b/stepmania/src/RageSoundReader_Vorbisfile.cpp index 240a072be2..cf61411f21 100644 --- a/stepmania/src/RageSoundReader_Vorbisfile.cpp +++ b/stepmania/src/RageSoundReader_Vorbisfile.cpp @@ -225,7 +225,7 @@ int RageSoundReader_Vorbisfile::Read( char *buf, int iFrames ) if( ret == OV_EBADLINK ) { SetError( ssprintf("Read: OV_EBADLINK") ); - return -1; + return ERROR; } if( ret == 0 ) @@ -243,6 +243,9 @@ int RageSoundReader_Vorbisfile::Read( char *buf, int iFrames ) iFrames -= iFramesRead; } + if( !frames_read ) + return END_OF_FILE; + return frames_read; } diff --git a/stepmania/src/RageSoundReader_WAV.cpp b/stepmania/src/RageSoundReader_WAV.cpp index 7b90406a6a..a47d728727 100644 --- a/stepmania/src/RageSoundReader_WAV.cpp +++ b/stepmania/src/RageSoundReader_WAV.cpp @@ -79,6 +79,9 @@ struct WavReaderPCM: public WavReader len /= 2; const int iBytesLeftInDataChunk = m_WavData.m_iDataChunkSize - (m_File.Tell() - m_WavData.m_iDataChunkPos); + if( !iBytesLeftInDataChunk ) + return RageSoundReader::END_OF_FILE; + len = min( len, iBytesLeftInDataChunk ); int iGot = m_File.Read( buf, len ); int iGotSamples = 0; @@ -296,10 +299,15 @@ public: if( m_iBufferUsed == m_iBufferAvail ) { if( !DecodeADPCMBlock() ) - return -1; + return RageSoundReader::ERROR; } if( m_iBufferAvail == 0 ) - break; /* EOF */ + { + if( !iGotFrames ) + return RageSoundReader::END_OF_FILE; + else + return iGotFrames; + } int iFramesToCopy = (m_iBufferAvail-m_iBufferUsed) / iBytesPerFrame; iFramesToCopy = min( iFramesToCopy, (int) (iFrames-iGotFrames) );