Skip to content

Commit 5ce3506

Browse files
committed
audio: steamaudio: add acoustic pathing and knife-edge diffraction offload
Add support for acoustic pathing and knife-edge obstacle diffraction to the Sound Open Firmware (SOF) Steam Audio component: 1. Implements 3-band diffraction IIR biquad filtering with RBJ shelf and peaking filters at Steam Audio standard band cutoffs (800 Hz, 2500 Hz, 8000 Hz). 2. Encodes diffracted audio into Higher-Order Ambisonics (HOA) soundfield arrival vectors up to Order 3. 3. Applies listener coordinate space rotation matrices to the Ambisonics soundfield representation. 4. Renders spatialized audio via head-fixed virtual spherical loudspeaker arrays into binaural stereo or multi-channel surround speakers. 5. Adds STEAMAUDIO_PARAM_PATHING_CONFIG (0x100A) IPC4 configuration handler and STEAMAUDIO_OUTPUT_PATHING (4) rendering mode. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
1 parent 8283c1c commit 5ce3506

4 files changed

Lines changed: 212 additions & 13 deletions

File tree

src/audio/steamaudio/steamaudio-generic.c

Lines changed: 127 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,22 +87,22 @@ static inline float fast_acos(float x)
8787
}
8888

8989
/* 3-Band Biquad Filter Computation */
90-
static void calc_biquad_coeffs(float gain_db, float freq, float sample_rate, int type, float coeffs[5])
90+
static void calc_biquad_coeffs(float linear_gain, float freq, float sample_rate, int type, float coeffs[5])
9191
{
9292
float w0 = 2.0f * PI * freq / sample_rate;
9393
float cos_w0 = fast_cos(w0);
9494
float sin_w0 = fast_sin(w0);
9595
float a = 1.0f; /* Q = 1.0 */
9696
float alpha = sin_w0 / (2.0f * a);
97-
float A = 1.0f;
98-
if (gain_db != 0.0f)
99-
A = 1.0f + gain_db * 0.115129f; /* 10^(dB/20) approx */
97+
98+
float g = sat_clamp(linear_gain, 0.0001f, 1.0f);
99+
float A = fast_sqrt(g);
100+
float sqrt_A = fast_sqrt(A);
100101

101102
float b0 = 1.0f, b1 = 0.0f, b2 = 0.0f, a0 = 1.0f, a1 = 0.0f, a2 = 0.0f;
102103

103104
if (type == 0) {
104-
/* Low shelf (400 Hz) */
105-
float sqrt_A = (A > 0.0f) ? (1.0f + 0.5f * (A - 1.0f)) : 1.0f;
105+
/* Low shelf (800 Hz) */
106106
b0 = A * ((A + 1.0f) - (A - 1.0f) * cos_w0 + 2.0f * sqrt_A * alpha);
107107
b1 = 2.0f * A * ((A - 1.0f) - (A + 1.0f) * cos_w0);
108108
b2 = A * ((A + 1.0f) - (A - 1.0f) * cos_w0 - 2.0f * sqrt_A * alpha);
@@ -118,8 +118,7 @@ static void calc_biquad_coeffs(float gain_db, float freq, float sample_rate, int
118118
a1 = -2.0f * cos_w0;
119119
a2 = 1.0f - alpha / A;
120120
} else {
121-
/* High shelf (15 kHz) */
122-
float sqrt_A = (A > 0.0f) ? (1.0f + 0.5f * (A - 1.0f)) : 1.0f;
121+
/* High shelf (8000 Hz) */
123122
b0 = A * ((A + 1.0f) + (A - 1.0f) * cos_w0 + 2.0f * sqrt_A * alpha);
124123
b1 = -2.0f * A * ((A - 1.0f) + (A + 1.0f) * cos_w0);
125124
b2 = A * ((A + 1.0f) - (A - 1.0f) * cos_w0 - 2.0f * sqrt_A * alpha);
@@ -128,14 +127,22 @@ static void calc_biquad_coeffs(float gain_db, float freq, float sample_rate, int
128127
a2 = (A + 1.0f) - (A - 1.0f) * cos_w0 - 2.0f * sqrt_A * alpha;
129128
}
130129

131-
float inv_a0 = 1.0f / a0;
130+
float inv_a0 = (a0 > 0.0001f) ? (1.0f / a0) : 1.0f;
132131
coeffs[0] = b0 * inv_a0;
133132
coeffs[1] = b1 * inv_a0;
134133
coeffs[2] = b2 * inv_a0;
135134
coeffs[3] = a1 * inv_a0;
136135
coeffs[4] = a2 * inv_a0;
137136
}
138137

138+
static inline float apply_biquad(float in, const float coeffs[5], float state[2])
139+
{
140+
float out = coeffs[0] * in + state[0];
141+
state[0] = coeffs[1] * in - coeffs[3] * out + state[1];
142+
state[1] = coeffs[2] * in - coeffs[4] * out;
143+
return out;
144+
}
145+
139146
void steamaudio_dsp_init(struct steamaudio_comp_data *cd, uint32_t sample_rate)
140147
{
141148
cd->sample_rate = sample_rate ? sample_rate : 48000;
@@ -151,9 +158,9 @@ void steamaudio_dsp_init(struct steamaudio_comp_data *cd, uint32_t sample_rate)
151158
cd->direct.crossfade_remaining = 0;
152159

153160
for (int b = 0; b < STEAMAUDIO_NUM_EQ_BANDS; b++) {
154-
float freq = (b == 0) ? 400.0f : ((b == 1) ? 2500.0f : 15000.0f);
155-
calc_biquad_coeffs(0.0f, freq, (float)cd->sample_rate, b, cd->direct.coeffs[0][b]);
156-
calc_biquad_coeffs(0.0f, freq, (float)cd->sample_rate, b, cd->direct.coeffs[1][b]);
161+
float freq = (b == 0) ? 800.0f : ((b == 1) ? 2500.0f : 8000.0f);
162+
calc_biquad_coeffs(1.0f, freq, (float)cd->sample_rate, b, cd->direct.coeffs[0][b]);
163+
calc_biquad_coeffs(1.0f, freq, (float)cd->sample_rate, b, cd->direct.coeffs[1][b]);
157164
}
158165

159166
/* Initialize Binaural */
@@ -189,6 +196,9 @@ void steamaudio_dsp_init(struct steamaudio_comp_data *cd, uint32_t sample_rate)
189196
/* Initialize Virtual Surround Sound */
190197
steamaudio_dsp_virtual_surround_init(&cd->virtual_surround, STEAMAUDIO_SPEAKER_LAYOUT_5_1, cd->sample_rate);
191198

199+
/* Initialize Acoustic Pathing */
200+
steamaudio_dsp_pathing_init(&cd->pathing, 1, cd->sample_rate);
201+
192202
cd->output_mode = STEAMAUDIO_OUTPUT_BINAURAL;
193203
memset(cd->in_channels, 0, sizeof(cd->in_channels));
194204
memset(cd->out_channels, 0, sizeof(cd->out_channels));
@@ -721,7 +731,7 @@ void steamaudio_dsp_ambisonics_decode_binaural(struct steamaudio_ambisonics_stat
721731
float sh[16];
722732
eval_sh_basis(rx, ry, rz, (int)ambi->order, sh);
723733

724-
float az = fast_atan2(rx, rz);
734+
float az = fast_atan2(vx, vz);
725735
float sin_az = fast_sin(az);
726736
float ild_l = (sin_az > 0.0f) ? (1.0f - 0.4f * sin_az) : 1.0f;
727737
float ild_r = (sin_az < 0.0f) ? (1.0f + 0.4f * sin_az) : 1.0f;
@@ -738,6 +748,105 @@ void steamaudio_dsp_ambisonics_decode_binaural(struct steamaudio_ambisonics_stat
738748
}
739749
}
740750

751+
void steamaudio_dsp_pathing_init(struct steamaudio_pathing_state *pathing, uint32_t order, uint32_t sample_rate)
752+
{
753+
memset(pathing, 0, sizeof(*pathing));
754+
pathing->order = (order <= 3) ? order : 1;
755+
pathing->num_channels = (pathing->order + 1) * (pathing->order + 1);
756+
pathing->binaural = true;
757+
pathing->eq_coeffs[0] = 1.0f;
758+
pathing->eq_coeffs[1] = 1.0f;
759+
pathing->eq_coeffs[2] = 1.0f;
760+
pathing->sh_coeffs[0] = 1.0f;
761+
pathing->rotation[0][0] = 1.0f;
762+
pathing->rotation[1][1] = 1.0f;
763+
pathing->rotation[2][2] = 1.0f;
764+
765+
calc_biquad_coeffs(1.0f, 800.0f, (float)sample_rate, 0, pathing->filter_coeffs[0]);
766+
calc_biquad_coeffs(1.0f, 2500.0f, (float)sample_rate, 1, pathing->filter_coeffs[1]);
767+
calc_biquad_coeffs(1.0f, 8000.0f, (float)sample_rate, 2, pathing->filter_coeffs[2]);
768+
}
769+
770+
void steamaudio_dsp_pathing_set_params(struct steamaudio_pathing_state *pathing,
771+
const float eq[STEAMAUDIO_NUM_EQ_BANDS],
772+
const float sh[STEAMAUDIO_MAX_HOA_CHANNELS],
773+
uint32_t order, bool binaural,
774+
const float rot[3][3],
775+
uint32_t sample_rate)
776+
{
777+
pathing->order = (order <= 3) ? order : 1;
778+
pathing->num_channels = (pathing->order + 1) * (pathing->order + 1);
779+
pathing->binaural = binaural;
780+
781+
for (int b = 0; b < STEAMAUDIO_NUM_EQ_BANDS; b++) {
782+
pathing->eq_coeffs[b] = sat_clamp(eq[b], 0.0f, 1.0f);
783+
int type = (b == 0) ? 0 : ((b == 1) ? 1 : 2);
784+
float freq = (b == 0) ? 800.0f : ((b == 1) ? 2500.0f : 8000.0f);
785+
calc_biquad_coeffs(pathing->eq_coeffs[b], freq, (float)sample_rate, type, pathing->filter_coeffs[b]);
786+
}
787+
788+
for (int i = 0; i < pathing->num_channels; i++)
789+
pathing->sh_coeffs[i] = sh[i];
790+
791+
if (rot) {
792+
for (int r = 0; r < 3; r++)
793+
for (int c = 0; c < 3; c++)
794+
pathing->rotation[r][c] = rot[r][c];
795+
}
796+
}
797+
798+
void steamaudio_dsp_pathing_process(struct steamaudio_pathing_state *pathing,
799+
struct steamaudio_ambisonics_state *ambi,
800+
struct steamaudio_panning_state *panning,
801+
const float *in, float *out_l, float *out_r,
802+
float out_ch[STEAMAUDIO_MAX_SPEAKERS][256],
803+
uint32_t frames)
804+
{
805+
float eq_buffer[256];
806+
for (uint32_t i = 0; i < frames; i++) {
807+
float s = in[i];
808+
s = apply_biquad(s, pathing->filter_coeffs[0], pathing->filter_states[0]);
809+
s = apply_biquad(s, pathing->filter_coeffs[1], pathing->filter_states[1]);
810+
s = apply_biquad(s, pathing->filter_coeffs[2], pathing->filter_states[2]);
811+
eq_buffer[i] = s;
812+
}
813+
814+
float hoa_channels[STEAMAUDIO_MAX_HOA_CHANNELS][256];
815+
int num_ch = pathing->num_channels;
816+
for (int ch = 0; ch < num_ch; ch++) {
817+
float coeff = pathing->sh_coeffs[ch];
818+
for (uint32_t i = 0; i < frames; i++)
819+
hoa_channels[ch][i] = eq_buffer[i] * coeff;
820+
}
821+
822+
if (pathing->binaural) {
823+
for (int r = 0; r < 3; r++)
824+
for (int c = 0; c < 3; c++)
825+
ambi->rotation[r][c] = pathing->rotation[r][c];
826+
ambi->order = pathing->order;
827+
828+
steamaudio_dsp_ambisonics_decode_binaural(ambi,
829+
(const float (*)[256])hoa_channels,
830+
out_l, out_r, frames);
831+
} else {
832+
float dir[3] = { 0.0f, 0.0f, 1.0f };
833+
if (num_ch >= 4) {
834+
dir[0] = pathing->sh_coeffs[3];
835+
dir[1] = pathing->sh_coeffs[1];
836+
dir[2] = pathing->sh_coeffs[2];
837+
float len = fast_sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]);
838+
if (len > 0.0001f) {
839+
float inv_len = 1.0f / len;
840+
dir[0] *= inv_len; dir[1] *= inv_len; dir[2] *= inv_len;
841+
}
842+
}
843+
steamaudio_dsp_panning_set_direction(panning, dir);
844+
steamaudio_dsp_panning_process(panning, eq_buffer, out_ch, frames);
845+
memcpy(out_l, out_ch[0], frames * sizeof(float));
846+
memcpy(out_r, out_ch[1], frames * sizeof(float));
847+
}
848+
}
849+
741850
static inline void steamaudio_dsp_render(struct steamaudio_comp_data *cd, uint32_t frames)
742851
{
743852
if (cd->output_mode == STEAMAUDIO_OUTPUT_SURROUND_PANNING) {
@@ -765,6 +874,11 @@ static inline void steamaudio_dsp_render(struct steamaudio_comp_data *cd, uint32
765874
cd->out_left, cd->out_right, frames);
766875
}
767876
process_reverb(cd, cd->in_scratch, cd->out_left, cd->out_right, frames);
877+
} else if (cd->output_mode == STEAMAUDIO_OUTPUT_PATHING) {
878+
steamaudio_dsp_pathing_process(&cd->pathing, &cd->ambisonics, &cd->panning,
879+
cd->in_scratch, cd->out_left, cd->out_right,
880+
cd->out_channels, frames);
881+
process_reverb(cd, cd->in_scratch, cd->out_left, cd->out_right, frames);
768882
} else {
769883
process_direct_path(cd, cd->in_scratch, cd->out_left, frames);
770884
memcpy(cd->in_scratch, cd->out_left, frames * sizeof(float));

src/audio/steamaudio/steamaudio-ipc4.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,34 @@ __cold int steamaudio_set_config(struct processing_module *mod,
140140
return 0;
141141
}
142142

143+
case STEAMAUDIO_PARAM_PATHING_CONFIG: {
144+
if (fragment_size < sizeof(struct sof_steamaudio_pathing_config))
145+
return -EINVAL;
146+
147+
const struct sof_steamaudio_pathing_config *cfg =
148+
(const struct sof_steamaudio_pathing_config *)fragment;
149+
150+
float eq[STEAMAUDIO_NUM_EQ_BANDS];
151+
for (int b = 0; b < STEAMAUDIO_NUM_EQ_BANDS; b++)
152+
eq[b] = cfg->eq_coeffs[b];
153+
154+
float sh[STEAMAUDIO_MAX_HOA_CHANNELS];
155+
for (int i = 0; i < STEAMAUDIO_MAX_HOA_CHANNELS; i++)
156+
sh[i] = cfg->sh_coeffs[i];
157+
158+
float rot[3][3];
159+
for (int r = 0; r < 3; r++)
160+
for (int c = 0; c < 3; c++)
161+
rot[r][c] = cfg->listener_rotation[r][c];
162+
163+
steamaudio_dsp_pathing_set_params(&cd->pathing, eq, sh,
164+
cfg->order, (cfg->binaural != 0),
165+
rot, cd->sample_rate);
166+
comp_dbg(dev, "steamaudio: pathing order=%u binaural=%u",
167+
cfg->order, cfg->binaural);
168+
return 0;
169+
}
170+
143171
case STEAMAUDIO_PARAM_BITSTREAM_MODE: {
144172
if (fragment_size < sizeof(uint32_t))
145173
return -EINVAL;
@@ -262,6 +290,25 @@ __cold int steamaudio_get_config(struct processing_module *mod,
262290
return 0;
263291
}
264292

293+
case STEAMAUDIO_PARAM_PATHING_CONFIG: {
294+
if (fragment_size < sizeof(struct sof_steamaudio_pathing_config))
295+
return -EINVAL;
296+
297+
struct sof_steamaudio_pathing_config *cfg = (struct sof_steamaudio_pathing_config *)fragment;
298+
memset(cfg, 0, sizeof(*cfg));
299+
cfg->comp_type = STEAMAUDIO_PARAM_PATHING_CONFIG;
300+
for (int b = 0; b < STEAMAUDIO_NUM_EQ_BANDS; b++)
301+
cfg->eq_coeffs[b] = cd->pathing.eq_coeffs[b];
302+
for (int i = 0; i < STEAMAUDIO_MAX_HOA_CHANNELS; i++)
303+
cfg->sh_coeffs[i] = cd->pathing.sh_coeffs[i];
304+
cfg->order = cd->pathing.order;
305+
cfg->binaural = cd->pathing.binaural ? 1 : 0;
306+
for (int r = 0; r < 3; r++)
307+
for (int c = 0; c < 3; c++)
308+
cfg->listener_rotation[r][c] = cd->pathing.rotation[r][c];
309+
return 0;
310+
}
311+
265312
case STEAMAUDIO_PARAM_BVH_QUERY: {
266313
if (fragment_size < sizeof(struct sof_steamaudio_bvh_query))
267314
return -EINVAL;

src/audio/steamaudio/steamaudio.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ static int steamaudio_reset(struct processing_module *mod)
105105
memset(cd->reverb.delay_buffers, 0, sizeof(cd->reverb.delay_buffers));
106106
memset(cd->reverb.damp_states, 0, sizeof(cd->reverb.damp_states));
107107
memset(cd->virtual_surround.delay_lines, 0, sizeof(cd->virtual_surround.delay_lines));
108+
memset(cd->pathing.filter_states, 0, sizeof(cd->pathing.filter_states));
108109
memset(cd->in_scratch, 0, sizeof(cd->in_scratch));
109110
memset(cd->in_channels, 0, sizeof(cd->in_channels));
110111
memset(cd->out_channels, 0, sizeof(cd->out_channels));

src/audio/steamaudio/steamaudio.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#define STEAMAUDIO_PARAM_PANNING_CONFIG 0x1007
3636
#define STEAMAUDIO_PARAM_VIRTUAL_SURROUND_CONFIG 0x1008
3737
#define STEAMAUDIO_PARAM_OUTPUT_MODE 0x1009
38+
#define STEAMAUDIO_PARAM_PATHING_CONFIG 0x100A
3839

3940
enum steamaudio_speaker_layout {
4041
STEAMAUDIO_SPEAKER_LAYOUT_STEREO = 0,
@@ -48,6 +49,7 @@ enum steamaudio_output_mode {
4849
STEAMAUDIO_OUTPUT_SURROUND_PANNING = 1,
4950
STEAMAUDIO_OUTPUT_VIRTUAL_SURROUND = 2,
5051
STEAMAUDIO_OUTPUT_AMBISONICS = 3,
52+
STEAMAUDIO_OUTPUT_PATHING = 4,
5153
};
5254

5355
/* Bitstream Encapsulation Header */
@@ -140,6 +142,15 @@ struct __attribute__((packed)) sof_steamaudio_bvh_query {
140142
uint32_t has_line_of_sight;
141143
};
142144

145+
struct __attribute__((packed)) sof_steamaudio_pathing_config {
146+
uint32_t comp_type;
147+
float eq_coeffs[STEAMAUDIO_NUM_EQ_BANDS]; /* 3-band diffraction transmission EQ */
148+
float sh_coeffs[STEAMAUDIO_MAX_HOA_CHANNELS]; /* Ambisonics SH arrival soundfield */
149+
uint32_t order; /* Ambisonics order (0, 1, 2, or 3) */
150+
uint32_t binaural; /* 1: Binaural headphone decode, 0: Panning decode */
151+
float listener_rotation[3][3]; /* Listener coordinate space rotation */
152+
};
153+
143154
/* Internal DSP Sub-Engine States */
144155
struct steamaudio_direct_state {
145156
float coeffs[2][STEAMAUDIO_NUM_EQ_BANDS][5]; /* b0, b1, b2, a1, a2 */
@@ -199,6 +210,17 @@ struct steamaudio_virtual_surround_state {
199210
float ild_gains[STEAMAUDIO_MAX_SPEAKERS][2];
200211
};
201212

213+
struct steamaudio_pathing_state {
214+
float eq_coeffs[STEAMAUDIO_NUM_EQ_BANDS];
215+
float sh_coeffs[STEAMAUDIO_MAX_HOA_CHANNELS];
216+
uint32_t order;
217+
int num_channels;
218+
bool binaural;
219+
float rotation[3][3];
220+
float filter_coeffs[STEAMAUDIO_NUM_EQ_BANDS][5];
221+
float filter_states[STEAMAUDIO_NUM_EQ_BANDS][2];
222+
};
223+
202224
/* BVH scene forward declaration */
203225
#define STEAMAUDIO_MAX_DSP_TRIANGLES 32
204226
#define STEAMAUDIO_MAX_DSP_BVH_NODES 64
@@ -269,6 +291,7 @@ struct steamaudio_comp_data {
269291
struct steamaudio_ambisonics_state ambisonics;
270292
struct steamaudio_panning_state panning;
271293
struct steamaudio_virtual_surround_state virtual_surround;
294+
struct steamaudio_pathing_state pathing;
272295
struct dsp_scene scene;
273296

274297
/* Temporary scratch buffers for processing frames */
@@ -302,6 +325,20 @@ void steamaudio_dsp_ambisonics_decode_binaural(struct steamaudio_ambisonics_stat
302325
const float in_ch[STEAMAUDIO_MAX_HOA_CHANNELS][256],
303326
float *out_l, float *out_r, uint32_t frames);
304327

328+
void steamaudio_dsp_pathing_init(struct steamaudio_pathing_state *pathing, uint32_t order, uint32_t sample_rate);
329+
void steamaudio_dsp_pathing_set_params(struct steamaudio_pathing_state *pathing,
330+
const float eq[STEAMAUDIO_NUM_EQ_BANDS],
331+
const float sh[STEAMAUDIO_MAX_HOA_CHANNELS],
332+
uint32_t order, bool binaural,
333+
const float rot[3][3],
334+
uint32_t sample_rate);
335+
void steamaudio_dsp_pathing_process(struct steamaudio_pathing_state *pathing,
336+
struct steamaudio_ambisonics_state *ambi,
337+
struct steamaudio_panning_state *panning,
338+
const float *in, float *out_l, float *out_r,
339+
float out_ch[STEAMAUDIO_MAX_SPEAKERS][256],
340+
uint32_t frames);
341+
305342
/* IPC4 control callbacks */
306343
int steamaudio_set_config(struct processing_module *mod,
307344
uint32_t param_id,

0 commit comments

Comments
 (0)