Use kAudioObjectPropertyElementMaster on macOS for compatibility
[openal-soft.git] / al / debug.cpp
blob8637c05059153545df4eff5596dd6db146b88799
1 #include "config.h"
3 #include "debug.h"
5 #include <algorithm>
6 #include <array>
7 #include <atomic>
8 #include <cstring>
9 #include <deque>
10 #include <mutex>
11 #include <optional>
12 #include <stdexcept>
13 #include <string>
14 #include <string_view>
15 #include <unordered_map>
16 #include <utility>
18 #include "AL/al.h"
19 #include "AL/alc.h"
20 #include "AL/alext.h"
22 #include "alc/context.h"
23 #include "alc/device.h"
24 #include "alnumeric.h"
25 #include "alspan.h"
26 #include "alstring.h"
27 #include "auxeffectslot.h"
28 #include "buffer.h"
29 #include "core/logging.h"
30 #include "core/voice.h"
31 #include "direct_defs.h"
32 #include "effect.h"
33 #include "error.h"
34 #include "filter.h"
35 #include "intrusive_ptr.h"
36 #include "opthelpers.h"
37 #include "source.h"
40 /* Declared here to prevent compilers from thinking it should be inlined, which
41 * GCC warns about increasing code size.
43 DebugGroup::~DebugGroup() = default;
45 namespace {
47 static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits");
49 template<typename T, T ...Vals>
50 constexpr auto make_array_sequence(std::integer_sequence<T, Vals...>)
51 { return std::array<T,sizeof...(Vals)>{Vals...}; }
53 template<typename T, size_t N>
54 constexpr auto make_array_sequence()
55 { return make_array_sequence(std::make_integer_sequence<T,N>{}); }
58 constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional<DebugSource>
60 switch(source)
62 case AL_DEBUG_SOURCE_API_EXT: return DebugSource::API;
63 case AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT: return DebugSource::System;
64 case AL_DEBUG_SOURCE_THIRD_PARTY_EXT: return DebugSource::ThirdParty;
65 case AL_DEBUG_SOURCE_APPLICATION_EXT: return DebugSource::Application;
66 case AL_DEBUG_SOURCE_OTHER_EXT: return DebugSource::Other;
68 return std::nullopt;
71 constexpr auto GetDebugType(ALenum type) noexcept -> std::optional<DebugType>
73 switch(type)
75 case AL_DEBUG_TYPE_ERROR_EXT: return DebugType::Error;
76 case AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT: return DebugType::DeprecatedBehavior;
77 case AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT: return DebugType::UndefinedBehavior;
78 case AL_DEBUG_TYPE_PORTABILITY_EXT: return DebugType::Portability;
79 case AL_DEBUG_TYPE_PERFORMANCE_EXT: return DebugType::Performance;
80 case AL_DEBUG_TYPE_MARKER_EXT: return DebugType::Marker;
81 case AL_DEBUG_TYPE_PUSH_GROUP_EXT: return DebugType::PushGroup;
82 case AL_DEBUG_TYPE_POP_GROUP_EXT: return DebugType::PopGroup;
83 case AL_DEBUG_TYPE_OTHER_EXT: return DebugType::Other;
85 return std::nullopt;
88 constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional<DebugSeverity>
90 switch(severity)
92 case AL_DEBUG_SEVERITY_HIGH_EXT: return DebugSeverity::High;
93 case AL_DEBUG_SEVERITY_MEDIUM_EXT: return DebugSeverity::Medium;
94 case AL_DEBUG_SEVERITY_LOW_EXT: return DebugSeverity::Low;
95 case AL_DEBUG_SEVERITY_NOTIFICATION_EXT: return DebugSeverity::Notification;
97 return std::nullopt;
101 constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum
103 switch(source)
105 case DebugSource::API: return AL_DEBUG_SOURCE_API_EXT;
106 case DebugSource::System: return AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT;
107 case DebugSource::ThirdParty: return AL_DEBUG_SOURCE_THIRD_PARTY_EXT;
108 case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
109 case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT;
111 throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))};
114 constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
116 switch(type)
118 case DebugType::Error: return AL_DEBUG_TYPE_ERROR_EXT;
119 case DebugType::DeprecatedBehavior: return AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT;
120 case DebugType::UndefinedBehavior: return AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT;
121 case DebugType::Portability: return AL_DEBUG_TYPE_PORTABILITY_EXT;
122 case DebugType::Performance: return AL_DEBUG_TYPE_PERFORMANCE_EXT;
123 case DebugType::Marker: return AL_DEBUG_TYPE_MARKER_EXT;
124 case DebugType::PushGroup: return AL_DEBUG_TYPE_PUSH_GROUP_EXT;
125 case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
126 case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT;
128 throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))};
131 constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
133 switch(severity)
135 case DebugSeverity::High: return AL_DEBUG_SEVERITY_HIGH_EXT;
136 case DebugSeverity::Medium: return AL_DEBUG_SEVERITY_MEDIUM_EXT;
137 case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
138 case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT;
140 throw std::runtime_error{"Unexpected debug severity value "+std::to_string(al::to_underlying(severity))};
144 constexpr auto GetDebugSourceName(DebugSource source) noexcept -> const char*
146 switch(source)
148 case DebugSource::API: return "API";
149 case DebugSource::System: return "Audio System";
150 case DebugSource::ThirdParty: return "Third Party";
151 case DebugSource::Application: return "Application";
152 case DebugSource::Other: return "Other";
154 return "<invalid source>";
157 constexpr auto GetDebugTypeName(DebugType type) noexcept -> const char*
159 switch(type)
161 case DebugType::Error: return "Error";
162 case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
163 case DebugType::UndefinedBehavior: return "Undefined Behavior";
164 case DebugType::Portability: return "Portability";
165 case DebugType::Performance: return "Performance";
166 case DebugType::Marker: return "Marker";
167 case DebugType::PushGroup: return "Push Group";
168 case DebugType::PopGroup: return "Pop Group";
169 case DebugType::Other: return "Other";
171 return "<invalid type>";
174 constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> const char*
176 switch(severity)
178 case DebugSeverity::High: return "High";
179 case DebugSeverity::Medium: return "Medium";
180 case DebugSeverity::Low: return "Low";
181 case DebugSeverity::Notification: return "Notification";
183 return "<invalid severity>";
186 } // namespace
189 void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
190 DebugType type, ALuint id, DebugSeverity severity, std::string_view message)
192 if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY
193 return;
195 if(message.length() >= MaxDebugMessageLength) UNLIKELY
197 ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(),
198 MaxDebugMessageLength, al::sizei(message), message.data());
199 return;
202 DebugGroup &debug = mDebugGroups.back();
204 const uint64_t idfilter{(1_u64 << (DebugSourceBase+al::to_underlying(source)))
205 | (1_u64 << (DebugTypeBase+al::to_underlying(type)))
206 | (uint64_t{id} << 32)};
207 auto iditer = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), idfilter);
208 if(iditer != debug.mIdFilters.cend() && *iditer == idfilter)
209 return;
211 const uint filter{(1u << (DebugSourceBase+al::to_underlying(source)))
212 | (1u << (DebugTypeBase+al::to_underlying(type)))
213 | (1u << (DebugSeverityBase+al::to_underlying(severity)))};
214 auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
215 if(iter != debug.mFilters.cend() && *iter == filter)
216 return;
218 if(mDebugCb)
220 auto callback = mDebugCb;
221 auto param = mDebugParam;
222 debuglock.unlock();
223 callback(GetDebugSourceEnum(source), GetDebugTypeEnum(type), id,
224 GetDebugSeverityEnum(severity), static_cast<ALsizei>(message.length()), message.data(),
225 param);
227 else
229 if(mDebugLog.size() < MaxDebugLoggedMessages)
230 mDebugLog.emplace_back(source, type, id, severity, message);
231 else UNLIKELY
232 ERR("Debug message log overflow. Lost message:\n"
233 " Source: %s\n"
234 " Type: %s\n"
235 " ID: %u\n"
236 " Severity: %s\n"
237 " Message: \"%.*s\"\n",
238 GetDebugSourceName(source), GetDebugTypeName(type), id,
239 GetDebugSeverityName(severity), al::sizei(message), message.data());
244 FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT,callback, void*,userParam)
245 FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context,
246 ALDEBUGPROCEXT callback, void *userParam) noexcept
248 std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
249 context->mDebugCb = callback;
250 context->mDebugParam = userParam;
254 FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum,source, ALenum,type, ALuint,id, ALenum,severity, ALsizei,length, const ALchar*,message)
255 FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source,
256 ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept
257 try {
258 if(!context->mContextFlags.test(ContextFlags::DebugBit))
259 return;
261 if(!message)
262 throw al::context_error{AL_INVALID_VALUE, "Null message pointer"};
264 auto msgview = (length < 0) ? std::string_view{message}
265 : std::string_view{message, static_cast<uint>(length)};
266 if(msgview.size() >= MaxDebugMessageLength)
267 throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)",
268 msgview.size(), MaxDebugMessageLength};
270 auto dsource = GetDebugSource(source);
271 if(!dsource)
272 throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
273 if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
274 throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
276 auto dtype = GetDebugType(type);
277 if(!dtype)
278 throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
280 auto dseverity = GetDebugSeverity(severity);
281 if(!dseverity)
282 throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
284 context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
286 catch(al::context_error& e) {
287 context->setError(e.errorCode(), "%s", e.what());
291 FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum,source, ALenum,type, ALenum,severity, ALsizei,count, const ALuint*,ids, ALboolean,enable)
292 FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source,
293 ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept
294 try {
295 if(count > 0)
297 if(!ids)
298 throw al::context_error{AL_INVALID_VALUE, "IDs is null with non-0 count"};
299 if(source == AL_DONT_CARE_EXT)
300 throw al::context_error{AL_INVALID_OPERATION,
301 "Debug source cannot be AL_DONT_CARE_EXT with IDs"};
302 if(type == AL_DONT_CARE_EXT)
303 throw al::context_error{AL_INVALID_OPERATION,
304 "Debug type cannot be AL_DONT_CARE_EXT with IDs"};
305 if(severity != AL_DONT_CARE_EXT)
306 throw al::context_error{AL_INVALID_OPERATION,
307 "Debug severity must be AL_DONT_CARE_EXT with IDs"};
310 if(enable != AL_TRUE && enable != AL_FALSE)
311 throw al::context_error{AL_INVALID_ENUM, "Invalid debug enable %d", enable};
313 static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount};
314 static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>();
316 auto srcIndices = al::span{Values}.subspan(DebugSourceBase,DebugSourceCount);
317 if(source != AL_DONT_CARE_EXT)
319 auto dsource = GetDebugSource(source);
320 if(!dsource)
321 throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
322 srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
325 auto typeIndices = al::span{Values}.subspan(DebugTypeBase,DebugTypeCount);
326 if(type != AL_DONT_CARE_EXT)
328 auto dtype = GetDebugType(type);
329 if(!dtype)
330 throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
331 typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
334 auto svrIndices = al::span{Values}.subspan(DebugSeverityBase,DebugSeverityCount);
335 if(severity != AL_DONT_CARE_EXT)
337 auto dseverity = GetDebugSeverity(severity);
338 if(!dseverity)
339 throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
340 svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
343 std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
344 DebugGroup &debug = context->mDebugGroups.back();
345 if(count > 0)
347 const uint filterbase{(1u<<srcIndices[0]) | (1u<<typeIndices[0])};
349 for(const uint id : al::span{ids, static_cast<uint>(count)})
351 const uint64_t filter{filterbase | (uint64_t{id} << 32)};
353 auto iter = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(),
354 filter);
355 if(!enable && (iter == debug.mIdFilters.cend() || *iter != filter))
356 debug.mIdFilters.insert(iter, filter);
357 else if(enable && iter != debug.mIdFilters.cend() && *iter == filter)
358 debug.mIdFilters.erase(iter);
361 else
363 auto apply_filter = [enable,&debug](const uint filter)
365 auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
366 if(!enable && (iter == debug.mFilters.cend() || *iter != filter))
367 debug.mFilters.insert(iter, filter);
368 else if(enable && iter != debug.mFilters.cend() && *iter == filter)
369 debug.mFilters.erase(iter);
371 auto apply_severity = [apply_filter,svrIndices](const uint filter)
373 std::for_each(svrIndices.cbegin(), svrIndices.cend(),
374 [apply_filter,filter](const uint idx){ apply_filter(filter | (1<<idx)); });
376 auto apply_type = [apply_severity,typeIndices](const uint filter)
378 std::for_each(typeIndices.cbegin(), typeIndices.cend(),
379 [apply_severity,filter](const uint idx){ apply_severity(filter | (1<<idx)); });
381 std::for_each(srcIndices.cbegin(), srcIndices.cend(),
382 [apply_type](const uint idx){ apply_type(1<<idx); });
385 catch(al::context_error& e) {
386 context->setError(e.errorCode(), "%s", e.what());
390 FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum,source, ALuint,id, ALsizei,length, const ALchar*,message)
391 FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source,
392 ALuint id, ALsizei length, const ALchar *message) noexcept
393 try {
394 if(length < 0)
396 size_t newlen{std::strlen(message)};
397 if(newlen >= MaxDebugMessageLength)
398 throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", newlen,
399 MaxDebugMessageLength};
400 length = static_cast<ALsizei>(newlen);
402 else if(length >= MaxDebugMessageLength)
403 throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length,
404 MaxDebugMessageLength};
406 auto dsource = GetDebugSource(source);
407 if(!dsource)
408 throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
409 if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
410 throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
412 std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
413 if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
414 throw al::context_error{AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"};
416 context->mDebugGroups.emplace_back(*dsource, id,
417 std::string_view{message, static_cast<uint>(length)});
418 auto &oldback = *(context->mDebugGroups.end()-2);
419 auto &newback = context->mDebugGroups.back();
421 newback.mFilters = oldback.mFilters;
422 newback.mIdFilters = oldback.mIdFilters;
424 if(context->mContextFlags.test(ContextFlags::DebugBit))
425 context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
426 DebugSeverity::Notification, newback.mMessage);
428 catch(al::context_error& e) {
429 context->setError(e.errorCode(), "%s", e.what());
432 FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
433 FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept
434 try {
435 std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
436 if(context->mDebugGroups.size() <= 1)
437 throw al::context_error{AL_STACK_UNDERFLOW_EXT,
438 "Attempting to pop the default debug group"};
440 DebugGroup &debug = context->mDebugGroups.back();
441 const auto source = debug.mSource;
442 const auto id = debug.mId;
443 std::string message{std::move(debug.mMessage)};
445 context->mDebugGroups.pop_back();
446 if(context->mContextFlags.test(ContextFlags::DebugBit))
447 context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
448 DebugSeverity::Notification, message);
450 catch(al::context_error& e) {
451 context->setError(e.errorCode(), "%s", e.what());
455 FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint,count, ALsizei,logBufSize, ALenum*,sources, ALenum*,types, ALuint*,ids, ALenum*,severities, ALsizei*,lengths, ALchar*,logBuf)
456 FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count,
457 ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities,
458 ALsizei *lengths, ALchar *logBuf) noexcept
459 try {
460 if(logBuf && logBufSize < 0)
461 throw al::context_error{AL_INVALID_VALUE, "Negative debug log buffer size"};
463 const auto sourcesSpan = al::span{sources, sources ? count : 0u};
464 const auto typesSpan = al::span{types, types ? count : 0u};
465 const auto idsSpan = al::span{ids, ids ? count : 0u};
466 const auto severitiesSpan = al::span{severities, severities ? count : 0u};
467 const auto lengthsSpan = al::span{lengths, lengths ? count : 0u};
468 const auto logSpan = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
470 auto sourceiter = sourcesSpan.begin();
471 auto typeiter = typesSpan.begin();
472 auto iditer = idsSpan.begin();
473 auto severityiter = severitiesSpan.begin();
474 auto lengthiter = lengthsSpan.begin();
475 auto logiter = logSpan.begin();
477 auto debuglock = std::lock_guard{context->mDebugCbLock};
478 for(ALuint i{0};i < count;++i)
480 if(context->mDebugLog.empty())
481 return i;
483 auto &entry = context->mDebugLog.front();
484 const auto tocopy = size_t{entry.mMessage.size() + 1};
485 if(al::to_address(logiter) != nullptr)
487 if(static_cast<size_t>(std::distance(logiter, logSpan.end())) < tocopy)
488 return i;
489 logiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logiter);
490 *(logiter++) = '\0';
493 if(al::to_address(sourceiter) != nullptr)
494 *(sourceiter++) = GetDebugSourceEnum(entry.mSource);
495 if(al::to_address(typeiter) != nullptr)
496 *(typeiter++) = GetDebugTypeEnum(entry.mType);
497 if(al::to_address(iditer) != nullptr)
498 *(iditer++) = entry.mId;
499 if(al::to_address(severityiter) != nullptr)
500 *(severityiter++) = GetDebugSeverityEnum(entry.mSeverity);
501 if(al::to_address(lengthiter) != nullptr)
502 *(lengthiter++) = static_cast<ALsizei>(tocopy);
504 context->mDebugLog.pop_front();
507 return count;
509 catch(al::context_error& e) {
510 context->setError(e.errorCode(), "%s", e.what());
511 return 0;
514 FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,length, const ALchar*,label)
515 FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
516 ALuint name, ALsizei length, const ALchar *label) noexcept
517 try {
518 if(!label && length != 0)
519 throw al::context_error{AL_INVALID_VALUE, "Null label pointer"};
521 auto objname = (length < 0) ? std::string_view{label}
522 : std::string_view{label, static_cast<uint>(length)};
523 if(objname.size() >= MaxObjectLabelLength)
524 throw al::context_error{AL_INVALID_VALUE, "Object label length too long (%zu >= %d)",
525 objname.size(), MaxObjectLabelLength};
527 switch(identifier)
529 case AL_SOURCE_EXT: ALsource::SetName(context, name, objname); return;
530 case AL_BUFFER: ALbuffer::SetName(context, name, objname); return;
531 case AL_FILTER_EXT: ALfilter::SetName(context, name, objname); return;
532 case AL_EFFECT_EXT: ALeffect::SetName(context, name, objname); return;
533 case AL_AUXILIARY_EFFECT_SLOT_EXT: ALeffectslot::SetName(context, name, objname); return;
536 throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
538 catch(al::context_error& e) {
539 context->setError(e.errorCode(), "%s", e.what());
542 FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label)
543 FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
544 ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
545 try {
546 if(bufSize < 0)
547 throw al::context_error{AL_INVALID_VALUE, "Negative label bufSize"};
549 if(!label && !length)
550 throw al::context_error{AL_INVALID_VALUE, "Null length and label"};
551 if(label && bufSize == 0)
552 throw al::context_error{AL_INVALID_VALUE, "Zero label bufSize"};
554 const auto labelOut = al::span{label, label ? static_cast<ALuint>(bufSize) : 0u};
555 auto copy_name = [name,length,labelOut](std::unordered_map<ALuint,std::string> &names)
557 std::string_view objname;
559 auto iter = names.find(name);
560 if(iter != names.end())
561 objname = iter->second;
563 if(labelOut.empty())
564 *length = static_cast<ALsizei>(objname.size());
565 else
567 const size_t tocopy{std::min(objname.size(), labelOut.size()-1)};
568 auto oiter = std::copy_n(objname.cbegin(), tocopy, labelOut.begin());
569 *oiter = '\0';
570 if(length)
571 *length = static_cast<ALsizei>(tocopy);
575 if(identifier == AL_SOURCE_EXT)
577 std::lock_guard srclock{context->mSourceLock};
578 copy_name(context->mSourceNames);
580 else if(identifier == AL_BUFFER)
582 auto *device = context->mALDevice.get();
583 auto buflock = std::lock_guard{device->BufferLock};
584 copy_name(device->mBufferNames);
586 else if(identifier == AL_FILTER_EXT)
588 auto *device = context->mALDevice.get();
589 auto buflock = std::lock_guard{device->FilterLock};
590 copy_name(device->mFilterNames);
592 else if(identifier == AL_EFFECT_EXT)
594 auto *device = context->mALDevice.get();
595 auto buflock = std::lock_guard{device->EffectLock};
596 copy_name(device->mEffectNames);
598 else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
600 std::lock_guard slotlock{context->mEffectSlotLock};
601 copy_name(context->mEffectSlotNames);
603 else
604 throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
606 catch(al::context_error& e) {
607 context->setError(e.errorCode(), "%s", e.what());