12754 lines
333 KiB
C++
12754 lines
333 KiB
C++
#include "fmod_settings.h"
|
|
|
|
#include "fmod.h"
|
|
#include "fmod_3d.h"
|
|
#ifdef FMOD_SUPPORT_NONBLOCKING
|
|
#include "fmod_async.h"
|
|
#endif
|
|
#include "fmod_autocleanup.h"
|
|
#include "fmod_channeli.h"
|
|
#include "fmod_channel_real.h"
|
|
#include "fmod_channel_stream.h"
|
|
#include "fmod_channel_software.h"
|
|
#include "fmod_codec_user.h"
|
|
#include "fmod_codeci.h"
|
|
#include "fmod_debug.h"
|
|
#include "fmod_dsp_chorus.h"
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
#include "fmod_dsp_codec.h"
|
|
#endif
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
#include "fmod_dsp_distortion.h"
|
|
#include "fmod_dsp_echo.h"
|
|
#include "fmod_dsp_delay.h"
|
|
#include "fmod_dsp_fft.h"
|
|
#include "fmod_dsp_filter.h"
|
|
#include "fmod_dsp_flange.h"
|
|
#include "fmod_dsp_highpass.h"
|
|
#include "fmod_dsp_itecho.h"
|
|
#include "fmod_dsp_compressor.h"
|
|
#include "fmod_dsp_sfxreverb.h"
|
|
#include "fmod_dsp_lowpass.h"
|
|
#include "fmod_dsp_lowpass2.h"
|
|
#include "fmod_dsp_lowpass_simple.h"
|
|
#include "fmod_dsp_normalize.h"
|
|
#include "fmod_dsp_oscillator.h"
|
|
#include "fmod_dsp_parameq.h"
|
|
#include "fmod_dsp_pitchshift.h"
|
|
#include "fmod_dsp_reverb.h"
|
|
#include "fmod_dsp_tremolo.h"
|
|
#endif
|
|
|
|
#include "fmod_dsp_resampler.h"
|
|
#ifndef PLATFORM_PS3
|
|
#include "fmod_dsp_soundcard.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_VSTPLUGIN
|
|
#include "fmod_dsp_vstplugin.h"
|
|
#endif
|
|
#include "fmod_dsp_wavetable.h"
|
|
#include "fmod_dspi.h"
|
|
#include "fmod_file.h"
|
|
#include "fmod_file_cdda.h"
|
|
#include "fmod_file_disk.h"
|
|
#include "fmod_file_memory.h"
|
|
#include "fmod_file_net.h"
|
|
#include "fmod_file_null.h"
|
|
#include "fmod_file_user.h"
|
|
#include "fmod_localcriticalsection.h"
|
|
#include "fmod_memory.h"
|
|
#include "fmod_metadata.h"
|
|
#include "fmod_net.h"
|
|
#include "fmod_output_emulated.h"
|
|
#include "fmod_output_polled.h"
|
|
#include "fmod_output_software.h"
|
|
#include "fmod_os_cdda.h"
|
|
#include "fmod_os_output.h"
|
|
#include "fmod_pluginfactory.h"
|
|
#include "fmod_sample_software.h"
|
|
#include "fmod_soundi.h"
|
|
#include "fmod_sound_sample.h"
|
|
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
#include "fmod_geometryi.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
#include "fmod_sound_stream.h"
|
|
#endif
|
|
#include "fmod_string.h"
|
|
#include "fmod_stringw.h"
|
|
#include "fmod_systemi.h"
|
|
#ifdef FMOD_SUPPORT_OPENAL
|
|
#include "fmod_output_openal.h"
|
|
#endif
|
|
#include "fmod_downmix.h"
|
|
#ifdef FMOD_SUPPORT_NEURAL
|
|
#include "fmod_downmix_neuralthx.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MYEARS
|
|
#include "fmod_downmix_myears.h"
|
|
#endif
|
|
|
|
/*
|
|
Codecs
|
|
*/
|
|
#ifdef FMOD_SUPPORT_AAC
|
|
#include "fmod_codec_aac.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_AIFF
|
|
#include "fmod_codec_aiff.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_ASF
|
|
#include "fmod_codec_asf.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_AT3
|
|
#include "fmod_codec_at3.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CDDA
|
|
#include "fmod_codec_cdda.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_DLS
|
|
#include "fmod_codec_dls.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_FLAC
|
|
#include "fmod_codec_flac.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_GCADPCM_DSP
|
|
#include "fmod_codec_dsp.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_FSB
|
|
#include "fmod_codec_fsb.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IT
|
|
#include "fmod_codec_it.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MOD
|
|
#include "fmod_codec_midi.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MOD
|
|
#include "fmod_codec_mod.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
#include "fmod_codec_mpeg.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MPEGPSP
|
|
#include "fmod_codec_mpegpsp.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_OGGVORBIS
|
|
#include "fmod_codec_oggvorbis.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
#include "fmod_codec_celt.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_PLAYLIST
|
|
#include "fmod_codec_playlist.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAW
|
|
#include "fmod_codec_raw.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_SF2
|
|
#include "fmod_codec_sf2.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_S3M
|
|
#include "fmod_codec_s3m.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_TAGS
|
|
#include "fmod_codec_tag.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_TREMOR
|
|
#include "fmod_codec_tremor.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_VAG
|
|
#if defined(PLATFORM_PS2) || defined(PLATFORM_PSP)
|
|
#include "fmod_codec_vag.h"
|
|
#else
|
|
#include "fmod_codec_swvag.h"
|
|
#endif
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_WAV
|
|
#include "fmod_codec_wav.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XM
|
|
#include "fmod_codec_xm.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
#include "fmod_codec_xma.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XWMA
|
|
#include "fmod_codec_xwma.h"
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
#include "fmod_codec_wav_imaadpcm.h"
|
|
#endif
|
|
|
|
/*
|
|
Output types
|
|
*/
|
|
#ifdef FMOD_SUPPORT_NOSOUND
|
|
#include "fmod_output_nosound.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_NOSOUND_NRT
|
|
#include "fmod_output_nosound_nrt.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_WAVWRITER
|
|
#include "fmod_output_wavwriter.h"
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_WAVWRITER_NRT
|
|
#include "fmod_output_wavwriter_nrt.h"
|
|
#endif
|
|
|
|
/*
|
|
Profiler modules
|
|
*/
|
|
#ifdef FMOD_SUPPORT_PROFILE
|
|
#include "fmod_profile.h"
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_DSP)
|
|
#include "fmod_profile_dsp.h"
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_MEMORY)
|
|
#include "fmod_profile_memory.h"
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CPU)
|
|
#include "fmod_profile_cpu.h"
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CHANNEL)
|
|
#include "fmod_profile_channel.h"
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CODEC)
|
|
#include "fmod_profile_codec.h"
|
|
#endif
|
|
|
|
#include "fmod_speakermap.h" /* Must be after mmreg include from codec wav */
|
|
|
|
namespace FMOD
|
|
{
|
|
|
|
FMOD_OS_CRITICALSECTION *SystemI::gSoundListCrit = 0;
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Returns a pointer to the instance of system object with the specified id
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getInstance(unsigned int id, SystemI **sys)
|
|
{
|
|
SystemI *current;
|
|
|
|
if (sys)
|
|
{
|
|
*sys = 0;
|
|
}
|
|
|
|
current = SAFE_CAST(SystemI, gGlobal->gSystemHead->getNext());
|
|
while (current != gGlobal->gSystemHead)
|
|
{
|
|
if (current->mIndex == id)
|
|
{
|
|
if (sys)
|
|
{
|
|
*sys = current;
|
|
}
|
|
return FMOD_OK;
|
|
}
|
|
|
|
current = SAFE_CAST(SystemI, current->getNext());
|
|
}
|
|
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::validate(System *system, SystemI **systemi)
|
|
{
|
|
FMOD::SystemI *sys = (FMOD::SystemI *)system;
|
|
|
|
if (!system)
|
|
{
|
|
return FMOD_ERR_INVALID_HANDLE;
|
|
}
|
|
|
|
if (!systemi)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!FMOD::gGlobal->gSystemHead->exists(sys))
|
|
{
|
|
return FMOD_ERR_INVALID_HANDLE;
|
|
}
|
|
|
|
*systemi = sys;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
void SystemI::streamThread(void *data)
|
|
{
|
|
SystemI *system = (SystemI *)data;
|
|
|
|
system->updateStreams();
|
|
}
|
|
|
|
|
|
/*
|
|
====================================================================================
|
|
|
|
PRIVATE MEMBER FUNCTIONS
|
|
|
|
====================================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::updateStreams()
|
|
{
|
|
mStreamTimeStamp.stampIn();
|
|
|
|
/*
|
|
Process Stream Channel list. Stream channels are added and removed with ChannelStream::alloc and ChannelStream::stop
|
|
*/
|
|
FMOD_OS_CriticalSection_Enter(mStreamListCrit);
|
|
{
|
|
mStreamListChannelCurrent = mStreamListChannelHead.getNext();
|
|
while (mStreamListChannelCurrent != &mStreamListChannelHead)
|
|
{
|
|
ChannelStream *channelstream = (ChannelStream *)mStreamListChannelCurrent->getData();
|
|
mStreamListChannelNext = mStreamListChannelCurrent->getNext();
|
|
|
|
FMOD_OS_CriticalSection_Leave(mStreamListCrit);
|
|
|
|
/*
|
|
Update the stream channel.
|
|
*/
|
|
FMOD_OS_CriticalSection_Enter(mStreamUpdateCrit);
|
|
if (!channelstream->mFinished)
|
|
{
|
|
channelstream->updateStream();
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(mStreamUpdateCrit);
|
|
|
|
|
|
FMOD_OS_CriticalSection_Enter(mStreamListCrit);
|
|
|
|
mStreamListChannelCurrent = mStreamListChannelNext;
|
|
}
|
|
mStreamListChannelNext = 0;
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(mStreamListCrit);
|
|
|
|
/*
|
|
Process stream sound list. Stream sounds are added at System::createSound and Sound::release.
|
|
Channel will always be valid until the sound is flagged as finished.
|
|
*/
|
|
FMOD_OS_CriticalSection_Enter(mStreamListCrit);
|
|
{
|
|
LinkedListNode *current = mStreamListSoundHead.getNext();
|
|
while (current != &mStreamListSoundHead)
|
|
{
|
|
Stream *stream = (Stream *)current->getData();
|
|
|
|
if ((stream->mChannel && stream->mChannel->mFinished))
|
|
{
|
|
stream->mFlags |= FMOD_SOUND_FLAG_THREADFINISHED; /* Main thread will wait for this flag while releasing. */
|
|
if (stream->mSubSound)
|
|
{
|
|
if (stream->mSubSoundShared)
|
|
{
|
|
stream->mSubSoundShared->mFlags |= FMOD_SOUND_FLAG_THREADFINISHED;
|
|
}
|
|
else if (stream->mSubSound[stream->mSubSoundIndex])
|
|
{
|
|
stream->mSubSound[stream->mSubSoundIndex]->mFlags |= FMOD_SOUND_FLAG_THREADFINISHED;
|
|
}
|
|
}
|
|
}
|
|
current = current->getNext();
|
|
}
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(mStreamListCrit);
|
|
|
|
mStreamTimeStamp.stampOut(95);
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Updates virtual voices, and any per voice calculations, ie emulated voice calcs, 3d calcs or geometry.
|
|
|
|
[PARAMETERS]
|
|
'delta' Delta in milliseconds from last frame.
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::updateChannels(int delta)
|
|
{
|
|
int emulatedused;
|
|
ChannelI *current;
|
|
|
|
/*
|
|
If the channel has stop, put it at the end of the list.
|
|
*/
|
|
current = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext());
|
|
while (current != &mChannelUsedListHead)
|
|
{
|
|
FMOD_RESULT result;
|
|
ChannelI *next = SAFE_CAST(ChannelI, current->getNext());
|
|
bool playing = false;
|
|
|
|
result = current->isPlaying(&playing);
|
|
if (playing)
|
|
{
|
|
current->update(delta);
|
|
}
|
|
|
|
result = current->isPlaying(&playing);
|
|
if (!playing)
|
|
{
|
|
current->stopEx(CHANNELI_STOPFLAG_REFSTAMP |
|
|
CHANNELI_STOPFLAG_UPDATELIST |
|
|
CHANNELI_STOPFLAG_RESETCALLBACKS |
|
|
CHANNELI_STOPFLAG_CALLENDCALLBACK |
|
|
CHANNELI_STOPFLAG_RESETCHANNELGROUP |
|
|
CHANNELI_STOPFLAG_PROCESSENDDELAY |
|
|
CHANNELI_STOPFLAG_UPDATESYNCPOINTS); /* Reset, return to freelist and call end callback. */
|
|
}
|
|
|
|
current = next;
|
|
}
|
|
|
|
/*
|
|
Now swap any emulated channels that have made it to the front of the list with real channels
|
|
*/
|
|
if (mEmulated)
|
|
{
|
|
mEmulated->mChannelPool->getChannelsUsed(&emulatedused);
|
|
|
|
if (emulatedused)
|
|
{
|
|
LinkedListNode *emucurrent, *realcurrent[CHANNELREAL_TYPE_MAX];
|
|
int count;
|
|
|
|
emucurrent = mChannelSortedListHead.getNext();
|
|
|
|
for (count = 0; count < CHANNELREAL_TYPE_MAX; count++)
|
|
{
|
|
realcurrent[count] = &mChannelSortedListHead;
|
|
}
|
|
|
|
do
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
ChannelI *emuchannel = 0;
|
|
ChannelReal *emuchannelreal [FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 };
|
|
ChannelI *realchannel [FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 };
|
|
ChannelReal *realchannelreal[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 };
|
|
FMOD_MODE mode;
|
|
int realchannelsneeded, realchannelsfound, channelsfound;
|
|
bool isvirtual;
|
|
|
|
/*
|
|
Search for an emulated channel from the head of the importance list.
|
|
*/
|
|
isvirtual = false;
|
|
do
|
|
{
|
|
emuchannel = (ChannelI *)emucurrent->getData();
|
|
if (!emuchannel)
|
|
{
|
|
/*
|
|
Back to head of list, didn't find anything. Don't crash.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
result = emuchannel->isVirtual(&isvirtual);
|
|
if (isvirtual && !(emuchannel->mFlags & CHANNELI_FLAG_FORCEVIRTUAL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
emucurrent = (ChannelI *)emucurrent->getNext();
|
|
} while (emucurrent != &mChannelSortedListHead);
|
|
|
|
if (emucurrent == &mChannelSortedListHead ||
|
|
emucurrent == realcurrent[0] ||
|
|
emucurrent == realcurrent[1] ||
|
|
emucurrent == realcurrent[2] ||
|
|
result != FMOD_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/*
|
|
Now we've found an emulated channel, see if there are any free
|
|
or lower priority channels that we can swap with of the same type
|
|
*/
|
|
channelsfound = 0; /* ChannelIs */
|
|
realchannelsfound = 0; /* RealChannels */
|
|
|
|
result = emuchannel->getRealChannel(emuchannelreal, 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
mode = emuchannelreal[0]->mMode;
|
|
|
|
/*
|
|
Find out how many subsamples we have to query the number of real channels we need for this emulated voice.
|
|
*/
|
|
|
|
if (emuchannelreal[0]->mSound)
|
|
{
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
|
|
if (emuchannelreal[0]->mSound->isStream())
|
|
{
|
|
Stream *stream = SAFE_CAST(Stream, emuchannelreal[0]->mSound);
|
|
Sample *sample = stream->mSample;
|
|
|
|
realchannelsneeded = sample->mNumSubSamples;
|
|
}
|
|
else
|
|
|
|
#endif
|
|
{
|
|
Sample *sample = SAFE_CAST(Sample, emuchannelreal[0]->mSound);
|
|
|
|
realchannelsneeded = sample->mNumSubSamples;
|
|
}
|
|
}
|
|
else if (emuchannelreal[0]->mDSP)
|
|
{
|
|
realchannelsneeded = 1;
|
|
}
|
|
else
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::updateChannels", "emuchannelreal[0] has no mSound or mDSP\n"));
|
|
return FMOD_ERR_INTERNAL;
|
|
}
|
|
|
|
if (realchannelsneeded < 1)
|
|
{
|
|
realchannelsneeded = 1;
|
|
}
|
|
|
|
/*
|
|
First try and find free real voices for this virtual channel, so we can swap with them.
|
|
*/
|
|
if (realchannelsfound < realchannelsneeded)
|
|
{
|
|
int found = 0;
|
|
ChannelReal *tempchannelreal[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 };
|
|
|
|
if (mode & FMOD_HARDWARE)
|
|
{
|
|
if (emuchannelreal[0]->mSound)
|
|
{
|
|
result = mOutput->getFreeChannel(mode, tempchannelreal, realchannelsneeded, emuchannelreal[0]->mSound->mChannels, &found, true);
|
|
}
|
|
else
|
|
{
|
|
return FMOD_ERR_INTERNAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
/*
|
|
Before looking for a software channel (which there might be available), see if it is a realtime compressed sample.
|
|
*/
|
|
if (mode & FMOD_CREATECOMPRESSEDSAMPLE
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
|| (emuchannelreal[0]->mSound && emuchannelreal[0]->mSound->mFormat == FMOD_SOUND_FORMAT_PCM16)
|
|
#endif
|
|
)
|
|
{
|
|
Sample *sample = SAFE_CAST(Sample, emuchannelreal[0]->mSound);
|
|
|
|
if (0)
|
|
{
|
|
}
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
if (sample->mFormat == FMOD_SOUND_FORMAT_MPEG)
|
|
{
|
|
result = mDSPCodecPool_MPEG.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
else if (sample->mFormat == FMOD_SOUND_FORMAT_XMA)
|
|
{
|
|
result = mDSPCodecPool_XMA.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
else if (sample->mFormat == FMOD_SOUND_FORMAT_IMAADPCM)
|
|
{
|
|
result = mDSPCodecPool_ADPCM.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
else if (sample->mFormat == FMOD_SOUND_FORMAT_CELT)
|
|
{
|
|
result = mDSPCodecPool_CELT.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
else if (sample->mFormat == FMOD_SOUND_FORMAT_PCM16)
|
|
{
|
|
result = mDSPCodecPool_RAW.areAnyFree();
|
|
}
|
|
#endif
|
|
#endif
|
|
else
|
|
{
|
|
result = FMOD_ERR_FORMAT;
|
|
}
|
|
}
|
|
else if (mode & (FMOD_CREATESTREAM | FMOD_NONBLOCKING) && emuchannelreal[0]->mSound && emuchannelreal[0]->mSound->mOpenState == FMOD_OPENSTATE_SETPOSITION)
|
|
{
|
|
result = FMOD_ERR_NOTREADY;
|
|
}
|
|
else
|
|
{
|
|
result = FMOD_OK;
|
|
}
|
|
|
|
if (result == FMOD_OK)
|
|
{
|
|
if (emuchannelreal[0]->mSound)
|
|
{
|
|
result = mSoftware->getFreeChannel(mode, tempchannelreal, realchannelsneeded, emuchannelreal[0]->mSound->mChannels, &found, true);
|
|
}
|
|
else if(emuchannelreal[0]->mDSP)
|
|
{
|
|
result = mSoftware->getFreeChannel(mode, tempchannelreal, realchannelsneeded, 1, &found, true);
|
|
}
|
|
else
|
|
{
|
|
return FMOD_ERR_INTERNAL;
|
|
}
|
|
}
|
|
#else
|
|
result = FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
|
|
for (count = 0; count < found; count++)
|
|
{
|
|
realchannelreal[realchannelsfound] = tempchannelreal[count];
|
|
realchannelsfound ++;
|
|
}
|
|
}
|
|
|
|
tryformore:
|
|
/*
|
|
If there are not enough stopped real channels lying around, find a real channel to swap with the virtual channel.
|
|
*/
|
|
if (realchannelsfound < realchannelsneeded)
|
|
{
|
|
CHANNELREAL_TYPE realtype;
|
|
|
|
if (mOutput->mChannelPool == mOutput->mChannelPool3D)
|
|
{
|
|
realtype = mode & FMOD_SOFTWARE ? CHANNELREAL_TYPE_SW : CHANNELREAL_TYPE_HW;
|
|
}
|
|
else
|
|
{
|
|
realtype = mode & FMOD_SOFTWARE ? CHANNELREAL_TYPE_SW : mode & FMOD_3D ? CHANNELREAL_TYPE_HW3D : CHANNELREAL_TYPE_HW2D;
|
|
}
|
|
|
|
if (realcurrent[realtype] == &mChannelSortedListHead)
|
|
{
|
|
realcurrent[realtype] = realcurrent[realtype]->getPrev();
|
|
}
|
|
|
|
do
|
|
{
|
|
if (realcurrent[realtype] == emucurrent)
|
|
{
|
|
result = FMOD_ERR_CHANNEL_ALLOC;
|
|
break;
|
|
}
|
|
|
|
realchannel[channelsfound] = (ChannelI *)realcurrent[realtype]->getData();
|
|
|
|
/*
|
|
TEMPORARY FIX, ARE VOICES BEING SWAPPED AROUND AND THE EMUCURRENT/REALCURRENT pointing to the wrong places?
|
|
*/
|
|
if (realcurrent[realtype] == &mChannelSortedListHead)
|
|
{
|
|
result = FMOD_ERR_CHANNEL_ALLOC;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
Only want a real channel channel
|
|
*/
|
|
realchannel[channelsfound]->isVirtual(&isvirtual);
|
|
if (isvirtual)
|
|
{
|
|
realchannel[channelsfound]->mFlags &= ~CHANNELI_FLAG_JUSTWENTVIRTUAL; /* Ok, it didnt swap, its going to come in virtual now. */
|
|
}
|
|
else
|
|
{
|
|
FMOD_MODE currentmode;
|
|
CHANNELREAL_TYPE currentrealtype;
|
|
|
|
/*
|
|
Only want a channel of the same type.
|
|
*/
|
|
realchannel[channelsfound]->getMode(¤tmode);
|
|
|
|
if (mOutput->mChannelPool == mOutput->mChannelPool3D)
|
|
{
|
|
currentrealtype = currentmode & FMOD_SOFTWARE ? CHANNELREAL_TYPE_SW : CHANNELREAL_TYPE_HW;
|
|
}
|
|
else
|
|
{
|
|
currentrealtype = currentmode & FMOD_SOFTWARE ? CHANNELREAL_TYPE_SW : currentmode & FMOD_3D ? CHANNELREAL_TYPE_HW3D : CHANNELREAL_TYPE_HW2D;
|
|
}
|
|
|
|
if (realtype == currentrealtype)
|
|
{
|
|
/*
|
|
AJS 20/09/06 If a FMOD_CREATECOMPRESSEDSAMPLE is going from virtual to real then we need to make sure there's
|
|
a dspcodec free for it to use. To ensure this, we make sure FMOD_CREATECOMPRESSEDSAMPLE only steals
|
|
FMOD_CREATECOMPRESSEDSAMPLE channels of the same format. This is so a dspcodec is freed for us to use.
|
|
*/
|
|
if (realtype == CHANNELREAL_TYPE_SW)
|
|
{
|
|
if (mode & FMOD_CREATECOMPRESSEDSAMPLE
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
|| (emuchannelreal[0]->mSound && emuchannelreal[0]->mSound->mFormat == FMOD_SOUND_FORMAT_PCM16)
|
|
#endif
|
|
)
|
|
{
|
|
Sample *sample = SAFE_CAST(Sample, emuchannelreal[0]->mSound);
|
|
|
|
if (0)
|
|
{
|
|
}
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
if (sample->mFormat == FMOD_SOUND_FORMAT_MPEG)
|
|
{
|
|
result = mDSPCodecPool_MPEG.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
else if (sample->mFormat == FMOD_SOUND_FORMAT_XMA)
|
|
{
|
|
result = mDSPCodecPool_XMA.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
else if (sample->mFormat == FMOD_SOUND_FORMAT_IMAADPCM)
|
|
{
|
|
result = mDSPCodecPool_ADPCM.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
else if (sample->mFormat == FMOD_SOUND_FORMAT_CELT)
|
|
{
|
|
result = mDSPCodecPool_CELT.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
else if (sample->mFormat == FMOD_SOUND_FORMAT_PCM16)
|
|
{
|
|
result = mDSPCodecPool_RAW.areAnyFree();
|
|
}
|
|
#endif
|
|
#endif
|
|
else
|
|
{
|
|
result = FMOD_ERR_FORMAT;
|
|
}
|
|
|
|
if (result == FMOD_OK)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
If we stole this channel would it free up a dspcodec of the same type that we want?
|
|
*/
|
|
if (currentmode & FMOD_CREATECOMPRESSEDSAMPLE
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
|| (emuchannelreal[0]->mSound && emuchannelreal[0]->mSound->mFormat == FMOD_SOUND_FORMAT_PCM16)
|
|
#endif
|
|
)
|
|
{
|
|
ChannelReal *tempcr[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 };
|
|
|
|
realchannel[channelsfound]->getRealChannel(tempcr, 0);
|
|
|
|
Sample *currentsample = SAFE_CAST(Sample, tempcr[0]->mSound);
|
|
|
|
if (currentsample->mFormat == sample->mFormat)
|
|
{
|
|
result = FMOD_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = FMOD_OK;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = FMOD_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
realcurrent[realtype] = realcurrent[realtype]->getPrev();
|
|
|
|
} while (1);
|
|
|
|
if (result == FMOD_OK)
|
|
{
|
|
int found = 0;
|
|
ChannelReal *tempchannelreal[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 };
|
|
|
|
realchannel[channelsfound]->getRealChannel(tempchannelreal, &found);
|
|
|
|
for (count = 0; count < found; count++)
|
|
{
|
|
realchannelreal[realchannelsfound] = tempchannelreal[count];
|
|
realchannelsfound ++;
|
|
}
|
|
channelsfound++;
|
|
|
|
/*
|
|
Ok, if we havent got enough 'realchannelreal's for this virtual channel to become active still,
|
|
start stealing multiple 'realchannel's (and their child 'realchannelreal's) :|
|
|
*/
|
|
if (realchannelsfound < realchannelsneeded)
|
|
{
|
|
realcurrent[realtype] = realcurrent[realtype]->getPrev();
|
|
goto tryformore;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
realchannel[channelsfound] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
============================================================================
|
|
SWAP THE REALCHANNELS AROUND
|
|
============================================================================
|
|
*/
|
|
if (realchannelsfound >= realchannelsneeded)
|
|
{
|
|
FMOD_CHANNEL_INFO emuinfo;
|
|
FMOD_CHANNEL_INFO realinfo[FMOD_CHANNEL_MAXREALSUBCHANNELS];
|
|
FMOD_MODE mode;
|
|
int emulatedtoswap = channelsfound;
|
|
|
|
emuchannel->getMode(&mode);
|
|
|
|
/*
|
|
============================================================================
|
|
Step 1. Get information from the real and emulated channel before swapping.
|
|
============================================================================
|
|
*/
|
|
|
|
/*
|
|
If there is a real voice involved, stop it.
|
|
*/
|
|
if (channelsfound)
|
|
{
|
|
for (count = 0; count < channelsfound; count++)
|
|
{
|
|
realchannel[count]->getChannelInfo(&realinfo[count]);
|
|
realchannel[count]->stopEx(CHANNELI_STOPFLAG_DONTFREELEVELS);
|
|
}
|
|
|
|
/*
|
|
Kill off any extra voices that we stole but aren't used, this will put them back in the 'free' pool.
|
|
*/
|
|
for (count = realchannelsneeded; count < realchannelsfound; count++)
|
|
{
|
|
realchannelreal[count]->mFlags &= ~(CHANNELREAL_FLAG_IN_USE | CHANNELREAL_FLAG_ALLOCATED | CHANNELREAL_FLAG_PLAYING | CHANNELREAL_FLAG_PAUSED);
|
|
realchannelreal[count]->mFlags |= CHANNELREAL_FLAG_STOPPED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
emuchannel->getChannelInfo(&emuinfo);
|
|
emuchannel->stopEx(CHANNELI_STOPFLAG_DONTFREELEVELS);
|
|
|
|
/*
|
|
====================================================================
|
|
Step 2. Convert 'emulatedchannel' to 'real' and re-start it.
|
|
|
|
Note: After this 'realchannel' will be pointing to an emulated voice
|
|
and 'emuchannel' will be pointing to a real voice!
|
|
====================================================================
|
|
*/
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
// STREAM SPECIAL CASE GOING FROM EMULATED TO REAL.
|
|
if (emuinfo.mSound && emuinfo.mSound->mMode & FMOD_CREATESTREAM)
|
|
{
|
|
Stream *stream = SAFE_CAST(Stream, emuinfo.mSound);
|
|
ChannelStream *channelstream = stream->mChannel;
|
|
|
|
emuchannel->mRealChannel[0] = channelstream;
|
|
|
|
channelstream->mNumRealChannels = realchannelsneeded;
|
|
for (count = 0; count < realchannelsneeded; count++)
|
|
{
|
|
channelstream->mRealChannel[count] = realchannelreal[count]; /* Set the channelstream's realchannel (the dsound/software/emu channel). */
|
|
channelstream->mRealChannel[count]->mSubChannelIndex = count;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// ORDINARY SAMPLE GOING FROM EMULATED TO REAL.
|
|
emuchannel->mFlags &= ~CHANNELI_FLAG_JUSTWENTVIRTUAL;
|
|
emuchannel->mNumRealChannels = realchannelsneeded;
|
|
for (count = 0; count < realchannelsneeded; count++)
|
|
{
|
|
emuchannel->mRealChannel[count] = realchannelreal[count];
|
|
emuchannel->mRealChannel[count]->mSubChannelIndex = count;
|
|
}
|
|
}
|
|
|
|
if (emuinfo.mDSP)
|
|
{
|
|
result = emuchannel->play(emuinfo.mDSP, true, false, false);
|
|
}
|
|
else
|
|
{
|
|
result = emuchannel->play(emuinfo.mSound, true, false, false);
|
|
}
|
|
if (result != FMOD_OK)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::updateChannels", "swapped channel failed to play. ZOMBIE CHANNEL!\n"));
|
|
}
|
|
emuchannel->setChannelInfo(&emuinfo);
|
|
emuchannel->setChannelGroup(emuchannel->mChannelGroup);
|
|
emuchannel->setPaused(emuinfo.mPaused);
|
|
}
|
|
|
|
/*
|
|
====================================================================
|
|
Step 3. Convert 'realchannel' to 'emulated' and re-start it.
|
|
====================================================================
|
|
*/
|
|
for (count = 0; count < emulatedtoswap; count++)
|
|
{
|
|
/*
|
|
ok we've done a swap with an emulated voice, now we'll have to create new emulated voices
|
|
*/
|
|
result = mEmulated->getFreeChannel(mode, emuchannelreal, 1, 1, 0);
|
|
|
|
/*
|
|
This channel is going from real to emulated, so just give it 1 subchannel and point it to 1 emulated voice
|
|
*/
|
|
realchannel[count]->mNumRealChannels = 1;
|
|
realchannel[count]->mRealChannel[0] = emuchannelreal[0];
|
|
|
|
if (realinfo[count].mSound)
|
|
{
|
|
result = realchannel[count]->play(realinfo[count].mSound->mSubSampleParent, true, false, false);
|
|
realchannel[count]->setChannelInfo(&realinfo[count]);
|
|
realchannel[count]->setChannelGroup(realchannel[count]->mChannelGroup);
|
|
realchannel[count]->setPaused(realinfo[count].mPaused);
|
|
}
|
|
else if (realinfo[count].mDSP)
|
|
{
|
|
result = realchannel[count]->play(realinfo[count].mDSP, true, false, false);
|
|
realchannel[count]->setChannelInfo(&realinfo[count]);
|
|
realchannel[count]->setChannelGroup(realchannel[count]->mChannelGroup);
|
|
realchannel[count]->setPaused(realinfo[count].mPaused);
|
|
}
|
|
|
|
emucurrent = (ChannelI *)emucurrent->getNext(); /* Don't let it try and swap the same channel again. */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
emuchannel->mFlags &= ~CHANNELI_FLAG_JUSTWENTVIRTUAL; /* Ok, it didnt swap, its going to come in virtual now. */
|
|
}
|
|
|
|
} while (1);
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::updateSoundGroups(int delta)
|
|
{
|
|
SoundGroupI *soundgroup;
|
|
|
|
FMOD_OS_CriticalSection_Enter(gSoundListCrit);
|
|
{
|
|
soundgroup = (SoundGroupI *)mSoundGroupUsedHead.getNext();
|
|
while (soundgroup != &mSoundGroupUsedHead)
|
|
{
|
|
int numaudible = 0;
|
|
SoundGroupI *next = (SoundGroupI *)soundgroup->getNext();
|
|
|
|
soundgroup->mPlayCount = 0;
|
|
|
|
if (soundgroup->mMaxAudibleBehavior == FMOD_SOUNDGROUP_BEHAVIOR_MUTE)
|
|
{
|
|
soundgroup->getNumPlaying(&numaudible);
|
|
}
|
|
|
|
if (numaudible)
|
|
{
|
|
LinkedListNode *current;
|
|
|
|
current = soundgroup->mChannelListHead.getNext();
|
|
while (current != &soundgroup->mChannelListHead)
|
|
{
|
|
ChannelI *channel;
|
|
LinkedListNode *next;
|
|
|
|
channel = (ChannelI *)current->getData();
|
|
next = current->getNext();
|
|
|
|
if (soundgroup->mMaxAudibleBehavior == FMOD_SOUNDGROUP_BEHAVIOR_MUTE && soundgroup->mMaxAudible >= 0)
|
|
{
|
|
soundgroup->mPlayCount++;
|
|
if (soundgroup->mPlayCount > soundgroup->mMaxAudible)
|
|
{
|
|
channel->mFadeTarget = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
channel->mFadeTarget = 1.0f;
|
|
}
|
|
}
|
|
|
|
if (channel->mFadeVolume != channel->mFadeTarget)
|
|
{
|
|
if (soundgroup->mFadeSpeed < 0.001f)
|
|
{
|
|
channel->mFadeVolume = channel->mFadeTarget;
|
|
}
|
|
else
|
|
{
|
|
if (channel->mFadeVolume < channel->mFadeTarget)
|
|
{
|
|
channel->mFadeVolume += ((float)delta / (soundgroup->mFadeSpeed * 1000.0f));
|
|
if (channel->mFadeVolume > channel->mFadeTarget)
|
|
{
|
|
channel->mFadeVolume = channel->mFadeTarget;
|
|
}
|
|
}
|
|
if (channel->mFadeVolume > channel->mFadeTarget)
|
|
{
|
|
channel->mFadeVolume -= ((float)delta / (soundgroup->mFadeSpeed * 1000.0f));
|
|
if (channel->mFadeVolume < channel->mFadeTarget)
|
|
{
|
|
channel->mFadeVolume = channel->mFadeTarget;
|
|
}
|
|
}
|
|
}
|
|
|
|
channel->setVolume(channel->mVolume, true);
|
|
}
|
|
|
|
current = next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Put back on the free list.
|
|
*/
|
|
soundgroup->removeNode();
|
|
soundgroup->addAfter(&mSoundGroupFreeHead);
|
|
}
|
|
soundgroup = next;
|
|
}
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(gSoundListCrit);
|
|
|
|
return FMOD_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef FMOD_SUPPORT_VSTPLUGIN
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::updateVSTPlugins()
|
|
{
|
|
if (!mVSTPluginsListHead.isEmpty())
|
|
{
|
|
LinkedListNode *dspvstnode = mVSTPluginsListHead.getNext();
|
|
while (dspvstnode != &mVSTPluginsListHead)
|
|
{
|
|
DSPVSTPlugin *dspvst = (DSPVSTPlugin *)dspvstnode->getData();
|
|
|
|
/*
|
|
Call VST idle function
|
|
*/
|
|
#ifdef FMOD_SUPPORT_DLLS
|
|
if (dspvst->mDescription.configidle)
|
|
{
|
|
dspvst->mDescription.configidle((FMOD_DSP_STATE *)dspvst);
|
|
}
|
|
#endif
|
|
|
|
dspvstnode = dspvstnode->getNext();
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::findChannel(FMOD_CHANNELINDEX id, SoundI *sound, ChannelI **channel)
|
|
{
|
|
ChannelI *chan = 0;
|
|
int count, subchannels, found;
|
|
ChannelReal *realchannel[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 };
|
|
FMOD_RESULT result;
|
|
FMOD_MODE mode;
|
|
|
|
if (!channel)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = sound->getMode(&mode);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
If the sound is unique, stop any instances of it
|
|
*/
|
|
if (mode & FMOD_UNIQUE)
|
|
{
|
|
result = stopSound(sound);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (id == FMOD_CHANNEL_REUSE)
|
|
{
|
|
chan = *channel;
|
|
|
|
if (!chan)
|
|
{
|
|
id = FMOD_CHANNEL_FREE;
|
|
}
|
|
else
|
|
{
|
|
if (!chan->mRealChannel[0] || chan->mRealChannel[0]->mFlags & CHANNELREAL_FLAG_STOPPED)
|
|
{
|
|
id = (FMOD_CHANNELINDEX)chan->mIndex;
|
|
}
|
|
else
|
|
{
|
|
chan->stopEx(CHANNELI_STOPFLAG_CALLENDCALLBACK | CHANNELI_STOPFLAG_RESETCHANNELGROUP);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (id != FMOD_CHANNEL_REUSE) /* id might be changed above, so it is not a simple else statement. */
|
|
{
|
|
*channel = 0;
|
|
|
|
if (id != FMOD_CHANNEL_FREE)
|
|
{
|
|
if (id < 0 || id >= mNumChannels)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
chan = &mChannel[id];
|
|
chan->stop();
|
|
}
|
|
else
|
|
{
|
|
if (!mChannelFreeListHead.isEmpty())
|
|
{
|
|
chan = SAFE_CAST(ChannelI, mChannelFreeListHead.getNext());
|
|
}
|
|
else /* There would be no stopped voices otherwise they would be in the freelist, so find the lowest priority channel and steal it. */
|
|
{
|
|
LinkedListNode *node = SAFE_CAST(LinkedListNode, mChannelSortedListHead.getPrev());
|
|
|
|
if (mChannelSortedListHead.isEmpty())
|
|
{
|
|
return FMOD_ERR_CHANNEL_ALLOC; /* This usually only happens if someone sets System::init to 0 channels. */
|
|
}
|
|
|
|
node = SAFE_CAST(LinkedListNode, mChannelSortedListHead.getPrev());
|
|
|
|
chan = (ChannelI *)node->getData();
|
|
chan->stopEx(CHANNELI_STOPFLAG_REFSTAMP | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_CALLENDCALLBACK | CHANNELI_STOPFLAG_RESETCHANNELGROUP);
|
|
}
|
|
}
|
|
|
|
chan->removeNode();
|
|
chan->addBefore(&mChannelUsedListHead);
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
if (mode & FMOD_CREATESTREAM)
|
|
{
|
|
Stream *stream = SAFE_CAST(Stream, sound);
|
|
Sample *sample = SAFE_CAST(Sample, stream->mSample);
|
|
|
|
if (sample)
|
|
{
|
|
subchannels = sample->mNumSubSamples;
|
|
}
|
|
else
|
|
{
|
|
subchannels = 0;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
Sample *sample = SAFE_CAST(Sample, sound);
|
|
|
|
subchannels = sample->mNumSubSamples;
|
|
}
|
|
|
|
if (!subchannels)
|
|
{
|
|
subchannels = 1;
|
|
}
|
|
|
|
/*
|
|
Assign a 'REAL' channel to the virtual channel
|
|
*/
|
|
if (((mode & FMOD_SOFTWARE || mOutputType == FMOD_OUTPUTTYPE_OPENAL) && mode & FMOD_CREATECOMPRESSEDSAMPLE)
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
|| (sound->mFormat == FMOD_SOUND_FORMAT_PCM16)
|
|
#endif
|
|
)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
|
|
/*
|
|
Before looking for a software channel (which there might be available), see if it is a realtime compressed sample.
|
|
*/
|
|
if (0)
|
|
{
|
|
}
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
else if (sound->mFormat == FMOD_SOUND_FORMAT_MPEG)
|
|
{
|
|
result = mDSPCodecPool_MPEG.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
else if (sound->mFormat == FMOD_SOUND_FORMAT_XMA)
|
|
{
|
|
result = mDSPCodecPool_XMA.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
else if (sound->mFormat == FMOD_SOUND_FORMAT_IMAADPCM)
|
|
{
|
|
result = mDSPCodecPool_ADPCM.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
else if (sound->mFormat == FMOD_SOUND_FORMAT_CELT)
|
|
{
|
|
result = mDSPCodecPool_CELT.areAnyFree();
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
else if (sound->mFormat == FMOD_SOUND_FORMAT_PCM16)
|
|
{
|
|
result = mDSPCodecPool_RAW.areAnyFree();
|
|
}
|
|
#endif
|
|
#endif
|
|
else
|
|
{
|
|
result = FMOD_ERR_FORMAT;
|
|
}
|
|
}
|
|
|
|
if ((mode & FMOD_CREATESTREAM) && sound->mCodec->mFlags & FMOD_CODEC_HARDWAREMUSICVOICES)
|
|
{
|
|
result = sound->mCodec->getHardwareMusicChannel(&realchannel[0]);
|
|
if (result == FMOD_OK)
|
|
{
|
|
found = 1;
|
|
}
|
|
}
|
|
else if (mode & FMOD_SOFTWARE || !mOutput->mDescription.createsample)
|
|
{
|
|
if (result == FMOD_OK)
|
|
{
|
|
result = mSoftware->getFreeChannel(mode, realchannel, subchannels, sound->mChannels, &found);
|
|
}
|
|
|
|
#else
|
|
|
|
result = FMOD_ERR_NEEDSSOFTWARE;
|
|
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
result = mOutput->getFreeChannel(mode, realchannel, subchannels, sound->mChannels, &found);
|
|
}
|
|
|
|
if (result != FMOD_OK || found != subchannels)
|
|
{
|
|
subchannels = 1;
|
|
result = mEmulated->getFreeChannel(mode, realchannel, subchannels, sound->mChannels, 0);
|
|
}
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (realchannel[0]->mOutput == mEmulated)
|
|
{
|
|
chan->mFlags |= CHANNELI_FLAG_JUSTWENTVIRTUAL;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
if (mode & FMOD_CREATESTREAM)
|
|
{
|
|
if (realchannel[0]->mOutput == mEmulated)
|
|
{
|
|
chan->mNumRealChannels = 1;
|
|
chan->mRealChannel[0] = realchannel[0];
|
|
chan->mRealChannel[0]->mSubChannelIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
Stream *stream = SAFE_CAST(Stream, sound);
|
|
ChannelStream *channelstream = stream->mChannel;
|
|
|
|
chan->mNumRealChannels = 1;
|
|
chan->mRealChannel[0] = channelstream; /* Set the ChannelI's realchannel (the channelstream). */
|
|
|
|
channelstream->mNumRealChannels = subchannels;
|
|
channelstream->mSubChannelIndex = 0;
|
|
for (count = 0; count < subchannels; count++)
|
|
{
|
|
channelstream->mRealChannel[count] = realchannel[count]; /* Set the channelstream's realchannel (the dsound/software/emu channel). */
|
|
channelstream->mRealChannel[count]->mSubChannelIndex = count;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
chan->mNumRealChannels = subchannels;
|
|
for (count = 0; count < subchannels; count++)
|
|
{
|
|
chan->mRealChannel[count] = realchannel[count];
|
|
chan->mRealChannel[count]->mSubChannelIndex = count;
|
|
}
|
|
}
|
|
|
|
*channel = chan;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::findChannel(FMOD_CHANNELINDEX id, DSPI *dsp, ChannelI **channel)
|
|
{
|
|
ChannelI *chan = 0;
|
|
ChannelReal *realchannel[FMOD_CHANNEL_MAXREALSUBCHANNELS] = { 0 };
|
|
FMOD_RESULT result;
|
|
|
|
if (!channel)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (id == FMOD_CHANNEL_REUSE)
|
|
{
|
|
chan = *channel;
|
|
|
|
if (!chan)
|
|
{
|
|
id = FMOD_CHANNEL_FREE;
|
|
}
|
|
else
|
|
{
|
|
if (chan->mRealChannel[0]->mFlags & CHANNELREAL_FLAG_STOPPED)
|
|
{
|
|
id = (FMOD_CHANNELINDEX)chan->mIndex;
|
|
}
|
|
else
|
|
{
|
|
chan->stopEx(CHANNELI_STOPFLAG_RESETCHANNELGROUP);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (id != FMOD_CHANNEL_REUSE) /* id might be changed above, so it is not a simple else statement. */
|
|
{
|
|
*channel = 0;
|
|
|
|
if (id != FMOD_CHANNEL_FREE)
|
|
{
|
|
chan = &mChannel[id];
|
|
chan->stop();
|
|
}
|
|
else
|
|
{
|
|
if (!mChannelFreeListHead.isEmpty())
|
|
{
|
|
chan = SAFE_CAST(ChannelI, mChannelFreeListHead.getNext());
|
|
}
|
|
else /* There would be no stopped voices otherwise they would be in the freelist, so find the lowest priority channel and steal it. */
|
|
{
|
|
LinkedListNode *node = SAFE_CAST(LinkedListNode, mChannelSortedListHead.getPrev());
|
|
|
|
if (mChannelSortedListHead.isEmpty())
|
|
{
|
|
return FMOD_ERR_CHANNEL_ALLOC; /* This usually only happens if someone sets System::init to 0 channels. */
|
|
}
|
|
|
|
node = SAFE_CAST(LinkedListNode, mChannelSortedListHead.getPrev());
|
|
|
|
chan = (ChannelI *)node->getData();
|
|
|
|
chan->stopEx(CHANNELI_STOPFLAG_REFSTAMP | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP);
|
|
}
|
|
}
|
|
|
|
chan->removeNode();
|
|
chan->addBefore(&mChannelUsedListHead);
|
|
}
|
|
|
|
/*
|
|
Assign a 'REAL' channel to the virtual channel
|
|
*/
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
result = mSoftware->getFreeChannel(FMOD_SOFTWARE, realchannel, 1, 1, 0);
|
|
if (result != FMOD_OK)
|
|
#endif
|
|
{
|
|
result = mEmulated->getFreeChannel(FMOD_SOFTWARE, realchannel, 1, 1, 0);
|
|
}
|
|
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
chan->mRealChannel[0] = realchannel[0];
|
|
chan->mNumRealChannels = 1;
|
|
|
|
*channel = chan;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample_out)
|
|
{
|
|
FMOD_RESULT result;
|
|
Output *output;
|
|
Sample *sample = 0;
|
|
AutoRelease<Sample> sample_cleanup;
|
|
int channels, subsamples, count;
|
|
unsigned int loopstart = 0, loopend = 0;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "mode %08x length %d samples, lengthbytes %d\n", mode, waveformat ? waveformat->lengthpcm : 0, waveformat ? waveformat->lengthbytes : 0));
|
|
|
|
if (!sample_out)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (waveformat->lengthpcm == (unsigned int)-1)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
/*
|
|
Check hardware/software bits in user mode and waveformat mode.
|
|
*/
|
|
if (!(mode & (FMOD_HARDWARE | FMOD_SOFTWARE)))
|
|
{
|
|
int num2d, num3d;
|
|
|
|
mode |= (waveformat->mode & (FMOD_HARDWARE | FMOD_SOFTWARE | FMOD_2D | FMOD_3D));
|
|
|
|
#if defined(PLATFORM_PS2)
|
|
if (waveformat->format != FMOD_SOUND_FORMAT_VAG)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "Sound format = %d, not VAG so using software.\n", waveformat->format));
|
|
mode |= FMOD_SOFTWARE;
|
|
}
|
|
#endif
|
|
|
|
getHardwareChannels(&num2d, &num3d, 0);
|
|
|
|
if (mode & FMOD_HARDWARE)
|
|
{
|
|
if (mode & FMOD_3D)
|
|
{
|
|
if (!num3d)
|
|
{
|
|
mode &= ~FMOD_HARDWARE;
|
|
mode |= FMOD_SOFTWARE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!num2d)
|
|
{
|
|
mode &= ~FMOD_HARDWARE;
|
|
mode |= FMOD_SOFTWARE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode & FMOD_SOFTWARE)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (!mSoftware)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "ERROR - Software not initialized\n"));
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
}
|
|
|
|
mode &= ~FMOD_HARDWARE;
|
|
output = mSoftware;
|
|
#else
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "ERROR - Software is disabled. Tried to create a software sample.\n"));
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
mode |= FMOD_HARDWARE;
|
|
output = mOutput;
|
|
}
|
|
|
|
/*
|
|
Check 2D/3D bits in user mode and waveformat mode.
|
|
*/
|
|
if (!(mode & (FMOD_2D | FMOD_3D)))
|
|
{
|
|
mode |= (waveformat->mode & (FMOD_2D | FMOD_3D));
|
|
}
|
|
|
|
if (mode & FMOD_3D)
|
|
{
|
|
mode &= ~FMOD_2D;
|
|
}
|
|
else
|
|
{
|
|
mode |= FMOD_2D;
|
|
}
|
|
|
|
/*
|
|
Check loop bits in user mode and waveformat mode
|
|
*/
|
|
if (!(mode & (FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI)))
|
|
{
|
|
mode |= (waveformat->mode & (FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI));
|
|
}
|
|
|
|
if (mode & FMOD_LOOP_NORMAL)
|
|
{
|
|
mode &= ~FMOD_LOOP_OFF;
|
|
mode &= ~FMOD_LOOP_BIDI;
|
|
mode |= FMOD_LOOP_NORMAL;
|
|
}
|
|
else if (mode & FMOD_LOOP_BIDI)
|
|
{
|
|
mode &= ~FMOD_LOOP_OFF;
|
|
mode |= FMOD_LOOP_BIDI;
|
|
}
|
|
else
|
|
{
|
|
mode |= FMOD_LOOP_OFF;
|
|
}
|
|
|
|
if (waveformat->mode & FMOD_CREATECOMPRESSEDSAMPLE)
|
|
{
|
|
mode |= FMOD_CREATECOMPRESSEDSAMPLE;
|
|
mode &= ~FMOD_CREATESAMPLE;
|
|
}
|
|
|
|
/*
|
|
Create the sample
|
|
*/
|
|
channels = waveformat->channels;
|
|
|
|
/*
|
|
Check for valid number of channels
|
|
*/
|
|
if (channels > FMOD_CHANNEL_MAXREALSUBCHANNELS && channels > (output->mDescription.getsamplemaxchannels ? output->mDescription.getsamplemaxchannels(output, mode, waveformat->format) : 1))
|
|
{
|
|
return FMOD_ERR_TOOMANYCHANNELS;
|
|
}
|
|
|
|
/*
|
|
If a voice is 3d and hardware, and cannot support multichannel input, then make it a multi-mono sample sound.
|
|
When these samples are played, they take up multiple mono voices.
|
|
*/
|
|
if (channels > 1 && channels > (output->mDescription.getsamplemaxchannels ? output->mDescription.getsamplemaxchannels(output, mode, waveformat->format) : 1))
|
|
{
|
|
subsamples = channels;
|
|
|
|
if (*sample_out)
|
|
{
|
|
sample = *sample_out;
|
|
}
|
|
else
|
|
{
|
|
sample = FMOD_Object_Calloc(Sample);
|
|
sample_cleanup = sample;
|
|
}
|
|
if (!sample)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
sample->mNumSubSamples = channels;
|
|
|
|
if (!sample->mName && !(mode & FMOD_LOWMEM))
|
|
{
|
|
sample->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN);
|
|
if (!sample->mName)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Set the default values
|
|
*/
|
|
if (sample->mName)
|
|
{
|
|
FMOD_strcpy(sample->mName, waveformat->name);
|
|
}
|
|
sample->mDefaultFrequency = (float)waveformat->frequency;
|
|
sample->mDefaultChannelMask = waveformat->channelmask;
|
|
sample->mLength = waveformat->lengthpcm;
|
|
sample->mMode = mode;
|
|
sample->mLoopStart = 0;
|
|
sample->mLoopLength = sample->mLength;
|
|
sample->mFormat = waveformat->format;
|
|
sample->mChannels = channels;
|
|
sample->mCodec = 0;
|
|
sample->mType = FMOD_SOUND_TYPE_USER;
|
|
sample->mSystem = this;
|
|
sample->mMinDistance = 1.0f * mDistanceScale;
|
|
sample->mMaxDistance = 10000.0f * mDistanceScale;
|
|
sample->mLockBuffer = mMultiSubSampleLockBuffer.alloc(SOUND_READCHUNKSIZE);
|
|
if (!sample->mLockBuffer)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
channels = 1;
|
|
}
|
|
else
|
|
{
|
|
subsamples = 1;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "subsamples = %d, channels = %d\n", subsamples, channels));
|
|
|
|
for (count = 0; count < subsamples; count++)
|
|
{
|
|
Sample *subsample = subsamples == 1 ? *sample_out : 0;
|
|
FMOD_CODEC_WAVEFORMAT subsamplewaveformat;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "subsample %d. output = %p\n", count, output));
|
|
|
|
FMOD_memcpy(&subsamplewaveformat, waveformat, sizeof(FMOD_CODEC_WAVEFORMAT));
|
|
subsamplewaveformat.channels = channels;
|
|
|
|
if (output->mDescription.createsample)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
output->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#endif
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "use hw\n", count));
|
|
|
|
result = output->mDescription.createsample(output, mode, &subsamplewaveformat, &subsample);
|
|
mCreatedHardwareSample = true;
|
|
}
|
|
else
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "mSoftware = %p\n", mSoftware));
|
|
|
|
result = mSoftware->createSample(mode, &subsamplewaveformat, &subsample);
|
|
#else
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "ERROR - Software is disabled. Tried to create a software sample.\n"));
|
|
result = FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
if (result != FMOD_OK)
|
|
{
|
|
if(subsamples > 1)
|
|
{
|
|
/* free the other subsamples created in this loop */
|
|
sample->release(false);
|
|
}
|
|
else if (subsample && subsample != *sample_out)
|
|
{
|
|
/* don't free sample_out that was passed in */
|
|
subsample->release();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (subsamples > 1)
|
|
{
|
|
sample->mSubSample[count] = subsample;
|
|
}
|
|
else
|
|
{
|
|
if (subsample != *sample_out)
|
|
{
|
|
sample_cleanup = subsample;
|
|
}
|
|
sample = subsample;
|
|
|
|
if (!sample->mName && !(mode & FMOD_LOWMEM))
|
|
{
|
|
sample->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN);
|
|
if (!sample->mName)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Set the default values
|
|
*/
|
|
if (subsample->mName)
|
|
{
|
|
FMOD_strcpy(subsample->mName, waveformat->name);
|
|
}
|
|
subsample->mDefaultFrequency = (float)waveformat->frequency;
|
|
subsample->mDefaultChannelMask = waveformat->channelmask;
|
|
subsample->mMode = mode;
|
|
subsample->mLoopStart = 0;
|
|
subsample->mLoopLength = subsample->mLength;
|
|
subsample->mFormat = waveformat->format;
|
|
subsample->mChannels = channels;
|
|
subsample->mCodec = 0;
|
|
subsample->mType = FMOD_SOUND_TYPE_USER;
|
|
subsample->mSystem = this;
|
|
subsample->mMinDistance = 1.0f * mDistanceScale;
|
|
subsample->mMaxDistance = 10000.0f * mDistanceScale;
|
|
subsample->mSubSampleParent = sample;
|
|
}
|
|
|
|
loopstart = waveformat->loopstart;
|
|
loopend = waveformat->loopend;
|
|
if (!loopend)
|
|
{
|
|
loopend = sample->mLength - 1;
|
|
}
|
|
sample->setLoopPoints(loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM);
|
|
|
|
*sample_out = sample;
|
|
sample_cleanup.releasePtr();
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSample", "done\n"));
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createSoundInternal(const char *name_or_data, FMOD_MODE mode_in, unsigned int buffersize, FMOD_TIMEUNIT buffersizetype, FMOD_CREATESOUNDEXINFO *exinfo, bool calledfromasync, SoundI **sound)
|
|
{
|
|
FMOD_RESULT result;
|
|
SoundI **subsound = 0;
|
|
AutoFree subsound_cleanup;
|
|
SoundI *soundi = 0;
|
|
Codec *codec = 0;
|
|
AutoRelease<Codec> codec_cleanup;
|
|
Metadata *metadata = 0;
|
|
File *file = 0;
|
|
AutoFree file_cleanup;
|
|
AutoClose<File> file_closer;
|
|
FMOD_CODEC_WAVEFORMAT waveformat;
|
|
bool netfile = false;
|
|
int count;
|
|
int numsubsounds;
|
|
int numcodecs;
|
|
int maxchannels = 0;
|
|
FMOD_MODE originalmode = mode_in;
|
|
void *filebufferstack = 0;
|
|
char filebufferstackmem[(16*1024) + 32]; /* +32 for wii/gc alignment */
|
|
|
|
#ifdef FMOD_DEBUG
|
|
if (mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT))
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "memory = %p : mode %08x\n", name_or_data, mode_in));
|
|
}
|
|
else
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "filename = %s : mode %08x\n", name_or_data ? name_or_data : "", mode_in));
|
|
}
|
|
if (exinfo)
|
|
{
|
|
if (exinfo->cbsize)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->cbsize = %d\n", exinfo->cbsize));
|
|
}
|
|
if (exinfo->length)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->length = %d\n", exinfo->length));
|
|
}
|
|
if (exinfo->fileoffset)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->fileoffset = %d\n", exinfo->fileoffset));
|
|
}
|
|
if (exinfo->numchannels)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->numchannels = %d\n", exinfo->numchannels));
|
|
}
|
|
if (exinfo->defaultfrequency)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->defaultfrequency = %d\n", exinfo->defaultfrequency));
|
|
}
|
|
if (exinfo->format)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->format = %d\n", exinfo->format));
|
|
}
|
|
if (exinfo->decodebuffersize)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->decodebuffersize = %d\n", exinfo->decodebuffersize));
|
|
}
|
|
if (exinfo->initialsubsound)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->initialsubsound = %d\n", exinfo->initialsubsound));
|
|
}
|
|
if (exinfo->numsubsounds)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->numsubsounds = %d\n", exinfo->numsubsounds));
|
|
}
|
|
if (exinfo->inclusionlist)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->inclusionlist = %p\n", exinfo->inclusionlist));
|
|
}
|
|
if (exinfo->inclusionlistnum)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->inclusionlistnum = %d\n", exinfo->inclusionlistnum));
|
|
}
|
|
if (exinfo->pcmreadcallback)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->pcmreadcallback = %p\n", exinfo->pcmreadcallback));
|
|
}
|
|
if (exinfo->pcmsetposcallback)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->pcmsetposcallback = %p\n", exinfo->pcmsetposcallback));
|
|
}
|
|
if (exinfo->nonblockcallback)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->nonblockcallback = %p\n", exinfo->nonblockcallback));
|
|
}
|
|
if (exinfo->dlsname)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->dlsname = %s\n", exinfo->dlsname));
|
|
}
|
|
if (exinfo->encryptionkey)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->encryptionkey = %s\n", exinfo->encryptionkey));
|
|
}
|
|
if (exinfo->maxpolyphony)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->maxpolyphony = %d\n", exinfo->maxpolyphony));
|
|
}
|
|
if (exinfo->userdata)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userdata = %p\n", exinfo->userdata));
|
|
}
|
|
if (exinfo->suggestedsoundtype)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->suggestedsoundtype = %d\n", exinfo->suggestedsoundtype));
|
|
}
|
|
if (exinfo->userdata)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userdata = %p\n", exinfo->userdata));
|
|
}
|
|
if (exinfo->useropen)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->useropen = %p\n", exinfo->useropen));
|
|
}
|
|
if (exinfo->userclose)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userclose = %p\n", exinfo->userclose));
|
|
}
|
|
if (exinfo->userread)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userread = %p\n", exinfo->userread));
|
|
}
|
|
if (exinfo->userseek)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->userseek = %p\n", exinfo->userseek));
|
|
}
|
|
if (exinfo->speakermap)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->speakermap = %d\n", exinfo->speakermap));
|
|
}
|
|
if (exinfo->initialsoundgroup)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->initialsoundgroup = %d\n", exinfo->initialsoundgroup));
|
|
}
|
|
if (exinfo->initialseekposition)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->initialseekposition = %d\n", exinfo->initialseekposition));
|
|
}
|
|
if (exinfo->initialseekpostype)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "exinfo->initialseekpostype = %d\n", exinfo->initialseekpostype));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!sound)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!(mode_in & FMOD_NONBLOCKING))
|
|
{
|
|
*sound = 0;
|
|
}
|
|
|
|
/*
|
|
Check this is a valid exinfo structure. Future revisions may want to set version numbers
|
|
here to account for backwards compatibility.
|
|
*/
|
|
if (exinfo && exinfo->cbsize != sizeof(FMOD_CREATESOUNDEXINFO))
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "VERSION MISMATCH : cbsize in code (%d) does not match cbsize in lib (%d). Make sure you are not mixing library and header versions of FMOD!\n", exinfo->cbsize, sizeof(FMOD_CREATESOUNDEXINFO)));
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
/*
|
|
1. Stream overrides sample && realtime sample.
|
|
2. Realtime sample overrides sample.
|
|
*/
|
|
if (mode_in & FMOD_CREATESTREAM)
|
|
{
|
|
mode_in &= ~FMOD_CREATESAMPLE;
|
|
mode_in &= ~FMOD_CREATECOMPRESSEDSAMPLE; /* Decode to PCM through stream thread. */
|
|
}
|
|
else if (mode_in & FMOD_CREATECOMPRESSEDSAMPLE)
|
|
{
|
|
mode_in &= ~FMOD_CREATESAMPLE;
|
|
}
|
|
|
|
/*
|
|
Turn off 3d flags if 2d is set
|
|
*/
|
|
if (mode_in & FMOD_2D)
|
|
{
|
|
mode_in &= ~(FMOD_3D_HEADRELATIVE | FMOD_3D_WORLDRELATIVE | FMOD_3D_LOGROLLOFF | FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF);
|
|
}
|
|
|
|
/*
|
|
Turn on 3d if one of the 3d flags is used.
|
|
*/
|
|
if (mode_in & (FMOD_3D_HEADRELATIVE | FMOD_3D_WORLDRELATIVE | FMOD_3D_LOGROLLOFF | FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF))
|
|
{
|
|
mode_in |= FMOD_3D;
|
|
}
|
|
|
|
/*
|
|
Fall back to software if hardware doesnt exist.
|
|
*/
|
|
if (!(mode_in & FMOD_SOFTWARE))
|
|
{
|
|
int num2d, num3d;
|
|
|
|
getHardwareChannels(&num2d, &num3d, 0);
|
|
|
|
if (mode_in & FMOD_3D)
|
|
{
|
|
if (!num3d)
|
|
{
|
|
mode_in &= ~FMOD_HARDWARE;
|
|
mode_in |= FMOD_SOFTWARE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!num2d)
|
|
{
|
|
mode_in &= ~FMOD_HARDWARE;
|
|
mode_in |= FMOD_SOFTWARE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode_in & FMOD_OPENUSER || mode_in & FMOD_OPENRAW)
|
|
{
|
|
if (!exinfo)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (exinfo->format == FMOD_SOUND_FORMAT_NONE ||
|
|
exinfo->numchannels <= 0 ||
|
|
exinfo->defaultfrequency == 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
}
|
|
|
|
/*
|
|
First open the file
|
|
*/
|
|
if (mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT))
|
|
{
|
|
file_cleanup = file = FMOD_Object_Alloc(MemoryFile);
|
|
CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK);
|
|
|
|
file->init(this, 0, 0); /* Set blocksize to 0 to stop it buffering, we don't need it. */
|
|
}
|
|
else if (mode_in & FMOD_OPENUSER)
|
|
{
|
|
file_cleanup = file = FMOD_Object_Alloc(NullFile);
|
|
CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK);
|
|
|
|
file->init(this, 0, mBufferSize);
|
|
mode_in &= ~FMOD_OPENRAW; /* Just in case the user passed this in. */
|
|
}
|
|
else
|
|
{
|
|
if (exinfo && exinfo->useropen && exinfo->userclose && exinfo->userread && exinfo->userseek && !exinfo->ignoresetfilesystem)
|
|
{
|
|
file_cleanup = file = FMOD_Object_Alloc(UserFile);
|
|
CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK);
|
|
|
|
result = ((UserFile *)file)->setUserCallbacks(exinfo->useropen, exinfo->userclose, exinfo->userread, exinfo->userseek);
|
|
CHECK_RESULT(result);
|
|
|
|
file->init(this, 0, mBufferSize);
|
|
}
|
|
else if (mUsesUserCallbacks && (!exinfo || !exinfo->ignoresetfilesystem))
|
|
{
|
|
file_cleanup = file = FMOD_Object_Alloc(UserFile);
|
|
CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK);
|
|
|
|
file->init(this, 0, mBufferSize);
|
|
}
|
|
#ifdef FMOD_SUPPORT_NET
|
|
else if ((mode_in & FMOD_UNICODE &&
|
|
(!FMOD_strnicmpW((const short *)(L"http://"), (const short *)name_or_data, 7) ||
|
|
!FMOD_strnicmpW((const short *)(L"http:\\\\"), (const short *)name_or_data, 7) ||
|
|
!FMOD_strnicmpW((const short *)(L"https://"), (const short *)name_or_data, 8) ||
|
|
!FMOD_strnicmpW((const short *)(L"https:\\\\"), (const short *)name_or_data, 8) ||
|
|
!FMOD_strnicmpW((const short *)(L"mms://"), (const short *)name_or_data, 6) ||
|
|
!FMOD_strnicmpW((const short *)(L"mms:\\\\"), (const short *)name_or_data, 6))) ||
|
|
(!FMOD_strnicmp("http://", name_or_data, 7) ||
|
|
!FMOD_strnicmp("http:\\\\", name_or_data, 7) ||
|
|
!FMOD_strnicmp("https://", name_or_data, 8) ||
|
|
!FMOD_strnicmp("https:\\\\", name_or_data, 8) ||
|
|
!FMOD_strnicmp("mms://", name_or_data, 6) ||
|
|
!FMOD_strnicmp("mms:\\\\", name_or_data, 6)))
|
|
{
|
|
file_cleanup = file = FMOD_Object_Alloc(NetFile);
|
|
CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK);
|
|
|
|
file->init(this, 0, mBufferSize);
|
|
netfile = true;
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CDDA
|
|
else if (FMOD_OS_CDDA_IsDeviceName((char *)name_or_data))
|
|
{
|
|
file_cleanup = file = FMOD_Object_Alloc(CddaFile);
|
|
CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK);
|
|
|
|
result = ((CddaFile *)file)->init((mode_in & FMOD_CDDA_FORCEASPI) ? true : false, (mode_in & FMOD_CDDA_JITTERCORRECT) ? true : false);
|
|
CHECK_RESULT(result);
|
|
|
|
file->init(this, 0, mBufferSize);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
file_cleanup = file = FMOD_Object_Alloc(DiskFile);
|
|
CHECK_RESULT(file == NULL ? FMOD_ERR_MEMORY : FMOD_OK);
|
|
|
|
file->init(this, 0, mBufferSize);
|
|
}
|
|
}
|
|
|
|
if (*sound && netfile)
|
|
{
|
|
(*sound)->mOpenState = FMOD_OPENSTATE_CONNECTING;
|
|
}
|
|
|
|
if (mode_in & FMOD_CREATESTREAM && file->mBlockSize && file->mBlockSize <= (16*1024))
|
|
{
|
|
filebufferstack = (void *)FMOD_ALIGNPOINTER(filebufferstackmem, 32);
|
|
|
|
/*
|
|
Stop it from doing an alloc, so that it doesn't fragment memory when it reallocs later.
|
|
If it failed (not enough stack) it will just do the normal realloc/heap thing.
|
|
*/
|
|
file->setBuffer(filebufferstack);
|
|
}
|
|
|
|
if (mode_in & FMOD_CREATESTREAM)
|
|
{
|
|
/*
|
|
Set the streaming flag. This is for PS3 FIOS.
|
|
*/
|
|
file->mFlags |= FMOD_FILE_STREAMING;
|
|
}
|
|
|
|
result = file->open(name_or_data, exinfo ? exinfo->length : 0, (mode_in & FMOD_UNICODE) ? 1 : 0, exinfo ? exinfo->encryptionkey : 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
file_closer = file;
|
|
|
|
if (*sound && netfile)
|
|
{
|
|
(*sound)->mOpenState = FMOD_OPENSTATE_BUFFERING;
|
|
}
|
|
|
|
result = file->setStartOffset(exinfo ? exinfo->fileoffset: 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
If the file exists, search for the appropriate codec.
|
|
*/
|
|
result = mPluginFactory->getNumCodecs(&numcodecs);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "%d codecs found. Scan all until one succeeds\n", numcodecs));
|
|
|
|
result = FMOD_ERR_FORMAT;
|
|
|
|
for (count = 0; count < numcodecs; count++)
|
|
{
|
|
FMOD_CODEC_DESCRIPTION_EX *codecdesc;
|
|
unsigned int handle;
|
|
|
|
result = mPluginFactory->getCodecHandle(count, &handle);
|
|
if (result != FMOD_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
result = mPluginFactory->getCodec(handle, &codecdesc);
|
|
if (result != FMOD_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (exinfo)
|
|
{
|
|
int memberoffset = (int)((FMOD_UINT_NATIVE)&exinfo->suggestedsoundtype - (FMOD_UINT_NATIVE)exinfo);
|
|
|
|
if (exinfo->cbsize > memberoffset && exinfo->suggestedsoundtype && !(mode_in & FMOD_OPENUSER))
|
|
{
|
|
/*
|
|
Ignore any codec that isn't the suggested type, except when the suggested type
|
|
is MPEG, we want to allow the tag codec to process any tags in the file first
|
|
*/
|
|
if (codecdesc->mType != exinfo->suggestedsoundtype
|
|
#ifdef FMOD_SUPPORT_TAGS
|
|
&& !(exinfo->suggestedsoundtype == FMOD_SOUND_TYPE_MPEG && codecdesc->mType == FMOD_SOUND_TYPE_TAG)
|
|
#endif
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode_in & FMOD_OPENRAW)
|
|
{
|
|
if (codecdesc->mType != FMOD_SOUND_TYPE_RAW)
|
|
{
|
|
codecdesc = 0;
|
|
continue;
|
|
}
|
|
}
|
|
else if (mode_in & FMOD_OPENUSER)
|
|
{
|
|
if (codecdesc->mType != FMOD_SOUND_TYPE_USER)
|
|
{
|
|
codecdesc = 0;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (codecdesc->mType == FMOD_SOUND_TYPE_RAW || codecdesc->mType == FMOD_SOUND_TYPE_USER)
|
|
{
|
|
codecdesc = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
result = mPluginFactory->createCodec(codecdesc, &codec);
|
|
if (result != FMOD_OK)
|
|
{
|
|
if (result == FMOD_ERR_MEMORY) /* Better not just skip this error. */
|
|
{
|
|
return result;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
codec->mFile = file;
|
|
codec->mMode = mode_in;
|
|
codec->mOriginalMode = originalmode;
|
|
codec->filehandle = file;
|
|
codec->filesize = file->mLength;
|
|
codec->mSystem = this;
|
|
codec->mFlags |= FMOD_CODEC_ACCURATELENGTH;
|
|
|
|
result = codec->mDescription.open(codec, mode_in, exinfo);
|
|
|
|
if (result == FMOD_OK)
|
|
{
|
|
mode_in = codec->mMode; /* The codec wanted to change the mode. */
|
|
|
|
result = codec->mDescription.getwaveformat(codec, 0, &waveformat);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (mode_in & FMOD_OPENUSER)
|
|
{
|
|
waveformat.format = exinfo->format;
|
|
waveformat.channels = exinfo->numchannels;
|
|
waveformat.frequency = exinfo->defaultfrequency;
|
|
|
|
result = SoundI::getSamplesFromBytes(exinfo->length, &waveformat.lengthpcm, exinfo->numchannels, exinfo->format);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
waveformat.blockalign = codec->waveformat[0].blockalign = 1; /* user codec can reference codec->waveformat here because it exists. */
|
|
}
|
|
else if (mode_in & FMOD_OPENRAW)
|
|
{
|
|
unsigned int filesize;
|
|
|
|
file->getSize(&filesize);
|
|
|
|
waveformat.format = codec->waveformat[0].format = exinfo->format; /* raw codec can reference codec->waveformat here because it exists. */
|
|
waveformat.channels = codec->waveformat[0].channels = exinfo->numchannels; /* raw codec can reference codec->waveformat here because it exists. */
|
|
waveformat.frequency = codec->waveformat[0].frequency = exinfo->defaultfrequency; /* raw codec can reference codec->waveformat here because it exists. */
|
|
|
|
result = SoundI::getSamplesFromBytes(filesize, &waveformat.lengthpcm, exinfo->numchannels, exinfo->format);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
maxchannels = waveformat.channels;
|
|
codec->mBlockAlign = waveformat.blockalign; /* keep the first one always */
|
|
|
|
if (codec->numsubsounds && mode_in & FMOD_CREATESTREAM)
|
|
{
|
|
for (count = 0; count < codec->numsubsounds; count++)
|
|
{
|
|
FMOD_CODEC_WAVEFORMAT waveformat2;
|
|
|
|
result = codec->mDescription.getwaveformat(codec, count, &waveformat2);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (waveformat2.channels > maxchannels)
|
|
{
|
|
maxchannels = waveformat2.channels;
|
|
codec->mBlockAlign = waveformat2.blockalign;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
if (waveformat.format == FMOD_SOUND_FORMAT_PCM16)
|
|
{
|
|
if (!mDSPCodecPool_RAW.mNumDSPCodecs)
|
|
{
|
|
result = mDSPCodecPool_RAW.init(FMOD_DSP_CATEGORY_DSPCODECRAW, 256, mAdvancedSettings.maxPCMcodecs ? mAdvancedSettings.maxPCMcodecs : FMOD_ADVANCEDSETTINGS_MAXPCMCODECS);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_TAGS
|
|
if (codec->mType == FMOD_SOUND_TYPE_TAG && !metadata)
|
|
{
|
|
metadata = codec->mMetadata;
|
|
}
|
|
#endif
|
|
|
|
codec->mFile = 0; /* Dont let the release close the file */
|
|
codec->mMetadata = 0;
|
|
codec->release();
|
|
codec = 0;
|
|
|
|
if (result != FMOD_ERR_FORMAT && result != FMOD_ERR_FILE_EOF)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "Fatal error (%d) scanning the codecs. (ie not FMOD_ERR_FORMAT or FMOD_ERR_FILE_EOF)\n", result));
|
|
|
|
if (metadata)
|
|
{
|
|
metadata->release();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (!codec)
|
|
{
|
|
if (metadata)
|
|
{
|
|
metadata->release();
|
|
}
|
|
return FMOD_ERR_FORMAT;
|
|
}
|
|
|
|
numsubsounds = exinfo && exinfo->numsubsounds ? exinfo->numsubsounds : codec->numsubsounds;
|
|
|
|
if (metadata)
|
|
{
|
|
if (codec->mMetadata)
|
|
{
|
|
codec->mMetadata->add(metadata);
|
|
metadata->release();
|
|
}
|
|
else
|
|
{
|
|
codec->mMetadata = metadata;
|
|
}
|
|
}
|
|
|
|
codec->getMetadataFromFile();
|
|
|
|
file_closer.releasePtr();
|
|
file_cleanup.releasePtr();
|
|
codec_cleanup = codec;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Format has %d subsounds.\n", numsubsounds));
|
|
|
|
if (numsubsounds > 0)
|
|
{
|
|
subsound = (SoundI **)FMOD_Memory_Calloc(numsubsounds * sizeof(SoundI *));
|
|
if (!subsound)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
subsound_cleanup = subsound;
|
|
}
|
|
|
|
result = codec->mDescription.getwaveformat(codec, 0, &waveformat);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (waveformat.channels > mMaxInputChannels)
|
|
{
|
|
return FMOD_ERR_TOOMANYCHANNELS;
|
|
}
|
|
|
|
/*
|
|
Load as a stream if the codec told us to.
|
|
*/
|
|
if (codec->mDescription.defaultasstream && !(mode_in & FMOD_CREATESAMPLE))
|
|
{
|
|
mode_in |= FMOD_CREATESTREAM;
|
|
}
|
|
|
|
/*
|
|
The file has now been successfully opened via the codec, now load it or prepare a stream.
|
|
*/
|
|
if (codec->mType == FMOD_SOUND_TYPE_PLAYLIST)
|
|
{
|
|
/*
|
|
Just create an empty sound container if a playlist was loaded
|
|
*/
|
|
SoundI *soundcontainer = 0;
|
|
AutoRelease<SoundI> soundcontainer_cleanup;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Create sample for playlist\n"));
|
|
|
|
if (mode_in & FMOD_NONBLOCKING)
|
|
{
|
|
soundcontainer = *sound;
|
|
}
|
|
else
|
|
{
|
|
soundcontainer = FMOD_Object_Calloc(Sample);
|
|
if (!soundcontainer)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
soundcontainer_cleanup = soundcontainer;
|
|
}
|
|
|
|
if (!soundcontainer->mName && !(mode_in & FMOD_LOWMEM))
|
|
{
|
|
soundcontainer->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN);
|
|
if (!soundcontainer->mName)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
codec_cleanup.releasePtr();
|
|
|
|
soundcontainer->mSystem = this;
|
|
soundcontainer->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0;
|
|
soundcontainer->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0;
|
|
soundcontainer->mPostCallbackSound = (FMOD_SOUND *)soundcontainer;
|
|
soundcontainer->mType = codec->mType;
|
|
soundcontainer->mCodec = codec;
|
|
soundcontainer->mMinDistance = 1.0f * mDistanceScale;
|
|
soundcontainer->mMaxDistance = 10000.0f * mDistanceScale;
|
|
soundcontainer->mSubSound = 0;
|
|
soundcontainer->mNumSubSounds = 0;
|
|
soundcontainer->mSubSoundIndex = -1;
|
|
soundcontainer->mUserData = exinfo ? exinfo->userdata : 0;
|
|
|
|
soundcontainer_cleanup.releasePtr();
|
|
*sound = soundcontainer;
|
|
}
|
|
else
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
if (mode_in & FMOD_CREATESTREAM)
|
|
{
|
|
unsigned int decodebuffersize;
|
|
unsigned int blocksize = 0;
|
|
Stream *stream;
|
|
Sample *sample = 0;
|
|
FMOD_MODE sample_mode = mode_in & ~(FMOD_CREATECOMPRESSEDSAMPLE);
|
|
FMOD_CODEC_WAVEFORMAT waveformatstream;
|
|
AutoRelease<Stream> stream_cleanup;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Create as FMOD_CREATESTREAM\n"));
|
|
|
|
if (mode_in & FMOD_NONBLOCKING)
|
|
{
|
|
stream = SAFE_CAST(Stream, *sound);
|
|
}
|
|
else
|
|
{
|
|
stream = FMOD_Object_Calloc(Stream);
|
|
if (!stream)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
stream_cleanup = stream;
|
|
}
|
|
|
|
if (!stream->mName && !(mode_in & FMOD_LOWMEM))
|
|
{
|
|
stream->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN);
|
|
if (!stream->mName)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
codec_cleanup.releasePtr();
|
|
subsound_cleanup.releasePtr();
|
|
|
|
stream->mSubSound = subsound;
|
|
stream->mNumSubSounds = numsubsounds;
|
|
stream->mCodec = codec;
|
|
stream->mSystem = this;
|
|
|
|
if (!(codec->mFlags & FMOD_CODEC_HARDWAREMUSICVOICES))
|
|
{
|
|
stream->getSamplesFromBytes(codec->mBlockAlign, &blocksize, maxchannels, waveformat.format);
|
|
|
|
/*
|
|
Calculate the PCM Buffersize in samples based on the user setting.
|
|
*/
|
|
decodebuffersize = exinfo ? exinfo->decodebuffersize : 0;
|
|
|
|
if (!decodebuffersize)
|
|
{
|
|
decodebuffersize = mAdvancedSettings.defaultDecodeBufferSize;
|
|
decodebuffersize *= waveformat.frequency;
|
|
decodebuffersize /= 1000;
|
|
}
|
|
|
|
if (!blocksize)
|
|
{
|
|
blocksize = decodebuffersize;
|
|
}
|
|
|
|
if (blocksize < 256)
|
|
{
|
|
if (blocksize <= 0)
|
|
{
|
|
blocksize = 1;
|
|
}
|
|
|
|
blocksize = ((256 + (blocksize - 1)) / blocksize) * blocksize;
|
|
}
|
|
|
|
decodebuffersize /= blocksize;
|
|
decodebuffersize *= blocksize;
|
|
if (decodebuffersize <= blocksize)
|
|
{
|
|
decodebuffersize = blocksize * 2;
|
|
}
|
|
|
|
if (!(mode_in & FMOD_OPENUSER) && numsubsounds == 0 && waveformat.lengthpcm <= decodebuffersize && sample_mode & FMOD_SOFTWARE)
|
|
{
|
|
decodebuffersize = waveformat.lengthpcm;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "decode buffersize = %d : blocksize = %d : format = %d\n", decodebuffersize, blocksize, waveformat.format));
|
|
|
|
/*
|
|
Create a small streaming sample to double buffer into
|
|
*/
|
|
sample_mode &= ~FMOD_LOOP_OFF;
|
|
sample_mode &= ~FMOD_LOOP_BIDI;
|
|
sample_mode &= ~FMOD_OPENMEMORY_POINT;
|
|
sample_mode |= FMOD_LOOP_NORMAL;
|
|
|
|
FMOD_memcpy(&waveformatstream, &waveformat, sizeof(FMOD_CODEC_WAVEFORMAT));
|
|
|
|
if (exinfo && exinfo->speakermap)
|
|
{
|
|
if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO)
|
|
{
|
|
waveformatstream.channelmask = SPEAKER_ALLMONO;
|
|
}
|
|
else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO)
|
|
{
|
|
waveformatstream.channelmask = SPEAKER_ALLSTEREO;
|
|
}
|
|
else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS)
|
|
{
|
|
waveformatstream.channelmask = SPEAKER_PROTOOLS;
|
|
}
|
|
}
|
|
|
|
waveformatstream.lengthpcm = decodebuffersize;
|
|
waveformatstream.channels = maxchannels;
|
|
waveformatstream.loopstart = 0;
|
|
waveformatstream.loopend = waveformatstream.lengthpcm - 1;
|
|
|
|
result = createSample(sample_mode, &waveformatstream, &sample);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Put in any post sample create info from the user.
|
|
*/
|
|
sample->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0;
|
|
sample->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0;
|
|
sample->mUserData = exinfo ? exinfo->userdata : 0;
|
|
sample->mPostCallbackSound = (FMOD_SOUND *)stream;
|
|
sample->mCodec = codec;
|
|
|
|
if (sample->mNumSubSamples)
|
|
{
|
|
for (int count = 0; count < sample->mNumSubSamples; count++)
|
|
{
|
|
sample->mSubSample[count]->mCodec = codec;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Set up stream members.
|
|
*/
|
|
stream->mSample = sample;
|
|
stream->mSubSoundIndex = 0;
|
|
stream->mLength = waveformat.lengthpcm;
|
|
stream->mLengthBytes = waveformat.lengthbytes;
|
|
stream->mDefaultFrequency = (float)waveformat.frequency;
|
|
stream->mMode = sample_mode | FMOD_UNIQUE | FMOD_CREATESTREAM;
|
|
stream->mMode = stream->mMode & ~FMOD_LOOP_NORMAL; /* Don't inherit the loop mode */
|
|
stream->mFormat = waveformat.format;
|
|
stream->mChannels = maxchannels;
|
|
stream->mSystem = this;
|
|
stream->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0;
|
|
stream->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0;
|
|
stream->mPostCallbackSound = (FMOD_SOUND *)stream;
|
|
stream->mBlockSize = blocksize;
|
|
stream->mType = codec->mType;
|
|
stream->mMinDistance = 1.0f * mDistanceScale;
|
|
stream->mMaxDistance = 10000.0f * mDistanceScale;
|
|
stream->mUserData = exinfo ? exinfo->userdata : 0;
|
|
if (stream->mName)
|
|
{
|
|
FMOD_strcpy(stream->mName, waveformat.name);
|
|
}
|
|
|
|
if (sample)
|
|
{
|
|
if (exinfo && exinfo->speakermap)
|
|
{
|
|
if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO)
|
|
{
|
|
sample->mDefaultChannelMask = SPEAKER_ALLMONO;
|
|
}
|
|
else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO)
|
|
{
|
|
sample->mDefaultChannelMask = SPEAKER_ALLSTEREO;
|
|
}
|
|
else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS)
|
|
{
|
|
sample->mDefaultChannelMask = SPEAKER_PROTOOLS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sample->mDefaultChannelMask = waveformat.channelmask;
|
|
}
|
|
|
|
/*
|
|
Fix up the hardware/software and 2d/3d bits of the mode.
|
|
*/
|
|
stream->mMode &= ~(FMOD_2D | FMOD_3D | FMOD_HARDWARE | FMOD_SOFTWARE);
|
|
stream->mMode |= (sample->mMode & (FMOD_2D | FMOD_3D | FMOD_HARDWARE | FMOD_SOFTWARE));
|
|
}
|
|
|
|
/*
|
|
Loop information.
|
|
*/
|
|
if (waveformat.mode & FMOD_LOOP_OFF || mode_in & FMOD_LOOP_OFF)
|
|
{
|
|
stream->mMode |= FMOD_LOOP_OFF;
|
|
}
|
|
else if (waveformat.mode & FMOD_LOOP_NORMAL || mode_in & FMOD_LOOP_NORMAL)
|
|
{
|
|
stream->mMode |= FMOD_LOOP_NORMAL;
|
|
}
|
|
else
|
|
{
|
|
stream->mMode |= FMOD_LOOP_OFF;
|
|
}
|
|
stream->setLoopPoints(waveformat.loopstart, FMOD_TIMEUNIT_PCM, waveformat.loopend, FMOD_TIMEUNIT_PCM);
|
|
|
|
if (!stream->mSoundGroupNode.getData())
|
|
{
|
|
result = stream->setSoundGroup(mSoundGroup);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Allocate stream channel
|
|
*/
|
|
stream->mChannel = FMOD_Object_Calloc(ChannelStream);
|
|
if (!stream->mChannel)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
stream->mChannel->mSystem = this;
|
|
|
|
FMOD_OS_CriticalSection_Enter(SystemI::mStreamListCrit);
|
|
{
|
|
stream->mStreamNode.setData(stream);
|
|
stream->mStreamNode.addBefore(&mStreamListSoundHead);
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(SystemI::mStreamListCrit);
|
|
|
|
if (numsubsounds > 0 && !(mode_in & FMOD_OPENUSER))
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "%d subsounds detected.\n", numsubsounds));
|
|
|
|
Stream *substream = 0;
|
|
|
|
substream = (Stream *)FMOD_Object_Calloc(Stream);
|
|
if (!substream)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
AutoRelease<Stream> substream_cleanup(substream);
|
|
|
|
if (numsubsounds > 1)
|
|
{
|
|
stream->mSubSoundShared = substream;
|
|
}
|
|
|
|
if (!(mode_in & FMOD_LOWMEM))
|
|
{
|
|
substream->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN);
|
|
if (!substream->mName)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
substream->mSystem = this;
|
|
substream->mSample = sample;
|
|
substream->mCodec = codec;
|
|
substream->mSubSoundIndex = 0;
|
|
substream->mChannel = stream->mChannel;
|
|
substream->mMode = stream->mMode;
|
|
substream->mType = stream->mType;
|
|
substream->mAsyncData = stream->mAsyncData;
|
|
substream->mMinDistance = 1.0f * mDistanceScale;
|
|
substream->mMaxDistance = 10000.0f * mDistanceScale;
|
|
substream->mBlockSize = blocksize;
|
|
substream->mSubSoundParent = stream;
|
|
if (numsubsounds > 1)
|
|
{
|
|
substream->mSubSoundShared = substream;
|
|
}
|
|
|
|
if (!substream->mSoundGroupNode.getData())
|
|
{
|
|
result = substream->setSoundGroup(mSoundGroup);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
for (count=0; count < numsubsounds; count++)
|
|
{
|
|
subsound[count] = substream;
|
|
|
|
if (count < codec->numsubsounds)
|
|
{
|
|
FMOD_CODEC_WAVEFORMAT waveformat2;
|
|
|
|
result = codec->mDescription.getwaveformat(codec, count, &waveformat2);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (substream->mName)
|
|
{
|
|
FMOD_strcpy(substream->mName, waveformat2.name);
|
|
}
|
|
substream->mSubSoundIndex = count;
|
|
substream->mFormat = waveformat2.format;
|
|
substream->mChannels = waveformat2.channels;
|
|
substream->mDefaultFrequency = (float)waveformat2.frequency;
|
|
substream->mDefaultChannelMask = waveformat2.channelmask;
|
|
substream->mLoopStart = waveformat2.loopstart;
|
|
substream->mLoopLength = waveformat2.loopend - waveformat2.loopstart + 1;
|
|
substream->mLength = waveformat2.lengthpcm;
|
|
substream->mLengthBytes = waveformat2.lengthbytes;
|
|
substream->setLoopPoints(waveformat2.loopstart, FMOD_TIMEUNIT_PCM, waveformat2.loopend, FMOD_TIMEUNIT_PCM);
|
|
|
|
if (codec->mDescription.soundcreate)
|
|
{
|
|
result = codec->mDescription.soundcreate(codec, count, (FMOD_SOUND *)substream);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
stream->mNumActiveSubSounds++;
|
|
}
|
|
|
|
if (codec->numsubsounds && substream->mSubSoundShared)
|
|
{
|
|
substream->updateSubSound(0, false);
|
|
}
|
|
|
|
if (stream->mType == FMOD_SOUND_TYPE_CDDA && !(mode_in & FMOD_LOWMEM) && !(exinfo && exinfo->initialsubsound))
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "Setting up parent sound to contain a sentence of all subsounds.\n"));
|
|
|
|
stream->setSubSoundSentence(0, numsubsounds);
|
|
}
|
|
|
|
substream_cleanup.releasePtr();
|
|
}
|
|
else
|
|
{
|
|
if (codec->mDescription.soundcreate)
|
|
{
|
|
result = codec->mDescription.soundcreate(codec, 0, (FMOD_SOUND *)stream);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Convert the initial position time unit to PCM
|
|
*/
|
|
if (exinfo)
|
|
{
|
|
switch (exinfo->initialseekpostype)
|
|
{
|
|
case 0: // Defaults to FMOD_TIMEUNIT_PCM
|
|
{
|
|
break;
|
|
}
|
|
case FMOD_TIMEUNIT_PCM:
|
|
{
|
|
break;
|
|
}
|
|
case FMOD_TIMEUNIT_PCMBYTES:
|
|
{
|
|
stream->getSamplesFromBytes(exinfo->initialseekposition, &exinfo->initialseekposition);
|
|
exinfo->initialseekpostype = FMOD_TIMEUNIT_PCM;
|
|
break;
|
|
}
|
|
case FMOD_TIMEUNIT_MS:
|
|
{
|
|
exinfo->initialseekposition = (unsigned int)((float)exinfo->initialseekposition / 1000.0f * stream->mDefaultFrequency);
|
|
exinfo->initialseekpostype = FMOD_TIMEUNIT_PCM;
|
|
break;
|
|
}
|
|
default: // Non-supported time unit chosen
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exinfo && exinfo->initialsubsound && stream->mSubSound)
|
|
{
|
|
Stream *substream = 0;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "Setting initial subsound. exinfo->initialsubsound = %d, stream->mNumSubSounds = %d.\n", exinfo->initialsubsound, stream->mNumSubSounds));
|
|
|
|
if (exinfo->initialsubsound < 0 || exinfo->initialsubsound >= stream->mNumSubSounds)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
stream->mSubSoundIndex = exinfo->initialsubsound;
|
|
stream->mChannel->mSubSoundListCurrent = exinfo->initialsubsound;
|
|
|
|
substream = SAFE_CAST(Stream, stream->mSubSound[stream->mSubSoundIndex]);
|
|
if (substream)
|
|
{
|
|
substream->mSubSoundIndex = exinfo->initialsubsound;
|
|
substream->mChannel->mSubSoundListCurrent = exinfo->initialsubsound;
|
|
|
|
if (substream->mSubSoundShared)
|
|
{
|
|
result = substream->updateSubSound(exinfo->initialsubsound, false);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
stream->mInitialPosition = exinfo->initialseekposition;
|
|
result = substream->setPosition(exinfo->initialseekposition, FMOD_TIMEUNIT_PCM);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "Seek stream to start\n"));
|
|
|
|
/*
|
|
Seek and pre-flush the buffer.
|
|
*/
|
|
stream->mInitialPosition = exinfo ? exinfo->initialseekposition : 0;
|
|
result = stream->setPosition(exinfo ? exinfo->initialseekposition : 0, FMOD_TIMEUNIT_PCM);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (!(mode_in & FMOD_OPENONLY) && !(codec->mFlags & FMOD_CODEC_HARDWAREMUSICVOICES))
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "flush stream buffer\n"));
|
|
|
|
result = stream->flush();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "flush successful.\n"));
|
|
}
|
|
|
|
/*
|
|
Change from a blocking single buffered sector sized file to a large doublebuffered file.
|
|
Dont do it for mod files, they dont have a file buffer at runtime anyway.
|
|
*/
|
|
if (!(mode_in & FMOD_OPENUSER) && !(codec->mFlags & FMOD_CODEC_HARDWAREMUSICVOICES)
|
|
&& codec->mType != FMOD_SOUND_TYPE_IT
|
|
&& codec->mType != FMOD_SOUND_TYPE_XM
|
|
&& codec->mType != FMOD_SOUND_TYPE_S3M
|
|
&& codec->mType != FMOD_SOUND_TYPE_MOD
|
|
&& codec->mType != FMOD_SOUND_TYPE_MIDI)
|
|
{
|
|
unsigned int sizebytes = 0;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "switch file handle from small blocking single buffered to large nonblocking doublebuffered.\n"));
|
|
|
|
if (buffersizetype == FMOD_TIMEUNIT_RAWBYTES)
|
|
{
|
|
sizebytes = buffersize;
|
|
}
|
|
else
|
|
{
|
|
unsigned int pcm = buffersize;
|
|
|
|
if (buffersizetype == FMOD_TIMEUNIT_MS)
|
|
{
|
|
pcm = buffersize * (unsigned int)stream->mDefaultFrequency / 1000;
|
|
}
|
|
else if (buffersizetype == FMOD_TIMEUNIT_PCMBYTES)
|
|
{
|
|
stream->getSamplesFromBytes(buffersize, &pcm);
|
|
}
|
|
|
|
/*
|
|
This section tries to guess the size in bytes (sizebytes) it would take with
|
|
this compressed format, to read the buffersize worth of output samples (pcm).
|
|
*/
|
|
if (stream->mLength == (unsigned int)-1 || file->mLength == (unsigned int)-1)
|
|
{
|
|
stream->getBytesFromSamples(pcm, &sizebytes); /* In this case, length is infinite, so make a rough guess based on PCM bytes. */
|
|
}
|
|
else
|
|
{
|
|
float frac;
|
|
|
|
if (stream->mLengthBytes)
|
|
{
|
|
frac = (float)stream->mLengthBytes / (float)stream->mLength;
|
|
}
|
|
else
|
|
{
|
|
frac = (float)file->mLength / (float)stream->mLength;
|
|
}
|
|
|
|
sizebytes = (unsigned int)((float)pcm * frac);
|
|
}
|
|
}
|
|
|
|
result = file->enableDoubleBuffer(sizebytes, filebufferstack); /* Now double buffered, size determined by user */
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
stream_cleanup.releasePtr();
|
|
*sound = stream;
|
|
}
|
|
else /* ^^^^^^ (mode_in & FMOD_CREATESTREAM) ^^^^^^ */
|
|
#endif
|
|
{
|
|
Sample *sample = 0, *samplecontainer = 0;
|
|
AutoRelease<Sample> samplecontainer_cleanup;
|
|
bool realtimesample = false;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Create as FMOD_CREATESAMPLE\n"));
|
|
|
|
if (numsubsounds > 0)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "Multi-sample sound (%d subsounds), create a sample container.\n", numsubsounds));
|
|
|
|
if (mode_in & FMOD_NONBLOCKING)
|
|
{
|
|
samplecontainer = SAFE_CAST(Sample, *sound);
|
|
}
|
|
else
|
|
{
|
|
samplecontainer = FMOD_Object_Calloc(Sample);
|
|
if (!samplecontainer)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
samplecontainer_cleanup = samplecontainer;
|
|
}
|
|
|
|
if (!samplecontainer->mName && !(mode_in & FMOD_LOWMEM))
|
|
{
|
|
samplecontainer->mName = (char *)FMOD_Memory_Calloc(FMOD_STRING_MAXNAMELEN);
|
|
if (!samplecontainer->mName)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
codec_cleanup.releasePtr();
|
|
subsound_cleanup.releasePtr();
|
|
|
|
samplecontainer->mSystem = this;
|
|
samplecontainer->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0;
|
|
samplecontainer->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0;
|
|
samplecontainer->mPostCallbackSound = (FMOD_SOUND *)samplecontainer;
|
|
samplecontainer->mType = codec->mType;
|
|
samplecontainer->mCodec = codec;
|
|
samplecontainer->mMinDistance = 1.0f * mDistanceScale;
|
|
samplecontainer->mMaxDistance = 10000.0f * mDistanceScale;
|
|
samplecontainer->mSubSound = subsound;
|
|
samplecontainer->mNumSubSounds = numsubsounds;
|
|
samplecontainer->mSubSoundIndex = -1;
|
|
samplecontainer->mUserData = exinfo ? exinfo->userdata : 0;
|
|
samplecontainer->mMode = mode_in;
|
|
samplecontainer->mDefaultFrequency = (float)waveformat.frequency;
|
|
}
|
|
|
|
for (count = 0; (count < (numsubsounds == 0 ? 1 : numsubsounds)) && (count < (numsubsounds == 0 ? 1 : codec->numsubsounds)); count++)
|
|
{
|
|
FMOD_MODE sample_mode = mode_in;
|
|
FMOD_CODEC_WAVEFORMAT waveformat2;
|
|
|
|
result = codec->mDescription.getwaveformat(codec, count, &waveformat2);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (!numsubsounds && (sample_mode & FMOD_NONBLOCKING))
|
|
{
|
|
sample = SAFE_CAST(Sample, *sound);
|
|
}
|
|
else
|
|
{
|
|
sample = 0;
|
|
}
|
|
|
|
/*
|
|
Check the user passed in inclusion list if this sample should be loaded or not.
|
|
*/
|
|
if (numsubsounds > 0 && exinfo && exinfo->inclusionlist)
|
|
{
|
|
bool found = false;
|
|
int count2;
|
|
|
|
for (count2 = 0; count2 < exinfo->inclusionlistnum; count2++)
|
|
{
|
|
if (exinfo->inclusionlist[count2] == count)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (exinfo && exinfo->speakermap)
|
|
{
|
|
if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLMONO)
|
|
{
|
|
waveformat2.channelmask = SPEAKER_ALLMONO;
|
|
}
|
|
else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_ALLSTEREO)
|
|
{
|
|
waveformat2.channelmask = SPEAKER_ALLSTEREO;
|
|
}
|
|
else if (exinfo->speakermap == FMOD_SPEAKERMAPTYPE_51_PROTOOLS)
|
|
{
|
|
waveformat2.channelmask = SPEAKER_PROTOOLS;
|
|
}
|
|
}
|
|
|
|
if (sample_mode & FMOD_CREATECOMPRESSEDSAMPLE)
|
|
{
|
|
if (waveformat2.format == FMOD_SOUND_FORMAT_MPEG ||
|
|
waveformat2.format == FMOD_SOUND_FORMAT_IMAADPCM ||
|
|
waveformat2.format == FMOD_SOUND_FORMAT_XMA ||
|
|
waveformat2.format == FMOD_SOUND_FORMAT_CELT) /* Only allow these formats through as realtime samples. */
|
|
{
|
|
realtimesample = true;
|
|
}
|
|
else
|
|
{
|
|
sample_mode &= ~FMOD_CREATECOMPRESSEDSAMPLE;
|
|
|
|
if (samplecontainer)
|
|
{
|
|
samplecontainer->mMode &= ~FMOD_CREATECOMPRESSEDSAMPLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!realtimesample && mode_in & FMOD_OPENMEMORY_POINT)
|
|
{
|
|
result = codec->canPointTo();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Create the sample
|
|
*/
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "creating subsound %d/%d\n", count, numsubsounds));
|
|
|
|
result = createSample(sample_mode, &waveformat2, &sample);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Put in any post sample create info from the user.
|
|
*/
|
|
codec_cleanup.releasePtr();
|
|
sample->mSubSoundIndex = count;
|
|
sample->mSubSoundParent = samplecontainer;
|
|
sample->mType = codec->mType;
|
|
sample->mCodec = codec;
|
|
sample->mMinDistance = 1.0f * mDistanceScale;
|
|
sample->mMaxDistance = 10000.0f * mDistanceScale;
|
|
sample->mUserData = exinfo ? exinfo->userdata : 0;
|
|
|
|
if (sample->mNumSubSamples)
|
|
{
|
|
for (int count = 0; count < sample->mNumSubSamples; count++)
|
|
{
|
|
sample->mSubSample[count]->mCodec = codec;
|
|
sample->mSubSample[count]->mSubSoundIndex = sample->mSubSoundIndex;
|
|
}
|
|
}
|
|
|
|
if (!sample->mSoundGroupNode.getData())
|
|
{
|
|
result = sample->setSoundGroup(mSoundGroup);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Now load it.
|
|
*/
|
|
if (!numsubsounds && count == 0)
|
|
{
|
|
sample->mPostReadCallback = exinfo ? exinfo->pcmreadcallback : 0;
|
|
sample->mPostSetPositionCallback = exinfo ? exinfo->pcmsetposcallback : 0;
|
|
sample->mPostCallbackSound = (FMOD_SOUND *)sample;
|
|
}
|
|
|
|
if (codec->mDescription.soundcreate)
|
|
{
|
|
SoundI *s = SAFE_CAST(SoundI, sample);
|
|
|
|
result = codec->mDescription.soundcreate(codec, count, (FMOD_SOUND *)s);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
result = codec->reset(); /* Reset the codec if it needs resetting, and clear PCM Buffer and reset it. */
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (waveformat2.mode & FMOD_CREATECOMPRESSEDSAMPLE || sample_mode & FMOD_CREATECOMPRESSEDSAMPLE)
|
|
{
|
|
result = codec->setPosition(count, 0, FMOD_TIMEUNIT_RAWBYTES);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = codec->setPosition(count, 0, FMOD_TIMEUNIT_PCM);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (sample->mPostSetPositionCallback)
|
|
{
|
|
if (numsubsounds > 0)
|
|
{
|
|
samplecontainer->mPostSetPositionCallback((FMOD_SOUND *)samplecontainer, count, 0, FMOD_TIMEUNIT_PCM);
|
|
}
|
|
else
|
|
{
|
|
sample->mPostSetPositionCallback((FMOD_SOUND *)sample, 0, 0, FMOD_TIMEUNIT_PCM);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Get the data for the sample (from the file or just point to it).
|
|
*/
|
|
if (sample_mode & FMOD_OPENMEMORY_POINT) /* If we are simply pointing to the user's memory address. */
|
|
{
|
|
if (codec->mNonInterleaved && sample->mNumSubSamples)
|
|
{
|
|
unsigned int onechanlengthbytes = 0;
|
|
|
|
sample->getBytesFromSamples(sample->mLength / sample->mNumSubSamples, &onechanlengthbytes);
|
|
|
|
for (int count = 0; count < sample->mNumSubSamples; count++)
|
|
{
|
|
result = ((Sample *)(sample->mSubSample[count]))->setBufferData((char *)name_or_data + codec->mFile->mCurrentPosition + (onechanlengthbytes * count));
|
|
if (result != FMOD_OK)
|
|
{
|
|
if (samplecontainer || codec)
|
|
{
|
|
sample->release(); /* Release it here because it hasnt been made a subsound yet. */
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = sample->setBufferData((char *)name_or_data + codec->mFile->mCurrentPosition);
|
|
if (result != FMOD_OK)
|
|
{
|
|
if (samplecontainer || codec)
|
|
{
|
|
sample->release(); /* Release it here because it hasnt been made a subsound yet. */
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
else if (!(sample_mode & FMOD_OPENONLY))
|
|
{
|
|
unsigned int samplelength, read;
|
|
|
|
if (waveformat2.mode & FMOD_CREATECOMPRESSEDSAMPLE || sample_mode & FMOD_CREATECOMPRESSEDSAMPLE)
|
|
{
|
|
samplelength = waveformat2.lengthbytes;
|
|
|
|
if (codec->mType != FMOD_SOUND_TYPE_FSB && codec->mType != FMOD_SOUND_TYPE_XMA)
|
|
{
|
|
samplelength -= codec->mSrcDataOffset;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
samplelength = waveformat2.lengthpcm;
|
|
}
|
|
|
|
result = sample->read(0, samplelength, &read);
|
|
if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF)
|
|
{
|
|
if (!(sample->mMode & FMOD_NONBLOCKING) && (samplecontainer || codec))
|
|
{
|
|
sample->release(); /* Release it here because it hasnt been made a subsound yet. */
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (samplelength != read && sample->mLoopLength == sample->mLength)
|
|
{
|
|
sample->mLoopLength = read;
|
|
}
|
|
|
|
/*
|
|
Now after the read, set the position back to 0 again.
|
|
*/
|
|
result = sample->setPositionInternal(0);
|
|
if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (numsubsounds > 0)
|
|
{
|
|
subsound[count] = sample;
|
|
|
|
samplecontainer->mNumActiveSubSounds++;
|
|
}
|
|
}
|
|
|
|
// Don't close the file handle for DLS files with inclusion lists, MIDI will
|
|
// need to specify which sub sounds to load after the DLS is created.
|
|
if (!(mode_in & FMOD_OPENONLY) && !(codec->mType == FMOD_SOUND_TYPE_DLS && numsubsounds > 0 && exinfo && exinfo->inclusionlist))
|
|
{
|
|
if (file)
|
|
{
|
|
file->close();
|
|
FMOD_Memory_Free(file);
|
|
}
|
|
codec->mFile = 0;
|
|
#if 0
|
|
if (!(mode_in & FMOD_CREATECOMPRESSEDSAMPLE))
|
|
{
|
|
codec->release();
|
|
codec = 0;
|
|
sample->mCodec = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (numsubsounds > 0)
|
|
{
|
|
samplecontainer_cleanup.releasePtr();
|
|
*sound = samplecontainer;
|
|
}
|
|
else
|
|
{
|
|
*sound = sample;
|
|
}
|
|
}
|
|
|
|
soundi = SAFE_CAST(SoundI, *sound);
|
|
FMOD_OS_CriticalSection_Enter(gSoundListCrit);
|
|
{
|
|
soundi->addAfter(&mSoundListHead);
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(gSoundListCrit);
|
|
|
|
if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "System::createSoundInternal", "sound open failed with error %d\n", result));
|
|
|
|
if (!(mode_in & FMOD_NONBLOCKING))
|
|
{
|
|
*sound = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Put the name of the sound into the name field.
|
|
*/
|
|
if (soundi->mName && ((mode_in & FMOD_UNICODE && !((short *)soundi->mName)[0]) || !soundi->mName[0]))
|
|
{
|
|
bool usefilename = true;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "No name found in file, use filename.\n"));
|
|
|
|
/*
|
|
First look for the name in a tag.
|
|
*/
|
|
if (codec->mMetadata)
|
|
{
|
|
FMOD_TAG tag;
|
|
|
|
result = codec->mMetadata->getTag("TITLE", 0, &tag);
|
|
if (result == FMOD_OK)
|
|
{
|
|
FMOD_strncpy(soundi->mName, (char *)tag.data, FMOD_STRING_MAXNAMELEN);
|
|
usefilename = false;
|
|
|
|
/*
|
|
Update tag so it is set back to "updated" status
|
|
*/
|
|
codec->mMetadata->addTag(tag.type, "TITLE", tag.data, tag.datalen, tag.datatype, true);
|
|
}
|
|
else
|
|
{
|
|
result = codec->mMetadata->getTag("TT2", 0, &tag);
|
|
if (result == FMOD_OK)
|
|
{
|
|
FMOD_strncpy(soundi->mName, (char *)tag.data, FMOD_STRING_MAXNAMELEN);
|
|
usefilename = false;
|
|
|
|
/*
|
|
Update tag so it is set back to "updated" status
|
|
*/
|
|
codec->mMetadata->addTag(tag.type, "TT2", tag.data, tag.datalen, tag.datatype, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (usefilename && !(mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT)) && name_or_data)
|
|
{
|
|
if (mode_in & FMOD_UNICODE)
|
|
{
|
|
for (count = FMOD_strlenW((short *)name_or_data); count >= 0; count--)
|
|
{
|
|
if (((short *)name_or_data)[count] == L'\\' || ((short *)name_or_data)[count] == L'/')
|
|
{
|
|
count++;
|
|
break;
|
|
}
|
|
}
|
|
if (count < 0)
|
|
{
|
|
count = 0;
|
|
}
|
|
|
|
FMOD_strncpyW((short *)soundi->mName, (short *)name_or_data + count, FMOD_STRING_MAXNAMELEN / 2);
|
|
}
|
|
else
|
|
{
|
|
for (count = FMOD_strlen(name_or_data); count >= 0; count--)
|
|
{
|
|
if (name_or_data[count] == '\\' || name_or_data[count] == '/')
|
|
{
|
|
count++;
|
|
break;
|
|
}
|
|
}
|
|
if (count < 0)
|
|
{
|
|
count = 0;
|
|
}
|
|
|
|
FMOD_strncpy(soundi->mName, (char *)name_or_data + count, FMOD_STRING_MAXNAMELEN);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!calledfromasync)
|
|
{
|
|
soundi->mOpenState = FMOD_OPENSTATE_READY;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSoundInternal", "done. OpenState now = FMOD_OPENSTATE_READY.\n\n"));
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::checkDriverList(bool fromsystemupdate)
|
|
{
|
|
const unsigned int UPDATEFREQUENCYMS = 1000;
|
|
FMOD_RESULT result = FMOD_OK;
|
|
bool devicelistchanged = false;
|
|
unsigned int timestamp = 0;
|
|
|
|
if (fromsystemupdate && !mCallback)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
FMOD_OS_Time_GetMs(×tamp);
|
|
if (!fromsystemupdate || ((timestamp - mDeviceListLastCheckedTime) >= UPDATEFREQUENCYMS))
|
|
{
|
|
mDeviceListLastCheckedTime = timestamp;
|
|
|
|
result = FMOD_OS_CheckDriverList(&devicelistchanged);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (devicelistchanged)
|
|
{
|
|
mDeviceListChanged = true;
|
|
|
|
/*
|
|
Force the output mode to re-enumerate on next driver list related call.
|
|
*/
|
|
mOutput->mEnumerated = false;
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
mOutput->mRecordEnumerated = false;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (fromsystemupdate && mDeviceListChanged)
|
|
{
|
|
mCallback((FMOD_SYSTEM *)this, FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED, 0, 0);
|
|
mDeviceListChanged = false;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
====================================================================================
|
|
|
|
PUBLIC MEMBER FUNCTIONS
|
|
|
|
====================================================================================
|
|
*/
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setUpPlugins()
|
|
{
|
|
FMOD_RESULT result;
|
|
AutoReleaseClear<PluginFactory*> pluginFactoryCleanup;
|
|
|
|
/*
|
|
Create the plugin factory here, because it is needed pre-init for output plugins!
|
|
*/
|
|
mPluginFactory = FMOD_Object_Alloc(FMOD::PluginFactory);
|
|
if (!mPluginFactory)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
pluginFactoryCleanup = &mPluginFactory;
|
|
|
|
result = mPluginFactory->setSystem(this);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Register plugins for fmod to use
|
|
*/
|
|
mPluginFactory->setPluginPath(mPluginPath);
|
|
|
|
/*
|
|
Register platform specific outputs
|
|
*/
|
|
CHECK_RESULT(FMOD_OS_Output_Register(mPluginFactory));
|
|
|
|
/*
|
|
Register cross platform outputs
|
|
*/
|
|
#ifdef FMOD_USE_PLUGINS
|
|
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("output_wavwriter"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("output_wavwriter_nrt"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("output_nosound"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("output_nosound_nrt"));
|
|
|
|
#else
|
|
|
|
#ifdef FMOD_SUPPORT_WAVWRITER
|
|
CHECK_RESULT(mPluginFactory->registerOutput(FMOD::OutputWavWriter::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_WAVWRITER_NRT
|
|
CHECK_RESULT(mPluginFactory->registerOutput(FMOD::OutputWavWriter_NRT::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_NOSOUND
|
|
CHECK_RESULT(mPluginFactory->registerOutput(FMOD::OutputNoSound::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_NOSOUND_NRT
|
|
CHECK_RESULT(mPluginFactory->registerOutput(FMOD::OutputNoSound_NRT::getDescriptionEx()));
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef FMOD_USE_PLUGINS
|
|
/*
|
|
Register file format codec plugins
|
|
*/
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_tag", 0, true, 100));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_cdda", 0, true, 200));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_fsb", &mFSBPluginHandle, true, 300));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_vag", 0, true, 500));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_wav", &mWAVPluginHandle, true, 600));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_oggvorbis", 0, true, 800));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_tremor", 0, true, 900));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_aiff", 0, true, 1000));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_flac", 0, true, 1100));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_mod", 0, true, 1200));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_s3m", 0, true, 1300));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_xm", 0, true, 1400));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_it", 0, true, 1500));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_midi", 0, true, 1600));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_dls", 0, true, 1700));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_sf2", 0, true, 1800));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_asf", 0, true, 1900));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_mpeg", &mMPEGPluginHandle, true, 2400)); /* Second last, due to slow 4k bytescan if mpeg format header isn't found */
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_playlist", 0, true, 2450));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("codec_celt", &mCELTPluginHandle, true, 2500));
|
|
|
|
#ifdef FMOD_SUPPORT_RAW
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecRaw::getDescriptionEx()));
|
|
#endif
|
|
|
|
/*
|
|
Register DSP plugins.
|
|
*/
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_oscillator"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_fft"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_lowpass"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_lowpass2"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_lowpass_simple"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_highpass"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_echo"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_delay"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_flange"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_tremolo"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_distortion"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_normalize"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_parameq"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_pitchshift"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_chorus"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_reverb"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_sfxreverb"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_itecho"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_compressor"));
|
|
CHECK_RESULT(mPluginFactory->tryLoadPlugin("dsp_dolbyheadphones"));
|
|
|
|
#else
|
|
|
|
/*
|
|
Register file format codecs
|
|
*/
|
|
#ifdef FMOD_SUPPORT_TAGS
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecTag::getDescriptionEx(), 0, 100));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CDDA
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecCDDA::getDescriptionEx(), 0, 200));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_FSB
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecFSB::getDescriptionEx(), &mFSBPluginHandle, 300));
|
|
#ifdef FMOD_FSB_USEHEADERCACHE
|
|
CodecFSB::gCacheHead.initNode();
|
|
#endif
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_GCADPCM_DSP
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecDSP::getDescriptionEx(), 0, 400));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_VAG
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecVAG::getDescriptionEx(), 0, 500));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_WAV
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecWav::getDescriptionEx(), &mWAVPluginHandle, 600));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_AT3
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecAT3::getDescriptionEx(), 0, 700));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_OGGVORBIS
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecOggVorbis::getDescriptionEx(), 0, 800));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_TREMOR
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecTremor::getDescriptionEx(), 0, 900));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_AIFF
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecAIFF::getDescriptionEx(), 0, 1000));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_FLAC
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecFLAC::getDescriptionEx(), 0, 1100));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MOD
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecMOD::getDescriptionEx(), 0, 1200));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_S3M
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecS3M::getDescriptionEx(), 0, 1300));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XM
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecXM::getDescriptionEx(), 0, 1400));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IT
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecIT::getDescriptionEx(), 0, 1500));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MIDI
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecMIDI::getDescriptionEx(), 0, 1600));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_DLS
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecDLS::getDescriptionEx(), 0, 1700));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_SF2
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecSF2::getDescriptionEx(), 0, 1800));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_ASF
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecASF::getDescriptionEx(), 0, 1900));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecXMA::getDescriptionEx(), 0, 2000));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XWMA
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecXWMA::getDescriptionEx(), 0, 2100));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MPEGPSP
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecMPEGPSP::getDescriptionEx(), 0, 2300));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecMPEG::getDescriptionEx(), &mMPEGPluginHandle, 2400)); /* Last, due to slow 4k bytescan if mpeg format header isn't found */
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_PLAYLIST
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecPlaylist::getDescriptionEx(), 0, 2450));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAW
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecRaw::getDescriptionEx(), 0, 2500));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecCELT::getDescriptionEx(), &mCELTPluginHandle, 2600));
|
|
#endif
|
|
|
|
|
|
/*
|
|
Register DSP plugins.
|
|
*/
|
|
#ifdef FMOD_SUPPORT_OSCILLATOR
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPOscillator::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_LOWPASS
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPLowPass::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_LOWPASS2
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPLowPass2::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_LOWPASS_SIMPLE
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPLowPassSimple::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_HIGHPASS
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPHighPass::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_ECHO
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPEcho::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_DELAY
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPDelay::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_FLANGE
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPFlange::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_TREMOLO
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPTremolo::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_DISTORTION
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPDistortion::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_NORMALIZE
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPNormalize::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_PARAMEQ
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPParamEq::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_PITCHSHIFT
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPPitchShift::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CHORUS
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPChorus::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_FREEVERB
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPReverb::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_ITECHO
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPITEcho::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_COMPRESSOR
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPCompressor::getDescriptionEx()));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_SFXREVERB
|
|
CHECK_RESULT(mPluginFactory->registerDSP(DSPSfxReverb::getDescriptionEx()));
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/*
|
|
Create the built in 'user' codec. This is for when people create a sound with FMOD_CREATEUSER
|
|
*/
|
|
#ifdef FMOD_SUPPORT_USERCODEC
|
|
CHECK_RESULT(mPluginFactory->registerCodec(CodecUser::getDescriptionEx(), 0, 2600));
|
|
#endif
|
|
|
|
pluginFactoryCleanup.releasePtr();
|
|
|
|
mPluginsLoaded = true;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::sortSpeakerList()
|
|
{
|
|
int count, count2;
|
|
bool used[FMOD_SPEAKER_MAX];
|
|
int outputchannels;
|
|
|
|
if (mSpeakerMode == FMOD_SPEAKERMODE_RAW)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
Make a sorted speaker list based on the angles.
|
|
*/
|
|
for (count = 0; count < FMOD_SPEAKER_MAX; count++)
|
|
{
|
|
mSpeakerList[count] =0;
|
|
}
|
|
|
|
FMOD_memset(used, 0, sizeof(bool) * FMOD_SPEAKER_MAX);
|
|
|
|
outputchannels = mMaxOutputChannels;
|
|
if (mSpeakerMode == FMOD_SPEAKERMODE_QUAD || mSpeakerMode == FMOD_SPEAKERMODE_PROLOGIC)
|
|
{
|
|
outputchannels = 6; /* 3d pretends it has rear right/rear left for SetSpeakerMix, so 5 instead of 4. */
|
|
}
|
|
|
|
for (count = 0; count < outputchannels; count++)
|
|
{
|
|
/* initialise to some high value */
|
|
float lowest = 2 * FMOD_ANGLESORT_REVOLUTION;
|
|
|
|
for (count2 = 0; count2 < outputchannels; count2++)
|
|
{
|
|
if (mSpeaker[count2].mSpeaker == FMOD_SPEAKER_LOW_FREQUENCY)
|
|
{
|
|
continue;
|
|
}
|
|
if (!mSpeaker[count2].mActive)
|
|
{
|
|
continue;
|
|
}
|
|
if (mSpeakerMode == FMOD_SPEAKERMODE_QUAD && mSpeaker[count2].mSpeaker == FMOD_SPEAKER_FRONT_CENTER)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (mSpeaker[count2].mXZAngle < lowest && !used[count2])
|
|
{
|
|
lowest = mSpeaker[count2].mXZAngle;
|
|
mSpeakerList[count] = &mSpeaker[count2];
|
|
}
|
|
}
|
|
if (mSpeakerList[count])
|
|
{
|
|
used[mSpeakerList[count]->mSpeaker] = true;
|
|
}
|
|
}
|
|
|
|
return prepareSpeakerPairs();
|
|
}
|
|
|
|
|
|
FMOD_RESULT SystemI::prepareSpeakerPairs()
|
|
{
|
|
int speaker = 0;
|
|
FMOD_SPEAKERCONFIG *spkA, *spkB;
|
|
|
|
/*
|
|
Step 1:
|
|
- Calculate speaker angles and normals
|
|
*/
|
|
while (mSpeakerList[speaker])
|
|
{
|
|
mSpeakerList[speaker]->mXZNormal = mSpeakerList[speaker]->mPosition;
|
|
mSpeakerList[speaker]->mXZNormal.y = 0;
|
|
FMOD_Vector_Normalize(&mSpeakerList[speaker]->mXZNormal);
|
|
|
|
mSpeakerList[speaker]->mXZAngle = FMOD_AngleSort_GetValue(mSpeakerList[speaker]->mXZNormal.x, mSpeakerList[speaker]->mXZNormal.z);
|
|
|
|
++speaker;
|
|
}
|
|
|
|
/*
|
|
Step 2:
|
|
- Limit speaker separation to a maximum of 180 degrees so that sources at poles
|
|
are rendered hard left or hard right.
|
|
*/
|
|
speaker = 0;
|
|
while (mSpeakerList[speaker])
|
|
{
|
|
spkA = mSpeakerList[speaker++];
|
|
if (mSpeakerList[speaker])
|
|
{
|
|
spkB = mSpeakerList[speaker];
|
|
}
|
|
else
|
|
{
|
|
spkB = mSpeakerList[0];
|
|
}
|
|
|
|
if (spkA->mXZAngle == spkB->mXZAngle) /* 2 speakers are at the same location so skip to the next pair */
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (FMOD_AngleSort_IsReflex(spkA->mXZAngle, spkB->mXZAngle))
|
|
{
|
|
FMOD_VECTOR lateral;
|
|
FMOD_Vector_Subtract(&spkA->mXZNormal, &spkB->mXZNormal, &lateral);
|
|
FMOD_Vector_Normalize(&lateral);
|
|
spkA->mXZNormal = lateral;
|
|
FMOD_Vector_Scale(&lateral, -1.0f, &spkB->mXZNormal);
|
|
spkA->mXZAngle = FMOD_AngleSort_GetValue(spkA->mXZNormal.x, spkA->mXZNormal.z);
|
|
spkB->mXZAngle = FMOD_AngleSort_GetValue(spkB->mXZNormal.x, spkB->mXZNormal.z);
|
|
|
|
// only possible to have one pair of reflex speakers
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Step 3:
|
|
- Determine whether pairs will use VBAP or lateral panning only
|
|
- Precalculate some constants for panning
|
|
*/
|
|
speaker = 0;
|
|
while (mSpeakerList[speaker])
|
|
{
|
|
spkA = mSpeakerList[speaker++];
|
|
if (mSpeakerList[speaker])
|
|
{
|
|
spkB = mSpeakerList[speaker];
|
|
}
|
|
else
|
|
{
|
|
spkB = mSpeakerList[0];
|
|
}
|
|
|
|
if (spkA->mXZAngle == spkB->mXZAngle) /* 2 speakers are at the same location so skip to the next pair */
|
|
{
|
|
continue;
|
|
}
|
|
|
|
spkA->mPairUseVBAP = !FMOD_AngleSort_IsStraight(spkA->mXZAngle, spkB->mXZAngle);
|
|
if(spkA->mPairUseVBAP)
|
|
{
|
|
/*
|
|
we would normally use the determinant to solve the speaker gains however this is a waste
|
|
cpu as the gains are normalised for power. We need to keep its sign however so that gains
|
|
are always positive.
|
|
*/
|
|
float determinant = spkA->mXZNormal.x * spkB->mXZNormal.z - spkB->mXZNormal.x * spkA->mXZNormal.z;
|
|
spkA->mPairVBAPSign = determinant > 0.0f ? 1.0f : -1.0f;
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::allocateDSPCodec(FMOD_SOUND_FORMAT format, DSPCodec **dsp)
|
|
{
|
|
if (0)
|
|
{
|
|
}
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
else if (format == FMOD_SOUND_FORMAT_MPEG)
|
|
{
|
|
return mDSPCodecPool_MPEG.alloc(dsp);
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
else if (format == FMOD_SOUND_FORMAT_XMA)
|
|
{
|
|
return mDSPCodecPool_XMA.alloc(dsp);
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
else if (format == FMOD_SOUND_FORMAT_IMAADPCM)
|
|
{
|
|
return mDSPCodecPool_ADPCM.alloc(dsp);
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
else if (format == FMOD_SOUND_FORMAT_CELT)
|
|
{
|
|
return mDSPCodecPool_CELT.alloc(dsp);
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
else if (format == FMOD_SOUND_FORMAT_PCM16)
|
|
{
|
|
return mDSPCodecPool_RAW.alloc(dsp);
|
|
}
|
|
#endif
|
|
|
|
return FMOD_ERR_FORMAT;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
SystemI::SystemI()
|
|
{
|
|
mInitialized = false;
|
|
mPluginsLoaded = false;
|
|
mOutputType = FMOD_OUTPUTTYPE_AUTODETECT;
|
|
mOutput = 0;
|
|
mEmulated = 0;
|
|
mMainThreadID = 0;
|
|
mChannel = 0;
|
|
mPluginFactory = 0;
|
|
mLastTimeStamp = 0;
|
|
mStreamFileBufferSize = 16*1024;
|
|
mStreamFileBufferSizeType = FMOD_TIMEUNIT_RAWBYTES;
|
|
mDeviceListLastCheckedTime = 0;
|
|
mDeviceListChanged = false;
|
|
mUserData = 0;
|
|
|
|
mNumSoftwareChannels = 32;
|
|
mMinHardwareChannels2D = 0;
|
|
mMaxHardwareChannels2D = 1000;
|
|
mMinHardwareChannels3D = 0;
|
|
mMaxHardwareChannels3D = 1000;
|
|
mCreatedHardwareSample = false;
|
|
mBufferSize = FILE_SECTORSIZE;
|
|
mUsesUserCallbacks = false;
|
|
|
|
#if defined(PLATFORM_XENON) || defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
mDSPBlockSize = 256;
|
|
mDSPBufferSize = mDSPBlockSize * 4;
|
|
#else
|
|
mDSPBlockSize = 1024;
|
|
mDSPBufferSize = mDSPBlockSize * 4;
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mSoftware = 0;
|
|
|
|
mDSPTempBuff = 0;
|
|
mDSPTempBuffMem = 0;
|
|
|
|
for (int count = 0; count < FMOD_DSP_MAXTREEDEPTH; count++)
|
|
{
|
|
mDSPMixBuff[count] = 0;
|
|
}
|
|
|
|
mDSPClock.mHi = 0;
|
|
mDSPClock.mLo = 0;
|
|
#endif
|
|
|
|
mMaxInputChannels = DSP_DEFAULTLEVELS_IN;
|
|
mMaxOutputChannels = 0;
|
|
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_LOW_FREQUENCY, 0.0f, 0.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f);
|
|
|
|
#ifdef PLATFORM_PS3
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -0.4f, -1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 0.4f, -1.0f);
|
|
#else
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -1.0f, 0.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 1.0f, 0.0f);
|
|
#endif
|
|
|
|
#if defined(PLATFORM_XENON)
|
|
mOutputFormat = FMOD_SOUND_FORMAT_PCMFLOAT;
|
|
|
|
setSpeakerMode(FMOD_SPEAKERMODE_5POINT1);
|
|
#elif defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
mOutputFormat = FMOD_SOUND_FORMAT_PCMFLOAT;
|
|
|
|
setSpeakerMode(FMOD_SPEAKERMODE_7POINT1);
|
|
#else
|
|
mOutputFormat = FMOD_SOUND_FORMAT_PCM16;
|
|
|
|
setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
|
|
#endif
|
|
#if defined(PLATFORM_IPHONE)
|
|
mOutputRate = 24000;
|
|
#else
|
|
mOutputRate = 48000;
|
|
#endif
|
|
mOutputHandle = 0;
|
|
mSelectedDriver = 0;
|
|
mResampleMethod = FMOD_DSP_RESAMPLER_LINEAR;
|
|
|
|
mNumListeners = 1;
|
|
mDistanceScale = 1;
|
|
mRolloffScale = 1;
|
|
mDopplerScale = 1;
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
mReverb3DActive = false;
|
|
FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF;
|
|
setReverbAmbientProperties(&prop);
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
mGeometryList = 0;
|
|
mGeometryMgr.mSystem = this;
|
|
#endif
|
|
|
|
FMOD_memset(mPluginPath, 0, FMOD_STRING_MAXPATHLEN);
|
|
|
|
mAdvancedSettings.maxXMAcodecs = 0;
|
|
mAdvancedSettings.maxADPCMcodecs = 0;
|
|
mAdvancedSettings.maxMPEGcodecs = 0;
|
|
mAdvancedSettings.maxCELTcodecs = 0;
|
|
mAdvancedSettings.maxPCMcodecs = 0;
|
|
mAdvancedSettings.max3DReverbDSPs = FMOD_ADVANCEDSETTINGS_MAX3DREVERBDSPS;
|
|
mAdvancedSettings.HRTFMinAngle = 180;
|
|
mAdvancedSettings.HRTFMaxAngle = 360;
|
|
mAdvancedSettings.HRTFFreq = 4000;
|
|
mAdvancedSettings.vol0virtualvol = 0.0f;
|
|
mAdvancedSettings.eventqueuesize = 32;
|
|
mAdvancedSettings.defaultDecodeBufferSize = FMOD_STREAMDECODEBUFFERSIZE_DEFAULT;
|
|
mAdvancedSettings.geometryMaxFadeTime = 0;
|
|
|
|
mMPEGPluginHandle = 0xFFFFFFFF;
|
|
mFSBPluginHandle = 0xFFFFFFFF;
|
|
mWAVPluginHandle = 0xFFFFFFFF;
|
|
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
mDSPCodecPool_MPEG.mSystem = this;
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
mDSPCodecPool_ADPCM.mSystem = this;
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
mDSPCodecPool_XMA.mSystem = this;
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
mDSPCodecPool_CELT.mSystem = this;
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
mDSPCodecPool_RAW.mSystem = this;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_ASIO
|
|
mASIOSpeakerList[0] = FMOD_SPEAKER_FRONT_LEFT;
|
|
mASIOSpeakerList[1] = FMOD_SPEAKER_FRONT_RIGHT;
|
|
#if (DSP_MAXLEVELS_OUT > 2)
|
|
mASIOSpeakerList[2] = FMOD_SPEAKER_FRONT_CENTER;
|
|
mASIOSpeakerList[3] = FMOD_SPEAKER_LOW_FREQUENCY;
|
|
mASIOSpeakerList[4] = FMOD_SPEAKER_BACK_LEFT;
|
|
mASIOSpeakerList[5] = FMOD_SPEAKER_BACK_RIGHT;
|
|
#endif
|
|
#if (DSP_MAXLEVELS_OUT > 6)
|
|
mASIOSpeakerList[6] = FMOD_SPEAKER_SIDE_LEFT;
|
|
mASIOSpeakerList[7] = FMOD_SPEAKER_SIDE_RIGHT;
|
|
#endif
|
|
|
|
#if (DSP_MAXLEVELS_OUT > 8)
|
|
mASIOSpeakerList[8] = FMOD_SPEAKER_FRONT_LEFT;
|
|
mASIOSpeakerList[9] = FMOD_SPEAKER_FRONT_RIGHT;
|
|
mASIOSpeakerList[10] = FMOD_SPEAKER_FRONT_LEFT;
|
|
mASIOSpeakerList[11] = FMOD_SPEAKER_FRONT_RIGHT;
|
|
mASIOSpeakerList[12] = FMOD_SPEAKER_FRONT_LEFT;
|
|
mASIOSpeakerList[13] = FMOD_SPEAKER_FRONT_RIGHT;
|
|
mASIOSpeakerList[14] = FMOD_SPEAKER_FRONT_LEFT;
|
|
mASIOSpeakerList[15] = FMOD_SPEAKER_FRONT_RIGHT;
|
|
#endif
|
|
#endif
|
|
|
|
mDownmix = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::release()
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
if (mInitialized)
|
|
{
|
|
result = close();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (mOutput)
|
|
{
|
|
mOutput->release();
|
|
mOutput = 0;
|
|
}
|
|
|
|
#if defined(FMOD_SUPPORT_GEOMETRY) && defined(FMOD_SUPPORT_GEOMETRY_THREADED)
|
|
mGeometryMgr.releaseOcclusionThread();
|
|
#endif
|
|
|
|
removeNode();
|
|
|
|
FMOD_Memory_Free(this);
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setOutput(FMOD_OUTPUTTYPE outputtype)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
int count, numoutputs;
|
|
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
if (mOutput)
|
|
{
|
|
if (outputtype == mOutputType)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
mOutput->release();
|
|
mOutput = 0;
|
|
}
|
|
|
|
if (!mPluginsLoaded)
|
|
{
|
|
result = setUpPlugins();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Scan plugin list and find an equal type.
|
|
*/
|
|
result = mPluginFactory->getNumOutputs(&numoutputs);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (outputtype == FMOD_OUTPUTTYPE_AUTODETECT)
|
|
{
|
|
FMOD_OS_Output_GetDefault(&outputtype);
|
|
}
|
|
|
|
for (count = 0; count < numoutputs; count++)
|
|
{
|
|
FMOD_OUTPUT_DESCRIPTION_EX *outputdesc = 0;
|
|
unsigned int handle;
|
|
|
|
result = mPluginFactory->getOutputHandle(count, &handle);
|
|
if (result != FMOD_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
result = mPluginFactory->getOutput(handle, &outputdesc);
|
|
if (result != FMOD_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (outputdesc->mType == outputtype)
|
|
{
|
|
result = mPluginFactory->createOutput(outputdesc, &mOutput);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
mOutputType = mOutput->mDescription.mType;
|
|
mOutputHandle = mOutput->mDescription.mHandle;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
}
|
|
|
|
return FMOD_ERR_PLUGIN_MISSING;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getOutput(FMOD_OUTPUTTYPE *output)
|
|
{
|
|
if (!output)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*output = mOutputType;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setSoftwareFormat(int samplerate, FMOD_SOUND_FORMAT format, int numoutputchannels, int maxinputchannels, FMOD_DSP_RESAMPLER resamplermethod)
|
|
{
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
if (samplerate < 8000 || samplerate > 192000)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (maxinputchannels > DSP_MAXLEVELS_IN || numoutputchannels > DSP_MAXLEVELS_OUT)
|
|
{
|
|
return FMOD_ERR_TOOMANYCHANNELS;
|
|
}
|
|
|
|
mOutputRate = samplerate;
|
|
mOutputFormat = format;
|
|
mResampleMethod = resamplermethod;
|
|
|
|
#ifndef PLATFORM_PS3
|
|
if (numoutputchannels)
|
|
{
|
|
mMaxOutputChannels = numoutputchannels;
|
|
|
|
mSpeakerMode = FMOD_SPEAKERMODE_RAW;
|
|
}
|
|
#endif
|
|
if (maxinputchannels > 0)
|
|
{
|
|
mMaxInputChannels = maxinputchannels;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getNumDrivers(int *numdrivers)
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
if (!numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mInitialized)
|
|
{
|
|
result = setOutput(mOutputType);
|
|
if (result != FMOD_OK)
|
|
{
|
|
*numdrivers = 0;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
result = checkDriverList(false);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (mOutput->mDescription.getnumdrivers)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
return mOutput->mDescription.getnumdrivers(mOutput, numdrivers);
|
|
}
|
|
|
|
*numdrivers = 0;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid)
|
|
{
|
|
FMOD_RESULT result;
|
|
int numdrivers;
|
|
|
|
result = getNumDrivers(&numdrivers);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mInitialized)
|
|
{
|
|
result = setOutput(mOutputType);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (mOutput->mDescription.getdriverinfo)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
return mOutput->mDescription.getdriverinfo(mOutput, id, name, namelen, guid);
|
|
}
|
|
|
|
if (mOutput->mDescription.getdrivername)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
return mOutput->mDescription.getdrivername(mOutput, id, name, namelen);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid)
|
|
{
|
|
FMOD_RESULT result;
|
|
int numdrivers;
|
|
|
|
result = getNumDrivers(&numdrivers);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mInitialized)
|
|
{
|
|
result = setOutput(mOutputType);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (mOutput->mDescription.getdriverinfow)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
return mOutput->mDescription.getdriverinfow(mOutput, id, name, namelen, guid);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getDriverCaps(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode)
|
|
{
|
|
FMOD_RESULT result;
|
|
FMOD_CAPS lcaps;
|
|
FMOD_SPEAKERMODE lcontrolpanelspeakermode;
|
|
int lminfrequency;
|
|
int lmaxfrequency;
|
|
int lnum2dchannels;
|
|
int lnum3dchannels;
|
|
int ltotalchannels;
|
|
int numdrivers;
|
|
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
result = getNumDrivers(&numdrivers);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mInitialized)
|
|
{
|
|
result = setOutput(mOutputType);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
lcaps = FMOD_CAPS_NONE;
|
|
lminfrequency = 0;
|
|
lmaxfrequency = 0;
|
|
lnum2dchannels = 0;
|
|
lnum3dchannels = 0;
|
|
ltotalchannels = 0;
|
|
lcontrolpanelspeakermode = FMOD_SPEAKERMODE_STEREO;
|
|
|
|
if (mOutput->mDescription.getdrivercapsex2)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
result = mOutput->mDescription.getdrivercapsex2(mOutput, id, &lcaps, &lminfrequency, &lmaxfrequency, &lcontrolpanelspeakermode, &lnum2dchannels, &lnum3dchannels, <otalchannels);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
if (mOutput->mDescription.getdrivercapsex)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
result = mOutput->mDescription.getdrivercapsex(mOutput, id, &lcaps, &lminfrequency, &lmaxfrequency, &lcontrolpanelspeakermode);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
else if (mOutput->mDescription.getdrivercaps)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
result = mOutput->mDescription.getdrivercaps(mOutput, id, &lcaps);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
mOutput->mNum2DChannelsFromCaps = lnum2dchannels;
|
|
mOutput->mNum3DChannelsFromCaps = lnum3dchannels;
|
|
mOutput->mTotalChannelsFromCaps = ltotalchannels;
|
|
|
|
if (caps)
|
|
{
|
|
*caps = lcaps;
|
|
}
|
|
if (minfrequency)
|
|
{
|
|
*minfrequency = lminfrequency;
|
|
}
|
|
if (maxfrequency)
|
|
{
|
|
*maxfrequency = lmaxfrequency;
|
|
}
|
|
if (controlpanelspeakermode)
|
|
{
|
|
*controlpanelspeakermode = lcontrolpanelspeakermode;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setDriver(int driver)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
int numdrivers = 0;
|
|
|
|
result = getNumDrivers(&numdrivers);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (driver < -1 || driver >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (driver == -1)
|
|
{
|
|
driver = 0;
|
|
}
|
|
|
|
/*
|
|
If the driver is already initialised, we need to reset it
|
|
*/
|
|
if (mInitialized)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
int samplerate = 0;
|
|
FMOD_SOUND_FORMAT soundformat = FMOD_SOUND_FORMAT_NONE;
|
|
FMOD_SPEAKERMODE speakermode = FMOD_SPEAKERMODE_STEREO;
|
|
|
|
if (mCreatedHardwareSample)
|
|
#endif
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::setDriver", "Cannot change driver when hardware samples have been created.\n"));
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
if (mOutput->mRecordNumActive)
|
|
{
|
|
result = mOutput->recordStopAll(false);
|
|
CHECK_RESULT(result);
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
/*
|
|
Stop the current driver
|
|
*/
|
|
if (mOutput->mDescription.stop)
|
|
{
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
|
|
mOutput->mDescription.stop(mOutput);
|
|
}
|
|
else if (mOutput->mDescription.polling)
|
|
{
|
|
OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput);
|
|
|
|
outputpolled->stop();
|
|
}
|
|
|
|
/*
|
|
Close the current driver
|
|
*/
|
|
if (mOutput->mDescription.close)
|
|
{
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
|
|
mOutput->mDescription.close(mOutput);
|
|
}
|
|
|
|
/*
|
|
Initialise the new driver
|
|
*/
|
|
speakermode = mSpeakerMode;
|
|
samplerate = mOutputRate;
|
|
soundformat = mOutputFormat;
|
|
|
|
if (mOutput->mDescription.initex)
|
|
{
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
|
|
result = mOutput->mDescription.initex(mOutput, driver, mFlags, &samplerate, mMaxOutputChannels, &soundformat, &speakermode, mDSPBlockSize, mDSPBufferSize / mDSPBlockSize, 0, 0, NULL);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
else if (mOutput->mDescription.init)
|
|
{
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
|
|
result = mOutput->mDescription.init(mOutput, driver, mFlags, &samplerate, mMaxOutputChannels, &soundformat, mDSPBlockSize, mDSPBufferSize / mDSPBlockSize, NULL);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Check if output mode changed the sample rate, format or speaker mode
|
|
*/
|
|
if (speakermode != mSpeakerMode || samplerate != mOutputRate || soundformat != mOutputFormat)
|
|
{
|
|
/*
|
|
Stop the new driver
|
|
*/
|
|
if (mOutput->mDescription.stop)
|
|
{
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
|
|
mOutput->mDescription.stop(mOutput);
|
|
}
|
|
else if (mOutput->mDescription.polling)
|
|
{
|
|
OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput);
|
|
|
|
outputpolled->stop();
|
|
}
|
|
|
|
/*
|
|
Close the new driver
|
|
*/
|
|
if (mOutput->mDescription.close)
|
|
{
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
|
|
mOutput->mDescription.close(mOutput);
|
|
}
|
|
|
|
// The new driver is not compatible with the current FMOD state, user must now choose a driver that is compatible (possibly the original one if it is still connected)
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::setDriver", "Selected driver does not support current output format, sample rate or number of channels.\n"));
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
/*
|
|
Start the new driver
|
|
*/
|
|
if (mOutput->mDescription.start)
|
|
{
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
|
|
result = mOutput->mDescription.start(mOutput);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
else if (mOutput->mDescription.polling)
|
|
{
|
|
OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput);
|
|
|
|
result = outputpolled->start();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
mSelectedDriver = driver;
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getDriver(int *driver)
|
|
{
|
|
if (!driver)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*driver = mSelectedDriver;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setHardwareChannels(int min2d, int max2d, int min3d, int max3d)
|
|
{
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
if (min2d < 0 || max2d < 0 || min3d < 0 || max3d < 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
mMinHardwareChannels2D = min2d;
|
|
mMaxHardwareChannels2D = max2d;
|
|
mMinHardwareChannels3D = min3d;
|
|
mMaxHardwareChannels3D = max3d;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getHardwareChannels(int *num2d, int *num3d, int *total)
|
|
{
|
|
int numhw3d = 0, numhw2d = 0, numhwtotal = 0;
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
|
|
if (mInitialized)
|
|
{
|
|
if (mOutput)
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
if (mOutput->mChannelPool)
|
|
{
|
|
result = mOutput->mChannelPool->getNumChannels(&numhw2d);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (mOutput->mChannelPool3D)
|
|
{
|
|
result = mOutput->mChannelPool3D->getNumChannels(&numhw3d);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
numhwtotal = numhw3d + numhw2d;
|
|
}
|
|
else
|
|
{
|
|
FMOD_RESULT result;
|
|
result = setOutput(mOutputType);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
numhw2d = mOutput->mNum2DChannelsFromCaps;
|
|
numhw3d = mOutput->mNum3DChannelsFromCaps;
|
|
numhwtotal = mOutput->mTotalChannelsFromCaps;
|
|
}
|
|
#endif
|
|
|
|
if (num3d)
|
|
{
|
|
*num3d = numhw3d;
|
|
}
|
|
|
|
if (num2d)
|
|
{
|
|
*num2d = numhw2d;
|
|
}
|
|
|
|
if (total)
|
|
{
|
|
*total = numhwtotal;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setSoftwareChannels(int numsoftwarechannels)
|
|
{
|
|
if (numsoftwarechannels < 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
mNumSoftwareChannels = numsoftwarechannels;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getSoftwareChannels(int *numsoftwarechannels)
|
|
{
|
|
if (!numsoftwarechannels)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*numsoftwarechannels = mNumSoftwareChannels;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setDSPBufferSize(unsigned int bufferlength, int numbuffers)
|
|
{
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
|
|
#else
|
|
|
|
if (bufferlength < 1 || numbuffers < 2)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
mDSPBlockSize = bufferlength;
|
|
mDSPBufferSize = bufferlength * numbuffers;
|
|
|
|
#endif
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getDSPBufferSize(unsigned int *bufferlength, int *numbuffers)
|
|
{
|
|
if (bufferlength)
|
|
{
|
|
*bufferlength = mDSPBlockSize;
|
|
}
|
|
if (numbuffers)
|
|
{
|
|
*numbuffers = mDSPBufferSize / mDSPBlockSize;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setFileSystem(FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, int blockalign)
|
|
{
|
|
setGlobalUserCallbacks(useropen, userclose, userread, userseek);
|
|
|
|
if (blockalign >= 0)
|
|
{
|
|
mBufferSize = blockalign;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::attachFileSystem(FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek)
|
|
{
|
|
mOpenRiderCallback = useropen;
|
|
mCloseRiderCallback = userclose;
|
|
mReadRiderCallback = userread;
|
|
mSeekRiderCallback = userseek;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Sets advanced features like configuring memory and cpu usage for FMOD_CREATEREALTIMESAMPLE usage.
|
|
|
|
[PARAMETERS]
|
|
'settings' Pointer to FMOD_ADVANCEDSETTINGS structure.
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
FMOD_ADVANCEDSETTINGS
|
|
SystemI::getAdvancedSettings
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setAdvancedSettings(FMOD_ADVANCEDSETTINGS *settings)
|
|
{
|
|
int memberoffset;
|
|
|
|
if (!settings)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
memberoffset = (int)((FMOD_UINT_NATIVE)&settings->maxXMAcodecs - (FMOD_UINT_NATIVE)settings);
|
|
|
|
if (settings->cbsize <= memberoffset)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (settings->maxADPCMcodecs < 0 || settings->maxADPCMcodecs > 65535)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->maxMPEGcodecs < 0 || settings->maxMPEGcodecs > 65535)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->maxXMAcodecs < 0 || settings->maxXMAcodecs > 65535)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->maxCELTcodecs < 0 || settings->maxCELTcodecs > 65535)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->maxPCMcodecs < 0 || settings->maxPCMcodecs > 65535)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
result = FMOD_CHECKFLOAT(settings->HRTFMinAngle);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(settings->HRTFMaxAngle);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(settings->HRTFFreq);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(settings->vol0virtualvol);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (settings->HRTFMinAngle < 0 || settings->HRTFMinAngle > 360)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->HRTFMaxAngle < settings->HRTFMinAngle || settings->HRTFMaxAngle > 360.0f)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->HRTFFreq < 0.0001f)
|
|
{
|
|
settings->HRTFFreq = mAdvancedSettings.HRTFFreq;
|
|
}
|
|
else if (settings->HRTFFreq < 10.0f || settings->HRTFFreq > 22050.0f)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->vol0virtualvol < 0.0f)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->eventqueuesize < 0 || settings->eventqueuesize > 65535)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->ASIONumChannels > DSP_MAXLEVELS_OUT || settings->ASIONumChannels < 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->debugLogFilename && FMOD_strlen(settings->debugLogFilename) >= 255)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
#ifdef FMOD_DEBUG
|
|
if (settings->debugLogFilename)
|
|
{
|
|
FMOD_strcpy(FMOD::gGlobal->gDebugFilename, settings->debugLogFilename);
|
|
}
|
|
#endif
|
|
|
|
if ((settings->defaultDecodeBufferSize == 0) || (settings->defaultDecodeBufferSize > 30000))
|
|
{
|
|
settings->defaultDecodeBufferSize = mAdvancedSettings.defaultDecodeBufferSize;
|
|
}
|
|
|
|
FMOD_memcpy(&mAdvancedSettings, settings, settings->cbsize);
|
|
|
|
#ifdef FMOD_DEBUG
|
|
if (settings->debugLogFilename) /* Dont store a pointer to the user name, set it back to mAdvancedSettings debuglog filename here. */
|
|
{
|
|
mAdvancedSettings.debugLogFilename = FMOD::gGlobal->gDebugFilename;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_ASIO
|
|
if (settings->ASIOSpeakerList)
|
|
{
|
|
int count;
|
|
|
|
for (count = 0; count < settings->ASIONumChannels; count++)
|
|
{
|
|
mASIOSpeakerList[count] = settings->ASIOSpeakerList[count];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
FMOD_ADVANCEDSETTINGS
|
|
SystemI::setAdvancedSettings
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getAdvancedSettings(FMOD_ADVANCEDSETTINGS *settings)
|
|
{
|
|
int usercbsize;
|
|
int asioNumChannels;
|
|
FMOD_SPEAKER *speakerlist;
|
|
char *debuglogfilename;
|
|
|
|
if (!settings)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (settings->ASIONumChannels > DSP_MAXLEVELS_OUT || settings->ASIONumChannels < 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
usercbsize = settings->cbsize;
|
|
speakerlist = settings->ASIOSpeakerList;
|
|
asioNumChannels = settings->ASIONumChannels;
|
|
debuglogfilename = settings->debugLogFilename;
|
|
|
|
FMOD_memcpy(settings, &mAdvancedSettings, usercbsize);
|
|
|
|
settings->cbsize = usercbsize;
|
|
settings->ASIOSpeakerList = speakerlist;
|
|
settings->debugLogFilename = debuglogfilename;
|
|
|
|
#ifdef FMOD_DEBUG
|
|
if (settings->debugLogFilename)
|
|
{
|
|
FMOD_strcpy(settings->debugLogFilename, FMOD::gGlobal->gDebugFilename);
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_ASIO
|
|
if (settings->ASIONumChannels > DSP_MAXLEVELS_OUT)
|
|
{
|
|
settings->ASIONumChannels = DSP_MAXLEVELS_OUT;
|
|
}
|
|
if (settings->ASIOSpeakerList)
|
|
{
|
|
int count;
|
|
|
|
for (count = 0; count < asioNumChannels; count++)
|
|
{
|
|
settings->ASIOSpeakerList[count] = mASIOSpeakerList[count];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setSpeakerMode(FMOD_SPEAKERMODE speakermode)
|
|
{
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
mSpeakerMode = speakermode;
|
|
|
|
switch (mSpeakerMode)
|
|
{
|
|
case FMOD_SPEAKERMODE_RAW:
|
|
{
|
|
/* Dont change mMaxOutputChannels */
|
|
return FMOD_OK;
|
|
}
|
|
case FMOD_SPEAKERMODE_MONO:
|
|
{
|
|
mMaxOutputChannels = 1;
|
|
break;
|
|
}
|
|
case FMOD_SPEAKERMODE_STEREO:
|
|
{
|
|
mMaxOutputChannels = 2;
|
|
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 0.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 0.0f);
|
|
break;
|
|
}
|
|
case FMOD_SPEAKERMODE_QUAD:
|
|
{
|
|
mMaxOutputChannels = 4;
|
|
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f);
|
|
break;
|
|
}
|
|
case FMOD_SPEAKERMODE_SURROUND:
|
|
{
|
|
mMaxOutputChannels = 5;
|
|
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f);
|
|
break;
|
|
}
|
|
case FMOD_SPEAKERMODE_5POINT1:
|
|
{
|
|
mMaxOutputChannels = 6;
|
|
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f);
|
|
break;
|
|
|
|
}
|
|
case FMOD_SPEAKERMODE_7POINT1:
|
|
{
|
|
mMaxOutputChannels = 8;
|
|
|
|
#if defined(FMOD_SUPPORT_MYEARS) && !defined(PLATFORM_PS3)
|
|
/* MyEars uses standard 7.1 speaker positioning whereas FMOD dos not */
|
|
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -0.5f, 0.866f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 0.5f, 0.866f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -0.707f, -0.707f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 0.707f, -0.707f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -1.0f, 0.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 1.0f, 0.0f);
|
|
|
|
#else
|
|
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f);
|
|
|
|
#ifdef PLATFORM_PS3
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -0.4f, -1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 0.4f, -1.0f);
|
|
#else
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_LEFT, -1.0f, 0.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_SIDE_RIGHT, 1.0f, 0.0f);
|
|
#endif
|
|
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
case FMOD_SPEAKERMODE_PROLOGIC:
|
|
{
|
|
mMaxOutputChannels = 2;
|
|
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_LEFT, -1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_RIGHT, 1.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_FRONT_CENTER, 0.0f, 1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_LEFT, -1.0f, -1.0f);
|
|
set3DSpeakerPosition(FMOD_SPEAKER_BACK_RIGHT, 1.0f, -1.0f);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return sortSpeakerList();
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setCallback(FMOD_SYSTEM_CALLBACK callback)
|
|
{
|
|
FMOD::gGlobal->gSystemCallback = callback;
|
|
mCallback = callback;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setPluginPath(const char *path)
|
|
{
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
if (FMOD_strlen(path) >= FMOD_STRING_MAXPATHLEN)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
FMOD_strncpy(mPluginPath, path, FMOD_STRING_MAXPATHLEN);
|
|
|
|
if (mPluginFactory)
|
|
{
|
|
mPluginFactory->setPluginPath(mPluginPath);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::loadPlugin(const char *filename, unsigned int *handle, unsigned int priority)
|
|
{
|
|
#ifdef FMOD_SUPPORT_DLLS
|
|
FMOD_RESULT result;
|
|
|
|
if (!mPluginsLoaded)
|
|
{
|
|
result = setUpPlugins();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return mPluginFactory->loadPlugin(filename, handle, false, priority);
|
|
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getNumPlugins(FMOD_PLUGINTYPE type, int *numplugins)
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
if (!numplugins)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mPluginsLoaded)
|
|
{
|
|
result = setUpPlugins();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case FMOD_PLUGINTYPE_OUTPUT:
|
|
{
|
|
mPluginFactory->getNumOutputs(numplugins);
|
|
break;
|
|
}
|
|
case FMOD_PLUGINTYPE_CODEC:
|
|
{
|
|
mPluginFactory->getNumCodecs(numplugins);
|
|
break;
|
|
}
|
|
case FMOD_PLUGINTYPE_DSP:
|
|
{
|
|
mPluginFactory->getNumDSPs(numplugins);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getPluginHandle(FMOD_PLUGINTYPE plugintype, int index, unsigned int *handle)
|
|
{
|
|
switch (plugintype)
|
|
{
|
|
case FMOD_PLUGINTYPE_CODEC:
|
|
{
|
|
return mPluginFactory->getCodecHandle(index, handle);
|
|
}
|
|
case FMOD_PLUGINTYPE_DSP:
|
|
{
|
|
return mPluginFactory->getDSPHandle(index, handle);
|
|
}
|
|
case FMOD_PLUGINTYPE_OUTPUT:
|
|
{
|
|
return mPluginFactory->getOutputHandle(index, handle);
|
|
}
|
|
default:
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getPluginInfo(unsigned int handle, FMOD_PLUGINTYPE *plugintype, char *name, int namelen, unsigned int *version)
|
|
{
|
|
FMOD_RESULT result;
|
|
FMOD_OUTPUT_DESCRIPTION_EX *outputdesc;
|
|
FMOD_CODEC_DESCRIPTION_EX *codecdesc;
|
|
FMOD_DSP_DESCRIPTION_EX *dspdesc;
|
|
|
|
if (!mPluginsLoaded)
|
|
{
|
|
result = setUpPlugins();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
result = mPluginFactory->getOutput(handle, &outputdesc);
|
|
if (result == FMOD_OK)
|
|
{
|
|
if (name)
|
|
{
|
|
FMOD_strncpy(name, outputdesc->name, namelen);
|
|
}
|
|
if (version)
|
|
{
|
|
*version = outputdesc->version;
|
|
}
|
|
if (plugintype)
|
|
{
|
|
*plugintype = FMOD_PLUGINTYPE_OUTPUT;
|
|
}
|
|
}
|
|
else if (result == FMOD_ERR_PLUGIN_MISSING)
|
|
{
|
|
result = mPluginFactory->getCodec(handle, &codecdesc);
|
|
if (result == FMOD_OK)
|
|
{
|
|
if (name)
|
|
{
|
|
FMOD_strncpy(name, codecdesc->name, namelen);
|
|
}
|
|
if (version)
|
|
{
|
|
*version = codecdesc->version;
|
|
}
|
|
if (plugintype)
|
|
{
|
|
*plugintype = FMOD_PLUGINTYPE_CODEC;
|
|
}
|
|
}
|
|
else if (result == FMOD_ERR_PLUGIN_MISSING)
|
|
{
|
|
result = mPluginFactory->getDSP(handle, &dspdesc);
|
|
if (result == FMOD_OK)
|
|
{
|
|
if (name)
|
|
{
|
|
FMOD_strncpy(name, dspdesc->name, namelen);
|
|
}
|
|
if (version)
|
|
{
|
|
*version = dspdesc->version;
|
|
}
|
|
if (plugintype)
|
|
{
|
|
*plugintype = FMOD_PLUGINTYPE_DSP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::unloadPlugin(unsigned int handle)
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
if (!mPluginsLoaded)
|
|
{
|
|
result = setUpPlugins();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return mPluginFactory->unloadPlugin(handle);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setOutputByPlugin(unsigned int handle)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
FMOD_OUTPUT_DESCRIPTION_EX *outputdesc = 0;
|
|
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
if (mOutput)
|
|
{
|
|
mOutput->release();
|
|
mOutput = 0;
|
|
}
|
|
|
|
if (!mPluginsLoaded)
|
|
{
|
|
result = setUpPlugins();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
result = mPluginFactory->getOutput(handle, &outputdesc);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = mPluginFactory->createOutput(outputdesc, &mOutput);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
mOutputType = mOutput->mDescription.mType;
|
|
mOutputHandle = mOutput->mDescription.mHandle;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getOutputByPlugin(unsigned int *handle)
|
|
{
|
|
if (!handle)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*handle = mOutputHandle;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
|
|
class SystemInitCleanup
|
|
{
|
|
public:
|
|
SystemInitCleanup()
|
|
: mSystem(0), mSpeakerMode(FMOD_SPEAKERMODE_MAX),
|
|
mOutputFormat(FMOD_SOUND_FORMAT_MAX), mOutputRate(0),
|
|
mSoundListCrit(0), mFileCrit(0), mAsyncCrit(0),
|
|
mProfilerInitialized(false)
|
|
{ }
|
|
|
|
void setSystem(SystemI *system)
|
|
{
|
|
mSystem = system;
|
|
mSpeakerMode = system->mSpeakerMode;
|
|
mOutputFormat = system->mOutputFormat;
|
|
mOutputRate = system->mOutputRate;
|
|
}
|
|
|
|
void profilerInitialized() { mProfilerInitialized = true; }
|
|
void setSoundListCrit(FMOD_OS_CRITICALSECTION **crit) { mSoundListCrit = crit; }
|
|
void setFileCrit(FMOD_OS_CRITICALSECTION **crit) { mFileCrit = crit; }
|
|
void setAsyncCrit(FMOD_OS_CRITICALSECTION **crit) { mAsyncCrit = crit; }
|
|
|
|
void cleanup(bool)
|
|
{
|
|
if(mSystem)
|
|
{
|
|
if(mSpeakerMode != FMOD_SPEAKERMODE_MAX)
|
|
{
|
|
mSystem->setSpeakerMode(mSpeakerMode);
|
|
}
|
|
if (mOutputFormat != FMOD_SOUND_FORMAT_MAX)
|
|
{
|
|
mSystem->mOutputFormat = mOutputFormat;
|
|
}
|
|
if (mOutputRate != 0)
|
|
{
|
|
mSystem->mOutputRate = mOutputRate;
|
|
}
|
|
mSystem->close();
|
|
|
|
#ifdef FMOD_SUPPORT_PROFILE
|
|
if (FMOD::gGlobal->gSystemInitCount == 0 && mProfilerInitialized)
|
|
{
|
|
FMOD_Profile_Release();
|
|
}
|
|
#endif
|
|
}
|
|
if(mAsyncCrit)
|
|
{
|
|
FMOD_OS_CriticalSection_Free(*mAsyncCrit);
|
|
*mAsyncCrit = 0;
|
|
}
|
|
if(mFileCrit)
|
|
{
|
|
FMOD_OS_CriticalSection_Free(*mFileCrit);
|
|
*mFileCrit = 0;
|
|
}
|
|
if(mSoundListCrit)
|
|
{
|
|
FMOD_OS_CriticalSection_Free(*mSoundListCrit);
|
|
*mSoundListCrit = 0;
|
|
}
|
|
}
|
|
|
|
private:
|
|
SystemI *mSystem;
|
|
FMOD_SPEAKERMODE mSpeakerMode;
|
|
FMOD_SOUND_FORMAT mOutputFormat;
|
|
int mOutputRate;
|
|
FMOD_OS_CRITICALSECTION **mSoundListCrit;
|
|
FMOD_OS_CRITICALSECTION **mFileCrit;
|
|
FMOD_OS_CRITICALSECTION **mAsyncCrit;
|
|
bool mProfilerInitialized;
|
|
};
|
|
|
|
|
|
FMOD_RESULT SystemI::init(int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata)
|
|
{
|
|
int count;
|
|
FMOD_RESULT result;
|
|
int channellimit = (int)(1 << CHANINDEX_BITS);
|
|
FMOD_SPEAKERMODE speakerMode;
|
|
AutoCleanup<bool, SystemInitCleanup> initCleanup(true);
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "FMOD Ex Version: %08x\n", FMOD_VERSION));
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "maxchannels = %d, flags = %08x, extradriverdata = %p\n", maxchannels, flags, extradriverdata));
|
|
|
|
if (maxchannels < 0 || maxchannels >= channellimit)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (mInitialized)
|
|
{
|
|
return FMOD_ERR_INITIALIZED;
|
|
}
|
|
|
|
result = closeEx(true);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
initCleanup.setSystem(this);
|
|
|
|
mFlags = flags;
|
|
FMOD_OS_Thread_GetCurrentID(&mMainThreadID);
|
|
|
|
/*
|
|
Set up output driver for this system
|
|
*/
|
|
result = setOutput(mOutputType);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
#if defined(FMOD_SUPPORT_MYEARS) && !defined(PLATFORM_PS3)
|
|
bool enablemyears = !(mFlags & FMOD_INIT_DISABLE_MYEARS) && !mDownmix && DownmixMyEars::dataExists();
|
|
if (enablemyears)
|
|
{
|
|
mOutputRate = DownmixMyEars::getSampleRate();
|
|
}
|
|
#endif
|
|
|
|
if (mOutput->mDescription.initex)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
speakerMode = mSpeakerMode;
|
|
result = mOutput->mDescription.initex(mOutput, mSelectedDriver, flags, &mOutputRate, mMaxOutputChannels, &mOutputFormat, &speakerMode, mDSPBlockSize, mDSPBufferSize / mDSPBlockSize, 0, 0, extradriverdata);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (speakerMode != mSpeakerMode)
|
|
{
|
|
setSpeakerMode(speakerMode);
|
|
}
|
|
}
|
|
else if (mOutput->mDescription.init)
|
|
{
|
|
int outputchannels = mMaxOutputChannels;
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
result = mOutput->mDescription.init(mOutput, mSelectedDriver, flags, &mOutputRate, outputchannels, &mOutputFormat, mDSPBlockSize, mDSPBufferSize / mDSPBlockSize, extradriverdata);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
#if defined(FMOD_SUPPORT_NEURAL) && !defined(PLATFORM_PS3)
|
|
if ((flags & FMOD_INIT_DTS_NEURALSURROUND) && (mSpeakerMode == FMOD_SPEAKERMODE_STEREO || mSpeakerMode == FMOD_SPEAKERMODE_5POINT1) && !mDownmix)
|
|
{
|
|
mDownmix = FMOD_Object_Calloc(DownmixNeural);
|
|
if (!mDownmix)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
result = mDownmix->init(mDSPBufferSize, mOutputRate, mSpeakerMode);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Software mixer should always be mixing in 7.1 if we are using Neural
|
|
*/
|
|
setSpeakerMode(FMOD_SPEAKERMODE_7POINT1);
|
|
}
|
|
#endif
|
|
|
|
#if defined(FMOD_SUPPORT_MYEARS) && !defined(PLATFORM_PS3)
|
|
/*
|
|
MyEars will be enabled by default as long as the data exists
|
|
and another downmixer such as NEURALSURROUND has not been set.
|
|
*/
|
|
if (enablemyears && !mDownmix)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Load MyEars data\n"));
|
|
|
|
mDownmix = FMOD_Object_Calloc(DownmixMyEars);
|
|
if (!mDownmix)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
result = mDownmix->init(mDSPBufferSize, mOutputRate, mSpeakerMode, 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Failed to initialise MyEars. Returned error (%d)\n", result));
|
|
|
|
mDownmix->shutdown();
|
|
|
|
FMOD_Memory_Free(mDownmix);
|
|
mDownmix = 0;
|
|
enablemyears = false;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Software mixer should always be mixing in 7.1 if we are using MyEars (or 5.1 for testing)
|
|
*/
|
|
int inputchannels;
|
|
mDownmix->getInputChannels(&inputchannels);
|
|
switch(inputchannels)
|
|
{
|
|
case 8:
|
|
setSpeakerMode(FMOD_SPEAKERMODE_7POINT1);
|
|
break;
|
|
case 6:
|
|
setSpeakerMode(FMOD_SPEAKERMODE_5POINT1);
|
|
break;
|
|
default:
|
|
result = mDownmix->shutdown();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
FMOD_Memory_Free(mDownmix);
|
|
mDownmix = 0;
|
|
return FMOD_ERR_INTERNAL;
|
|
}
|
|
|
|
/*
|
|
Override software HRTF to false when using MyEars
|
|
*/
|
|
mFlags &= ~FMOD_INIT_SOFTWARE_HRTF;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (!(mFlags & FMOD_INIT_SOFTWARE_DISABLE))
|
|
{
|
|
FMOD_DSP_DESCRIPTION description;
|
|
FMOD_DSP_DESCRIPTION_EX descriptionex;
|
|
FMOD_SOUND_FORMAT bufferformat;
|
|
int bufferchannels;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Set up software engine\n"));
|
|
|
|
/*
|
|
Set up the 'software' output object.
|
|
*/
|
|
mSoftware = FMOD_Object_Alloc(OutputSoftware);
|
|
if (!mSoftware)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
mSoftware->mSystem = this;
|
|
|
|
if (!mDSPCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Create(&mDSPCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
if (!mDSPLockCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Create(&mDSPLockCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
if (!mDSPConnectionCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Create(&mDSPConnectionCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
#ifdef FMOD_NEED_DSPCODECPOOLINITCRIT
|
|
if(!mDSPCodecPoolInitCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Create(&mDSPCodecPoolInitCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
mConnectionRequestUsedHead.initNode();
|
|
mConnectionRequestFreeHead.initNode();
|
|
|
|
for (count = 0; count < FMOD_DSP_CONNECTION_REQUEST_MAX; count++)
|
|
{
|
|
DSPConnectionRequest *request = &mConnectionRequest[count];
|
|
|
|
request->initNode();
|
|
request->addBefore(&mConnectionRequestFreeHead);
|
|
|
|
}
|
|
|
|
result = getSoftwareFormat(0, &bufferformat, &bufferchannels, 0, 0, 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (mMaxInputChannels < bufferchannels)
|
|
{
|
|
mMaxInputChannels = bufferchannels;
|
|
}
|
|
|
|
/*
|
|
Create a global intermediate pair of buffers for the DSP engine to use.
|
|
*/
|
|
mDSPTempBuffMem = (float *)FMOD_Memory_Calloc((mDSPBlockSize * (bufferchannels < mMaxInputChannels ? mMaxInputChannels : bufferchannels) * sizeof(float)) + 16);
|
|
if (!mDSPTempBuffMem)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
mDSPTempBuff = (float *)FMOD_ALIGNPOINTER(mDSPTempBuffMem, 16);
|
|
|
|
/*
|
|
Create a pool of connections so we dont have to use malloc/free each time a connection/disconnection happens.
|
|
Usually with the default software engine it is:
|
|
1 for soundcard to mixer.
|
|
'mNumSoftwareChannels' for mixer to channel head(s)
|
|
'mNumSoftwareChannels' for channelhead(s) to wavetable(s)/generator(s).
|
|
*/
|
|
result = mDSPConnectionPool.init(this, 1 + (mNumSoftwareChannels * 2), bufferchannels < 2 ? 2 : bufferchannels, mMaxInputChannels);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Create the soundcard unit and main mixer unit then connect them together.
|
|
*/
|
|
FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX));
|
|
FMOD_strcpy(descriptionex.name, "FMOD SoundCard Unit");
|
|
descriptionex.version = 0x00010100;
|
|
descriptionex.channels = bufferchannels;
|
|
descriptionex.create = 0;
|
|
descriptionex.release = 0;
|
|
descriptionex.read = 0;
|
|
descriptionex.setposition = 0;
|
|
descriptionex.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD;
|
|
descriptionex.mFormat = bufferformat;
|
|
result = createDSP(&descriptionex, &mDSPSoundCard);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
mDSPSoundCard->updateTreeLevel(0); /* Set root to 0 to get initial tree level for rest of network to base off. */
|
|
#endif
|
|
mDSPSoundCard->setActive(true);
|
|
|
|
FMOD_memset(&description, 0, sizeof(FMOD_DSP_DESCRIPTION));
|
|
FMOD_strcpy(description.name, "FMOD ChannelGroup Target Unit");
|
|
description.version = 0x00010100;
|
|
description.channels = 0;
|
|
description.create = 0;
|
|
description.release = 0;
|
|
description.read = 0;
|
|
description.setposition = 0;
|
|
result = createDSP(&description, &mDSPChannelGroupTarget);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
mDSPChannelGroupTarget->setDefaults((float)mOutputRate, -1, -1, -1);
|
|
mDSPChannelGroupTarget->setActive(true);
|
|
|
|
result = mDSPSoundCard->addInput(mDSPChannelGroupTarget);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
result = createChannelGroup("FMOD master group", &mChannelGroup);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = createSoundGroup("FMOD master group", &mSoundGroup);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (!(mFlags & FMOD_INIT_SOFTWARE_DISABLE))
|
|
{
|
|
result = mSoftware->init(mNumSoftwareChannels);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
If the user wants to add extra code around the polled start,
|
|
check here for overriden start function.
|
|
Plugin writer must call OutputPolled::start.
|
|
*/
|
|
if (mOutput->mDescription.start)
|
|
{
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
|
|
result = mOutput->mDescription.start(mOutput);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
else if (mOutput->mDescription.polling)
|
|
{
|
|
OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput);
|
|
|
|
result = outputpolled->start();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Set up emulated output\n"));
|
|
|
|
/*
|
|
Set up emulated output for virtual channels
|
|
*/
|
|
if (maxchannels > 0)
|
|
{
|
|
mEmulated = FMOD_Object_Alloc(OutputEmulated);
|
|
if (!mEmulated)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
mEmulated->mSystem = this;
|
|
|
|
result = mEmulated->init(maxchannels);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "create the channel pool\n"));
|
|
|
|
/*
|
|
Create a pool of channels the user will interface with, that will contain pointers to real or virtual channels
|
|
*/
|
|
if (maxchannels)
|
|
{
|
|
mChannel = (ChannelI *)FMOD_Memory_Calloc(sizeof(ChannelI) * maxchannels);
|
|
if (!mChannel)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
mNumChannels = maxchannels;
|
|
|
|
for (count = 0; count < mNumChannels; count++)
|
|
{
|
|
new (&mChannel[count]) ChannelI(count, this);
|
|
|
|
mChannel[count].addAfter(&mChannelFreeListHead);
|
|
mChannel[count].setChannelGroup(mChannelGroup); /* Put channels into primary channel group */
|
|
}
|
|
}
|
|
|
|
/*
|
|
Global list of sounds critical section
|
|
*/
|
|
if (!gSoundListCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Create(&gSoundListCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
initCleanup.setSoundListCrit(&gSoundListCrit);
|
|
}
|
|
|
|
/*
|
|
Sound lock critical section
|
|
*/
|
|
if (!mMultiSubSampleLockBufferCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Create(&mMultiSubSampleLockBufferCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "Set up streamer\n"));
|
|
|
|
/*
|
|
Create stream thread.
|
|
*/
|
|
if (!mStreamThreadActive && !(mFlags & FMOD_INIT_STREAM_FROM_UPDATE)) /* Single buffered is not nonblocking! */
|
|
{
|
|
AutoFreeCrit realchanCritCleanup;
|
|
AutoFreeCrit updateCritCleanup;
|
|
AutoFreeCrit listCritCleanup;
|
|
|
|
result = FMOD_OS_CriticalSection_Create(&mStreamRealchanCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
realchanCritCleanup = mStreamRealchanCrit;
|
|
result = FMOD_OS_CriticalSection_Create(&mStreamUpdateCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
updateCritCleanup = mStreamUpdateCrit;
|
|
result = FMOD_OS_CriticalSection_Create(&mStreamListCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
listCritCleanup = mStreamListCrit;
|
|
|
|
result = mStreamThread.initThread("FMOD stream thread", streamThread, this, STREAM_THREADPRIORITY, 0, STREAM_STACKSIZE, false, 10, this);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mStreamThreadActive = true;
|
|
realchanCritCleanup.releasePtr();
|
|
updateCritCleanup.releasePtr();
|
|
listCritCleanup.releasePtr();
|
|
}
|
|
|
|
if (!FMOD::gGlobal->gFileCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Create(&FMOD::gGlobal->gFileCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
initCleanup.setFileCrit(&FMOD::gGlobal->gFileCrit);
|
|
}
|
|
|
|
if (!FMOD::gGlobal->gAsyncCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Create(&FMOD::gGlobal->gAsyncCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
initCleanup.setAsyncCrit(&FMOD::gGlobal->gAsyncCrit);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
Initialize codecs up front if specified.
|
|
*/
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
if (!(mFlags & FMOD_INIT_SOFTWARE_DISABLE))
|
|
{
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
if (mAdvancedSettings.maxADPCMcodecs)
|
|
{
|
|
int count;
|
|
|
|
result = mDSPCodecPool_ADPCM.init(FMOD_DSP_CATEGORY_DSPCODECADPCM, 64, mAdvancedSettings.maxADPCMcodecs);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
for (count = 0; count < mDSPCodecPool_ADPCM.mNumDSPCodecs; count++)
|
|
{
|
|
DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mDSPCodecPool_ADPCM.mPool[count]);
|
|
CodecWav *wav = (CodecWav *)dspcodec->mCodec;
|
|
|
|
wav->mSrcFormat = &wav->mSrcFormatMemory;
|
|
wav->mReadBuffer = mDSPCodecPool_ADPCM.mReadBuffer;
|
|
wav->mSrcFormat->Format.wFormatTag = WAVE_FORMAT_IMA_ADPCM;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
if (mAdvancedSettings.maxMPEGcodecs)
|
|
{
|
|
int count;
|
|
|
|
result = mDSPCodecPool_MPEG.init(FMOD_DSP_CATEGORY_DSPCODECMPEG, 1152, mAdvancedSettings.maxMPEGcodecs);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
for (count = 0; count < mDSPCodecPool_MPEG.mNumDSPCodecs; count++)
|
|
{
|
|
DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mDSPCodecPool_MPEG.mPool[count]);
|
|
CodecMPEG *mpeg = (CodecMPEG *)dspcodec->mCodec;
|
|
|
|
mpeg->mSrcDataOffset = 0; /* Raw data will start at 0. */
|
|
mpeg->mWaveFormatMemory = 0; /* This will be set up upon play. */
|
|
mpeg->reset();
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
if (mAdvancedSettings.maxXMAcodecs)
|
|
{
|
|
int count;
|
|
|
|
result = mDSPCodecPool_XMA.init(FMOD_DSP_CATEGORY_DSPCODECXMA, 512, mAdvancedSettings.maxXMAcodecs);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
for (count = 0; count < mDSPCodecPool_XMA.mNumDSPCodecs; count++)
|
|
{
|
|
DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mDSPCodecPool_XMA.mPool[count]);
|
|
CodecXMA *xma = (CodecXMA *)dspcodec->mCodec;
|
|
|
|
xma->mSrcDataOffset = 0; /* Raw data will start at 0. */
|
|
xma->mWaveFormatMemory = 0; /* This will be set up upon play. */
|
|
|
|
result = xma->XMAInit(1);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
if (mAdvancedSettings.maxCELTcodecs)
|
|
{
|
|
int count;
|
|
FMOD_CODEC_DESCRIPTION_EX *desc;
|
|
|
|
result = mPluginFactory->getCodec(mCELTPluginHandle, &desc);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = mDSPCodecPool_CELT.init(FMOD_DSP_CATEGORY_DSPCODECCELT, FMOD_CELT_FRAMESIZESAMPLES, mAdvancedSettings.maxCELTcodecs);
|
|
if (result != FMOD_OK)
|
|
{
|
|
close();
|
|
return result;
|
|
}
|
|
|
|
for (count = 0; count < mDSPCodecPool_CELT.mNumDSPCodecs; count++)
|
|
{
|
|
DSPCodec *dspcodec = SAFE_CAST(DSPCodec, mDSPCodecPool_CELT.mPool[count]);
|
|
CodecCELT *celt = (CodecCELT *)dspcodec->mCodec;
|
|
|
|
celt->mSrcDataOffset = 0; /* Raw data will start at 0. */
|
|
celt->mWaveFormatMemory = 0; /* This will be set up upon play. */
|
|
|
|
result = desc->init(celt, 1);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
if (mAdvancedSettings.maxPCMcodecs)
|
|
{
|
|
result = mDSPCodecPool_RAW.init(FMOD_DSP_CATEGORY_DSPCODECRAW, 256, mAdvancedSettings.maxPCMcodecs);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Initialize 2D and 3D reverb masters
|
|
*/
|
|
{
|
|
const FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF;
|
|
|
|
result = mReverbGlobal.init(this, false);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_REVERB
|
|
result = setReverbProperties(&prop);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
result = mReverb3D.init(this, false);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = set3DReverbProperties(&prop);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (mFlags & FMOD_INIT_ENABLE_PROFILE)
|
|
{
|
|
#ifdef FMOD_SUPPORT_PROFILE
|
|
result = FMOD_Profile_Create(mAdvancedSettings.profileport);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
initCleanup.profilerInitialized();
|
|
#endif
|
|
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_DSP)
|
|
result = FMOD_ProfileDsp_Create();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_MEMORY)
|
|
result = FMOD_ProfileMemory_Create();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CPU)
|
|
result = FMOD_ProfileCpu_Create();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CHANNEL)
|
|
result = FMOD_ProfileChannel_Create();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
#if defined(FMOD_SUPPORT_PROFILE) && defined(FMOD_SUPPORT_PROFILE_CODEC)
|
|
result = FMOD_ProfileCodec_Create();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef FMOD_SUPPORT_VSTPLUGIN
|
|
mVSTPluginsListHead.initNode();
|
|
#endif
|
|
|
|
mSpeakerLevelsPool.mSystem = this;
|
|
result = mHistoryBufferPool.init(mAdvancedSettings.maxSpectrumWaveDataBuffers, FMOD_MAX(mMaxOutputChannels, mMaxInputChannels) );
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
mInitialized = true;
|
|
|
|
gGlobal->gSystemInitCount++;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::init", "done\n\n"));
|
|
|
|
initCleanup.releasePtr();
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::close()
|
|
{
|
|
return closeEx(false);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::closeEx(bool calledfrominit)
|
|
{
|
|
FMOD_RESULT result;
|
|
int count;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "\n"));
|
|
|
|
update(); /* Execute an update to make sure everything is in sync. */
|
|
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
if (mOutput && mOutput->mRecordNumActive)
|
|
{
|
|
result = mOutput->recordStopAll(false);
|
|
CHECK_RESULT(result);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Stop all sounds
|
|
*/
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Stop all sounds\n"));
|
|
for (count = 0; count < mNumChannels; count++)
|
|
{
|
|
mChannel[count].stopEx(CHANNELI_STOPFLAG_REFSTAMP | CHANNELI_STOPFLAG_UPDATELIST | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP |CHANNELI_STOPFLAG_UPDATESYNCPOINTS);
|
|
}
|
|
|
|
update(); /* For ps2 it needs to execute an update to get the channel commands to execute. */
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
if (mStreamThreadActive)
|
|
{
|
|
mStreamThread.closeThread();
|
|
mStreamThreadActive = false;
|
|
|
|
FMOD_OS_CriticalSection_Free(mStreamRealchanCrit);
|
|
mStreamRealchanCrit = 0;
|
|
|
|
FMOD_OS_CriticalSection_Free(mStreamUpdateCrit);
|
|
mStreamUpdateCrit = 0;
|
|
|
|
FMOD_OS_CriticalSection_Free(mStreamListCrit);
|
|
mStreamListCrit = 0;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Stream thread destroyed\n"));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Shut down streamer and async thread, only if this is the last instance of a system object.
|
|
*/
|
|
if (gGlobal->gSystemInitCount == 1 && mInitialized)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Shut down streamer and FMOD_NONBLOCKING and FileSystem thread.\n"));
|
|
|
|
#ifdef FMOD_SUPPORT_NONBLOCKING
|
|
result = AsyncThread::shutDown();
|
|
if (result!= FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
if (FMOD::gGlobal->gAsyncCrit)
|
|
{
|
|
FMOD_OS_CriticalSection_Free(FMOD::gGlobal->gAsyncCrit);
|
|
FMOD::gGlobal->gAsyncCrit = 0;
|
|
}
|
|
|
|
if (gSoundListCrit)
|
|
{
|
|
FMOD_OS_CriticalSection_Free(gSoundListCrit);
|
|
gSoundListCrit = 0;
|
|
}
|
|
|
|
if (mFlags & FMOD_INIT_ENABLE_PROFILE)
|
|
{
|
|
#ifdef FMOD_SUPPORT_PROFILE
|
|
result = FMOD_Profile_Release();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Shut down file system.\n"));
|
|
result = File::shutDown();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Recursively remove all channelgroups.
|
|
*/
|
|
if (mChannelGroup)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Free master channel group.\n"));
|
|
|
|
result = mChannelGroup->releaseInternal(true);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mChannelGroup = 0;
|
|
}
|
|
|
|
/*
|
|
Remove 'main' sound group.
|
|
*/
|
|
if (mSoundGroup)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Remove 'master' sound group.\n"));
|
|
|
|
result = mSoundGroup->releaseInternal();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mSoundGroup = 0;
|
|
}
|
|
|
|
if (mOutput)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Shut down output.\n"));
|
|
|
|
if (mOutput->mDescription.stop)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
mOutput->mDescription.stop(mOutput);
|
|
}
|
|
else if (mOutput->mDescription.polling)
|
|
{
|
|
OutputPolled *outputpolled = SAFE_CAST(OutputPolled, mOutput);
|
|
|
|
outputpolled->stop();
|
|
}
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mDSPChannelGroupTarget)
|
|
{
|
|
result = mDSPChannelGroupTarget->release();
|
|
mDSPChannelGroupTarget = 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
set3DReverbActive(false); /* Stop it from creating new reverbs inside update functions. */
|
|
|
|
ReverbI *reverb_c = SAFE_CAST(ReverbI, mReverb3DHead.getNext());
|
|
ReverbI *reverb_tmp;
|
|
|
|
while (reverb_c != &mReverb3DHead)
|
|
{
|
|
reverb_tmp = reverb_c;
|
|
reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext());
|
|
reverb_tmp->release(true);
|
|
}
|
|
mReverb3D.release(false);
|
|
#endif
|
|
|
|
mReverbGlobal.release(false);
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mDSPTempBuffMem)
|
|
{
|
|
FMOD_Memory_Free(mDSPTempBuffMem);
|
|
mDSPTempBuff = mDSPTempBuffMem = 0;
|
|
}
|
|
#endif
|
|
if (mOutput)
|
|
{
|
|
/*
|
|
Dont clean up the output if it is coming from init, because the user set up the output pre-init.
|
|
*/
|
|
if (calledfrominit)
|
|
{
|
|
if (mOutput->mDescription.close)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
mOutput->mDescription.close(mOutput);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
result = mOutput->release();
|
|
mOutput = 0;
|
|
}
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mSoftware)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Free software output.\n"));
|
|
|
|
result = mSoftware->release();
|
|
mSoftware = 0;
|
|
}
|
|
#endif
|
|
|
|
if (mEmulated)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Free emulated output.\n"));
|
|
|
|
result = mEmulated->release();
|
|
mEmulated = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Free codec pools for FMOD_CREATECOMPRESSEDSAMPLE.
|
|
*/
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
result = mDSPCodecPool_MPEG.close();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
result = mDSPCodecPool_XMA.close();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
result = mDSPCodecPool_ADPCM.close();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
result = mDSPCodecPool_RAW.close();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if (mChannel)
|
|
{
|
|
int count;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Free channel pool.\n"));
|
|
|
|
for (count = 0; count < mNumChannels; count++)
|
|
{
|
|
if (mChannel[count].mLevels)
|
|
{
|
|
FMOD_Memory_Free(mChannel[count].mLevels);
|
|
mChannel[count].mLevels = 0;
|
|
}
|
|
}
|
|
|
|
FMOD_Memory_Free(mChannel);
|
|
mChannel = 0;
|
|
mNumChannels = 0;
|
|
}
|
|
|
|
mChannelFreeListHead.initNode();
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
|
|
if (mDSPSoundCard)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Remove DSP Soundcard unit.\n"));
|
|
|
|
result = mDSPSoundCard->release(); /* Release last because some things above point to it. */
|
|
mDSPSoundCard = 0;
|
|
}
|
|
|
|
/*
|
|
Remove miscllaneous DSP stuff
|
|
*/
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "Remove miscllaneous DSP stuff.\n"));
|
|
|
|
result = mDSPConnectionPool.close();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
for (count = 0; count < FMOD_DSP_MAXTREEDEPTH; count++)
|
|
{
|
|
if (mDSPMixBuff[count])
|
|
{
|
|
FMOD_Memory_Free(mDSPMixBuff[count]);
|
|
mDSPMixBuff[count] = 0;
|
|
}
|
|
}
|
|
|
|
if (mDSPCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Free(mDSPCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mDSPCrit = 0;
|
|
}
|
|
if (mDSPLockCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Free(mDSPLockCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mDSPLockCrit = 0;
|
|
}
|
|
if (mDSPConnectionCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Free(mDSPConnectionCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mDSPConnectionCrit = 0;
|
|
}
|
|
#ifdef FMOD_NEED_DSPCODECPOOLINITCRIT
|
|
if (mDSPCodecPoolInitCrit)
|
|
{
|
|
result = FMOD_OS_CriticalSection_Free(mDSPCodecPoolInitCrit);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mDSPCodecPoolInitCrit = 0;
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_MPEG_SPU
|
|
CodecMPEG::closeAll();
|
|
#endif
|
|
|
|
/*
|
|
Sound lock critical section
|
|
*/
|
|
if (mMultiSubSampleLockBufferCrit)
|
|
{
|
|
FMOD_OS_CriticalSection_Free(mMultiSubSampleLockBufferCrit);
|
|
mMultiSubSampleLockBufferCrit = 0;
|
|
}
|
|
|
|
mSpeakerLevelsPool.release();
|
|
mHistoryBufferPool.release();
|
|
|
|
if (mDownmix)
|
|
{
|
|
result = mDownmix->shutdown();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
FMOD_Memory_Free(mDownmix);
|
|
mDownmix = 0;
|
|
}
|
|
|
|
if (mPluginFactory && !calledfrominit)
|
|
{
|
|
CHECK_RESULT(mPluginFactory->release());
|
|
mPluginFactory = 0;
|
|
mPluginsLoaded = false;
|
|
}
|
|
|
|
if (mInitialized)
|
|
{
|
|
gGlobal->gSystemInitCount--;
|
|
}
|
|
|
|
mInitialized = false;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::close", "done.\n\n"));
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::update()
|
|
{
|
|
FMOD_RESULT result;
|
|
unsigned int currenttimestamp;
|
|
int delta, count;
|
|
|
|
if (!mInitialized)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
#ifdef FMOD_DEBUG
|
|
{
|
|
FMOD_UINT_NATIVE id;
|
|
|
|
FMOD_OS_Thread_GetCurrentID(&id);
|
|
|
|
if (id != mMainThreadID)
|
|
{
|
|
FLOG((FMOD_DEBUG_TYPE_THREAD, __FILE__, __LINE__, "SystemI::update", "Warning! You are calling FMOD from different threads! This is not safe!\n"));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
mUpdateTimeStamp.stampIn();
|
|
|
|
// unsigned int a, b;
|
|
// FMOD_OS_Time_GetNs(&a);
|
|
|
|
/*
|
|
Calculate the time delta since this was last called.
|
|
*/
|
|
if (!mLastTimeStamp)
|
|
{
|
|
FMOD_OS_Time_GetMs(&mLastTimeStamp);
|
|
}
|
|
|
|
FMOD_OS_Time_GetMs(¤ttimestamp);
|
|
|
|
if (currenttimestamp >= mLastTimeStamp)
|
|
{
|
|
delta = currenttimestamp - mLastTimeStamp;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Timestamp has wrapped around?
|
|
*/
|
|
delta = currenttimestamp;
|
|
}
|
|
|
|
mLastTimeStamp = currenttimestamp;
|
|
|
|
/*
|
|
Update emulated output object.
|
|
*/
|
|
if (mEmulated)
|
|
{
|
|
result = mEmulated->update();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
/*
|
|
Update reverb-specific details
|
|
*/
|
|
result = update3DReverbs();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Update per channel stuff including virtual voice swapping.
|
|
*/
|
|
result = updateChannels(delta);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Update any fading or volume logic by sound groups.
|
|
*/
|
|
result = updateSoundGroups(delta);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (!mSoftware)
|
|
#endif
|
|
{
|
|
FMOD_OS_Time_GetMs(&FMOD::gGlobal->gDSPClockTimeStamp);
|
|
FMOD::gGlobal->gDSPClock.mHi += delta;
|
|
|
|
mDSPClock.mValue += ((FMOD_UINT64)delta * (FMOD_UINT64)mOutputRate / (FMOD_UINT64)1000);
|
|
}
|
|
|
|
/*
|
|
Update hardware output object.
|
|
*/
|
|
if (mOutput && mOutput->mDescription.update)
|
|
{
|
|
mUpdateTimeStamp.setPaused(true);
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
result = mOutput->mDescription.update(mOutput);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
mUpdateTimeStamp.setPaused(false);
|
|
}
|
|
|
|
/*
|
|
Determine if the device list has changed
|
|
*/
|
|
result = checkDriverList(true);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
/*
|
|
Clean up recording device list, stop finished devices
|
|
*/
|
|
if (mOutput && mOutput->mRecordNumActive)
|
|
{
|
|
result = mOutput->recordStopAll(true);
|
|
CHECK_RESULT(result);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Clear moved flag from listeners.
|
|
*/
|
|
for (count = 0; count < mNumListeners; count++)
|
|
{
|
|
mListener[count].mMoved = false;
|
|
mListener[count].mRotated = false;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
mGeometryMgr.mMoved = false;
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_NONBLOCKING
|
|
result = AsyncThread::update();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
mUpdateTimeStamp.stampOut(95);
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
if (mFlags & FMOD_INIT_STREAM_FROM_UPDATE)
|
|
{
|
|
updateStreams();
|
|
}
|
|
#endif
|
|
|
|
if (mFlags & FMOD_INIT_SYNCMIXERWITHUPDATE)
|
|
{
|
|
if(mOutput->mDescription.polling)
|
|
{
|
|
OutputPolled *outputpolled = (OutputPolled *)mOutput;
|
|
|
|
outputpolled->wakeupThread();
|
|
}
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_PROFILE
|
|
if (mFlags & FMOD_INIT_ENABLE_PROFILE)
|
|
{
|
|
result = FMOD_Profile_Update(this, delta);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_VSTPLUGIN
|
|
updateVSTPlugins();
|
|
#endif
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::set3DSettings(float dopplerscale, float distancescale, float rolloffscale)
|
|
{
|
|
if (dopplerscale < 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (distancescale <= 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (rolloffscale < 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
mDopplerScale = dopplerscale;
|
|
mDistanceScale = distancescale;
|
|
mRolloffScale = rolloffscale;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::set3DNumListeners(int numlisteners)
|
|
{
|
|
if (numlisteners < 1 || numlisteners > LISTENER_MAX)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
mNumListeners = numlisteners;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up)
|
|
{
|
|
if (listener < 0 || listener >= LISTENER_MAX)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (forward)
|
|
{
|
|
#ifdef FMOD_DEBUG
|
|
{
|
|
FMOD_RESULT result;
|
|
float length;
|
|
|
|
result = FMOD_CHECKFLOAT(forward->x);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(forward->y);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(forward->z);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
length = FMOD_Vector_DotProduct(forward, forward);
|
|
if (length < 0.9f || length > 1.1f)
|
|
{
|
|
return FMOD_ERR_INVALID_VECTOR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (mListener[listener].mLastFront.x != forward->x ||
|
|
mListener[listener].mLastFront.y != forward->y ||
|
|
mListener[listener].mLastFront.z != forward->z)
|
|
{
|
|
mListener[listener].mRotated = true;
|
|
}
|
|
|
|
mListener[listener].mLastFront = mListener[listener].mFront;
|
|
mListener[listener].mFront = *forward;
|
|
}
|
|
|
|
if (up)
|
|
{
|
|
#ifdef FMOD_DEBUG
|
|
{
|
|
FMOD_RESULT result;
|
|
float length;
|
|
|
|
result = FMOD_CHECKFLOAT(up->x);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(up->y);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(up->z);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
length = FMOD_Vector_DotProduct(up, up);
|
|
if (length < 0.9f || length > 1.1f)
|
|
{
|
|
return FMOD_ERR_INVALID_VECTOR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (mListener[listener].mLastUp.x != up->x ||
|
|
mListener[listener].mLastUp.y != up->y ||
|
|
mListener[listener].mLastUp.z != up->z)
|
|
{
|
|
mListener[listener].mRotated = true;
|
|
}
|
|
|
|
mListener[listener].mLastUp = mListener[listener].mUp;
|
|
mListener[listener].mUp = *up;
|
|
}
|
|
|
|
if (pos)
|
|
{
|
|
#ifdef FMOD_DEBUG
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
result = FMOD_CHECKFLOAT(pos->x);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(pos->y);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(pos->z);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (mListener[listener].mLastPosition.x != pos->x ||
|
|
mListener[listener].mLastPosition.y != pos->y ||
|
|
mListener[listener].mLastPosition.z != pos->z)
|
|
{
|
|
mListener[listener].mMoved = true;
|
|
}
|
|
|
|
mListener[listener].mPosition = *pos;
|
|
mListener[listener].mLastPosition = mListener[listener].mPosition;
|
|
}
|
|
|
|
if (vel)
|
|
{
|
|
#ifdef FMOD_DEBUG
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
result = FMOD_CHECKFLOAT(vel->x);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(vel->y);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = FMOD_CHECKFLOAT(vel->z);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (mListener[listener].mLastVelocity.x != vel->x ||
|
|
mListener[listener].mLastVelocity.y != vel->y ||
|
|
mListener[listener].mLastVelocity.z != vel->z)
|
|
{
|
|
mListener[listener].mMoved = true;
|
|
}
|
|
|
|
mListener[listener].mLastVelocity = mListener[listener].mVelocity;
|
|
mListener[listener].mVelocity = *vel;
|
|
}
|
|
|
|
{
|
|
FMOD_VECTOR l_front, l_up;
|
|
|
|
l_up = mListener[listener].mUp;
|
|
l_front = mListener[listener].mFront;
|
|
|
|
if (mFlags & FMOD_INIT_3D_RIGHTHANDED)
|
|
{
|
|
l_up.z = -l_up.z;
|
|
l_front.z = -l_front.z;
|
|
}
|
|
|
|
#ifdef FMOD_DEBUG
|
|
{
|
|
float dot = FMOD_Vector_DotProduct(&l_up, &l_front);
|
|
|
|
if (dot < -0.01f || dot > 0.01f)
|
|
{
|
|
return FMOD_ERR_INVALID_VECTOR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FMOD_Vector_CrossProduct(&l_up, &l_front, &mListener[listener].mRight);
|
|
}
|
|
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::set3DRolloffCallback(FMOD_3D_ROLLOFFCALLBACK callback)
|
|
{
|
|
mRolloffCallback = callback;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::set3DSpeakerPosition(FMOD_SPEAKER speaker, float x, float y, bool active)
|
|
{
|
|
if (speaker < 0 || speaker >= FMOD_SPEAKER_MAX)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
mSpeaker[speaker].mSpeaker = speaker;
|
|
mSpeaker[speaker].mPosition.x = x;
|
|
mSpeaker[speaker].mPosition.y = 0;
|
|
mSpeaker[speaker].mPosition.z = y;
|
|
mSpeaker[speaker].mActive = active;
|
|
|
|
/* calculate XZ angle for sorting */
|
|
mSpeaker[speaker].mXZAngle = FMOD_AngleSort_GetValue(mSpeaker[speaker].mPosition.x, mSpeaker[speaker].mPosition.z);
|
|
|
|
return sortSpeakerList();
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::get3DSpeakerPosition(FMOD_SPEAKER speaker, float *x, float *y, bool *active)
|
|
{
|
|
if (speaker < 0 || speaker >= FMOD_SPEAKER_MAX)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (x)
|
|
{
|
|
*x = mSpeaker[speaker].mPosition.x;
|
|
}
|
|
if (y)
|
|
{
|
|
*y = mSpeaker[speaker].mPosition.z;
|
|
}
|
|
if (active)
|
|
{
|
|
*active = mSpeaker[speaker].mActive;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#endif // #ifndef FMOD_STATICFORPLUGINS
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::get3DNumListeners(int *numlisteners)
|
|
{
|
|
if (!numlisteners)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*numlisteners = mNumListeners;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::get3DListenerAttributes(int listener, FMOD_VECTOR *pos, FMOD_VECTOR *vel, FMOD_VECTOR *forward, FMOD_VECTOR *up)
|
|
{
|
|
if (listener < 0 || listener >= LISTENER_MAX)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (pos)
|
|
{
|
|
*pos = mListener[listener].mPosition;
|
|
}
|
|
|
|
if (vel)
|
|
{
|
|
*vel = mListener[listener].mVelocity;
|
|
}
|
|
|
|
if (forward)
|
|
{
|
|
*forward = mListener[listener].mFront;
|
|
}
|
|
|
|
if (up)
|
|
{
|
|
*up = mListener[listener].mUp;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::get3DSettings(float *dopplerscale, float *distancescale, float *rolloffscale)
|
|
{
|
|
if (dopplerscale)
|
|
{
|
|
*dopplerscale = mDopplerScale;
|
|
}
|
|
if (distancescale)
|
|
{
|
|
*distancescale = mDistanceScale;
|
|
}
|
|
if (rolloffscale)
|
|
{
|
|
*rolloffscale = mRolloffScale;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getVersion(unsigned int *version)
|
|
{
|
|
if (!version)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*version = FMOD_VERSION;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getOutputHandle(void **handle)
|
|
{
|
|
if (!handle)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mOutput)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (mOutput->mDescription.gethandle)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
return mOutput->mDescription.gethandle(mOutput, handle);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getChannelsPlaying(int *channels)
|
|
{
|
|
if (!channels)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*channels = mChannelUsedListHead.count();
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getCPUUsage(float *dsp, float *stream, float *geometry, float *update, float *total)
|
|
{
|
|
FMOD_RESULT result;
|
|
float totalcpu, usage;
|
|
|
|
totalcpu = 0;
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
result = mDSPTimeStamp.getCPUUsage(&usage);
|
|
if (result == FMOD_OK)
|
|
{
|
|
totalcpu += usage;
|
|
|
|
if (dsp)
|
|
{
|
|
*dsp = usage;
|
|
}
|
|
}
|
|
#else
|
|
if (dsp)
|
|
{
|
|
*dsp = 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
result = mStreamTimeStamp.getCPUUsage(&usage);
|
|
if (result == FMOD_OK)
|
|
{
|
|
totalcpu += usage;
|
|
|
|
if (stream)
|
|
{
|
|
*stream = usage;
|
|
}
|
|
}
|
|
#else
|
|
if (stream)
|
|
{
|
|
*stream = 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_GEOMETRY_THREADED
|
|
result = mGeometryTimeStamp.getCPUUsage(&usage);
|
|
if (result == FMOD_OK)
|
|
{
|
|
totalcpu += usage;
|
|
|
|
if (geometry)
|
|
{
|
|
*geometry = usage;
|
|
}
|
|
}
|
|
#else
|
|
if (geometry)
|
|
{
|
|
*geometry = 0;
|
|
}
|
|
#endif
|
|
|
|
result = mUpdateTimeStamp.getCPUUsage(&usage);
|
|
if (result == FMOD_OK)
|
|
{
|
|
totalcpu += usage;
|
|
|
|
if (update)
|
|
{
|
|
*update = usage;
|
|
}
|
|
}
|
|
|
|
if (total)
|
|
{
|
|
*total = totalcpu;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getSoundRAM(int *currentalloced, int *maxalloced, int *total)
|
|
{
|
|
if (mOutput && mOutput->mDescription.getsoundram)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
mOutput->mDescription.getsoundram(mOutput, currentalloced, maxalloced, total);
|
|
}
|
|
else
|
|
{
|
|
if (currentalloced)
|
|
{
|
|
*currentalloced = 0;
|
|
}
|
|
if (maxalloced)
|
|
{
|
|
*maxalloced = 0;
|
|
}
|
|
if (total)
|
|
{
|
|
*total = 0;
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getNumCDROMDrives(int *numdrives)
|
|
{
|
|
#ifdef FMOD_SUPPORT_CDDA
|
|
return FMOD_OS_CDDA_GetNumDevices(numdrives);
|
|
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getCDROMDriveName(int drive, char *drivename, int drivenamelen, char *scsiname, int scsinamelen, char *devicename, int devicenamelen)
|
|
{
|
|
#ifdef FMOD_SUPPORT_CDDA
|
|
return FMOD_OS_CDDA_GetDeviceName(drive, drivename, drivenamelen, scsiname, scsinamelen, devicename, devicenamelen);
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Retrieves the spectrum from the currently playing output signal.
|
|
|
|
[PARAMETERS]
|
|
'spectrumarray' Pointer to an array of floats to receive spectrum data. Data range is 0-1. Decibels = 10.0f * (float)log10(val) * 2.0f;
|
|
'numvalues' Size of array in floating point values being passed to the function. Must be a power of 2. (ie 128/256/512 etc). Min = 64. Max = 8192.
|
|
'channeloffset' Channel to analyze. If the signal is multichannel (such as a stereo output), then this value represents which channel to analyze. On a stereo signal 0 = left, 1 = right.
|
|
'windowtype' Pre-FFT window method. This filters the PCM data before entering the spectrum analyzer to reduce transient frequency error for more accurate results. See FMOD_DSP_FFT_WINDOW for different types of fft window techniques possible and for a more detailed explanation.
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
The larger the windowsize, the more CPU the FFT will take. Choose the right value to trade off between accuracy / speed.<br>
|
|
The larger the windowsize, the more 'lag' the spectrum will seem to inherit. This is because the window size stretches the analysis back in time to what was already played. For example if the window size happened to be 44100 and the output rate was 44100 it would be analyzing the past second of data, and giving you the average spectrum over that time period.<br>
|
|
If you are not displaying the result in dB, then the data may seem smaller than it should be. To display it you may want to normalize the data - that is, find the maximum value in the resulting spectrum, and scale all values in the array by 1 / max. (ie if the max was 0.5f, then it would become 1).<br>
|
|
To get the spectrum for both channels of a stereo signal, call this function twice, once with channeloffset = 0, and again with channeloffset = 1. Then add the spectrums together and divide by 2 to get the average spectrum for both channels.<br>
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
FMOD_DSP_FFT_WINDOW
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getSpectrum(float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GETSPECTRUM
|
|
FMOD_RESULT result = FMOD_OK;
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
DSPI *soundcard;
|
|
#else
|
|
DSPSoundCard *soundcard;
|
|
#endif
|
|
float *buffer;
|
|
unsigned int position, length;
|
|
int numchannels, windowsize;
|
|
static DSPFFT fft;
|
|
|
|
if (!mDSPSoundCard)
|
|
{
|
|
return FMOD_ERR_INITIALIZATION;
|
|
}
|
|
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
soundcard = SAFE_CAST(DSPI, mDSPSoundCard);
|
|
#else
|
|
soundcard = SAFE_CAST(DSPSoundCard, mDSPSoundCard);
|
|
#endif
|
|
if (!soundcard)
|
|
{
|
|
return FMOD_ERR_INITIALIZATION;
|
|
}
|
|
|
|
windowsize = numvalues * 2;
|
|
|
|
if (windowsize != (1 << 7) &&
|
|
windowsize != (1 << 8) &&
|
|
windowsize != (1 << 9) &&
|
|
windowsize != (1 << 10) &&
|
|
windowsize != (1 << 11) &&
|
|
windowsize != (1 << 12) &&
|
|
windowsize != (1 << 13) &&
|
|
windowsize != (1 << 14))
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = getSoftwareFormat(0, 0, &numchannels, 0, 0, 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (channeloffset >= numchannels)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = soundcard->startBuffering();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = soundcard->getHistoryBuffer(&buffer, &position, &length);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (windowsize > (int)length)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
position -= windowsize;
|
|
if ((int)position < 0)
|
|
{
|
|
position += length;
|
|
}
|
|
|
|
mUpdateTimeStamp.stampIn();
|
|
|
|
result = fft.getSpectrum(buffer, position, length, spectrumarray, windowsize, channeloffset, numchannels, windowtype);
|
|
|
|
mUpdateTimeStamp.stampOut(95);
|
|
|
|
return result;
|
|
#else
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif // FMOD_SUPPORT_GETSPECTRUM
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
FMOD_DSP_FFT_WINDOW
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getWaveData(float *wavearray, int numvalues, int channeloffset)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
FMOD_RESULT result = FMOD_OK;
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
DSPI *soundcard;
|
|
#else
|
|
DSPSoundCard *soundcard;
|
|
#endif
|
|
float *buffer;
|
|
unsigned int position, length;
|
|
int numchannels, count;
|
|
|
|
if (!mDSPSoundCard)
|
|
{
|
|
return FMOD_ERR_INITIALIZATION;
|
|
}
|
|
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
soundcard = SAFE_CAST(DSPI, mDSPSoundCard);
|
|
#else
|
|
soundcard = SAFE_CAST(DSPSoundCard, mDSPSoundCard);
|
|
#endif
|
|
if (!soundcard)
|
|
{
|
|
return FMOD_ERR_INITIALIZATION;
|
|
}
|
|
|
|
result = getSoftwareFormat(0, 0, &numchannels, 0, 0, 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (channeloffset >= numchannels)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = soundcard->startBuffering();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = soundcard->getHistoryBuffer(&buffer, &position, &length);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (numvalues > (int)length)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
position -= numvalues;
|
|
if ((int)position < 0)
|
|
{
|
|
position += length;
|
|
}
|
|
|
|
for (count = 0; count < numvalues; count++)
|
|
{
|
|
wavearray[count] = buffer[position*numchannels+channeloffset];
|
|
position++;
|
|
if (position >= length)
|
|
{
|
|
position = 0;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
#else
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createSound(const char *name_or_data, FMOD_MODE mode_in, FMOD_CREATESOUNDEXINFO *exinfo, SoundI **sound)
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
if (!mInitialized)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (!sound)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (!name_or_data && !(mode_in & FMOD_OPENUSER))
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (mode_in & FMOD_NONBLOCKING && !(mode_in & (FMOD_SOFTWARE | FMOD_HARDWARE)))
|
|
{
|
|
/*
|
|
When using nonblocking, we can't rely on the codec to determine hardware or software if
|
|
neither is specified. Since the sample is created of the type of the output mode and
|
|
returned to the caller, we can't change it later if the codec requires it.
|
|
*/
|
|
mode_in |= FMOD_HARDWARE;
|
|
}
|
|
if (!(mode_in & FMOD_SOFTWARE))
|
|
{
|
|
if (!mOutput)
|
|
{
|
|
return FMOD_ERR_OUTPUT_NOHARDWARE;
|
|
}
|
|
}
|
|
|
|
#if !defined(PLATFORM_WII) && !defined(PLATFORM_PSP) // We can allow this on the Wii, and PSP
|
|
if (mode_in & FMOD_HARDWARE && mode_in & FMOD_OPENMEMORY_POINT && !(mode_in & FMOD_CREATESTREAM))
|
|
{
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
}
|
|
#endif
|
|
|
|
*sound = 0;
|
|
|
|
#ifdef FMOD_SUPPORT_NONBLOCKING
|
|
|
|
if (mode_in & FMOD_NONBLOCKING)
|
|
{
|
|
SoundI *soundi;
|
|
|
|
#ifdef FMOD_DEBUG
|
|
if (mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT))
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "memory = %p : mode %08x\n", name_or_data, mode_in));
|
|
}
|
|
else
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "filename = %s : mode %08x\n", name_or_data, mode_in));
|
|
}
|
|
#endif
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "FMOD_NONBLOCKING specified. Putting into queue to be opened asynchronously!\n"));
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
if (mode_in & FMOD_CREATESTREAM)
|
|
{
|
|
Stream *stream = FMOD_Object_Calloc(Stream);
|
|
if (!stream)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
*sound = stream;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
Sample *sample = 0;
|
|
int hwchannels = 0;
|
|
|
|
if (!(mode_in & FMOD_SOFTWARE))
|
|
{
|
|
getHardwareChannels(0, 0, &hwchannels);
|
|
}
|
|
|
|
if (hwchannels && mOutput->mDescription.createsample)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
result = mOutput->mDescription.createsample(mOutput, (FMOD_MODE)0, 0, &sample);
|
|
mCreatedHardwareSample = true;
|
|
}
|
|
else
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
result = mSoftware->createSample((FMOD_MODE)0, 0, &sample);
|
|
#else
|
|
result = FMOD_ERR_NEEDSSOFTWARE;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "ERROR - Software not initialized\n"));
|
|
#endif
|
|
}
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
*sound = sample;
|
|
}
|
|
|
|
soundi = SAFE_CAST(SoundI, *sound);
|
|
|
|
/*
|
|
Calculate the size of the AsyncData plus any data that's pointed to by the exinfo struct.
|
|
We need to do a deep copy of this exinfo struct soon.
|
|
*/
|
|
int sizeof_asyncdata = sizeof(AsyncData);
|
|
|
|
if (exinfo)
|
|
{
|
|
sizeof_asyncdata += (exinfo->inclusionlistnum * sizeof(int));
|
|
|
|
if (exinfo->dlsname)
|
|
{
|
|
sizeof_asyncdata += (FMOD_strlen(exinfo->dlsname) + 1);
|
|
}
|
|
|
|
if (exinfo->encryptionkey)
|
|
{
|
|
sizeof_asyncdata += (FMOD_strlen(exinfo->encryptionkey) + 1);
|
|
}
|
|
}
|
|
|
|
soundi->mAsyncData = (AsyncData *)FMOD_Memory_Calloc(sizeof_asyncdata);
|
|
if (!soundi->mAsyncData)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "allocated async data mem\n"));
|
|
|
|
if (mode_in & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT))
|
|
{
|
|
soundi->mAsyncData->mNameData = (void *)name_or_data;
|
|
}
|
|
else if (name_or_data)
|
|
{
|
|
if (mode_in & FMOD_UNICODE)
|
|
{
|
|
// don't need to divide FMOD_STRING_MAXNAMELEN by 2 here, as AsyncData::mName is big enough
|
|
FMOD_strncpyW((short *)soundi->mAsyncData->mName, (short *)name_or_data, FMOD_STRING_MAXNAMELEN);
|
|
}
|
|
else
|
|
{
|
|
FMOD_strncpy(soundi->mAsyncData->mName, name_or_data, FMOD_STRING_MAXNAMELEN);
|
|
}
|
|
}
|
|
|
|
soundi->mAsyncData->mBufferSize = mStreamFileBufferSize;
|
|
soundi->mAsyncData->mBufferSizeType = mStreamFileBufferSizeType;
|
|
|
|
soundi->mMode = mode_in;
|
|
soundi->mSystem = this;
|
|
soundi->mOpenState = FMOD_OPENSTATE_LOADING;
|
|
|
|
if (exinfo)
|
|
{
|
|
FMOD_memcpy(&soundi->mAsyncData->mExInfo, exinfo, sizeof(FMOD_CREATESOUNDEXINFO));
|
|
soundi->mAsyncData->mExInfoExists = true;
|
|
|
|
if (exinfo->initialsoundgroup)
|
|
{
|
|
soundi->setSoundGroup((FMOD::SoundGroupI *)exinfo->initialsoundgroup);
|
|
}
|
|
|
|
/*
|
|
Now do a deep copy of things that are pointed to by the exinfo struct by tacking them
|
|
on the end of the soundi->mAsyncData memory block that we previously allocated and diddling
|
|
the pointers to them. We calculated the size of this block to take into account this extra
|
|
stuff so we're not going to stomp on memory.
|
|
*/
|
|
char *p = (char *)(soundi->mAsyncData + 1); // Pointer arithmetic. This puts us at the first byte after the end of the mAsyncData struct
|
|
|
|
if (soundi->mAsyncData->mExInfo.inclusionlistnum)
|
|
{
|
|
FMOD_memcpy(p, soundi->mAsyncData->mExInfo.inclusionlist, soundi->mAsyncData->mExInfo.inclusionlistnum * sizeof(int));
|
|
soundi->mAsyncData->mExInfo.inclusionlist = (int *)p;
|
|
p += (soundi->mAsyncData->mExInfo.inclusionlistnum * sizeof(int));
|
|
}
|
|
|
|
if (soundi->mAsyncData->mExInfo.dlsname)
|
|
{
|
|
FMOD_strcpy(p, soundi->mAsyncData->mExInfo.dlsname);
|
|
soundi->mAsyncData->mExInfo.dlsname = p;
|
|
p += (FMOD_strlen(soundi->mAsyncData->mExInfo.dlsname) + 1);
|
|
}
|
|
|
|
if (soundi->mAsyncData->mExInfo.encryptionkey)
|
|
{
|
|
FMOD_strcpy(p, soundi->mAsyncData->mExInfo.encryptionkey);
|
|
soundi->mAsyncData->mExInfo.encryptionkey = p;
|
|
p += (FMOD_strlen(soundi->mAsyncData->mExInfo.encryptionkey) + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
soundi->mAsyncData->mExInfoExists = false;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "getasyncthread\n"));
|
|
|
|
result = AsyncThread::getAsyncThread(soundi);
|
|
if (result != FMOD_OK)
|
|
{
|
|
soundi->mOpenState = FMOD_OPENSTATE_ERROR; /* Stop it from hanging in release because state is 'loading'. */
|
|
soundi->release();
|
|
*sound = 0;
|
|
return result;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "setdata soundi = %p : node = %p\n", soundi, &soundi->mAsyncData->mNode));
|
|
|
|
FMOD_OS_CriticalSection_Enter(soundi->mAsyncData->mThread->mCrit);
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "add node to async list : head = %p. list count = %d\n", &soundi->mAsyncData->mThread->mHead, soundi->mAsyncData->mThread->mHead.count()));
|
|
soundi->mAsyncData->mNode.setData(soundi);
|
|
soundi->mAsyncData->mNode.addBefore(&soundi->mAsyncData->mThread->mHead);
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(soundi->mAsyncData->mThread->mCrit);
|
|
|
|
soundi->mAsyncData->mThread->mThread.wakeupThread();
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "SystemI::createSound", "done\n"));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (exinfo)
|
|
{
|
|
FMOD_CREATESOUNDEXINFO exinfo_copy;
|
|
|
|
memcpy(&exinfo_copy, exinfo, sizeof(FMOD_CREATESOUNDEXINFO)); /* Just in case the createSoundInternal modifies the exinfo struct internally somewhere. */
|
|
|
|
result = createSoundInternal(name_or_data, mode_in, mStreamFileBufferSize, mStreamFileBufferSizeType, &exinfo_copy, false, sound);
|
|
}
|
|
else
|
|
{
|
|
result = createSoundInternal(name_or_data, mode_in, mStreamFileBufferSize, mStreamFileBufferSizeType, exinfo, false, sound);
|
|
}
|
|
|
|
if (*sound && exinfo && exinfo->initialsoundgroup)
|
|
{
|
|
(*sound)->setSoundGroup((FMOD::SoundGroupI *)exinfo->initialsoundgroup);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createStream(const char *name_or_data, FMOD_MODE mode_in, FMOD_CREATESOUNDEXINFO *exinfo, SoundI **sound)
|
|
{
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
return createSound(name_or_data, mode_in | FMOD_CREATESTREAM, exinfo, sound);
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createCodec(FMOD_CODEC_DESCRIPTION *description, unsigned int priority)
|
|
{
|
|
if (!description)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mFlags & FMOD_INIT_SOFTWARE_DISABLE)
|
|
#endif
|
|
{
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
{
|
|
FMOD_RESULT result;
|
|
FMOD_CODEC_DESCRIPTION_EX descriptionex;
|
|
|
|
descriptionex.name = description->name;
|
|
descriptionex.version = description->version;
|
|
descriptionex.timeunits = description->timeunits;
|
|
descriptionex.defaultasstream = description->defaultasstream;
|
|
descriptionex.open = description->open;
|
|
descriptionex.close = description->close;
|
|
descriptionex.read = description->read;
|
|
descriptionex.getlength = description->getlength;
|
|
descriptionex.setposition = description->setposition;
|
|
descriptionex.getposition = description->getposition;
|
|
descriptionex.soundcreate = description->soundcreate;
|
|
descriptionex.getwaveformat = description->getwaveformat;
|
|
descriptionex.mType = FMOD_SOUND_TYPE_UNKNOWN;
|
|
descriptionex.mSize = sizeof(Codec);
|
|
descriptionex.mModule = 0;
|
|
descriptionex.reset = 0;
|
|
|
|
result = mPluginFactory->registerCodec(&descriptionex, 0, priority);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createDSP(FMOD_DSP_DESCRIPTION *description, DSPI **dsp)
|
|
{
|
|
if (!dsp)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*dsp = 0;
|
|
|
|
if (!description)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mFlags & FMOD_INIT_SOFTWARE_DISABLE)
|
|
#endif
|
|
{
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
{
|
|
FMOD_RESULT result;
|
|
FMOD_DSP_DESCRIPTION_EX descriptionex;
|
|
|
|
FMOD_strcpy(descriptionex.name, description->name);
|
|
descriptionex.version = description->version;
|
|
descriptionex.channels = description->channels;
|
|
descriptionex.create = description->create;
|
|
descriptionex.release = description->release;
|
|
descriptionex.reset = description->reset;
|
|
descriptionex.read = description->read;
|
|
descriptionex.setposition = description->setposition;
|
|
|
|
descriptionex.numparameters = description->numparameters;
|
|
descriptionex.paramdesc = description->paramdesc;
|
|
descriptionex.setparameter = description->setparameter;
|
|
descriptionex.getparameter = description->getparameter;
|
|
descriptionex.config = description->config;
|
|
descriptionex.configwidth = description->configwidth;
|
|
descriptionex.configheight = description->configheight;
|
|
descriptionex.userdata = description->userdata;
|
|
descriptionex.getmemoryused = 0;
|
|
|
|
descriptionex.mSize = 0;
|
|
descriptionex.mCategory = FMOD_DSP_CATEGORY_FILTER;
|
|
descriptionex.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT;
|
|
#ifdef FMOD_SUPPORT_DLLS
|
|
descriptionex.mModule = 0;
|
|
descriptionex.mAEffect = 0;
|
|
#endif
|
|
descriptionex.mResamplerBlockLength = 0;
|
|
descriptionex.mType = FMOD_DSP_TYPE_UNKNOWN;
|
|
descriptionex.mDSPSoundCard = mDSPSoundCard;
|
|
|
|
result = mPluginFactory->createDSP(&descriptionex, dsp);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
(*dsp)->mSystem = this;
|
|
}
|
|
#endif
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createDSP(FMOD_DSP_DESCRIPTION_EX *description, DSPI **dsp, bool allocate)
|
|
{
|
|
if (!dsp)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (allocate)
|
|
{
|
|
*dsp = 0;
|
|
}
|
|
|
|
if (!description)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mFlags & FMOD_INIT_SOFTWARE_DISABLE)
|
|
#endif
|
|
{
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
{
|
|
FMOD_RESULT result;
|
|
FMOD_DSP_DESCRIPTION_EX descriptionex;
|
|
|
|
FMOD_strcpy(descriptionex.name, description->name);
|
|
descriptionex.version = description->version;
|
|
descriptionex.channels = description->channels;
|
|
descriptionex.create = description->create;
|
|
descriptionex.release = description->release;
|
|
descriptionex.reset = description->reset;
|
|
descriptionex.read = description->read;
|
|
descriptionex.setposition = description->setposition;
|
|
|
|
descriptionex.numparameters = description->numparameters;
|
|
descriptionex.paramdesc = description->paramdesc;
|
|
descriptionex.setparameter = description->setparameter;
|
|
descriptionex.getparameter = description->getparameter;
|
|
descriptionex.config = description->config;
|
|
descriptionex.configwidth = description->configwidth;
|
|
descriptionex.configheight = description->configheight;
|
|
descriptionex.userdata = description->userdata;
|
|
descriptionex.getmemoryused = description->getmemoryused;
|
|
|
|
descriptionex.mSize = description->mSize;
|
|
descriptionex.mCategory = description->mCategory;
|
|
descriptionex.mFormat = description->mFormat;
|
|
#ifdef FMOD_SUPPORT_DLLS
|
|
descriptionex.mModule = 0;
|
|
descriptionex.mAEffect = description->mAEffect;
|
|
#endif
|
|
descriptionex.mResamplerBlockLength = description->mResamplerBlockLength;
|
|
descriptionex.mType = description->mType;
|
|
descriptionex.mDSPSoundCard = description->mDSPSoundCard;
|
|
|
|
result = mPluginFactory->createDSP(&descriptionex, dsp);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
(*dsp)->mSystem = this;
|
|
}
|
|
#endif
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createDSPByType(FMOD_DSP_TYPE type, DSPI **dsp)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
FMOD_RESULT result;
|
|
|
|
if (!mPluginFactory)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (!dsp)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*dsp = 0;
|
|
|
|
if (type == FMOD_DSP_TYPE_MIXER)
|
|
{
|
|
FMOD_DSP_DESCRIPTION desc;
|
|
|
|
FMOD_memset(&desc, 0, sizeof(FMOD_DSP_DESCRIPTION));
|
|
|
|
FMOD_strcpy(desc.name, "FMOD Mixer unit");
|
|
|
|
result = createDSP(&desc, dsp);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
(*dsp)->mDescription.mType = FMOD_DSP_TYPE_MIXER;
|
|
}
|
|
else
|
|
{
|
|
int count, numdsps;
|
|
|
|
result = mPluginFactory->getNumDSPs(&numdsps);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
for (count = 0; count < numdsps; count++)
|
|
{
|
|
FMOD_DSP_DESCRIPTION_EX *descriptionex = 0;
|
|
unsigned int handle;
|
|
|
|
result = mPluginFactory->getDSPHandle(count, &handle);
|
|
if (result != FMOD_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
result = mPluginFactory->getDSP(handle, &descriptionex);
|
|
if (result != FMOD_OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (descriptionex->mType == type)
|
|
{
|
|
result = mPluginFactory->createDSP(descriptionex, dsp);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
}
|
|
|
|
return FMOD_ERR_PLUGIN_MISSING;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createDSPByPlugin(unsigned int handle, DSPI **dsp)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
FMOD_DSP_DESCRIPTION_EX *descriptionex = 0;
|
|
FMOD_RESULT result;
|
|
|
|
if (!mPluginFactory)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (!dsp)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*dsp = 0;
|
|
|
|
result = mPluginFactory->getDSP(handle, &descriptionex);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = mPluginFactory->createDSP(descriptionex, dsp);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createChannelGroupInternal(const char *name, ChannelGroupI **channelgroup, bool createdsp, bool storenameinchannelgroup)
|
|
{
|
|
ChannelGroupI *newchannelgroup;
|
|
AutoRelease<ChannelGroupI> newchannelgroupauto;
|
|
|
|
if (!channelgroup)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mSoftware && createdsp)
|
|
{
|
|
newchannelgroup = FMOD_Object_Calloc(ChannelGroupSoftware);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
newchannelgroup = FMOD_Object_Calloc(ChannelGroupI);
|
|
}
|
|
|
|
if (!newchannelgroup)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
newchannelgroupauto = newchannelgroup;
|
|
|
|
newchannelgroup->addAfter(&mChannelGroupHead);
|
|
newchannelgroup->mSystem = this;
|
|
|
|
if (name && storenameinchannelgroup)
|
|
{
|
|
newchannelgroup->mName = FMOD_strdup(name);
|
|
if (!newchannelgroup->mName)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newchannelgroup->mName = 0;
|
|
}
|
|
|
|
/*
|
|
Create a DSP unit for this channelgroup then add it to the soundcard unit.
|
|
*/
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mSoftware)
|
|
{
|
|
if (createdsp)
|
|
{
|
|
FMOD_RESULT result;
|
|
FMOD_DSP_DESCRIPTION_EX description;
|
|
|
|
FMOD_memset(&description, 0, sizeof(FMOD_DSP_DESCRIPTION_EX));
|
|
|
|
FMOD_strcpy(description.name, "ChannelGroup");
|
|
if (name)
|
|
{
|
|
FMOD_strcat(description.name, ":");
|
|
/*
|
|
description.name is hardwired to 32 so don't overflow it
|
|
*/
|
|
FMOD_strncat(description.name, name, 18);
|
|
}
|
|
description.version = 0x00010100;
|
|
|
|
newchannelgroup->mDSPHead = &((ChannelGroupSoftware *)newchannelgroup)->mDSPHeadMemory;
|
|
#if defined(PLATFORM_PS3) || defined(PLATFORM_WINDOWS_PS3MODE)
|
|
newchannelgroup->mDSPHead = (DSPI *)FMOD_ALIGNPOINTER(newchannelgroup->mDSPHead, 128);
|
|
new (newchannelgroup->mDSPHead) DSPFilter;
|
|
#endif
|
|
|
|
result = createDSP(&description, (DSPI **)&newchannelgroup->mDSPHead, false);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
newchannelgroup->mDSPHead->setDefaults((float)mOutputRate, -1, -1, -1);
|
|
newchannelgroup->mDSPHead->setActive(true);
|
|
|
|
result = mDSPChannelGroupTarget->addInputQueued(newchannelgroup->mDSPHead, false, 0, 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
newchannelgroup->mDSPMixTarget = newchannelgroup->mDSPHead;
|
|
}
|
|
else
|
|
{
|
|
newchannelgroup->mDSPMixTarget = mDSPChannelGroupTarget;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
newchannelgroupauto.releasePtr();
|
|
|
|
if (name && !FMOD_stricmp("music", name))
|
|
{
|
|
/*
|
|
Store pointer to "music" channelgroup.
|
|
*/
|
|
|
|
mOutput->mMusicChannelGroup = newchannelgroup;
|
|
}
|
|
|
|
*channelgroup = newchannelgroup;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createChannelGroup(const char *name, ChannelGroupI **channelgroup)
|
|
{
|
|
if (!channelgroup)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mSoftware)
|
|
{
|
|
return createChannelGroupInternal(name, channelgroup, true, true);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
return createChannelGroupInternal(name, channelgroup, false, true);
|
|
}
|
|
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createSoundGroup(const char *name, SoundGroupI **soundgroup)
|
|
{
|
|
SoundGroupI *newsoundgroup;
|
|
AutoCritRelease<SoundGroupI> autonewsoundgroup(gSoundListCrit);
|
|
|
|
if (!soundgroup)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
newsoundgroup = FMOD_Object_Calloc(SoundGroupI);
|
|
if (!newsoundgroup)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
FMOD_OS_CriticalSection_Enter(gSoundListCrit);
|
|
{
|
|
newsoundgroup->addAfter(&mSoundGroupFreeHead);
|
|
newsoundgroup->mSystem = this;
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(gSoundListCrit);
|
|
|
|
autonewsoundgroup = newsoundgroup;
|
|
|
|
if (name)
|
|
{
|
|
newsoundgroup->mName = FMOD_strdup(name);
|
|
if (!newsoundgroup->mName)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newsoundgroup->mName = 0;
|
|
}
|
|
|
|
autonewsoundgroup.releasePtr();
|
|
|
|
*soundgroup = newsoundgroup;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::playSound(FMOD_CHANNELINDEX channelid, SoundI *sound, bool paused, ChannelI **channel)
|
|
{
|
|
FMOD_RESULT result;
|
|
ChannelI *chan = 0;
|
|
bool mute = false;
|
|
|
|
if (channel)
|
|
{
|
|
if (channelid == FMOD_CHANNEL_REUSE)
|
|
{
|
|
result = ChannelI::validate((Channel *)*channel, &chan);
|
|
}
|
|
}
|
|
|
|
if (!sound)
|
|
{
|
|
if (channel)
|
|
{
|
|
*channel = 0;
|
|
}
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (sound->mOpenState != FMOD_OPENSTATE_READY)
|
|
{
|
|
if (channel)
|
|
{
|
|
*channel = 0;
|
|
}
|
|
return FMOD_ERR_NOTREADY;
|
|
}
|
|
|
|
if (sound->mType == FMOD_SOUND_TYPE_PLAYLIST)
|
|
{
|
|
return FMOD_ERR_FORMAT;
|
|
}
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
if (sound->mSoundGroup && sound->mSoundGroup->mMaxAudible >= 0)
|
|
{
|
|
int numplaying;
|
|
|
|
result = sound->mSoundGroup->getNumPlaying(&numplaying);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (numplaying >= sound->mSoundGroup->mMaxAudible)
|
|
{
|
|
switch (sound->mSoundGroup->mMaxAudibleBehavior)
|
|
{
|
|
case FMOD_SOUNDGROUP_BEHAVIOR_FAIL:
|
|
{
|
|
return FMOD_ERR_MAXAUDIBLE;
|
|
}
|
|
case FMOD_SOUNDGROUP_BEHAVIOR_MUTE:
|
|
{
|
|
mute = true;
|
|
break;
|
|
}
|
|
case FMOD_SOUNDGROUP_BEHAVIOR_STEALLOWEST:
|
|
{
|
|
LinkedListNode *current;
|
|
float lowest = 9999.0f;
|
|
|
|
/*
|
|
Find least important sound and replace it.
|
|
*/
|
|
current = mChannelUsedListHead.getNext();
|
|
while (current != &mChannelUsedListHead)
|
|
{
|
|
ChannelI *channel = (ChannelI *)current;
|
|
|
|
if (channel->mRealChannel[0] && channel->mRealChannel[0]->mSound)
|
|
{
|
|
SoundGroupI *soundgroup = channel->mRealChannel[0]->mSound->mSoundGroup;
|
|
|
|
if (soundgroup == sound->mSoundGroup)
|
|
{
|
|
float audibility;
|
|
|
|
channel->getAudibility(&audibility);
|
|
|
|
if (audibility < lowest)
|
|
{
|
|
chan = channel;
|
|
lowest = audibility;
|
|
channelid = (FMOD_CHANNELINDEX)chan->mIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
current = current->getNext();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
First find a channel
|
|
*/
|
|
result = findChannel(channelid, sound, &chan);
|
|
if (result != FMOD_OK)
|
|
{
|
|
if (channel)
|
|
{
|
|
*channel = 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
result = chan->play(sound, paused, true, mute);
|
|
if (result != FMOD_OK)
|
|
{
|
|
if (channel)
|
|
{
|
|
*channel = 0;
|
|
}
|
|
chan->stopEx(CHANNELI_STOPFLAG_UPDATELIST | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP | CHANNELI_STOPFLAG_UPDATESYNCPOINTS);
|
|
return result;
|
|
}
|
|
|
|
result = chan->updatePosition();
|
|
if (result != FMOD_OK)
|
|
{
|
|
if (channel)
|
|
{
|
|
*channel = 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (channelid == FMOD_CHANNEL_REUSE && *channel)
|
|
{
|
|
chan->mHandleCurrent = chan->mHandleOriginal;
|
|
}
|
|
else
|
|
{
|
|
result = chan->referenceStamp(true);
|
|
if (result != FMOD_OK)
|
|
{
|
|
if (channel)
|
|
{
|
|
*channel = 0;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (channel)
|
|
{
|
|
*channel = (ChannelI *)((FMOD_UINT_NATIVE)chan->mHandleCurrent);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::playDSP(FMOD_CHANNELINDEX channelid, DSPI *dsp, bool paused, ChannelI **channel)
|
|
{
|
|
FMOD_RESULT result;
|
|
ChannelI *chan = 0;
|
|
|
|
if (!dsp)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (channel)
|
|
{
|
|
if (channelid == FMOD_CHANNEL_REUSE)
|
|
{
|
|
ChannelI::validate((Channel *)*channel, &chan);
|
|
}
|
|
if (channel)
|
|
{
|
|
*channel = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
First find a channel
|
|
*/
|
|
result = findChannel(channelid, dsp, &chan);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = chan->play(dsp, paused, true, false);
|
|
if (result != FMOD_OK)
|
|
{
|
|
chan->stopEx(CHANNELI_STOPFLAG_UPDATELIST | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP | CHANNELI_STOPFLAG_UPDATESYNCPOINTS);
|
|
return result;
|
|
}
|
|
|
|
result = chan->updatePosition();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (channelid == FMOD_CHANNEL_REUSE && *channel)
|
|
{
|
|
chan->mHandleCurrent = chan->mHandleOriginal;
|
|
}
|
|
else
|
|
{
|
|
result = chan->referenceStamp(true);
|
|
if (result != FMOD_OK)
|
|
{
|
|
if (channel)
|
|
{
|
|
*channel = 0;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (channel)
|
|
{
|
|
*channel = (ChannelI *)((FMOD_UINT_NATIVE)chan->mHandleCurrent);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getChannel(int id, ChannelI **channel)
|
|
{
|
|
FMOD_UINT_NATIVE handle;
|
|
|
|
if (!channel)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (id < 0 || id >= mNumChannels)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
handle = (mIndex << SYSTEMID_SHIFT) | ((id << CHANINDEX_SHIFT) & (CHANINDEX_MASK << CHANINDEX_SHIFT));
|
|
*channel = (ChannelI *)handle;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getMasterChannelGroup(ChannelGroupI **channelgroup)
|
|
{
|
|
if (!channelgroup)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mChannelGroup)
|
|
{
|
|
*channelgroup = 0;
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
*channelgroup = mChannelGroup;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getMasterSoundGroup(SoundGroupI **soundgroup)
|
|
{
|
|
if (!soundgroup)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mSoundGroup)
|
|
{
|
|
*soundgroup = 0;
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
*soundgroup = mSoundGroup;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createReverb(ReverbI **reverb)
|
|
{
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
FMOD_RESULT result;
|
|
ReverbI *newreverb;
|
|
|
|
/*
|
|
initialise a new reverb instance
|
|
*/
|
|
newreverb = (ReverbI*)FMOD_Object_Alloc(ReverbI);
|
|
if (!newreverb)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
/*
|
|
Initialise the reverb object.
|
|
*/
|
|
result = newreverb->init(this, true, FMOD_REVERB_VIRTUAL);
|
|
if (result != FMOD_OK)
|
|
{
|
|
FMOD_Memory_Free(newreverb);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Add the reverb object to the system's channel reverb list
|
|
*/
|
|
newreverb->addBefore(&mReverb3DHead);
|
|
|
|
if (reverb)
|
|
{
|
|
*reverb = newreverb;
|
|
}
|
|
|
|
mReverbGlobal.setDisableIfNoEnvironment(false);
|
|
|
|
mReverb3D.setDisableIfNoEnvironment(false);
|
|
|
|
set3DReverbActive(true);
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
unsigned int SystemI::count3DPhysicalReverbs()
|
|
{
|
|
unsigned int count = 0;
|
|
|
|
ReverbI* chanreverb_c = SAFE_CAST(ReverbI, mReverb3DHead.getNext());
|
|
while (chanreverb_c != &mReverb3DHead)
|
|
{
|
|
if (chanreverb_c->getMode() == FMOD_REVERB_PHYSICAL)
|
|
{
|
|
++count;
|
|
}
|
|
chanreverb_c = SAFE_CAST(ReverbI, chanreverb_c->getNext());
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
unsigned int SystemI::count3DVirtualReverbs()
|
|
{
|
|
unsigned int count = 0;
|
|
|
|
ReverbI* chanreverb_c = SAFE_CAST(ReverbI, mReverb3DHead.getNext());
|
|
while (chanreverb_c != &mReverb3DHead)
|
|
{
|
|
if (chanreverb_c->getMode() == FMOD_REVERB_VIRTUAL)
|
|
{
|
|
++count;
|
|
}
|
|
chanreverb_c = SAFE_CAST(ReverbI, chanreverb_c->getNext());
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::set3DReverbActive(bool state)
|
|
{
|
|
mReverb3DActive = state;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::get3DReverbActive(bool *state)
|
|
{
|
|
if (state)
|
|
{
|
|
*state = mReverb3DActive;
|
|
}
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setReverbProperties(const FMOD_REVERB_PROPERTIES *prop, bool force_create)
|
|
{
|
|
#ifdef FMOD_SUPPORT_REVERB
|
|
FMOD_RESULT result;
|
|
|
|
if (!prop)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (prop->Instance < 0 || prop->Instance >= FMOD_REVERB_MAXINSTANCES)
|
|
{
|
|
return FMOD_ERR_REVERB_INSTANCE;
|
|
}
|
|
|
|
int instance = prop->Instance;
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
/*
|
|
Create DSP if necessary
|
|
*/
|
|
if ((!mReverbGlobal.mInstance[instance].mDSP) && (force_create || (prop->Environment!=-1)) && mSoftware)
|
|
{
|
|
/*
|
|
Create
|
|
*/
|
|
result = mReverbGlobal.createDSP(instance);
|
|
if (result == FMOD_OK)
|
|
{
|
|
/*
|
|
Link into network
|
|
*/
|
|
if (mDSPChannelGroupTarget)
|
|
{
|
|
result = mDSPChannelGroupTarget->addInput(mReverbGlobal.mInstance[instance].mDSP);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mReverbGlobal.setGain(1.0f);
|
|
}
|
|
else
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
/*
|
|
Connect all channels to the reverb input
|
|
*/
|
|
ChannelI *channel_c = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext());
|
|
|
|
while (channel_c != &mChannelUsedListHead)
|
|
{
|
|
FMOD_RESULT result;
|
|
FMOD_REVERB_CHANNELPROPERTIES cprops;
|
|
|
|
FMOD_memset(&cprops, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES));
|
|
|
|
cprops.Flags |= (instance == 0 ? FMOD_REVERB_CHANNELFLAGS_INSTANCE0 :
|
|
instance == 1 ? FMOD_REVERB_CHANNELFLAGS_INSTANCE1 :
|
|
instance == 2 ? FMOD_REVERB_CHANNELFLAGS_INSTANCE2 :
|
|
instance == 3 ? FMOD_REVERB_CHANNELFLAGS_INSTANCE3 : 0);
|
|
|
|
/*
|
|
Flush the channel properties through
|
|
*/
|
|
result = channel_c->getReverbProperties(&cprops);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = channel_c->setReverbProperties( &cprops);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
channel_c = SAFE_CAST(ChannelI, channel_c->getNext());
|
|
}
|
|
|
|
mReverbGlobal.mInstance[instance].mDSP->setActive(true);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Set DSP (sw/hw handled by ReverbI)
|
|
*/
|
|
result = mReverbGlobal.setProperties(prop);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getReverbProperties(FMOD_REVERB_PROPERTIES *prop)
|
|
{
|
|
if (!prop)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (prop->Instance < 0 || prop->Instance >= FMOD_REVERB_MAXINSTANCES)
|
|
{
|
|
return FMOD_ERR_REVERB_INSTANCE;
|
|
}
|
|
|
|
return mReverbGlobal.getProperties(prop);
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
FMOD_RESULT SystemI::set3DReverbProperties(const FMOD_REVERB_PROPERTIES *prop, bool force_create)
|
|
{
|
|
FMOD_RESULT result;
|
|
int instance = 0;
|
|
|
|
if (!prop)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
/*
|
|
Create DSP if necessary
|
|
*/
|
|
if ((!mReverb3D.mInstance[instance].mDSP) && (force_create || (prop->Environment!=-1)))
|
|
{
|
|
/*
|
|
Create
|
|
*/
|
|
result = mReverb3D.createDSP(instance);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
Link into network
|
|
*/
|
|
if (mDSPChannelGroupTarget)
|
|
{
|
|
result = mDSPChannelGroupTarget->addInput(mReverb3D.mInstance[instance].mDSP);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
mReverb3D.setGain(1.0f);
|
|
}
|
|
else
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
/*
|
|
Connect all channels to the reverb input
|
|
*/
|
|
ChannelI* channel_c = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext());
|
|
while (channel_c != &mChannelUsedListHead)
|
|
{
|
|
/*
|
|
Flush the channel properties through
|
|
*/
|
|
FMOD_REVERB_CHANNELPROPERTIES cprops;
|
|
|
|
FMOD_memset(&cprops, 0, sizeof(FMOD_REVERB_CHANNELPROPERTIES));
|
|
|
|
result = channel_c->getReverbProperties(&cprops);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
result = channel_c->setReverbProperties( &cprops);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
channel_c = SAFE_CAST(ChannelI, channel_c->getNext());
|
|
}
|
|
|
|
mReverb3D.mInstance[instance].mDSP->setActive(true);
|
|
}
|
|
|
|
/*
|
|
Set DSP (sw/hw handled by ReverbI)
|
|
*/
|
|
result = mReverb3D.setProperties(prop);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
FMOD_RESULT SystemI::get3DReverbProperties(FMOD_REVERB_PROPERTIES *prop)
|
|
{
|
|
return mReverb3D.getProperties(prop);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getDSPHead(DSPI **dsp)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (!dsp)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mDSPSoundCard)
|
|
{
|
|
return FMOD_ERR_INTERNAL;
|
|
}
|
|
|
|
*dsp = mDSPSoundCard;
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::addDSP(DSPI *dsp, DSPConnectionI **dspconnection)
|
|
{
|
|
FMOD_RESULT result;
|
|
DSPI *dsphead = 0;
|
|
|
|
if (!dsp)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = getDSPHead(&dsphead);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
result = dsphead->insertInputBetween(dsp, 0, false, dspconnection);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::lockDSP()
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
FMOD_OS_CriticalSection_Enter(mDSPLockCrit);
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::unlockDSP()
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
FMOD_OS_CriticalSection_Leave(mDSPLockCrit);
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getDSPClock(unsigned int *hi, unsigned int *lo)
|
|
{
|
|
if (hi)
|
|
{
|
|
*hi = mDSPClock.mHi;
|
|
}
|
|
if (lo)
|
|
{
|
|
*lo = mDSPClock.mLo;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getRecordNumDrivers(int *numdrivers)
|
|
{
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
FMOD_RESULT result = FMOD_OK;
|
|
|
|
if (!mOutput)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (!numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = checkDriverList(false);
|
|
CHECK_RESULT(result);
|
|
|
|
if (mOutput->mDescription.record_getnumdrivers)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
return mOutput->mDescription.record_getnumdrivers(mOutput, numdrivers);
|
|
}
|
|
|
|
*numdrivers = 0;
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getRecordDriverInfo(int id, char *name, int namelen, FMOD_GUID *guid)
|
|
{
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
FMOD_RESULT result = FMOD_OK;
|
|
int numdrivers = 0;
|
|
|
|
if (!mOutput)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
result = getRecordNumDrivers(&numdrivers);
|
|
CHECK_RESULT(result);
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (mOutput->mDescription.record_getdriverinfo)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
return mOutput->mDescription.record_getdriverinfo(mOutput, id, name, namelen, guid);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getRecordDriverInfoW(int id, short *name, int namelen, FMOD_GUID *guid)
|
|
{
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
FMOD_RESULT result = FMOD_OK;
|
|
int numdrivers = 0;
|
|
|
|
if (!mOutput)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
result = getRecordNumDrivers(&numdrivers);
|
|
CHECK_RESULT(result);
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (mOutput->mDescription.record_getdriverinfow)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
return mOutput->mDescription.record_getdriverinfow(mOutput, id, name, namelen, guid);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getRecordDriverCaps(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency)
|
|
{
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
FMOD_RESULT result = FMOD_OK;
|
|
FMOD_CAPS lcaps = FMOD_CAPS_NONE;
|
|
int lminfrequency = 0;
|
|
int lmaxfrequency = 0;
|
|
int numdrivers = 0;
|
|
|
|
if (!mOutput)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
result = getRecordNumDrivers(&numdrivers);
|
|
CHECK_RESULT(result);
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (mOutput->mDescription.record_getdrivercaps)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
result = mOutput->mDescription.record_getdrivercaps(mOutput, id, &lcaps, &lminfrequency, &lmaxfrequency);
|
|
CHECK_RESULT(result);
|
|
}
|
|
|
|
if (caps)
|
|
{
|
|
*caps = lcaps;
|
|
}
|
|
if (minfrequency)
|
|
{
|
|
*minfrequency = lminfrequency;
|
|
}
|
|
if (maxfrequency)
|
|
{
|
|
*maxfrequency = lmaxfrequency;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::recordStart(int id, SoundI *sound, bool loop)
|
|
{
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
int numdrivers = 0;
|
|
FMOD_RESULT result = FMOD_OK;
|
|
FMOD_RECORDING_INFO *recordinfo = NULL;
|
|
|
|
if (!mInitialized)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (!sound || (sound->mMode & FMOD_CREATESTREAM))
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = getRecordNumDrivers(&numdrivers);
|
|
CHECK_RESULT(result);
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
recordStop(id);
|
|
|
|
/*
|
|
Create recording info for this record instance
|
|
*/
|
|
recordinfo = FMOD_Object_Calloc(FMOD_RECORDING_INFO);
|
|
if (!recordinfo)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
recordinfo->mRecordId = id;
|
|
recordinfo->mRecordDriver = -1;
|
|
recordinfo->mRecordLoop = loop;
|
|
recordinfo->mRecordSound = sound;
|
|
recordinfo->mRecordRate = (int)sound->mDefaultFrequency; /* This will get set to what the hardware can do in the driver */
|
|
|
|
/*
|
|
Multi-mic support requires GUIDs to be implemented for the platform
|
|
*/
|
|
result = getRecordDriverInfo(id, NULL, 0, &recordinfo->mRecordGUID);
|
|
CHECK_RESULT(result);
|
|
|
|
if (mOutput->mDescription.record_start)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
mOutput->readfrommixer = Output::mixCallback; /* Reset 'read only' variable in FMOD_OUTPUT_STATE in case the user changed it. */
|
|
#else
|
|
mOutput->readfrommixer = 0;
|
|
#endif
|
|
|
|
result = mOutput->mDescription.record_start(mOutput, recordinfo, (FMOD_SOUND *)sound, loop);
|
|
CHECK_RESULT(result);
|
|
}
|
|
|
|
/*
|
|
Create a temp buffer to assist in moving data from hardware to sound sample
|
|
*/
|
|
{
|
|
unsigned int bufferlengthbytes = 0;
|
|
|
|
SoundI::getBytesFromSamples(FMOD_RECORD_TEMPBUFFERSIZE, &bufferlengthbytes, sound->mChannels, FMOD_SOUND_FORMAT_PCMFLOAT);
|
|
recordinfo->mRecordTempBufferLength = FMOD_RECORD_TEMPBUFFERSIZE;
|
|
|
|
recordinfo->mRecordTempBuffer = (float*)FMOD_Memory_Calloc(bufferlengthbytes);
|
|
if (!recordinfo->mRecordTempBuffer)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Setup a DSPResampler to handle record resampling if required
|
|
*/
|
|
if (recordinfo->mRecordRate != sound->mDefaultFrequency)
|
|
{
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
unsigned int blocklength = 0;
|
|
FMOD_DSP_DESCRIPTION_EX description;
|
|
|
|
/*
|
|
Total latency for resampling is ~30ms, we need 3 blocks for the resampler,
|
|
so each block will be ~10ms.
|
|
*/
|
|
blocklength = (int)(0.01f * recordinfo->mRecordRate);
|
|
blocklength /= 16; /* Round down to nearest 16 bytes. */
|
|
blocklength *= 16;
|
|
|
|
FMOD_memset(&description, 0, sizeof(FMOD_DSP_DESCRIPTION_EX));
|
|
description.channels = sound->mChannels;
|
|
description.mFormat = FMOD_SOUND_FORMAT_PCMFLOAT; /* Record read callback will always give floats. */
|
|
description.userdata = mOutput;
|
|
description.read = Output::recordResamplerReadCallback;
|
|
description.mResamplerBlockLength = blocklength;
|
|
|
|
recordinfo->mRecordResamplerDSP = FMOD_Object_Calloc(DSPResampler);
|
|
if (!recordinfo->mRecordResamplerDSP)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
recordinfo->mRecordResamplerDSP->mSystem = this;
|
|
recordinfo->mRecordResamplerDSP->mBuffer = recordinfo->mRecordTempBuffer;
|
|
recordinfo->mRecordResamplerDSP->alloc(&description);
|
|
recordinfo->mRecordResamplerDSP->mTargetFrequency = (int)sound->mDefaultFrequency;
|
|
recordinfo->mRecordResamplerDSP->setFrequency((float)recordinfo->mRecordRate);
|
|
recordinfo->mRecordResamplerDSP->setFinished(false);
|
|
#else
|
|
return FMOD_ERR_NEEDSSOFTWARE;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
Make the new record device "live" by putting in the list
|
|
*/
|
|
FMOD_OS_CriticalSection_Enter(mOutput->mRecordInfoCrit);
|
|
{
|
|
recordinfo->addAfter(&mOutput->mRecordInfoHead);
|
|
mOutput->mRecordNumActive++;
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(mOutput->mRecordInfoCrit);
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::recordStop(int id)
|
|
{
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
FMOD_RESULT result = FMOD_OK;
|
|
int numdrivers = 0;
|
|
FMOD_RECORDING_INFO *currentrecordinfo = 0;
|
|
|
|
if (!mInitialized)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
result = getRecordNumDrivers(&numdrivers);
|
|
CHECK_RESULT(result);
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = mOutput->recordGetInfo(id, ¤trecordinfo);
|
|
CHECK_RESULT(result);
|
|
|
|
if (currentrecordinfo)
|
|
{
|
|
result = mOutput->recordStop(currentrecordinfo);
|
|
CHECK_RESULT(result);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getRecordPosition(int id, unsigned int *position)
|
|
{
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
FMOD_RESULT result = FMOD_OK;
|
|
FMOD_RECORDING_INFO *info = NULL;
|
|
int numdrivers = 0;
|
|
|
|
if (!mInitialized)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (!position)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = getRecordNumDrivers(&numdrivers);
|
|
CHECK_RESULT(result);
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
mOutput->recordGetInfo(id, &info);
|
|
if (info)
|
|
{
|
|
*position = info->mRecordOffset;
|
|
}
|
|
else
|
|
{
|
|
*position = 0;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::isRecording(int id, bool *recording)
|
|
{
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
FMOD_RESULT result = FMOD_OK;
|
|
FMOD_RECORDING_INFO *info = NULL;
|
|
int numdrivers = 0;
|
|
|
|
if (!mInitialized)
|
|
{
|
|
return FMOD_ERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (!recording)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = getRecordNumDrivers(&numdrivers);
|
|
CHECK_RESULT(result);
|
|
|
|
if (id < 0 || id >= numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
mOutput->recordGetInfo(id, &info);
|
|
if (info)
|
|
{
|
|
*recording = true;
|
|
}
|
|
else
|
|
{
|
|
*recording = false;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::createGeometry(int maxNumPolygons, int maxNumVertices, GeometryI **geometry)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
FMOD_RESULT result;
|
|
|
|
if (!geometry)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (maxNumPolygons <= 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (maxNumVertices <= 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (geometry == 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*geometry = (GeometryI *)FMOD_Memory_Alloc(sizeof (GeometryI));
|
|
if (!*geometry)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
new (*geometry) GeometryI(&mGeometryMgr);
|
|
|
|
result = (*geometry)->alloc(maxNumPolygons, maxNumVertices);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (mGeometryList)
|
|
{
|
|
(*geometry)->addBefore(mGeometryList);
|
|
}
|
|
|
|
mGeometryList = *geometry;
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setGeometrySettings(float maxWorldSize)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
FMOD_RESULT result;
|
|
|
|
if (maxWorldSize <= 0.0f)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
result = mGeometryMgr.setWorldSize(maxWorldSize);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (mGeometryList)
|
|
{
|
|
// we have to remove everything from the tree and re-add
|
|
// it when the world size changes
|
|
GeometryI* current = mGeometryList;
|
|
do
|
|
{
|
|
current->removeFromTree();
|
|
current = SAFE_CAST(GeometryI, current->getNext());
|
|
}
|
|
while (current != mGeometryList);
|
|
|
|
current = mGeometryList;
|
|
do
|
|
{
|
|
result = current->setWorldSize(maxWorldSize); // re-adds automatically
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
current = SAFE_CAST(GeometryI, current->getNext());
|
|
}
|
|
while (current != mGeometryList);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getGeometrySettings(float *maxWorldSize)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
if (!maxWorldSize)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*maxWorldSize = mGeometryMgr.getWorldSize();
|
|
|
|
return FMOD_OK;
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::loadGeometry(const void *data, int dataSize, GeometryI **geometry)
|
|
{
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
FMOD_RESULT result;
|
|
|
|
if (!data)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
if (!geometry)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
*geometry = (GeometryI *)FMOD_Memory_Alloc(sizeof (GeometryI));
|
|
if (!*geometry)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
new (*geometry) GeometryI(&mGeometryMgr);
|
|
|
|
result = (*geometry)->load(data, dataSize);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (mGeometryList)
|
|
{
|
|
(*geometry)->addBefore(mGeometryList);
|
|
}
|
|
|
|
mGeometryList = *geometry;
|
|
|
|
return FMOD_OK;
|
|
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
FMOD_RESULT SystemI::getGeometryOcclusion(const FMOD_VECTOR *listener,
|
|
const FMOD_VECTOR *source,
|
|
float *direct, float *reverb)
|
|
{
|
|
if(!source || !listener)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
float direct_occlusion = 0.0f, reverb_occlusion = 0.0f;
|
|
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
CHECK_RESULT(mGeometryMgr.lineTestAll(listener, source,
|
|
&direct_occlusion, &reverb_occlusion));
|
|
#endif
|
|
|
|
if(direct)
|
|
{
|
|
*direct = direct_occlusion;
|
|
}
|
|
|
|
if(reverb)
|
|
{
|
|
*reverb = reverb_occlusion;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setNetworkProxy(const char *proxy)
|
|
{
|
|
#ifdef FMOD_SUPPORT_NET
|
|
return FMOD_Net_SetProxy(proxy);
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getNetworkProxy(char *proxy, int proxylen)
|
|
{
|
|
#ifdef FMOD_SUPPORT_NET
|
|
return FMOD_Net_GetProxy(proxy, proxylen);
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setNetworkTimeout(int timeout)
|
|
{
|
|
#ifdef FMOD_SUPPORT_NET
|
|
return FMOD_Net_SetTimeout(timeout);
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getNetworkTimeout(int *timeout)
|
|
{
|
|
#ifdef FMOD_SUPPORT_NET
|
|
return FMOD_Net_GetTimeout(timeout);
|
|
#else
|
|
return FMOD_ERR_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setStreamBufferSize(unsigned int filebuffersize, FMOD_TIMEUNIT filebuffersizetype)
|
|
{
|
|
if (filebuffersize <= 0)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (filebuffersizetype != FMOD_TIMEUNIT_MS &&
|
|
filebuffersizetype != FMOD_TIMEUNIT_PCM &&
|
|
filebuffersizetype != FMOD_TIMEUNIT_PCMBYTES &&
|
|
filebuffersizetype != FMOD_TIMEUNIT_RAWBYTES)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
mStreamFileBufferSize = filebuffersize;
|
|
mStreamFileBufferSizeType = filebuffersizetype;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getStreamBufferSize(unsigned int *filebuffersize, FMOD_TIMEUNIT *filebuffersizetype)
|
|
{
|
|
if (filebuffersize)
|
|
{
|
|
*filebuffersize = mStreamFileBufferSize;
|
|
}
|
|
if (filebuffersizetype)
|
|
{
|
|
*filebuffersizetype = mStreamFileBufferSizeType;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setUserData(void *userdata)
|
|
{
|
|
mUserData = userdata;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getUserData(void **userdata)
|
|
{
|
|
if (!userdata)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*userdata = mUserData;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getSoundList(SoundI **sound)
|
|
{
|
|
if (!sound)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*sound = &mSoundListHead;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Stops all playing channels using this sound.
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::stopSound(SoundI *sound)
|
|
{
|
|
ChannelI *current;
|
|
bool streamstopped = false;
|
|
|
|
#ifdef FMOD_SUPPORT_RECORDING
|
|
/*
|
|
If this sound is currently recording, stop the recording
|
|
*/
|
|
if (mOutput && mOutput->mRecordNumActive)
|
|
{
|
|
FMOD_RECORDING_INFO *currentrecordinfo = NULL;
|
|
|
|
currentrecordinfo = SAFE_CAST(FMOD_RECORDING_INFO, mOutput->mRecordInfoHead.getNext());
|
|
while (currentrecordinfo != &mOutput->mRecordInfoHead)
|
|
{
|
|
FMOD_RECORDING_INFO *next = SAFE_CAST(FMOD_RECORDING_INFO, currentrecordinfo->getNext());
|
|
|
|
if (currentrecordinfo->mRecordSound == sound)
|
|
{
|
|
mOutput->recordStop(currentrecordinfo);
|
|
break;
|
|
}
|
|
currentrecordinfo = next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
if (sound->isStream())
|
|
{
|
|
FMOD_OS_CriticalSection_Enter(mStreamListCrit);
|
|
|
|
if (!mStreamListChannelHead.isEmpty())
|
|
{
|
|
LinkedListNode *current;
|
|
Stream *stream = SAFE_CAST(Stream, sound);
|
|
|
|
current = mStreamListChannelHead.getNext();
|
|
while (current != &mStreamListChannelHead)
|
|
{
|
|
ChannelStream *channelstream = (ChannelStream *)current->getData();
|
|
Stream *currentstream = SAFE_CAST(Stream, channelstream->mSound);
|
|
LinkedListNode *next = current->getNext();
|
|
|
|
streamstopped = false;
|
|
|
|
/*
|
|
Might not have been played yet.
|
|
*/
|
|
if (!currentstream)
|
|
{
|
|
current = next;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
If this channel is playing this stream, stop the channel.
|
|
*/
|
|
if (currentstream == stream)
|
|
{
|
|
FMOD_OS_CriticalSection_Leave(mStreamListCrit);
|
|
|
|
channelstream->mParent->stop();
|
|
streamstopped = true;
|
|
|
|
FMOD_OS_CriticalSection_Enter(mStreamListCrit);
|
|
}
|
|
|
|
/*
|
|
If this channel is playing a sentence and it is the sound that is currently playing, stop the channel.
|
|
*/
|
|
#ifdef FMOD_SUPPORT_SENTENCING
|
|
else if (currentstream->mSubSoundList)
|
|
{
|
|
if (currentstream->mSubSound[currentstream->mSubSoundList[channelstream->mSubSoundListCurrent].mIndex] == sound)
|
|
{
|
|
FMOD_OS_CriticalSection_Leave(mStreamListCrit);
|
|
|
|
channelstream->mParent->stop();
|
|
streamstopped = true;
|
|
|
|
FMOD_OS_CriticalSection_Enter(mStreamListCrit);
|
|
}
|
|
else
|
|
{
|
|
int count;
|
|
|
|
for (count = 0; count < currentstream->mNumSubSounds; count++)
|
|
{
|
|
if (currentstream->mSubSound[count] == sound)
|
|
{
|
|
if (!(currentstream->mCodec && currentstream->mCodec->mFlags & FMOD_CODEC_USERLENGTH))
|
|
{
|
|
currentstream->mLength -= sound->mLength;
|
|
}
|
|
currentstream->mSubSound[count] = 0;
|
|
break; /* found it, so break out of subsound search. */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
If this channel is playing a substream, stop the channel.
|
|
*/
|
|
else if (currentstream->mSubSound)
|
|
{
|
|
int count;
|
|
|
|
for (count = 0; count < currentstream->mNumSubSounds; count++)
|
|
{
|
|
if (currentstream->mSubSound[count] == sound)
|
|
{
|
|
FMOD_OS_CriticalSection_Leave(mStreamListCrit);
|
|
|
|
channelstream->mParent->stop();
|
|
streamstopped = true;
|
|
|
|
FMOD_OS_CriticalSection_Enter(mStreamListCrit);
|
|
break; /* found it, so break out of subsound search. */
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
If this channel is using this stream's sample: Stop that channel.
|
|
*/
|
|
else if (currentstream->mSample == stream->mSample)
|
|
{
|
|
FMOD_OS_CriticalSection_Leave(mStreamListCrit);
|
|
|
|
channelstream->mParent->stop();
|
|
streamstopped = true;
|
|
|
|
FMOD_OS_CriticalSection_Enter(mStreamListCrit);
|
|
}
|
|
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
FMOD_OS_CriticalSection_Leave(mStreamListCrit);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Note that release/stopSound() can be called from the event system, on sounds that have never played before.
|
|
So don't let these sounds execute an async channel loop, it will crash. The 'Played' flag is never set on these types of sound, they're just temporary storage.
|
|
*/
|
|
if (sound->mFlags & FMOD_SOUND_FLAG_PLAYED && !streamstopped)
|
|
{
|
|
if (sound->mNumAudible) /* Channel::stop hasn't been called on all instances of this sound yet. Let's do it for them. */
|
|
{
|
|
if (sound->mMode & FMOD_SOFTWARE) /* This is slow, but they didn't stop the sound. Make sure the mixer is not continuing to do things with the channel after we stop. */
|
|
{
|
|
lockDSP();
|
|
}
|
|
|
|
current = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext());
|
|
while (current != &mChannelUsedListHead)
|
|
{
|
|
ChannelI *next = SAFE_CAST(ChannelI, current->getNext());
|
|
|
|
if (current->mRealChannel[0])
|
|
{
|
|
SoundI *channelsound = 0;
|
|
|
|
current->getCurrentSound(&channelsound);
|
|
if (channelsound == sound)
|
|
{
|
|
/*
|
|
Same call as current->stop, except we dont want end callbacks when releasing.
|
|
*/
|
|
current->stopEx(CHANNELI_STOPFLAG_REFSTAMP | CHANNELI_STOPFLAG_UPDATELIST | CHANNELI_STOPFLAG_RESETCALLBACKS | CHANNELI_STOPFLAG_RESETCHANNELGROUP | CHANNELI_STOPFLAG_UPDATESYNCPOINTS);
|
|
}
|
|
}
|
|
|
|
current = next;
|
|
}
|
|
|
|
if (sound->mMode & FMOD_SOFTWARE)
|
|
{
|
|
unlockDSP();
|
|
}
|
|
|
|
}
|
|
else if (sound->mMode & FMOD_SOFTWARE) /* Apparently all instances of this voice have been stopped, BUT! Are they still active in the mixer? */
|
|
{
|
|
FMOD_OPENSTATE openstate;
|
|
|
|
sound->getOpenState(&openstate, 0,0);
|
|
|
|
if (openstate == FMOD_OPENSTATE_STREAMING)
|
|
{
|
|
lockDSP(); /* Just stall for 1 DSP tick. */
|
|
unlockDSP();
|
|
}
|
|
}
|
|
|
|
if (!sound->isStream())
|
|
{
|
|
sound->mFlags &= ~FMOD_SOUND_FLAG_PLAYED; /* For sample banks, avoid constant dsp locks over and over again for nothing. */
|
|
}
|
|
}
|
|
|
|
#ifdef PLATFORM_PS2
|
|
update();
|
|
#endif
|
|
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::stopDSP(DSPI *dsp)
|
|
{
|
|
ChannelI *current;
|
|
|
|
current = SAFE_CAST(ChannelI, mChannelUsedListHead.getNext());
|
|
while (current != &mChannelUsedListHead)
|
|
{
|
|
ChannelI *next = SAFE_CAST(ChannelI, current->getNext());
|
|
DSPI *channeldsp;
|
|
|
|
current->getCurrentDSP(&channeldsp);
|
|
if (channeldsp == dsp)
|
|
{
|
|
current->stop();
|
|
}
|
|
|
|
current = next;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
On success, FMOD_OK is returned.
|
|
On failure, an error code is returned.
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getListenerObject(int listener, Listener **listenerobject)
|
|
{
|
|
if (!listenerobject || (listener < 0) || (listener >= mNumListeners))
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*listenerobject = &mListener[listener];
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
On success, FMOD_OK is returned.
|
|
On failure, an error code is returned.
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setGlobalUserCallbacks(FMOD_FILE_OPENCALLBACK open, FMOD_FILE_CLOSECALLBACK close, FMOD_FILE_READCALLBACK read, FMOD_FILE_SEEKCALLBACK seek)
|
|
{
|
|
if (!open || !close || !read || !seek)
|
|
{
|
|
open = 0;
|
|
close = 0;
|
|
read = 0;
|
|
seek = 0;
|
|
|
|
mUsesUserCallbacks = false;
|
|
}
|
|
else
|
|
{
|
|
mUsesUserCallbacks = true;
|
|
}
|
|
|
|
mOpenCallback = open;
|
|
mCloseCallback = close;
|
|
mReadCallback = read;
|
|
mSeekCallback = seek;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::flushDSPConnectionRequests(bool calledfrommainthread)
|
|
{
|
|
FMOD_OS_CriticalSection_Enter(mDSPConnectionCrit);
|
|
{
|
|
DSPConnectionRequest *request;
|
|
|
|
if (mConnectionRequestFlushing)
|
|
{
|
|
FMOD_OS_CriticalSection_Leave(mDSPConnectionCrit);
|
|
return FMOD_OK; /* addinput and disconnect can trigger this function to recursively execute. */
|
|
}
|
|
|
|
if (!mConnectionRequestUsedHead.isEmpty())
|
|
{
|
|
mConnectionRequestFlushing = true;
|
|
|
|
if (calledfrommainthread)
|
|
{
|
|
FMOD_OS_CriticalSection_Enter(mDSPCrit);
|
|
}
|
|
|
|
request = (DSPConnectionRequest *)mConnectionRequestUsedHead.getNext();
|
|
while (request != &mConnectionRequestUsedHead)
|
|
{
|
|
switch (request->mRequest)
|
|
{
|
|
case DSPCONNECTION_REQUEST_ADDINPUT:
|
|
{
|
|
request->mThis->addInputInternal(request->mTarget, false, request->mConnection, 0, false);
|
|
break;
|
|
}
|
|
case DSPCONNECTION_REQUEST_ADDINPUT_ERRCHECK:
|
|
{
|
|
request->mThis->addInputInternal(request->mTarget, true, request->mConnection, 0, false);
|
|
break;
|
|
}
|
|
case DSPCONNECTION_REQUEST_DISCONNECTFROM:
|
|
{
|
|
request->mThis->disconnectFromInternal(request->mTarget, request->mConnection, false);
|
|
if (request->mTarget)
|
|
{
|
|
request->mTarget->mFlags &= ~FMOD_DSP_FLAG_QUEUEDFORDISCONNECT;
|
|
}
|
|
else
|
|
{
|
|
request->mThis->mFlags &= ~FMOD_DSP_FLAG_QUEUEDFORDISCONNECT;
|
|
}
|
|
break;
|
|
}
|
|
case DSPCONNECTION_REQUEST_DISCONNECTALLINPUTS:
|
|
{
|
|
request->mThis->disconnectAllInternal(true, false, false);
|
|
break;
|
|
}
|
|
case DSPCONNECTION_REQUEST_DISCONNECTALLOUTPUTS:
|
|
{
|
|
request->mThis->disconnectAllInternal(false, true, false);
|
|
request->mThis->mFlags &= ~FMOD_DSP_FLAG_QUEUEDFORDISCONNECT;
|
|
break;
|
|
}
|
|
case DSPCONNECTION_REQUEST_DISCONNECTALL:
|
|
{
|
|
request->mThis->disconnectAllInternal(true, true, false);
|
|
request->mThis->mFlags &= ~FMOD_DSP_FLAG_QUEUEDFORDISCONNECT;
|
|
break;
|
|
}
|
|
case DSPCONNECTION_REQUEST_INSERTINBETWEEN:
|
|
case DSPCONNECTION_REQUEST_INSERTINBETWEEN_SEARCH:
|
|
{
|
|
request->mThis->insertInputBetweenInternal(request->mTarget, request->mInputIndex, request->mRequest == DSPCONNECTION_REQUEST_INSERTINBETWEEN_SEARCH ? true : false, request->mConnection, false);
|
|
request->mTarget->mFlags |= FMOD_DSP_FLAG_USEDADDDSP;
|
|
request->mTarget->reset();
|
|
request->mTarget->setActive(true);
|
|
break;
|
|
}
|
|
case DSPCONNECTION_REQUEST_REVERBUPDATEPARAMETERS:
|
|
{
|
|
#ifdef FMOD_SUPPORT_SFXREVERB
|
|
DSPI *reverb = (DSPI *)request->mThis;
|
|
if (reverb->mDescription.update)
|
|
{
|
|
reverb->mDescription.update((FMOD_DSP_STATE *)reverb);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
#ifdef FMOD_DSP_NONBLOCKING_REMOVE_RELEASE
|
|
case DSPCONNECTION_REQUEST_REMOVE:
|
|
{
|
|
request->mThis->removeInternal(false);
|
|
break;
|
|
}
|
|
case DSPCONNECTION_REQUEST_RELEASE:
|
|
{
|
|
request->mThis->releaseInternal(request->mTarget ? true : false, false);
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
request->removeNode();
|
|
request->addBefore(&mConnectionRequestFreeHead); /* Put back on free list. */
|
|
|
|
request = (DSPConnectionRequest *)mConnectionRequestUsedHead.getNext();
|
|
}
|
|
mConnectionRequestFlushing = false;
|
|
|
|
if (calledfrommainthread)
|
|
{
|
|
FMOD_OS_CriticalSection_Leave(mDSPCrit);
|
|
}
|
|
}
|
|
}
|
|
FMOD_OS_CriticalSection_Leave(mDSPConnectionCrit);
|
|
|
|
return FMOD_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getGlobals(Global **global)
|
|
{
|
|
if (!global)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*global = gGlobal;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
FMOD_RESULT F_API System_SetDebugLevel(unsigned int level)
|
|
{
|
|
#ifdef FMOD_DEBUG
|
|
gGlobal->gDebugLevel = (FMOD_DEBUGLEVEL)level;
|
|
#endif
|
|
return FMOD_OK;
|
|
}
|
|
|
|
unsigned int F_API System_GetDebugLevel()
|
|
{
|
|
#ifdef FMOD_DEBUG
|
|
return (unsigned int )gGlobal->gDebugLevel;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
FMOD_RESULT F_API System_SetDebugMode(unsigned int mode)
|
|
{
|
|
#ifdef FMOD_DEBUG
|
|
gGlobal->gDebugMode = (FMOD_DEBUGMODE)mode;
|
|
#endif
|
|
return FMOD_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::update3DReverbs()
|
|
{
|
|
float t = 0.0f;
|
|
bool reverb3d_active;
|
|
ReverbI *reverb_c;
|
|
|
|
//
|
|
// Standardised properties are all in floating point form for correct arithmetic
|
|
//
|
|
FMOD_REVERB_STDPROPERTIES stdprops;
|
|
FMOD_memset(&stdprops, 0, sizeof(FMOD_REVERB_STDPROPERTIES));
|
|
|
|
//
|
|
// For all General mode reverbs, set the level of the listener gain
|
|
//
|
|
for (reverb_c = SAFE_CAST(ReverbI, mReverb3DHead.getNext()); reverb_c != &mReverb3DHead; reverb_c = SAFE_CAST(ReverbI, reverb_c->getNext()))
|
|
{
|
|
float distance_gain, distance_coeff;
|
|
bool active;
|
|
|
|
reverb_c->getActive(&active);
|
|
|
|
if (active)
|
|
{
|
|
/*
|
|
Find listener-reverb distance gain
|
|
*/
|
|
reverb_c->calculateDistanceGain(&mListener[0].mPosition, &distance_gain, &distance_coeff);
|
|
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
/*
|
|
Find reverb occlusion gain of the listener-reverb path
|
|
*/
|
|
if (distance_gain > 0.0f)
|
|
{
|
|
float direct_occlusion, reverb_occlusion;
|
|
FMOD_RESULT result;
|
|
FMOD_VECTOR pos;
|
|
|
|
reverb_c->get3DAttributes(&pos, 0, 0);
|
|
|
|
result = mGeometryMgr.lineTestAll(&mListener[0].mPosition, &pos, &direct_occlusion, &reverb_occlusion);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
distance_gain *= (1.0f - reverb_occlusion);
|
|
distance_coeff *= (1.0f - reverb_occlusion);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Reverb modules - ones which have DSP : set the presence gain
|
|
*/
|
|
if (reverb_c->getMode() == FMOD_REVERB_PHYSICAL)
|
|
{
|
|
if (distance_gain != reverb_c->getGain())
|
|
{
|
|
reverb_c->setGain(distance_gain);
|
|
}
|
|
}
|
|
//
|
|
// Virtual reverbs - ones without DSP and just have properties and geometry : add to running average
|
|
//
|
|
else if (reverb_c->getMode() == FMOD_REVERB_VIRTUAL)
|
|
{
|
|
if (distance_coeff >= 0.001f)
|
|
{
|
|
FMOD_REVERB_PROPERTIES reverbprops;
|
|
|
|
FMOD_memset(&reverbprops, 0, sizeof(FMOD_REVERB_PROPERTIES));
|
|
|
|
reverb_c->getProperties(&reverbprops);
|
|
|
|
ReverbI::sumProps(&stdprops, &reverbprops, distance_coeff);
|
|
t += distance_coeff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
get3DReverbActive(&reverb3d_active);
|
|
|
|
if (reverb3d_active)
|
|
{
|
|
FMOD_REVERB_PROPERTIES qprops;
|
|
|
|
/*
|
|
Complement with ambient setting if there's not enough reverb presence
|
|
*/
|
|
if (t < 1.0f)
|
|
{
|
|
FMOD_REVERB_PROPERTIES ambience;
|
|
|
|
getReverbAmbientProperties(&ambience);
|
|
|
|
if (ambience.Environment == -1)
|
|
{
|
|
ambience.Room = -10000;
|
|
ReverbI::sumRoomProps(&stdprops, &ambience, 1.0f - t);
|
|
}
|
|
else
|
|
{
|
|
ReverbI::sumProps(&stdprops, &ambience, 1.0f - t);
|
|
}
|
|
|
|
t = 1.0f;
|
|
}
|
|
|
|
/*
|
|
Normalise the properties with the accumulated t value
|
|
*/
|
|
ReverbI::factorProps(&qprops, &stdprops, 1.0f/t);
|
|
set3DReverbProperties(&qprops, true);
|
|
}
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::setReverbAmbientProperties(FMOD_REVERB_PROPERTIES* prop)
|
|
{
|
|
if (!prop)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (prop->Environment != -1)
|
|
{
|
|
set3DReverbActive(true);
|
|
}
|
|
|
|
FMOD_memcpy(&mReverb3DAmbientProperties, prop, sizeof(FMOD_REVERB_PROPERTIES));
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getReverbAmbientProperties(FMOD_REVERB_PROPERTIES* prop)
|
|
{
|
|
if (!prop)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
FMOD_memcpy(prop, &mReverb3DAmbientProperties, sizeof(FMOD_REVERB_PROPERTIES));
|
|
|
|
return FMOD_OK;
|
|
}
|
|
#endif
|
|
|
|
unsigned int F_API System_GetDebugMode()
|
|
{
|
|
#ifdef FMOD_DEBUG
|
|
return (unsigned int )gGlobal->gDebugMode;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
[API]
|
|
[
|
|
[DESCRIPTION]
|
|
Callback for system events.
|
|
|
|
[PARAMETERS]
|
|
'system' Pointer to a system handle. Note this could be null if FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED is triggered from the EventSystem.
|
|
'type' The type of callback. Refer to FMOD_SYSTEM_CALLBACKTYPE.
|
|
'commanddata1' The first callback type specific data generated by the callback. See remarks for meaning.
|
|
'commanddata2' The second callback type specific data generated by the callback. See remarks for meaning.
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
<u>C++ Users</u>. Cast <b>FMOD_SYSTEM *</b> to <b>FMOD::System *</b> inside the callback and use as normal.<br>
|
|
<br>
|
|
<u>'commanddata1' and 'commanddata2' meanings.</u><br>
|
|
These 2 values are set by the callback depending on what is happening in the callback and the type of callback.<br>
|
|
<li><b>FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED</b><br>
|
|
<i>commanddata1</i>: Always 0.<br>
|
|
<i>commanddata2</i>: Always 0.<br>
|
|
<br>
|
|
<li><b>FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED</b><br>
|
|
<i>commanddata1</i>: A string (char*) which represents the file and line number of the allocation inside FMOD.<br>
|
|
<i>commanddata2</i>: The size (int) of the requested allocation.<br>
|
|
<br>
|
|
<li><b>FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED</b><br>
|
|
<i>commanddata1</i>: The handle of the created thread. See notes below for thread handle types<br>
|
|
<i>commanddata2</i>: A string (char*) which represents the name of the thread.<br>
|
|
<br>
|
|
<li><b>FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION</b><br>
|
|
<i>commanddata1</i>: Pointer to a FMOD::DSP object that was the target of the DSP connection.<br>
|
|
<i>commanddata2</i>: Pointer to a FMOD::DSP object that was the source of the DSP connection.<br>
|
|
<br>
|
|
<li><b>FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL</b><br>
|
|
<i>commanddata1</i>: Pointer to a FMOD::DSP object that was trying to exceed the DSP tree level maximum.<br>
|
|
<i>commanddata2</i>: 0.<br>
|
|
<br>
|
|
|
|
<b>Note!</b> For FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED, the user must call System::update for the callback to trigger! See FMOD_SYSTEM_CALLBACKTYPE for details.<br>
|
|
<br>
|
|
<b>Note!</b> For FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED, the handle that is returned (via commanddata1) is different on each platform. The types to cast to are as follows.<br>
|
|
<li>iPhone, Linux, Mac, Solaris : pthread_t
|
|
<li>PS2 : int
|
|
<li>PS3 : sys_ppu_thread_t
|
|
<li>PSP : PSPThreadWrapper. This is a custom struct you can define as typedef struct PSPThreadWrapper { SceUID id; int (*func)(void *param); void *param; };
|
|
<li>Wii : OSThread
|
|
<li>Win32, Win64, Xbox360 : HANDLE
|
|
<br>
|
|
<br>
|
|
<br>
|
|
Here is an example of a system callback.<br>
|
|
<br>
|
|
<PRE>
|
|
FMOD_RESULT F_CALLBACK systemcallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACKTYPE type, void *commanddata1, void *commanddata2)
|
|
{
|
|
<ul>FMOD::System *sys = (FMOD::System *)system;
|
|
<br>
|
|
switch (type)
|
|
{
|
|
<ul>case FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED:
|
|
{
|
|
<ul>int numdrivers;
|
|
<br>
|
|
printf("NOTE : FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED occured.\n");
|
|
<br>
|
|
sys->getNumDrivers(&numdrivers);
|
|
<br>
|
|
printf("Numdevices = %d\n", numdrivers);
|
|
break;
|
|
</ul>}
|
|
case FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED:
|
|
{
|
|
<ul>printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED occured.\n");
|
|
printf("%s.\n", commanddata1);
|
|
printf("%d bytes.\n", commanddata2);
|
|
break;
|
|
</ul>}
|
|
case FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED:
|
|
{
|
|
<ul>printf("NOTE : FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED occured.\n");
|
|
printf("Thread ID = %d\n", (int)commanddata1);
|
|
printf("Thread Name = %s\n", (char *)commanddata2);
|
|
break;
|
|
</ul>}
|
|
case FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION:
|
|
{
|
|
<ul>FMOD::DSP *source = (FMOD::DSP *)commanddata1;
|
|
FMOD::DSP *dest = (FMOD::DSP *)commanddata2;
|
|
<br>
|
|
printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION occured.\n");
|
|
if (source)
|
|
{
|
|
<ul>char name[256];
|
|
source->getInfo(name, 0,0,0,0);
|
|
printf("SOURCE = %s\n", name);
|
|
</ul>}
|
|
if (dest)
|
|
{
|
|
<ul>char name[256];
|
|
dest->getInfo(name, 0,0,0,0);
|
|
printf("DEST = %s\n", name);
|
|
</ul>}
|
|
break;
|
|
</ul>}
|
|
case FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL:
|
|
{
|
|
<ul>FMOD::DSP *source = (FMOD::DSP *)commanddata1;
|
|
<br>
|
|
printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL occured.\n");
|
|
if (source)
|
|
{
|
|
<ul>char name[256];
|
|
source->getInfo(name, 0,0,0,0);
|
|
printf("SOURCE = %s\n", name);
|
|
</ul>}
|
|
break;
|
|
</ul>}
|
|
</ul>}
|
|
<br>
|
|
return FMOD_OK;
|
|
</ul>}
|
|
</PRE>
|
|
<br>
|
|
[PLATFORMS]
|
|
Win32, Win64, Linux, Linux64, Macintosh, Xbox, Xbox360, PlayStation 2, GameCube, PlayStation Portable, PlayStation 3, Wii, Solaris
|
|
|
|
[SEE_ALSO]
|
|
System::setCallback
|
|
FMOD_SYSTEM_CALLBACKTYPE
|
|
System::update
|
|
]
|
|
*/
|
|
/*
|
|
FMOD_RESULT F_CALLBACK FMOD_SYSTEM_CALLBACK(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACKTYPE type, void* commanddata1, void* commanddata2)
|
|
{
|
|
#if 0
|
|
// here purely for documentation purposes.
|
|
#endif
|
|
}
|
|
*/
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getMemoryInfo(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details)
|
|
{
|
|
#ifdef FMOD_SUPPORT_MEMORYTRACKER
|
|
GETMEMORYINFO_IMPL
|
|
#else
|
|
return FMOD_ERR_UNIMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef FMOD_SUPPORT_MEMORYTRACKER
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT SystemI::getMemoryUsedImpl(MemoryTracker *tracker)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
|
|
#ifndef FMOD_STATICFORPLUGINS
|
|
LinkedListNode *node;
|
|
int count;
|
|
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, sizeof(*this));
|
|
|
|
for (node = mSoundListHead.getNext(); node != &mSoundListHead; node = node->getNext())
|
|
{
|
|
SoundI *soundi = (SoundI *)node;
|
|
CHECK_RESULT(soundi->getMemoryUsed(tracker));
|
|
}
|
|
|
|
if (gSoundListCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
if (mChannel)
|
|
{
|
|
for (count=0; count < mNumChannels; count++)
|
|
{
|
|
CHECK_RESULT(mChannel[count].getMemoryUsed(tracker));
|
|
}
|
|
}
|
|
|
|
if (mOutput && mOutput->mDescription.getmemoryused)
|
|
{
|
|
CHECK_RESULT(mOutput->mDescription.getmemoryused(mOutput, tracker));
|
|
}
|
|
|
|
if (mEmulated)
|
|
{
|
|
CHECK_RESULT(mEmulated->getMemoryUsed(tracker));
|
|
}
|
|
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
|
if (mDSPTempBuffMem)
|
|
{
|
|
int bufferchannels;
|
|
result = getSoftwareFormat(0, 0, &bufferchannels, 0, 0, 0);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, (mDSPBlockSize * (bufferchannels < mMaxInputChannels ? mMaxInputChannels : bufferchannels) * sizeof(float)) + 16);
|
|
}
|
|
|
|
for (count = 0; count < FMOD_DSP_MAXTREEDEPTH; count++)
|
|
{
|
|
if (mDSPMixBuff[count])
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, (mDSPBlockSize * (mMaxOutputChannels < mMaxInputChannels ? mMaxInputChannels : mMaxOutputChannels) * sizeof(float)) + 16);
|
|
}
|
|
}
|
|
|
|
CHECK_RESULT(mDSPConnectionPool.getMemoryUsed(tracker));
|
|
|
|
if (mDSPCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
if (mDSPLockCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
if (mDSPConnectionCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
#ifdef FMOD_NEED_DSPCODECPOOLINITCRIT
|
|
if (mDSPCodecPoolInitCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
#endif
|
|
|
|
// FMOD_OS_GetMemoryUsed(tracker);
|
|
|
|
{
|
|
LinkedListNode *current;
|
|
|
|
current = FMOD::gGlobal->gFileThreadHead.getNext();
|
|
while (current != &FMOD::gGlobal->gFileThreadHead)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_FILE, sizeof(FileThread));
|
|
tracker->add(false, FMOD_MEMBITS_FILE, gSizeofSemaphore);
|
|
tracker->add(false, FMOD_MEMBITS_FILE, gSizeofCriticalSection);
|
|
current = current->getNext();
|
|
}
|
|
}
|
|
|
|
if (mDSPSoundCard)
|
|
{
|
|
#ifdef PLATFORM_PS3
|
|
CHECK_RESULT(((DSPI *)mDSPSoundCard)->getMemoryUsed(tracker));
|
|
#else
|
|
CHECK_RESULT(((DSPSoundCard *)mDSPSoundCard)->getMemoryUsed(tracker));
|
|
#endif
|
|
}
|
|
|
|
if (mDSPChannelGroupTarget)
|
|
{
|
|
CHECK_RESULT(mDSPChannelGroupTarget->getMemoryUsed(tracker));
|
|
}
|
|
|
|
if (mSoftware)
|
|
{
|
|
CHECK_RESULT(mSoftware->getMemoryUsed(tracker));
|
|
}
|
|
#endif
|
|
|
|
if (mPluginFactory)
|
|
{
|
|
CHECK_RESULT(mPluginFactory->getMemoryUsed(tracker));
|
|
}
|
|
|
|
for (node = mChannelGroupHead.getNext(); node != &mChannelGroupHead; node = node->getNext())
|
|
{
|
|
ChannelGroupI *channelgroupi = (ChannelGroupI *)node;
|
|
CHECK_RESULT(channelgroupi->getMemoryUsed(tracker));
|
|
}
|
|
|
|
if (mSoundGroup)
|
|
{
|
|
CHECK_RESULT(mSoundGroup->getMemoryUsed(tracker));
|
|
}
|
|
|
|
// MemSingleton mMultiSubSampleLockBuffer;
|
|
|
|
if (mMultiSubSampleLockBufferCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
if (FMOD::gGlobal->gFileCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
if (FMOD::gGlobal->gAsyncCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
CHECK_RESULT(mSpeakerLevelsPool.getMemoryUsed(tracker));
|
|
CHECK_RESULT(mHistoryBufferPool.getMemoryUsed(tracker));
|
|
|
|
#ifdef FMOD_SUPPORT_STREAMING
|
|
// LinkedListNode mStreamListSoundHead;
|
|
CHECK_RESULT(mStreamThread.getMemoryUsed(tracker));
|
|
|
|
if (mStreamRealchanCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
if (mStreamUpdateCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
|
|
if (mStreamListCrit)
|
|
{
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, gSizeofCriticalSection);
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
CHECK_RESULT(mDSPCodecPool_MPEG.getMemoryUsed(tracker));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
CHECK_RESULT(mDSPCodecPool_ADPCM.getMemoryUsed(tracker));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_XMA
|
|
CHECK_RESULT(mDSPCodecPool_XMA.getMemoryUsed(tracker));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_CELT
|
|
CHECK_RESULT(mDSPCodecPool_CELT.getMemoryUsed(tracker));
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_RAWCODEC
|
|
CHECK_RESULT(mDSPCodecPool_RAW.getMemoryUsed(tracker));
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_GEOMETRY
|
|
// GeometryI *mGeometryList;
|
|
// GeometryMgr mGeometryMgr;
|
|
#endif
|
|
|
|
CHECK_RESULT(mReverbGlobal.getMemoryUsed(tracker));
|
|
|
|
#ifdef FMOD_SUPPORT_MULTIREVERB
|
|
CHECK_RESULT(mReverb3D.getMemoryUsed(tracker));
|
|
|
|
for (node = mReverb3DHead.getNext(); node != &mReverb3DHead; node = node->getNext())
|
|
{
|
|
CHECK_RESULT(((ReverbI *)node)->getMemoryUsed(tracker));
|
|
}
|
|
#endif
|
|
|
|
#ifdef FMOD_SUPPORT_PROFILE
|
|
if (gGlobal->gProfile)
|
|
{
|
|
CHECK_RESULT(gGlobal->gProfile->getMemoryUsed(tracker));
|
|
}
|
|
#endif
|
|
|
|
|
|
tracker->add(false, FMOD_MEMBITS_SYSTEM, FMOD_OS_GetMemoryUsed());
|
|
|
|
#ifdef FMOD_SUPPORT_PROFILE
|
|
if (gGlobal->gProfile)
|
|
{
|
|
CHECK_RESULT(gGlobal->gProfile->getMemoryUsed(tracker));
|
|
}
|
|
#endif
|
|
|
|
#endif // #ifndef FMOD_STATICFORPLUGINS
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|