int RageSoundReader_Split::Read( float *pBuf, int iFrames ) { m_iRequestFrames = iFrames; int iRet = m_pImpl->ReadBuffer(); int iSamplesAvailable = m_pImpl->m_sBuffer.size(); const float *pSrc = &m_pImpl->m_sBuffer[0]; if( m_pImpl->m_iBufferPositionFrames < m_iPositionFrame ) { int iSkipFrames = m_iPositionFrame - m_pImpl->m_iBufferPositionFrames; int iSkipSamples = iSkipFrames * m_pImpl->m_pSource->GetNumChannels(); pSrc += iSkipSamples; iSamplesAvailable -= iSkipSamples; } int iFramesWanted = iFrames; int iFramesAvailable = iSamplesAvailable / (m_pImpl->m_pSource->GetNumChannels()); /* Report any errors from Read() if we don't have any data buffered to * return. If we do have data, finish returning it first. */ if( iFramesAvailable == 0 && iRet < 0 ) return iRet; iFramesAvailable = min( iFramesAvailable, iFramesWanted ); { RageSoundMixBuffer mix; for( int i = 0; i < (int) m_aChannels.size(); ++i ) { const ChannelMap &chan = m_aChannels[i]; mix.SetWriteOffset( chan.m_iToChannel ); mix.write( pSrc + chan.m_iFromChannel, iFramesAvailable, m_pImpl->m_pSource->GetNumChannels(), m_iNumOutputChannels ); } mix.read( pBuf ); } m_iPositionFrame += iFramesAvailable; /* We no longer need the data we requested. Clear our request, so the * memory can be freed. */ m_iRequestFrames = 0; m_pImpl->ReadBuffer(); return iFramesAvailable; }
RageSoundMixBuffer &RageSoundDriver::MixIntoBuffer( int iFrames, int64_t iFrameNumber, int64_t iCurrentFrame ) { ASSERT_M( m_DecodeThread.IsCreated(), "RageSoundDriver::StartDecodeThread() was never called" ); if( iFrameNumber - iCurrentFrame + iFrames > 0 ) { g_iTotalAhead += (int) (iFrameNumber - iCurrentFrame + iFrames); ++g_iTotalAheadCount; } static RageSoundMixBuffer mix; for( unsigned i = 0; i < ARRAYLEN(m_Sounds); ++i ) { /* s.m_pSound can not safely be accessed from here. */ Sound &s = m_Sounds[i]; if( s.m_State == Sound::HALTING ) { /* This indicates that this stream can be reused. */ s.m_bPaused = false; s.m_State = Sound::STOPPED; // LOG->Trace("set %p from HALTING to STOPPED", m_Sounds[i].m_pSound); continue; } if( s.m_State != Sound::STOPPING && s.m_State != Sound::PLAYING ) continue; /* STOPPING or PLAYING. Read sound data. */ if( m_Sounds[i].m_bPaused ) continue; int iGotFrames = 0; int iFramesLeft = iFrames; /* Does the sound have a start time? */ if( !s.m_StartTime.IsZero() && iCurrentFrame != -1 ) { /* If the sound is supposed to start at a time past this buffer, insert silence. */ const int64_t iFramesUntilThisBuffer = iFrameNumber - iCurrentFrame; const float fSecondsBeforeStart = -s.m_StartTime.Ago(); const int64_t iFramesBeforeStart = int64_t(fSecondsBeforeStart * GetSampleRate()); const int iSilentFramesInThisBuffer = clamp( int(iFramesBeforeStart-iFramesUntilThisBuffer), 0, iFramesLeft ); iGotFrames += iSilentFramesInThisBuffer; iFramesLeft -= iSilentFramesInThisBuffer; /* If we didn't completely fill the buffer, then we've written all of the silence. */ if( iFramesLeft ) s.m_StartTime.SetZero(); } /* Fill actual data. */ sound_block *p[2]; unsigned pSize[2]; s.m_Buffer.get_read_pointers( p, pSize ); while( iFramesLeft && pSize[0] ) { if( !p[0]->m_FramesInBuffer ) { /* We've processed all of the sound in this block. Mark it read. */ s.m_Buffer.advance_read_pointer( 1 ); ++p[0]; --pSize[0]; /* If we have more data in p[0], keep going. */ if( pSize[0] ) continue; // more data /* We've used up p[0]. Try p[1]. */ swap( p[0], p[1] ); swap( pSize[0], pSize[1] ); continue; } /* Note that, until we call advance_read_pointer, we can safely write to p[0]. */ const int frames_to_read = min( iFramesLeft, p[0]->m_FramesInBuffer ); mix.SetWriteOffset( iGotFrames*channels ); mix.write( p[0]->m_BufferNext, frames_to_read * channels ); { Sound::QueuedPosMap pos; pos.iStreamFrame = iFrameNumber+iGotFrames; pos.iHardwareFrame = p[0]->m_iPosition; pos.iFrames = frames_to_read; s.m_PosMapQueue.write( &pos, 1 ); } p[0]->m_BufferNext += frames_to_read*channels; p[0]->m_FramesInBuffer -= frames_to_read; p[0]->m_iPosition += frames_to_read; // LOG->Trace( "incr fr rd += %i (state %i) (%p)", // (int) frames_to_read, s.m_State, s.m_pSound ); iGotFrames += frames_to_read; iFramesLeft -= frames_to_read; } /* If we don't have enough to fill the buffer, we've underrun. */ if( iGotFrames < iFrames && s.m_State == Sound::PLAYING ) ++underruns; } return mix; }
int RageSoundReader_Chain::ReadBlock( int16_t *pBuffer, int iFrames ) { /* How many samples should we read before we need to start up a sound? */ int iFramesToRead = INT_MAX; if( m_iNextSound < m_Sounds.size() ) { int iStartFrame = m_iCurrentFrame; int iOffsetFrame = m_Sounds[m_iNextSound].GetOffsetFrame(m_iActualSampleRate); ASSERT_M( iOffsetFrame >= iStartFrame, ssprintf("%i %i", iOffsetFrame, iStartFrame) ); iFramesToRead = iOffsetFrame - iStartFrame; } iFramesToRead = min( iFramesToRead, iFrames ); if( iFramesToRead > 0 && m_apActiveSounds.size() == 1 && m_apActiveSounds.front().fPan == 0 && m_apActiveSounds.front().pSound->GetNumChannels() == m_iChannels && m_apActiveSounds.front().pSound->GetSampleRate() == m_iActualSampleRate ) { /* We have only one source, and it matches our target. Don't mix; read * directly from the source into the destination. This is to optimize * the common case of having one BGM track and no autoplay sounds. */ int iBytes = m_apActiveSounds.front().pSound->Read( (char *) pBuffer, iFramesToRead * sizeof(int16_t) * m_iChannels ); if( iBytes == 0 ) ReleaseSound( 0 ); return iBytes / (sizeof(int16_t) * m_iChannels); } if( iFramesToRead > 0 && !m_apActiveSounds.empty() ) { RageSoundMixBuffer mix; /* Read iFramesToRead from each sound. */ int16_t Buffer[2048]; iFramesToRead = min( iFramesToRead, 1024 ); int iMaxFramesRead = 0; for( unsigned i = 0; i < m_apActiveSounds.size(); ) { ActiveSound &s = m_apActiveSounds[i]; SoundReader *pSound = s.pSound; int iSamples = min( iFramesToRead * pSound->GetNumChannels(), ARRAYLEN(Buffer) ); int iBytesRead = pSound->Read( (char *) Buffer, iSamples*sizeof(int16_t) ); if( iBytesRead == -1 || iBytesRead == 0 ) { /* The sound is at EOF. Release it. */ ReleaseSound( i ); continue; } int iSamplesRead = iBytesRead / sizeof(int16_t); int iFramesRead = iSamplesRead / pSound->GetNumChannels(); iMaxFramesRead = max( iMaxFramesRead, iFramesRead ); if( m_iChannels == 2 && pSound->GetNumChannels() == 1 ) { RageSoundUtil::ConvertMonoToStereoInPlace( Buffer, iSamplesRead ); iSamplesRead *= 2; } if( fabsf(s.fPan) > 0.0001f ) RageSoundUtil::Pan( Buffer, iFramesRead, s.fPan ); mix.write( Buffer, iSamplesRead ); ++i; } /* Read mixed frames into the output buffer. */ mix.read( (int16_t *) pBuffer ); return iMaxFramesRead; } /* If we have more sounds ahead of us, pretend we read the entire block, since * there's silence in between. Otherwise, we're at EOF. */ if( iFramesToRead > 0 ) { memset( pBuffer, 0, iFramesToRead * m_iChannels * sizeof(int16_t) ); return iFramesToRead; } return 0; }