aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2020-02-17 00:22:51 -0800
committerChris Robinson <[email protected]>2020-02-17 00:22:51 -0800
commitccc6da0a5bb691987b95ec7f7b6c8a7cda13b7da (patch)
tree6b05a016e93d3aafef188cfe4298c94ea824d44b
parent3156b17057569fb8d3074603d8d8b4ba0dd73c57 (diff)
Set and retrieve a buffer's callback
-rw-r--r--al/buffer.cpp152
-rw-r--r--al/buffer.h3
2 files changed, 147 insertions, 8 deletions
diff --git a/al/buffer.cpp b/al/buffer.cpp
index c7acb9aa..b2543434 100644
--- a/al/buffer.cpp
+++ b/al/buffer.cpp
@@ -544,11 +544,80 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
ALBuf->mFmtType = DstType;
ALBuf->Access = access;
+ ALBuf->Callback = nullptr;
+ ALBuf->UserData = nullptr;
+
ALBuf->SampleLen = frames;
ALBuf->LoopStart = 0;
ALBuf->LoopEnd = ALBuf->SampleLen;
}
+/** 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,
+ void *userptr)
+{
+ if UNLIKELY(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0)
+ SETERR_RETURN(context, AL_INVALID_OPERATION,, "Modifying callback for in-use buffer %u",
+ 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))
+ 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))
+ SETERR_RETURN(context, AL_INVALID_ENUM,, "Unsupported callback format");
+
+ if(!ALBuf->mData.empty())
+ {
+ ALBuf->mData.clear();
+ ALBuf->mData.shrink_to_fit();
+ }
+
+ ALBuf->Callback = callback;
+ ALBuf->UserData = userptr;
+
+ ALBuf->OriginalType = SrcType;
+ ALBuf->OriginalSize = 0;
+ ALBuf->OriginalAlign = 1;
+
+ ALBuf->Frequency = static_cast<ALuint>(freq);
+ ALBuf->mFmtChannels = DstChannels;
+ ALBuf->mFmtType = DstType;
+ ALBuf->Access = 0;
+
+ ALBuf->SampleLen = 0;
+ ALBuf->LoopStart = 0;
+ ALBuf->LoopEnd = ALBuf->SampleLen;
+}
+
+
struct DecompResult { UserFmtChannels channels; UserFmtType type; };
al::optional<DecompResult> DecomposeUserFormat(ALenum format)
{
@@ -1376,43 +1445,110 @@ START_API_FUNC
END_API_FUNC
-AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint /*buffer*/, ALenum /*format*/, ALsizei /*freq*/, LPALBUFFERCALLBACKTYPESOFT /*callback*/, ALvoid* /*userptr*/, ALbitfieldSOFT /*flags*/)
+AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq,
+ LPALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr, ALbitfieldSOFT flags)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if UNLIKELY(!context) return;
- context->setError(AL_INVALID_OPERATION, "alBufferCallbackSOFT not functional");
+ ALCdevice *device{context->mDevice.get()};
+ std::lock_guard<std::mutex> _{device->BufferLock};
+
+ ALbuffer *albuf = LookupBuffer(device, buffer);
+ if UNLIKELY(!albuf)
+ context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
+ else if UNLIKELY(freq < 1)
+ 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);
+ if UNLIKELY(!usrfmt)
+ context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
+ else
+ PrepareCallback(context.get(), albuf, freq, usrfmt->channels, usrfmt->type, callback,
+ userptr);
+ }
}
END_API_FUNC
-AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint /*buffer*/, ALenum /*param*/, ALvoid** /*value*/)
+AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **value)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if UNLIKELY(!context) return;
- context->setError(AL_INVALID_OPERATION, "alGetBufferPtrSOFT not functional");
+ ALCdevice *device{context->mDevice.get()};
+ std::lock_guard<std::mutex> _{device->BufferLock};
+ ALbuffer *albuf = LookupBuffer(device, buffer);
+ if UNLIKELY(!albuf)
+ context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
+ else if UNLIKELY(!value)
+ context->setError(AL_INVALID_VALUE, "NULL pointer");
+ else switch(param)
+ {
+ case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
+ *value = reinterpret_cast<void*>(albuf->Callback);
+ break;
+ case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
+ *value = albuf->UserData;
+ break;
+
+ default:
+ context->setError(AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param);
+ }
}
END_API_FUNC
-AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint /*buffer*/, ALenum /*param*/, ALvoid** /*value1*/, ALvoid** /*value2*/, ALvoid** /*value3*/)
+AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3)
START_API_FUNC
{
ContextRef context{GetContextRef()};
if UNLIKELY(!context) return;
- context->setError(AL_INVALID_OPERATION, "alGetBuffer3PtrSOFT not functional");
+ ALCdevice *device{context->mDevice.get()};
+ std::lock_guard<std::mutex> _{device->BufferLock};
+ if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
+ context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
+ else if UNLIKELY(!value1 || !value2 || !value3)
+ context->setError(AL_INVALID_VALUE, "NULL pointer");
+ else switch(param)
+ {
+ default:
+ context->setError(AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param);
+ }
}
END_API_FUNC
-AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint /*buffer*/, ALenum /*param*/, ALvoid** /*values*/)
+AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **values)
START_API_FUNC
{
+ switch(param)
+ {
+ case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
+ case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
+ alGetBufferPtrSOFT(buffer, param, values);
+ return;
+ }
+
ContextRef context{GetContextRef()};
if UNLIKELY(!context) return;
- context->setError(AL_INVALID_OPERATION, "alGetBufferPtrvSOFT not functional");
+ ALCdevice *device{context->mDevice.get()};
+ std::lock_guard<std::mutex> _{device->BufferLock};
+ if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
+ context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
+ else if UNLIKELY(!values)
+ context->setError(AL_INVALID_VALUE, "NULL pointer");
+ else switch(param)
+ {
+ default:
+ context->setError(AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", param);
+ }
}
END_API_FUNC
diff --git a/al/buffer.h b/al/buffer.h
index 07012b2d..d1c3c747 100644
--- a/al/buffer.h
+++ b/al/buffer.h
@@ -80,6 +80,9 @@ struct ALbuffer {
ALenum AmbiLayout{AL_FUMA_SOFT};
ALenum AmbiScaling{AL_FUMA_SOFT};
+ LPALBUFFERCALLBACKTYPESOFT Callback{nullptr};
+ void *UserData{nullptr};
+
ALuint LoopStart{0u};
ALuint LoopEnd{0u};