#include "fmod_settings.h"
#include "fmod_codeci.h"
#include "fmod_file.h"
#include "fmod_metadata.h"
#include "fmod_soundi.h"
#include "fmod_file_disk.h"
namespace FMOD
{
#ifndef PLATFORM_PS3_SPU
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Codec::release()
{
FMOD_RESULT result;
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Codec::release", "\n"));
if (mDescription.close)
{
mDescription.close(this);
}
if (mFile)
{
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Codec::release", "Close file (mFile = %p)\n", mFile));
mFile->close();
FMOD_Memory_Free(mFile);
mFile = 0;
}
if (mWaveFormatMemory && mType == FMOD_SOUND_TYPE_FSB)
{
FMOD_Memory_Free(mWaveFormatMemory);
mWaveFormatMemory = 0;
}
if (mMetadata)
{
mMetadata->release();
mMetadata = 0;
}
result = Plugin::release();
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "Codec::release", "done\n"));
return result;
}
#endif // !PLATFORM_PS3_SPU
/*
[
[DESCRIPTION]
This is just a helper function for codecs if mPCMBuffer and mPCMBufferLengthBytes have been allocated inside the codec code.
It simply calls the decode in a loop, feeding the output buffer from a smaller pcm decode buffer (mPCMBuffer).
Codecs that want to skip offline buffering and write directly to the output pointer (ie raw 1:1 pcm that just does an fread for example)
should not allocate those variables.
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Codec::read(void *buffer, unsigned int sizebytes, unsigned int *bytesread)
{
FMOD_RESULT result = FMOD_OK;
bool checkmetadata = false;
unsigned int read = 0;
if (mPCMBuffer && mPCMBufferLengthBytes)
{
while (sizebytes)
{
unsigned int lenbytes = sizebytes;
unsigned int bytesdecoded = 0;
if (!mPCMBufferOffsetBytes)
{
result = mDescription.read(this, mPCMBuffer, mPCMBufferLengthBytes, &bytesdecoded);
if (result != FMOD_OK)
{
break;
}
mPCMBufferFilledBytes = bytesdecoded;
lenbytes = bytesdecoded;
if (lenbytes > sizebytes)
{
lenbytes = sizebytes;
}
checkmetadata = true;
}
if (mPCMBufferOffsetBytes + lenbytes > mPCMBufferFilledBytes)
{
lenbytes = mPCMBufferFilledBytes - mPCMBufferOffsetBytes;
}
FMOD_memcpy((char *)buffer + read, mPCMBuffer + mPCMBufferOffsetBytes, lenbytes);
mPCMBufferOffsetBytes += lenbytes;
if (mPCMBufferOffsetBytes >= mPCMBufferFilledBytes)
{
mPCMBufferOffsetBytes = 0;
}
if (!lenbytes)
{
lenbytes = sizebytes;
break;
}
sizebytes -= lenbytes;
read += lenbytes;
}
}
else
{
result = mDescription.read(this, buffer, sizebytes, &read);
if (result == FMOD_OK)
{
checkmetadata = true;
}
}
#ifndef PLATFORM_PS3_SPU
if (checkmetadata)
{
getMetadataFromFile();
}
#endif
if (bytesread)
{
*bytesread = read;
}
return result;
}
#ifndef PLATFORM_PS3_SPU
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Codec::getMetadataFromFile()
{
FMOD_RESULT result = FMOD_OK;
Metadata *filemetadata;
if (mFile)
{
result = mFile->getMetadata(&filemetadata);
if (result == FMOD_OK)
{
if (!mMetadata)
{
mMetadata = FMOD_Object_Alloc(Metadata);
if (!mMetadata)
{
return FMOD_ERR_MEMORY;
}
}
result = mMetadata->add(filemetadata);
}
}
return result;
}
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Codec::getLength(unsigned int *length, FMOD_TIMEUNIT lengthtype)
{
FMOD_RESULT result;
if (lengthtype == FMOD_TIMEUNIT_RAWBYTES)
{
FMOD_CODEC_WAVEFORMAT waveformat;
result = mDescription.getwaveformat(this, mSubSoundIndex, &waveformat);
if (result != FMOD_OK)
{
return result;
}
*length = waveformat.lengthbytes;
return FMOD_OK;
}
if (!mDescription.getlength)
{
*length = 0;
return FMOD_ERR_UNSUPPORTED;
}
result = mDescription.getlength(this, length, lengthtype);
if (result != FMOD_OK)
{
return result;
}
return FMOD_OK;
}
#endif // !PLATFORM_PS3_SPU
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Codec::setPosition(int subsound, unsigned int position, FMOD_TIMEUNIT postype)
{
FMOD_RESULT result;
FMOD_CODEC_WAVEFORMAT waveformat;
if (numsubsounds && subsound >= numsubsounds)
{
return FMOD_ERR_INVALID_POSITION;
}
if (!mDescription.setposition)
{
return FMOD_ERR_UNSUPPORTED;
}
if (subsound < 0)
{
subsound = mSubSoundIndex;
}
if (!numsubsounds)
{
subsound = 0;
}
result = mDescription.getwaveformat(this, subsound, &waveformat);
if (result != FMOD_OK)
{
return result;
}
/*
If the incoming timeformat is not a codec friendly format, convert it to the
codec's favourite time format. (for convertable formats like ms/pcm/bytes)
*/
if (mDescription.timeunits & FMOD_TIMEUNIT_PCM)
{
if (postype & FMOD_TIMEUNIT_PCMBYTES)
{
SoundI::getSamplesFromBytes(position, &position, waveformat.channels, waveformat.format);
postype = FMOD_TIMEUNIT_PCM;
}
else if (postype & FMOD_TIMEUNIT_MS)
{
position = (unsigned int)((float)position / 1000.0f * waveformat.frequency);
postype = FMOD_TIMEUNIT_PCM;
}
}
else if (mDescription.timeunits & FMOD_TIMEUNIT_PCMBYTES)
{
if (postype & FMOD_TIMEUNIT_PCM)
{
SoundI::getBytesFromSamples(position, &position, waveformat.channels, waveformat.format);
postype = FMOD_TIMEUNIT_PCMBYTES;
}
else if (postype & FMOD_TIMEUNIT_MS)
{
position = (unsigned int)((float)position / 1000.0f * waveformat.frequency);
SoundI::getBytesFromSamples(position, &position, waveformat.channels, waveformat.format);
postype = FMOD_TIMEUNIT_PCMBYTES;
}
}
else if (mDescription.timeunits & FMOD_TIMEUNIT_MS)
{
if (postype & FMOD_TIMEUNIT_PCM)
{
position = (unsigned int)((float)position / waveformat.frequency * 1000.0f);
postype = FMOD_TIMEUNIT_MS;
}
else if (postype & FMOD_TIMEUNIT_PCMBYTES)
{
SoundI::getSamplesFromBytes(position, &position, waveformat.channels, waveformat.format);
position = (unsigned int)((float)position / waveformat.frequency * 1000.0f);
postype = FMOD_TIMEUNIT_MS;
}
}
if (!(postype & mDescription.timeunits))
{
return FMOD_ERR_FORMAT;
}
mPCMBufferOffsetBytes = 0;
result = mDescription.setposition(this, subsound, position, postype);
if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF)
{
return result;
}
mSubSoundIndex = subsound;
return FMOD_OK;
}
#ifndef PLATFORM_PS3_SPU
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
FMOD_RESULT Codec::getPosition(unsigned int *position, FMOD_TIMEUNIT postype)
{
FMOD_RESULT result;
if (postype == FMOD_TIMEUNIT_RAWBYTES)
{
if (!mFile)
{
*position = 0;
}
result = mFile->tell(position);
if (result != FMOD_OK)
{
*position = 0;
return result;
}
*position -= mSrcDataOffset;
}
if (!mDescription.getposition)
{
return FMOD_ERR_UNSUPPORTED;
}
if (!(postype & mDescription.timeunits))
{
return FMOD_ERR_FORMAT;
}
result = mDescription.getposition(this, position, postype);
if (result != FMOD_OK)
{
return result;
}
return FMOD_OK;
}
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
FMOD_TAGDATATYPE
]
*/
FMOD_RESULT Codec::metaData(FMOD_TAGTYPE type, const char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype, bool unique)
{
if (!mMetadata)
{
mMetadata = FMOD_Object_Alloc(Metadata);
if (!mMetadata)
{
return FMOD_ERR_MEMORY;
}
}
return mMetadata->addTag(type, name, data, datalen, datatype, unique);
}
#endif // !PLATFORM_PS3_SPU
/*
[
[DESCRIPTION]
[PARAMETERS]
[RETURN_VALUE]
[REMARKS]
[PLATFORMS]
[SEE_ALSO]
]
*/
#if defined(FMOD_SUPPORT_MEMORYTRACKER) && !defined(PLATFORM_PS3_SPU)
FMOD_RESULT Codec::getMemoryUsedImpl(MemoryTracker *tracker)
{
tracker->add(false, FMOD_MEMBITS_CODEC, mDescription.mSize);
if (mFile)
{
CHECK_RESULT(((FMOD::DiskFile *)mFile)->getMemoryUsed(tracker));
}
if (mDescription.getmemoryused)
{
CHECK_RESULT(mDescription.getmemoryused(this, tracker));
}
return FMOD_OK;
}
#endif
/*
[API]
[
[DESCRIPTION]
Open callback for the codec for when FMOD tries to open a sound using this codec.
This is the callback the file format check is done in, codec related memory is allocated, and things are generally initialized / set up for the codec.
[PARAMETERS]
'codec_state' Pointer to the codec state. The user can use this variable to access runtime plugin specific variables and plugin writer user data.
'usermode' Mode that the user supplied via System::createSound. This is informational and can be ignored, or used if it has relevance to your codec.
'userexinfo' Extra info structure that the user supplied via System::createSound. This is informational and can be ignored, or used if it has relevance to your codec.
[RETURN_VALUE]
[REMARKS]
The usermode and userexinfo parameters tell the codec what was passed in by the user.
Generally these can be ignored, as the file format usually determines the format and frequency of the sound.
If you have a flexible format codec (ie you don't mind what output format your codec writes to), you might want to use the parameter that was passed in by the user to specify the output sound format / frequency.
For example if you normally create a codec that is always 32bit floating point, the user might supply 16bit integer to save memory, so you could use this information to decode your data to this format instead of the original default format.
Read and seek within the file using the 'fileread' and 'fileseek' members of the FMOD_CODEC codec that is passed in.
Note: DO NOT USE YOUR OWN FILESYSTEM.
The reasons for this are: