mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
323 lines
11 KiB
C++
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;
|
|
}
|