Browse Source

Audio: Initial Support for OpenAL Extensions (and ALC_SOFTX_HRTF)

Signed-off-by: Squareys <Squareys@googlemail.com>
pull/111/head
Squareys 11 years ago
parent
commit
4f5551dbec
  1. 93
      src/Magnum/Audio/Context.cpp
  2. 250
      src/Magnum/Audio/Context.h

93
src/Magnum/Audio/Context.cpp

@ -3,6 +3,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015
Vladimír Vondruš <mosra@centrum.cz>
Copyright © 2015 Jonathan Hale <squareys@googlemail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@ -25,17 +26,36 @@
#include "Context.h"
#include <unordered_map>
#include <al.h>
#include <alc.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>
#include <Corrade/Utility/String.h>
#include "Magnum/Magnum.h"
#include "Magnum/Audio/Extensions.h"
namespace Magnum { namespace Audio {
const std::vector<Extension>& Extension::extensions() {
#define _extension(prefix, vendor, extension) {Extensions::prefix::vendor::extension::Index, Extensions::prefix::vendor::extension::string()}
static const std::vector<Extension> extensions{
_extension(AL,EXT,FLOAT32),
_extension(AL,EXT,DOUBLE),
_extension(ALC,SOFTX,HRTF)
};
#undef _entension
return extensions;
}
Context* Context::_current = nullptr;
Context::Context() {
Context::Context(): Context{Configuration{}} {}
Context::Context(const Configuration& config) {
CORRADE_ASSERT(!_current, "Audio::Context: context already created", );
/* Open default device */
@ -46,12 +66,26 @@ Context::Context() {
std::exit(1);
}
_context = alcCreateContext(_device, nullptr);
if(!_context) {
if(!tryCreateContext(config)) {
Error() << "Audio::Context: cannot create context:" << alcGetError(_device);
std::exit(1);
}
/* Add all extensions to a map for faster lookup */
std::unordered_map<std::string, Extension> extensionMap;
for(const Extension& extension: Extension::extensions())
extensionMap.emplace(extension._string, extension);
/* Check for presence of extensions */
const std::vector<std::string> extensions = extensionStrings();
for(const std::string& extension: extensions) {
const auto found = extensionMap.find(extension);
if(found != extensionMap.end()) {
_supportedExtensions.push_back(found->second);
_extensionStatus.set(found->second._index);
}
}
alcMakeContextCurrent(_context);
_current = this;
@ -67,4 +101,55 @@ Context::~Context() {
alcCloseDevice(_device);
}
std::vector<std::string> Context::extensionStrings() const {
std::vector<std::string> extensions;
/* Don't crash when glGetString() returns nullptr */
const char* e = reinterpret_cast<const char*>(alGetString(AL_EXTENSIONS));
if(e) extensions = Utility::String::splitWithoutEmptyParts(e, ' ');
return extensions;
}
bool Context::tryCreateContext(const Configuration& config) {
/* The following parameters are order dependent!
Make sure to always add sufficient space at end of the attributes
array.*/
Int attributes[]{
ALC_FREQUENCY, config.frequency(),
0, 0,
0, 0,
0, 0,
0, 0,
0 /* sentinel */
};
/* last valid index in the attributes array */
int last = 1;
if(config.isHrtfEnabled() != Configuration::EnabledState::Default) {
attributes[++last] = ALC_HRTF_SOFT;
attributes[++last] = (config.isHrtfEnabled() == Configuration::EnabledState::Enabled)
? ALC_TRUE : ALC_FALSE;
}
if(config.monoSourcesCount() != -1) {
attributes[++last] = ALC_MONO_SOURCES;
attributes[++last] = config.monoSourcesCount();
}
if(config.stereoSourcesCount() != -1) {
attributes[++last] = ALC_STEREO_SOURCES;
attributes[++last] = config.stereoSourcesCount();
}
if(config.refreshRate() != -1) {
attributes[++last] = ALC_REFRESH;
attributes[++last] = config.refreshRate();
}
_context = alcCreateContext(_device, attributes);
return !!_context;
}
}}

250
src/Magnum/Audio/Context.h

@ -5,6 +5,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015
Vladimír Vondruš <mosra@centrum.cz>
Copyright © 2015 Jonathan Hale <squareys@googlemail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@ -30,9 +31,18 @@
*/
#include <string>
#include <vector>
#include <array>
#include <bitset>
#include <al.h>
#include <Corrade/Containers/EnumSet.h>
#include <Magnum/Magnum.h>
#include "Magnum/Audio/visibility.h"
#include "Magnum/Audio/Buffer.h"
#ifndef DOXYGEN_GENERATING_OUTPUT
typedef struct ALCdevice_struct ALCdevice;
@ -41,6 +51,36 @@ typedef struct ALCcontext_struct ALCcontext;
namespace Magnum { namespace Audio {
class Context;
/**
@brief Run-time information about OpenAL extension
Encapsulates runtime information about OpenAL extension, such as name string,
minimal required OpenAL version and version in which the extension was adopted
to core.
See also @ref Audio::Extensions namespace, which contain compile-time information
about OpenAL extensions.
*/
class MAGNUM_AUDIO_EXPORT Extension {
friend Context;
public:
/** @brief All OpenAL extensions */
static const std::vector<Extension>& extensions();
/** @brief Extension string */
constexpr const char* string() const { return _string; }
private:
/* MSVC seems to have problems with const members */
std::size_t _index;
const char* _string;
constexpr Extension(std::size_t index, const char* string): _index(index), _string(string) {}
};
/**
@brief OpenAL context
*/
@ -49,12 +89,19 @@ class MAGNUM_AUDIO_EXPORT Context {
/** @brief Current context */
static Context* current() { return _current; }
class Configuration;
/**
* @brief Constructor
*
* Creates OpenAL context.
* Creates OpenAL context with given configuration.
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
explicit Context(const Configuration& configuration = Configuration());
#else
explicit Context(const Configuration& configuration);
explicit Context();
#endif
/**
* @brief Destructor
@ -84,13 +131,214 @@ class MAGNUM_AUDIO_EXPORT Context {
*/
std::string versionString() const { return alGetString(AL_VERSION); }
/**
* @brief Extension strings
*
* The result is *not* cached, repeated queries will result in repeated
* OpenAL calls. Note that this function returns list of all extensions
* reported by the driver (even those not supported by Magnum), see
* @ref supportedExtensions(), @ref Extension::extensions() or
* @ref isExtensionSupported() for alternatives.
* @see @fn_al{Get} with @def_al{NUM_EXTENSIONS}, @fn_al{GetString}
* with @def_al{EXTENSIONS}
*/
std::vector<std::string> extensionStrings() const;
/**
* @brief Supported extensions
*
* The list contains only extensions from OpenAL versions newer than
* the current.
* @see @ref isExtensionSupported(), @ref Extension::extensions()
*/
const std::vector<Extension>& supportedExtensions() const {
return _supportedExtensions;
}
/**
* @brief Whether given extension is supported
*
* Extensions usable with this function are listed in @ref Extensions
* namespace in header @ref Extensions.h. Example usage:
* @code
* if(Context::current()->isExtensionSupported<Extensions::ALC::SOFTX::HRTF>()) {
* // amazing binaural audio
* } else {
* // probably left/right stereo only
* }
* @endcode
*
* @see @ref isExtensionSupported(const Extension&) const,
* @ref MAGNUM_AUDIO_ASSERT_EXTENSION_SUPPORTED()
*/
template<class T> bool isExtensionSupported() const {
return _extensionStatus[T::Index];
}
/**
* @brief Whether given extension is supported
*
* Can be used e.g. for listing extensions available on current
* hardware, but for general usage prefer @ref isExtensionSupported() const,
* as it does most operations in compile time.
* @see @ref supportedExtensions(), @ref Extension::extensions(),
* @ref MAGNUM_AUDIO_ASSERT_EXTENSION_SUPPORTED()
*/
bool isExtensionSupported(const Extension& extension) const {
return _extensionStatus[extension._index];
}
private:
static Context* _current;
/* Create a context with given configuration. Returns `true` on success.
* @ref alcCreateContext(). */
bool tryCreateContext(const Configuration& config);
ALCdevice* _device;
ALCcontext* _context;
std::bitset<64> _extensionStatus;
std::vector<Extension> _supportedExtensions;
};
class MAGNUM_AUDIO_EXPORT Context::Configuration {
public:
/**
* @brief Enum for boolean values with a driver specific default
* value
*/
enum class EnabledState: Byte {
Default = 0,
Enabled = 1,
Disabled = 2
};
/**
* @brief Constructor
*/
explicit Configuration():
_frequency(44100),
_enableHrtf(),
_monoSources(-1),
_stereoSources(-1),
_refreshRate(-1)
{}
/** @brief Sampling rate in Hz */
Int frequency() const { return _frequency; }
/**
* @brief Set sampling rate (in Hz)
*
* Default is `44100`.
*/
Configuration& setFrequency(Int freq) {
_frequency = freq;
return *this;
}
/** @brief Whether to use hrtfs */
EnabledState isHrtfEnabled() const { return _enableHrtf; }
/**
* @brief Set whether to use hrtfs
*
* Defaults to local OpenAL configuration or false.
*/
Configuration& setHrtfEnabled(EnabledState hrtf) {
_enableHrtf = hrtf;
return *this;
}
/**
* @brief Hint for how mono sources to support
*
* Returns `-1`, if no hint was set.
*/
Int monoSourcesCount() const { return _monoSources; }
/**
* @brief Set hint for how mono sources to support
*
* If not set, no hint will given to OpenAL.
*/
Configuration& setMonoSourcesCount(Int sources) {
_monoSources = sources;
return *this;
}
/**
* @brief Hint for how stereo sources to support
*
* Returns `-1`, if no hint was set.
*/
Int stereoSourcesCount() const { return _stereoSources; }
/**
* @brief Set hint for how stereo sources to support
*
* If not set, no hint will given to OpenAL.
*/
Configuration& setStereoSourcesCount(Int sources) {
_stereoSources = sources;
return *this;
}
/** @brief Rate at which the OpenAL device is refreshed (in Hz) */
Int refreshRate() const { return _refreshRate; }
/**
* @brief Set rate at which the OpenAL device is refreshed (in Hz)
* @return Reference to self (for method chaining)
*/
Configuration& setRefreshRate(Int rate) {
_refreshRate = rate;
return *this;
}
private:
Int _frequency;
EnabledState _enableHrtf;
Int _monoSources;
Int _stereoSources;
Int _refreshRate;
};
/** @hideinitializer
@brief Assert that given OpenAL extension is supported
@param extension Extension name (from
@ref Magnum::Audio::Extensions "Audio::Extensions" namespace)
Useful for initial checks on availability of required features.
By default, if assertion fails, an message is printed to error output and the
application aborts. If `CORRADE_NO_ASSERT` is defined, this macro does nothing.
Example usage:
@code
MAGNUM_ASSERT_AUDIO_EXTENSION_SUPPORTED(Extensions::ALC::SOFTX::HRTF);
@endcode
@see @ref Magnum::Audio::Context::isExtensionSupported() "Audio::Context::isExtensionSupported()",
@ref CORRADE_ASSERT(), @ref CORRADE_INTERNAL_ASSERT()
*/
#ifdef CORRADE_NO_ASSERT
#define MAGNUM_ASSERT_AUDIO_EXTENSION_SUPPORTED(extension) do {} while(0)
#else
#define MAGNUM_ASSERT_AUDIO_EXTENSION_SUPPORTED(extension) \
do { \
if(!Magnum::Audio::Context::current()->isExtensionSupported<extension>()) { \
Corrade::Utility::Error() << "Magnum: required OpenAL extension" << extension::string() << "is not supported"; \
std::abort(); \
} \
} while(0)
#endif
}}
#endif

Loading…
Cancel
Save