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;
}
Esempio n. 3
0
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;
}