#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_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_cleanup; Metadata *metadata = 0; File *file = 0; AutoFree file_cleanup; AutoClose 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 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_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 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 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 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 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.
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.
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).
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.
[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 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 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] C++ Users. Cast FMOD_SYSTEM * to FMOD::System * inside the callback and use as normal.

'commanddata1' and 'commanddata2' meanings.
These 2 values are set by the callback depending on what is happening in the callback and the type of callback.
  • FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED
    commanddata1: Always 0.
    commanddata2: Always 0.

  • FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED
    commanddata1: A string (char*) which represents the file and line number of the allocation inside FMOD.
    commanddata2: The size (int) of the requested allocation.

  • FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED
    commanddata1: The handle of the created thread. See notes below for thread handle types
    commanddata2: A string (char*) which represents the name of the thread.

  • FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION
    commanddata1: Pointer to a FMOD::DSP object that was the target of the DSP connection.
    commanddata2: Pointer to a FMOD::DSP object that was the source of the DSP connection.

  • FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL
    commanddata1: Pointer to a FMOD::DSP object that was trying to exceed the DSP tree level maximum.
    commanddata2: 0.

    Note! For FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED, the user must call System::update for the callback to trigger! See FMOD_SYSTEM_CALLBACKTYPE for details.

    Note! 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.
  • iPhone, Linux, Mac, Solaris : pthread_t
  • PS2 : int
  • PS3 : sys_ppu_thread_t
  • PSP : PSPThreadWrapper. This is a custom struct you can define as typedef struct PSPThreadWrapper { SceUID id; int (*func)(void *param); void *param; };
  • Wii : OSThread
  • Win32, Win64, Xbox360 : HANDLE


    Here is an example of a system callback.

        FMOD_RESULT F_CALLBACK systemcallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACKTYPE type, void *commanddata1, void *commanddata2)
        {
        
      FMOD::System *sys = (FMOD::System *)system;
      switch (type) {
        case FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED: {
          int numdrivers;
          printf("NOTE : FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED occured.\n");
          sys->getNumDrivers(&numdrivers);
          printf("Numdevices = %d\n", numdrivers); break;
        } case FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED: {
          printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED occured.\n"); printf("%s.\n", commanddata1); printf("%d bytes.\n", commanddata2); break;
        } case FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED: {
          printf("NOTE : FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED occured.\n"); printf("Thread ID = %d\n", (int)commanddata1); printf("Thread Name = %s\n", (char *)commanddata2); break;
        } case FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION: {
          FMOD::DSP *source = (FMOD::DSP *)commanddata1; FMOD::DSP *dest = (FMOD::DSP *)commanddata2;
          printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION occured.\n"); if (source) {
            char name[256]; source->getInfo(name, 0,0,0,0); printf("SOURCE = %s\n", name);
          } if (dest) {
            char name[256]; dest->getInfo(name, 0,0,0,0); printf("DEST = %s\n", name);
          } break;
        } case FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL: {
          FMOD::DSP *source = (FMOD::DSP *)commanddata1;
          printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_BADDSPLEVEL occured.\n"); if (source) {
            char name[256]; source->getInfo(name, 0,0,0,0); printf("SOURCE = %s\n", name);
          } break;
        }
      }
      return FMOD_OK;
    }

    [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 }