mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-13 15:44:36 +00:00
update openal-soft to 1.24.3
keeping the alt 87514151c4 (diff-73a8dc1ce58605f6c5ea53548454c3bae516ec5132a29c9d7ff7edf9730c75be)
This commit is contained in:
parent
12db0500e8
commit
ba32094b7b
276 changed files with 49304 additions and 8712 deletions
393
Engine/lib/openal-soft/alc/backends/sdl3.cpp
Normal file
393
Engine/lib/openal-soft/alc/backends/sdl3.cpp
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2024 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sdl3.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
_Pragma("GCC diagnostic push")
|
||||
_Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
|
||||
#include "SDL3/SDL_audio.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include "SDL3/SDL_stdinc.h"
|
||||
_Pragma("GCC diagnostic pop")
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
_Pragma("GCC diagnostic push")
|
||||
_Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
|
||||
constexpr auto DefaultPlaybackDeviceID = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
_Pragma("GCC diagnostic pop")
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct SdlDeleter {
|
||||
/* NOLINTNEXTLINE(cppcoreguidelines-no-malloc) */
|
||||
void operator()(gsl::owner<T*> ptr) const { SDL_free(ptr); }
|
||||
};
|
||||
template<typename T>
|
||||
using unique_sdl_ptr = std::unique_ptr<T,SdlDeleter<T>>;
|
||||
|
||||
|
||||
struct DeviceEntry {
|
||||
std::string mName;
|
||||
SDL_AudioDeviceID mPhysDeviceID{};
|
||||
};
|
||||
|
||||
std::vector<DeviceEntry> gPlaybackDevices;
|
||||
|
||||
void EnumeratePlaybackDevices()
|
||||
{
|
||||
auto numdevs = int{};
|
||||
auto devicelist = unique_sdl_ptr<SDL_AudioDeviceID>{SDL_GetAudioPlaybackDevices(&numdevs)};
|
||||
if(!devicelist || numdevs < 0)
|
||||
{
|
||||
ERR("Failed to get playback devices: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
auto devids = al::span{devicelist.get(), static_cast<uint>(numdevs)};
|
||||
auto newlist = std::vector<DeviceEntry>{};
|
||||
|
||||
newlist.reserve(devids.size());
|
||||
std::transform(devids.begin(), devids.end(), std::back_inserter(newlist),
|
||||
[](SDL_AudioDeviceID id)
|
||||
{
|
||||
auto *name = SDL_GetAudioDeviceName(id);
|
||||
if(!name) return DeviceEntry{};
|
||||
TRACE("Got device \"{}\", ID {}", name, id);
|
||||
return DeviceEntry{name, id};
|
||||
});
|
||||
|
||||
gPlaybackDevices.swap(newlist);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto getDefaultDeviceName() noexcept -> std::string_view
|
||||
{ return "Default Device"sv; }
|
||||
|
||||
|
||||
struct Sdl3Backend final : public BackendBase {
|
||||
explicit Sdl3Backend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~Sdl3Backend() final;
|
||||
|
||||
void audioCallback(SDL_AudioStream *stream, int additional_amount, int total_amount) noexcept;
|
||||
|
||||
void open(std::string_view name) final;
|
||||
auto reset() -> bool final;
|
||||
void start() final;
|
||||
void stop() final;
|
||||
|
||||
SDL_AudioDeviceID mDeviceID{0};
|
||||
SDL_AudioStream *mStream{nullptr};
|
||||
uint mNumChannels{0};
|
||||
uint mFrameSize{0};
|
||||
std::vector<std::byte> mBuffer;
|
||||
};
|
||||
|
||||
Sdl3Backend::~Sdl3Backend()
|
||||
{
|
||||
if(mStream)
|
||||
SDL_DestroyAudioStream(mStream);
|
||||
mStream = nullptr;
|
||||
}
|
||||
|
||||
void Sdl3Backend::audioCallback(SDL_AudioStream *stream, int additional_amount, int total_amount)
|
||||
noexcept
|
||||
{
|
||||
if(additional_amount < 0)
|
||||
additional_amount = total_amount;
|
||||
if(additional_amount <= 0)
|
||||
return;
|
||||
|
||||
const auto ulen = static_cast<unsigned int>(additional_amount);
|
||||
assert((ulen % mFrameSize) == 0);
|
||||
|
||||
if(ulen > mBuffer.size())
|
||||
{
|
||||
mBuffer.resize(ulen);
|
||||
std::fill(mBuffer.begin(), mBuffer.end(), (mDevice->FmtType == DevFmtUByte)
|
||||
? std::byte{0x80} : std::byte{});
|
||||
}
|
||||
|
||||
mDevice->renderSamples(mBuffer.data(), ulen / mFrameSize, mNumChannels);
|
||||
SDL_PutAudioStreamData(stream, mBuffer.data(), additional_amount);
|
||||
}
|
||||
|
||||
void Sdl3Backend::open(std::string_view name)
|
||||
{
|
||||
const auto defaultDeviceName = getDefaultDeviceName();
|
||||
if(name.empty() || name == defaultDeviceName)
|
||||
{
|
||||
name = defaultDeviceName;
|
||||
mDeviceID = DefaultPlaybackDeviceID;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(gPlaybackDevices.empty())
|
||||
EnumeratePlaybackDevices();
|
||||
|
||||
const auto iter = std::find_if(gPlaybackDevices.cbegin(), gPlaybackDevices.cend(),
|
||||
[name](const DeviceEntry &entry) { return name == entry.mName; });
|
||||
if(iter == gPlaybackDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "No device named {}", name};
|
||||
|
||||
mDeviceID = iter->mPhysDeviceID;
|
||||
}
|
||||
|
||||
mStream = SDL_OpenAudioDeviceStream(mDeviceID, nullptr, nullptr, nullptr);
|
||||
if(!mStream)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()};
|
||||
|
||||
auto have = SDL_AudioSpec{};
|
||||
auto update_size = int{};
|
||||
if(SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(mStream), &have, &update_size))
|
||||
{
|
||||
auto devtype = mDevice->FmtType;
|
||||
switch(have.format)
|
||||
{
|
||||
case SDL_AUDIO_U8: devtype = DevFmtUByte; break;
|
||||
case SDL_AUDIO_S8: devtype = DevFmtByte; break;
|
||||
case SDL_AUDIO_S16: devtype = DevFmtShort; break;
|
||||
case SDL_AUDIO_S32: devtype = DevFmtInt; break;
|
||||
case SDL_AUDIO_F32: devtype = DevFmtFloat; break;
|
||||
default: break;
|
||||
}
|
||||
mDevice->FmtType = devtype;
|
||||
|
||||
if(have.freq >= int{MinOutputRate} && have.freq <= int{MaxOutputRate})
|
||||
mDevice->mSampleRate = static_cast<uint>(have.freq);
|
||||
|
||||
/* SDL guarantees these layouts for the given channel count. */
|
||||
if(have.channels == 8)
|
||||
mDevice->FmtChans = DevFmtX71;
|
||||
else if(have.channels == 7)
|
||||
mDevice->FmtChans = DevFmtX61;
|
||||
else if(have.channels == 6)
|
||||
mDevice->FmtChans = DevFmtX51;
|
||||
else if(have.channels == 4)
|
||||
mDevice->FmtChans = DevFmtQuad;
|
||||
else if(have.channels >= 2)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
else if(have.channels == 1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
mDevice->mAmbiOrder = 0;
|
||||
|
||||
mNumChannels = static_cast<uint>(have.channels);
|
||||
mFrameSize = mDevice->bytesFromFmt() * mNumChannels;
|
||||
|
||||
if(update_size >= 64)
|
||||
{
|
||||
/* We have to assume the total buffer size is just twice the update
|
||||
* size. SDL doesn't tell us the full end-to-end buffer latency.
|
||||
*/
|
||||
mDevice->mUpdateSize = static_cast<uint>(update_size);
|
||||
mDevice->mBufferSize = mDevice->mUpdateSize*2u;
|
||||
}
|
||||
else
|
||||
ERR("Invalid update size from SDL stream: {}", update_size);
|
||||
}
|
||||
else
|
||||
ERR("Failed to get format from SDL stream: {}", SDL_GetError());
|
||||
|
||||
mDeviceName = name;
|
||||
}
|
||||
|
||||
auto Sdl3Backend::reset() -> bool
|
||||
{
|
||||
static constexpr auto callback = [](void *ptr, SDL_AudioStream *stream, int additional_amount,
|
||||
int total_amount) noexcept
|
||||
{
|
||||
return static_cast<Sdl3Backend*>(ptr)->audioCallback(stream, additional_amount,
|
||||
total_amount);
|
||||
};
|
||||
|
||||
if(mStream)
|
||||
SDL_DestroyAudioStream(mStream);
|
||||
mStream = nullptr;
|
||||
|
||||
mBuffer.clear();
|
||||
mBuffer.shrink_to_fit();
|
||||
|
||||
auto want = SDL_AudioSpec{};
|
||||
if(!SDL_GetAudioDeviceFormat(mDeviceID, &want, nullptr))
|
||||
ERR("Failed to get device format: {}", SDL_GetError());
|
||||
|
||||
if(mDevice->Flags.test(FrequencyRequest) || want.freq < int{MinOutputRate})
|
||||
want.freq = static_cast<int>(mDevice->mSampleRate);
|
||||
if(mDevice->Flags.test(SampleTypeRequest)
|
||||
|| !(want.format == SDL_AUDIO_U8 || want.format == SDL_AUDIO_S8
|
||||
|| want.format == SDL_AUDIO_S16 || want.format == SDL_AUDIO_S32
|
||||
|| want.format == SDL_AUDIO_F32))
|
||||
{
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtUByte: want.format = SDL_AUDIO_U8; break;
|
||||
case DevFmtByte: want.format = SDL_AUDIO_S8; break;
|
||||
case DevFmtUShort: [[fallthrough]];
|
||||
case DevFmtShort: want.format = SDL_AUDIO_S16; break;
|
||||
case DevFmtUInt: [[fallthrough]];
|
||||
case DevFmtInt: want.format = SDL_AUDIO_S32; break;
|
||||
case DevFmtFloat: want.format = SDL_AUDIO_F32; break;
|
||||
}
|
||||
}
|
||||
if(mDevice->Flags.test(ChannelsRequest) || want.channels < 1)
|
||||
want.channels = static_cast<int>(std::min<uint>(mDevice->channelsFromFmt(),
|
||||
std::numeric_limits<int>::max()));
|
||||
|
||||
mStream = SDL_OpenAudioDeviceStream(mDeviceID, &want, callback, this);
|
||||
if(!mStream)
|
||||
{
|
||||
/* If creating the stream failed, try again without a specific format. */
|
||||
mStream = SDL_OpenAudioDeviceStream(mDeviceID, nullptr, callback, this);
|
||||
if(!mStream)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to recreate stream: {}", SDL_GetError()};
|
||||
}
|
||||
|
||||
auto update_size = int{};
|
||||
auto have = SDL_AudioSpec{};
|
||||
SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(mStream), &have, &update_size);
|
||||
|
||||
have = SDL_AudioSpec{};
|
||||
if(!SDL_GetAudioStreamFormat(mStream, &have, nullptr))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to get stream format: {}", SDL_GetError()};
|
||||
|
||||
if(!mDevice->Flags.test(ChannelsRequest)
|
||||
|| (static_cast<uint>(have.channels) != mDevice->channelsFromFmt()
|
||||
&& !(mDevice->FmtChans == DevFmtStereo && have.channels >= 2)))
|
||||
{
|
||||
/* SDL guarantees these layouts for the given channel count. */
|
||||
if(have.channels == 8)
|
||||
mDevice->FmtChans = DevFmtX71;
|
||||
else if(have.channels == 7)
|
||||
mDevice->FmtChans = DevFmtX61;
|
||||
else if(have.channels == 6)
|
||||
mDevice->FmtChans = DevFmtX51;
|
||||
else if(have.channels == 4)
|
||||
mDevice->FmtChans = DevFmtQuad;
|
||||
else if(have.channels >= 2)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
else if(have.channels == 1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
else
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Unhandled SDL channel count: {}", have.channels};
|
||||
mDevice->mAmbiOrder = 0;
|
||||
}
|
||||
mNumChannels = static_cast<uint>(have.channels);
|
||||
|
||||
switch(have.format)
|
||||
{
|
||||
case SDL_AUDIO_U8: mDevice->FmtType = DevFmtUByte; break;
|
||||
case SDL_AUDIO_S8: mDevice->FmtType = DevFmtByte; break;
|
||||
case SDL_AUDIO_S16: mDevice->FmtType = DevFmtShort; break;
|
||||
case SDL_AUDIO_S32: mDevice->FmtType = DevFmtInt; break;
|
||||
case SDL_AUDIO_F32: mDevice->FmtType = DevFmtFloat; break;
|
||||
default:
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Unhandled SDL format: {:#04x}", al::to_underlying(have.format)};
|
||||
}
|
||||
|
||||
mFrameSize = mDevice->bytesFromFmt() * mNumChannels;
|
||||
|
||||
if(have.freq < int{MinOutputRate})
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Unhandled SDL sample rate: {}", have.freq};
|
||||
mDevice->mSampleRate = static_cast<uint>(have.freq);
|
||||
|
||||
if(update_size >= 64)
|
||||
{
|
||||
mDevice->mUpdateSize = static_cast<uint>(update_size);
|
||||
mDevice->mBufferSize = mDevice->mUpdateSize*2u;
|
||||
|
||||
mBuffer.resize(size_t{mDevice->mUpdateSize} * mFrameSize);
|
||||
std::fill(mBuffer.begin(), mBuffer.end(), (mDevice->FmtType == DevFmtUByte)
|
||||
? std::byte{0x80} : std::byte{});
|
||||
}
|
||||
else
|
||||
ERR("Invalid update size from SDL stream: {}", update_size);
|
||||
|
||||
setDefaultWFXChannelOrder();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sdl3Backend::start()
|
||||
{ SDL_ResumeAudioStreamDevice(mStream); }
|
||||
|
||||
void Sdl3Backend::stop()
|
||||
{ SDL_PauseAudioStreamDevice(mStream); }
|
||||
|
||||
} // namespace
|
||||
|
||||
auto SDL3BackendFactory::getFactory() -> BackendFactory&
|
||||
{
|
||||
static SDL3BackendFactory factory{};
|
||||
return factory;
|
||||
}
|
||||
|
||||
auto SDL3BackendFactory::init() -> bool
|
||||
{
|
||||
if(!SDL_InitSubSystem(SDL_INIT_AUDIO))
|
||||
return false;
|
||||
TRACE("Current SDL3 audio driver: \"{}\"", SDL_GetCurrentAudioDriver());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto SDL3BackendFactory::querySupport(BackendType type) -> bool
|
||||
{ return type == BackendType::Playback; }
|
||||
|
||||
auto SDL3BackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
auto outnames = std::vector<std::string>{};
|
||||
|
||||
if(type != BackendType::Playback)
|
||||
return outnames;
|
||||
|
||||
EnumeratePlaybackDevices();
|
||||
outnames.reserve(gPlaybackDevices.size()+1);
|
||||
outnames.emplace_back(getDefaultDeviceName());
|
||||
std::transform(gPlaybackDevices.begin(), gPlaybackDevices.end(), std::back_inserter(outnames),
|
||||
std::mem_fn(&DeviceEntry::mName));
|
||||
|
||||
return outnames;
|
||||
}
|
||||
|
||||
auto SDL3BackendFactory::createBackend(DeviceBase *device, BackendType type) -> BackendPtr
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new Sdl3Backend{device}};
|
||||
return nullptr;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue