Add binaural rendering support.
Native builds / linux-x64 (push) Failing after 11s
Native builds / macos-arm64 (push) Has been cancelled
Native builds / macos-x64 (push) Has been cancelled
Native builds / windows-x64 (push) Has been cancelled
Native builds / Publish GitHub Release (push) Has been cancelled

This commit is contained in:
2026-09-06 19:14:24 +08:00
parent 329445ed25
commit 85105f21d4
38 changed files with 9660 additions and 214 deletions
+664
View File
@@ -0,0 +1,664 @@
#define EJOC_BUILD_DLL
#include "eac3joc_core.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <new>
#include <vector>
namespace ejoc::binaural {
struct Complex {
double re;
double im;
};
inline Complex add(Complex a, Complex b) noexcept {
return {a.re + b.re, a.im + b.im};
}
inline Complex mul(Complex a, Complex b) noexcept {
return {a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re};
}
inline Complex scale(Complex value, double gain) noexcept {
return {value.re * gain, value.im * gain};
}
constexpr double kPi = 3.141592653589793238462643383279502884;
constexpr int kChannels = EJOC_BINAURAL_INPUT_CHANNELS;
constexpr int kEars = EJOC_BINAURAL_OUTPUT_CHANNELS;
constexpr int kBlock = EJOC_BINAURAL_BLOCK_SAMPLES;
constexpr int kSlots = kBlock / 64;
constexpr int kQmf = EJOC_BINAURAL_QMF_BANDS;
constexpr int kHybrid = EJOC_BINAURAL_HYBRID_BANDS;
constexpr int kRank = 4;
class Renderer final {
public:
Renderer() noexcept {
initialize_fft();
reset();
}
int configure_kernels(
const double* qmf_analysis,
const double* hybrid_low,
const int16_t* hybrid_indices,
const double* hybrid_values,
uint32_t hybrid_count,
const double* qmf_basis,
const double* qmf_taps) noexcept {
if (!qmf_analysis || !hybrid_low || !hybrid_indices || !hybrid_values ||
!qmf_basis || !qmf_taps || hybrid_count == 0) {
return fail("invalid binaural kernel configuration");
}
std::memcpy(qmf_analysis_.data(), qmf_analysis,
qmf_analysis_.size() * sizeof(double));
hybrid_low_.assign(hybrid_low, hybrid_low + 3 * 2 * 13 * 16 * 2);
hybrid_indices_.assign(hybrid_indices, hybrid_indices + hybrid_count * 4);
hybrid_values_.assign(hybrid_values, hybrid_values + hybrid_count);
std::memcpy(qmf_basis_.data(), qmf_basis,
qmf_basis_.size() * sizeof(double));
std::memcpy(qmf_taps_.data(), qmf_taps,
qmf_taps_.size() * sizeof(double));
kernels_ready_ = true;
reset();
return 0;
}
int configure_room(
uint32_t bands,
uint32_t allpass_count,
const uint32_t* allpass_delays,
const double* allpass_gains,
const uint32_t* fdn_delays,
const double* fdn_matrix,
uint32_t output_tap_delay,
const double* feedback_complex,
const double* output_taps,
const double* output_complex,
uint32_t extra_count,
const uint32_t* extra_delays,
const double* extra_fields_complex,
const double* extra_matrices) noexcept {
if (bands != 64 || !fdn_delays || !fdn_matrix || !feedback_complex ||
!output_taps || !output_complex ||
(allpass_count && (!allpass_delays || !allpass_gains)) ||
(extra_count && (!extra_delays || !extra_fields_complex || !extra_matrices))) {
return fail("invalid binaural room configuration");
}
room_bands_ = bands;
if (allpass_count) {
allpass_delays_.assign(allpass_delays, allpass_delays + allpass_count);
allpass_gains_.assign(allpass_gains, allpass_gains + allpass_count);
} else {
allpass_delays_.clear();
allpass_gains_.clear();
}
allpass_offsets_.resize(allpass_count);
allpass_positions_.assign(allpass_count, 0);
size_t allpass_size = 0;
for (uint32_t index = 0; index < allpass_count; ++index) {
if (allpass_delays_[index] == 0) {
return fail("binaural allpass delay must be positive");
}
allpass_offsets_[index] = allpass_size;
allpass_size += static_cast<size_t>(allpass_delays_[index]) * bands;
}
allpass_memory_.assign(allpass_size, {});
room_capacity_ = 0;
for (int branch = 0; branch < 4; ++branch) {
fdn_delays_[branch] = fdn_delays[branch];
room_capacity_ = std::max(room_capacity_, fdn_delays_[branch]);
}
if (room_capacity_ == 0) {
return fail("binaural room delay must be positive");
}
std::copy(fdn_matrix, fdn_matrix + 16, fdn_matrix_.begin());
output_tap_delay_ = output_tap_delay;
for (int band = 0; band < 64; ++band) {
for (int branch = 0; branch < 4; ++branch) {
const size_t complex_index = (static_cast<size_t>(band) * 4 + branch) * 2;
feedback_[band][branch] = {
feedback_complex[complex_index], feedback_complex[complex_index + 1]};
output_taps_[band][branch] = output_taps[band * 4 + branch];
for (int ear = 0; ear < 2; ++ear) {
const size_t output_index =
((static_cast<size_t>(ear) * 64 + band) * 4 + branch) * 2;
output_matrix_[ear][band][branch] = {
output_complex[output_index], output_complex[output_index + 1]};
}
}
}
room_memory_.assign(static_cast<size_t>(room_capacity_) * 64 * 4, {});
if (extra_count) {
extra_delays_.assign(extra_delays, extra_delays + extra_count);
} else {
extra_delays_.clear();
}
extra_fields_.resize(static_cast<size_t>(extra_count) * 64);
extra_matrices_.resize(static_cast<size_t>(extra_count) * 16);
for (uint32_t extra = 0; extra < extra_count; ++extra) {
for (int band = 0; band < 64; ++band) {
const size_t source = (static_cast<size_t>(extra) * 64 + band) * 2;
extra_fields_[static_cast<size_t>(extra) * 64 + band] = {
extra_fields_complex[source], extra_fields_complex[source + 1]};
}
std::copy(extra_matrices + static_cast<size_t>(extra) * 16,
extra_matrices + static_cast<size_t>(extra + 1) * 16,
extra_matrices_.begin() + static_cast<size_t>(extra) * 16);
}
room_ready_ = true;
reset();
return 0;
}
int reset() noexcept {
qmf_history_.fill(0.0);
hybrid_low_history_.fill({});
hybrid_high_history_.fill({});
synthesis_history_.fill(0.0);
std::fill(allpass_memory_.begin(), allpass_memory_.end(), Complex{});
std::fill(allpass_positions_.begin(), allpass_positions_.end(), 0u);
std::fill(room_memory_.begin(), room_memory_.end(), Complex{});
room_position_ = 0;
error_[0] = '\0';
return 0;
}
const char* error() const noexcept {
return error_[0] ? error_ : "";
}
int process(
const double* input,
const double* gains,
const double* room_sends,
double output_gain,
double* output) noexcept {
if (!kernels_ready_ || !room_ready_) {
return fail("binaural renderer is not configured");
}
if (!input || !gains || !room_sends || !output || !std::isfinite(output_gain)) {
return fail("invalid binaural process arguments");
}
for (int slot = 0; slot < kSlots; ++slot) {
std::array<Complex, kChannels * kQmf> qmf{};
std::array<Complex, kChannels * kHybrid> hybrid{};
analyze_qmf(input + static_cast<size_t>(slot) * 64 * kChannels, qmf);
analyze_hybrid(qmf, hybrid);
std::array<Complex, kEars * kHybrid> rendered{};
std::array<Complex, kHybrid> room_input{};
for (int source = kChannels - 1; source >= 0; --source) {
for (int band = 0; band < kHybrid; ++band) {
const Complex value = hybrid[source * kHybrid + band];
room_input[band] = add(room_input[band], scale(value, room_sends[source]));
for (int ear = 0; ear < kEars; ++ear) {
const size_t gain_index =
(((static_cast<size_t>(source) * kEars + ear) * kHybrid + band) * 2);
const Complex gain{gains[gain_index], gains[gain_index + 1]};
rendered[ear * kHybrid + band] = add(
rendered[ear * kHybrid + band], mul(value, gain));
}
}
}
const auto room = process_room(room_input);
for (size_t index = 0; index < rendered.size(); ++index) {
rendered[index] = add(rendered[index], room[index]);
}
std::array<Complex, kEars * kQmf> qmf_output{};
synthesize_hybrid(rendered, qmf_output);
for (int ear = 0; ear < kEars; ++ear) {
std::array<double, 64> samples{};
synthesize_qmf(qmf_output.data() + ear * kQmf, ear, samples);
for (int sample = 0; sample < 64; ++sample) {
output[(static_cast<size_t>(slot) * 64 + sample) * 2 + ear] =
samples[sample] * output_gain;
}
}
}
return 0;
}
private:
int fail(const char* message) noexcept {
std::snprintf(error_, sizeof(error_), "%s", message);
return -1;
}
void initialize_fft() noexcept {
for (int index = 0; index < 128; ++index) {
int value = index;
int reversed = 0;
for (int bit = 0; bit < 7; ++bit) {
reversed = (reversed << 1) | (value & 1);
value >>= 1;
}
bit_reverse_[index] = static_cast<uint8_t>(reversed);
}
for (int phase = 0; phase < 64; ++phase) {
const double angle = -kPi * static_cast<double>(phase) / 128.0;
premod_[phase] = {std::cos(angle), std::sin(angle)};
const double post_angle =
-3.0 * (static_cast<double>(phase) + 0.5) * kPi / 128.0;
post_[phase] = {std::cos(post_angle), std::sin(post_angle)};
even_post_[phase] = {0.0, (phase & 1) ? -1.0 : 1.0};
}
}
void fft128(std::array<Complex, 128>& values) const noexcept {
for (int index = 0; index < 128; ++index) {
const int reversed = bit_reverse_[index];
if (reversed > index) {
std::swap(values[index], values[reversed]);
}
}
for (int length = 2; length <= 128; length <<= 1) {
const double angle = -2.0 * kPi / static_cast<double>(length);
const Complex step{std::cos(angle), std::sin(angle)};
for (int start = 0; start < 128; start += length) {
Complex rotation{1.0, 0.0};
for (int offset = 0; offset < length / 2; ++offset) {
const Complex even = values[start + offset];
const Complex odd = mul(values[start + offset + length / 2], rotation);
values[start + offset] = {even.re + odd.re, even.im + odd.im};
values[start + offset + length / 2] = {
even.re - odd.re, even.im - odd.im};
rotation = mul(rotation, step);
}
}
}
}
void qmf_transform(const std::array<double, 64>& source,
std::array<Complex, 64>& target) const noexcept {
std::array<Complex, 128> work{};
for (int phase = 0; phase < 64; ++phase) {
work[phase] = scale(premod_[phase], source[phase]);
}
fft128(work);
for (int band = 0; band < 64; ++band) {
target[band] = mul(work[band], post_[band]);
}
}
void analyze_qmf(const double* input,
std::array<Complex, kChannels * kQmf>& output) noexcept {
for (int channel = 0; channel < kChannels; ++channel) {
for (int lag = 9; lag > 0; --lag) {
for (int phase = 0; phase < 64; ++phase) {
qmf_history_[qmf_history_index(lag, channel, phase)] =
qmf_history_[qmf_history_index(lag - 1, channel, phase)];
}
}
for (int phase = 0; phase < 64; ++phase) {
qmf_history_[qmf_history_index(0, channel, phase)] =
input[phase * kChannels + channel];
}
std::array<double, 64> even{};
std::array<double, 64> odd{};
for (int phase = 0; phase < 64; ++phase) {
for (int lag = 0; lag < 10; ++lag) {
const double value =
qmf_history_[qmf_history_index(lag, channel, phase)] *
qmf_analysis_[phase * 10 + lag];
(lag & 1 ? odd[phase] : even[phase]) += value;
}
}
std::array<Complex, 64> even_fft{};
std::array<Complex, 64> odd_fft{};
qmf_transform(even, even_fft);
qmf_transform(odd, odd_fft);
for (int band = 0; band < 64; ++band) {
output[channel * 64 + band] = add(
odd_fft[band], mul(even_fft[band], even_post_[band]));
}
}
}
void analyze_hybrid(
const std::array<Complex, kChannels * kQmf>& qmf,
std::array<Complex, kChannels * kHybrid>& output) noexcept {
for (int channel = 0; channel < kChannels; ++channel) {
for (int lag = 12; lag > 0; --lag) {
for (int band = 0; band < 3; ++band) {
hybrid_low_history_[hybrid_low_history_index(lag, channel, band)] =
hybrid_low_history_[hybrid_low_history_index(lag - 1, channel, band)];
}
}
for (int band = 0; band < 3; ++band) {
hybrid_low_history_[hybrid_low_history_index(0, channel, band)] =
qmf[channel * 64 + band];
}
for (int output_band = 0; output_band < 16; ++output_band) {
Complex value{};
for (int lag = 0; lag < 13; ++lag) {
for (int input_band = 0; input_band < 3; ++input_band) {
const Complex source = hybrid_low_history_[
hybrid_low_history_index(lag, channel, input_band)];
const double components[2]{source.re, source.im};
for (int input_component = 0; input_component < 2; ++input_component) {
value.re += components[input_component] * hybrid_low_[
hybrid_low_kernel_index(input_band, input_component, lag,
output_band, 0)];
value.im += components[input_component] * hybrid_low_[
hybrid_low_kernel_index(input_band, input_component, lag,
output_band, 1)];
}
}
}
output[channel * kHybrid + output_band] = value;
}
for (int band = 0; band < 61; ++band) {
output[channel * kHybrid + 16 + band] =
hybrid_high_history_[hybrid_high_history_index(0, channel, band)];
for (int delay = 0; delay < 5; ++delay) {
hybrid_high_history_[hybrid_high_history_index(delay, channel, band)] =
hybrid_high_history_[hybrid_high_history_index(delay + 1, channel, band)];
}
hybrid_high_history_[hybrid_high_history_index(5, channel, band)] =
qmf[channel * 64 + 3 + band];
}
}
}
std::array<Complex, kEars * kHybrid> process_room(
const std::array<Complex, kHybrid>& input) noexcept {
std::array<Complex, 64> filtered{};
for (int band = 0; band < 64; ++band) {
filtered[band] = scale(input[band], 0.70710677);
}
for (size_t stage = 0; stage < allpass_delays_.size(); ++stage) {
const uint32_t position = allpass_positions_[stage];
const double gain = allpass_gains_[stage];
for (int band = 0; band < 64; ++band) {
Complex& memory = allpass_memory_[
allpass_offsets_[stage] + static_cast<size_t>(position) * 64 + band];
const Complex residual = add(filtered[band], scale(memory, -gain));
filtered[band] = add(scale(residual, gain), memory);
memory = residual;
}
allpass_positions_[stage] = (position + 1) % allpass_delays_[stage];
}
std::array<Complex, 64 * 4> branches{};
std::array<Complex, 64 * 4> taps{};
for (int band = 0; band < 64; ++band) {
for (int branch = 0; branch < 4; ++branch) {
Complex value = filtered[band];
for (int source = 0; source < 4; ++source) {
const uint32_t position =
(room_position_ + room_capacity_ - fdn_delays_[source]) % room_capacity_;
value = add(value, scale(room_memory_[
room_memory_index(position, band, source)],
fdn_matrix_[branch * 4 + source]));
}
branches[band * 4 + branch] = value;
const uint32_t tap_position =
(room_position_ + room_capacity_ -
(output_tap_delay_ % room_capacity_)) % room_capacity_;
taps[band * 4 + branch] =
room_memory_[room_memory_index(tap_position, band, branch)];
}
}
for (int band = 0; band < 64; ++band) {
for (int branch = 0; branch < 4; ++branch) {
room_memory_[room_memory_index(room_position_, band, branch)] =
mul(branches[band * 4 + branch], feedback_[band][branch]);
}
}
room_position_ = (room_position_ + 1) % room_capacity_;
std::array<Complex, 64 * 4> extra{};
for (size_t index = 0; index < extra_delays_.size(); ++index) {
const uint32_t position =
(room_position_ + room_capacity_ -
((extra_delays_[index] + 1) % room_capacity_)) % room_capacity_;
for (int band = 0; band < 64; ++band) {
for (int target = 0; target < 4; ++target) {
Complex mixed{};
for (int source = 0; source < 4; ++source) {
mixed = add(mixed, scale(room_memory_[
room_memory_index(position, band, source)],
extra_matrices_[index * 16 + target * 4 + source]));
}
extra[band * 4 + target] = add(
extra[band * 4 + target],
mul(mixed, extra_fields_[index * 64 + band]));
}
}
}
std::array<Complex, kEars * kHybrid> output{};
for (int ear = 0; ear < 2; ++ear) {
for (int band = 0; band < 64; ++band) {
Complex value{};
for (int branch = 0; branch < 4; ++branch) {
const Complex signal = add(
scale(taps[band * 4 + branch], output_taps_[band][branch]),
extra[band * 4 + branch]);
value = add(value, mul(
signal, output_matrix_[ear][band][branch]));
}
output[ear * kHybrid + band] = value;
}
}
return output;
}
void synthesize_hybrid(
const std::array<Complex, kEars * kHybrid>& input,
std::array<Complex, kEars * kQmf>& output) const noexcept {
for (size_t mapping = 0; mapping < hybrid_values_.size(); ++mapping) {
const int16_t* index = hybrid_indices_.data() + mapping * 4;
const int input_band = index[0];
const int input_component = index[1];
const int output_band = index[2];
const int output_component = index[3];
const double gain = hybrid_values_[mapping];
for (int ear = 0; ear < 2; ++ear) {
const Complex source = input[ear * kHybrid + input_band];
Complex& target = output[ear * kQmf + output_band];
const double component = input_component == 0 ? source.re : source.im;
(output_component == 0 ? target.re : target.im) += component * gain;
}
}
}
void synthesize_qmf(const Complex* input, int ear,
std::array<double, 64>& output) noexcept {
std::array<double, 64 * kRank> features{};
std::array<double, 128> flat{};
for (int band = 0; band < 64; ++band) {
flat[band * 2] = input[band].re;
flat[band * 2 + 1] = input[band].im;
}
for (int phase = 0; phase < 64; ++phase) {
for (int rank = 0; rank < kRank; ++rank) {
double value = 0.0;
const size_t base = (static_cast<size_t>(phase) * kRank + rank) * 128;
for (int component = 0; component < 128; ++component) {
value += flat[component] * qmf_basis_[base + component];
}
features[phase * kRank + rank] = value;
}
}
for (int phase = 0; phase < 64; ++phase) {
double value = 0.0;
for (int lag = 0; lag < 10; ++lag) {
for (int rank = 0; rank < kRank; ++rank) {
const double feature = lag == 0
? features[phase * kRank + rank]
: synthesis_history_[synthesis_history_index(
ear, lag - 1, phase, rank)];
value += feature * qmf_taps_[
((static_cast<size_t>(phase) * 10 + lag) * kRank + rank)];
}
}
output[phase] = value;
}
for (int lag = 8; lag > 0; --lag) {
for (int phase = 0; phase < 64; ++phase) {
for (int rank = 0; rank < kRank; ++rank) {
synthesis_history_[synthesis_history_index(ear, lag, phase, rank)] =
synthesis_history_[synthesis_history_index(
ear, lag - 1, phase, rank)];
}
}
}
for (int phase = 0; phase < 64; ++phase) {
for (int rank = 0; rank < kRank; ++rank) {
synthesis_history_[synthesis_history_index(ear, 0, phase, rank)] =
features[phase * kRank + rank];
}
}
}
static size_t qmf_history_index(int lag, int channel, int phase) noexcept {
return (static_cast<size_t>(lag) * kChannels + channel) * 64 + phase;
}
static size_t hybrid_low_history_index(int lag, int channel, int band) noexcept {
return (static_cast<size_t>(lag) * kChannels + channel) * 3 + band;
}
static size_t hybrid_high_history_index(int delay, int channel, int band) noexcept {
return (static_cast<size_t>(delay) * kChannels + channel) * 61 + band;
}
static size_t hybrid_low_kernel_index(
int input_band, int input_component, int lag,
int output_band, int output_component) noexcept {
return (((static_cast<size_t>(input_band) * 2 + input_component) * 13 + lag) *
16 + output_band) * 2 + output_component;
}
size_t room_memory_index(uint32_t position, int band, int branch) const noexcept {
return (static_cast<size_t>(position) * 64 + band) * 4 + branch;
}
static size_t synthesis_history_index(
int ear, int lag, int phase, int rank) noexcept {
return (((static_cast<size_t>(ear) * 9 + lag) * 64 + phase) * kRank + rank);
}
bool kernels_ready_ = false;
bool room_ready_ = false;
std::array<double, 64 * 10> qmf_analysis_{};
std::vector<double> hybrid_low_;
std::vector<int16_t> hybrid_indices_;
std::vector<double> hybrid_values_;
std::array<double, 64 * kRank * 128> qmf_basis_{};
std::array<double, 64 * 10 * kRank> qmf_taps_{};
std::array<double, 10 * kChannels * 64> qmf_history_{};
std::array<Complex, 13 * kChannels * 3> hybrid_low_history_{};
std::array<Complex, 6 * kChannels * 61> hybrid_high_history_{};
std::array<double, kEars * 9 * 64 * kRank> synthesis_history_{};
uint32_t room_bands_ = 0;
std::vector<uint32_t> allpass_delays_;
std::vector<double> allpass_gains_;
std::vector<size_t> allpass_offsets_;
std::vector<uint32_t> allpass_positions_;
std::vector<Complex> allpass_memory_;
std::array<uint32_t, 4> fdn_delays_{};
std::array<double, 16> fdn_matrix_{};
uint32_t room_capacity_ = 0;
uint32_t output_tap_delay_ = 0;
std::array<std::array<Complex, 4>, 64> feedback_{};
std::array<std::array<double, 4>, 64> output_taps_{};
std::array<std::array<std::array<Complex, 4>, 64>, 2> output_matrix_{};
std::vector<Complex> room_memory_;
uint32_t room_position_ = 0;
std::vector<uint32_t> extra_delays_;
std::vector<Complex> extra_fields_;
std::vector<double> extra_matrices_;
std::array<uint8_t, 128> bit_reverse_{};
std::array<Complex, 64> premod_{};
std::array<Complex, 64> post_{};
std::array<Complex, 64> even_post_{};
char error_[256]{};
};
} // namespace ejoc::binaural
extern "C" {
ejoc_binaural_renderer_handle EJOC_CALL ejoc_binaural_renderer_create(void) {
return new (std::nothrow) ejoc::binaural::Renderer();
}
void EJOC_CALL ejoc_binaural_renderer_destroy(ejoc_binaural_renderer_handle handle) {
delete static_cast<ejoc::binaural::Renderer*>(handle);
}
int EJOC_CALL ejoc_binaural_renderer_reset(ejoc_binaural_renderer_handle handle) {
return handle ? static_cast<ejoc::binaural::Renderer*>(handle)->reset() : -1;
}
const char* EJOC_CALL ejoc_binaural_renderer_last_error(
ejoc_binaural_renderer_handle handle) {
return handle ? static_cast<ejoc::binaural::Renderer*>(handle)->error()
: "null binaural renderer handle";
}
int EJOC_CALL ejoc_binaural_renderer_configure_kernels(
ejoc_binaural_renderer_handle handle,
const double* qmf_analysis,
const double* hybrid_low,
const int16_t* hybrid_indices,
const double* hybrid_values,
uint32_t hybrid_count,
const double* qmf_basis,
const double* qmf_taps) {
return handle ? static_cast<ejoc::binaural::Renderer*>(handle)->configure_kernels(
qmf_analysis, hybrid_low, hybrid_indices, hybrid_values,
hybrid_count, qmf_basis, qmf_taps) : -1;
}
int EJOC_CALL ejoc_binaural_renderer_configure_room(
ejoc_binaural_renderer_handle handle,
uint32_t bands,
uint32_t allpass_count,
const uint32_t* allpass_delays,
const double* allpass_gains,
const uint32_t* fdn_delays,
const double* fdn_matrix,
uint32_t output_tap_delay,
const double* feedback_complex,
const double* output_taps,
const double* output_complex,
uint32_t extra_count,
const uint32_t* extra_delays,
const double* extra_fields_complex,
const double* extra_matrices) {
return handle ? static_cast<ejoc::binaural::Renderer*>(handle)->configure_room(
bands, allpass_count, allpass_delays, allpass_gains,
fdn_delays, fdn_matrix, output_tap_delay,
feedback_complex, output_taps, output_complex,
extra_count, extra_delays, extra_fields_complex, extra_matrices) : -1;
}
int EJOC_CALL ejoc_binaural_renderer_process(
ejoc_binaural_renderer_handle handle,
const double* input16_interleaved,
const double* gains_complex,
const double* room_sends,
double output_gain,
double* output_stereo_interleaved) {
return handle ? static_cast<ejoc::binaural::Renderer*>(handle)->process(
input16_interleaved, gains_complex, room_sends,
output_gain, output_stereo_interleaved) : -1;
}
} // extern "C"
+4 -4
View File
@@ -664,13 +664,13 @@ uint32_t EJOC_CALL ejoc_abi_version(void) {
const char* EJOC_CALL ejoc_build_info(void) {
#if defined(_MSC_VER)
return "eac3joc-core abi=1 compiler=MSVC fft=fixed64 speaker=double crt=static-by-build";
return "eac3joc-core abi=1 compiler=MSVC fft=fixed64 speaker=double binaural=double crt=static-by-build";
#elif defined(__clang__)
return "eac3joc-core abi=1 compiler=Clang fft=fixed64 speaker=double";
return "eac3joc-core abi=1 compiler=Clang fft=fixed64 speaker=double binaural=double";
#elif defined(__GNUC__)
return "eac3joc-core abi=1 compiler=GCC fft=fixed64 speaker=double";
return "eac3joc-core abi=1 compiler=GCC fft=fixed64 speaker=double binaural=double";
#else
return "eac3joc-core abi=1 compiler=unknown fft=fixed64 speaker=double";
return "eac3joc-core abi=1 compiler=unknown fft=fixed64 speaker=double binaural=double";
#endif
}
File diff suppressed because it is too large Load Diff