@@ -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+
139146void 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+
741850static 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 ));
0 commit comments