update openal

This commit is contained in:
AzaezelX 2024-06-30 14:35:57 -05:00
parent 62f3b93ff9
commit 6721a6b021
287 changed files with 33851 additions and 27325 deletions

View file

@ -31,24 +31,26 @@
#include <cstring>
#include <exception>
#include <functional>
#include <system_error>
#include <thread>
#include <vector>
#include "albit.h"
#include "albyte.h"
#include "alc/alconfig.h"
#include "almalloc.h"
#include "alnumeric.h"
#include "alstring.h"
#include "althrd_setname.h"
#include "core/device.h"
#include "core/helpers.h"
#include "core/logging.h"
#include "opthelpers.h"
#include "strutils.h"
#include "threads.h"
#include "vector.h"
namespace {
using namespace std::string_view_literals;
using std::chrono::seconds;
using std::chrono::milliseconds;
using std::chrono::nanoseconds;
@ -56,38 +58,43 @@ using std::chrono::nanoseconds;
using ubyte = unsigned char;
using ushort = unsigned short;
constexpr char waveDevice[] = "Wave File Writer";
struct FileDeleter {
void operator()(gsl::owner<FILE*> f) { fclose(f); }
};
using FilePtr = std::unique_ptr<FILE,FileDeleter>;
constexpr ubyte SUBTYPE_PCM[]{
[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Wave File Writer"sv; }
constexpr std::array<ubyte,16> SUBTYPE_PCM{{
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
0x00, 0x38, 0x9b, 0x71
};
constexpr ubyte SUBTYPE_FLOAT[]{
}};
constexpr std::array<ubyte,16> SUBTYPE_FLOAT{{
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
0x00, 0x38, 0x9b, 0x71
};
}};
constexpr ubyte SUBTYPE_BFORMAT_PCM[]{
constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_PCM{{
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
0xca, 0x00, 0x00, 0x00
};
}};
constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{
constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_FLOAT{{
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
0xca, 0x00, 0x00, 0x00
};
}};
void fwrite16le(ushort val, FILE *f)
{
ubyte data[2]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff) };
fwrite(data, 1, 2, f);
std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff)};
fwrite(data.data(), 1, data.size(), f);
}
void fwrite32le(uint val, FILE *f)
{
ubyte data[4]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff),
static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff) };
fwrite(data, 1, 4, f);
std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff),
static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff)};
fwrite(data.data(), 1, data.size(), f);
}
@ -97,34 +104,27 @@ struct WaveBackend final : public BackendBase {
int mixerProc();
void open(const char *name) override;
void open(std::string_view name) override;
bool reset() override;
void start() override;
void stop() override;
FILE *mFile{nullptr};
FilePtr mFile{nullptr};
long mDataStart{-1};
al::vector<al::byte> mBuffer;
std::vector<std::byte> mBuffer;
std::atomic<bool> mKillNow{true};
std::thread mThread;
DEF_NEWDEL(WaveBackend)
};
WaveBackend::~WaveBackend()
{
if(mFile)
fclose(mFile);
mFile = nullptr;
}
WaveBackend::~WaveBackend() = default;
int WaveBackend::mixerProc()
{
const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
althrd_setname(MIXER_THREAD_NAME);
althrd_setname(GetMixerThreadName());
const size_t frameStep{mDevice->channelsFromFmt()};
const size_t frameSize{mDevice->frameSizeFromFmt()};
@ -155,13 +155,13 @@ int WaveBackend::mixerProc()
if(bytesize == 2)
{
const size_t len{mBuffer.size() & ~size_t{1}};
const size_t len{mBuffer.size() & ~1_uz};
for(size_t i{0};i < len;i+=2)
std::swap(mBuffer[i], mBuffer[i+1]);
}
else if(bytesize == 4)
{
const size_t len{mBuffer.size() & ~size_t{3}};
const size_t len{mBuffer.size() & ~3_uz};
for(size_t i{0};i < len;i+=4)
{
std::swap(mBuffer[i ], mBuffer[i+3]);
@ -170,8 +170,8 @@ int WaveBackend::mixerProc()
}
}
const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile)};
if(fs < mDevice->UpdateSize || ferror(mFile))
const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile.get())};
if(fs < mDevice->UpdateSize || ferror(mFile.get()))
{
ERR("Error writing to file\n");
mDevice->handleDisconnect("Failed to write playback samples");
@ -195,32 +195,32 @@ int WaveBackend::mixerProc()
return 0;
}
void WaveBackend::open(const char *name)
void WaveBackend::open(std::string_view name)
{
auto fname = ConfigValueStr(nullptr, "wave", "file");
auto fname = ConfigValueStr({}, "wave", "file");
if(!fname) throw al::backend_exception{al::backend_error::NoDevice,
"No wave output filename"};
if(!name)
name = waveDevice;
else if(strcmp(name, waveDevice) != 0)
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
name};
if(name.empty())
name = GetDeviceName();
else if(name != GetDeviceName())
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
al::sizei(name), name.data()};
/* There's only one "device", so if it's already open, we're done. */
if(mFile) return;
#ifdef _WIN32
{
std::wstring wname{utf8_to_wstr(fname->c_str())};
mFile = _wfopen(wname.c_str(), L"wb");
std::wstring wname{utf8_to_wstr(fname.value())};
mFile = FilePtr{_wfopen(wname.c_str(), L"wb")};
}
#else
mFile = fopen(fname->c_str(), "wb");
mFile = FilePtr{fopen(fname->c_str(), "wb")};
#endif
if(!mFile)
throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '%s': %s",
fname->c_str(), strerror(errno)};
fname->c_str(), std::generic_category().message(errno).c_str()};
mDevice->DeviceName = name;
}
@ -229,12 +229,11 @@ bool WaveBackend::reset()
{
uint channels{0}, bytes{0}, chanmask{0};
bool isbformat{false};
size_t val;
fseek(mFile, 0, SEEK_SET);
clearerr(mFile);
fseek(mFile.get(), 0, SEEK_SET);
clearerr(mFile.get());
if(GetConfigValueBool(nullptr, "wave", "bformat", false))
if(GetConfigValueBool({}, "wave", "bformat", false))
{
mDevice->FmtChans = DevFmtAmbi3D;
mDevice->mAmbiOrder = 1;
@ -265,6 +264,9 @@ bool WaveBackend::reset()
case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
case DevFmtX7144:
mDevice->FmtChans = DevFmtX714;
[[fallthrough]];
case DevFmtX714:
chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400 | 0x1000 | 0x4000
| 0x8000 | 0x20000;
@ -273,7 +275,7 @@ bool WaveBackend::reset()
case DevFmtX3D71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
case DevFmtAmbi3D:
/* .amb output requires FuMa */
mDevice->mAmbiOrder = minu(mDevice->mAmbiOrder, 3);
mDevice->mAmbiOrder = std::min(mDevice->mAmbiOrder, 3u);
mDevice->mAmbiLayout = DevAmbiLayout::FuMa;
mDevice->mAmbiScale = DevAmbiScaling::FuMa;
isbformat = true;
@ -283,49 +285,48 @@ bool WaveBackend::reset()
bytes = mDevice->bytesFromFmt();
channels = mDevice->channelsFromFmt();
rewind(mFile);
rewind(mFile.get());
fputs("RIFF", mFile);
fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close
fputs("RIFF", mFile.get());
fwrite32le(0xFFFFFFFF, mFile.get()); // 'RIFF' header len; filled in at close
fputs("WAVE", mFile);
fputs("WAVE", mFile.get());
fputs("fmt ", mFile);
fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
fputs("fmt ", mFile.get());
fwrite32le(40, mFile.get()); // 'fmt ' header len; 40 bytes for EXTENSIBLE
// 16-bit val, format type id (extensible: 0xFFFE)
fwrite16le(0xFFFE, mFile);
fwrite16le(0xFFFE, mFile.get());
// 16-bit val, channel count
fwrite16le(static_cast<ushort>(channels), mFile);
fwrite16le(static_cast<ushort>(channels), mFile.get());
// 32-bit val, frequency
fwrite32le(mDevice->Frequency, mFile);
fwrite32le(mDevice->Frequency, mFile.get());
// 32-bit val, bytes per second
fwrite32le(mDevice->Frequency * channels * bytes, mFile);
fwrite32le(mDevice->Frequency * channels * bytes, mFile.get());
// 16-bit val, frame size
fwrite16le(static_cast<ushort>(channels * bytes), mFile);
fwrite16le(static_cast<ushort>(channels * bytes), mFile.get());
// 16-bit val, bits per sample
fwrite16le(static_cast<ushort>(bytes * 8), mFile);
fwrite16le(static_cast<ushort>(bytes * 8), mFile.get());
// 16-bit val, extra byte count
fwrite16le(22, mFile);
fwrite16le(22, mFile.get());
// 16-bit val, valid bits per sample
fwrite16le(static_cast<ushort>(bytes * 8), mFile);
fwrite16le(static_cast<ushort>(bytes * 8), mFile.get());
// 32-bit val, channel mask
fwrite32le(chanmask, mFile);
fwrite32le(chanmask, mFile.get());
// 16 byte GUID, sub-type format
val = fwrite((mDevice->FmtType == DevFmtFloat) ?
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile);
(void)val;
std::ignore = fwrite((mDevice->FmtType == DevFmtFloat) ?
(isbformat ? SUBTYPE_BFORMAT_FLOAT.data() : SUBTYPE_FLOAT.data()) :
(isbformat ? SUBTYPE_BFORMAT_PCM.data() : SUBTYPE_PCM.data()), 1, 16, mFile.get());
fputs("data", mFile);
fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close
fputs("data", mFile.get());
fwrite32le(0xFFFFFFFF, mFile.get()); // 'data' header len; filled in at close
if(ferror(mFile))
if(ferror(mFile.get()))
{
ERR("Error writing header: %s\n", strerror(errno));
ERR("Error writing header: %s\n", std::generic_category().message(errno).c_str());
return false;
}
mDataStart = ftell(mFile);
mDataStart = ftell(mFile.get());
setDefaultWFXChannelOrder();
@ -337,7 +338,7 @@ bool WaveBackend::reset()
void WaveBackend::start()
{
if(mDataStart > 0 && fseek(mFile, 0, SEEK_END) != 0)
if(mDataStart > 0 && fseek(mFile.get(), 0, SEEK_END) != 0)
WARN("Failed to seek on output file\n");
try {
mKillNow.store(false, std::memory_order_release);
@ -357,14 +358,14 @@ void WaveBackend::stop()
if(mDataStart > 0)
{
long size{ftell(mFile)};
long size{ftell(mFile.get())};
if(size > 0)
{
long dataLen{size - mDataStart};
if(fseek(mFile, 4, SEEK_SET) == 0)
fwrite32le(static_cast<uint>(size-8), mFile); // 'WAVE' header len
if(fseek(mFile, mDataStart-4, SEEK_SET) == 0)
fwrite32le(static_cast<uint>(dataLen), mFile); // 'data' header len
if(fseek(mFile.get(), 4, SEEK_SET) == 0)
fwrite32le(static_cast<uint>(size-8), mFile.get()); // 'WAVE' header len
if(fseek(mFile.get(), mDataStart-4, SEEK_SET) == 0)
fwrite32le(static_cast<uint>(dataLen), mFile.get()); // 'data' header len
}
}
}
@ -378,19 +379,16 @@ bool WaveBackendFactory::init()
bool WaveBackendFactory::querySupport(BackendType type)
{ return type == BackendType::Playback; }
std::string WaveBackendFactory::probe(BackendType type)
auto WaveBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
{
std::string outnames;
switch(type)
{
case BackendType::Playback:
/* Includes null char. */
outnames.append(waveDevice, sizeof(waveDevice));
break;
return std::vector{std::string{GetDeviceName()}};
case BackendType::Capture:
break;
}
return outnames;
return {};
}
BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type)