// Copyright (C) 2020 Jérôme Leclercq // This file is part of the "Nazara Engine - Audio module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #include #include #include #include #include namespace Nz { namespace { DynLib s_library; std::string s_deviceName; std::string s_rendererName; std::string s_vendorName; ALCdevice* s_device = nullptr; ALCcontext* s_context = nullptr; unsigned int s_version; /*! * \brief Parses the devices * \return Number of devices * * \param deviceString String for the device (input / output) * \param devices List of names of the devices */ std::size_t ParseDevices(const char* deviceString, std::vector& devices) { if (!deviceString) return 0; std::size_t startSize = devices.size(); std::size_t length; while ((length = std::strlen(deviceString)) > 0) { devices.emplace_back(deviceString, length); deviceString += length + 1; } return devices.size() - startSize; } } /*! * \ingroup audio * \class Nz::OpenAL * \brief Audio class that represents the link with OpenAL * * \remark This class is meant to be used by Module Audio */ /*! * \brief Gets the entry for the function name * \return Pointer to the function * * \param entryPoint Name of the entry * * \remark This does not produces a NazaraError if entry does not exist */ OpenALFunc OpenAL::GetEntry(const std::string& entryPoint) { return LoadEntry(entryPoint.data(), false); } /*! * \brief Gets the name of the renderer * \return Name of the renderer */ std::string OpenAL::GetRendererName() { return s_rendererName; } /*! * \brief Gets the name of the vendor * \return Name of the vendor */ std::string OpenAL::GetVendorName() { return s_vendorName; } /*! * \brief Gets the version of OpenAL * \return Version of OpenAL */ unsigned int OpenAL::GetVersion() { return s_version; } /*! * \brief Initializes the module OpenAL * \return true if initialization is successful * * \param openDevice True to get information from the device * * \remark Produces a NazaraError if one of the entry failed * \remark Produces a NazaraError if opening device failed with openDevice parameter set to true */ bool OpenAL::Initialize(bool openDevice) { if (IsInitialized()) return true; #if defined(NAZARA_PLATFORM_WINDOWS) ///FIXME: Is OpenAL Soft a better implementation than Creative ? /// If we could use OpenAL Soft everytime, this would allow us to use sonorous extensions /// and give us more technical possibilities with audio const char* libs[] = { "soft_oal.dll", "wrap_oal.dll", "openal32.dll" }; #elif defined(NAZARA_PLATFORM_LINUX) const char* libs[] = { "libopenal.so.1", "libopenal.so.0", "libopenal.so" }; #elif defined(NAZARA_PLATFORM_MACOSX) const char* libs[] = { "libopenal.dylib", "libopenal.1.dylib", }; #else NazaraError("Unknown OS"); return false; #endif bool succeeded = false; for (const char* path : libs) { ErrorFlags errFlags(ErrorMode::Silent); std::filesystem::path libPath(path); if (!s_library.Load(libPath)) continue; errFlags.SetFlags(0); try { // al alBuffer3f = reinterpret_cast(LoadEntry("alBuffer3f")); alBuffer3i = reinterpret_cast(LoadEntry("alBuffer3i")); alBufferData = reinterpret_cast(LoadEntry("alBufferData")); alBufferf = reinterpret_cast(LoadEntry("alBufferf")); alBufferfv = reinterpret_cast(LoadEntry("alBufferfv")); alBufferi = reinterpret_cast(LoadEntry("alBufferi")); alBufferiv = reinterpret_cast(LoadEntry("alBufferiv")); alDeleteBuffers = reinterpret_cast(LoadEntry("alDeleteBuffers")); alDeleteSources = reinterpret_cast(LoadEntry("alDeleteSources")); alDisable = reinterpret_cast(LoadEntry("alDisable")); alDistanceModel = reinterpret_cast(LoadEntry("alDistanceModel")); alDopplerFactor = reinterpret_cast(LoadEntry("alDopplerFactor")); alDopplerVelocity = reinterpret_cast(LoadEntry("alDopplerVelocity")); alEnable = reinterpret_cast(LoadEntry("alEnable")); alGenBuffers = reinterpret_cast(LoadEntry("alGenBuffers")); alGenSources = reinterpret_cast(LoadEntry("alGenSources")); alGetBoolean = reinterpret_cast(LoadEntry("alGetBoolean")); alGetBooleanv = reinterpret_cast(LoadEntry("alGetBooleanv")); alGetBuffer3f = reinterpret_cast(LoadEntry("alGetBuffer3f")); alGetBuffer3i = reinterpret_cast(LoadEntry("alGetBuffer3i")); alGetBufferf = reinterpret_cast(LoadEntry("alGetBufferf")); alGetBufferfv = reinterpret_cast(LoadEntry("alGetBufferfv")); alGetBufferi = reinterpret_cast(LoadEntry("alGetBufferi")); alGetBufferiv = reinterpret_cast(LoadEntry("alGetBufferiv")); alGetDouble = reinterpret_cast(LoadEntry("alGetDouble")); alGetDoublev = reinterpret_cast(LoadEntry("alGetDoublev")); alGetEnumValue = reinterpret_cast(LoadEntry("alGetEnumValue")); alGetError = reinterpret_cast(LoadEntry("alGetError")); alGetFloat = reinterpret_cast(LoadEntry("alGetFloat")); alGetFloatv = reinterpret_cast(LoadEntry("alGetFloatv")); alGetInteger = reinterpret_cast(LoadEntry("alGetInteger")); alGetIntegerv = reinterpret_cast(LoadEntry("alGetIntegerv")); alGetListener3f = reinterpret_cast(LoadEntry("alGetListener3f")); alGetListener3i = reinterpret_cast(LoadEntry("alGetListener3i")); alGetListenerf = reinterpret_cast(LoadEntry("alGetListenerf")); alGetListenerfv = reinterpret_cast(LoadEntry("alGetListenerfv")); alGetListeneri = reinterpret_cast(LoadEntry("alGetListeneri")); alGetListeneriv = reinterpret_cast(LoadEntry("alGetListeneriv")); alGetProcAddress = reinterpret_cast(LoadEntry("alGetProcAddress")); alGetSource3f = reinterpret_cast(LoadEntry("alGetSource3f")); alGetSource3i = reinterpret_cast(LoadEntry("alGetSource3i")); alGetSourcef = reinterpret_cast(LoadEntry("alGetSourcef")); alGetSourcefv = reinterpret_cast(LoadEntry("alGetSourcefv")); alGetSourcei = reinterpret_cast(LoadEntry("alGetSourcei")); alGetSourceiv = reinterpret_cast(LoadEntry("alGetSourceiv")); alGetString = reinterpret_cast(LoadEntry("alGetString")); alIsBuffer = reinterpret_cast(LoadEntry("alIsBuffer")); alIsEnabled = reinterpret_cast(LoadEntry("alIsEnabled")); alIsExtensionPresent = reinterpret_cast(LoadEntry("alIsExtensionPresent")); alIsSource = reinterpret_cast(LoadEntry("alIsSource")); alListener3f = reinterpret_cast(LoadEntry("alListener3f")); alListener3i = reinterpret_cast(LoadEntry("alListener3i")); alListenerf = reinterpret_cast(LoadEntry("alListenerf")); alListenerfv = reinterpret_cast(LoadEntry("alListenerfv")); alListeneri = reinterpret_cast(LoadEntry("alListeneri")); alListeneriv = reinterpret_cast(LoadEntry("alListeneriv")); alSource3f = reinterpret_cast(LoadEntry("alSource3f")); alSource3i = reinterpret_cast(LoadEntry("alSource3i")); alSourcef = reinterpret_cast(LoadEntry("alSourcef")); alSourcefv = reinterpret_cast(LoadEntry("alSourcefv")); alSourcei = reinterpret_cast(LoadEntry("alSourcei")); alSourceiv = reinterpret_cast(LoadEntry("alSourceiv")); alSourcePause = reinterpret_cast(LoadEntry("alSourcePause")); alSourcePausev = reinterpret_cast(LoadEntry("alSourcePausev")); alSourcePlay = reinterpret_cast(LoadEntry("alSourcePlay")); alSourcePlayv = reinterpret_cast(LoadEntry("alSourcePlayv")); alSourceQueueBuffers = reinterpret_cast(LoadEntry("alSourceQueueBuffers")); alSourceRewind = reinterpret_cast(LoadEntry("alSourceRewind")); alSourceRewindv = reinterpret_cast(LoadEntry("alSourceRewindv")); alSourceStop = reinterpret_cast(LoadEntry("alSourceStop")); alSourceStopv = reinterpret_cast(LoadEntry("alSourceStopv")); alSourceUnqueueBuffers = reinterpret_cast(LoadEntry("alSourceUnqueueBuffers")); alSpeedOfSound = reinterpret_cast(LoadEntry("alSpeedOfSound")); // alc alcCaptureCloseDevice = reinterpret_cast(LoadEntry("alcCaptureCloseDevice")); alcCaptureOpenDevice = reinterpret_cast(LoadEntry("alcCaptureOpenDevice")); alcCaptureSamples = reinterpret_cast(LoadEntry("alcCaptureSamples")); alcCaptureStart = reinterpret_cast(LoadEntry("alcCaptureStart")); alcCaptureStop = reinterpret_cast(LoadEntry("alcCaptureStop")); alcCloseDevice = reinterpret_cast(LoadEntry("alcCloseDevice")); alcCreateContext = reinterpret_cast(LoadEntry("alcCreateContext")); alcDestroyContext = reinterpret_cast(LoadEntry("alcDestroyContext")); alcGetContextsDevice = reinterpret_cast(LoadEntry("alcGetContextsDevice")); alcGetCurrentContext = reinterpret_cast(LoadEntry("alcGetCurrentContext")); alcGetEnumValue = reinterpret_cast(LoadEntry("alcGetEnumValue")); alcGetError = reinterpret_cast(LoadEntry("alcGetError")); alcGetIntegerv = reinterpret_cast(LoadEntry("alcGetIntegerv")); alcGetProcAddress = reinterpret_cast(LoadEntry("alcGetProcAddress")); alcGetString = reinterpret_cast(LoadEntry("alcGetString")); alcIsExtensionPresent = reinterpret_cast(LoadEntry("alcIsExtensionPresent")); alcMakeContextCurrent = reinterpret_cast(LoadEntry("alcMakeContextCurrent")); alcOpenDevice = reinterpret_cast(LoadEntry("alcOpenDevice")); alcProcessContext = reinterpret_cast(LoadEntry("alcProcessContext")); alcSuspendContext = reinterpret_cast(LoadEntry("alcSuspendContext")); succeeded = true; break; } catch (const std::exception& e) { NazaraWarning(libPath.generic_u8string() + " loading failed: " + std::string(e.what())); continue; } } if (!succeeded) { NazaraError("Failed to load OpenAL"); Uninitialize(); return false; } if (openDevice) OpenDevice(); return true; } /*! * \brief Checks whether the module is initialized * \return true if it is the case */ bool OpenAL::IsInitialized() { return s_library.IsLoaded(); } /*! * \brief Queries the input devices * \return Number of devices * * \param devices List of names of the input devices */ std::size_t OpenAL::QueryInputDevices(std::vector& devices) { const char* deviceString = reinterpret_cast(alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER)); if (!deviceString) return 0; return ParseDevices(deviceString, devices); } /*! * \brief Queries the output devices * \return Number of devices * * \param devices List of names of the output devices */ std::size_t OpenAL::QueryOutputDevices(std::vector& devices) { const char* deviceString = reinterpret_cast(alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER)); if (!deviceString) return 0; return ParseDevices(deviceString, devices); } /*! * \brief Sets the active device * \return true if device is successfully opened * * \param deviceName Name of the device */ bool OpenAL::SetDevice(const std::string& deviceName) { s_deviceName = deviceName; if (IsInitialized()) { CloseDevice(); return OpenDevice(); } else return true; } /*! * \brief Uninitializes the module */ void OpenAL::Uninitialize() { CloseDevice(); s_rendererName.clear(); s_vendorName.clear(); s_library.Unload(); } ALenum OpenAL::AudioFormat[AudioFormatCount] = {0}; // Added values with loading of OpenAL /*! * \brief Closes the device * * \remark Produces a NazaraWarning if you try to close an active device */ void OpenAL::CloseDevice() { if (s_device) { if (s_context) { alcMakeContextCurrent(nullptr); alcDestroyContext(s_context); s_context = nullptr; } if (!alcCloseDevice(s_device)) // We could not close the close, this means that it's still in use NazaraWarning("Failed to close device"); s_device = nullptr; } } /*! * \brief Opens the device * \return true if open is successful * * \remark Produces a NazaraError if it could not create the context */ bool OpenAL::OpenDevice() { // Initialisation of the module s_device = alcOpenDevice(s_deviceName.empty() ? nullptr : s_deviceName.data()); // We choose the default device if (!s_device) { NazaraError("Failed to open default device"); return false; } // One context is enough s_context = alcCreateContext(s_device, nullptr); if (!s_context) { NazaraError("Failed to create context"); return false; } if (!alcMakeContextCurrent(s_context)) { NazaraError("Failed to activate context"); return false; } s_rendererName = reinterpret_cast(alGetString(AL_RENDERER)); s_vendorName = reinterpret_cast(alGetString(AL_VENDOR)); const ALchar* version = alGetString(AL_VERSION); if (version) { unsigned int major = version[0] - '0'; unsigned int minor = version[2] - '0'; if (major != 0 && major <= 9) { if (minor > 9) { NazaraWarning("Unable to retrieve OpenAL minor version (using 0)"); minor = 0; } s_version = major*100 + minor*10; NazaraDebug("OpenAL version: " + NumberToString(major) + '.' + NumberToString(minor)); } else { NazaraDebug("Unable to retrieve OpenAL major version"); s_version = 0; } } else { NazaraDebug("Unable to retrieve OpenAL version"); s_version = 0; } // We complete the formats table AudioFormat[UnderlyingCast(AudioFormat::I16_Mono)] = AL_FORMAT_MONO16; AudioFormat[UnderlyingCast(AudioFormat::I16_Stereo)] = AL_FORMAT_STEREO16; // "The presence of an enum value does not guarantee the applicability of an extension to the current context." if (alIsExtensionPresent("AL_EXT_MCFORMATS")) { AudioFormat[UnderlyingCast(AudioFormat::I16_Quad)] = alGetEnumValue("AL_FORMAT_QUAD16"); AudioFormat[UnderlyingCast(AudioFormat::I16_5_1)] = alGetEnumValue("AL_FORMAT_51CHN16"); AudioFormat[UnderlyingCast(AudioFormat::I16_6_1)] = alGetEnumValue("AL_FORMAT_61CHN16"); AudioFormat[UnderlyingCast(AudioFormat::I16_7_1)] = alGetEnumValue("AL_FORMAT_71CHN16"); } else if (alIsExtensionPresent("AL_LOKI_quadriphonic")) AudioFormat[UnderlyingCast(AudioFormat::I16_Quad)] = alGetEnumValue("AL_FORMAT_QUAD16_LOKI"); return true; } /*! * \brief Loads the entry for the function name * \return Pointer to the function * * \param name Name of the entry * \param throwException Should throw exception if failed ? * * \remark Produces a std::runtime_error if entry does not exist and throwException is set to true */ OpenALFunc OpenAL::LoadEntry(const char* name, bool throwException) { OpenALFunc entry = reinterpret_cast(s_library.GetSymbol(name)); if (!entry && throwException) { std::ostringstream oss; oss << "failed to load \"" << name << '"'; throw std::runtime_error(oss.str()); } return entry; } // al OpenALDetail::LPALBUFFER3F alBuffer3f = nullptr; OpenALDetail::LPALBUFFER3I alBuffer3i = nullptr; OpenALDetail::LPALBUFFERDATA alBufferData = nullptr; OpenALDetail::LPALBUFFERF alBufferf = nullptr; OpenALDetail::LPALBUFFERFV alBufferfv = nullptr; OpenALDetail::LPALBUFFERI alBufferi = nullptr; OpenALDetail::LPALBUFFERIV alBufferiv = nullptr; OpenALDetail::LPALDELETEBUFFERS alDeleteBuffers = nullptr; OpenALDetail::LPALDELETESOURCES alDeleteSources = nullptr; OpenALDetail::LPALDISABLE alDisable = nullptr; OpenALDetail::LPALDISTANCEMODEL alDistanceModel = nullptr; OpenALDetail::LPALDOPPLERFACTOR alDopplerFactor = nullptr; OpenALDetail::LPALDOPPLERVELOCITY alDopplerVelocity = nullptr; OpenALDetail::LPALENABLE alEnable = nullptr; OpenALDetail::LPALGENBUFFERS alGenBuffers = nullptr; OpenALDetail::LPALGENSOURCES alGenSources = nullptr; OpenALDetail::LPALGETBOOLEAN alGetBoolean = nullptr; OpenALDetail::LPALGETBOOLEANV alGetBooleanv = nullptr; OpenALDetail::LPALGETBUFFER3F alGetBuffer3f = nullptr; OpenALDetail::LPALGETBUFFER3I alGetBuffer3i = nullptr; OpenALDetail::LPALGETBUFFERF alGetBufferf = nullptr; OpenALDetail::LPALGETBUFFERFV alGetBufferfv = nullptr; OpenALDetail::LPALGETBUFFERI alGetBufferi = nullptr; OpenALDetail::LPALGETBUFFERIV alGetBufferiv = nullptr; OpenALDetail::LPALGETDOUBLE alGetDouble = nullptr; OpenALDetail::LPALGETDOUBLEV alGetDoublev = nullptr; OpenALDetail::LPALGETENUMVALUE alGetEnumValue = nullptr; OpenALDetail::LPALGETERROR alGetError = nullptr; OpenALDetail::LPALGETFLOAT alGetFloat = nullptr; OpenALDetail::LPALGETFLOATV alGetFloatv = nullptr; OpenALDetail::LPALGETINTEGER alGetInteger = nullptr; OpenALDetail::LPALGETINTEGERV alGetIntegerv = nullptr; OpenALDetail::LPALGETLISTENER3F alGetListener3f = nullptr; OpenALDetail::LPALGETLISTENER3I alGetListener3i = nullptr; OpenALDetail::LPALGETLISTENERF alGetListenerf = nullptr; OpenALDetail::LPALGETLISTENERFV alGetListenerfv = nullptr; OpenALDetail::LPALGETLISTENERI alGetListeneri = nullptr; OpenALDetail::LPALGETLISTENERIV alGetListeneriv = nullptr; OpenALDetail::LPALGETPROCADDRESS alGetProcAddress = nullptr; OpenALDetail::LPALGETSOURCE3F alGetSource3f = nullptr; OpenALDetail::LPALGETSOURCE3I alGetSource3i = nullptr; OpenALDetail::LPALGETSOURCEF alGetSourcef = nullptr; OpenALDetail::LPALGETSOURCEFV alGetSourcefv = nullptr; OpenALDetail::LPALGETSOURCEI alGetSourcei = nullptr; OpenALDetail::LPALGETSOURCEIV alGetSourceiv = nullptr; OpenALDetail::LPALGETSTRING alGetString = nullptr; OpenALDetail::LPALISBUFFER alIsBuffer = nullptr; OpenALDetail::LPALISENABLED alIsEnabled = nullptr; OpenALDetail::LPALISEXTENSIONPRESENT alIsExtensionPresent = nullptr; OpenALDetail::LPALISSOURCE alIsSource = nullptr; OpenALDetail::LPALLISTENER3F alListener3f = nullptr; OpenALDetail::LPALLISTENER3I alListener3i = nullptr; OpenALDetail::LPALLISTENERF alListenerf = nullptr; OpenALDetail::LPALLISTENERFV alListenerfv = nullptr; OpenALDetail::LPALLISTENERI alListeneri = nullptr; OpenALDetail::LPALLISTENERIV alListeneriv = nullptr; OpenALDetail::LPALSOURCE3F alSource3f = nullptr; OpenALDetail::LPALSOURCE3I alSource3i = nullptr; OpenALDetail::LPALSOURCEF alSourcef = nullptr; OpenALDetail::LPALSOURCEFV alSourcefv = nullptr; OpenALDetail::LPALSOURCEI alSourcei = nullptr; OpenALDetail::LPALSOURCEIV alSourceiv = nullptr; OpenALDetail::LPALSOURCEPAUSE alSourcePause = nullptr; OpenALDetail::LPALSOURCEPAUSEV alSourcePausev = nullptr; OpenALDetail::LPALSOURCEPLAY alSourcePlay = nullptr; OpenALDetail::LPALSOURCEPLAYV alSourcePlayv = nullptr; OpenALDetail::LPALSOURCEQUEUEBUFFERS alSourceQueueBuffers = nullptr; OpenALDetail::LPALSOURCEREWIND alSourceRewind = nullptr; OpenALDetail::LPALSOURCEREWINDV alSourceRewindv = nullptr; OpenALDetail::LPALSOURCESTOP alSourceStop = nullptr; OpenALDetail::LPALSOURCESTOPV alSourceStopv = nullptr; OpenALDetail::LPALSOURCEUNQUEUEBUFFERS alSourceUnqueueBuffers = nullptr; OpenALDetail::LPALSPEEDOFSOUND alSpeedOfSound = nullptr; // alc OpenALDetail::LPALCCAPTURECLOSEDEVICE alcCaptureCloseDevice = nullptr; OpenALDetail::LPALCCAPTUREOPENDEVICE alcCaptureOpenDevice = nullptr; OpenALDetail::LPALCCAPTURESAMPLES alcCaptureSamples = nullptr; OpenALDetail::LPALCCAPTURESTART alcCaptureStart = nullptr; OpenALDetail::LPALCCAPTURESTOP alcCaptureStop = nullptr; OpenALDetail::LPALCCLOSEDEVICE alcCloseDevice = nullptr; OpenALDetail::LPALCCREATECONTEXT alcCreateContext = nullptr; OpenALDetail::LPALCDESTROYCONTEXT alcDestroyContext = nullptr; OpenALDetail::LPALCGETCONTEXTSDEVICE alcGetContextsDevice = nullptr; OpenALDetail::LPALCGETCURRENTCONTEXT alcGetCurrentContext = nullptr; OpenALDetail::LPALCGETENUMVALUE alcGetEnumValue = nullptr; OpenALDetail::LPALCGETERROR alcGetError = nullptr; OpenALDetail::LPALCGETINTEGERV alcGetIntegerv = nullptr; OpenALDetail::LPALCGETPROCADDRESS alcGetProcAddress = nullptr; OpenALDetail::LPALCGETSTRING alcGetString = nullptr; OpenALDetail::LPALCISEXTENSIONPRESENT alcIsExtensionPresent = nullptr; OpenALDetail::LPALCMAKECONTEXTCURRENT alcMakeContextCurrent = nullptr; OpenALDetail::LPALCOPENDEVICE alcOpenDevice = nullptr; OpenALDetail::LPALCPROCESSCONTEXT alcProcessContext = nullptr; OpenALDetail::LPALCSUSPENDCONTEXT alcSuspendContext = nullptr; }