Browse Source

Audio: port Context away from std::vector and std::string.

Basically mirrors what 95b3f8578d did for
GL::Context.
next
Vladimír Vondruš 2 months ago
parent
commit
82664481b7
  1. 11
      doc/changelog.dox
  2. 120
      src/Magnum/Audio/Context.cpp
  3. 93
      src/Magnum/Audio/Context.h
  4. 53
      src/Magnum/Audio/Test/ContextALTest.cpp

11
doc/changelog.dox

@ -1991,6 +1991,17 @@ See also:
@relativeref{Corrade,Containers::Array} / @relativeref{Corrade,Containers::Array} /
@relativeref{Corrade,Containers::ArrayView} of them, instead of a @relativeref{Corrade,Containers::ArrayView} of them, instead of a
@ref std::string and a @ref std::vector. @ref std::string and a @ref std::vector.
- @ref Audio::Context::deviceSpecifierStrings(),
@relativeref{Audio::Context,hrtfSpecifierString()},
@relativeref{Audio::Context,deviceSpecifierString()},
@relativeref{Audio::Context,vendorString()},
@relativeref{Audio::Context,rendererString()},
@relativeref{Audio::Context,versionString()},
@relativeref{Audio::Context,extensionStrings()} now return
@relativeref{Corrade,Containers::StringView} or a
@relativeref{Corrade,Containers::Array} /
@relativeref{Corrade,Containers::ArrayView} of them, instead of a
@ref std::string and a @ref std::vector.
- All @ref GL::Buffer::label() "label()" and - All @ref GL::Buffer::label() "label()" and
@ref GL::Buffer::setLabel() "setLabel()" APIs now work with a @ref GL::Buffer::setLabel() "setLabel()" APIs now work with a
@relativeref{Corrade,Containers::StringView} / @relativeref{Corrade,Containers::StringView} /

120
src/Magnum/Audio/Context.cpp

@ -29,11 +29,10 @@
#include "Context.h" #include "Context.h"
#include <algorithm> /* std::lower_bound() */ #include <algorithm> /* std::lower_bound() */
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Utility/Arguments.h> #include <Corrade/Utility/Arguments.h>
#include <Corrade/Utility/Assert.h> #include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h> #include <Corrade/Utility/Debug.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/String.h>
#include "Magnum/Audio/Extensions.h" #include "Magnum/Audio/Extensions.h"
@ -61,6 +60,16 @@ constexpr Extension ExtensionList[]{
#undef _entension #undef _entension
}; };
const Extension* findExtension(const Containers::StringView extension) {
const auto found = std::lower_bound(std::begin(ExtensionList), std::end(ExtensionList), extension, [](const Extension& a, const Containers::StringView& b) {
return a.string() < b;
});
if(found != std::end(ExtensionList) && found->string() == extension)
return found;
return {};
}
} }
Containers::ArrayView<const Extension> Extension::extensions() { Containers::ArrayView<const Extension> Extension::extensions() {
@ -108,11 +117,11 @@ const char* alcErrorString(const ALenum error) {
} }
std::vector<std::string> Context::deviceSpecifierStrings() { Containers::Array<Containers::StringView> Context::deviceSpecifierStrings() {
std::vector<std::string> list; Containers::Array<Containers::StringView> list;
const char* device = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); const char* device = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
while(*device) { while(*device) {
list.emplace_back(device); arrayAppend(list, {device, Containers::StringViewFlag::Global});
device += list.back().size() + 1; device += list.back().size() + 1;
} }
@ -197,9 +206,17 @@ Context::Context(NoCreateT, const Int argc, const char* const* const argv) noexc
/* Decide how to display initialization log */ /* Decide how to display initialization log */
_displayInitializationLog = !(args.value("log") == "quiet" || args.value("log") == "QUIET"); _displayInitializationLog = !(args.value("log") == "quiet" || args.value("log") == "QUIET");
/* Disable extensions */ /* Disable extensions. Here we search for them among the known extensions
for(auto&& extension: Utility::String::splitWithoutEmptyParts(args.value("disable-extensions"))) and store the Extension objects instead, which avoids the string copying
_disabledExtensionStrings.push_back(extension); and another binary search in tryCreate(). */
const Containers::StringView disabledExtensions = args.value<Containers::StringView>("disable-extensions");
if(!disabledExtensions.isEmpty()) {
const Containers::Array<Containers::StringView> split = disabledExtensions.splitOnWhitespaceWithoutEmptyParts();
arrayReserve(_disabledExtensions, split.size());
for(const Containers::StringView extension: split)
if(const Extension* found = findExtension(extension))
arrayAppend(_disabledExtensions, *found);
}
} }
void Context::create(const Configuration& configuration) { void Context::create(const Configuration& configuration) {
@ -211,7 +228,7 @@ bool Context::tryCreate(const Configuration& configuration) {
CORRADE_ASSERT(!currentContext, "Audio::Context: context already created", false); CORRADE_ASSERT(!currentContext, "Audio::Context: context already created", false);
/* Open the device */ /* Open the device */
const ALCchar* const deviceSpecifier = configuration.deviceSpecifier().empty() ? alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER) : configuration.deviceSpecifier().data(); const ALCchar* const deviceSpecifier = configuration.deviceSpecifier().isEmpty() ? alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER) : configuration.deviceSpecifier().data();
if(!(_device = alcOpenDevice(deviceSpecifier))) { if(!(_device = alcOpenDevice(deviceSpecifier))) {
Error() << "Audio::Context: cannot open sound device" << deviceSpecifier; Error() << "Audio::Context: cannot open sound device" << deviceSpecifier;
return false; return false;
@ -270,11 +287,10 @@ bool Context::tryCreate(const Configuration& configuration) {
currentContext = this; currentContext = this;
/* Check for presence of extensions */ /* Check for presence of extensions */
const std::vector<std::string> extensions = extensionStrings(); const Containers::Array<Containers::StringView> extensions = extensionStrings();
for(const std::string& extension: extensions) { for(const Containers::StringView extension: extensions) {
const auto found = std::lower_bound(std::begin(ExtensionList), std::end(ExtensionList), extension, [](const Extension& a, const std::string& b) { return a.string() < b; }); if(const Extension* found = findExtension(extension)) {
if(found != std::end(ExtensionList) && found->string() == extension) { arrayAppend(_supportedExtensions, *found);
_supportedExtensions.push_back(*found);
_extensionStatus.set(found->index(), true); _extensionStatus.set(found->index(), true);
} }
} }
@ -286,33 +302,15 @@ bool Context::tryCreate(const Configuration& configuration) {
Debug{output} << "OpenAL version:" << versionString(); Debug{output} << "OpenAL version:" << versionString();
/* Disable extensions as requested by the user */ /* Disable extensions as requested by the user */
if(!_disabledExtensionStrings.empty()) { if(!_disabledExtensions.isEmpty()) {
bool headerPrinted = false; Debug{output} << "Disabling extensions:";
/* Disable extensions that are known and supported and print a message /* Disable extensions that are known and supported and print a message
for each */ for each */
for(auto&& extension: _disabledExtensionStrings) { for(const Extension& extension: _disabledExtensions) {
const auto found = std::lower_bound(std::begin(ExtensionList), std::end(ExtensionList), extension, [](const Extension& a, const std::string& b) { return a.string() < b; }); _extensionStatus.set(extension.index(), false);
/* No error message here because some of the extensions could be _extensionDisabledStatus.set(extension.index(), true);
from Vulkan or OpenGL. That also means we print the header only Debug{output} << " " << extension.string();
when we actually have something to say */
if(found == std::end(ExtensionList) || found->string() != extension)
continue;
/* If the extension isn't supported in the first place, don't do
anything. If it is, set its status as unsupported but flip the
corresponding bit in the disabled bitmap so we know it is
supported and only got disabled */
if(!_extensionStatus[found->index()])
continue;
_extensionStatus.set(found->index(), false);
_disabledExtensions.set(found->index(), true);
if(!headerPrinted) {
Debug{output} << "Disabling extensions:";
headerPrinted = true;
}
Debug{output} << " " << extension;
} }
} }
@ -335,15 +333,12 @@ Context::~Context() {
currentContext = nullptr; currentContext = nullptr;
} }
std::vector<std::string> Context::extensionStrings() const { Containers::Array<Containers::StringView> Context::extensionStrings() const {
std::vector<std::string> extensions; Containers::Array<Containers::StringView> extensions = Containers::StringView{reinterpret_cast<const char*>(alGetString(AL_EXTENSIONS)), Containers::StringViewFlag::Global}.splitWithoutEmptyParts(' ');
/* Don't crash when alGetString() returns nullptr */ /* Add ALC extensions as well. This reallocates the above array but that's
extensions = Utility::String::splitWithoutEmptyParts(Utility::String::fromArray(reinterpret_cast<const char*>(alGetString(AL_EXTENSIONS))), ' '); fine, it's not a hot code path. */
arrayAppend(extensions, Containers::StringView{reinterpret_cast<const char*>(alcGetString(_device, ALC_EXTENSIONS)), Containers::StringViewFlag::Global}.splitWithoutEmptyParts(' '));
/* Add ALC extensions as well */
auto splitAlcExts = Utility::String::splitWithoutEmptyParts(Utility::String::fromArray(reinterpret_cast<const char*>(alcGetString(_device, ALC_EXTENSIONS))), ' ');
extensions.insert(extensions.end(), splitAlcExts.begin(), splitAlcExts.end());
return extensions; return extensions;
} }
@ -369,9 +364,9 @@ Context::HrtfStatus Context::hrtfStatus() const {
return Context::HrtfStatus(status); return Context::HrtfStatus(status);
} }
std::string Context::hrtfSpecifierString() const { Containers::StringView Context::hrtfSpecifierString() const {
/* Returns a string on ALC_SOFT_HRTF, nullptr on ALC_SOFTX_HRTF */ /* Returns a string on ALC_SOFT_HRTF, nullptr on ALC_SOFTX_HRTF */
return Utility::String::fromArray(alcGetString(_device, ALC_HRTF_SPECIFIER_SOFT)); return {alcGetString(_device, ALC_HRTF_SPECIFIER_SOFT), Containers::StringViewFlag::Global};
} }
Int Context::monoSourceCount() const { Int Context::monoSourceCount() const {
@ -392,33 +387,20 @@ Int Context::refreshRate() const {
return count; return count;
} }
std::string Context::deviceSpecifierString() const { Containers::StringView Context::deviceSpecifierString() const {
return alcGetString(_device, ALC_DEVICE_SPECIFIER); return {alcGetString(_device, ALC_DEVICE_SPECIFIER), Containers::StringViewFlag::Global};
}
std::string Context::vendorString() const {
return alGetString(AL_VENDOR);
} }
std::string Context::rendererString() const { Containers::StringView Context::vendorString() const {
return alGetString(AL_RENDERER); return {alGetString(AL_VENDOR), Containers::StringViewFlag::Global};
} }
std::string Context::versionString() const { Containers::StringView Context::rendererString() const {
return alGetString(AL_VERSION); return {alGetString(AL_RENDERER), Containers::StringViewFlag::Global};
}
Context::Configuration::Configuration() = default;
Context::Configuration::~Configuration() = default;
Context::Configuration& Context::Configuration::setDeviceSpecifier(const std::string& specifier) {
_deviceSpecifier = specifier;
return *this;
} }
Context::Configuration& Context::Configuration::setDeviceSpecifier(std::string&& specifier) { Containers::StringView Context::versionString() const {
_deviceSpecifier = Utility::move(specifier); return {alGetString(AL_VERSION), Containers::StringViewFlag::Global};
return *this;
} }
}} }}

93
src/Magnum/Audio/Context.h

@ -33,9 +33,9 @@
*/ */
#include <cstdlib> /* std::abort() in MAGNUM_ASSERT_AUDIO_EXTENSION_SUPPORTED() */ #include <cstdlib> /* std::abort() in MAGNUM_ASSERT_AUDIO_EXTENSION_SUPPORTED() */
#include <string> #include <Corrade/Containers/Array.h>
#include <vector>
#include <Corrade/Containers/EnumSet.h> #include <Corrade/Containers/EnumSet.h>
#include <Corrade/Containers/String.h> /** @todo PIMPL Configuration instead? */
#include <Corrade/Utility/Debug.h> #include <Corrade/Utility/Debug.h>
#include "Magnum/Magnum.h" #include "Magnum/Magnum.h"
@ -50,6 +50,12 @@
#include <alc.h> #include <alc.h>
#include "MagnumExternal/OpenAL/extensions.h" #include "MagnumExternal/OpenAL/extensions.h"
#ifdef MAGNUM_BUILD_DEPRECATED
/* For return types of Context::versionString() etc., which used to be a
std::string. Not ideal for the return types, but at least something. */
#include <Corrade/Containers/StringStl.h>
#endif
namespace Magnum { namespace Audio { namespace Magnum { namespace Audio {
namespace Implementation { namespace Implementation {
@ -180,7 +186,7 @@ class MAGNUM_AUDIO_EXPORT Context {
* @see @ref deviceSpecifierString(), @ref Configuration::setDeviceSpecifier() * @see @ref deviceSpecifierString(), @ref Configuration::setDeviceSpecifier()
* @fn_alc{GetString} with @def_alc_keyword{DEVICE_SPECIFIER} * @fn_alc{GetString} with @def_alc_keyword{DEVICE_SPECIFIER}
*/ */
static std::vector<std::string> deviceSpecifierStrings(); static Containers::Array<Containers::StringView> deviceSpecifierStrings();
/** /**
* @brief Whether there is any current context * @brief Whether there is any current context
@ -306,11 +312,14 @@ class MAGNUM_AUDIO_EXPORT Context {
* @brief HRTF specifier * @brief HRTF specifier
* *
* Name of the HRTF being used. The result is *not* cached, repeated * Name of the HRTF being used. The result is *not* cached, repeated
* queries will result in repeated OpenAL calls. * queries will result in repeated OpenAL calls. The returned view is
* always @relativeref{Corrade::Containers::StringViewFlag,Global}, and
* if non-empty it's also
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated}.
* @see @fn_al{GetString} with @def_alc_keyword{HRTF_SPECIFIER_SOFT} * @see @fn_al{GetString} with @def_alc_keyword{HRTF_SPECIFIER_SOFT}
* @requires_al_extension @alc_extension{SOFT,HRTF} * @requires_al_extension @alc_extension{SOFT,HRTF}
*/ */
std::string hrtfSpecifierString() const; Containers::StringView hrtfSpecifierString() const;
/** /**
* @brief Count of supported mono sources * @brief Count of supported mono sources
@ -349,40 +358,48 @@ class MAGNUM_AUDIO_EXPORT Context {
* @brief Device specifier string * @brief Device specifier string
* *
* The result is *not* cached, repeated queries will result in repeated * The result is *not* cached, repeated queries will result in repeated
* OpenAL calls. * OpenAL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @ref deviceSpecifierStrings(), @ref vendorString(), @ref rendererString(), * @see @ref deviceSpecifierStrings(), @ref vendorString(), @ref rendererString(),
* @fn_al{GetString} with @def_alc_keyword{DEVICE_SPECIFIER} * @fn_al{GetString} with @def_alc_keyword{DEVICE_SPECIFIER}
*/ */
std::string deviceSpecifierString() const; Containers::StringView deviceSpecifierString() const;
/** /**
* @brief Vendor string * @brief Vendor string
* *
* The result is *not* cached, repeated queries will result in repeated * The result is *not* cached, repeated queries will result in repeated
* OpenAL calls. * OpenAL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @ref deviceSpecifierString(), @ref rendererString(), * @see @ref deviceSpecifierString(), @ref rendererString(),
* @fn_al{GetString} with @def_al_keyword{VENDOR} * @fn_al{GetString} with @def_al_keyword{VENDOR}
*/ */
std::string vendorString() const; Containers::StringView vendorString() const;
/** /**
* @brief Renderer string * @brief Renderer string
* *
* The result is *not* cached, repeated queries will result in repeated * The result is *not* cached, repeated queries will result in repeated
* OpenAL calls. * OpenAL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @ref deviceSpecifierString(), @ref vendorString(), * @see @ref deviceSpecifierString(), @ref vendorString(),
* @fn_al{GetString} with @def_al_keyword{RENDERER} * @fn_al{GetString} with @def_al_keyword{RENDERER}
*/ */
std::string rendererString() const; Containers::StringView rendererString() const;
/** /**
* @brief Version string * @brief Version string
* *
* The result is *not* cached, repeated queries will result in repeated * The result is *not* cached, repeated queries will result in repeated
* OpenAL calls. * OpenAL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @fn_al{GetString} with @def_al_keyword{VERSION} * @see @fn_al{GetString} with @def_al_keyword{VERSION}
*/ */
std::string versionString() const; Containers::StringView versionString() const;
/** /**
* @brief Extension strings * @brief Extension strings
@ -391,12 +408,15 @@ class MAGNUM_AUDIO_EXPORT Context {
* OpenAL calls. Note that this function returns list of all extensions * OpenAL calls. Note that this function returns list of all extensions
* reported by the driver (even those not supported by Magnum), see * reported by the driver (even those not supported by Magnum), see
* @ref supportedExtensions(), @ref Extension::extensions() or * @ref supportedExtensions(), @ref Extension::extensions() or
* @ref isExtensionSupported() for alternatives. * @ref isExtensionSupported() for alternatives. The returned views are
* always @relativeref{Corrade::Containers::StringViewFlag,Global}, but
* as the extensions are split from a space-separated list only the
* last one is guaranteed to be null-terminated.
* @see @fn_al{Get} with @def_al_keyword{NUM_EXTENSIONS}, * @see @fn_al{Get} with @def_al_keyword{NUM_EXTENSIONS},
* @fn_al{GetString} with @def_al_keyword{EXTENSIONS}, * @fn_al{GetString} with @def_al_keyword{EXTENSIONS},
* @fn_alc{GetString} with @def_alc_keyword{EXTENSIONS} * @fn_alc{GetString} with @def_alc_keyword{EXTENSIONS}
*/ */
std::vector<std::string> extensionStrings() const; Containers::Array<Containers::StringView> extensionStrings() const;
/** /**
* @brief Supported extensions * @brief Supported extensions
@ -405,7 +425,7 @@ class MAGNUM_AUDIO_EXPORT Context {
* the current. * the current.
* @see @ref isExtensionSupported(), @ref Extension::extensions() * @see @ref isExtensionSupported(), @ref Extension::extensions()
*/ */
const std::vector<Extension>& supportedExtensions() const { Containers::ArrayView<const Extension> supportedExtensions() const {
return _supportedExtensions; return _supportedExtensions;
} }
@ -446,7 +466,7 @@ class MAGNUM_AUDIO_EXPORT Context {
* if they are advertised as being supported by the driver. * if they are advertised as being supported by the driver.
*/ */
template<class T> bool isExtensionDisabled() const { template<class T> bool isExtensionDisabled() const {
return _disabledExtensions[T::Index]; return _extensionDisabledStatus[T::Index];
} }
/** /**
@ -457,7 +477,7 @@ class MAGNUM_AUDIO_EXPORT Context {
* as it does most operations in compile time. * as it does most operations in compile time.
*/ */
bool isExtensionDisabled(const Extension& extension) const { bool isExtensionDisabled(const Extension& extension) const {
return _disabledExtensions[extension.index()]; return _extensionDisabledStatus[extension.index()];
} }
private: private:
@ -467,9 +487,9 @@ class MAGNUM_AUDIO_EXPORT Context {
ALCcontext* _context; ALCcontext* _context;
Math::BitVector<Implementation::ExtensionCount> _extensionStatus; Math::BitVector<Implementation::ExtensionCount> _extensionStatus;
Math::BitVector<Implementation::ExtensionCount> _disabledExtensions; Math::BitVector<Implementation::ExtensionCount> _extensionDisabledStatus;
std::vector<Extension> _supportedExtensions; Containers::Array<Extension> _supportedExtensions;
std::vector<std::string> _disabledExtensionStrings; Containers::Array<Extension> _disabledExtensions;
}; };
/** /**
@ -477,7 +497,7 @@ class MAGNUM_AUDIO_EXPORT Context {
@see @ref Context() @see @ref Context()
*/ */
class MAGNUM_AUDIO_EXPORT Context::Configuration { class Context::Configuration {
public: public:
/** /**
* @brief HRTF configuration * @brief HRTF configuration
@ -491,22 +511,33 @@ class MAGNUM_AUDIO_EXPORT Context::Configuration {
Disabled = 2 /**< Disabled */ Disabled = 2 /**< Disabled */
}; };
explicit Configuration(); /**
~Configuration(); * @brief Device specifier
*
/** @brief Device specifier */ * The returned string view is
const std::string& deviceSpecifier() const { return _deviceSpecifier; } * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* is valid until the next call to @ref setDeviceSpecifier().
*/
Containers::StringView deviceSpecifier() const { return _deviceSpecifier; }
/** /**
* @brief Set device specifier * @brief Set device specifier
* @return Reference to self (for method chaining) * @return Reference to self (for method chaining)
* *
* If set to empty string (the default), default device specifier is * If set to an empty string (the default), default device specifier is
* used. * used.
*
* @note The function makes a copy of the view if it's not not global
* or null-terminated, use the
* @link Corrade::Containers::Literals::StringLiterals::operator""_s() Containers::Literals::operator""_s() @endlink
* literal to prevent that where possible.
*
* @see @ref Context::deviceSpecifierStrings() * @see @ref Context::deviceSpecifierStrings()
*/ */
Configuration& setDeviceSpecifier(const std::string& specifier); Configuration& setDeviceSpecifier(Containers::StringView specifier) {
Configuration& setDeviceSpecifier(std::string&& specifier); /**< @overload */ _deviceSpecifier = Containers::String::nullTerminatedGlobalView(specifier);
return *this;
}
/** @brief Sampling rate in Hz */ /** @brief Sampling rate in Hz */
Int frequency() const { return _frequency; } Int frequency() const { return _frequency; }
@ -592,7 +623,7 @@ class MAGNUM_AUDIO_EXPORT Context::Configuration {
} }
private: private:
std::string _deviceSpecifier; Containers::String _deviceSpecifier;
Int _frequency{-1}; Int _frequency{-1};
Hrtf _hrtf{}; Hrtf _hrtf{};

53
src/Magnum/Audio/Test/ContextALTest.cpp

@ -54,6 +54,8 @@ struct ContextALTest: TestSuite::Tester {
void isExtensionSupported(); void isExtensionSupported();
void isExtensionUnsupported(); void isExtensionUnsupported();
void isExtensionDisabled(); void isExtensionDisabled();
void stringFlags();
}; };
const struct { const struct {
@ -82,13 +84,15 @@ ContextALTest::ContextALTest():
&ContextALTest::extensionsString, &ContextALTest::extensionsString,
&ContextALTest::isExtensionSupported, &ContextALTest::isExtensionSupported,
&ContextALTest::isExtensionUnsupported, &ContextALTest::isExtensionUnsupported,
&ContextALTest::isExtensionDisabled}); &ContextALTest::isExtensionDisabled,
&ContextALTest::stringFlags});
} }
void ContextALTest::deviceSpecifierStrings() { void ContextALTest::deviceSpecifierStrings() {
/* Just verify that it produces something reasonable without crashing or /* Just verify that it produces something reasonable without crashing or
entering an infinite loop */ entering an infinite loop */
CORRADE_VERIFY(!Context::deviceSpecifierStrings().empty()); CORRADE_VERIFY(!Context::deviceSpecifierStrings().isEmpty());
} }
void ContextALTest::constructDefault() { void ContextALTest::constructDefault() {
@ -155,10 +159,10 @@ void ContextALTest::constructConfiguration() {
/* HRTF gets enabled only if the extension is supported */ /* HRTF gets enabled only if the extension is supported */
if(context.isExtensionSupported<Extensions::ALC::SOFT::HRTF>()) { if(context.isExtensionSupported<Extensions::ALC::SOFT::HRTF>()) {
CORRADE_COMPARE(context.hrtfStatus(), Context::HrtfStatus::Enabled); CORRADE_COMPARE(context.hrtfStatus(), Context::HrtfStatus::Enabled);
CORRADE_VERIFY(!context.hrtfSpecifierString().empty()); CORRADE_VERIFY(!context.hrtfSpecifierString().isEmpty());
} else if(context.isExtensionSupported<Extensions::ALC::SOFTX::HRTF>()) { } else if(context.isExtensionSupported<Extensions::ALC::SOFTX::HRTF>()) {
CORRADE_COMPARE(context.hrtfStatus(), Context::HrtfStatus::Enabled); CORRADE_COMPARE(context.hrtfStatus(), Context::HrtfStatus::Enabled);
CORRADE_VERIFY(context.hrtfSpecifierString().empty()); CORRADE_VERIFY(context.hrtfSpecifierString().isEmpty());
} else { } else {
CORRADE_COMPARE(context.hrtfStatus(), Context::HrtfStatus::Disabled); CORRADE_COMPARE(context.hrtfStatus(), Context::HrtfStatus::Disabled);
} }
@ -232,9 +236,7 @@ void ContextALTest::ignoreUnrelatedOptions() {
void ContextALTest::extensionsString() { void ContextALTest::extensionsString() {
Context context; Context context;
std::vector<std::string> extensions = context.extensionStrings(); CORRADE_VERIFY(!context.extensionStrings().isEmpty());
CORRADE_VERIFY(!extensions.empty());
} }
void ContextALTest::isExtensionSupported() { void ContextALTest::isExtensionSupported() {
@ -278,6 +280,43 @@ void ContextALTest::isExtensionDisabled() {
CORRADE_VERIFY(context.isExtensionDisabled(e)); CORRADE_VERIFY(context.isExtensionDisabled(e));
} }
void ContextALTest::stringFlags() {
Containers::Array<Containers::StringView> deviceSpecifierStrings = Context::deviceSpecifierStrings();
CORRADE_VERIFY(!deviceSpecifierStrings.isEmpty());
for(Containers::StringView specifier: deviceSpecifierStrings) {
CORRADE_VERIFY(!specifier.isEmpty());
CORRADE_COMPARE(specifier.flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
}
Context context;
/* HRTF specifier string might be null, in which case it's *not*
null-terminated obviously */
CORRADE_COMPARE(context.hrtfSpecifierString().flags(), Containers::StringViewFlag::Global|(context.hrtfSpecifierString().data() ? Containers::StringViewFlag::NullTerminated : Containers::StringViewFlags{}));
CORRADE_VERIFY(!context.deviceSpecifierString().isEmpty());
CORRADE_COMPARE(context.deviceSpecifierString().flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
CORRADE_VERIFY(!context.vendorString().isEmpty());
CORRADE_COMPARE(context.vendorString().flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
CORRADE_VERIFY(!context.rendererString().isEmpty());
CORRADE_COMPARE(context.rendererString().flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
CORRADE_VERIFY(!context.versionString().isEmpty());
CORRADE_COMPARE(context.versionString().flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
for(Containers::StringView extension: context.extensionStrings()) {
CORRADE_ITERATION(extension);
CORRADE_VERIFY(!extension.isEmpty());
/* The extensions are split from a long string and thus aren't all
null-terminated, only the last one */
CORRADE_COMPARE_AS(extension.flags(), Containers::StringViewFlag::Global,
TestSuite::Compare::GreaterOrEqual);
}
}
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::Audio::Test::ContextALTest) CORRADE_TEST_MAIN(Magnum::Audio::Test::ContextALTest)

Loading…
Cancel
Save