mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-21 12:25:30 +00:00
* BugFix: Correct convexDecomp compilation by setting the LINUX flag when necessary.
* BugFix: Update OpenAL to correct a compilation error on Linux.
This commit is contained in:
parent
e071f1d901
commit
7380161054
234 changed files with 30864 additions and 7523 deletions
|
|
@ -35,6 +35,7 @@
|
|||
#include <mutex>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
|
|
@ -43,16 +44,23 @@
|
|||
|
||||
#include "albit.h"
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "atomic.h"
|
||||
#include "core/except.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax_globals.h"
|
||||
#include "eax_x_ram.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -110,10 +118,10 @@ void DecodeIMA4Block(int16_t *dst, const al::byte *src, size_t numchans, size_t
|
|||
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
sample[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
|
||||
sample[c] = src[0] | (src[1]<<8);
|
||||
sample[c] = (sample[c]^0x8000) - 32768;
|
||||
src += 2;
|
||||
index[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
|
||||
index[c] = src[0] | (src[1]<<8);
|
||||
index[c] = clampi((index[c]^0x8000) - 32768, 0, 88);
|
||||
src += 2;
|
||||
|
||||
|
|
@ -126,8 +134,8 @@ void DecodeIMA4Block(int16_t *dst, const al::byte *src, size_t numchans, size_t
|
|||
{
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
code[c] = al::to_integer<ALuint>(src[0]) | (al::to_integer<ALuint>(src[1])<< 8) |
|
||||
(al::to_integer<ALuint>(src[2])<<16) | (al::to_integer<ALuint>(src[3])<<24);
|
||||
code[c] = ALuint{src[0]} | (ALuint{src[1]}<< 8) | (ALuint{src[2]}<<16)
|
||||
| (ALuint{src[3]}<<24);
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
|
@ -156,25 +164,23 @@ void DecodeMSADPCMBlock(int16_t *dst, const al::byte *src, size_t numchans, size
|
|||
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
blockpred[c] = std::min<ALubyte>(al::to_integer<ALubyte>(src[0]), 6);
|
||||
blockpred[c] = std::min<ALubyte>(src[0], 6);
|
||||
++src;
|
||||
}
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
delta[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
|
||||
delta[c] = src[0] | (src[1]<<8);
|
||||
delta[c] = (delta[c]^0x8000) - 32768;
|
||||
src += 2;
|
||||
}
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
samples[c][0] = static_cast<ALshort>(al::to_integer<int>(src[0]) |
|
||||
(al::to_integer<int>(src[1])<<8));
|
||||
samples[c][0] = static_cast<ALshort>(src[0] | (src[1]<<8));
|
||||
src += 2;
|
||||
}
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
samples[c][1] = static_cast<ALshort>(al::to_integer<int>(src[0]) |
|
||||
(al::to_integer<int>(src[1])<<8));
|
||||
samples[c][1] = static_cast<ALshort>(src[0] | (src[1]<<8));
|
||||
src += 2;
|
||||
}
|
||||
|
||||
|
|
@ -198,13 +204,13 @@ void DecodeMSADPCMBlock(int16_t *dst, const al::byte *src, size_t numchans, size
|
|||
|
||||
int pred{(samples[c][0]*MSADPCMAdaptionCoeff[blockpred[c]][0] +
|
||||
samples[c][1]*MSADPCMAdaptionCoeff[blockpred[c]][1]) / 256};
|
||||
pred += (al::to_integer<int>(nibble^0x08) - 0x08) * delta[c];
|
||||
pred += ((nibble^0x08) - 0x08) * delta[c];
|
||||
pred = clampi(pred, -32768, 32767);
|
||||
|
||||
samples[c][1] = samples[c][0];
|
||||
samples[c][0] = static_cast<int16_t>(pred);
|
||||
|
||||
delta[c] = (MSADPCMAdaption[al::to_integer<ALubyte>(nibble)] * delta[c]) / 256;
|
||||
delta[c] = (MSADPCMAdaption[nibble] * delta[c]) / 256;
|
||||
delta[c] = maxi(16, delta[c]);
|
||||
|
||||
*(dst++) = static_cast<int16_t>(pred);
|
||||
|
|
@ -271,6 +277,9 @@ ALuint ChannelsFromUserFmt(UserFmtChannels chans, ALuint ambiorder) noexcept
|
|||
case UserFmtX71: return 8;
|
||||
case UserFmtBFormat2D: return (ambiorder*2) + 1;
|
||||
case UserFmtBFormat3D: return (ambiorder+1) * (ambiorder+1);
|
||||
case UserFmtUHJ2: return 2;
|
||||
case UserFmtUHJ3: return 3;
|
||||
case UserFmtUHJ4: return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -310,11 +319,82 @@ ALenum EnumFromAmbiScaling(AmbiScaling scale)
|
|||
{
|
||||
case AmbiScaling::FuMa: return AL_FUMA_SOFT;
|
||||
case AmbiScaling::SN3D: return AL_SN3D_SOFT;
|
||||
case AmbiScaling::N3D: return AL_SN3D_SOFT;
|
||||
case AmbiScaling::N3D: return AL_N3D_SOFT;
|
||||
case AmbiScaling::UHJ: break;
|
||||
}
|
||||
throw std::runtime_error{"Invalid AmbiScaling: "+std::to_string(int(scale))};
|
||||
}
|
||||
|
||||
al::optional<FmtChannels> FmtFromUserFmt(UserFmtChannels chans)
|
||||
{
|
||||
switch(chans)
|
||||
{
|
||||
case UserFmtMono: return al::make_optional(FmtMono);
|
||||
case UserFmtStereo: return al::make_optional(FmtStereo);
|
||||
case UserFmtRear: return al::make_optional(FmtRear);
|
||||
case UserFmtQuad: return al::make_optional(FmtQuad);
|
||||
case UserFmtX51: return al::make_optional(FmtX51);
|
||||
case UserFmtX61: return al::make_optional(FmtX61);
|
||||
case UserFmtX71: return al::make_optional(FmtX71);
|
||||
case UserFmtBFormat2D: return al::make_optional(FmtBFormat2D);
|
||||
case UserFmtBFormat3D: return al::make_optional(FmtBFormat3D);
|
||||
case UserFmtUHJ2: return al::make_optional(FmtUHJ2);
|
||||
case UserFmtUHJ3: return al::make_optional(FmtUHJ3);
|
||||
case UserFmtUHJ4: return al::make_optional(FmtUHJ4);
|
||||
}
|
||||
return al::nullopt;
|
||||
}
|
||||
al::optional<FmtType> FmtFromUserFmt(UserFmtType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case UserFmtUByte: return al::make_optional(FmtUByte);
|
||||
case UserFmtShort: return al::make_optional(FmtShort);
|
||||
case UserFmtFloat: return al::make_optional(FmtFloat);
|
||||
case UserFmtDouble: return al::make_optional(FmtDouble);
|
||||
case UserFmtMulaw: return al::make_optional(FmtMulaw);
|
||||
case UserFmtAlaw: return al::make_optional(FmtAlaw);
|
||||
/* ADPCM not handled here. */
|
||||
case UserFmtIMA4: break;
|
||||
case UserFmtMSADPCM: break;
|
||||
}
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffer,
|
||||
const ALuint newsize) noexcept
|
||||
{
|
||||
ALuint freemem{device.eax_x_ram_free_size};
|
||||
/* If the buffer is currently in "hardware", add its memory to the free
|
||||
* pool since it'll be "replaced".
|
||||
*/
|
||||
if(buffer.eax_x_ram_is_hardware)
|
||||
freemem += buffer.OriginalSize;
|
||||
return freemem >= newsize;
|
||||
}
|
||||
|
||||
void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept
|
||||
{
|
||||
if(buffer.eax_x_ram_is_hardware)
|
||||
return;
|
||||
|
||||
if(device.eax_x_ram_free_size >= buffer.OriginalSize)
|
||||
{
|
||||
device.eax_x_ram_free_size -= buffer.OriginalSize;
|
||||
buffer.eax_x_ram_is_hardware = true;
|
||||
}
|
||||
}
|
||||
|
||||
void eax_x_ram_clear(ALCdevice& al_device, ALbuffer& al_buffer)
|
||||
{
|
||||
if(al_buffer.eax_x_ram_is_hardware)
|
||||
al_device.eax_x_ram_free_size += al_buffer.OriginalSize;
|
||||
al_buffer.eax_x_ram_is_hardware = false;
|
||||
}
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
constexpr ALbitfieldSOFT INVALID_STORAGE_MASK{~unsigned(AL_MAP_READ_BIT_SOFT |
|
||||
AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT)};
|
||||
|
|
@ -352,13 +432,12 @@ ALbuffer *AllocBuffer(ALCdevice *device)
|
|||
{
|
||||
auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(),
|
||||
[](const BufferSubList &entry) noexcept -> bool
|
||||
{ return entry.FreeMask != 0; }
|
||||
);
|
||||
|
||||
{ return entry.FreeMask != 0; });
|
||||
auto lidx = static_cast<ALuint>(std::distance(device->BufferList.begin(), sublist));
|
||||
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
||||
ASSUME(slidx < 64);
|
||||
|
||||
ALbuffer *buffer{::new (sublist->Buffers + slidx) ALbuffer{}};
|
||||
ALbuffer *buffer{al::construct_at(sublist->Buffers + slidx)};
|
||||
|
||||
/* Add 1 to avoid buffer ID 0. */
|
||||
buffer->id = ((lidx<<6) | slidx) + 1;
|
||||
|
|
@ -370,6 +449,10 @@ ALbuffer *AllocBuffer(ALCdevice *device)
|
|||
|
||||
void FreeBuffer(ALCdevice *device, ALbuffer *buffer)
|
||||
{
|
||||
#ifdef ALSOFT_EAX
|
||||
eax_x_ram_clear(*device, *buffer);
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
const ALuint id{buffer->id - 1};
|
||||
const size_t lidx{id >> 6};
|
||||
const ALuint slidx{id & 0x3f};
|
||||
|
|
@ -453,46 +536,26 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
ALBuf->id);
|
||||
|
||||
/* Currently no channel configurations need to be converted. */
|
||||
FmtChannels DstChannels{FmtMono};
|
||||
switch(SrcChannels)
|
||||
{
|
||||
case UserFmtMono: DstChannels = FmtMono; break;
|
||||
case UserFmtStereo: DstChannels = FmtStereo; break;
|
||||
case UserFmtRear: DstChannels = FmtRear; break;
|
||||
case UserFmtQuad: DstChannels = FmtQuad; break;
|
||||
case UserFmtX51: DstChannels = FmtX51; break;
|
||||
case UserFmtX61: DstChannels = FmtX61; break;
|
||||
case UserFmtX71: DstChannels = FmtX71; break;
|
||||
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
|
||||
case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
|
||||
}
|
||||
if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
|
||||
auto DstChannels = FmtFromUserFmt(SrcChannels);
|
||||
if UNLIKELY(!DstChannels)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM, , "Invalid format");
|
||||
|
||||
/* IMA4 and MSADPCM convert to 16-bit short. */
|
||||
FmtType DstType{FmtUByte};
|
||||
switch(SrcType)
|
||||
{
|
||||
case UserFmtUByte: DstType = FmtUByte; break;
|
||||
case UserFmtShort: DstType = FmtShort; break;
|
||||
case UserFmtFloat: DstType = FmtFloat; break;
|
||||
case UserFmtDouble: DstType = FmtDouble; break;
|
||||
case UserFmtAlaw: DstType = FmtAlaw; break;
|
||||
case UserFmtMulaw: DstType = FmtMulaw; break;
|
||||
case UserFmtIMA4: DstType = FmtShort; break;
|
||||
case UserFmtMSADPCM: DstType = FmtShort; break;
|
||||
}
|
||||
|
||||
/* TODO: Currently we can only map samples when they're not converted. To
|
||||
/* IMA4 and MSADPCM convert to 16-bit short.
|
||||
*
|
||||
* TODO: Currently we can only map samples when they're not converted. To
|
||||
* allow it would need some kind of double-buffering to hold onto a copy of
|
||||
* the original data.
|
||||
*/
|
||||
if((access&MAP_READ_WRITE_FLAGS))
|
||||
{
|
||||
if UNLIKELY(static_cast<long>(SrcType) != static_cast<long>(DstType))
|
||||
if UNLIKELY(SrcType == UserFmtIMA4 || SrcType == UserFmtMSADPCM)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "%s samples cannot be mapped",
|
||||
NameFromUserFmtType(SrcType));
|
||||
}
|
||||
auto DstType = (SrcType == UserFmtIMA4 || SrcType == UserFmtMSADPCM)
|
||||
? al::make_optional(FmtShort) : FmtFromUserFmt(SrcType);
|
||||
if UNLIKELY(!DstType)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM, , "Invalid format");
|
||||
|
||||
const ALuint unpackalign{ALBuf->UnpackAlign};
|
||||
const ALuint align{SanitizeAlignment(SrcType, unpackalign)};
|
||||
|
|
@ -500,13 +563,13 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid unpack alignment %u for %s samples",
|
||||
unpackalign, NameFromUserFmtType(SrcType));
|
||||
|
||||
const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
|
||||
ALBuf->UnpackAmbiOrder : 0};
|
||||
const ALuint ambiorder{IsBFormat(*DstChannels) ? ALBuf->UnpackAmbiOrder :
|
||||
(IsUHJ(*DstChannels) ? 1 : 0)};
|
||||
|
||||
if((access&AL_PRESERVE_DATA_BIT_SOFT))
|
||||
{
|
||||
/* Can only preserve data with the same format and alignment. */
|
||||
if UNLIKELY(ALBuf->mChannels != DstChannels || ALBuf->OriginalType != SrcType)
|
||||
if UNLIKELY(ALBuf->mChannels != *DstChannels || ALBuf->OriginalType != SrcType)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched format");
|
||||
if UNLIKELY(ALBuf->OriginalAlign != align)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched alignment");
|
||||
|
|
@ -534,13 +597,23 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
/* Convert the sample frames to the number of bytes needed for internal
|
||||
* storage.
|
||||
*/
|
||||
ALuint NumChannels{ChannelsFromFmt(DstChannels, ambiorder)};
|
||||
ALuint FrameSize{NumChannels * BytesFromFmt(DstType)};
|
||||
ALuint NumChannels{ChannelsFromFmt(*DstChannels, ambiorder)};
|
||||
ALuint FrameSize{NumChannels * BytesFromFmt(*DstType)};
|
||||
if UNLIKELY(frames > std::numeric_limits<size_t>::max()/FrameSize)
|
||||
SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
|
||||
"Buffer size overflow, %d frames x %d bytes per frame", frames, FrameSize);
|
||||
size_t newsize{static_cast<size_t>(frames) * FrameSize};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
if(ALBuf->eax_x_ram_mode == AL_STORAGE_HARDWARE)
|
||||
{
|
||||
ALCdevice &device = *context->mALDevice;
|
||||
if(!eax_x_ram_check_availability(device, *ALBuf, size))
|
||||
SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
|
||||
"Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Round up to the next 16-byte multiple. This could reallocate only when
|
||||
* increasing or the new size is less than half the current, but then the
|
||||
* buffer's AL_SIZE would not be very reliable for accounting buffer memory
|
||||
|
|
@ -561,7 +634,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
|
||||
if(SrcType == UserFmtIMA4)
|
||||
{
|
||||
assert(DstType == FmtShort);
|
||||
assert(*DstType == FmtShort);
|
||||
if(SrcData != nullptr && !ALBuf->mData.empty())
|
||||
Convert_int16_ima4(reinterpret_cast<int16_t*>(ALBuf->mData.data()), SrcData,
|
||||
NumChannels, frames, align);
|
||||
|
|
@ -569,7 +642,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
}
|
||||
else if(SrcType == UserFmtMSADPCM)
|
||||
{
|
||||
assert(DstType == FmtShort);
|
||||
assert(*DstType == FmtShort);
|
||||
if(SrcData != nullptr && !ALBuf->mData.empty())
|
||||
Convert_int16_msadpcm(reinterpret_cast<int16_t*>(ALBuf->mData.data()), SrcData,
|
||||
NumChannels, frames, align);
|
||||
|
|
@ -577,7 +650,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
}
|
||||
else
|
||||
{
|
||||
assert(static_cast<long>(SrcType) == static_cast<long>(DstType));
|
||||
assert(DstType.has_value());
|
||||
if(SrcData != nullptr && !ALBuf->mData.empty())
|
||||
std::copy_n(SrcData, frames*FrameSize, ALBuf->mData.begin());
|
||||
ALBuf->OriginalAlign = 1;
|
||||
|
|
@ -588,8 +661,8 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
ALBuf->Access = access;
|
||||
|
||||
ALBuf->mSampleRate = static_cast<ALuint>(freq);
|
||||
ALBuf->mChannels = DstChannels;
|
||||
ALBuf->mType = DstType;
|
||||
ALBuf->mChannels = *DstChannels;
|
||||
ALBuf->mType = *DstType;
|
||||
ALBuf->mAmbiOrder = ambiorder;
|
||||
|
||||
ALBuf->mCallback = nullptr;
|
||||
|
|
@ -598,11 +671,16 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
ALBuf->mSampleLen = frames;
|
||||
ALBuf->mLoopStart = 0;
|
||||
ALBuf->mLoopEnd = ALBuf->mSampleLen;
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
if(eax_g_is_enabled && ALBuf->eax_x_ram_mode != AL_STORAGE_ACCESSIBLE)
|
||||
eax_x_ram_apply(*context->mALDevice, *ALBuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Prepares the buffer to use the specified callback, using the specified format. */
|
||||
void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
||||
UserFmtChannels SrcChannels, UserFmtType SrcType, LPALBUFFERCALLBACKTYPESOFT callback,
|
||||
UserFmtChannels SrcChannels, UserFmtType SrcType, ALBUFFERCALLBACKTYPESOFT callback,
|
||||
void *userptr)
|
||||
{
|
||||
if UNLIKELY(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0)
|
||||
|
|
@ -610,43 +688,25 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
|||
ALBuf->id);
|
||||
|
||||
/* Currently no channel configurations need to be converted. */
|
||||
FmtChannels DstChannels{FmtMono};
|
||||
switch(SrcChannels)
|
||||
{
|
||||
case UserFmtMono: DstChannels = FmtMono; break;
|
||||
case UserFmtStereo: DstChannels = FmtStereo; break;
|
||||
case UserFmtRear: DstChannels = FmtRear; break;
|
||||
case UserFmtQuad: DstChannels = FmtQuad; break;
|
||||
case UserFmtX51: DstChannels = FmtX51; break;
|
||||
case UserFmtX61: DstChannels = FmtX61; break;
|
||||
case UserFmtX71: DstChannels = FmtX71; break;
|
||||
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
|
||||
case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
|
||||
}
|
||||
if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
|
||||
auto DstChannels = FmtFromUserFmt(SrcChannels);
|
||||
if UNLIKELY(!DstChannels)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format");
|
||||
|
||||
/* IMA4 and MSADPCM convert to 16-bit short. Not supported with callbacks. */
|
||||
FmtType DstType{FmtUByte};
|
||||
switch(SrcType)
|
||||
{
|
||||
case UserFmtUByte: DstType = FmtUByte; break;
|
||||
case UserFmtShort: DstType = FmtShort; break;
|
||||
case UserFmtFloat: DstType = FmtFloat; break;
|
||||
case UserFmtDouble: DstType = FmtDouble; break;
|
||||
case UserFmtAlaw: DstType = FmtAlaw; break;
|
||||
case UserFmtMulaw: DstType = FmtMulaw; break;
|
||||
case UserFmtIMA4: DstType = FmtShort; break;
|
||||
case UserFmtMSADPCM: DstType = FmtShort; break;
|
||||
}
|
||||
if UNLIKELY(static_cast<long>(SrcType) != static_cast<long>(DstType))
|
||||
auto DstType = FmtFromUserFmt(SrcType);
|
||||
if UNLIKELY(!DstType)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM,, "Unsupported callback format");
|
||||
|
||||
const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
|
||||
ALBuf->UnpackAmbiOrder : 0};
|
||||
const ALuint ambiorder{IsBFormat(*DstChannels) ? ALBuf->UnpackAmbiOrder :
|
||||
(IsUHJ(*DstChannels) ? 1 : 0)};
|
||||
|
||||
al::vector<al::byte,16>(FrameSizeFromFmt(DstChannels, DstType, ambiorder) *
|
||||
size_t{BufferLineSize + (MaxResamplerPadding>>1)}).swap(ALBuf->mData);
|
||||
static constexpr uint line_size{BufferLineSize + MaxPostVoiceLoad};
|
||||
al::vector<al::byte,16>(FrameSizeFromFmt(*DstChannels, *DstType, ambiorder) *
|
||||
size_t{line_size}).swap(ALBuf->mData);
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
eax_x_ram_clear(*context->mALDevice, *ALBuf);
|
||||
#endif
|
||||
|
||||
ALBuf->mCallback = callback;
|
||||
ALBuf->mUserData = userptr;
|
||||
|
|
@ -657,8 +717,8 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
|||
ALBuf->Access = 0;
|
||||
|
||||
ALBuf->mSampleRate = static_cast<ALuint>(freq);
|
||||
ALBuf->mChannels = DstChannels;
|
||||
ALBuf->mType = DstType;
|
||||
ALBuf->mChannels = *DstChannels;
|
||||
ALBuf->mType = *DstType;
|
||||
ALBuf->mAmbiOrder = ambiorder;
|
||||
|
||||
ALBuf->mSampleLen = 0;
|
||||
|
|
@ -675,7 +735,7 @@ al::optional<DecompResult> DecomposeUserFormat(ALenum format)
|
|||
UserFmtChannels channels;
|
||||
UserFmtType type;
|
||||
};
|
||||
static const std::array<FormatMap,46> UserFmtList{{
|
||||
static const std::array<FormatMap,55> UserFmtList{{
|
||||
{ AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
|
||||
{ AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
|
||||
{ AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
|
||||
|
|
@ -731,6 +791,18 @@ al::optional<DecompResult> DecomposeUserFormat(ALenum format)
|
|||
{ AL_FORMAT_BFORMAT3D_16, UserFmtBFormat3D, UserFmtShort },
|
||||
{ AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
|
||||
{ AL_FORMAT_BFORMAT3D_MULAW, UserFmtBFormat3D, UserFmtMulaw },
|
||||
|
||||
{ AL_FORMAT_UHJ2CHN8_SOFT, UserFmtUHJ2, UserFmtUByte },
|
||||
{ AL_FORMAT_UHJ2CHN16_SOFT, UserFmtUHJ2, UserFmtShort },
|
||||
{ AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, UserFmtUHJ2, UserFmtFloat },
|
||||
|
||||
{ AL_FORMAT_UHJ3CHN8_SOFT, UserFmtUHJ3, UserFmtUByte },
|
||||
{ AL_FORMAT_UHJ3CHN16_SOFT, UserFmtUHJ3, UserFmtShort },
|
||||
{ AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, UserFmtUHJ3, UserFmtFloat },
|
||||
|
||||
{ AL_FORMAT_UHJ4CHN8_SOFT, UserFmtUHJ4, UserFmtUByte },
|
||||
{ AL_FORMAT_UHJ4CHN16_SOFT, UserFmtUHJ4, UserFmtShort },
|
||||
{ AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, UserFmtUHJ4, UserFmtFloat },
|
||||
}};
|
||||
|
||||
for(const auto &fmt : UserFmtList)
|
||||
|
|
@ -754,7 +826,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if(!EnsureBuffers(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
|
|
@ -794,7 +866,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
/* First try to find any buffers that are invalid or in-use. */
|
||||
|
|
@ -834,7 +906,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if LIKELY(context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if(!buffer || LookupBuffer(device, buffer))
|
||||
return AL_TRUE;
|
||||
|
|
@ -855,7 +927,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -877,8 +949,10 @@ START_API_FUNC
|
|||
if UNLIKELY(!usrfmt)
|
||||
context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
|
||||
else
|
||||
{
|
||||
LoadData(context.get(), albuf, freq, static_cast<ALuint>(size), usrfmt->channels,
|
||||
usrfmt->type, static_cast<const al::byte*>(data), flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
@ -889,7 +963,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return nullptr;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -942,7 +1016,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -965,7 +1039,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -997,7 +1071,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1127,7 +1201,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1147,7 +1221,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1166,7 +1240,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1188,7 +1262,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1250,7 +1324,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1283,7 +1357,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1321,7 +1395,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1343,7 +1417,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1371,7 +1445,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1393,7 +1467,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
|
@ -1450,7 +1524,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
|
@ -1488,7 +1562,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
|
@ -1510,13 +1584,13 @@ END_API_FUNC
|
|||
|
||||
|
||||
AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq,
|
||||
LPALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr, ALbitfieldSOFT flags)
|
||||
ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1526,8 +1600,6 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
|
||||
else if UNLIKELY(callback == nullptr)
|
||||
context->setError(AL_INVALID_VALUE, "NULL callback");
|
||||
else if UNLIKELY(flags != 0)
|
||||
context->setError(AL_INVALID_VALUE, "Invalid callback flags 0x%x", flags);
|
||||
else
|
||||
{
|
||||
auto usrfmt = DecomposeUserFormat(format);
|
||||
|
|
@ -1546,7 +1618,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
|
@ -1574,7 +1646,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
|
@ -1602,7 +1674,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
|
@ -1630,3 +1702,161 @@ BufferSubList::~BufferSubList()
|
|||
al_free(Buffers);
|
||||
Buffers = nullptr;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint* buffers, ALint value)
|
||||
START_API_FUNC
|
||||
{
|
||||
#define EAX_PREFIX "[EAXSetBufferMode] "
|
||||
|
||||
const auto context = ContextRef{GetContextRef()};
|
||||
if(!context)
|
||||
{
|
||||
ERR(EAX_PREFIX "%s\n", "No current context.");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!eax_g_is_enabled)
|
||||
{
|
||||
context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
switch(value)
|
||||
{
|
||||
case AL_STORAGE_AUTOMATIC:
|
||||
case AL_STORAGE_HARDWARE:
|
||||
case AL_STORAGE_ACCESSIBLE:
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(n == 0)
|
||||
return ALC_TRUE;
|
||||
|
||||
if(n < 0)
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!buffers)
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
auto device = context->mALDevice.get();
|
||||
std::lock_guard<std::mutex> device_lock{device->BufferLock};
|
||||
size_t total_needed{0};
|
||||
|
||||
// Validate the buffers.
|
||||
//
|
||||
for(auto i = 0;i < n;++i)
|
||||
{
|
||||
const auto buffer = buffers[i];
|
||||
if(buffer == AL_NONE)
|
||||
continue;
|
||||
|
||||
const auto al_buffer = LookupBuffer(device, buffer);
|
||||
if (!al_buffer)
|
||||
{
|
||||
ERR(EAX_PREFIX "Invalid buffer ID %u.\n", buffer);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
/* TODO: Is the store location allowed to change for in-use buffers, or
|
||||
* only when not set/queued on a source?
|
||||
*/
|
||||
|
||||
if(value == AL_STORAGE_HARDWARE && !al_buffer->eax_x_ram_is_hardware)
|
||||
{
|
||||
/* FIXME: This doesn't account for duplicate buffers. When the same
|
||||
* buffer ID is specified multiple times in the provided list, it
|
||||
* counts each instance as more memory that needs to fit in X-RAM.
|
||||
*/
|
||||
if(unlikely(std::numeric_limits<size_t>::max()-al_buffer->OriginalSize < total_needed))
|
||||
{
|
||||
context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Buffer size overflow (%u + %zu)\n",
|
||||
al_buffer->OriginalSize, total_needed);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
total_needed += al_buffer->OriginalSize;
|
||||
}
|
||||
}
|
||||
if(total_needed > device->eax_x_ram_free_size)
|
||||
{
|
||||
context->setError(AL_INVALID_ENUM, EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)",
|
||||
total_needed, device->eax_x_ram_free_size);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
// Update the mode.
|
||||
//
|
||||
for(auto i = 0;i < n;++i)
|
||||
{
|
||||
const auto buffer = buffers[i];
|
||||
if(buffer == AL_NONE)
|
||||
continue;
|
||||
|
||||
const auto al_buffer = LookupBuffer(device, buffer);
|
||||
assert(al_buffer);
|
||||
|
||||
if(value != AL_STORAGE_ACCESSIBLE)
|
||||
eax_x_ram_apply(*device, *al_buffer);
|
||||
else
|
||||
eax_x_ram_clear(*device, *al_buffer);
|
||||
al_buffer->eax_x_ram_mode = value;
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
|
||||
#undef EAX_PREFIX
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint* pReserved)
|
||||
START_API_FUNC
|
||||
{
|
||||
#define EAX_PREFIX "[EAXGetBufferMode] "
|
||||
|
||||
const auto context = ContextRef{GetContextRef()};
|
||||
if(!context)
|
||||
{
|
||||
ERR(EAX_PREFIX "%s\n", "No current context.");
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
if(!eax_g_is_enabled)
|
||||
{
|
||||
context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
if(pReserved)
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Non-null reserved parameter");
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
auto device = context->mALDevice.get();
|
||||
std::lock_guard<std::mutex> device_lock{device->BufferLock};
|
||||
|
||||
const auto al_buffer = LookupBuffer(device, buffer);
|
||||
if(!al_buffer)
|
||||
{
|
||||
context->setError(AL_INVALID_NAME, EAX_PREFIX "Invalid buffer ID %u", buffer);
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
return al_buffer->eax_x_ram_mode;
|
||||
|
||||
#undef EAX_PREFIX
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue