mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-29 16:25:42 +00:00
update openal
This commit is contained in:
parent
62f3b93ff9
commit
6721a6b021
287 changed files with 33851 additions and 27325 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -12,9 +12,10 @@ if(Qt5Widgets_FOUND)
|
|||
verstr.cpp
|
||||
verstr.h
|
||||
${UIS} ${RSCS} ${TRS} ${MOCS})
|
||||
target_link_libraries(alsoft-config Qt5::Widgets)
|
||||
target_link_libraries(alsoft-config PUBLIC Qt5::Widgets PRIVATE alcommon)
|
||||
target_include_directories(alsoft-config PRIVATE "${alsoft-config_BINARY_DIR}"
|
||||
"${OpenAL_BINARY_DIR}")
|
||||
target_compile_definitions(alsoft-config PRIVATE QT_NO_KEYWORDS)
|
||||
set_target_properties(alsoft-config PROPERTIES ${DEFAULT_TARGET_PROPS}
|
||||
RUNTIME_OUTPUT_DIRECTORY ${OpenAL_BINARY_DIR})
|
||||
if(TARGET build_version)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QListWidget>
|
||||
|
||||
|
|
@ -8,15 +10,10 @@ namespace Ui {
|
|||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
private slots:
|
||||
private Q_SLOTS:
|
||||
void cancelCloseAction();
|
||||
|
||||
void saveCurrentConfig();
|
||||
|
|
@ -32,7 +29,7 @@ private slots:
|
|||
|
||||
void updatePeriodSizeEdit(int size);
|
||||
void updatePeriodSizeSlider();
|
||||
void updatePeriodCountEdit(int size);
|
||||
void updatePeriodCountEdit(int count);
|
||||
void updatePeriodCountSlider();
|
||||
|
||||
void selectQuadDecoderFile();
|
||||
|
|
@ -60,22 +57,26 @@ private slots:
|
|||
|
||||
void selectWaveOutput();
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent=nullptr);
|
||||
~MainWindow() override;
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
std::unique_ptr<QValidator> mPeriodSizeValidator;
|
||||
std::unique_ptr<QValidator> mPeriodCountValidator;
|
||||
std::unique_ptr<QValidator> mSourceCountValidator;
|
||||
std::unique_ptr<QValidator> mEffectSlotValidator;
|
||||
std::unique_ptr<QValidator> mSourceSendValidator;
|
||||
std::unique_ptr<QValidator> mSampleRateValidator;
|
||||
std::unique_ptr<QValidator> mJackBufferValidator;
|
||||
|
||||
QValidator *mPeriodSizeValidator;
|
||||
QValidator *mPeriodCountValidator;
|
||||
QValidator *mSourceCountValidator;
|
||||
QValidator *mEffectSlotValidator;
|
||||
QValidator *mSourceSendValidator;
|
||||
QValidator *mSampleRateValidator;
|
||||
QValidator *mJackBufferValidator;
|
||||
std::unique_ptr<Ui::MainWindow> ui;
|
||||
|
||||
bool mNeedsSave;
|
||||
bool mNeedsSave{};
|
||||
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
void selectDecoderFile(QLineEdit *line, const char *name);
|
||||
void selectDecoderFile(QLineEdit *line, const char *caption);
|
||||
|
||||
QStringList collectHrtfs();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,137 +0,0 @@
|
|||
/* $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1987, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "getopt.h"
|
||||
|
||||
int opterr = 1, /* if error message should be printed */
|
||||
optind = 1, /* index into parent argv vector */
|
||||
optopt, /* character checked for validity */
|
||||
optreset; /* reset getopt */
|
||||
char *optarg; /* argument associated with option */
|
||||
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG (int)':'
|
||||
#define EMSG ""
|
||||
|
||||
/*
|
||||
* Get program name in Windows
|
||||
*/
|
||||
const char * _getprogname(void);
|
||||
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt(int nargc, char * const nargv[], const char *ostr)
|
||||
{
|
||||
static char *place = EMSG; /* option letter processing */
|
||||
char *oli; /* option letter list index */
|
||||
|
||||
if (optreset || *place == 0) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
place = nargv[optind];
|
||||
if (optind >= nargc || *place++ != '-') {
|
||||
/* Argument is absent or is not an option */
|
||||
place = EMSG;
|
||||
return (-1);
|
||||
}
|
||||
optopt = *place++;
|
||||
if (optopt == '-' && *place == 0) {
|
||||
/* "--" => end of options */
|
||||
++optind;
|
||||
place = EMSG;
|
||||
return (-1);
|
||||
}
|
||||
if (optopt == 0) {
|
||||
/* Solitary '-', treat as a '-' option
|
||||
if the program (eg su) is looking for it. */
|
||||
place = EMSG;
|
||||
if (strchr(ostr, '-') == NULL)
|
||||
return (-1);
|
||||
optopt = '-';
|
||||
}
|
||||
} else
|
||||
optopt = *place++;
|
||||
|
||||
/* See if option letter is one the caller wanted... */
|
||||
if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) {
|
||||
if (*place == 0)
|
||||
++optind;
|
||||
if (opterr && *ostr != ':')
|
||||
(void)fprintf(stderr,
|
||||
"%s: illegal option -- %c\n", _getprogname(),
|
||||
optopt);
|
||||
return (BADCH);
|
||||
}
|
||||
|
||||
/* Does this option need an argument? */
|
||||
if (oli[1] != ':') {
|
||||
/* don't need argument */
|
||||
optarg = NULL;
|
||||
if (*place == 0)
|
||||
++optind;
|
||||
} else {
|
||||
/* Option-argument is either the rest of this argument or the
|
||||
entire next argument. */
|
||||
if (*place)
|
||||
optarg = place;
|
||||
else if (nargc > ++optind)
|
||||
optarg = nargv[optind];
|
||||
else {
|
||||
/* option-argument absent */
|
||||
place = EMSG;
|
||||
if (*ostr == ':')
|
||||
return (BADARG);
|
||||
if (opterr)
|
||||
(void)fprintf(stderr,
|
||||
"%s: option requires an argument -- %c\n",
|
||||
_getprogname(), optopt);
|
||||
return (BADCH);
|
||||
}
|
||||
place = EMSG;
|
||||
++optind;
|
||||
}
|
||||
return (optopt); /* return option letter */
|
||||
}
|
||||
|
||||
const char * _getprogname() {
|
||||
char *pgmptr = NULL;
|
||||
_get_pgmptr(&pgmptr);
|
||||
return strrchr(pgmptr,'\\')+1;
|
||||
}
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef GETOPT_H
|
||||
#define GETOPT_H
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#else /* _WIN32 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind, opterr, optopt, optreset;
|
||||
|
||||
int getopt(int nargc, char * const nargv[], const char *ostr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
#endif /* !GETOPT_H */
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,12 +2,15 @@
|
|||
#define LOADDEF_H
|
||||
|
||||
#include <istream>
|
||||
#include <string_view>
|
||||
|
||||
#include "alspan.h"
|
||||
|
||||
#include "makemhr.h"
|
||||
|
||||
|
||||
bool LoadDefInput(std::istream &istream, const char *startbytes, std::streamsize startbytecount,
|
||||
const char *filename, const uint fftSize, const uint truncSize, const uint outRate,
|
||||
bool LoadDefInput(std::istream &istream, const al::span<const char> startbytes,
|
||||
const std::string_view filename, const uint fftSize, const uint truncSize, const uint outRate,
|
||||
const ChannelModeT chanMode, HrirDataT *hData);
|
||||
|
||||
#endif /* LOADDEF_H */
|
||||
|
|
|
|||
|
|
@ -33,12 +33,15 @@
|
|||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "aloptional.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "alnumeric.h"
|
||||
#include "makemhr.h"
|
||||
#include "polyphase_resampler.h"
|
||||
#include "sofa-support.h"
|
||||
|
|
@ -46,6 +49,9 @@
|
|||
#include "mysofa.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
using uint = unsigned int;
|
||||
|
||||
/* Attempts to produce a compatible layout. Most data sets tend to be
|
||||
|
|
@ -54,19 +60,19 @@ using uint = unsigned int;
|
|||
* possible. Those sets that contain purely random measurements or use
|
||||
* different major axes will fail.
|
||||
*/
|
||||
static bool PrepareLayout(const uint m, const float *xyzs, HrirDataT *hData)
|
||||
auto PrepareLayout(const al::span<const float> xyzs, HrirDataT *hData) -> bool
|
||||
{
|
||||
fprintf(stdout, "Detecting compatible layout...\n");
|
||||
|
||||
auto fds = GetCompatibleLayout(m, xyzs);
|
||||
auto fds = GetCompatibleLayout(xyzs);
|
||||
if(fds.size() > MAX_FD_COUNT)
|
||||
{
|
||||
fprintf(stdout, "Incompatible layout (inumerable radii).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
double distances[MAX_FD_COUNT]{};
|
||||
uint evCounts[MAX_FD_COUNT]{};
|
||||
std::array<double,MAX_FD_COUNT> distances{};
|
||||
std::array<uint,MAX_FD_COUNT> evCounts{};
|
||||
auto azCounts = std::vector<std::array<uint,MAX_EV_COUNT>>(MAX_FD_COUNT);
|
||||
for(auto &azs : azCounts) azs.fill(0u);
|
||||
|
||||
|
|
@ -86,12 +92,11 @@ static bool PrepareLayout(const uint m, const float *xyzs, HrirDataT *hData)
|
|||
|
||||
++fi;
|
||||
}
|
||||
fprintf(stdout, "Using %u of %u IRs.\n", ir_total, m);
|
||||
const auto azs = al::as_span(azCounts).first<MAX_FD_COUNT>();
|
||||
return PrepareHrirData({distances, fi}, evCounts, azs, hData);
|
||||
fprintf(stdout, "Using %u of %zu IRs.\n", ir_total, xyzs.size()/3);
|
||||
const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>();
|
||||
return PrepareHrirData(al::span{distances}.first(fi), evCounts, azs, hData);
|
||||
}
|
||||
|
||||
|
||||
float GetSampleRate(MYSOFA_HRTF *sofaHrtf)
|
||||
{
|
||||
const char *srate_dim{nullptr};
|
||||
|
|
@ -100,7 +105,7 @@ float GetSampleRate(MYSOFA_HRTF *sofaHrtf)
|
|||
MYSOFA_ATTRIBUTE *srate_attrs{srate_array->attributes};
|
||||
while(srate_attrs)
|
||||
{
|
||||
if(std::string{"DIMENSION_LIST"} == srate_attrs->name)
|
||||
if("DIMENSION_LIST"sv == srate_attrs->name)
|
||||
{
|
||||
if(srate_dim)
|
||||
{
|
||||
|
|
@ -109,7 +114,7 @@ float GetSampleRate(MYSOFA_HRTF *sofaHrtf)
|
|||
}
|
||||
srate_dim = srate_attrs->value;
|
||||
}
|
||||
else if(std::string{"Units"} == srate_attrs->name)
|
||||
else if("Units"sv == srate_attrs->name)
|
||||
{
|
||||
if(srate_units)
|
||||
{
|
||||
|
|
@ -128,7 +133,7 @@ float GetSampleRate(MYSOFA_HRTF *sofaHrtf)
|
|||
fprintf(stderr, "Missing sample rate dimensions\n");
|
||||
return 0.0f;
|
||||
}
|
||||
if(srate_dim != std::string{"I"})
|
||||
if(srate_dim != "I"sv)
|
||||
{
|
||||
fprintf(stderr, "Unsupported sample rate dimensions: %s\n", srate_dim);
|
||||
return 0.0f;
|
||||
|
|
@ -138,40 +143,40 @@ float GetSampleRate(MYSOFA_HRTF *sofaHrtf)
|
|||
fprintf(stderr, "Missing sample rate unit type\n");
|
||||
return 0.0f;
|
||||
}
|
||||
if(srate_units != std::string{"hertz"})
|
||||
if(srate_units != "hertz"sv)
|
||||
{
|
||||
fprintf(stderr, "Unsupported sample rate unit type: %s\n", srate_units);
|
||||
return 0.0f;
|
||||
}
|
||||
/* I dimensions guarantees 1 element, so just extract it. */
|
||||
if(srate_array->values[0] < MIN_RATE || srate_array->values[0] > MAX_RATE)
|
||||
const auto values = al::span{srate_array->values, sofaHrtf->I};
|
||||
if(values[0] < float{MIN_RATE} || values[0] > float{MAX_RATE})
|
||||
{
|
||||
fprintf(stderr, "Sample rate out of range: %f (expected %u to %u)", srate_array->values[0],
|
||||
MIN_RATE, MAX_RATE);
|
||||
fprintf(stderr, "Sample rate out of range: %f (expected %u to %u)", values[0], MIN_RATE,
|
||||
MAX_RATE);
|
||||
return 0.0f;
|
||||
}
|
||||
return srate_array->values[0];
|
||||
return values[0];
|
||||
}
|
||||
|
||||
enum class DelayType : uint8_t {
|
||||
None,
|
||||
I_R, /* [1][Channels] */
|
||||
M_R, /* [HRIRs][Channels] */
|
||||
Invalid,
|
||||
};
|
||||
DelayType PrepareDelay(MYSOFA_HRTF *sofaHrtf)
|
||||
auto PrepareDelay(MYSOFA_HRTF *sofaHrtf) -> std::optional<DelayType>
|
||||
{
|
||||
const char *delay_dim{nullptr};
|
||||
MYSOFA_ARRAY *delay_array{&sofaHrtf->DataDelay};
|
||||
MYSOFA_ATTRIBUTE *delay_attrs{delay_array->attributes};
|
||||
while(delay_attrs)
|
||||
{
|
||||
if(std::string{"DIMENSION_LIST"} == delay_attrs->name)
|
||||
if("DIMENSION_LIST"sv == delay_attrs->name)
|
||||
{
|
||||
if(delay_dim)
|
||||
{
|
||||
fprintf(stderr, "Duplicate Delay.DIMENSION_LIST\n");
|
||||
return DelayType::Invalid;
|
||||
return std::nullopt;
|
||||
}
|
||||
delay_dim = delay_attrs->value;
|
||||
}
|
||||
|
|
@ -185,13 +190,13 @@ DelayType PrepareDelay(MYSOFA_HRTF *sofaHrtf)
|
|||
fprintf(stderr, "Missing delay dimensions\n");
|
||||
return DelayType::None;
|
||||
}
|
||||
if(delay_dim == std::string{"I,R"})
|
||||
if(delay_dim == "I,R"sv)
|
||||
return DelayType::I_R;
|
||||
else if(delay_dim == std::string{"M,R"})
|
||||
if(delay_dim == "M,R"sv)
|
||||
return DelayType::M_R;
|
||||
|
||||
fprintf(stderr, "Unsupported delay dimensions: %s\n", delay_dim);
|
||||
return DelayType::Invalid;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool CheckIrData(MYSOFA_HRTF *sofaHrtf)
|
||||
|
|
@ -201,7 +206,7 @@ bool CheckIrData(MYSOFA_HRTF *sofaHrtf)
|
|||
MYSOFA_ATTRIBUTE *ir_attrs{ir_array->attributes};
|
||||
while(ir_attrs)
|
||||
{
|
||||
if(std::string{"DIMENSION_LIST"} == ir_attrs->name)
|
||||
if("DIMENSION_LIST"sv == ir_attrs->name)
|
||||
{
|
||||
if(ir_dim)
|
||||
{
|
||||
|
|
@ -220,7 +225,7 @@ bool CheckIrData(MYSOFA_HRTF *sofaHrtf)
|
|||
fprintf(stderr, "Missing IR dimensions\n");
|
||||
return false;
|
||||
}
|
||||
if(ir_dim != std::string{"M,R,N"})
|
||||
if(ir_dim != "M,R,N"sv)
|
||||
{
|
||||
fprintf(stderr, "Unsupported IR dimensions: %s\n", ir_dim);
|
||||
return false;
|
||||
|
|
@ -230,13 +235,13 @@ bool CheckIrData(MYSOFA_HRTF *sofaHrtf)
|
|||
|
||||
|
||||
/* Calculate the onset time of a HRIR. */
|
||||
static constexpr int OnsetRateMultiple{10};
|
||||
static double CalcHrirOnset(PPhaseResampler &rs, const uint rate, const uint n,
|
||||
al::span<double> upsampled, const double *hrir)
|
||||
constexpr int OnsetRateMultiple{10};
|
||||
auto CalcHrirOnset(PPhaseResampler &rs, const uint rate, al::span<double> upsampled,
|
||||
const al::span<const double> hrir) -> double
|
||||
{
|
||||
rs.process(n, hrir, static_cast<uint>(upsampled.size()), upsampled.data());
|
||||
rs.process(hrir, upsampled);
|
||||
|
||||
auto abs_lt = [](const double &lhs, const double &rhs) -> bool
|
||||
auto abs_lt = [](const double lhs, const double rhs) -> bool
|
||||
{ return std::abs(lhs) < std::abs(rhs); };
|
||||
auto iter = std::max_element(upsampled.cbegin(), upsampled.cend(), abs_lt);
|
||||
return static_cast<double>(std::distance(upsampled.cbegin(), iter)) /
|
||||
|
|
@ -244,16 +249,16 @@ static double CalcHrirOnset(PPhaseResampler &rs, const uint rate, const uint n,
|
|||
}
|
||||
|
||||
/* Calculate the magnitude response of a HRIR. */
|
||||
static void CalcHrirMagnitude(const uint points, const uint n, al::span<complex_d> h, double *hrir)
|
||||
void CalcHrirMagnitude(const uint points, al::span<complex_d> h, const al::span<double> hrir)
|
||||
{
|
||||
auto iter = std::copy_n(hrir, points, h.begin());
|
||||
auto iter = std::copy_n(hrir.cbegin(), points, h.begin());
|
||||
std::fill(iter, h.end(), complex_d{0.0, 0.0});
|
||||
|
||||
FftForward(n, h.data());
|
||||
MagnitudeResponse(n, h.data(), hrir);
|
||||
forward_fft(h);
|
||||
MagnitudeResponse(h, hrir.first((h.size()/2) + 1));
|
||||
}
|
||||
|
||||
static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayType delayType,
|
||||
bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayType delayType,
|
||||
const uint outRate)
|
||||
{
|
||||
std::atomic<uint> loaded_count{0u};
|
||||
|
|
@ -261,27 +266,27 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayTy
|
|||
auto load_proc = [sofaHrtf,hData,delayType,outRate,&loaded_count]() -> bool
|
||||
{
|
||||
const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
|
||||
hData->mHrirsBase.resize(channels * hData->mIrCount * hData->mIrSize, 0.0);
|
||||
double *hrirs = hData->mHrirsBase.data();
|
||||
hData->mHrirsBase.resize(channels * size_t{hData->mIrCount} * hData->mIrSize, 0.0);
|
||||
const auto hrirs = al::span{hData->mHrirsBase};
|
||||
|
||||
std::unique_ptr<double[]> restmp;
|
||||
al::optional<PPhaseResampler> resampler;
|
||||
std::vector<double> restmp;
|
||||
std::optional<PPhaseResampler> resampler;
|
||||
if(outRate && outRate != hData->mIrRate)
|
||||
{
|
||||
resampler.emplace().init(hData->mIrRate, outRate);
|
||||
restmp = std::make_unique<double[]>(sofaHrtf->N);
|
||||
restmp.resize(sofaHrtf->N);
|
||||
}
|
||||
|
||||
const auto srcPosValues = al::span{sofaHrtf->SourcePosition.values, sofaHrtf->M*3_uz};
|
||||
const auto irValues = al::span{sofaHrtf->DataIR.values,
|
||||
size_t{sofaHrtf->M}*sofaHrtf->R*sofaHrtf->N};
|
||||
for(uint si{0u};si < sofaHrtf->M;++si)
|
||||
{
|
||||
loaded_count.fetch_add(1u);
|
||||
|
||||
float aer[3]{
|
||||
sofaHrtf->SourcePosition.values[3*si],
|
||||
sofaHrtf->SourcePosition.values[3*si + 1],
|
||||
sofaHrtf->SourcePosition.values[3*si + 2]
|
||||
};
|
||||
mysofa_c2s(aer);
|
||||
std::array aer{srcPosValues[3_uz*si], srcPosValues[3_uz*si + 1],
|
||||
srcPosValues[3_uz*si + 2]};
|
||||
mysofa_c2s(aer.data());
|
||||
|
||||
if(std::abs(aer[1]) >= 89.999f)
|
||||
aer[0] = 0.0f;
|
||||
|
|
@ -307,8 +312,8 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayTy
|
|||
ai %= static_cast<uint>(field->mEvs[ei].mAzs.size());
|
||||
if(std::abs(af) >= 0.1) continue;
|
||||
|
||||
HrirAzT *azd = &field->mEvs[ei].mAzs[ai];
|
||||
if(azd->mIrs[0] != nullptr)
|
||||
HrirAzT &azd = field->mEvs[ei].mAzs[ai];
|
||||
if(!azd.mIrs[0].empty())
|
||||
{
|
||||
fprintf(stderr, "\nMultiple measurements near [ a=%f, e=%f, r=%f ].\n",
|
||||
aer[0], aer[1], aer[2]);
|
||||
|
|
@ -317,30 +322,33 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayTy
|
|||
|
||||
for(uint ti{0u};ti < channels;++ti)
|
||||
{
|
||||
azd->mIrs[ti] = &hrirs[hData->mIrSize * (hData->mIrCount*ti + azd->mIndex)];
|
||||
azd.mIrs[ti] = hrirs.subspan(
|
||||
(size_t{hData->mIrCount}*ti + azd.mIndex) * hData->mIrSize, hData->mIrSize);
|
||||
const auto ir = irValues.subspan((size_t{si}*sofaHrtf->R + ti)*sofaHrtf->N,
|
||||
sofaHrtf->N);
|
||||
if(!resampler)
|
||||
std::copy_n(&sofaHrtf->DataIR.values[(si*sofaHrtf->R + ti)*sofaHrtf->N],
|
||||
sofaHrtf->N, azd->mIrs[ti]);
|
||||
std::copy_n(ir.cbegin(), ir.size(), azd.mIrs[ti].begin());
|
||||
else
|
||||
{
|
||||
std::copy_n(&sofaHrtf->DataIR.values[(si*sofaHrtf->R + ti)*sofaHrtf->N],
|
||||
sofaHrtf->N, restmp.get());
|
||||
resampler->process(sofaHrtf->N, restmp.get(), hData->mIrSize, azd->mIrs[ti]);
|
||||
std::copy_n(ir.cbegin(), ir.size(), restmp.begin());
|
||||
resampler->process(restmp, azd.mIrs[ti]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Include any per-channel or per-HRIR delays. */
|
||||
if(delayType == DelayType::I_R)
|
||||
{
|
||||
const float *delayValues{sofaHrtf->DataDelay.values};
|
||||
const auto delayValues = al::span{sofaHrtf->DataDelay.values,
|
||||
size_t{sofaHrtf->I}*sofaHrtf->R};
|
||||
for(uint ti{0u};ti < channels;++ti)
|
||||
azd->mDelays[ti] = delayValues[ti] / static_cast<float>(hData->mIrRate);
|
||||
azd.mDelays[ti] = delayValues[ti] / static_cast<float>(hData->mIrRate);
|
||||
}
|
||||
else if(delayType == DelayType::M_R)
|
||||
{
|
||||
const float *delayValues{sofaHrtf->DataDelay.values};
|
||||
const auto delayValues = al::span{sofaHrtf->DataDelay.values,
|
||||
size_t{sofaHrtf->M}*sofaHrtf->R};
|
||||
for(uint ti{0u};ti < channels;++ti)
|
||||
azd->mDelays[ti] = delayValues[si*sofaHrtf->R + ti] /
|
||||
azd.mDelays[ti] = delayValues[si*sofaHrtf->R + ti] /
|
||||
static_cast<float>(hData->mIrRate);
|
||||
}
|
||||
}
|
||||
|
|
@ -374,7 +382,7 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayTy
|
|||
struct MagCalculator {
|
||||
const uint mFftSize{};
|
||||
const uint mIrPoints{};
|
||||
std::vector<double*> mIrs{};
|
||||
std::vector<al::span<double>> mIrs{};
|
||||
std::atomic<size_t> mCurrent{};
|
||||
std::atomic<size_t> mDone{};
|
||||
|
||||
|
|
@ -382,7 +390,7 @@ struct MagCalculator {
|
|||
{
|
||||
auto htemp = std::vector<complex_d>(mFftSize);
|
||||
|
||||
while(1)
|
||||
while(true)
|
||||
{
|
||||
/* Load the current index to process. */
|
||||
size_t idx{mCurrent.load()};
|
||||
|
|
@ -397,7 +405,7 @@ struct MagCalculator {
|
|||
*/
|
||||
} while(!mCurrent.compare_exchange_weak(idx, idx+1, std::memory_order_relaxed));
|
||||
|
||||
CalcHrirMagnitude(mIrPoints, mFftSize, htemp, mIrs[idx]);
|
||||
CalcHrirMagnitude(mIrPoints, htemp, mIrs[idx]);
|
||||
|
||||
/* Increment the number of IRs done. */
|
||||
mDone.fetch_add(1);
|
||||
|
|
@ -405,22 +413,25 @@ struct MagCalculator {
|
|||
}
|
||||
};
|
||||
|
||||
bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSize,
|
||||
} // namespace
|
||||
|
||||
bool LoadSofaFile(const std::string_view filename, const uint numThreads, const uint fftSize,
|
||||
const uint truncSize, const uint outRate, const ChannelModeT chanMode, HrirDataT *hData)
|
||||
{
|
||||
int err;
|
||||
MySofaHrtfPtr sofaHrtf{mysofa_load(filename, &err)};
|
||||
MySofaHrtfPtr sofaHrtf{mysofa_load(std::string{filename}.c_str(), &err)};
|
||||
if(!sofaHrtf)
|
||||
{
|
||||
fprintf(stdout, "Error: Could not load %s: %s\n", filename, SofaErrorStr(err));
|
||||
fprintf(stdout, "Error: Could not load %.*s: %s\n", al::sizei(filename), filename.data(),
|
||||
SofaErrorStr(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* NOTE: Some valid SOFA files are failing this check. */
|
||||
err = mysofa_check(sofaHrtf.get());
|
||||
if(err != MYSOFA_OK)
|
||||
fprintf(stderr, "Warning: Supposedly malformed source file '%s' (%s).\n", filename,
|
||||
SofaErrorStr(err));
|
||||
fprintf(stderr, "Warning: Supposedly malformed source file '%.*s' (%s).\n",
|
||||
al::sizei(filename), filename.data(), SofaErrorStr(err));
|
||||
|
||||
mysofa_tocartesian(sofaHrtf.get());
|
||||
|
||||
|
|
@ -459,19 +470,19 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz
|
|||
/* Assume a default head radius of 9cm. */
|
||||
hData->mRadius = 0.09;
|
||||
|
||||
hData->mIrRate = static_cast<uint>(GetSampleRate(sofaHrtf.get()) + 0.5f);
|
||||
hData->mIrRate = static_cast<uint>(std::lround(GetSampleRate(sofaHrtf.get())));
|
||||
if(!hData->mIrRate)
|
||||
return false;
|
||||
|
||||
DelayType delayType = PrepareDelay(sofaHrtf.get());
|
||||
if(delayType == DelayType::Invalid)
|
||||
const auto delayType = PrepareDelay(sofaHrtf.get());
|
||||
if(!delayType)
|
||||
return false;
|
||||
|
||||
if(!CheckIrData(sofaHrtf.get()))
|
||||
return false;
|
||||
if(!PrepareLayout(sofaHrtf->M, sofaHrtf->SourcePosition.values, hData))
|
||||
if(!PrepareLayout(al::span{sofaHrtf->SourcePosition.values, sofaHrtf->M*3_uz}, hData))
|
||||
return false;
|
||||
if(!LoadResponses(sofaHrtf.get(), hData, delayType, outRate))
|
||||
if(!LoadResponses(sofaHrtf.get(), hData, *delayType, outRate))
|
||||
return false;
|
||||
sofaHrtf = nullptr;
|
||||
|
||||
|
|
@ -484,7 +495,7 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz
|
|||
for(;ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
|
||||
{
|
||||
HrirAzT &azd = hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
if(azd.mIrs[0] != nullptr) break;
|
||||
if(!azd.mIrs[0].empty()) break;
|
||||
}
|
||||
if(ai < hData->mFds[fi].mEvs[ei].mAzs.size())
|
||||
break;
|
||||
|
|
@ -500,7 +511,7 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz
|
|||
for(uint ai{0u};ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
|
||||
{
|
||||
HrirAzT &azd = hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
if(azd.mIrs[0] == nullptr)
|
||||
if(azd.mIrs[0].empty())
|
||||
{
|
||||
fprintf(stderr, "Missing source reference [ %d, %d, %d ].\n", fi, ei, ai);
|
||||
return false;
|
||||
|
|
@ -512,7 +523,7 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz
|
|||
|
||||
size_t hrir_total{0};
|
||||
const uint channels{(hData->mChannelType == CT_STEREO) ? 2u : 1u};
|
||||
double *hrirs = hData->mHrirsBase.data();
|
||||
const auto hrirs = al::span{hData->mHrirsBase};
|
||||
for(uint fi{0u};fi < hData->mFds.size();fi++)
|
||||
{
|
||||
for(uint ei{0u};ei < hData->mFds[fi].mEvStart;ei++)
|
||||
|
|
@ -520,8 +531,9 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz
|
|||
for(uint ai{0u};ai < hData->mFds[fi].mEvs[ei].mAzs.size();ai++)
|
||||
{
|
||||
HrirAzT &azd = hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
for(uint ti{0u};ti < channels;ti++)
|
||||
azd.mIrs[ti] = &hrirs[hData->mIrSize * (hData->mIrCount*ti + azd.mIndex)];
|
||||
for(size_t ti{0u};ti < channels;ti++)
|
||||
azd.mIrs[ti] = hrirs.subspan((hData->mIrCount*ti + azd.mIndex)*hData->mIrSize,
|
||||
hData->mIrSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -533,7 +545,7 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz
|
|||
auto onset_proc = [hData,channels,&hrir_done]() -> bool
|
||||
{
|
||||
/* Temporary buffer used to calculate the IR's onset. */
|
||||
auto upsampled = std::vector<double>(OnsetRateMultiple * hData->mIrPoints);
|
||||
auto upsampled = std::vector<double>(size_t{OnsetRateMultiple} * hData->mIrPoints);
|
||||
/* This resampler is used to help detect the response onset. */
|
||||
PPhaseResampler rs;
|
||||
rs.init(hData->mIrRate, OnsetRateMultiple*hData->mIrRate);
|
||||
|
|
@ -547,8 +559,8 @@ bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSiz
|
|||
for(uint ti{0};ti < channels;ti++)
|
||||
{
|
||||
hrir_done.fetch_add(1u, std::memory_order_acq_rel);
|
||||
azd.mDelays[ti] += CalcHrirOnset(rs, hData->mIrRate, hData->mIrPoints,
|
||||
upsampled, azd.mIrs[ti]);
|
||||
azd.mDelays[ti] += CalcHrirOnset(rs, hData->mIrRate, upsampled,
|
||||
azd.mIrs[ti].first(hData->mIrPoints));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#ifndef LOADSOFA_H
|
||||
#define LOADSOFA_H
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "makemhr.h"
|
||||
|
||||
|
||||
bool LoadSofaFile(const char *filename, const uint numThreads, const uint fftSize,
|
||||
bool LoadSofaFile(const std::string_view filename, const uint numThreads, const uint fftSize,
|
||||
const uint truncSize, const uint outRate, const ChannelModeT chanMode, HrirDataT *hData);
|
||||
|
||||
#endif /* LOADSOFA_H */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,43 +1,54 @@
|
|||
#ifndef MAKEMHR_H
|
||||
#define MAKEMHR_H
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
|
||||
#include "alcomplex.h"
|
||||
#include "alspan.h"
|
||||
#include "polyphase_resampler.h"
|
||||
|
||||
|
||||
// The maximum path length used when processing filenames.
|
||||
#define MAX_PATH_LEN (256)
|
||||
enum { MAX_PATH_LEN = 256u };
|
||||
|
||||
// The limit to the number of 'distances' listed in the data set definition.
|
||||
// Must be less than 256
|
||||
#define MAX_FD_COUNT (16)
|
||||
enum { MAX_FD_COUNT = 16u };
|
||||
|
||||
// The limits to the number of 'elevations' listed in the data set definition.
|
||||
// Must be less than 256.
|
||||
#define MIN_EV_COUNT (5)
|
||||
#define MAX_EV_COUNT (181)
|
||||
enum {
|
||||
MIN_EV_COUNT = 5u,
|
||||
MAX_EV_COUNT = 181u
|
||||
};
|
||||
|
||||
// The limits for each of the 'azimuths' listed in the data set definition.
|
||||
// Must be less than 256.
|
||||
#define MIN_AZ_COUNT (1)
|
||||
#define MAX_AZ_COUNT (255)
|
||||
enum {
|
||||
MIN_AZ_COUNT = 1u,
|
||||
MAX_AZ_COUNT = 255u
|
||||
};
|
||||
|
||||
// The limits for the 'distance' from source to listener for each field in
|
||||
// the definition file.
|
||||
#define MIN_DISTANCE (0.05)
|
||||
#define MAX_DISTANCE (2.50)
|
||||
inline constexpr double MIN_DISTANCE{0.05};
|
||||
inline constexpr double MAX_DISTANCE{2.50};
|
||||
|
||||
// The limits for the sample 'rate' metric in the data set definition and for
|
||||
// resampling.
|
||||
#define MIN_RATE (32000)
|
||||
#define MAX_RATE (96000)
|
||||
enum {
|
||||
MIN_RATE = 32000u,
|
||||
MAX_RATE = 96000u
|
||||
};
|
||||
|
||||
// The limits for the HRIR 'points' metric in the data set definition.
|
||||
#define MIN_POINTS (16)
|
||||
#define MAX_POINTS (8192)
|
||||
enum {
|
||||
MIN_POINTS = 16u,
|
||||
MAX_POINTS = 8192u
|
||||
};
|
||||
|
||||
|
||||
using uint = unsigned int;
|
||||
|
|
@ -68,8 +79,8 @@ enum ChannelTypeT {
|
|||
struct HrirAzT {
|
||||
double mAzimuth{0.0};
|
||||
uint mIndex{0u};
|
||||
double mDelays[2]{0.0, 0.0};
|
||||
double *mIrs[2]{nullptr, nullptr};
|
||||
std::array<double,2> mDelays{};
|
||||
std::array<al::span<double>,2> mIrs{};
|
||||
};
|
||||
|
||||
struct HrirEvT {
|
||||
|
|
@ -109,19 +120,31 @@ struct HrirDataT {
|
|||
bool PrepareHrirData(const al::span<const double> distances,
|
||||
const al::span<const uint,MAX_FD_COUNT> evCounts,
|
||||
const al::span<const std::array<uint,MAX_EV_COUNT>,MAX_FD_COUNT> azCounts, HrirDataT *hData);
|
||||
void MagnitudeResponse(const uint n, const complex_d *in, double *out);
|
||||
|
||||
/* Calculate the magnitude response of the given input. This is used in
|
||||
* place of phase decomposition, since the phase residuals are discarded for
|
||||
* minimum phase reconstruction. The mirrored half of the response is also
|
||||
* discarded.
|
||||
*/
|
||||
inline void MagnitudeResponse(const al::span<const complex_d> in, const al::span<double> out)
|
||||
{
|
||||
static constexpr double Epsilon{1e-9};
|
||||
for(size_t i{0};i < out.size();++i)
|
||||
out[i] = std::max(std::abs(in[i]), Epsilon);
|
||||
}
|
||||
|
||||
// Performs a forward FFT.
|
||||
inline void FftForward(const uint n, complex_d *inout)
|
||||
{ forward_fft(al::as_span(inout, n)); }
|
||||
{ forward_fft(al::span{inout, n}); }
|
||||
|
||||
// Performs an inverse FFT.
|
||||
// Performs an inverse FFT, scaling the result by the number of elements.
|
||||
inline void FftInverse(const uint n, complex_d *inout)
|
||||
{
|
||||
inverse_fft(al::as_span(inout, n));
|
||||
double f{1.0 / n};
|
||||
for(uint i{0};i < n;i++)
|
||||
inout[i] *= f;
|
||||
const auto values = al::span{inout, n};
|
||||
inverse_fft(values);
|
||||
|
||||
const double f{1.0 / n};
|
||||
std::for_each(values.begin(), values.end(), [f](complex_d &value) { value *= f; });
|
||||
}
|
||||
|
||||
// Performs linear interpolation.
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@
|
|||
#define FUNCTION_CAST(T, ptr) (T)(ptr)
|
||||
#endif
|
||||
|
||||
#define MAX_WIDTH 80
|
||||
enum { MaxWidth = 80 };
|
||||
|
||||
static void printList(const char *list, char separator)
|
||||
{
|
||||
size_t col = MAX_WIDTH, len;
|
||||
size_t col = MaxWidth, len;
|
||||
const char *indent = " ";
|
||||
const char *next;
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ static void printList(const char *list, char separator)
|
|||
else
|
||||
len = strlen(list);
|
||||
|
||||
if(len + col + 2 >= MAX_WIDTH)
|
||||
if(len + col + 2 >= MaxWidth)
|
||||
{
|
||||
fprintf(stdout, "\n%s", indent);
|
||||
col = strlen(indent);
|
||||
|
|
@ -167,11 +167,11 @@ static void printHRTFInfo(ALCdevice *device)
|
|||
|
||||
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtfs);
|
||||
if(!num_hrtfs)
|
||||
printf("No HRTFs found\n");
|
||||
printf("No HRTF profiles found\n");
|
||||
else
|
||||
{
|
||||
ALCint i;
|
||||
printf("Available HRTFs:\n");
|
||||
printf("Available HRTF profiles:\n");
|
||||
for(i = 0;i < num_hrtfs;++i)
|
||||
{
|
||||
const ALCchar *name = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
|
||||
|
|
@ -181,6 +181,14 @@ static void printHRTFInfo(ALCdevice *device)
|
|||
checkALCErrors(device);
|
||||
}
|
||||
|
||||
static void printALCIntegerValue(ALCdevice *device, ALCenum enumValue, char* enumName)
|
||||
{
|
||||
ALCint value;
|
||||
alcGetIntegerv(device, enumValue, 1, &value);
|
||||
if (checkALCErrors(device) == ALC_NO_ERROR)
|
||||
printf("%s: %d\n", enumName, value);
|
||||
}
|
||||
|
||||
static void printModeInfo(ALCdevice *device)
|
||||
{
|
||||
ALCint srate = 0;
|
||||
|
|
@ -213,6 +221,68 @@ static void printModeInfo(ALCdevice *device)
|
|||
alcGetIntegerv(device, ALC_FREQUENCY, 1, &srate);
|
||||
if(checkALCErrors(device) == ALC_NO_ERROR)
|
||||
printf("Device sample rate: %dhz\n", srate);
|
||||
|
||||
if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
|
||||
{
|
||||
const ALCchar *hrtfname = "(disabled)";
|
||||
ALCint isenabled = 0;
|
||||
|
||||
alcGetIntegerv(device, ALC_HRTF_SOFT, 1, &isenabled);
|
||||
checkALCErrors(device);
|
||||
if(isenabled == ALC_TRUE)
|
||||
{
|
||||
hrtfname = alcGetString(device, ALC_HRTF_SPECIFIER_SOFT);
|
||||
checkALCErrors(device);
|
||||
}
|
||||
printf("Device HRTF profile: %s\n", hrtfname ? hrtfname : "<null>");
|
||||
}
|
||||
|
||||
printALCIntegerValue(device, ALC_MONO_SOURCES, "Device number of mono sources");
|
||||
printALCIntegerValue(device, ALC_STEREO_SOURCES, "Device number of stereo sources");
|
||||
}
|
||||
|
||||
static void printALCSOFTSystemEventIsSupportedResult(LPALCEVENTISSUPPORTEDSOFT alcEventIsSupportedSOFT, ALCenum eventType, ALCenum deviceType)
|
||||
{
|
||||
if (alcEventIsSupportedSOFT == NULL)
|
||||
{
|
||||
printf("ERROR (alcEventIsSupportedSOFT missing)\n");
|
||||
return;
|
||||
}
|
||||
ALCenum supported = alcEventIsSupportedSOFT(eventType, deviceType);
|
||||
if (supported == ALC_EVENT_SUPPORTED_SOFT)
|
||||
{
|
||||
printf("SUPPORTED\n");
|
||||
}
|
||||
else if (supported == ALC_EVENT_NOT_SUPPORTED_SOFT)
|
||||
{
|
||||
printf("NOT SUPPORTED\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("UNEXPECTED VALUE : %d\n", supported);
|
||||
}
|
||||
}
|
||||
|
||||
static void printALC_SOFT_system_event(void)
|
||||
{
|
||||
if(alcIsExtensionPresent(NULL, "ALC_SOFT_system_events"))
|
||||
{
|
||||
LPALCEVENTISSUPPORTEDSOFT alcEventIsSupportedSOFT;
|
||||
alcEventIsSupportedSOFT = FUNCTION_CAST(LPALCEVENTISSUPPORTEDSOFT, alGetProcAddress("alcEventIsSupportedSOFT"));
|
||||
printf("Available events:\n");
|
||||
printf(" ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - ");
|
||||
printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT, ALC_PLAYBACK_DEVICE_SOFT);
|
||||
printf(" ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT for ALC_CAPTURE_DEVICE_SOFT - ");
|
||||
printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT, ALC_CAPTURE_DEVICE_SOFT);
|
||||
printf(" ALC_EVENT_TYPE_DEVICE_ADDED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - ");
|
||||
printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_ADDED_SOFT, ALC_PLAYBACK_DEVICE_SOFT);
|
||||
printf(" ALC_EVENT_TYPE_DEVICE_ADDED_SOFT for ALC_CAPTURE_DEVICE_SOFT - ");
|
||||
printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_ADDED_SOFT, ALC_CAPTURE_DEVICE_SOFT);
|
||||
printf(" ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - ");
|
||||
printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT, ALC_PLAYBACK_DEVICE_SOFT);
|
||||
printf(" ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT for ALC_CAPTURE_DEVICE_SOFT - ");
|
||||
printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT, ALC_CAPTURE_DEVICE_SOFT);
|
||||
}
|
||||
}
|
||||
|
||||
static void printALInfo(void)
|
||||
|
|
@ -323,7 +393,7 @@ static void printEFXInfo(ALCdevice *device)
|
|||
|
||||
palFilteri(object, AL_FILTER_TYPE, filters[i]);
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
memmove(current, next+1, strlen(next));
|
||||
memmove(current, next+1, strlen(next)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */
|
||||
else
|
||||
current = next+1;
|
||||
}
|
||||
|
|
@ -342,7 +412,7 @@ static void printEFXInfo(ALCdevice *device)
|
|||
|
||||
palEffecti(object, AL_EFFECT_TYPE, effects[i]);
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
memmove(current, next+1, strlen(next));
|
||||
memmove(current, next+1, strlen(next)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */
|
||||
else
|
||||
current = next+1;
|
||||
}
|
||||
|
|
@ -355,7 +425,7 @@ static void printEFXInfo(ALCdevice *device)
|
|||
|
||||
palEffecti(object, AL_EFFECT_TYPE, dedeffects[i]);
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
memmove(current, next+1, strlen(next));
|
||||
memmove(current, next+1, strlen(next)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */
|
||||
else
|
||||
current = next+1;
|
||||
}
|
||||
|
|
@ -366,7 +436,7 @@ static void printEFXInfo(ALCdevice *device)
|
|||
{
|
||||
char *next = strchr(current, ',');
|
||||
assert(next != NULL);
|
||||
memmove(current, next+1, strlen(next));
|
||||
memmove(current, next+1, strlen(next)); /* NOLINT(clang-analyzer-security.insecureAPI.*) */
|
||||
}
|
||||
}
|
||||
printf("Supported effects:");
|
||||
|
|
@ -420,6 +490,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
printALCInfo(device);
|
||||
printHRTFInfo(device);
|
||||
printALC_SOFT_system_event();
|
||||
|
||||
context = alcCreateContext(device, NULL);
|
||||
if(!context || alcMakeContextCurrent(context) == ALC_FALSE)
|
||||
|
|
|
|||
|
|
@ -21,20 +21,27 @@
|
|||
* Or visit: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
|
||||
#include "sofa-support.h"
|
||||
|
||||
#include "mysofa.h"
|
||||
|
||||
#include "win_main_utf8.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
static void PrintSofaAttributes(const char *prefix, struct MYSOFA_ATTRIBUTE *attribute)
|
||||
void PrintSofaAttributes(const char *prefix, MYSOFA_ATTRIBUTE *attribute)
|
||||
{
|
||||
while(attribute)
|
||||
{
|
||||
|
|
@ -43,11 +50,17 @@ static void PrintSofaAttributes(const char *prefix, struct MYSOFA_ATTRIBUTE *att
|
|||
}
|
||||
}
|
||||
|
||||
static void PrintSofaArray(const char *prefix, struct MYSOFA_ARRAY *array)
|
||||
void PrintSofaArray(const char *prefix, MYSOFA_ARRAY *array, bool showValues=true)
|
||||
{
|
||||
PrintSofaAttributes(prefix, array->attributes);
|
||||
for(uint i{0u};i < array->elements;i++)
|
||||
fprintf(stdout, "%s[%u]: %.6f\n", prefix, i, array->values[i]);
|
||||
if(showValues)
|
||||
{
|
||||
const auto values = al::span{array->values, array->elements};
|
||||
for(size_t i{0u};i < values.size();++i)
|
||||
fprintf(stdout, "%s[%zu]: %.6f\n", prefix, i, values[i]);
|
||||
}
|
||||
else
|
||||
fprintf(stdout, "%s[...]: <%u values suppressed>\n", prefix, array->elements);
|
||||
}
|
||||
|
||||
/* Attempts to produce a compatible layout. Most data sets tend to be
|
||||
|
|
@ -56,11 +69,11 @@ static void PrintSofaArray(const char *prefix, struct MYSOFA_ARRAY *array)
|
|||
* possible. Those sets that contain purely random measurements or use
|
||||
* different major axes will fail.
|
||||
*/
|
||||
static void PrintCompatibleLayout(const uint m, const float *xyzs)
|
||||
void PrintCompatibleLayout(const al::span<const float> xyzs)
|
||||
{
|
||||
fputc('\n', stdout);
|
||||
|
||||
auto fds = GetCompatibleLayout(m, xyzs);
|
||||
auto fds = GetCompatibleLayout(xyzs);
|
||||
if(fds.empty())
|
||||
{
|
||||
fprintf(stdout, "No compatible field layouts in SOFA file.\n");
|
||||
|
|
@ -74,8 +87,8 @@ static void PrintCompatibleLayout(const uint m, const float *xyzs)
|
|||
used_elems += fds[fi].mAzCounts[ei];
|
||||
}
|
||||
|
||||
fprintf(stdout, "Compatible Layout (%u of %u measurements):\n\ndistance = %.3f", used_elems, m,
|
||||
fds[0].mDistance);
|
||||
fprintf(stdout, "Compatible Layout (%u of %zu measurements):\n\ndistance = %.3f", used_elems,
|
||||
xyzs.size()/3, fds[0].mDistance);
|
||||
for(size_t fi{1u};fi < fds.size();fi++)
|
||||
fprintf(stdout, ", %.3f", fds[fi].mDistance);
|
||||
|
||||
|
|
@ -92,13 +105,13 @@ static void PrintCompatibleLayout(const uint m, const float *xyzs)
|
|||
}
|
||||
|
||||
// Load and inspect the given SOFA file.
|
||||
static void SofaInfo(const char *filename)
|
||||
void SofaInfo(const std::string &filename)
|
||||
{
|
||||
int err;
|
||||
MySofaHrtfPtr sofa{mysofa_load(filename, &err)};
|
||||
MySofaHrtfPtr sofa{mysofa_load(filename.c_str(), &err)};
|
||||
if(!sofa)
|
||||
{
|
||||
fprintf(stdout, "Error: Could not load source file '%s' (%s).\n", filename,
|
||||
fprintf(stdout, "Error: Could not load source file '%s' (%s).\n", filename.c_str(),
|
||||
SofaErrorStr(err));
|
||||
return;
|
||||
}
|
||||
|
|
@ -106,7 +119,7 @@ static void SofaInfo(const char *filename)
|
|||
/* NOTE: Some valid SOFA files are failing this check. */
|
||||
err = mysofa_check(sofa.get());
|
||||
if(err != MYSOFA_OK)
|
||||
fprintf(stdout, "Warning: Supposedly malformed source file '%s' (%s).\n", filename,
|
||||
fprintf(stdout, "Warning: Supposedly malformed source file '%s' (%s).\n", filename.c_str(),
|
||||
SofaErrorStr(err));
|
||||
|
||||
mysofa_tocartesian(sofa.get());
|
||||
|
|
@ -120,20 +133,30 @@ static void SofaInfo(const char *filename)
|
|||
|
||||
PrintSofaArray("SampleRate", &sofa->DataSamplingRate);
|
||||
PrintSofaArray("DataDelay", &sofa->DataDelay);
|
||||
PrintSofaArray("SourcePosition", &sofa->SourcePosition, false);
|
||||
|
||||
PrintCompatibleLayout(sofa->M, sofa->SourcePosition.values);
|
||||
PrintCompatibleLayout(al::span{sofa->SourcePosition.values, sofa->M*3_uz});
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int main(al::span<std::string_view> args)
|
||||
{
|
||||
if(argc != 2)
|
||||
if(args.size() != 2)
|
||||
{
|
||||
fprintf(stdout, "Usage: %s <sofa-file>\n", argv[0]);
|
||||
fprintf(stdout, "Usage: %.*s <sofa-file>\n", al::sizei(args[0]), args[0].data());
|
||||
return 0;
|
||||
}
|
||||
|
||||
SofaInfo(argv[1]);
|
||||
SofaInfo(std::string{args[1]});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
assert(argc >= 0);
|
||||
auto args = std::vector<std::string_view>(static_cast<unsigned int>(argc));
|
||||
std::copy_n(argv, args.size(), args.begin());
|
||||
return main(al::span{args});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@
|
|||
|
||||
#include "sofa-support.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ using double3 = std::array<double,3>;
|
|||
* equality of unique elements.
|
||||
*/
|
||||
std::vector<double> GetUniquelySortedElems(const std::vector<double3> &aers, const uint axis,
|
||||
const double *const (&filters)[3], const double (&epsilons)[3])
|
||||
const std::array<const double*,3> &filters, const std::array<double,3> &epsilons)
|
||||
{
|
||||
std::vector<double> elems;
|
||||
for(const double3 &aer : aers)
|
||||
|
|
@ -178,13 +178,13 @@ const char *SofaErrorStr(int err)
|
|||
return "Unknown";
|
||||
}
|
||||
|
||||
std::vector<SofaField> GetCompatibleLayout(const size_t m, const float *xyzs)
|
||||
auto GetCompatibleLayout(const al::span<const float> xyzs) -> std::vector<SofaField>
|
||||
{
|
||||
auto aers = std::vector<double3>(m, double3{});
|
||||
for(size_t i{0u};i < m;++i)
|
||||
auto aers = std::vector<double3>(xyzs.size()/3, double3{});
|
||||
for(size_t i{0u};i < aers.size();++i)
|
||||
{
|
||||
float vals[3]{xyzs[i*3], xyzs[i*3 + 1], xyzs[i*3 + 2]};
|
||||
mysofa_c2s(&vals[0]);
|
||||
std::array vals{xyzs[i*3], xyzs[i*3 + 1], xyzs[i*3 + 2]};
|
||||
mysofa_c2s(vals.data());
|
||||
aers[i] = {vals[0], vals[1], vals[2]};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "alspan.h"
|
||||
|
||||
#include "mysofa.h"
|
||||
|
||||
|
||||
|
|
@ -25,6 +27,6 @@ struct SofaField {
|
|||
|
||||
const char *SofaErrorStr(int err);
|
||||
|
||||
std::vector<SofaField> GetCompatibleLayout(const size_t m, const float *xyzs);
|
||||
auto GetCompatibleLayout(al::span<const float> xyzs) -> std::vector<SofaField>;
|
||||
|
||||
#endif /* UTILS_SOFA_SUPPORT_H */
|
||||
|
|
|
|||
|
|
@ -24,21 +24,26 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "albit.h"
|
||||
#include "albyte.h"
|
||||
#include "alcomplex.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumbers.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "vector.h"
|
||||
#include "opthelpers.h"
|
||||
#include "phase_shifter.h"
|
||||
|
|
@ -48,8 +53,10 @@
|
|||
#include "win_main_utf8.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct FileDeleter {
|
||||
void operator()(FILE *file) { fclose(file); }
|
||||
void operator()(gsl::owner<FILE*> file) { fclose(file); }
|
||||
};
|
||||
using FilePtr = std::unique_ptr<FILE,FileDeleter>;
|
||||
|
||||
|
|
@ -64,44 +71,35 @@ using ushort = unsigned short;
|
|||
using uint = unsigned int;
|
||||
using complex_d = std::complex<double>;
|
||||
|
||||
using byte4 = std::array<al::byte,4>;
|
||||
using byte4 = std::array<std::byte,4>;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template<al::endian = al::endian::native>
|
||||
byte4 f32AsLEBytes(const float &value) = delete;
|
||||
|
||||
template<>
|
||||
byte4 f32AsLEBytes<al::endian::little>(const float &value)
|
||||
byte4 f32AsLEBytes(const float value)
|
||||
{
|
||||
byte4 ret{};
|
||||
std::memcpy(ret.data(), &value, 4);
|
||||
return ret;
|
||||
}
|
||||
template<>
|
||||
byte4 f32AsLEBytes<al::endian::big>(const float &value)
|
||||
{
|
||||
byte4 ret{};
|
||||
std::memcpy(ret.data(), &value, 4);
|
||||
std::swap(ret[0], ret[3]);
|
||||
std::swap(ret[1], ret[2]);
|
||||
auto ret = al::bit_cast<byte4>(value);
|
||||
if constexpr(al::endian::native == al::endian::big)
|
||||
{
|
||||
std::swap(ret[0], ret[3]);
|
||||
std::swap(ret[1], ret[2]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +111,7 @@ using FloatBufferSpan = al::span<float,BufferLineSize>;
|
|||
|
||||
|
||||
struct UhjDecoder {
|
||||
constexpr static size_t sFilterDelay{1024};
|
||||
constexpr static std::size_t sFilterDelay{1024};
|
||||
|
||||
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mS{};
|
||||
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mD{};
|
||||
|
|
@ -126,12 +124,10 @@ struct UhjDecoder {
|
|||
|
||||
alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mTemp{};
|
||||
|
||||
void decode(const float *RESTRICT InSamples, const size_t InChannels,
|
||||
const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo);
|
||||
void decode2(const float *RESTRICT InSamples, const al::span<FloatBufferLine> OutSamples,
|
||||
const size_t SamplesToDo);
|
||||
|
||||
DEF_NEWDEL(UhjDecoder)
|
||||
void decode(const al::span<const float> InSamples, const std::size_t InChannels,
|
||||
const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo);
|
||||
void decode2(const al::span<const float> InSamples, const al::span<FloatBufferLine> OutSamples,
|
||||
const std::size_t SamplesToDo);
|
||||
};
|
||||
|
||||
const PhaseShifterT<UhjDecoder::sFilterDelay*2> PShift{};
|
||||
|
|
@ -210,37 +206,37 @@ const PhaseShifterT<UhjDecoder::sFilterDelay*2> PShift{};
|
|||
*
|
||||
* Not halving produces a result matching the original input.
|
||||
*/
|
||||
void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels,
|
||||
const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo)
|
||||
void UhjDecoder::decode(const al::span<const float> InSamples, const std::size_t InChannels,
|
||||
const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo)
|
||||
{
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
float *woutput{OutSamples[0].data()};
|
||||
float *xoutput{OutSamples[1].data()};
|
||||
float *youtput{OutSamples[2].data()};
|
||||
auto woutput = al::span{OutSamples[0]};
|
||||
auto xoutput = al::span{OutSamples[1]};
|
||||
auto youtput = al::span{OutSamples[2]};
|
||||
|
||||
/* Add a delay to the input channels, to align it with the all-passed
|
||||
* signal.
|
||||
*/
|
||||
|
||||
/* S = Left + Right */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
mS[sFilterDelay+i] = InSamples[i*InChannels + 0] + InSamples[i*InChannels + 1];
|
||||
|
||||
/* D = Left - Right */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
mD[sFilterDelay+i] = InSamples[i*InChannels + 0] - InSamples[i*InChannels + 1];
|
||||
|
||||
if(InChannels > 2)
|
||||
{
|
||||
/* T */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
mT[sFilterDelay+i] = InSamples[i*InChannels + 2];
|
||||
}
|
||||
if(InChannels > 3)
|
||||
{
|
||||
/* Q */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
mQ[sFilterDelay+i] = InSamples[i*InChannels + 3];
|
||||
}
|
||||
|
||||
|
|
@ -249,9 +245,9 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
|
|||
std::transform(mD.cbegin(), mD.cbegin()+SamplesToDo+sFilterDelay, mT.cbegin(), tmpiter,
|
||||
[](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; });
|
||||
std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin());
|
||||
PShift.process({xoutput, SamplesToDo}, mTemp.data());
|
||||
PShift.process(xoutput.first(SamplesToDo), mTemp);
|
||||
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
{
|
||||
/* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */
|
||||
woutput[i] = 0.981532f*mS[i] + 0.197484f*xoutput[i];
|
||||
|
|
@ -263,9 +259,9 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
|
|||
tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin());
|
||||
std::copy_n(mS.cbegin(), SamplesToDo+sFilterDelay, tmpiter);
|
||||
std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin());
|
||||
PShift.process({youtput, SamplesToDo}, mTemp.data());
|
||||
PShift.process(youtput.first(SamplesToDo), mTemp);
|
||||
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
{
|
||||
/* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */
|
||||
youtput[i] = 0.795968f*mD[i] - 0.676392f*mT[i] + 0.186633f*youtput[i];
|
||||
|
|
@ -273,9 +269,9 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
|
|||
|
||||
if(OutSamples.size() > 3)
|
||||
{
|
||||
float *zoutput{OutSamples[3].data()};
|
||||
auto zoutput = al::span{OutSamples[3]};
|
||||
/* Z = 1.023332*Q */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
zoutput[i] = 1.023332f*mQ[i];
|
||||
}
|
||||
|
||||
|
|
@ -304,30 +300,30 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
|
|||
* NOTE: As above, S and D should not be halved. The only consequence of
|
||||
* halving here is merely a -6dB reduction in output, but it's still incorrect.
|
||||
*/
|
||||
void UhjDecoder::decode2(const float *RESTRICT InSamples,
|
||||
const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo)
|
||||
void UhjDecoder::decode2(const al::span<const float> InSamples,
|
||||
const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo)
|
||||
{
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
float *woutput{OutSamples[0].data()};
|
||||
float *xoutput{OutSamples[1].data()};
|
||||
float *youtput{OutSamples[2].data()};
|
||||
auto woutput = al::span{OutSamples[0]};
|
||||
auto xoutput = al::span{OutSamples[1]};
|
||||
auto youtput = al::span{OutSamples[2]};
|
||||
|
||||
/* S = Left + Right */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
mS[sFilterDelay+i] = InSamples[i*2 + 0] + InSamples[i*2 + 1];
|
||||
|
||||
/* D = Left - Right */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
mD[sFilterDelay+i] = InSamples[i*2 + 0] - InSamples[i*2 + 1];
|
||||
|
||||
/* Precompute j*D and store in xoutput. */
|
||||
auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin());
|
||||
std::copy_n(mD.cbegin(), SamplesToDo+sFilterDelay, tmpiter);
|
||||
std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin());
|
||||
PShift.process({xoutput, SamplesToDo}, mTemp.data());
|
||||
PShift.process(xoutput.first(SamplesToDo), mTemp);
|
||||
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
{
|
||||
/* W = 0.981530*S + j*0.163585*D */
|
||||
woutput[i] = 0.981530f*mS[i] + 0.163585f*xoutput[i];
|
||||
|
|
@ -339,9 +335,9 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples,
|
|||
tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin());
|
||||
std::copy_n(mS.cbegin(), SamplesToDo+sFilterDelay, tmpiter);
|
||||
std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin());
|
||||
PShift.process({youtput, SamplesToDo}, mTemp.data());
|
||||
PShift.process(youtput.first(SamplesToDo), mTemp);
|
||||
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
for(std::size_t i{0};i < SamplesToDo;++i)
|
||||
{
|
||||
/* Y = 0.762956*D + j*0.384230*S */
|
||||
youtput[i] = 0.762956f*mD[i] + 0.384230f*youtput[i];
|
||||
|
|
@ -352,11 +348,11 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples,
|
|||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(al::span<std::string_view> args)
|
||||
{
|
||||
if(argc < 2 || std::strcmp(argv[1], "-h") == 0 || std::strcmp(argv[1], "--help") == 0)
|
||||
if(args.size() < 2 || args[1] == "-h" || args[1] == "--help")
|
||||
{
|
||||
printf("Usage: %s <[options] filename.wav...>\n\n"
|
||||
printf("Usage: %.*s <[options] filename.wav...>\n\n"
|
||||
" Options:\n"
|
||||
" --general Use the general equations for 2-channel UHJ (default).\n"
|
||||
" --alternative Use the alternative equations for 2-channel UHJ.\n"
|
||||
|
|
@ -364,35 +360,36 @@ int main(int argc, char **argv)
|
|||
"Note: When decoding 2-channel UHJ to an .amb file, the result should not use\n"
|
||||
"the normal B-Format shelf filters! Only 3- and 4-channel UHJ can accurately\n"
|
||||
"reconstruct the original B-Format signal.",
|
||||
argv[0]);
|
||||
al::sizei(args[0]), args[0].data());
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t num_files{0}, num_decoded{0};
|
||||
std::size_t num_files{0}, num_decoded{0};
|
||||
bool use_general{true};
|
||||
for(int fidx{1};fidx < argc;++fidx)
|
||||
for(size_t fidx{1};fidx < args.size();++fidx)
|
||||
{
|
||||
if(std::strcmp(argv[fidx], "--general") == 0)
|
||||
if(args[fidx] == "--general")
|
||||
{
|
||||
use_general = true;
|
||||
continue;
|
||||
}
|
||||
if(std::strcmp(argv[fidx], "--alternative") == 0)
|
||||
if(args[fidx] == "--alternative")
|
||||
{
|
||||
use_general = false;
|
||||
continue;
|
||||
}
|
||||
++num_files;
|
||||
SF_INFO ininfo{};
|
||||
SndFilePtr infile{sf_open(argv[fidx], SFM_READ, &ininfo)};
|
||||
SndFilePtr infile{sf_open(std::string{args[fidx]}.c_str(), SFM_READ, &ininfo)};
|
||||
if(!infile)
|
||||
{
|
||||
fprintf(stderr, "Failed to open %s\n", argv[fidx]);
|
||||
fprintf(stderr, "Failed to open %.*s\n", al::sizei(args[fidx]), args[fidx].data());
|
||||
continue;
|
||||
}
|
||||
if(sf_command(infile.get(), SFC_WAVEX_GET_AMBISONIC, NULL, 0) == SF_AMBISONIC_B_FORMAT)
|
||||
if(sf_command(infile.get(), SFC_WAVEX_GET_AMBISONIC, nullptr, 0) == SF_AMBISONIC_B_FORMAT)
|
||||
{
|
||||
fprintf(stderr, "%s is already B-Format\n", argv[fidx]);
|
||||
fprintf(stderr, "%.*s is already B-Format\n", al::sizei(args[fidx]),
|
||||
args[fidx].data());
|
||||
continue;
|
||||
}
|
||||
uint outchans{};
|
||||
|
|
@ -402,13 +399,15 @@ int main(int argc, char **argv)
|
|||
outchans = static_cast<uint>(ininfo.channels);
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s is not a 2-, 3-, or 4-channel file\n", argv[fidx]);
|
||||
fprintf(stderr, "%.*s is not a 2-, 3-, or 4-channel file\n", al::sizei(args[fidx]),
|
||||
args[fidx].data());
|
||||
continue;
|
||||
}
|
||||
printf("Converting %s from %d-channel UHJ%s...\n", argv[fidx], ininfo.channels,
|
||||
printf("Converting %.*s from %d-channel UHJ%s...\n", al::sizei(args[fidx]),
|
||||
args[fidx].data(), ininfo.channels,
|
||||
(ininfo.channels == 2) ? use_general ? " (general)" : " (alternative)" : "");
|
||||
|
||||
std::string outname{argv[fidx]};
|
||||
std::string outname{args[fidx]};
|
||||
auto lastslash = outname.find_last_of('/');
|
||||
if(lastslash != std::string::npos)
|
||||
outname.erase(0, lastslash+1);
|
||||
|
|
@ -439,7 +438,7 @@ int main(int argc, char **argv)
|
|||
// 32-bit val, frequency
|
||||
fwrite32le(static_cast<uint>(ininfo.samplerate), outfile.get());
|
||||
// 32-bit val, bytes per second
|
||||
fwrite32le(static_cast<uint>(ininfo.samplerate)*sizeof(float)*outchans, outfile.get());
|
||||
fwrite32le(static_cast<uint>(ininfo.samplerate)*outchans*uint{sizeof(float)}, outfile.get());
|
||||
// 16-bit val, frame size
|
||||
fwrite16le(static_cast<ushort>(sizeof(float)*outchans), outfile.get());
|
||||
// 16-bit val, bits per sample
|
||||
|
|
@ -451,47 +450,48 @@ int main(int argc, char **argv)
|
|||
// 32-bit val, channel mask
|
||||
fwrite32le(0, outfile.get());
|
||||
// 16 byte GUID, sub-type format
|
||||
fwrite(SUBTYPE_BFORMAT_FLOAT, 1, 16, outfile.get());
|
||||
fwrite(SUBTYPE_BFORMAT_FLOAT.data(), 1, SUBTYPE_BFORMAT_FLOAT.size(), outfile.get());
|
||||
|
||||
fputs("data", outfile.get());
|
||||
fwrite32le(0xFFFFFFFF, outfile.get()); // 'data' header len; filled in at close
|
||||
if(ferror(outfile.get()))
|
||||
{
|
||||
fprintf(stderr, "Error writing wave file header: %s (%d)\n", strerror(errno), errno);
|
||||
fprintf(stderr, "Error writing wave file header: %s (%d)\n",
|
||||
std::generic_category().message(errno).c_str(), errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto DataStart = ftell(outfile.get());
|
||||
|
||||
auto decoder = std::make_unique<UhjDecoder>();
|
||||
auto inmem = std::make_unique<float[]>(BufferLineSize*static_cast<uint>(ininfo.channels));
|
||||
auto inmem = std::vector<float>(size_t{BufferLineSize}*static_cast<uint>(ininfo.channels));
|
||||
auto decmem = al::vector<std::array<float,BufferLineSize>, 16>(outchans);
|
||||
auto outmem = std::make_unique<byte4[]>(BufferLineSize*outchans);
|
||||
auto outmem = std::vector<byte4>(size_t{BufferLineSize}*outchans);
|
||||
|
||||
/* A number of initial samples need to be skipped to cut the lead-in
|
||||
* from the all-pass filter delay. The same number of samples need to
|
||||
* be fed through the decoder after reaching the end of the input file
|
||||
* to ensure none of the original input is lost.
|
||||
*/
|
||||
size_t LeadIn{UhjDecoder::sFilterDelay};
|
||||
std::size_t LeadIn{UhjDecoder::sFilterDelay};
|
||||
sf_count_t LeadOut{UhjDecoder::sFilterDelay};
|
||||
while(LeadOut > 0)
|
||||
{
|
||||
sf_count_t sgot{sf_readf_float(infile.get(), inmem.get(), BufferLineSize)};
|
||||
sf_count_t sgot{sf_readf_float(infile.get(), inmem.data(), BufferLineSize)};
|
||||
sgot = std::max<sf_count_t>(sgot, 0);
|
||||
if(sgot < BufferLineSize)
|
||||
{
|
||||
const sf_count_t remaining{std::min(BufferLineSize - sgot, LeadOut)};
|
||||
std::fill_n(inmem.get() + sgot*ininfo.channels, remaining*ininfo.channels, 0.0f);
|
||||
std::fill_n(inmem.begin() + sgot*ininfo.channels, remaining*ininfo.channels, 0.0f);
|
||||
sgot += remaining;
|
||||
LeadOut -= remaining;
|
||||
}
|
||||
|
||||
auto got = static_cast<size_t>(sgot);
|
||||
auto got = static_cast<std::size_t>(sgot);
|
||||
if(ininfo.channels > 2 || use_general)
|
||||
decoder->decode(inmem.get(), static_cast<uint>(ininfo.channels), decmem, got);
|
||||
decoder->decode(inmem, static_cast<uint>(ininfo.channels), decmem, got);
|
||||
else
|
||||
decoder->decode2(inmem.get(), decmem, got);
|
||||
decoder->decode2(inmem, decmem, got);
|
||||
if(LeadIn >= got)
|
||||
{
|
||||
LeadIn -= got;
|
||||
|
|
@ -499,19 +499,20 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
got -= LeadIn;
|
||||
for(size_t i{0};i < got;++i)
|
||||
for(std::size_t i{0};i < got;++i)
|
||||
{
|
||||
/* Attenuate by -3dB for FuMa output levels. */
|
||||
constexpr auto inv_sqrt2 = static_cast<float>(1.0/al::numbers::sqrt2);
|
||||
for(size_t j{0};j < outchans;++j)
|
||||
for(std::size_t j{0};j < outchans;++j)
|
||||
outmem[i*outchans + j] = f32AsLEBytes(decmem[j][LeadIn+i] * inv_sqrt2);
|
||||
}
|
||||
LeadIn = 0;
|
||||
|
||||
size_t wrote{fwrite(outmem.get(), sizeof(byte4)*outchans, got, outfile.get())};
|
||||
std::size_t wrote{fwrite(outmem.data(), sizeof(byte4)*outchans, got, outfile.get())};
|
||||
if(wrote < got)
|
||||
{
|
||||
fprintf(stderr, "Error writing wave data: %s (%d)\n", strerror(errno), errno);
|
||||
fprintf(stderr, "Error writing wave data: %s (%d)\n",
|
||||
std::generic_category().message(errno).c_str(), errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -536,3 +537,13 @@ int main(int argc, char **argv)
|
|||
printf("Decoded %zu file%s\n", num_decoded, (num_decoded==1)?"":"s");
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
assert(argc >= 0);
|
||||
auto args = std::vector<std::string_view>(static_cast<unsigned int>(argc));
|
||||
std::copy_n(argv, args.size(), args.begin());
|
||||
return main(al::span{args});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,19 +24,21 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <inttypes.h>
|
||||
#include <cassert>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "alnumbers.h"
|
||||
#include "alspan.h"
|
||||
#include "opthelpers.h"
|
||||
#include "alstring.h"
|
||||
#include "phase_shifter.h"
|
||||
#include "vector.h"
|
||||
|
||||
|
|
@ -81,9 +83,7 @@ struct UhjEncoder {
|
|||
alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mTemp{};
|
||||
|
||||
void encode(const al::span<FloatBufferLine> OutSamples,
|
||||
const al::span<FloatBufferLine,4> InSamples, const size_t SamplesToDo);
|
||||
|
||||
DEF_NEWDEL(UhjEncoder)
|
||||
const al::span<const FloatBufferLine,4> InSamples, const size_t SamplesToDo);
|
||||
};
|
||||
|
||||
const PhaseShifterT<UhjEncoder::sFilterDelay*2> PShift{};
|
||||
|
|
@ -103,18 +103,18 @@ const PhaseShifterT<UhjEncoder::sFilterDelay*2> PShift{};
|
|||
* output, and Q is excluded from 2- and 3-channel output.
|
||||
*/
|
||||
void UhjEncoder::encode(const al::span<FloatBufferLine> OutSamples,
|
||||
const al::span<FloatBufferLine,4> InSamples, const size_t SamplesToDo)
|
||||
const al::span<const FloatBufferLine,4> InSamples, const size_t SamplesToDo)
|
||||
{
|
||||
const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0].data())};
|
||||
const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1].data())};
|
||||
const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2].data())};
|
||||
const float *RESTRICT zinput{al::assume_aligned<16>(InSamples[3].data())};
|
||||
const auto winput = al::span{InSamples[0]}.first(SamplesToDo);
|
||||
const auto xinput = al::span{InSamples[1]}.first(SamplesToDo);
|
||||
const auto yinput = al::span{InSamples[2]}.first(SamplesToDo);
|
||||
const auto zinput = al::span{InSamples[3]}.first(SamplesToDo);
|
||||
|
||||
/* Combine the previously delayed input signal with the new input. */
|
||||
std::copy_n(winput, SamplesToDo, mW.begin()+sFilterDelay);
|
||||
std::copy_n(xinput, SamplesToDo, mX.begin()+sFilterDelay);
|
||||
std::copy_n(yinput, SamplesToDo, mY.begin()+sFilterDelay);
|
||||
std::copy_n(zinput, SamplesToDo, mZ.begin()+sFilterDelay);
|
||||
std::copy(winput.begin(), winput.end(), mW.begin()+sFilterDelay);
|
||||
std::copy(xinput.begin(), xinput.end(), mX.begin()+sFilterDelay);
|
||||
std::copy(yinput.begin(), yinput.end(), mY.begin()+sFilterDelay);
|
||||
std::copy(zinput.begin(), zinput.end(), mZ.begin()+sFilterDelay);
|
||||
|
||||
/* S = 0.9396926*W + 0.1855740*X */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
|
|
@ -122,22 +122,22 @@ void UhjEncoder::encode(const al::span<FloatBufferLine> OutSamples,
|
|||
|
||||
/* Precompute j(-0.3420201*W + 0.5098604*X) and store in mD. */
|
||||
auto tmpiter = std::copy(mWXHistory1.cbegin(), mWXHistory1.cend(), mTemp.begin());
|
||||
std::transform(winput, winput+SamplesToDo, xinput, tmpiter,
|
||||
std::transform(winput.begin(), winput.end(), xinput.begin(), tmpiter,
|
||||
[](const float w, const float x) noexcept -> float
|
||||
{ return -0.3420201f*w + 0.5098604f*x; });
|
||||
std::copy_n(mTemp.cbegin()+SamplesToDo, mWXHistory1.size(), mWXHistory1.begin());
|
||||
PShift.process({mD.data(), SamplesToDo}, mTemp.data());
|
||||
PShift.process(al::span{mD}.first(SamplesToDo), mTemp);
|
||||
|
||||
/* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y */
|
||||
for(size_t i{0};i < SamplesToDo;++i)
|
||||
mD[i] = mD[i] + 0.6554516f*mY[i];
|
||||
|
||||
/* Left = (S + D)/2.0 */
|
||||
float *RESTRICT left{al::assume_aligned<16>(OutSamples[0].data())};
|
||||
auto left = al::span{OutSamples[0]};
|
||||
for(size_t i{0};i < SamplesToDo;i++)
|
||||
left[i] = (mS[i] + mD[i]) * 0.5f;
|
||||
/* Right = (S - D)/2.0 */
|
||||
float *RESTRICT right{al::assume_aligned<16>(OutSamples[1].data())};
|
||||
auto right = al::span{OutSamples[1]};
|
||||
for(size_t i{0};i < SamplesToDo;i++)
|
||||
right[i] = (mS[i] - mD[i]) * 0.5f;
|
||||
|
||||
|
|
@ -145,21 +145,21 @@ void UhjEncoder::encode(const al::span<FloatBufferLine> OutSamples,
|
|||
{
|
||||
/* Precompute j(-0.1432*W + 0.6512*X) and store in mT. */
|
||||
tmpiter = std::copy(mWXHistory2.cbegin(), mWXHistory2.cend(), mTemp.begin());
|
||||
std::transform(winput, winput+SamplesToDo, xinput, tmpiter,
|
||||
std::transform(winput.begin(), winput.end(), xinput.begin(), tmpiter,
|
||||
[](const float w, const float x) noexcept -> float
|
||||
{ return -0.1432f*w + 0.6512f*x; });
|
||||
std::copy_n(mTemp.cbegin()+SamplesToDo, mWXHistory2.size(), mWXHistory2.begin());
|
||||
PShift.process({mT.data(), SamplesToDo}, mTemp.data());
|
||||
PShift.process(al::span{mT}.first(SamplesToDo), mTemp);
|
||||
|
||||
/* T = j(-0.1432*W + 0.6512*X) - 0.7071068*Y */
|
||||
float *RESTRICT t{al::assume_aligned<16>(OutSamples[2].data())};
|
||||
auto t = al::span{OutSamples[2]};
|
||||
for(size_t i{0};i < SamplesToDo;i++)
|
||||
t[i] = mT[i] - 0.7071068f*mY[i];
|
||||
}
|
||||
if(OutSamples.size() > 3)
|
||||
{
|
||||
/* Q = 0.9772*Z */
|
||||
float *RESTRICT q{al::assume_aligned<16>(OutSamples[3].data())};
|
||||
auto q = al::span{OutSamples[3]};
|
||||
for(size_t i{0};i < SamplesToDo;i++)
|
||||
q[i] = 0.9772f*mZ[i];
|
||||
}
|
||||
|
|
@ -179,50 +179,58 @@ struct SpeakerPos {
|
|||
};
|
||||
|
||||
/* Azimuth is counter-clockwise. */
|
||||
constexpr SpeakerPos StereoMap[2]{
|
||||
{ SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f },
|
||||
}, QuadMap[4]{
|
||||
{ SF_CHANNEL_MAP_LEFT, 45.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_RIGHT, -45.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_REAR_LEFT, 135.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_REAR_RIGHT, -135.0f, 0.0f },
|
||||
}, X51Map[6]{
|
||||
{ SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_LFE, 0.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_SIDE_LEFT, 110.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_SIDE_RIGHT, -110.0f, 0.0f },
|
||||
}, X51RearMap[6]{
|
||||
{ SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_LFE, 0.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_REAR_LEFT, 110.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_REAR_RIGHT, -110.0f, 0.0f },
|
||||
}, X71Map[8]{
|
||||
{ SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_LFE, 0.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f },
|
||||
}, X714Map[12]{
|
||||
{ SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_LFE, 0.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f },
|
||||
{ SF_CHANNEL_MAP_TOP_FRONT_LEFT, 45.0f, 35.0f },
|
||||
{ SF_CHANNEL_MAP_TOP_FRONT_RIGHT, -45.0f, 35.0f },
|
||||
{ SF_CHANNEL_MAP_TOP_REAR_LEFT, 135.0f, 35.0f },
|
||||
{ SF_CHANNEL_MAP_TOP_REAR_RIGHT, -135.0f, 35.0f },
|
||||
constexpr std::array MonoMap{
|
||||
SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f},
|
||||
};
|
||||
constexpr std::array StereoMap{
|
||||
SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f},
|
||||
};
|
||||
constexpr std::array QuadMap{
|
||||
SpeakerPos{SF_CHANNEL_MAP_LEFT, 45.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_RIGHT, -45.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_REAR_LEFT, 135.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_REAR_RIGHT, -135.0f, 0.0f},
|
||||
};
|
||||
constexpr std::array X51Map{
|
||||
SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_LFE, 0.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_SIDE_LEFT, 110.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_SIDE_RIGHT, -110.0f, 0.0f},
|
||||
};
|
||||
constexpr std::array X51RearMap{
|
||||
SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_LFE, 0.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_REAR_LEFT, 110.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_REAR_RIGHT, -110.0f, 0.0f},
|
||||
};
|
||||
constexpr std::array X71Map{
|
||||
SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_LFE, 0.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f},
|
||||
};
|
||||
constexpr std::array X714Map{
|
||||
SpeakerPos{SF_CHANNEL_MAP_LEFT, 30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_RIGHT, -30.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_CENTER, 0.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_LFE, 0.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_REAR_LEFT, 150.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_REAR_RIGHT, -150.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_SIDE_LEFT, 90.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_SIDE_RIGHT, -90.0f, 0.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_TOP_FRONT_LEFT, 45.0f, 35.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_TOP_FRONT_RIGHT, -45.0f, 35.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_TOP_REAR_LEFT, 135.0f, 35.0f},
|
||||
SpeakerPos{SF_CHANNEL_MAP_TOP_REAR_RIGHT, -135.0f, 35.0f},
|
||||
};
|
||||
|
||||
constexpr auto GenCoeffs(double x /*+front*/, double y /*+left*/, double z /*+up*/) noexcept
|
||||
|
|
@ -236,39 +244,37 @@ constexpr auto GenCoeffs(double x /*+front*/, double y /*+left*/, double z /*+up
|
|||
}};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(al::span<std::string_view> args)
|
||||
{
|
||||
if(argc < 2 || std::strcmp(argv[1], "-h") == 0 || std::strcmp(argv[1], "--help") == 0)
|
||||
if(args.size() < 2 || args[1] == "-h" || args[1] == "--help")
|
||||
{
|
||||
printf("Usage: %s <infile...>\n\n", argv[0]);
|
||||
printf("Usage: %.*s <infile...>\n\n", al::sizei(args[0]), args[0].data());
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint uhjchans{2};
|
||||
size_t num_files{0}, num_encoded{0};
|
||||
for(int fidx{1};fidx < argc;++fidx)
|
||||
for(size_t fidx{1};fidx < args.size();++fidx)
|
||||
{
|
||||
if(strcmp(argv[fidx], "-bhj") == 0)
|
||||
if(args[fidx] == "-bhj")
|
||||
{
|
||||
uhjchans = 2;
|
||||
continue;
|
||||
}
|
||||
if(strcmp(argv[fidx], "-thj") == 0)
|
||||
if(args[fidx] == "-thj")
|
||||
{
|
||||
uhjchans = 3;
|
||||
continue;
|
||||
}
|
||||
if(strcmp(argv[fidx], "-phj") == 0)
|
||||
if(args[fidx] == "-phj")
|
||||
{
|
||||
uhjchans = 4;
|
||||
continue;
|
||||
}
|
||||
++num_files;
|
||||
|
||||
std::string outname{argv[fidx]};
|
||||
std::string outname{args[fidx]};
|
||||
size_t lastslash{outname.find_last_of('/')};
|
||||
if(lastslash != std::string::npos)
|
||||
outname.erase(0, lastslash+1);
|
||||
|
|
@ -278,13 +284,14 @@ int main(int argc, char **argv)
|
|||
outname += ".uhj.flac";
|
||||
|
||||
SF_INFO ininfo{};
|
||||
SndFilePtr infile{sf_open(argv[fidx], SFM_READ, &ininfo)};
|
||||
SndFilePtr infile{sf_open(std::string{args[fidx]}.c_str(), SFM_READ, &ininfo)};
|
||||
if(!infile)
|
||||
{
|
||||
fprintf(stderr, "Failed to open %s\n", argv[fidx]);
|
||||
fprintf(stderr, "Failed to open %.*s\n", al::sizei(args[fidx]), args[fidx].data());
|
||||
continue;
|
||||
}
|
||||
printf("Converting %s to %s...\n", argv[fidx], outname.c_str());
|
||||
printf("Converting %.*s to %s...\n", al::sizei(args[fidx]), args[fidx].data(),
|
||||
outname.c_str());
|
||||
|
||||
/* Work out the channel map, preferably using the actual channel map
|
||||
* from the file/format, but falling back to assuming WFX order.
|
||||
|
|
@ -294,6 +301,7 @@ int main(int argc, char **argv)
|
|||
if(sf_command(infile.get(), SFC_GET_CHANNEL_MAP_INFO, chanmap.data(),
|
||||
ininfo.channels*int{sizeof(int)}) == SF_TRUE)
|
||||
{
|
||||
static const std::array<int,1> monomap{{SF_CHANNEL_MAP_CENTER}};
|
||||
static const std::array<int,2> stereomap{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT}};
|
||||
static const std::array<int,4> quadmap{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT,
|
||||
SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT}};
|
||||
|
|
@ -323,14 +331,13 @@ int main(int argc, char **argv)
|
|||
{
|
||||
if(a.size() != b.size())
|
||||
return false;
|
||||
for(const int id : a)
|
||||
{
|
||||
if(std::find(b.begin(), b.end(), id) != b.end())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
auto find_channel = [b](const int id) -> bool
|
||||
{ return std::find(b.begin(), b.end(), id) != b.end(); };
|
||||
return std::all_of(a.cbegin(), a.cend(), find_channel);
|
||||
};
|
||||
if(match_chanmap(chanmap, stereomap))
|
||||
if(match_chanmap(chanmap, monomap))
|
||||
spkrs = MonoMap;
|
||||
else if(match_chanmap(chanmap, stereomap))
|
||||
spkrs = StereoMap;
|
||||
else if(match_chanmap(chanmap, quadmap))
|
||||
spkrs = QuadMap;
|
||||
|
|
@ -363,20 +370,26 @@ int main(int argc, char **argv)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
else if(ininfo.channels == 1)
|
||||
{
|
||||
fprintf(stderr, " ... assuming front-center\n");
|
||||
spkrs = MonoMap;
|
||||
chanmap[0] = SF_CHANNEL_MAP_CENTER;
|
||||
}
|
||||
else if(ininfo.channels == 2)
|
||||
{
|
||||
fprintf(stderr, " ... assuming WFX order stereo\n");
|
||||
spkrs = StereoMap;
|
||||
chanmap[0] = SF_CHANNEL_MAP_FRONT_LEFT;
|
||||
chanmap[1] = SF_CHANNEL_MAP_FRONT_RIGHT;
|
||||
chanmap[0] = SF_CHANNEL_MAP_LEFT;
|
||||
chanmap[1] = SF_CHANNEL_MAP_RIGHT;
|
||||
}
|
||||
else if(ininfo.channels == 6)
|
||||
{
|
||||
fprintf(stderr, " ... assuming WFX order 5.1\n");
|
||||
spkrs = X51Map;
|
||||
chanmap[0] = SF_CHANNEL_MAP_FRONT_LEFT;
|
||||
chanmap[1] = SF_CHANNEL_MAP_FRONT_RIGHT;
|
||||
chanmap[2] = SF_CHANNEL_MAP_FRONT_CENTER;
|
||||
chanmap[0] = SF_CHANNEL_MAP_LEFT;
|
||||
chanmap[1] = SF_CHANNEL_MAP_RIGHT;
|
||||
chanmap[2] = SF_CHANNEL_MAP_CENTER;
|
||||
chanmap[3] = SF_CHANNEL_MAP_LFE;
|
||||
chanmap[4] = SF_CHANNEL_MAP_SIDE_LEFT;
|
||||
chanmap[5] = SF_CHANNEL_MAP_SIDE_RIGHT;
|
||||
|
|
@ -385,9 +398,9 @@ int main(int argc, char **argv)
|
|||
{
|
||||
fprintf(stderr, " ... assuming WFX order 7.1\n");
|
||||
spkrs = X71Map;
|
||||
chanmap[0] = SF_CHANNEL_MAP_FRONT_LEFT;
|
||||
chanmap[1] = SF_CHANNEL_MAP_FRONT_RIGHT;
|
||||
chanmap[2] = SF_CHANNEL_MAP_FRONT_CENTER;
|
||||
chanmap[0] = SF_CHANNEL_MAP_LEFT;
|
||||
chanmap[1] = SF_CHANNEL_MAP_RIGHT;
|
||||
chanmap[2] = SF_CHANNEL_MAP_CENTER;
|
||||
chanmap[3] = SF_CHANNEL_MAP_LFE;
|
||||
chanmap[4] = SF_CHANNEL_MAP_REAR_LEFT;
|
||||
chanmap[5] = SF_CHANNEL_MAP_REAR_RIGHT;
|
||||
|
|
@ -413,11 +426,15 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
auto encoder = std::make_unique<UhjEncoder>();
|
||||
auto splbuf = al::vector<FloatBufferLine, 16>(static_cast<uint>(9+ininfo.channels)+uhjchans);
|
||||
auto ambmem = al::span<FloatBufferLine,4>{splbuf.data(), 4};
|
||||
auto encmem = al::span<FloatBufferLine,4>{&splbuf[4], 4};
|
||||
auto srcmem = al::span<float,BufferLineSize>{splbuf[8].data(), BufferLineSize};
|
||||
auto outmem = al::span<float>{splbuf[9].data(), BufferLineSize*uhjchans};
|
||||
auto splbuf = al::vector<FloatBufferLine, 16>(9);
|
||||
auto ambmem = al::span{splbuf}.subspan<0,4>();
|
||||
auto encmem = al::span{splbuf}.subspan<4,4>();
|
||||
auto srcmem = al::span{splbuf[8]};
|
||||
auto membuf = al::vector<float,16>((static_cast<uint>(ininfo.channels)+size_t{uhjchans})
|
||||
* BufferLineSize);
|
||||
auto outmem = al::span{membuf}.first(size_t{BufferLineSize}*uhjchans);
|
||||
auto inmem = al::span{membuf}.last(size_t{BufferLineSize}
|
||||
* static_cast<uint>(ininfo.channels));
|
||||
|
||||
/* A number of initial samples need to be skipped to cut the lead-in
|
||||
* from the all-pass filter delay. The same number of samples need to
|
||||
|
|
@ -429,14 +446,13 @@ int main(int argc, char **argv)
|
|||
sf_count_t LeadOut{UhjEncoder::sFilterDelay};
|
||||
while(LeadIn > 0 || LeadOut > 0)
|
||||
{
|
||||
auto inmem = outmem.data() + outmem.size();
|
||||
auto sgot = sf_readf_float(infile.get(), inmem, BufferLineSize);
|
||||
auto sgot = sf_readf_float(infile.get(), inmem.data(), BufferLineSize);
|
||||
|
||||
sgot = std::max<sf_count_t>(sgot, 0);
|
||||
if(sgot < BufferLineSize)
|
||||
{
|
||||
const sf_count_t remaining{std::min(BufferLineSize - sgot, LeadOut)};
|
||||
std::fill_n(inmem + sgot*ininfo.channels, remaining*ininfo.channels, 0.0f);
|
||||
std::fill_n(inmem.begin() + sgot*ininfo.channels, remaining*ininfo.channels, 0.0f);
|
||||
sgot += remaining;
|
||||
LeadOut -= remaining;
|
||||
}
|
||||
|
|
@ -455,21 +471,18 @@ int main(int argc, char **argv)
|
|||
for(size_t c{0};c < chans;++c)
|
||||
{
|
||||
for(size_t i{0};i < got;++i)
|
||||
ambmem[c][i] = inmem[i*static_cast<uint>(ininfo.channels)] * scale;
|
||||
++inmem;
|
||||
ambmem[c][i] = inmem[i*static_cast<uint>(ininfo.channels) + c] * scale;
|
||||
}
|
||||
}
|
||||
else for(const int chanid : chanmap)
|
||||
else for(size_t idx{0};idx < chanmap.size();++idx)
|
||||
{
|
||||
const int chanid{chanmap[idx]};
|
||||
/* Skip LFE. Or mix directly into W? Or W+X? */
|
||||
if(chanid == SF_CHANNEL_MAP_LFE)
|
||||
{
|
||||
++inmem;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto spkr = std::find_if(spkrs.cbegin(), spkrs.cend(),
|
||||
[chanid](const SpeakerPos &pos){return pos.mChannelID == chanid;});
|
||||
[chanid](const SpeakerPos pos){return pos.mChannelID == chanid;});
|
||||
if(spkr == spkrs.cend())
|
||||
{
|
||||
fprintf(stderr, " ... failed to find channel ID %d\n", chanid);
|
||||
|
|
@ -477,8 +490,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
for(size_t i{0};i < got;++i)
|
||||
srcmem[i] = inmem[i * static_cast<uint>(ininfo.channels)];
|
||||
++inmem;
|
||||
srcmem[i] = inmem[i*static_cast<uint>(ininfo.channels) + idx];
|
||||
|
||||
static constexpr auto Deg2Rad = al::numbers::pi / 180.0;
|
||||
const auto coeffs = GenCoeffs(
|
||||
|
|
@ -502,11 +514,9 @@ int main(int argc, char **argv)
|
|||
got -= LeadIn;
|
||||
for(size_t c{0};c < uhjchans;++c)
|
||||
{
|
||||
constexpr float max_val{8388607.0f / 8388608.0f};
|
||||
auto clamp = [](float v, float mn, float mx) noexcept
|
||||
{ return std::min(std::max(v, mn), mx); };
|
||||
static constexpr float max_val{8388607.0f / 8388608.0f};
|
||||
for(size_t i{0};i < got;++i)
|
||||
outmem[i*uhjchans + c] = clamp(encmem[c][LeadIn+i], -1.0f, max_val);
|
||||
outmem[i*uhjchans + c] = std::clamp(encmem[c][LeadIn+i], -1.0f, max_val);
|
||||
}
|
||||
LeadIn = 0;
|
||||
|
||||
|
|
@ -529,3 +539,13 @@ int main(int argc, char **argv)
|
|||
(num_encoded == 1) ? "" : "s");
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
assert(argc >= 0);
|
||||
auto args = std::vector<std::string_view>(static_cast<unsigned int>(argc));
|
||||
std::copy_n(argv, args.size(), args.begin());
|
||||
return main(al::span{args});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue