Torque3D/Engine/source/sfx/fmod/sfxFMODBuffer.cpp
2012-09-19 11:15:01 -04:00

323 lines
11 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "sfx/fmod/sfxFMODBuffer.h"
#include "sfx/fmod/sfxFMODDevice.h"
#include "sfx/sfxDescription.h"
#include "core/util/safeDelete.h"
#include "core/volume.h"
//-----------------------------------------------------------------------------
static const char* sExtensions[] =
{
"", // First try without doing anything with the given path.
"", // Then try it without an extension but by expanding it through Torque::FS.
"aiff",
"asf",
"asx",
"dls",
"flac",
"fsb",
"it",
"m3u",
"mid",
"mod",
"mp2",
"mp3",
"ogg",
"pls",
"s3m",
"vag",
"wav",
"wax",
"wma",
"xm",
#ifdef TORQUE_OS_XENON
".xma",
#endif
NULL
};
//-----------------------------------------------------------------------------
SFXFMODBuffer* SFXFMODBuffer::create( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
{
SFXFMODBuffer *buffer = new SFXFMODBuffer( stream, description );
if( !buffer->mSound )
SAFE_DELETE( buffer );
return buffer;
}
//-----------------------------------------------------------------------------
SFXFMODBuffer* SFXFMODBuffer::create( const String& filename, SFXDescription* description )
{
if( Con::getBoolVariable( "$pref::SFX::FMOD::noCustomFileLoading", false ) )
return NULL;
SFXFMODBuffer *buffer = new SFXFMODBuffer( filename, description );
if( !buffer->mSound )
SAFE_DELETE( buffer );
return buffer;
}
//-----------------------------------------------------------------------------
SFXFMODBuffer::SFXFMODBuffer( const String& filename, SFXDescription* description )
: Parent( description ),
mSound( NULL )
{
FMOD_MODE fMode = ( description->mUseHardware ? FMOD_HARDWARE : FMOD_SOFTWARE )
| ( description->mIs3D ? FMOD_3D : FMOD_2D );
if( description->mIsStreaming )
{
fMode |= FMOD_CREATESTREAM;
mIsUnique = true;
}
// Go through the extensions and try each with the given path. The
// first two are special. First we try without touching the filename at all
// so FMOD gets a chance to handle URLs and whatever, and then second we
// try by expanding the path but without adding an extension.
Torque::Path path = filename;
for( U32 i = 0; sExtensions[ i ]; ++ i )
{
path.setExtension( sExtensions[ i ] );
if( !i || Torque::FS::IsFile( path ) )
{
// Translate to full path.
//TODO: Remove this when hooking up the file system functions in sfxFMODDevice.cpp
String fullPath;
if( !i )
fullPath = filename;
else
{
Torque::Path realPath;
if( !Torque::FS::GetFSPath( path, realPath ) )
continue;
fullPath = realPath.getFullPath().c_str();
}
mSound = NULL;
FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_System_CreateSound(
SFXFMODDevice::smSystem,
fullPath.c_str(),
fMode,
( FMOD_CREATESOUNDEXINFO* ) NULL,
&mSound );
if( result == FMOD_OK )
{
SFXFMODDevice::smFunc->FMOD_Sound_GetMode( mSound, &mMode );
// Read out format.
int numChannels;
int bitsPerSample;
unsigned int length;
float frequency;
SFXFMODDevice::smFunc->FMOD_Sound_GetFormat( mSound, ( FMOD_SOUND_TYPE* ) NULL, ( FMOD_SOUND_FORMAT* ) NULL, &numChannels, &bitsPerSample );
SFXFMODDevice::smFunc->FMOD_Sound_GetLength( mSound, &length, FMOD_TIMEUNIT_MS );
SFXFMODDevice::smFunc->FMOD_Sound_GetDefaults( mSound, &frequency, ( float* ) NULL, ( float* ) NULL, ( int* ) NULL );
mDuration = length;
mFormat = SFXFormat( numChannels, numChannels * bitsPerSample, frequency );
break;
}
}
}
if( !mSound )
Con::errorf( "SFXFMODBuffer::SFXFMODBuffer - failed to load '%s' through FMOD", filename.c_str() );
}
//-----------------------------------------------------------------------------
SFXFMODBuffer::SFXFMODBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
: Parent( stream, description ),
mSound( NULL )
{
FMOD_MODE fMode = ( description->mUseHardware ? FMOD_HARDWARE : FMOD_SOFTWARE )
| ( description->mIs3D ? FMOD_3D : FMOD_2D );
FMOD_CREATESOUNDEXINFO* pCreatesoundexinfo = NULL;
FMOD_CREATESOUNDEXINFO createsoundexinfo;
fMode |= FMOD_OPENUSER; // this tells fmod we are supplying the data directly
if( isStreaming() )
fMode |= FMOD_LOOP_NORMAL | FMOD_UNIQUE;
const SFXFormat& format = getFormat();
U32 channels = format.getChannels();
U32 frequency = format.getSamplesPerSecond();
U32 bitsPerChannel = format.getBitsPerSample() / channels;
U32 dataSize = mBufferSize;
FMOD_SOUND_FORMAT sfxFmt = FMOD_SOUND_FORMAT_NONE;
switch(bitsPerChannel)
{
case 8:
sfxFmt = FMOD_SOUND_FORMAT_PCM8;
break;
case 16:
sfxFmt = FMOD_SOUND_FORMAT_PCM16;
break;
case 24:
sfxFmt = FMOD_SOUND_FORMAT_PCM24;
break;
case 32:
sfxFmt = FMOD_SOUND_FORMAT_PCM32;
break;
default:
AssertISV(false, "SFXFMODBuffer::SFXFMODBuffer() - unsupported bits-per-sample (what format is it in, 15bit PCM?)");
break;
}
dMemset(&createsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
createsoundexinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* required. */
createsoundexinfo.decodebuffersize = frequency; /* Chunk size of stream update in samples. This will be the amount of data passed to the user callback. */
createsoundexinfo.length = dataSize; /* Length of PCM data in bytes of whole sound (for Sound::getLength) */
createsoundexinfo.numchannels = channels; /* Number of channels in the sound. */
createsoundexinfo.defaultfrequency = frequency; /* Default playback rate of sound. */
createsoundexinfo.format = sfxFmt; /* Data format of sound. */
createsoundexinfo.pcmreadcallback = NULL; /* User callback for reading. */
createsoundexinfo.pcmsetposcallback = NULL; /* User callback for seeking. */
pCreatesoundexinfo = &createsoundexinfo;
FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_System_CreateSound(
SFXFMODDevice::smSystem,
( const char* ) NULL,
fMode,
pCreatesoundexinfo,
&mSound );
if( result != FMOD_OK )
{
mSound = NULL;
Con::errorf( "SFXFMODBuffer::SFXFMODBuffer - failed to create buffer (%i)", result );
}
else
SFXFMODDevice::smFunc->FMOD_Sound_GetMode( mSound, &mMode );
}
//-----------------------------------------------------------------------------
SFXFMODBuffer::~SFXFMODBuffer()
{
if( mSound )
FModAssert( SFXFMODDevice::smFunc->FMOD_Sound_Release( mSound ),
"SFXFMODBuffer::~SFXFMODBuffer - Failed to release a sound!" );
mSound = NULL;
}
//-----------------------------------------------------------------------------
void SFXFMODBuffer::_flush()
{
AssertFatal( isStreaming(), "SFXFMODBuffer::_flush() - not a streaming buffer" );
AssertFatal( SFXInternal::isSFXThread(), "SFXFMODBuffer::_flush() - not on SFX thread" );
Parent::_flush();
SFXFMODDevice::smFunc->FMOD_Channel_SetPosition
( ( ( SFXFMODVoice* ) mUniqueVoice.getPointer() )->mChannel, 0, FMOD_TIMEUNIT_PCM );
}
//-----------------------------------------------------------------------------
bool SFXFMODBuffer::_copyData( U32 offset, const U8* data, U32 length )
{
AssertFatal( data != NULL && length > 0, "Must have data!" );
// Fill the buffer with the resource data.
void* lpvWrite;
U32 dwLength;
void* lpvWrite2;
U32 dwLength2;
int res = SFXFMODDevice::smFunc->FMOD_Sound_Lock(
mSound,
offset, // Offset at which to start lock.
length, // Size of lock.
&lpvWrite, // Gets address of first part of lock.
&lpvWrite2, // Address of wraparound not needed.
&dwLength, // Gets size of first part of lock.
&dwLength2 // Size of wraparound not needed.
);
if ( res != FMOD_OK )
{
// You can remove this if it gets spammy. However since we can
// safely fail in this case it doesn't seem right to assert...
// at the same time it can be very annoying not to know why
// an upload fails!
Con::errorf("SFXFMODBuffer::_copyData - failed to lock a sound buffer! (%d)", this);
return false;
}
// Copy the first part.
dMemcpy( lpvWrite, data, dwLength );
// Do we have a wrap?
if ( lpvWrite2 )
dMemcpy( lpvWrite2, data + dwLength, dwLength2 );
// And finally, unlock.
FModAssert( SFXFMODDevice::smFunc->FMOD_Sound_Unlock(
mSound,
lpvWrite, // Address of lock start.
lpvWrite2, // No wraparound portion.
dwLength, // Size of lock.
dwLength2 ), // No wraparound size.
"Failed to unlock sound buffer!" );
return true;
}
//-----------------------------------------------------------------------------
U32 SFXFMODBuffer::getMemoryUsed() const
{
unsigned int memoryUsed;
SFXFMODDevice::smFunc->FMOD_Sound_GetMemoryInfo(
mSound,
FMOD_MEMBITS_ALL,
FMOD_EVENT_MEMBITS_ALL,
&memoryUsed,
( unsigned int* ) NULL );
return memoryUsed;
}