aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--al/buffer.cpp162
-rw-r--r--alc/alc.cpp2
-rw-r--r--alc/context.cpp1
-rw-r--r--include/AL/alext.h4
4 files changed, 149 insertions, 20 deletions
diff --git a/al/buffer.cpp b/al/buffer.cpp
index c2aafb6f..3e7f8175 100644
--- a/al/buffer.cpp
+++ b/al/buffer.cpp
@@ -451,6 +451,105 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
ALBuf->mLoopEnd = ALBuf->mSampleLen;
}
+/** Prepares the buffer to use caller-specified storage. */
+void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
+ const FmtChannels DstChannels, const FmtType DstType, al::byte *sdata, const ALuint sdatalen)
+{
+ if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
+ return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u",
+ ALBuf->id);
+
+ const ALuint unpackalign{ALBuf->UnpackAlign};
+ const ALuint align{SanitizeAlignment(DstType, unpackalign)};
+ if(align < 1) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples",
+ unpackalign, NameFromFmtType(DstType));
+
+ auto get_type_alignment = [](const FmtType type) noexcept -> ALuint
+ {
+ /* NOTE: This only needs to be the required alignment for the CPU to
+ * read/write the given sample type in the mixer.
+ */
+ switch(type)
+ {
+ case FmtUByte: return alignof(ALubyte);
+ case FmtShort: return alignof(ALshort);
+ case FmtFloat: return alignof(ALfloat);
+ case FmtDouble: return alignof(ALdouble);
+ case FmtMulaw: return alignof(ALubyte);
+ case FmtAlaw: return alignof(ALubyte);
+ case FmtIMA4: break;
+ case FmtMSADPCM: break;
+ }
+ return 1;
+ };
+ const auto typealign = get_type_alignment(DstType);
+ if((reinterpret_cast<uintptr_t>(sdata) & (typealign-1)) != 0)
+ return context->setError(AL_INVALID_VALUE, "Pointer %p is misaligned for %s samples (%u)",
+ static_cast<void*>(sdata), NameFromFmtType(DstType), typealign);
+
+ const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder :
+ (IsUHJ(DstChannels) ? 1 : 0)};
+
+ /* Convert the size in bytes to blocks using the unpack block alignment. */
+ const ALuint NumChannels{ChannelsFromFmt(DstChannels, ambiorder)};
+ const ALuint BlockSize{NumChannels *
+ ((DstType == FmtIMA4) ? (align-1)/2 + 4 :
+ (DstType == FmtMSADPCM) ? (align-2)/2 + 7 :
+ (align * BytesFromFmt(DstType)))};
+ if((sdatalen%BlockSize) != 0) UNLIKELY
+ return context->setError(AL_INVALID_VALUE,
+ "Data size %u is not a multiple of frame size %u (%u unpack alignment)",
+ sdatalen, BlockSize, align);
+ const ALuint blocks{sdatalen / BlockSize};
+
+ if(blocks > std::numeric_limits<ALsizei>::max()/align) UNLIKELY
+ return context->setError(AL_OUT_OF_MEMORY,
+ "Buffer size overflow, %d blocks x %d samples per block", blocks, align);
+ if(blocks > std::numeric_limits<size_t>::max()/BlockSize) UNLIKELY
+ return context->setError(AL_OUT_OF_MEMORY,
+ "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize);
+
+#ifdef ALSOFT_EAX
+ if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
+ {
+ ALCdevice &device = *context->mALDevice;
+ if(!eax_x_ram_check_availability(device, *ALBuf, sdatalen))
+ return context->setError(AL_OUT_OF_MEMORY,
+ "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size,
+ sdatalen);
+ }
+#endif
+
+ decltype(ALBuf->mDataStorage){}.swap(ALBuf->mDataStorage);
+ ALBuf->mData = {static_cast<al::byte*>(sdata), sdatalen};
+
+#ifdef ALSOFT_EAX
+ eax_x_ram_clear(*context->mALDevice, *ALBuf);
+#endif
+
+ ALBuf->mCallback = nullptr;
+ ALBuf->mUserData = nullptr;
+
+ ALBuf->OriginalSize = sdatalen;
+ ALBuf->Access = 0;
+
+ ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1;
+ ALBuf->mSampleRate = static_cast<ALuint>(freq);
+ ALBuf->mChannels = DstChannels;
+ ALBuf->mType = DstType;
+ ALBuf->mAmbiOrder = ambiorder;
+
+ ALBuf->mSampleLen = blocks * align;
+ ALBuf->mLoopStart = 0;
+ ALBuf->mLoopEnd = ALBuf->mSampleLen;
+
+#ifdef ALSOFT_EAX
+ if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
+ eax_x_ram_apply(*context->mALDevice, *ALBuf);
+#endif
+}
+
struct DecompResult { FmtChannels channels; FmtType type; };
al::optional<DecompResult> DecomposeUserFormat(ALenum format)
@@ -690,6 +789,33 @@ START_API_FUNC
}
END_API_FUNC
+void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size,
+ ALsizei freq)
+START_API_FUNC
+{
+ ContextRef context{GetContextRef()};
+ if(!context) UNLIKELY return;
+
+ ALCdevice *device{context->mALDevice.get()};
+ std::lock_guard<std::mutex> _{device->BufferLock};
+
+ ALbuffer *albuf = LookupBuffer(device, buffer);
+ if(!albuf) UNLIKELY
+ return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
+ if(size < 0) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Negative storage size %d", size);
+ if(freq < 1) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
+
+ auto usrfmt = DecomposeUserFormat(format);
+ if(!usrfmt) UNLIKELY
+ return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
+
+ PrepareUserPtr(context.get(), albuf, freq, usrfmt->channels, usrfmt->type,
+ static_cast<al::byte*>(data), static_cast<ALuint>(size));
+}
+END_API_FUNC
+
AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access)
START_API_FUNC
{
@@ -1478,14 +1604,14 @@ START_API_FUNC
//
for(auto i = 0;i < n;++i)
{
- const auto buffer = buffers[i];
- if(buffer == AL_NONE)
+ const auto bufid = buffers[i];
+ if(bufid == AL_NONE)
continue;
- const auto al_buffer = LookupBuffer(device, buffer);
- if(!al_buffer) UNLIKELY
+ const auto buffer = LookupBuffer(device, bufid);
+ if(!buffer) UNLIKELY
{
- ERR(EAX_PREFIX "Invalid buffer ID %u.\n", buffer);
+ ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid);
return ALC_FALSE;
}
@@ -1493,24 +1619,24 @@ START_API_FUNC
* only when not set/queued on a source?
*/
- if(*storage == EaxStorage::Hardware && !al_buffer->eax_x_ram_is_hardware)
+ if(*storage == EaxStorage::Hardware && !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(std::numeric_limits<size_t>::max()-al_buffer->OriginalSize < total_needed) UNLIKELY
+ if(std::numeric_limits<size_t>::max()-buffer->OriginalSize < total_needed) UNLIKELY
{
- context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Buffer size overflow (%u + %zu)\n",
- al_buffer->OriginalSize, total_needed);
+ context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Size overflow (%u + %zu)\n",
+ buffer->OriginalSize, total_needed);
return ALC_FALSE;
}
- total_needed += al_buffer->OriginalSize;
+ total_needed += 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)",
+ context->setError(AL_OUT_OF_MEMORY,EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)",
total_needed, device->eax_x_ram_free_size);
return ALC_FALSE;
}
@@ -1519,18 +1645,18 @@ START_API_FUNC
//
for(auto i = 0;i < n;++i)
{
- const auto buffer = buffers[i];
- if(buffer == AL_NONE)
+ const auto bufid = buffers[i];
+ if(bufid == AL_NONE)
continue;
- const auto al_buffer = LookupBuffer(device, buffer);
- assert(al_buffer);
+ const auto buffer = LookupBuffer(device, bufid);
+ assert(buffer);
if(*storage == EaxStorage::Hardware)
- eax_x_ram_apply(*device, *al_buffer);
+ eax_x_ram_apply(*device, *buffer);
else
- eax_x_ram_clear(*device, *al_buffer);
- al_buffer->eax_x_ram_mode = *storage;
+ eax_x_ram_clear(*device, *buffer);
+ buffer->eax_x_ram_mode = *storage;
}
return AL_TRUE;
diff --git a/alc/alc.cpp b/alc/alc.cpp
index bb83e1f7..eb3873f2 100644
--- a/alc/alc.cpp
+++ b/alc/alc.cpp
@@ -457,6 +457,8 @@ const struct {
DECL(alSourcePlayAtTimevSOFT),
DECL(alBufferSubDataSOFT),
+
+ DECL(alBufferDataStatic),
#ifdef ALSOFT_EAX
}, eaxFunctions[] = {
DECL(EAXGet),
diff --git a/alc/context.cpp b/alc/context.cpp
index 37607fd8..e02c549b 100644
--- a/alc/context.cpp
+++ b/alc/context.cpp
@@ -57,6 +57,7 @@ constexpr ALchar alExtList[] =
"AL_EXT_OFFSET "
"AL_EXT_source_distance_model "
"AL_EXT_SOURCE_RADIUS "
+ "AL_EXT_STATIC_BUFFER "
"AL_EXT_STEREO_ANGLES "
"AL_LOKI_quadriphonic "
"AL_SOFT_bformat_ex "
diff --git a/include/AL/alext.h b/include/AL/alext.h
index 6fe67be7..d313a999 100644
--- a/include/AL/alext.h
+++ b/include/AL/alext.h
@@ -141,9 +141,9 @@ extern "C" {
#ifndef AL_EXT_STATIC_BUFFER
#define AL_EXT_STATIC_BUFFER 1
-typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei);
+typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei);
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq);
+void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq);
#endif
#endif