SAF
|
Files | |
file | saf_hoa.c |
Public source for the higher-order Ambisonics module (SAF_HOA_MODULE) | |
file | saf_hoa.h |
Main header for the higher-order Ambisonics module (SAF_HOA_MODULE) | |
file | saf_hoa_internal.c |
Internal source for the higher-order Ambisonics module (SAF_HOA_MODULE) | |
file | saf_hoa_internal.h |
Internal header for the higher-order Ambisonics module (SAF_HOA_MODULE) | |
Enumerations | |
enum | LOUDSPEAKER_AMBI_DECODER_METHODS { LOUDSPEAKER_DECODER_DEFAULT , LOUDSPEAKER_DECODER_SAD , LOUDSPEAKER_DECODER_MMD , LOUDSPEAKER_DECODER_EPAD , LOUDSPEAKER_DECODER_ALLRAD } |
Ambisonic decoding options for loudspeaker playback. More... | |
enum | BINAURAL_AMBI_DECODER_METHODS { BINAURAL_DECODER_DEFAULT , BINAURAL_DECODER_LS , BINAURAL_DECODER_LSDIFFEQ , BINAURAL_DECODER_SPR , BINAURAL_DECODER_TA , BINAURAL_DECODER_MAGLS } |
Ambisonic decoding options for binaural/headphone playback. More... | |
enum | HOA_CH_ORDER { HOA_CH_ORDER_ACN , HOA_CH_ORDER_FUMA } |
Available Ambisonic channel ordering conventions. More... | |
enum | HOA_NORM { HOA_NORM_N3D , HOA_NORM_SN3D , HOA_NORM_FUMA } |
Available Ambisonic normalisation conventions. More... | |
Functions | |
void | convertHOAChannelConvention (float *insig, int order, int signalLength, HOA_CH_ORDER inConvention, HOA_CH_ORDER outConvention) |
Converts an Ambisonic signal from one channel ordering convention to another. | |
void | convertHOANormConvention (float *insig, int order, int signalLength, HOA_NORM inConvention, HOA_NORM outConvention) |
Converts an Ambisonic signal from one normalisation convention to another. | |
void | getRSH (int order, float *dirs_deg, int nDirs, float *Y) |
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere. | |
void | getRSH_recur (int order, float *dirs_deg, int nDirs, float *Y) |
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere. | |
void | getMaxREweights (int order, int diagMtxFlag, float *a_n) |
Computes the weights required to manipulate a hyper-cardioid beam-pattern, such that it has maximum energy in the given look-direction. | |
void | truncationEQ (float *w_n, int order_truncated, int order_target, double *kr, int nBands, float softThreshold, float *gain) |
Filter that equalises the high frequency roll-off due to SH truncation and tapering; as described in [1]. | |
void | getLoudspeakerDecoderMtx (float *ls_dirs_deg, int nLS, LOUDSPEAKER_AMBI_DECODER_METHODS method, int order, int enableMaxrE, float *decMtx) |
Computes an ambisonic decoding matrix of a specific order, for a given loudspeaker layout. | |
void | getBinauralAmbiDecoderMtx (float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, BINAURAL_AMBI_DECODER_METHODS method, int order, float *freqVector, float *itd_s, float *weights, int enableDiffCM, int enableMaxrE, float_complex *decMtx) |
Computes binaural ambisonic decoding matrices (one per frequency) at a specific order, for a given HRTF set. | |
void | getBinauralAmbiDecoderFilters (float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int fftSize, float fs, BINAURAL_AMBI_DECODER_METHODS method, int order, float *itd_s, float *weights, int enableDiffCM, int enableMaxrE, float *decFilters) |
Computes binaural ambisonic decoding filters for a given HRTF set. | |
void | applyDiffCovMatching (float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *weights, float_complex *decMtx) |
Imposes a diffuse-field covariance constraint on a given binaural decoding matrix, as described in [1]. | |
Higher-order Ambisonics module
Ambisonic decoding options for binaural/headphone playback.
Enumerator | |
---|---|
BINAURAL_DECODER_DEFAULT | The default decoder is BINAURAL_DECODER_LS. |
BINAURAL_DECODER_LS | Least-squares (LS) decoder. The simplest binaural decoder, which is based on a least-squares fit of the spherical harmonic patterns onto the HRTF directivity patterns. |
BINAURAL_DECODER_LSDIFFEQ | Least-squares (LS) decoder with diffuse-field spectral equalisation [1]. Note that the diffuse-field EQ is applied in the spherical harmonic domain (to account for the truncation error/loss of high frequencies), so this is not the same as applying diffuseFieldEqualiseHRTFs() on the HRTFs followed by BINAURAL_DECODER_LS. |
BINAURAL_DECODER_SPR | Spatial resampling decoder (on the same lines as the virtual loudspeaker approach) [4]. |
BINAURAL_DECODER_TA | Time-alignment decoder [2]. Relies on discarding the phase information of the HRTFs, past the frequency at which humans are less sensitive to inter-aural time difference cues. Therefore, the least-squares fitting priorites matching the interaural level differences (ILDs), rather than the interaural time differences (ITDs). |
BINAURAL_DECODER_MAGLS | Magnitude least-squares decoder [3]. On similar lines to the time- alignment decoder, but differing slightly in its execution. |
enum HOA_CH_ORDER |
Available Ambisonic channel ordering conventions.
enum HOA_NORM |
Available Ambisonic normalisation conventions.
Ambisonic decoding options for loudspeaker playback.
Note that the MMD and EPAD decoding options revert back to "SAD" if the loudspeakers are uniformly distributed on the sphere. The benefits afforded by MMD, EPAD [1], and AllRAD [2] relate to their improved performance when using irregular loudspeaker arrangements.
Enumerator | |
---|---|
LOUDSPEAKER_DECODER_DEFAULT | The default decoder is LOUDSPEAKER_DECODER_SAD. |
LOUDSPEAKER_DECODER_SAD | Sampling Ambisonic Decoder (SAD): transpose of the loudspeaker spherical harmonic matrix, scaled by the number of loudspeakers. This is the simplest decoding approach, as it essentially just generates hyper- cardioid beamformers (aka virtual microphones) towards each loudspeaker direction. This approach is numerically robust to irregular loudspeaker arrangements. However, it does not preserve the energy of a source (or localisation cues) as it is panned around in different directions over irregular setups. |
LOUDSPEAKER_DECODER_MMD | Mode-Matching Decoder (MMD): pseudo-inverse of the loudspeaker spherical harmonic matrix. Due to the pseudo-inverse, more signal energy is lent to regions on the surface of the sphere that are more sparsely populated with loudspeakers; (this is essentially a least-squares solution). Therefore, this approach can help balance out directional loudness differences when using slightly irregular setups. However, one must also be careful since loudspeakers that are very far way from all the other loudspeakers (e.g. voice-of-god) may be given significantly more signal energy than expected. Therefore, this approach is not recommended for highly irregular loudspeaker arrangements! |
LOUDSPEAKER_DECODER_EPAD | Energy-Preserving Ambisonic Decoder (EPAD) [1]. This decoder aims to preserve the energy of a source, as it panned around to directions of the sphere; essentially, addressing the energy-preserving issues of the SAD and MMD decoding approaches for irregular layouts. |
LOUDSPEAKER_DECODER_ALLRAD | All-Round Ambisonic Decoder (AllRAD): SAD decoding to a t-design, panned for the target loudspeaker directions using VBAP [2]. Perhaps the Ambisonic decoder we would most recommend for irregular loudspeaker layouts. Note, given a high (well... technically infinite) order, AllRAD will converge to VBAP. However, since lower-orders are employed in practice, AllRAD is not as spatially "sharp" as VBAP, but it will yield more consistent source spread when panning a source inbetween the loudspeakers. The approach is highly robust to irregular loudspeaker setups, and exhibits low directional error and good energy-preserving properties. |
void applyDiffCovMatching | ( | float_complex * | hrtfs, |
float * | hrtf_dirs_deg, | ||
int | N_dirs, | ||
int | N_bands, | ||
int | order, | ||
float * | weights, | ||
float_complex * | decMtx ) |
Imposes a diffuse-field covariance constraint on a given binaural decoding matrix, as described in [1].
[in] | hrtfs | The HRTFs; FLAT: N_bands x NUM_EARS x N_dirs |
[in] | hrtf_dirs_deg | HRTF directions; FLAT: N_dirs x 2 |
[in] | N_dirs | Number of HRTF directions |
[in] | N_bands | Number of frequency bands/bins |
[in] | order | Decoding order |
[in] | weights | Integration weights (set to NULL if not available); N_dirs x 1 |
[in,out] | decMtx | Decoding matrix; FLAT: N_bands x NUM_EARS x (order+1)^2 |
void convertHOAChannelConvention | ( | float * | insig, |
int | order, | ||
int | signalLength, | ||
HOA_CH_ORDER | inConvention, | ||
HOA_CH_ORDER | outConvention ) |
Converts an Ambisonic signal from one channel ordering convention to another.
[in,out] | insig | Input signal with the channel ordering convention of: inConvention; FLAT: (order+1)^2 x signalLength |
[in] | order | Ambisonic order |
[in] | signalLength | Signal length in samples |
[in] | inConvention | Channel order convention of input signals |
[in] | outConvention | Channel order convention of output signals |
void convertHOANormConvention | ( | float * | insig, |
int | order, | ||
int | signalLength, | ||
HOA_NORM | inConvention, | ||
HOA_NORM | outConvention ) |
Converts an Ambisonic signal from one normalisation convention to another.
[in,out] | insig | Input signal with the channel ordering convention of: inConvention, which should be converted to: outConvention, "in-place"; FLAT: (order+1)^2 x signalLength |
[in] | order | Ambisonic order |
[in] | signalLength | Signal length in samples |
[in] | inConvention | Normalisation convention of the input signals |
[in] | outConvention | Normalisation convention of the output signals |
void getBinauralAmbiDecoderFilters | ( | float_complex * | hrtfs, |
float * | hrtf_dirs_deg, | ||
int | N_dirs, | ||
int | fftSize, | ||
float | fs, | ||
BINAURAL_AMBI_DECODER_METHODS | method, | ||
int | order, | ||
float * | itd_s, | ||
float * | weights, | ||
int | enableDiffCM, | ||
int | enableMaxrE, | ||
float * | decFilters ) |
Computes binaural ambisonic decoding filters for a given HRTF set.
[in] | hrtfs | The HRTFs; FLAT: (fftSize/2+1) x NUM_EARS x N_dirs |
[in] | hrtf_dirs_deg | HRTF directions; FLAT: N_dirs x 2 |
[in] | N_dirs | Number of HRTF directions |
[in] | fftSize | FFT size |
[in] | fs | Sampling rate |
[in] | method | Decoder method (see BINAURAL_AMBI_DECODER_METHODS enum) |
[in] | order | Decoding order |
[in] | itd_s | Only needed for BINAURAL_DECODER_TA decoder (can set to NULL if using different method); N_dirs x 1 |
[in] | weights | Integration weights (set to NULL if not available); N_dirs x 1 |
[in] | enableDiffCM | Set to '0' to disable diffuse correction, '1' to enable |
[in] | enableMaxrE | Set to '0' to disable maxRE weighting, '1' to enable |
[out] | decFilters | Decoding filters; FLAT: NUM_EARS x (order+1)^2 x fftSize |
void getBinauralAmbiDecoderMtx | ( | float_complex * | hrtfs, |
float * | hrtf_dirs_deg, | ||
int | N_dirs, | ||
int | N_bands, | ||
BINAURAL_AMBI_DECODER_METHODS | method, | ||
int | order, | ||
float * | freqVector, | ||
float * | itd_s, | ||
float * | weights, | ||
int | enableDiffCM, | ||
int | enableMaxrE, | ||
float_complex * | decMtx ) |
Computes binaural ambisonic decoding matrices (one per frequency) at a specific order, for a given HRTF set.
[in] | hrtfs | The HRTFs; FLAT: N_bands x NUM_EARS x N_dirs |
[in] | hrtf_dirs_deg | HRTF directions; FLAT: N_dirs x 2 |
[in] | N_dirs | Number of HRTF directions |
[in] | N_bands | Number of frequency bands/bins |
[in] | method | Decoder method (see BINAURAL_AMBI_DECODER_METHODS enum) |
[in] | order | Decoding order |
[in] | freqVector | Only needed for BINAURAL_DECODER_TA or BINAURAL_DECODER_MAGLS decoders (set to NULL if using a different method); N_bands x 1 |
[in] | itd_s | Only needed for BINAURAL_DECODER_TA decoder (set to NULL if using different method); N_dirs x 1 |
[in] | weights | Integration weights (set to NULL if not available); N_dirs x 1 |
[in] | enableDiffCM | Set to '0' to disable diffuse correction, '1' to enable |
[in] | enableMaxrE | Set to '0' to disable maxRE weighting, '1' to enable |
[out] | decMtx | Decoding matrices (one per frequency); FLAT: N_bands x NUM_EARS x (order+1)^2 |
void getLoudspeakerDecoderMtx | ( | float * | ls_dirs_deg, |
int | nLS, | ||
LOUDSPEAKER_AMBI_DECODER_METHODS | method, | ||
int | order, | ||
int | enableMaxrE, | ||
float * | decMtx ) |
Computes an ambisonic decoding matrix of a specific order, for a given loudspeaker layout.
[in] | ls_dirs_deg | Loudspeaker directions in DEGREES [azi elev]; FLAT: nLS x 2 |
[in] | nLS | Number of loudspeakers |
[in] | method | Decoding method (see LOUDSPEAKER_AMBI_DECODER_METHODS enum) |
[in] | order | Decoding order |
[in] | enableMaxrE | Set to '0' to disable, '1' to enable |
[out] | decMtx | Decoding matrix; FLAT: nLS x (order+1)^2 |
void getMaxREweights | ( | int | order, |
int | diagMtxFlag, | ||
float * | a_n ) |
Computes the weights required to manipulate a hyper-cardioid beam-pattern, such that it has maximum energy in the given look-direction.
Due to the side and back lobes of the beamformers employed by the Ambisonic decoder, when panning a source there can be unwanted energy given to loudspeakers directly opposite the true source direction. This max_rE weighting [1] essentially spatially tapers the spherical harmonic components used to generate the beamformers, thus reducing the contribution of the higher order components. This results in worse spatial selectivity, as the width of the beam pattern main lobe is widened, however, the back lobes are also reduced, thus mitigating perceptual issues that may arise due to the aforementioned problem.
[in] | order | Order of spherical harmonic expansion |
[in] | diagMtxFlag | Set to '0' if you want the weights to be returned as a vector, or to '1' as a diagonal matrix instead. |
[out] | a_n | The max_rE weights, as a vector/diagonal matrix; (order+1)^2 x 1 OR FLAT: (order+1)^2 x (order+1)^2 |
void getRSH | ( | int | order, |
float * | dirs_deg, | ||
int | nDirs, | ||
float * | Y ) |
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
The spherical harmonic values are computed WITHOUT the 1/sqrt(4*pi) term. Compared to getRSH_recur(), this function uses unnorm_legendreP() and double precision, so is more suitable for being computed in an initialisation stage. This version is indeed slower, but more precise (especially for high orders).
[in] | order | Order of spherical harmonic expansion |
[in] | dirs_deg | Directions on the sphere [azi, ELEVATION] convention, in DEGREES; FLAT: nDirs x 2 |
[in] | nDirs | Number of directions |
[out] | Y | The SH weights [WITHOUT the 1/sqrt(4*pi)]; FLAT: (order+1)^2 x nDirs |
void getRSH_recur | ( | int | order, |
float * | dirs_deg, | ||
int | nDirs, | ||
float * | Y ) |
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
The real spherical harmonics are computed WITHOUT the 1/sqrt(4*pi) term. Compared to getRSH(), this function uses unnorm_legendreP_recur() and single precision, so is more suitable for being computed in a real-time loop. It sacrifices some precision, and numerical error propogates through the recursion, but it is much faster.
The function also uses static memory buffers for single direction and up to 7th order, which speeds things up considerably for such use cases.
[in] | order | Order of spherical harmonic expansion |
[in] | dirs_deg | Directions on the sphere [azi, ELEVATION] convention, in DEGREES; FLAT: nDirs x 2 |
[in] | nDirs | Number of directions |
[out] | Y | The SH weights [WITHOUT the 1/sqrt(4*pi)]; FLAT: (order+1)^2 x nDirs |
void truncationEQ | ( | float * | w_n, |
int | order_truncated, | ||
int | order_target, | ||
double * | kr, | ||
int | nBands, | ||
float | softThreshold, | ||
float * | gain ) |
Filter that equalises the high frequency roll-off due to SH truncation and tapering; as described in [1].
[in] | w_n | Tapering weights; (order_truncated + 1) x 1 E.g. maxRE, or all ones for truncation only |
[in] | order_truncated | Input SH order |
[in] | order_target | Target SH order, (should be higher, e.g. 38) |
[in] | kr | kr vector, r e.g. 0.085 m; nBands x 1 |
[in] | nBands | Number of frequency bins |
[in] | softThreshold | Threshold in dB, soft limiting above to +6dB |
[out] | gain | Gain factor for compensation filter; nBands x 1 |