mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
mcpx: Support 3D voice HRTF filtering
This commit is contained in:
committed by
mborgerson
parent
05b1f2c13d
commit
344f7da132
@@ -215,6 +215,9 @@ display:
|
||||
|
||||
audio:
|
||||
use_dsp: bool
|
||||
hrtf:
|
||||
type: bool
|
||||
default: true
|
||||
volume_limit:
|
||||
type: number
|
||||
default: 1
|
||||
|
||||
+82
-2
@@ -45,6 +45,7 @@
|
||||
#include "adpcm.h"
|
||||
#include "svf.h"
|
||||
#include "fpconv.h"
|
||||
#include "hrtf.h"
|
||||
|
||||
#define GET_MASK(v, mask) (((v) & (mask)) >> ctz32(mask))
|
||||
|
||||
@@ -85,6 +86,7 @@ typedef struct MCPXAPUVoiceFilter {
|
||||
float resample_buf[NUM_SAMPLES_PER_FRAME * 2];
|
||||
SRC_STATE *resampler;
|
||||
sv_filter svf[2];
|
||||
HrtfFilter hrtf;
|
||||
} MCPXAPUVoiceFilter;
|
||||
|
||||
typedef struct MCPXAPUState {
|
||||
@@ -123,6 +125,15 @@ typedef struct MCPXAPUState {
|
||||
float sample_buf[NUM_SAMPLES_PER_FRAME][2];
|
||||
uint64_t voice_locked[4];
|
||||
QemuSpin voice_spinlocks[MCPX_HW_MAX_VOICES];
|
||||
|
||||
struct {
|
||||
int current_entry;
|
||||
// FIXME: Stored in RAM
|
||||
struct {
|
||||
float hrir[2][HRTF_NUM_TAPS];
|
||||
float itd;
|
||||
} entries[HRTF_ENTRY_COUNT];
|
||||
} hrtf;
|
||||
} vp;
|
||||
|
||||
/* Global Processor */
|
||||
@@ -496,6 +507,13 @@ static bool is_voice_locked(MCPXAPUState *d, uint16_t v)
|
||||
return (qatomic_read(&d->vp.voice_locked[v / 64]) & mask) != 0;
|
||||
}
|
||||
|
||||
static void set_hrir_coeff_tar(MCPXAPUState *d, int channel, int coeff_idx,
|
||||
int8_t value)
|
||||
{
|
||||
int entry = d->vp.hrtf.current_entry;
|
||||
d->vp.hrtf.entries[entry].hrir[channel][coeff_idx] = int8_to_float(value);
|
||||
}
|
||||
|
||||
static void fe_method(MCPXAPUState *d, uint32_t method, uint32_t argument)
|
||||
{
|
||||
unsigned int slot;
|
||||
@@ -648,6 +666,11 @@ static void fe_method(MCPXAPUState *d, uint32_t method, uint32_t argument)
|
||||
NV_PAVS_VOICE_PAR_STATE, NV_PAVS_VOICE_PAR_STATE_PAUSED,
|
||||
(argument & NV1BA0_PIO_VOICE_PAUSE_ACTION) != 0);
|
||||
break;
|
||||
case NV1BA0_PIO_SET_CURRENT_HRTF_ENTRY: {
|
||||
int handle = GET_MASK(argument, NV1BA0_PIO_SET_CURRENT_HRTF_ENTRY_HANDLE);
|
||||
d->vp.hrtf.current_entry = handle;
|
||||
break;
|
||||
}
|
||||
case NV1BA0_PIO_SET_CURRENT_VOICE:
|
||||
d->regs[NV_PAPU_FECV] = argument;
|
||||
break;
|
||||
@@ -679,6 +702,23 @@ static void fe_method(MCPXAPUState *d, uint32_t method, uint32_t argument)
|
||||
voice_set_mask(d, d->regs[NV_PAPU_FECV], NV_PAVS_VOICE_CFG_MISC,
|
||||
0xFFFFFFFF, argument);
|
||||
break;
|
||||
case NV1BA0_PIO_SET_VOICE_TAR_HRTF: {
|
||||
int handle = GET_MASK(argument, NV1BA0_PIO_SET_VOICE_TAR_HRTF_HANDLE);
|
||||
int current_voice = d->regs[NV_PAPU_FECV];
|
||||
voice_set_mask(d, current_voice, NV_PAVS_VOICE_CFG_HRTF_TARGET,
|
||||
NV_PAVS_VOICE_CFG_HRTF_TARGET_HANDLE, handle);
|
||||
if (current_voice < MCPX_HW_MAX_3D_VOICES &&
|
||||
handle != HRTF_NULL_HANDLE) {
|
||||
// FIXME: Xbox software seems to reliably set voice HRTF handles
|
||||
// after updating filter parameters, however it may be possible to
|
||||
// update parameter targets for an active voice.
|
||||
assert(handle < HRTF_ENTRY_COUNT);
|
||||
hrtf_filter_set_target_params(&d->vp.filters[current_voice].hrtf,
|
||||
d->vp.hrtf.entries[handle].hrir,
|
||||
d->vp.hrtf.entries[handle].itd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NV1BA0_PIO_SET_VOICE_TAR_VOLA:
|
||||
voice_set_mask(d, d->regs[NV_PAPU_FECV], NV_PAVS_VOICE_TAR_VOLA,
|
||||
0xFFFFFFFF, argument);
|
||||
@@ -724,6 +764,31 @@ static void fe_method(MCPXAPUState *d, uint32_t method, uint32_t argument)
|
||||
voice_set_mask(d, d->regs[NV_PAPU_FECV], NV_PAVS_VOICE_PAR_NEXT,
|
||||
NV_PAVS_VOICE_PAR_NEXT_EBO, argument);
|
||||
break;
|
||||
case NV1BA0_PIO_SET_HRIR ... NV1BA0_PIO_SET_HRIR_X - 1: {
|
||||
assert(d->vp.hrtf.current_entry < HRTF_ENTRY_COUNT);
|
||||
slot = (method - NV1BA0_PIO_SET_HRIR) / 4;
|
||||
int8_t left0 = GET_MASK(argument, NV1BA0_PIO_SET_HRIR_LEFT0);
|
||||
int8_t right0 = GET_MASK(argument, NV1BA0_PIO_SET_HRIR_RIGHT0);
|
||||
int8_t left1 = GET_MASK(argument, NV1BA0_PIO_SET_HRIR_LEFT1);
|
||||
int8_t right1 = GET_MASK(argument, NV1BA0_PIO_SET_HRIR_RIGHT1);
|
||||
int coeff_idx = slot * 2;
|
||||
set_hrir_coeff_tar(d, 0, coeff_idx, left0);
|
||||
set_hrir_coeff_tar(d, 1, coeff_idx, right0);
|
||||
coeff_idx += 1;
|
||||
set_hrir_coeff_tar(d, 0, coeff_idx, left1);
|
||||
set_hrir_coeff_tar(d, 1, coeff_idx, right1);
|
||||
break;
|
||||
}
|
||||
case NV1BA0_PIO_SET_HRIR_X: {
|
||||
assert(d->vp.hrtf.current_entry < HRTF_ENTRY_COUNT);
|
||||
int8_t left30 = GET_MASK(argument, NV1BA0_PIO_SET_HRIR_X_LEFT30);
|
||||
int8_t right30 = GET_MASK(argument, NV1BA0_PIO_SET_HRIR_X_RIGHT30);
|
||||
int16_t itd = GET_MASK(argument, NV1BA0_PIO_SET_HRIR_X_ITD);
|
||||
set_hrir_coeff_tar(d, 0, 30, left30);
|
||||
set_hrir_coeff_tar(d, 1, 30, right30);
|
||||
d->vp.hrtf.entries[d->vp.hrtf.current_entry].itd = s6p9_to_float(itd);
|
||||
break;
|
||||
}
|
||||
case NV1BA0_PIO_SET_CURRENT_INBUF_SGE:
|
||||
d->inbuf_sge_handle = argument & NV1BA0_PIO_SET_CURRENT_INBUF_SGE_HANDLE;
|
||||
break;
|
||||
@@ -893,6 +958,7 @@ static void vp_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
|
||||
case NV1BA0_PIO_VOICE_RELEASE:
|
||||
case NV1BA0_PIO_VOICE_OFF:
|
||||
case NV1BA0_PIO_VOICE_PAUSE:
|
||||
case NV1BA0_PIO_SET_CURRENT_HRTF_ENTRY:
|
||||
case NV1BA0_PIO_SET_CURRENT_VOICE:
|
||||
case NV1BA0_PIO_SET_VOICE_CFG_VBIN:
|
||||
case NV1BA0_PIO_SET_VOICE_CFG_FMT:
|
||||
@@ -901,6 +967,7 @@ static void vp_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
|
||||
case NV1BA0_PIO_SET_VOICE_CFG_ENV1:
|
||||
case NV1BA0_PIO_SET_VOICE_CFG_ENVF:
|
||||
case NV1BA0_PIO_SET_VOICE_CFG_MISC:
|
||||
case NV1BA0_PIO_SET_VOICE_TAR_HRTF:
|
||||
case NV1BA0_PIO_SET_VOICE_TAR_VOLA:
|
||||
case NV1BA0_PIO_SET_VOICE_TAR_VOLB:
|
||||
case NV1BA0_PIO_SET_VOICE_TAR_VOLC:
|
||||
@@ -912,6 +979,8 @@ static void vp_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
|
||||
case NV1BA0_PIO_SET_VOICE_CFG_BUF_LBO:
|
||||
case NV1BA0_PIO_SET_VOICE_BUF_CBO:
|
||||
case NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO:
|
||||
case NV1BA0_PIO_SET_HRIR ... NV1BA0_PIO_SET_HRIR_X - 1:
|
||||
case NV1BA0_PIO_SET_HRIR_X:
|
||||
case NV1BA0_PIO_SET_CURRENT_INBUF_SGE:
|
||||
case NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET:
|
||||
CASE_4(NV1BA0_PIO_SET_OUTBUF_BA, 8): // 8 byte pitch, 4 entries
|
||||
@@ -1873,6 +1942,15 @@ static void voice_process(MCPXAPUState *d,
|
||||
}
|
||||
}
|
||||
|
||||
if (v < MCPX_HW_MAX_3D_VOICES && g_config.audio.hrtf) {
|
||||
uint16_t hrtf_handle =
|
||||
voice_get_mask(d, v, NV_PAVS_VOICE_CFG_HRTF_TARGET,
|
||||
NV_PAVS_VOICE_CFG_HRTF_TARGET_HANDLE);
|
||||
if (hrtf_handle != HRTF_NULL_HANDLE) {
|
||||
hrtf_filter_process(&d->vp.filters[v].hrtf, samples, samples);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: ParaEQ
|
||||
|
||||
for (int b = 0; b < 8; b++) {
|
||||
@@ -1880,8 +1958,7 @@ static void voice_process(MCPXAPUState *d,
|
||||
float hr;
|
||||
if ((v < MCPX_HW_MAX_3D_VOICES) && (b < 4)) {
|
||||
// FIXME: Not sure if submix/voice headroom factor in for HRTF
|
||||
// Note: Attenuate extra 6dB to simulate HRTF
|
||||
hr = 1 << (d->vp.hrtf_headroom + 1);
|
||||
hr = 1 << d->vp.hrtf_headroom;
|
||||
} else {
|
||||
hr = 1 << d->vp.submix_headroom[bin[b]];
|
||||
}
|
||||
@@ -2483,6 +2560,9 @@ static void mcpx_apu_reset(MCPXAPUState *d)
|
||||
memset(d->vp.hrtf_submix, 0, sizeof(d->vp.hrtf_submix));
|
||||
memset(d->vp.submix_headroom, 0, sizeof(d->vp.submix_headroom));
|
||||
memset(d->vp.voice_locked, 0, sizeof(d->vp.voice_locked));
|
||||
for (int v = 0; v < ARRAY_SIZE(d->vp.filters); v++) {
|
||||
hrtf_filter_init(&d->vp.filters[v].hrtf);
|
||||
}
|
||||
|
||||
// FIXME: Reset DSP state
|
||||
memset(d->gp.dsp->core.pram_opcache, 0,
|
||||
|
||||
+19
-1
@@ -145,6 +145,8 @@
|
||||
#define NV1BA0_PIO_VOICE_PAUSE 0x00000140
|
||||
# define NV1BA0_PIO_VOICE_PAUSE_HANDLE 0x0000FFFF
|
||||
# define NV1BA0_PIO_VOICE_PAUSE_ACTION (1 << 18)
|
||||
#define NV1BA0_PIO_SET_CURRENT_HRTF_ENTRY 0x00000160
|
||||
# define NV1BA0_PIO_SET_CURRENT_HRTF_ENTRY_HANDLE 0x0000FFFF
|
||||
#define NV1BA0_PIO_SET_CONTEXT_DMA_NOTIFY 0x00000180
|
||||
#define NV1BA0_PIO_SET_CURRENT_SSL_CONTEXT_DMA 0x0000018C
|
||||
#define NV1BA0_PIO_SET_CURRENT_SSL 0x00000190
|
||||
@@ -153,7 +155,7 @@
|
||||
#define NV1BA0_PIO_SET_SSL_SEGMENT_LENGTH 0x00000604
|
||||
#define NV1BA0_PIO_SET_SUBMIX_HEADROOM 0x00000200
|
||||
# define NV1BA0_PIO_SET_SUBMIX_HEADROOM_AMOUNT 0x7
|
||||
#define NV1BA0_PIO_SET_HRTF_HEADROOM 0x00000280
|
||||
#define NV1BA0_PIO_SET_HRTF_HEADROOM 0x00000280
|
||||
# define NV1BA0_PIO_SET_HRTF_HEADROOM_AMOUNT 0x7
|
||||
#define NV1BA0_PIO_SET_HRTF_SUBMIXES 0x000002C0
|
||||
#define NV1BA0_PIO_SET_CURRENT_VOICE 0x000002F8
|
||||
@@ -165,6 +167,8 @@
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_ENV1 0x00000310
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_ENVF 0x00000314
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_MISC 0x00000318
|
||||
#define NV1BA0_PIO_SET_VOICE_TAR_HRTF 0x0000031C
|
||||
# define NV1BA0_PIO_SET_VOICE_TAR_HRTF_HANDLE 0x0000FFFF
|
||||
#define NV1BA0_PIO_SET_VOICE_SSL_A 0x00000320
|
||||
# define NV1BA0_PIO_SET_VOICE_SSL_A_COUNT 0x000000FF
|
||||
# define NV1BA0_PIO_SET_VOICE_SSL_A_BASE 0xFFFFFF00
|
||||
@@ -185,6 +189,15 @@
|
||||
# define NV1BA0_PIO_SET_VOICE_BUF_CBO_OFFSET 0x00FFFFFF
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO 0x000003DC
|
||||
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO_OFFSET 0x00FFFFFF
|
||||
#define NV1BA0_PIO_SET_HRIR 0x00000400
|
||||
# define NV1BA0_PIO_SET_HRIR_LEFT0 0x000000FF
|
||||
# define NV1BA0_PIO_SET_HRIR_RIGHT0 0x0000FF00
|
||||
# define NV1BA0_PIO_SET_HRIR_LEFT1 0x00FF0000
|
||||
# define NV1BA0_PIO_SET_HRIR_RIGHT1 0xFF000000
|
||||
#define NV1BA0_PIO_SET_HRIR_X 0x0000043C
|
||||
# define NV1BA0_PIO_SET_HRIR_X_LEFT30 0x000000FF
|
||||
# define NV1BA0_PIO_SET_HRIR_X_RIGHT30 0x0000FF00
|
||||
# define NV1BA0_PIO_SET_HRIR_X_ITD 0xFFFF0000
|
||||
#define NV1BA0_PIO_SET_SSL_SEGMENT_OFFSET 0x00000600
|
||||
#define NV1BA0_PIO_SET_SSL_SEGMENT_LENGTH 0x00000604
|
||||
#define NV1BA0_PIO_SET_CURRENT_INBUF_SGE 0x00000804
|
||||
@@ -249,6 +262,8 @@
|
||||
#define NV_PAVS_VOICE_CFG_MISC 0x00000018
|
||||
# define NV_PAVS_VOICE_CFG_MISC_EF_RELEASERATE (0xFFF << 0)
|
||||
# define NV_PAVS_VOICE_CFG_MISC_FMODE (3 << 16)
|
||||
#define NV_PAVS_VOICE_CFG_HRTF_TARGET 0x0000001C
|
||||
# define NV_PAVS_VOICE_CFG_HRTF_TARGET_HANDLE 0x0000FFFF
|
||||
#define NV_PAVS_VOICE_CUR_PSL_START 0x00000020
|
||||
# define NV_PAVS_VOICE_CUR_PSL_START_BA 0x00FFFFFF
|
||||
#define NV_PAVS_VOICE_CUR_PSH_SAMPLE 0x00000024
|
||||
@@ -337,6 +352,9 @@ enum MCPX_HW_NOTIFIER {
|
||||
#define NV1BA0_NOTIFICATION_STATUS_DONE_SUCCESS 0x01
|
||||
#define NV1BA0_NOTIFICATION_STATUS_IN_PROGRESS 0x80
|
||||
|
||||
#define HRTF_NULL_HANDLE 0xFFFF
|
||||
#define HRTF_ENTRY_COUNT 128
|
||||
|
||||
// clang-format on
|
||||
|
||||
#endif
|
||||
|
||||
+13
-1
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Helper FP conversions
|
||||
*
|
||||
* Copyright (c) 2020-2021 Matt Borgerson
|
||||
* Copyright (c) 2020-2025 Matt Borgerson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -21,6 +21,13 @@
|
||||
#ifndef FLOATCONV_H
|
||||
#define FLOATCONV_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static float int8_to_float(int8_t x)
|
||||
{
|
||||
return x / 128.0f;
|
||||
}
|
||||
|
||||
static float uint8_to_float(uint8_t value)
|
||||
{
|
||||
return ((int)value - 0x80) / (1.0 * 0x80);
|
||||
@@ -31,6 +38,11 @@ static float int16_to_float(int16_t value)
|
||||
return value / (1.0 * 0x8000);
|
||||
}
|
||||
|
||||
static float s6p9_to_float(int16_t value)
|
||||
{
|
||||
return value / 512.0f;
|
||||
}
|
||||
|
||||
static float int32_to_float(int32_t value)
|
||||
{
|
||||
return value / (1.0 * 0x80000000);
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* HRTF Filter
|
||||
*
|
||||
* Copyright (c) 2025 Matt Borgerson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HW_XBOX_MCPX_HRTF_H
|
||||
#define HW_XBOX_MCPX_HRTF_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "apu_regs.h"
|
||||
|
||||
#define HRTF_SAMPLES_PER_FRAME NUM_SAMPLES_PER_FRAME
|
||||
#define HRTF_NUM_TAPS 31
|
||||
#define HRTF_MAX_DELAY_SAMPLES 42
|
||||
#define HRTF_BUFLEN (HRTF_NUM_TAPS + HRTF_MAX_DELAY_SAMPLES)
|
||||
#define HRTF_PARAM_SMOOTH_ALPHA 0.01f
|
||||
|
||||
typedef struct {
|
||||
int buf_pos;
|
||||
struct {
|
||||
float buf[HRTF_BUFLEN];
|
||||
float hrir_coeff_cur[HRTF_NUM_TAPS];
|
||||
float hrir_coeff_tar[HRTF_NUM_TAPS];
|
||||
} ch[2];
|
||||
float itd_cur;
|
||||
float itd_tar;
|
||||
} HrtfFilter;
|
||||
|
||||
static inline void hrtf_filter_init(HrtfFilter *f)
|
||||
{
|
||||
memset(f, 0, sizeof(*f));
|
||||
}
|
||||
|
||||
static inline void
|
||||
hrtf_filter_set_target_params(HrtfFilter *f, float hrir_coeff[2][HRTF_NUM_TAPS],
|
||||
float itd)
|
||||
{
|
||||
f->itd_tar =
|
||||
fmaxf(-HRTF_MAX_DELAY_SAMPLES, fminf(itd, HRTF_MAX_DELAY_SAMPLES));
|
||||
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
float *coeff = f->ch[ch].hrir_coeff_tar;
|
||||
memcpy(coeff, hrir_coeff[ch], sizeof(f->ch[ch].hrir_coeff_tar));
|
||||
|
||||
// Normalize coefficients for unity filter gain
|
||||
float s = 0.0f;
|
||||
for (int k = 0; k < HRTF_NUM_TAPS; k++) {
|
||||
s += fabsf(coeff[k]);
|
||||
}
|
||||
if (s == 0.0f || s == 1.0f) {
|
||||
break;
|
||||
}
|
||||
for (int k = 0; k < HRTF_NUM_TAPS; k++) {
|
||||
coeff[k] /= s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline float hrtf_filter_smooth_param(float cur, float tar)
|
||||
{
|
||||
// FIXME: Match hardware parameter transition
|
||||
return cur + HRTF_PARAM_SMOOTH_ALPHA * (tar - cur);
|
||||
}
|
||||
|
||||
static inline void hrtf_filter_step_parameters(HrtfFilter *f)
|
||||
{
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
float *coeff_cur = f->ch[ch].hrir_coeff_cur;
|
||||
float *coeff_tar = f->ch[ch].hrir_coeff_tar;
|
||||
for (int k = 0; k < HRTF_NUM_TAPS; k++) {
|
||||
coeff_cur[k] = hrtf_filter_smooth_param(coeff_cur[k], coeff_tar[k]);
|
||||
}
|
||||
}
|
||||
f->itd_cur = hrtf_filter_smooth_param(f->itd_cur, f->itd_tar);
|
||||
}
|
||||
|
||||
static inline void hrtf_filter_process(HrtfFilter *f,
|
||||
float in[HRTF_SAMPLES_PER_FRAME][2],
|
||||
float out[HRTF_SAMPLES_PER_FRAME][2])
|
||||
{
|
||||
for (int n = 0; n < HRTF_SAMPLES_PER_FRAME; n++) {
|
||||
hrtf_filter_step_parameters(f);
|
||||
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
float *buf = f->ch[ch].buf;
|
||||
float *coeff = f->ch[ch].hrir_coeff_cur;
|
||||
|
||||
// Push new sample
|
||||
buf[f->buf_pos] = in[n][ch];
|
||||
|
||||
// Interaural time difference (channel delay)
|
||||
float d = f->itd_cur * (ch == 0 ? +1.0f : -1.0f);
|
||||
if (d < 0.0f) {
|
||||
d = 0.0f;
|
||||
}
|
||||
int di = d;
|
||||
float dfrac = d - di;
|
||||
|
||||
// HRIR Convolution
|
||||
float acc = 0.0f;
|
||||
for (int k = 0; k < HRTF_NUM_TAPS; k++) {
|
||||
int idx1 = (f->buf_pos - di - k + HRTF_BUFLEN) % HRTF_BUFLEN;
|
||||
float s = buf[idx1];
|
||||
|
||||
// Linear interpolation for fractional part
|
||||
if (dfrac > 0.0f) {
|
||||
int idx2 = (idx1 - 1 + HRTF_BUFLEN) % HRTF_BUFLEN;
|
||||
s = s * (1 - dfrac) + buf[idx2] * dfrac;
|
||||
}
|
||||
acc += coeff[k] * s;
|
||||
}
|
||||
|
||||
out[n][ch] = acc;
|
||||
}
|
||||
|
||||
f->buf_pos = (f->buf_pos + 1) % HRTF_BUFLEN;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -228,6 +228,8 @@ void DebugApuWindow::Draw()
|
||||
mcpx_apu_debug_set_ep_realtime_enabled(ep_realtime);
|
||||
}
|
||||
|
||||
ImGui::Checkbox("HRTF Filtering\n", &g_config.audio.hrtf);
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user