2224 lines
53 KiB
C++
Executable file
2224 lines
53 KiB
C++
Executable file
#include "fmod_settings.h"
|
|
|
|
#ifdef FMOD_SUPPORT_OPENAL
|
|
|
|
#include "fmod_output_openal.h"
|
|
#include "fmod_sample_openal.h"
|
|
#include "fmod_channel_openal.h"
|
|
#include "fmod_channel_openal_eax2.h"
|
|
#include "fmod_channel_openal_eax3.h"
|
|
#include "fmod_channel_openal_eax4.h"
|
|
#include "fmod_channel_openal_eax5.h"
|
|
|
|
namespace FMOD
|
|
{
|
|
|
|
#define SPEED_OF_SOUND 340.0f // Same as A3D
|
|
|
|
#define EXIT_ON_CONDITION(condition, error) \
|
|
if (condition) { close(); return error; }
|
|
#define SAFE_RELEASE(pPointer) \
|
|
if ((pPointer) != NULL) { (pPointer)->release(); (pPointer) = NULL; }
|
|
#define SAFE_FREE(pPointer) \
|
|
if ((pPointer) != NULL) { FMOD_Memory_Free((pPointer)); (pPointer) = NULL; }
|
|
|
|
FMOD_OUTPUT_DESCRIPTION_EX openaloutput;
|
|
|
|
#ifdef PLUGIN_EXPORTS
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*
|
|
FMODGetOutputDescription is mandantory for every fmod plugin.
|
|
This is the symbol the registerplugin function searches for.
|
|
Must be declared with F_API to make it export as stdcall.
|
|
*/
|
|
F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION_EX * F_API FMODGetOutputDescriptionEx()
|
|
{
|
|
return OutputOpenAL::getDescriptionEx();
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* PLUGIN_EXPORTS */
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Assign methods for various FMOD output hooks required by output type
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OUTPUT_DESCRIPTION_EX
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_OUTPUT_DESCRIPTION_EX *OutputOpenAL::getDescriptionEx()
|
|
{
|
|
openaloutput.name = "FMOD OpenAL Output";
|
|
openaloutput.version = 0x00010100;
|
|
openaloutput.mType = FMOD_OUTPUTTYPE_OPENAL;
|
|
openaloutput.mSize = sizeof(OutputOpenAL);
|
|
openaloutput.polling = true;
|
|
|
|
openaloutput.getnumdrivers = &OutputOpenAL::getNumDriversCallback;
|
|
openaloutput.getdrivername = &OutputOpenAL::getDriverNameCallback;
|
|
openaloutput.getdrivercapsex2 = &OutputOpenAL::getDriverCapsExCallback;
|
|
openaloutput.init = &OutputOpenAL::initCallback;
|
|
openaloutput.close = &OutputOpenAL::closeCallback;
|
|
openaloutput.start = &OutputOpenAL::startCallback;
|
|
openaloutput.stop = &OutputOpenAL::stopCallback;
|
|
openaloutput.gethandle = &OutputOpenAL::getHandleCallback;
|
|
openaloutput.update = &OutputOpenAL::updateCallback;
|
|
openaloutput.postmixcallback = &OutputOpenAL::postMixCallback;
|
|
openaloutput.getposition = &OutputOpenAL::getPositionCallback;
|
|
openaloutput.lock = &OutputOpenAL::lockCallback;
|
|
openaloutput.unlock = &OutputOpenAL::unlockCallback;
|
|
openaloutput.createsample = &OutputOpenAL::createSampleCallback;
|
|
openaloutput.reverb_setproperties = &OutputOpenAL::setReverbPropertiesCallback;
|
|
openaloutput.getsamplemaxchannels = &OutputOpenAL::getSampleMaxChannelsCallback;
|
|
|
|
return &openaloutput;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
OpenAL Constructor
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
OutputOpenAL::OutputOpenAL()
|
|
{
|
|
mDLLInitialised = false;
|
|
mEnumerated = false;
|
|
mInitialised = false;
|
|
mNumDrivers = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Load the OpenAL DLL
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::registerDLL()
|
|
{
|
|
if (mDLLInitialised)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
if (!LoadOALLibrary(NULL, &mOALFnTable))
|
|
{
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
mDLLInitialised = true;
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Enumerate a list of OpenAL devices
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
OpenAL devices list extracts device names for native devices i.e. Creative, the
|
|
generic devices use whatever is currently the default
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::enumerate()
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
char *deviceList = NULL;
|
|
ALCdevice *device = NULL;
|
|
ALCcontext *context = NULL;
|
|
|
|
/*
|
|
Set up gGlobal - for debug / file / memory access by this plugin.
|
|
*/
|
|
Plugin::init();
|
|
|
|
// Check if already enumerated
|
|
if (mEnumerated)
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::enumerate", "Enumerating...\n"));
|
|
|
|
// Do one time initialisation code
|
|
if (!mSetupOnce)
|
|
{
|
|
// Ensure the DLL is loaded
|
|
result = registerDLL();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
mSetupOnce = true;
|
|
}
|
|
|
|
// Clean up any driver enumeration (may have enumerated once already)
|
|
for (int i = 0; i < mNumDrivers; i++)
|
|
{
|
|
SAFE_FREE(mDriverNames[i]);
|
|
}
|
|
mNumDrivers = 0;
|
|
|
|
// Get a list of all devices, each device is NULL terminated, list is double NULL terminated
|
|
deviceList = (char *)mOALFnTable.alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
|
while (*deviceList != NULL && mNumDrivers < FMOD_OUTPUT_MAXDRIVERS)
|
|
{
|
|
// Open the device
|
|
device = mOALFnTable.alcOpenDevice(deviceList);
|
|
if (device == NULL)
|
|
{
|
|
// Couldn't open device, move onto the next
|
|
deviceList += FMOD_strlen(deviceList) + 1;
|
|
continue;
|
|
}
|
|
|
|
// Create a context for the device
|
|
context = mOALFnTable.alcCreateContext(device, NULL);
|
|
if (context == NULL)
|
|
{
|
|
// Couldn't create context, move onto the next
|
|
mOALFnTable.alcCloseDevice(device);
|
|
deviceList += FMOD_strlen(deviceList) + 1;
|
|
continue;
|
|
}
|
|
|
|
// Make context active
|
|
mOALFnTable.alcGetError(device);
|
|
mOALFnTable.alcMakeContextCurrent(context);
|
|
if (mOALFnTable.alcGetError(device) != ALC_NO_ERROR)
|
|
{
|
|
// Couldn't set context as current, move onto the next
|
|
mOALFnTable.alcDestroyContext(context);
|
|
mOALFnTable.alcCloseDevice(device);
|
|
deviceList += FMOD_strlen(deviceList) + 1;
|
|
continue;
|
|
}
|
|
|
|
// Get the version of OpenAL the device supports
|
|
int majorVersion = 0;
|
|
int minorVersion = 0;
|
|
mOALFnTable.alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &majorVersion);
|
|
mOALFnTable.alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &minorVersion);
|
|
mOALFnTable.alcGetError(device);
|
|
|
|
// Only add devices that are OpenAL 1.1 compliant or greater
|
|
if (majorVersion >= 1 && minorVersion >= 1)
|
|
{
|
|
// Get the name of this device (may be different to name in above list due to fallback support)
|
|
const char *deviceName = mOALFnTable.alcGetString(device, ALC_DEVICE_SPECIFIER);
|
|
bool dupeFound = false;
|
|
|
|
// Check the list if this device is a duplicate
|
|
for (int i = 0; i < mNumDrivers; i++)
|
|
{
|
|
if (FMOD_strcmp(deviceName, mDriverNames[i]) == 0)
|
|
{
|
|
// Found a device already in the list, ignore it (e.g. may have fallen back from hardware to software)
|
|
dupeFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Only add if not a duplicate entry
|
|
if (!dupeFound)
|
|
{
|
|
// Allocate memory for the device name
|
|
mDriverNames[mNumDrivers] = (char *)FMOD_Memory_Calloc(FMOD_strlen(deviceName) + 1);
|
|
if (mDriverNames[mNumDrivers] == NULL)
|
|
{
|
|
mOALFnTable.alcMakeContextCurrent(NULL);
|
|
mOALFnTable.alcDestroyContext(context);
|
|
mOALFnTable.alcCloseDevice(device);
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
// Add the new device to the list of useable enumerated devices
|
|
FMOD_strcpy(mDriverNames[mNumDrivers], deviceName);
|
|
mNumDrivers++;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::enumerate", "Found Driver: \"%s\"\n", deviceName));
|
|
}
|
|
}
|
|
|
|
// Clean up and move onto the next device in the list
|
|
mOALFnTable.alcMakeContextCurrent(NULL);
|
|
mOALFnTable.alcDestroyContext(context);
|
|
mOALFnTable.alcCloseDevice(device);
|
|
deviceList += FMOD_strlen(deviceList) + 1;
|
|
}
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::enumerate", "Done.\n"));
|
|
mEnumerated = true;
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Return the number of OpenAL devices
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::getNumDrivers(int *numdrivers)
|
|
{
|
|
if (!numdrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (!mEnumerated)
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
result = enumerate();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
*numdrivers = mNumDrivers;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Return the name of an OpenAL device given its index
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::getDriverName(int driver, char *name, int namelen)
|
|
{
|
|
if (!mEnumerated)
|
|
{
|
|
FMOD_RESULT result;
|
|
|
|
result = enumerate();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (driver < 0 || driver >= mNumDrivers)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (name && namelen >= 1)
|
|
{
|
|
FMOD_strncpy(name, mDriverNames[driver], namelen - 1);
|
|
name[namelen - 1] = 0;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Return the capabilities of the OpenAL device at the given index
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::getDriverCapsEx(int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels)
|
|
{
|
|
ALCdevice *device = NULL;
|
|
ALCcontext *context = NULL;
|
|
FMOD_RESULT result = FMOD_OK;
|
|
|
|
if (!mEnumerated)
|
|
{
|
|
result = enumerate();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Check if using the software mode (fallback from hardware is already considered in the enumeration)
|
|
if (FMOD_strcmp(mDriverNames[id], "Generic Software") == 0 ||
|
|
FMOD_strcmp(mDriverNames[id], "DirectSound") == 0 ||
|
|
FMOD_strcmp(mDriverNames[id], "MMSYSTEM") == 0)
|
|
{
|
|
*caps |= FMOD_CAPS_HARDWARE_EMULATED;
|
|
}
|
|
// Must be using hardware through DSound or a native driver
|
|
else
|
|
{
|
|
*caps |= FMOD_CAPS_HARDWARE;
|
|
}
|
|
|
|
// OpenAL only supports PCM8 and PCM16, but we force PCM16
|
|
*caps |= FMOD_CAPS_OUTPUT_FORMAT_PCM16;
|
|
|
|
// OpenAL frequency is forced to 44100
|
|
*minfrequency = *maxfrequency = 44100;
|
|
|
|
/*
|
|
Check for EAX support
|
|
*/
|
|
#ifdef FMOD_SUPPORT_EAX
|
|
// Open the selected device
|
|
device = mOALFnTable.alcOpenDevice(mDriverNames[id]);
|
|
if (!device)
|
|
{
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Create a context for the device
|
|
context = mOALFnTable.alcCreateContext(device, NULL);
|
|
if (!context)
|
|
{
|
|
mOALFnTable.alcCloseDevice(device);
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Make context active
|
|
mOALFnTable.alcGetError(device);
|
|
mOALFnTable.alcMakeContextCurrent(context);
|
|
if (mOALFnTable.alcGetError(device) != ALC_NO_ERROR)
|
|
{
|
|
mOALFnTable.alcDestroyContext(context);
|
|
mOALFnTable.alcCloseDevice(device);
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Determine highest EAX version supported
|
|
if (mOALFnTable.alIsExtensionPresent("EAX5.0"))
|
|
{
|
|
*caps |= FMOD_CAPS_REVERB_EAX5;
|
|
|
|
result = getSpeakerModeEAX5(controlpanelspeakermode);
|
|
if (result != FMOD_OK)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_WARNING, __FILE__, __LINE__, "OutputOpenAL::getDriverCapsEx", "Could not get speaker mode, defaulting to Stereo\n"));
|
|
*controlpanelspeakermode = FMOD_SPEAKERMODE_STEREO;
|
|
}
|
|
mNumHwChannels = 128;
|
|
}
|
|
else if (mOALFnTable.alIsExtensionPresent("EAX4.0"))
|
|
{
|
|
*caps |= FMOD_CAPS_REVERB_EAX4;
|
|
mNumHwChannels = 64;
|
|
}
|
|
else if (mOALFnTable.alIsExtensionPresent("EAX3.0"))
|
|
{
|
|
*caps |= FMOD_CAPS_REVERB_EAX3;
|
|
mNumHwChannels = 64;
|
|
}
|
|
else if (mOALFnTable.alIsExtensionPresent("EAX2.0"))
|
|
{
|
|
*caps |= FMOD_CAPS_REVERB_EAX2;
|
|
mNumHwChannels = 32;
|
|
}
|
|
else
|
|
{
|
|
mNumHwChannels = 8;
|
|
}
|
|
|
|
// Cleanup OpenAL device
|
|
mOALFnTable.alcMakeContextCurrent(NULL);
|
|
mOALFnTable.alcDestroyContext(context);
|
|
mOALFnTable.alcCloseDevice(device);
|
|
#endif
|
|
|
|
*num2dchannels = mNumHwChannels;
|
|
*num3dchannels = mNumHwChannels;
|
|
*totalchannels = mNumHwChannels;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Initialise EAX for use by OpenAL
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::initEAX()
|
|
{
|
|
#ifdef FMOD_SUPPORT_EAX
|
|
// Determine supported version of EAX
|
|
if (mOALFnTable.alIsExtensionPresent("EAX5.0"))
|
|
{
|
|
mReverbVersion = REVERB_VERSION_EAX5;
|
|
mNumHwChannels = 128;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX version 5.0 detected\n"));
|
|
}
|
|
else if (mOALFnTable.alIsExtensionPresent("EAX4.0"))
|
|
{
|
|
mReverbVersion = REVERB_VERSION_EAX4;
|
|
mNumHwChannels = 60;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX version 4.0 detected\n"));
|
|
}
|
|
else if (mOALFnTable.alIsExtensionPresent("EAX3.0"))
|
|
{
|
|
mReverbVersion = REVERB_VERSION_EAX3;
|
|
mNumHwChannels = 60;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX version 3.0 detected\n"));
|
|
}
|
|
else if (mOALFnTable.alIsExtensionPresent("EAX2.0"))
|
|
{
|
|
mReverbVersion = REVERB_VERSION_EAX2;
|
|
mNumHwChannels = 30;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX version 2.0 detected\n"));
|
|
}
|
|
else
|
|
{
|
|
// Should not get this far, software mode OpenAL emulates EAX2
|
|
mReverbVersion = REVERB_VERSION_NONE;
|
|
mNumHwChannels = 8;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "No EAX version detected"));
|
|
}
|
|
|
|
// Load EAX Get and Set functions
|
|
mEAXGet = (EAXGet)mOALFnTable.alGetProcAddress("EAXGet");
|
|
mEAXSet = (EAXSet)mOALFnTable.alGetProcAddress("EAXSet");
|
|
if (!mEAXGet || !mEAXSet)
|
|
{
|
|
mReverbVersion = REVERB_VERSION_NONE;
|
|
}
|
|
|
|
// Determine if board with XRAM
|
|
if (mOALFnTable.alIsExtensionPresent("EAX-RAM"))
|
|
{
|
|
// Load EAX GetBufferMode and SetBufferMode functions
|
|
mEAXGetBufferMode = (EAXGetBufferMode)mOALFnTable.alGetProcAddress("EAXGetBufferMode");
|
|
mEAXSetBufferMode = (EAXSetBufferMode)mOALFnTable.alGetProcAddress("EAXSetBufferMode");
|
|
if (!mEAXGetBufferMode || !mEAXSetBufferMode)
|
|
{
|
|
// Will fail on none XFi XRAM cards
|
|
}
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::initEAX", "EAX-RAM detected\n"));
|
|
}
|
|
|
|
// EAX5.0 Requires you to setup a session before making any EAX5.0 calls
|
|
if (mReverbVersion == REVERB_VERSION_EAX5)
|
|
{
|
|
setupSessionEAX5();
|
|
}
|
|
#else
|
|
mReverbVersion = REVERB_VERSION_NONE;
|
|
mNumHwChannels = 8;
|
|
#endif
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Initialise OpenAL output
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::init(int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
int formatBits = 0;
|
|
mDevice = NULL;
|
|
mContext = NULL;
|
|
mBufferData = NULL;
|
|
mNumChannels = 0;
|
|
mChannels = NULL;
|
|
mNumSources = 0;
|
|
mSources = NULL;
|
|
mMixerReverbDisabled = false;
|
|
mEAXSet = NULL;
|
|
mEAXGet = NULL;
|
|
mEAXSetBufferMode = NULL;
|
|
mEAXGetBufferMode = NULL;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Initialising...\n"));
|
|
|
|
if (!mEnumerated)
|
|
{
|
|
result = enumerate();
|
|
EXIT_ON_CONDITION(result != FMOD_OK, result);
|
|
}
|
|
|
|
if (mNumDrivers <= 0)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "No sound devices with support for OpenAL v1.1\n"));
|
|
close();
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Use provided number of channels
|
|
mOutputChannels = outputchannels;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Using provided number of channels: %d\n", mOutputChannels));
|
|
// Override specified output rate
|
|
mRate = *outputrate = 44100;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Forcing sample rate: 44100\n"));
|
|
// Override specified output format
|
|
mFormat = *outputformat = FMOD_SOUND_FORMAT_PCM16;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Forcing output format: PCM16\n"));
|
|
|
|
// Get the number of bits per sample
|
|
result = SoundI::getBitsFromFormat(mFormat, &formatBits);
|
|
EXIT_ON_CONDITION(result != FMOD_OK, result);
|
|
// Set buffer size
|
|
mNumBuffers = dspnumbuffers;
|
|
mBufferLength = dspbufferlength;
|
|
mBufferLengthBytes = mBufferLength * mOutputChannels * (formatBits / 8);
|
|
|
|
// Open the selected device
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Opening device\n"));
|
|
mDevice = mOALFnTable.alcOpenDevice(mDriverNames[selecteddriver]);
|
|
if (!mDevice)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "Couldn't open device: %s", mDriverNames[selecteddriver]));
|
|
close();
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Create a context for the device
|
|
mContext = mOALFnTable.alcCreateContext(mDevice, NULL);
|
|
if (!mContext)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "Couldn't create context for device: %s", mDriverNames[selecteddriver]));
|
|
close();
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Make context active
|
|
mOALFnTable.alcGetError(mDevice);
|
|
mOALFnTable.alcMakeContextCurrent(mContext);
|
|
if (mOALFnTable.alcGetError(mDevice) != ALC_NO_ERROR)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "Couldn't make context current for device: %s", mDriverNames[selecteddriver]));
|
|
close();
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Set speed of sound
|
|
mOALFnTable.alSpeedOfSound(SPEED_OF_SOUND);
|
|
if (mOALFnTable.alcGetError(mDevice) != ALC_NO_ERROR)
|
|
{
|
|
FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "Couldn't set speed of sound to %.02f", SPEED_OF_SOUND));
|
|
close();
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Initialised EAX
|
|
result = initEAX();
|
|
EXIT_ON_CONDITION(result != FMOD_OK, result);
|
|
|
|
// Clear Error Codes
|
|
mOALFnTable.alGetError();
|
|
mOALFnTable.alcGetError(mDevice);
|
|
|
|
// Limit hardware channels by the user set max
|
|
if (mNumHwChannels > mSystem->mMaxHardwareChannels3D)
|
|
{
|
|
mNumHwChannels = mSystem->mMaxHardwareChannels3D;
|
|
}
|
|
// Reserve hardware channels for the FMOD software mixer
|
|
mNumChannels = mNumHwChannels - mOutputChannels;
|
|
// Shouldn't go negative, mNumHwChannels min is set at 8, but just in case
|
|
if (mNumChannels < 0)
|
|
{
|
|
close();
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// Allocate memory for mix buffer
|
|
mBufferData = (char *)FMOD_Memory_Calloc(mNumBuffers * mBufferLengthBytes);
|
|
EXIT_ON_CONDITION(mBufferData == NULL, FMOD_ERR_MEMORY);
|
|
|
|
// Allocate memory for OpenAL sources
|
|
mSources = (SourceOpenAL *)FMOD_Memory_Calloc(mNumHwChannels * sizeof(SourceOpenAL));
|
|
EXIT_ON_CONDITION(mSources == NULL, FMOD_ERR_MEMORY);
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Initialising device with %d channels\n", mNumHwChannels));
|
|
|
|
/*
|
|
Create OpenAL sources for each hardware channel
|
|
*/
|
|
// Clear errors
|
|
mOALFnTable.alGetError();
|
|
do
|
|
{
|
|
mOALFnTable.alGenSources(1, &mSources[mNumSources].sid);
|
|
if (mOALFnTable.alGetError() != AL_NO_ERROR)
|
|
{
|
|
break; // Could not create OpenAL source
|
|
}
|
|
|
|
// Assign this source as currently unused
|
|
mSources[mNumSources].used = false;
|
|
|
|
// Allocate space for the buffer IDs
|
|
mSources[mNumSources].bid = (ALuint *)FMOD_Memory_Calloc(mNumBuffers * sizeof(ALuint));
|
|
EXIT_ON_CONDITION(mSources[mNumSources].bid == NULL, FMOD_ERR_MEMORY);
|
|
|
|
// Generate buffers for each source
|
|
for (int i = 0; i < mNumBuffers; i++)
|
|
{
|
|
mOALFnTable.alGenBuffers(1, &mSources[mNumSources].bid[i]);
|
|
if (mOALFnTable.alGetError() != AL_NO_ERROR)
|
|
{
|
|
break; // Could not create OpenAL buffer
|
|
}
|
|
|
|
if (mEAXSetBufferMode)
|
|
{
|
|
mEAXSetBufferMode(1, &mSources[mNumSources].bid[i], mOALFnTable.alGetEnumValue("AL_STORAGE_ACCESSIBLE"));
|
|
if (mOALFnTable.alGetError() != AL_NO_ERROR)
|
|
{
|
|
break; // Could not change OpenAL buffer mode
|
|
}
|
|
}
|
|
|
|
mOALFnTable.alBufferData(mSources[mNumSources].bid[i], AL_FORMAT_MONO16, mBufferData, mBufferLength * (formatBits / 8), mRate);
|
|
if (mOALFnTable.alGetError() != AL_NO_ERROR)
|
|
{
|
|
break; // Could not fill OpenAL buffer
|
|
}
|
|
}
|
|
|
|
mNumSources++;
|
|
|
|
} while (mNumSources < mNumHwChannels);
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Initialised device with %d channels\n", mNumSources));
|
|
|
|
// Update mNumChannels incase enough sources couldn't be created
|
|
mNumChannels = mNumSources - mOutputChannels;
|
|
// Ensure enough sources were created for the FMOD software mixer
|
|
if (mNumSources < mOutputChannels)
|
|
{
|
|
return FMOD_ERR_OUTPUT_INIT;
|
|
}
|
|
|
|
// FMOD software mixer uses the top OpenAL sources for output
|
|
mMixerSourceOffset = mNumSources - mOutputChannels;
|
|
|
|
// Set up the hardware channel pool
|
|
mChannelPool3D = FMOD_Object_Alloc(ChannelPool);
|
|
EXIT_ON_CONDITION(mChannelPool3D == NULL, FMOD_ERR_MEMORY);
|
|
result = mChannelPool3D->init(mSystem, this, mNumChannels);
|
|
EXIT_ON_CONDITION(result != FMOD_OK, result);
|
|
|
|
// Allocate channel memory
|
|
switch (mReverbVersion)
|
|
{
|
|
#ifdef FMOD_SUPPORT_EAX
|
|
case REVERB_VERSION_EAX5:
|
|
{
|
|
mChannels = (ChannelOpenALEAX5 *)FMOD_Memory_Calloc(sizeof(ChannelOpenALEAX5) * mNumChannels);
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX4:
|
|
{
|
|
mChannels = (ChannelOpenALEAX4 *)FMOD_Memory_Calloc(sizeof(ChannelOpenALEAX4) * mNumChannels);
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX3:
|
|
{
|
|
mChannels = (ChannelOpenALEAX3 *)FMOD_Memory_Calloc(sizeof(ChannelOpenALEAX3) * mNumChannels);
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX2:
|
|
{
|
|
mChannels = (ChannelOpenALEAX2 *)FMOD_Memory_Calloc(sizeof(ChannelOpenALEAX2) * mNumChannels);
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
{
|
|
mChannels = (ChannelOpenAL *)FMOD_Memory_Calloc(sizeof(ChannelOpenAL) * mNumChannels);
|
|
break;
|
|
}
|
|
}
|
|
EXIT_ON_CONDITION(mChannels == NULL, FMOD_ERR_MEMORY);
|
|
|
|
// Create the channels
|
|
for (int i = 0; i < mNumChannels; i++)
|
|
{
|
|
switch (mReverbVersion)
|
|
{
|
|
#ifdef FMOD_SUPPORT_EAX
|
|
case REVERB_VERSION_EAX5:
|
|
{
|
|
new (&mChannels[i]) ChannelOpenALEAX5;
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX4:
|
|
{
|
|
new (&mChannels[i]) ChannelOpenALEAX4;
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX3:
|
|
{
|
|
new (&mChannels[i]) ChannelOpenALEAX3;
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX2:
|
|
{
|
|
new (&mChannels[i]) ChannelOpenALEAX2;
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
{
|
|
new (&mChannels[i]) ChannelOpenAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Apply the channel to the channel pool
|
|
result = mChannelPool3D->setChannel(i, &mChannels[i]);
|
|
EXIT_ON_CONDITION(result != FMOD_OK, result);
|
|
}
|
|
|
|
// 2D channel pool is the same as the 3D pool
|
|
mChannelPool = mChannelPool3D;
|
|
|
|
// Get format enum for mix buffer
|
|
switch (mOutputChannels)
|
|
{
|
|
case 1:
|
|
mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_MONO16");
|
|
break;
|
|
case 2:
|
|
mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_STEREO16");
|
|
break;
|
|
case 4:
|
|
mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_QUAD16");
|
|
break;
|
|
case 6:
|
|
mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_51CHN16");
|
|
break;
|
|
case 7:
|
|
mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_61CHN16");
|
|
break;
|
|
case 8:
|
|
mFormatOAL = mOALFnTable.alGetEnumValue("AL_FORMAT_71CHN16");
|
|
break;
|
|
default:
|
|
mFormatOAL = 0;
|
|
FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::init", "alGetEnumValue failed\n"));
|
|
return FMOD_ERR_OUTPUT_DRIVERCALL;
|
|
}
|
|
|
|
mInitialised = true;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::init", "Done.\n"));
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Shutdown and cleanup resources created
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::close()
|
|
{
|
|
/*
|
|
Set up gGlobal - for debug / file / memory access by this plugin.
|
|
*/
|
|
Plugin::init();
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Closing...\n"));
|
|
|
|
// Release the channel pool, (both pools are the same)
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Free channel pool\n"));
|
|
SAFE_RELEASE(mChannelPool);
|
|
mChannelPool = mChannelPool3D = NULL;
|
|
// Free the channel data
|
|
SAFE_FREE(mChannels);
|
|
|
|
// Cleanup OpenAL sources
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Free OpenAL sources and buffers\n"));
|
|
if (mSources)
|
|
{
|
|
for (int i = 0; i < mNumSources; i++)
|
|
{
|
|
mOALFnTable.alDeleteSources(1, &mSources[i].sid);
|
|
mOALFnTable.alDeleteBuffers(mNumBuffers, mSources[i].bid);
|
|
SAFE_FREE(mSources[i].bid);
|
|
}
|
|
SAFE_FREE(mSources);
|
|
}
|
|
|
|
// Clean up the mix buffer
|
|
SAFE_FREE(mBufferData)
|
|
|
|
// Cleanup OpenAL context and device
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Closing OpenAL device\n"));
|
|
if (mContext)
|
|
{
|
|
mOALFnTable.alcMakeContextCurrent(NULL);
|
|
mOALFnTable.alcDestroyContext(mContext);
|
|
mContext = NULL;
|
|
}
|
|
if (mDevice)
|
|
{
|
|
mOALFnTable.alcCloseDevice(mDevice);
|
|
mDevice = NULL;
|
|
}
|
|
|
|
// Cleanup driver list
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Free OpenAL driver list\n"));
|
|
for (int i = 0; i < mNumDrivers; i++)
|
|
{
|
|
SAFE_FREE(mDriverNames[i]);
|
|
}
|
|
|
|
// Cleanup OpenAL library
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "FreeLibrary on OpenAL32.dll\n"));
|
|
UnloadOALLibrary();
|
|
|
|
mDLLInitialised = false;
|
|
mEnumerated = false;
|
|
mSetupOnce = false;
|
|
mNumDrivers = 0;
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::close", "Done.\n"));
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Start playing the software mixer buffer
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::start()
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
|
|
// Reset PCM base position
|
|
mPcmBase = 0;
|
|
|
|
// Mark the mixer sources as in use by software mixer
|
|
for (int i = 0; i < mOutputChannels; i++)
|
|
{
|
|
mSources[mMixerSourceOffset + i].used = true;
|
|
}
|
|
|
|
// Fill OpenAL buffers
|
|
for (int j = 0; j < mNumBuffers; j++)
|
|
{
|
|
int dataOffset = j * mBufferLengthBytes;
|
|
|
|
// Note: If using OpenAL "Generic Software" device, OpenAL will downmix to stereo
|
|
mOALFnTable.alBufferData(mSources[mMixerSourceOffset].bid[j], mFormatOAL, mBufferData + dataOffset, mBufferLengthBytes, mRate);
|
|
mOALFnTable.alSourceQueueBuffers(mSources[mMixerSourceOffset].sid, 1, &mSources[mMixerSourceOffset].bid[j]);
|
|
}
|
|
|
|
// Start the mixer thread
|
|
result = OutputTimer::start();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// Set the properties of the mixer source, then start it
|
|
mOALFnTable.alSourcef(mSources[mMixerSourceOffset].sid, AL_GAIN, 1.0f);
|
|
mOALFnTable.alSourcef(mSources[mMixerSourceOffset].sid, AL_PITCH, 1.0f);
|
|
mOALFnTable.alSourcei(mSources[mMixerSourceOffset].sid, AL_LOOPING, AL_FALSE);
|
|
mOALFnTable.alSourcePlay(mSources[mMixerSourceOffset].sid);
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Stop playing the shared buffer
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::stop()
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
|
|
if (mInitialised)
|
|
{
|
|
// Stop the mixer thread
|
|
result = OutputTimer::stop();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// Stop the mixer source
|
|
mOALFnTable.alSourceStop(mSources[mMixerSourceOffset].sid);
|
|
mOALFnTable.alSourcei(mSources[mMixerSourceOffset].sid, AL_BUFFER, NULL);
|
|
|
|
// Mark the mixer sources as not in use by software mixer
|
|
for (int i = 0; i < mOutputChannels; i++)
|
|
{
|
|
mSources[mMixerSourceOffset + i].used = false;
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Return a handle to the OpenAL device
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::getHandle(void **handle)
|
|
{
|
|
if (!handle)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*handle = mDevice;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Update OpenAL parameters
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::update()
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
int numListeners = 0;
|
|
float dopplerScale = 0.0f;
|
|
float distanceScale = 0.0f;
|
|
float rolloffScale = 0.0f;
|
|
static float lastDopplerScale = 0.0f;
|
|
static float lastDistanceScale = 0.0f;
|
|
static float lastRolloffScale = 0.0f;
|
|
|
|
// Note: FMOD default is LHS, OAL default is RHS
|
|
FMOD_VECTOR fPosition = { 0.0f, 0.0f, 0.0f };
|
|
FMOD_VECTOR fVelocity = { 0.0f, 0.0f, 0.0f };
|
|
FMOD_VECTOR fForward = { 0.0f, 0.0f, 1.0f };
|
|
FMOD_VECTOR fUp = { 0.0f, 1.0f, 0.0f };
|
|
ALfloat alPosition[] = { 0.0f, 0.0f, 0.0f };
|
|
ALfloat alVelocity[] = { 0.0f, 0.0f, 0.0f };
|
|
ALfloat alOrientation[] = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }; // First 3 are Forward, second 3 are Up
|
|
|
|
// Get the number of FMOD listeners
|
|
result = mSystem->get3DNumListeners(&numListeners);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// If there is only one listener get its details
|
|
if (numListeners == 1)
|
|
{
|
|
result = mSystem->get3DListenerAttributes(0, &fPosition, &fVelocity, &fForward, &fUp);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Get FMOD 3D sound settings
|
|
result = mSystem->get3DSettings(&dopplerScale, &distanceScale, &rolloffScale);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// Only update settings if necessary
|
|
if (!(dopplerScale != lastDopplerScale ||
|
|
distanceScale != lastDistanceScale ||
|
|
rolloffScale != lastRolloffScale ||
|
|
mSystem->mListener[0].mMoved ||
|
|
mSystem->mListener[0].mRotated))
|
|
{
|
|
return FMOD_OK;
|
|
}
|
|
|
|
/* Update OpenAL listener details from FMOD data */
|
|
alPosition[0] = fPosition.x;
|
|
alPosition[1] = fPosition.y;
|
|
alPosition[2] = fPosition.z;
|
|
|
|
alVelocity[0] = fVelocity.x;
|
|
alVelocity[1] = fVelocity.y;
|
|
alVelocity[2] = fVelocity.z;
|
|
|
|
alOrientation[0] = fForward.x;
|
|
alOrientation[1] = fForward.y;
|
|
alOrientation[2] = fForward.z;
|
|
alOrientation[3] = fUp.x;
|
|
alOrientation[4] = fUp.y;
|
|
alOrientation[5] = fUp.z;
|
|
|
|
// Flip the Z-axis if FMOD is not in RHS mode
|
|
if (!(mSystem->mFlags & FMOD_INIT_3D_RIGHTHANDED))
|
|
{
|
|
alPosition[2] *= -1; // fPosition.z
|
|
alVelocity[2] *= -1; // fVelocity.z
|
|
alOrientation[2] *= -1; // fForward.z
|
|
alOrientation[5] *= -1; // fUp.z
|
|
}
|
|
|
|
// Set the listener properties
|
|
mOALFnTable.alListenerfv(AL_POSITION, alPosition);
|
|
mOALFnTable.alListenerfv(AL_VELOCITY, alVelocity);
|
|
mOALFnTable.alListenerfv(AL_ORIENTATION, alOrientation);
|
|
|
|
// Update the rolloff scale of all the sources
|
|
for (int i = 0; i < mNumSources; i++)
|
|
{
|
|
mOALFnTable.alSourcef(mSources[i].sid, AL_ROLLOFF_FACTOR, rolloffScale);
|
|
}
|
|
|
|
// Update the doppler factor
|
|
mOALFnTable.alDopplerFactor(dopplerScale);
|
|
// Update distance factor by setting the speed of sound
|
|
mOALFnTable.alSpeedOfSound(SPEED_OF_SOUND * distanceScale);
|
|
|
|
// Remember scale values for next update()
|
|
lastDopplerScale = dopplerScale;
|
|
lastDistanceScale = distanceScale;
|
|
lastRolloffScale = rolloffScale;
|
|
|
|
// Update channels using tweaked distance models or fudge positioning for multilistener stuff
|
|
for (int j = 0; j < mNumChannels; j++)
|
|
{
|
|
ChannelReal *channelReal = NULL;
|
|
ChannelOpenAL *channelOpenAL = NULL;
|
|
|
|
result = mChannelPool3D->getChannel(j, &channelReal);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
channelOpenAL = SAFE_CAST(ChannelOpenAL, channelReal);
|
|
if (channelOpenAL == NULL)
|
|
{
|
|
return FMOD_ERR_INTERNAL;
|
|
}
|
|
|
|
// Process the channel for multilistener or tweaked distance models
|
|
if (numListeners > 1 || channelOpenAL->mMode & (FMOD_3D_LINEARROLLOFF | FMOD_3D_CUSTOMROLLOFF))
|
|
{
|
|
result = channelOpenAL->set3DAttributes();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Process the OpenAL sources
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::postMix()
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
ChannelReal *channelReal = NULL;
|
|
ChannelOpenAL *channelOpenAL = NULL;
|
|
static int lastUpdatedChannel = 0;
|
|
|
|
// Only update half of the channels each mix *optimisation*
|
|
for (int i = 0; i < mNumChannels; i += 2)
|
|
{
|
|
result = mChannelPool3D->getChannel(lastUpdatedChannel++, &channelReal);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
channelOpenAL = SAFE_CAST(ChannelOpenAL, channelReal);
|
|
if (channelOpenAL == NULL)
|
|
{
|
|
return FMOD_ERR_INTERNAL;
|
|
}
|
|
|
|
result = channelOpenAL->updateChannel();
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// Wrap the current channel index
|
|
if (lastUpdatedChannel >= mNumChannels)
|
|
{
|
|
lastUpdatedChannel = 0;
|
|
}
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Return the sample offset in the buffer of the block currently playing
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::getPosition(unsigned int *pcm)
|
|
{
|
|
if (pcm == NULL)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
// Get index of current buffer
|
|
ALint processed = 0;
|
|
mOALFnTable.alGetSourcei(mSources[mMixerSourceOffset].sid, AL_BUFFERS_PROCESSED, &processed);
|
|
|
|
// Check for starvation
|
|
if (processed == mNumBuffers)
|
|
{
|
|
mOALFnTable.alSourcePlay(mSources[mMixerSourceOffset].sid);
|
|
processed = 0;
|
|
}
|
|
|
|
// Return the sample number of the current buffer being processed
|
|
*pcm = (mPcmBase + (processed * mBufferLength)) % (mNumBuffers * mBufferLength);
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Give the mixer thread access to the shared mixer buffer
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::lock(unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2)
|
|
{
|
|
if (!mBufferData)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
*ptr1 = mBufferData + offset;
|
|
*len1 = length;
|
|
*ptr2 = NULL;
|
|
*len2 = 0;
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::unlock(void *ptr1, void *ptr2, unsigned int len1, unsigned int len2)
|
|
{
|
|
ALuint bufferId = 0;
|
|
ALint playing = AL_STOPPED;
|
|
char *pNewDataPos = (char *)ptr1;
|
|
unsigned int dataToBuffer = len1;
|
|
|
|
if (!mBufferData)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
// Keep looping until all the data written to mBufferData by the mixer thread is in OpenAL buffers
|
|
while (dataToBuffer >= mBufferLengthBytes)
|
|
{
|
|
// Remove a used buffer from the source
|
|
mOALFnTable.alSourceUnqueueBuffers(mSources[mMixerSourceOffset].sid, 1, &bufferId);
|
|
// Copy the next chunk of new data into the currently unqueued OpenAL buffer
|
|
mOALFnTable.alBufferData(bufferId, mFormatOAL, pNewDataPos, mBufferLengthBytes, mRate);
|
|
// Requeue the buffer that is now filled with new data
|
|
mOALFnTable.alSourceQueueBuffers(mSources[mMixerSourceOffset].sid, 1, &bufferId);
|
|
|
|
// Update positions for next chunk of data to buffer
|
|
pNewDataPos += mBufferLengthBytes;
|
|
dataToBuffer -= mBufferLengthBytes;
|
|
|
|
// Move to the next sample write position in mBufferData
|
|
mPcmBase = (mPcmBase + mBufferLength) % (mNumBuffers * mBufferLength);
|
|
}
|
|
|
|
// If the buffers have stopped, start them up again (this means all buffers finished before new data arrived)
|
|
mOALFnTable.alGetSourcei(mSources[mMixerSourceOffset].sid, AL_SOURCE_STATE, &playing);
|
|
if (playing != AL_PLAYING)
|
|
{
|
|
mOALFnTable.alSourcePlay(mSources[mMixerSourceOffset].sid);
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Create an OpenAL hardware sample
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::createSample(FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
SampleOpenAL *newsample = NULL;
|
|
unsigned int overflowBytes = 0;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::createSample", "PCM Length: %d, Byte Length: %d, Num Channels: %d, Format: %d, Mode: %08x\n", waveformat ? waveformat->lengthpcm : 0, waveformat ? waveformat->lengthbytes : 0, waveformat ? waveformat->channels : 0, waveformat ? waveformat->format : FMOD_SOUND_FORMAT_NONE, mode));
|
|
|
|
if (!sample)
|
|
{
|
|
return FMOD_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
// If a waveformat was specified then ensure it is valid
|
|
if (waveformat)
|
|
{
|
|
int formatBits = 0;
|
|
result = SoundI::getBitsFromFormat(waveformat->format, &formatBits);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// If no bits are returned and the format isn't NONE, MPEG or IMMADPCM then the format is invalid
|
|
if (formatBits == 0 && waveformat->format != FMOD_SOUND_FORMAT_NONE
|
|
#ifdef FMOD_SUPPORT_DSPCODEC
|
|
#ifdef FMOD_SUPPORT_IMAADPCM
|
|
&& waveformat->format != FMOD_SOUND_FORMAT_IMAADPCM
|
|
#endif
|
|
#ifdef FMOD_SUPPORT_MPEG
|
|
&& waveformat->format != FMOD_SOUND_FORMAT_MPEG
|
|
#endif
|
|
#endif
|
|
)
|
|
{
|
|
return FMOD_ERR_FORMAT;
|
|
}
|
|
}
|
|
|
|
// Check if a sample was provided
|
|
if (*sample == NULL)
|
|
{
|
|
// If not, allocate one
|
|
newsample = FMOD_Object_Calloc(SampleOpenAL);
|
|
if (newsample == NULL)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If so, use it
|
|
newsample = SAFE_CAST(SampleOpenAL, *sample);
|
|
}
|
|
|
|
// If no waveformat is provided, nothing more to do
|
|
if (waveformat == NULL)
|
|
{
|
|
*sample = newsample;
|
|
return FMOD_OK;
|
|
}
|
|
|
|
// Check for compressed formats
|
|
if (waveformat->format == FMOD_SOUND_FORMAT_IMAADPCM || waveformat->format == FMOD_SOUND_FORMAT_MPEG)
|
|
{
|
|
newsample->mLengthBytes = waveformat->lengthbytes;
|
|
newsample->mLoopPointDataEnd = NULL;
|
|
overflowBytes = 0;
|
|
}
|
|
else
|
|
{
|
|
// Get the length of the sample in bytes
|
|
result = SoundI::getBytesFromSamples(waveformat->lengthpcm, &newsample->mLengthBytes, waveformat->channels, waveformat->format);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// Get the length of the overflow bytes for resampling and loop points
|
|
result = SoundI::getBytesFromSamples(FMOD_DSP_RESAMPLER_OVERFLOWLENGTH, &overflowBytes, waveformat->channels, waveformat->format);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (overflowBytes <= 8)
|
|
{
|
|
// Loop points can use already allocated data
|
|
newsample->mLoopPointDataEnd = newsample->mLoopPointDataEndMemory;
|
|
}
|
|
else
|
|
{
|
|
// Loop point data is large so allocate now
|
|
newsample->mLoopPointDataEnd = (char *)FMOD_Memory_Calloc(overflowBytes);
|
|
if (newsample->mLoopPointDataEnd = NULL)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Only allocate sample memory if needed
|
|
if (mode & FMOD_OPENMEMORY_POINT)
|
|
{
|
|
newsample->mBufferMemory = NULL;
|
|
newsample->mBuffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Allocate enough memory for the sample, overflow at either end and 16 byte alignment
|
|
newsample->mBufferMemory = FMOD_Memory_Calloc(newsample->mLengthBytes + (overflowBytes * 2) + 16);
|
|
if (!newsample->mBufferMemory)
|
|
{
|
|
return FMOD_ERR_MEMORY;
|
|
}
|
|
|
|
// Move the memory pointer past the the front overflow and align to 16 byte boundry
|
|
newsample->mBuffer = (char *)(((unsigned int)newsample->mBufferMemory + overflowBytes + 15) & ~15);
|
|
}
|
|
|
|
newsample->mFormat = waveformat->format;
|
|
newsample->mLength = waveformat->lengthpcm;
|
|
*sample = newsample;
|
|
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::createSample", "done\n"));
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Pass the reverb properties through to the current reverb version
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::setReverbProperties(const FMOD_REVERB_PROPERTIES *prop)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
|
|
#if defined(FMOD_SUPPORT_EAX)
|
|
switch (mReverbVersion)
|
|
{
|
|
case REVERB_VERSION_EAX5:
|
|
{
|
|
result = setPropertiesEAX5(prop);
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX4:
|
|
{
|
|
result = setPropertiesEAX4(prop);
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX3:
|
|
{
|
|
result = setPropertiesEAX3(prop);
|
|
break;
|
|
}
|
|
case REVERB_VERSION_EAX2:
|
|
{
|
|
result = setPropertiesEAX2(prop);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Get the maximum number of channels a sample can have
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
int OutputOpenAL::getSampleMaxChannels(FMOD_MODE mode, FMOD_SOUND_FORMAT format)
|
|
{
|
|
return 16; /* 2D and 3D voices can be panned without needing to split them up. Allow up to 16 channel wide. */
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
Get a free OpenALChannel from the channel pool
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
Each channel is associated with OpenAL sources, if not enough are available then no channel can be returned
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::getFreeChannel(FMOD_MODE mode, ChannelReal **realchannel, int numchannels, int numsoundchannels, int *found)
|
|
{
|
|
FMOD_RESULT result = FMOD_OK;
|
|
ChannelOpenAL *channel = NULL;
|
|
FMOD_SPEAKERMODE speakerMode = FMOD_SPEAKERMODE_STEREO;
|
|
int requiredSources = 2;
|
|
int sourcesFound = 0;
|
|
|
|
// Try to get a free channel from the pool
|
|
result = Output::getFreeChannel(mode, realchannel, numchannels, numsoundchannels, found);
|
|
if (result != FMOD_OK)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
channel = SAFE_CAST(ChannelOpenAL, *realchannel);
|
|
|
|
/* Maximum number of sources needed is equal to the speaker mode since
|
|
the DSP network will mix to that before it gets to the OpenALChannel */
|
|
switch (mSystem->mSpeakerMode)
|
|
{
|
|
case FMOD_SPEAKERMODE_MONO : requiredSources = 1;
|
|
break;
|
|
case FMOD_SPEAKERMODE_STEREO : requiredSources = 2;
|
|
break;
|
|
case FMOD_SPEAKERMODE_QUAD : requiredSources = 4;
|
|
break;
|
|
case FMOD_SPEAKERMODE_5POINT1 : requiredSources = 6;
|
|
break;
|
|
case FMOD_SPEAKERMODE_7POINT1 : requiredSources = 8;
|
|
break;
|
|
default : requiredSources = 2;
|
|
break;
|
|
}
|
|
|
|
/* If the number of channels in the source is less than the speaker mode
|
|
then we only need enough sources for the sound source */
|
|
if (numsoundchannels < requiredSources)
|
|
{
|
|
requiredSources = numsoundchannels;
|
|
}
|
|
|
|
// Find enough sources for the number of sound channels required
|
|
for (int i = 0; i < mNumSources; i++)
|
|
{
|
|
if (!mSources[i].used)
|
|
{
|
|
// Check if we have found enough sources
|
|
if (++sourcesFound == requiredSources)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if there aren't enough source
|
|
if (sourcesFound != requiredSources)
|
|
{
|
|
// Remove any real channels that were retrieved from "OutputPolled::getFreeChannel"
|
|
while ((*found)--)
|
|
{
|
|
if (realchannel[*found])
|
|
{
|
|
// Remove the flags set by the channel real base code
|
|
realchannel[*found]->mFlags &= ~CHANNELREAL_FLAG_ALLOCATED;
|
|
realchannel[*found]->mFlags &= ~CHANNELREAL_FLAG_IN_USE;
|
|
realchannel[*found]->mFlags |= CHANNELREAL_FLAG_STOPPED;
|
|
realchannel[*found] = NULL;
|
|
}
|
|
}
|
|
|
|
return FMOD_ERR_CHANNEL_ALLOC;
|
|
}
|
|
|
|
return FMOD_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
==============================================================================================================
|
|
|
|
CALLBACK INTERFACE
|
|
|
|
==============================================================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::getNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->getNumDrivers(numdrivers);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::getDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->getDriverName(id, name, namelen);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT OutputOpenAL::getDriverCapsExCallback(FMOD_OUTPUT_STATE *output, int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode, int *num2dchannels, int *num3dchannels, int *totalchannels)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->getDriverCapsEx(id, caps, minfrequency, maxfrequency, controlpanelspeakermode, num2dchannels, num3dchannels, totalchannels);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::initCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->init(selecteddriver, flags, outputrate, outputchannels, outputformat, dspbufferlength, dspnumbuffers, extradriverdata);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::closeCallback(FMOD_OUTPUT_STATE *output)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->close();
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::startCallback(FMOD_OUTPUT_STATE *output)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->start();
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::stopCallback(FMOD_OUTPUT_STATE *output)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->stop();
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::updateCallback(FMOD_OUTPUT_STATE *output)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->update();
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::getHandleCallback(FMOD_OUTPUT_STATE *output, void **handle)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->getHandle(handle);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::getPositionCallback(FMOD_OUTPUT_STATE *output, unsigned int *pcm)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->getPosition(pcm);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::lockCallback(FMOD_OUTPUT_STATE *output, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->lock(offset, length, ptr1, ptr2, len1, len2);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::unlockCallback(FMOD_OUTPUT_STATE *output, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->unlock(ptr1, ptr2, len1, len2);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::createSampleCallback(FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_CODEC_WAVEFORMAT *waveformat, Sample **sample)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->createSample(mode, waveformat, sample);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::setReverbPropertiesCallback(FMOD_OUTPUT_STATE *output, const FMOD_REVERB_PROPERTIES *prop)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->setReverbProperties(prop);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
int F_CALLBACK OutputOpenAL::getSampleMaxChannelsCallback(FMOD_OUTPUT_STATE *output, FMOD_MODE mode, FMOD_SOUND_FORMAT format)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->getSampleMaxChannels(mode, format);
|
|
}
|
|
|
|
|
|
/*
|
|
[
|
|
[DESCRIPTION]
|
|
|
|
[PARAMETERS]
|
|
|
|
[RETURN_VALUE]
|
|
FMOD_OK
|
|
|
|
[REMARKS]
|
|
|
|
[PLATFORMS]
|
|
Win32, Win64
|
|
|
|
[SEE_ALSO]
|
|
]
|
|
*/
|
|
FMOD_RESULT F_CALLBACK OutputOpenAL::postMixCallback(FMOD_OUTPUT_STATE *output)
|
|
{
|
|
OutputOpenAL *openal = (OutputOpenAL *)output;
|
|
|
|
return openal->postMix();
|
|
}
|
|
|
|
} /* namespace FMOD */
|
|
|
|
#endif /* #ifdef FMOD_SUPPORT_OPENAL */
|