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