fmodex/fmod/src/fmod_sound_stream.cpp

755 lines
18 KiB
C++
Executable file

#include "fmod_settings.h"
#ifdef FMOD_SUPPORT_STREAMING
#include "fmod_async.h"
#include "fmod_codeci.h"
#include "fmod_channel_stream.h"
#include "fmod_file.h"
#include "fmod_sound_sample.h"
#include "fmod_sound_stream.h"
#include "fmod_systemi.h"
namespace FMOD
{
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
Stream::Stream()
{
mLastPos = 0;
mSubSound = 0;
mLoopCountCurrent = -1;
mBlockSize = 1;
mChannel = 0;
mInitialPosition = 0;
}
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Stream::fill(unsigned int offset, unsigned int length, unsigned int *read_out, bool calledfromsentence)
{
FMOD_RESULT result = FMOD_OK;
unsigned int read = 0;
Sample *sample = mSample;
if (read_out)
{
*read_out = 0;
}
if (mSubSoundParent)
{
sample = ((Stream *)mSubSoundParent)->mSample;
}
if (!(mFlags & FMOD_SOUND_FLAG_FINISHED))
{
unsigned int r;
unsigned int size;
unsigned int len;
#ifdef FMOD_SUPPORT_SENTENCING
if (mSubSoundList)
{
Stream *stream = this;
int count = 0;
do
{
stream = SAFE_CAST(Stream, mSubSound[mSubSoundList[mChannel->mSubSoundListCurrent].mIndex]);
if (!stream)
{
if (count >= mSubSoundListNum)
{
return FMOD_ERR_SUBSOUNDS;
}
count++;
mChannel->mSubSoundListCurrent++;
if (mChannel->mSubSoundListCurrent >= mSubSoundListNum)
{
if (!(mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent))
{
mChannel->mSubSoundListCurrent = mSubSoundListNum - 1;
mPosition = mLength;
mFlags |= FMOD_SOUND_FLAG_FINISHED;
return FMOD_ERR_FILE_EOF;
}
else
{
mChannel->mSubSoundListCurrent = 0;
mPosition = 0;
}
}
}
} while (!stream);
len = length;
do
{
unsigned int r;
unsigned int endpoint;
if (mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent)
{
endpoint = mLoopStart + mLoopLength - 1;
}
else
{
if (sample->mCodec->mFlags & FMOD_CODEC_ACCURATELENGTH)
{
endpoint = mLength - 1;
}
else
{
endpoint = (unsigned int)-1;
}
}
size = len;
if (mPosition > endpoint)
{
size = 0;
}
else if (mPosition + size > endpoint)
{
size = (endpoint - mPosition) + 1;
}
result = stream->fill(offset, size, &r, true);
if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF))
{
return result;
}
read += r;
len -= r;
size -= r;
offset += r;
mPosition += r;
if (mPosition > endpoint || result == FMOD_ERR_FILE_EOF)
{
bool finished = false;
if (mPosition > endpoint)
{
if (mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent)
{
setPosition(mLoopStart, FMOD_TIMEUNIT_PCM);
if (mLoopCountCurrent > 0)
{
mLoopCountCurrent--;
}
stream = SAFE_CAST(Stream, mSubSound[mSubSoundIndex]);
}
else
{
mPosition = mLength;
mFlags |= FMOD_SOUND_FLAG_FINISHED;
break;
}
}
else
{
do
{
mChannel->mSubSoundListCurrent++;
if (mChannel->mSubSoundListCurrent >= mSubSoundListNum)
{
if (!(mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent))
{
finished = true;
mChannel->mSubSoundListCurrent = mSubSoundListNum - 1;
mPosition = mLength;
mFlags |= FMOD_SOUND_FLAG_FINISHED;
break;
}
else
{
mChannel->mSubSoundListCurrent = 0;
mPosition = 0;
}
}
mSubSoundIndex = mSubSoundList[mChannel->mSubSoundListCurrent].mIndex;
stream = SAFE_CAST(Stream, mSubSound[mSubSoundIndex]);
if (mSubSoundShared)
{
stream->updateSubSound(mSubSoundIndex, true);
}
} while (!stream && !finished); /* While loop skips past null entries. */
if (finished)
{
break;
}
if (stream)
{
sample->mCodec = stream->mCodec; /* Switch parent level codec to the new substream's codec */
if (mCodec != sample->mCodec)
{
result = sample->seek(-1, 0);
}
else
{
result = sample->seek(mSubSoundIndex, 0);
}
stream->mPosition = 0;
stream->mFlags &= ~FMOD_SOUND_FLAG_FINISHED; /* If it was read and finished before, the seek to 0 will unfinish it. */
}
}
}
} while (len);
}
else
#endif
{
len = length;
do
{
unsigned int endpoint, streamlength = mLength;
Stream *stream = this;
unsigned int bytestoread = 0;
if (mSubSound)
{
stream = SAFE_CAST(Stream, mSubSound[mSubSoundIndex]);
if (!stream)
{
break;
}
}
if (mSubSoundShared)
{
FMOD_CODEC_WAVEFORMAT waveformat;
stream->mCodec->mDescription.getwaveformat(stream->mCodec, mSubSoundIndex, &waveformat);
streamlength = waveformat.lengthpcm;
}
else
{
streamlength = stream->mLength;
}
if (mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent && !calledfromsentence) /* Ignore loop modes/points in a sentence. */
{
endpoint = stream->mLoopStart + stream->mLoopLength - 1;
}
else
{
if (sample->mCodec->mFlags & FMOD_CODEC_ACCURATELENGTH)
{
endpoint = streamlength - 1;
}
else
{
endpoint = (unsigned int)-1;
}
}
size = len;
if (offset + size > sample->mLength)
{
size = sample->mLength - offset;
}
if (stream->mPosition > endpoint)
{
size = 0;
}
else if (stream->mPosition + size > endpoint)
{
size = (endpoint - stream->mPosition) + 1;
}
/*
Handle unaligned read at the end.
*/
getBytesFromSamples(size, &bytestoread, false);
if (!bytestoread)
{
r = 0;
result = FMOD_ERR_FILE_EOF;
}
else
{
result = sample->read(offset, size, &r);
if ((result != FMOD_OK) && (result != FMOD_ERR_FILE_EOF) && (result != FMOD_ERR_FILE_DISKEJECTED))
{
return result;
}
}
if (result == FMOD_OK && !r && size)
{
break;
}
read += r;
len -= r;
size -= r;
offset += r;
if (offset >= sample->mLength)
{
offset = 0;
}
stream->mLastPos = stream->mPosition;
stream->mPosition += r;
if (read_out)
{
*read_out = read;
}
if (stream->mPosition > endpoint || result == FMOD_ERR_FILE_EOF)
{
if (calledfromsentence)
{
return FMOD_ERR_FILE_EOF; /* Let the parent sentence behavior take care of the transition. */
}
if (mMode & FMOD_LOOP_NORMAL && mLoopCountCurrent)
{
stream->mPosition = mLoopStart;
if (mLength != (unsigned int)-1)
{
result = sample->seek(mSubSoundIndex, stream->mPosition);
if (result != FMOD_OK)
{
return result;
}
stream->mPosition = sample->mPosition;
}
if (mLoopCountCurrent > 0)
{
mLoopCountCurrent--;
}
}
else
{
if (stream != this)
{
mPosition = mLength;
mFlags |= FMOD_SOUND_FLAG_FINISHED;
}
stream->mPosition = streamlength;
stream->mFlags |= FMOD_SOUND_FLAG_FINISHED;
if (stream->mSubSoundParent)
{
stream->mSubSoundParent->mFlags |= FMOD_SOUND_FLAG_FINISHED;
}
break;
}
}
else
{
if (!r)
{
break;
}
}
} while (len);
}
}
if (read < length)
{
unsigned int size;
unsigned int len;
len = length - read;
do
{
size = len;
if (offset + size > sample->mLength)
{
size = sample->mLength - offset;
}
sample->clear(offset, size);
len -= size;
offset += size;
if (offset >= sample->mLength)
{
offset = 0;
}
} while (len);
}
return result;
}
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Stream::flush()
{
if (mSample)
{
FMOD_RESULT result;
unsigned int filllength;
if (!(mMode & FMOD_OPENUSER) && mLength <= mSample->mLength && !mSubSoundList && mSample->mMode & FMOD_SOFTWARE)
{
filllength = mLength;
}
else
{
filllength = mSample->mLength;
}
result = fill(0, filllength);
if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF)
{
return result;
}
if (filllength < mSample->mLength)
{
result = mSample->clear(filllength, mSample->mLength - filllength);
}
}
mFlags &= ~FMOD_SOUND_FLAG_WANTSTOFLUSH;
return FMOD_OK;
}
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Stream::setPosition(unsigned int position, FMOD_TIMEUNIT postype)
{
FMOD_RESULT result = FMOD_OK;
bool seekable = true;
unsigned int endpoint;
if (postype == FMOD_TIMEUNIT_PCM)
{
if (mMode & FMOD_LOOP_OFF)
{
endpoint = mLength - 1;
}
else
{
endpoint = mLoopStart + mLoopLength - 1;
}
if (position > endpoint)
{
return FMOD_ERR_INVALID_POSITION;
}
}
if (mCodec->mFile)
{
seekable = mCodec->mFile->mFlags & FMOD_FILE_SEEKABLE ? true : false;
}
mFlags &= ~(FMOD_SOUND_FLAG_FINISHED | FMOD_SOUND_FLAG_THREADFINISHED);
if (mSubSoundParent)
{
mSubSoundParent->mFlags &= ~(FMOD_SOUND_FLAG_FINISHED | FMOD_SOUND_FLAG_THREADFINISHED);
}
if (seekable)
{
#ifdef FMOD_SUPPORT_SENTENCING
if (mSubSound && mSubSoundList && postype == FMOD_TIMEUNIT_PCM)
{
int count;
unsigned int offset = 0;
for (count = 0; count < mSubSoundListNum; count++)
{
SoundI *subsound = mSubSound[mSubSoundList[count].mIndex];
if (subsound)
{
FMOD_RESULT result;
if (!mSubSoundList)
{
return FMOD_ERR_INVALID_PARAM;
}
if (position >= offset && position < offset + mSubSoundList[count].mLength)
{
Stream *substream = SAFE_CAST(Stream, subsound);
mChannel->mSubSoundListCurrent = count;
mSubSoundIndex = mSubSoundList[count].mIndex;
if (mSubSoundShared) /* Same codec. */
{
result = substream->updateSubSound(mSubSoundIndex, true);
}
else
{
substream->mSubSoundIndex = mSubSoundIndex;
mSample->mCodec = substream->mCodec; /* Switch parent level codec to the new substream's codec */
}
result = substream->setPosition(position - offset, postype);
break;
}
offset += mSubSoundList[count].mLength;
}
}
}
else
#endif
if (mSubSound && postype == FMOD_TIMEUNIT_PCM)
{
SoundI *subsound = mSubSound[mSubSoundIndex];
if (subsound)
{
Stream *substream = SAFE_CAST(Stream, subsound);
result = substream->setPosition(position, postype);
}
}
else
{
mCodec->reset();
result = mCodec->setPosition(((mSubSoundParent && mSubSoundParent->mNumSubSounds) || mNumSubSounds) ? mSubSoundIndex : 0, position, postype);
if (result != FMOD_OK)
{
return result;
}
if (mSubSoundParent)
{
mSubSoundParent->mSubSoundIndex = mSubSoundIndex;
}
}
if (mSample && mSample->mPostSetPositionCallback)
{
mSample->mPostSetPositionCallback((FMOD_SOUND *)this, mSubSoundIndex, position, postype);
}
if (postype != FMOD_TIMEUNIT_MS && postype != FMOD_TIMEUNIT_PCM && postype != FMOD_TIMEUNIT_PCMBYTES)
{
position = 0;
}
mLastPos = mPosition = position;
}
else
{
if (mLastPos != 0 || position != 0)
{
return FMOD_ERR_FILE_COULDNOTSEEK;
}
}
return result;
}
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Stream::getPosition(unsigned int *position, FMOD_TIMEUNIT postype)
{
FMOD_RESULT result;
if (mOpenState != FMOD_OPENSTATE_READY && mOpenState != FMOD_OPENSTATE_SETPOSITION)
{
return FMOD_ERR_NOTREADY;
}
if (!position)
{
return FMOD_ERR_INVALID_PARAM;
}
if (postype == (FMOD_TIMEUNIT)(FMOD_TIMEUNIT_SENTENCE_SUBSOUND | FMOD_TIMEUNIT_BUFFERED))
{
*position = mChannel->mSubSoundListCurrent;
}
else if (postype == FMOD_TIMEUNIT_PCM || postype == FMOD_TIMEUNIT_PCMBYTES || postype == FMOD_TIMEUNIT_MS)
{
if (postype == FMOD_TIMEUNIT_PCM)
{
*position = mLastPos;
}
else if (postype == FMOD_TIMEUNIT_MS)
{
*position = (unsigned int)((float)mLastPos / 1000.0f * mDefaultFrequency);
}
else if (postype == FMOD_TIMEUNIT_PCM)
{
getBytesFromSamples(mLastPos, position);
}
}
else
{
result = mCodec->getPosition(position, postype);
if (result != FMOD_OK)
{
return result;
}
}
return FMOD_OK;
}
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Stream::setLoopCount(int loopcount)
{
if (mOpenState != FMOD_OPENSTATE_READY && mOpenState != FMOD_OPENSTATE_SETPOSITION)
{
return FMOD_ERR_NOTREADY;
}
mLoopCountCurrent = loopcount;
mLoopCount = loopcount;
return FMOD_OK;
}
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
#ifdef FMOD_SUPPORT_MEMORYTRACKER
FMOD_RESULT Stream::getMemoryUsedImpl(MemoryTracker *tracker)
{
tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(Stream) - sizeof(SoundI));
if (mSample && (!mSubSoundParent || (mSubSoundParent && ((Stream *)mSubSoundParent)->mSample != mSample)))
{
CHECK_RESULT(mSample->getMemoryUsed(tracker));
}
if (mChannel && (!mSubSoundParent || (mSubSoundParent && ((Stream *)mSubSoundParent)->mChannel != mChannel)))
{
tracker->add(false, FMOD_MEMBITS_SOUND, sizeof(ChannelStream));
}
return SoundI::getMemoryUsedImpl(tracker);
}
#endif
}
#endif /* #ifdef FMOD_SUPPORT_STREAMING */