fmodex/win32/src/fmod_output_openal.cpp

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 */