Home > SUpDEq-v5.0.0 > supdeq_interpHRTF.m

supdeq_interpHRTF

PURPOSE ^

% SUpDEq - Spatial Upsampling by Directional Equalization

SYNOPSIS ^

function [interpHRTFset, HRTF_L_ip, HRTF_R_ip] = supdeq_interpHRTF(HRTFset, ipSamplingGrid, ppMethod, ipMethod, mc, headRadius, tikhEps, limitMC, mcKnee, mcMinPhase, limFade)

DESCRIPTION ^

% SUpDEq - Spatial Upsampling by Directional Equalization

 function [interpHRTFset, HRTF_interp_L, HRTF_interp_R] = supdeq_interpHRTF(HRTFset, ipSamplingGrid, ppMethod, ipMethod, mc, headRadius, tikhEps, limitMC, mcKnee, mcMinPhase, limFade) 

 This function performs HRTF interpolation of the input HRTFset according
 to the passed interpolation sampling grid. Various interpolation and
 time-alignment (pre-processing) methods can be applied. Furthermore,
 post-interpolation magnitude correction for time-aligned interpolation
 (MCA) can be applied to further improve the interpolation results.

 Output:
 interpHRTFset         - Struct with the interpolated HRTF for the 
                         left (HRTF_L) and right (HRTF_R) ear, absolute 
                         frequency scale f, various interpolation parameters
                         according to the applied method, and FFToversize
 HRTF_L/R_ip           - Interpolated complex HRTFs (single-sided spectrum)

 Input:
 HRTFset               - Struct with (sparse) HRTF dataset in frequency
                         domain (single-sided complex spectra). Struct needs to
                         provide HRTF_L/HRTF_R, the samplingGrid, and Nmax
 ipSamplingGrid        - Spatial sampling grid (Q x 2 or Q x 3 matrix), 
                         defining the interpolation points, where the first 
                         column holds the azimuth, the second the elevation 
                         (both in degree), and optionally the third the 
                         sampling weights.
                         Azimuth in degree
                         (0=front, 90=left, 180=back, 270=right)
                         (0 points to positive x-axis, 90 to positive y-axis)
                         Elevation in degree
                         (0=North Pole, 90=front, 180=South Pole)
                         (0 points to positive z-axis, 180 to negative z-axis)
 ppMethod              - String defining the pre/post-processing applied
                         to the HRTFs before/after interpolation. Besides
                         'MagPhase', all of the pre-processing methods are
                         time-alignment approaches.
                         'SUpDEq'          - SUpDEq method [3][5] 
                         'SUpDEq_Lim'      - Modified SUpDeq method where the eqDataset is limited to frequencies above the spatial aliasing frequency fA [5]
                         'SUpDEq_AP'       - Modified SUpDEq method applying rigid sphere allpass filter (only the phase components of the rigid sphere transfer functions)
                         'SUpDEq_Lim_AP'   - Modified SUpDeq method where the eqDataset is limited as above (SUpDEq_Lim) and only the phase components are used (SUpDEq_AP)
                         'PC'              - Phase-Correction (open sphere transfer functions) [4][5]
                         'OBTA'            - Onset-Based Time-Alignment [1][2][5]
                         'MagPhase'        - Split HRTF to magnitude and unwrapped phase - No time-alignment
                         'None'            - Perform interpolation without any pre/post-processing 
                          Default: 'SUpDEq'
 ipMethod              - String defining the ipMethod
                         'SH'      - Spherical harmonics interpolation [1-5]
                         'NN'      - Natural neighbor interpolation with Voronoi weights [6]
                         'Bary'    - Barycentric interpolation [7]
                          Default: 'SH'
 mc                     - Define maximum boost in dB if magnitude correction according to [8] should be applied to interpolated HRTFs in a further postprocessing step
                          Set to nan if magnitude correction should not be applied
                          Set to inf if no limiting should be applied, i.e., unlimited boost
                          Default: inf
 headRadius             - Head radius in m. Required for time-alignment methods SUpDEq [3][5] and PC [4][5]
                          Default: 0.0875
 tikhEps                - Define epsilon of Tikhonov regularization if regularization should be applied
                          Applying the Tikhonov regularization will always result in a least-square fit 
                          solution for the SH transform. Only relevant if ipMethod is 'SH'.
                          Default: 0 (no Tikhonov regularization)
 limitMC                - Boolean to set magnitude correction filters to 0 dB below spatial aliasing frequency fA, 
                          assuming that (SH) interpolation below fA is physically correct. 
                          Default: true (correction filter operate only above fA)
                          Only applies if 'mc' is not nan
 mcKnee                 - Knee in dB of the soft-limiting applied to the correction filters. Soft-limiting is applied according to maximum boost 'mc' 
                          Only applies if 'mc' is not nan or inf
                          Default: 0 dB (no knee)
 mcMinPhase             - Boolean to design correction filters as minimum-phase filters. If set to false, correction filters are zero-phase filters
                          Default: true (minimum-phase filters) as this provides a better performance in terms of ITD errors in combination with classical SUpDEq processing
                          When applying other preprocessing methods, using zero-phase filters can be more appropriate. 
 limFade                - String to define whether to apply a linear fade upwards from aliasing frequency fA to fAt = fA + 1/3 Oct, or downwards, i.e., from fA to fAt = fA - 1/3 Oct
                          'fadeUp' - upwards
                          'fadeDown' - downwards
                           Default: 'fadeDown'

 Dependencies: SUpDEq toolbox, AKtools, SOFiA toolbox, SFS toolbox, TriangleRayIntersection

 References: (Pre-Processing methods)
 Onset-Based Time-Alignment (OBTA)
 [1] M. J. Evans, J. A. S. Angus, and A. I. Tew, "Analyzing head-related transfer function measurements 
 using surface spherical harmonics,"
 J. Acoust. Soc. Am., vol. 104, no. 4, pp. 2400-2411, 1998.
 [2] F. Brinkmann and S. Weinzierl, "Comparison of head-related transfer functions pre-processing 
 techniques for spherical harmonics decomposition,"
 in Proceedings of the AES Conference on Audio for Virtual and Augmented Reality, 2018, pp. 1-10.

 Spatial Upsampling by Directional Equalization (SUpDEq)
 [3] C. Pörschmann, J. M. Arend, and F. Brinkmann, 
 "Directional Equalization of Sparse Head-Related Transfer Function Sets for Spatial Upsampling," 
 IEEE/ACM Trans. Audio, Speech, Lang. Process., vol. 27, no. 6, pp. 1060-1071, 2019.

 Phase-Correction (PC)
 [4] Z. Ben-Hur, D. Lou Alon, R. Mehra, and B. Rafaely, 
 "Efficient Representation and Sparse Sampling of Head-Related Transfer Functions 
 Using Phase-Correction Based on Ear Alignment," 
 IEEE/ACM Trans. Audio, Speech, Lang. Process., vol. 27, no. 12, pp. 2249-2262, 2019.

 Comparison of time-alignment methods & perceptual evaluation
 [5] J. M. Arend, F. Brinkmann, and C. Pörschmann, “Assessing Spherical 
 Harmonics Interpolation of Time-Aligned Head-Related Transfer Functions,” 
 J. Audio Eng. Soc., vol. 69, no. 1/2, pp. 104–117, 2021.
 
 Spherical harmonics interpolation
 [1-5]

 Natural-Neighbor Interpolation
 [6] R. Sibson, “A brief description of natural neighbor interpolation,” 
 in Interpreting Multivariate Data, V. Barnett, Chichester, England: John Wiley & Sons, 1981, pp. 21–36.
 Good read about NN interpolation: https://github.com/gwlucastrig/Tinfour/wiki/Introduction-to-Natural-Neighbor-Interpolation
 
 Barycentric Coordinates / Interpolation
 [7] P. Shirley and S. Marschner, Fundamentals of Computer Graphics, 3rd ed. Boca Raton, FL: Taylor & Francis, 2009.
 Good read about Barycentric interpolation: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/barycentric-coordinates
 
 Magnitude-Corrected and Time-Aligned Interpolation (MCA)
 [8] J. M. Arend, C. Pörschmann, S. Weinzierl, F. Brinkmann, 
 "Magnitude-Corrected and Time-Aligned Interpolation of Head-Related Transfer Functions," 
 (Manuscript submitted for publication).

 (C) 2020-2022 by JMA, Johannes M. Arend
               TU Berlin, Audio Communication Group
               TH Köln, Institute of Communications Engineering

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 %% SUpDEq - Spatial Upsampling by Directional Equalization
0002 %
0003 % function [interpHRTFset, HRTF_interp_L, HRTF_interp_R] = supdeq_interpHRTF(HRTFset, ipSamplingGrid, ppMethod, ipMethod, mc, headRadius, tikhEps, limitMC, mcKnee, mcMinPhase, limFade)
0004 %
0005 % This function performs HRTF interpolation of the input HRTFset according
0006 % to the passed interpolation sampling grid. Various interpolation and
0007 % time-alignment (pre-processing) methods can be applied. Furthermore,
0008 % post-interpolation magnitude correction for time-aligned interpolation
0009 % (MCA) can be applied to further improve the interpolation results.
0010 %
0011 % Output:
0012 % interpHRTFset         - Struct with the interpolated HRTF for the
0013 %                         left (HRTF_L) and right (HRTF_R) ear, absolute
0014 %                         frequency scale f, various interpolation parameters
0015 %                         according to the applied method, and FFToversize
0016 % HRTF_L/R_ip           - Interpolated complex HRTFs (single-sided spectrum)
0017 %
0018 % Input:
0019 % HRTFset               - Struct with (sparse) HRTF dataset in frequency
0020 %                         domain (single-sided complex spectra). Struct needs to
0021 %                         provide HRTF_L/HRTF_R, the samplingGrid, and Nmax
0022 % ipSamplingGrid        - Spatial sampling grid (Q x 2 or Q x 3 matrix),
0023 %                         defining the interpolation points, where the first
0024 %                         column holds the azimuth, the second the elevation
0025 %                         (both in degree), and optionally the third the
0026 %                         sampling weights.
0027 %                         Azimuth in degree
0028 %                         (0=front, 90=left, 180=back, 270=right)
0029 %                         (0 points to positive x-axis, 90 to positive y-axis)
0030 %                         Elevation in degree
0031 %                         (0=North Pole, 90=front, 180=South Pole)
0032 %                         (0 points to positive z-axis, 180 to negative z-axis)
0033 % ppMethod              - String defining the pre/post-processing applied
0034 %                         to the HRTFs before/after interpolation. Besides
0035 %                         'MagPhase', all of the pre-processing methods are
0036 %                         time-alignment approaches.
0037 %                         'SUpDEq'          - SUpDEq method [3][5]
0038 %                         'SUpDEq_Lim'      - Modified SUpDeq method where the eqDataset is limited to frequencies above the spatial aliasing frequency fA [5]
0039 %                         'SUpDEq_AP'       - Modified SUpDEq method applying rigid sphere allpass filter (only the phase components of the rigid sphere transfer functions)
0040 %                         'SUpDEq_Lim_AP'   - Modified SUpDeq method where the eqDataset is limited as above (SUpDEq_Lim) and only the phase components are used (SUpDEq_AP)
0041 %                         'PC'              - Phase-Correction (open sphere transfer functions) [4][5]
0042 %                         'OBTA'            - Onset-Based Time-Alignment [1][2][5]
0043 %                         'MagPhase'        - Split HRTF to magnitude and unwrapped phase - No time-alignment
0044 %                         'None'            - Perform interpolation without any pre/post-processing
0045 %                          Default: 'SUpDEq'
0046 % ipMethod              - String defining the ipMethod
0047 %                         'SH'      - Spherical harmonics interpolation [1-5]
0048 %                         'NN'      - Natural neighbor interpolation with Voronoi weights [6]
0049 %                         'Bary'    - Barycentric interpolation [7]
0050 %                          Default: 'SH'
0051 % mc                     - Define maximum boost in dB if magnitude correction according to [8] should be applied to interpolated HRTFs in a further postprocessing step
0052 %                          Set to nan if magnitude correction should not be applied
0053 %                          Set to inf if no limiting should be applied, i.e., unlimited boost
0054 %                          Default: inf
0055 % headRadius             - Head radius in m. Required for time-alignment methods SUpDEq [3][5] and PC [4][5]
0056 %                          Default: 0.0875
0057 % tikhEps                - Define epsilon of Tikhonov regularization if regularization should be applied
0058 %                          Applying the Tikhonov regularization will always result in a least-square fit
0059 %                          solution for the SH transform. Only relevant if ipMethod is 'SH'.
0060 %                          Default: 0 (no Tikhonov regularization)
0061 % limitMC                - Boolean to set magnitude correction filters to 0 dB below spatial aliasing frequency fA,
0062 %                          assuming that (SH) interpolation below fA is physically correct.
0063 %                          Default: true (correction filter operate only above fA)
0064 %                          Only applies if 'mc' is not nan
0065 % mcKnee                 - Knee in dB of the soft-limiting applied to the correction filters. Soft-limiting is applied according to maximum boost 'mc'
0066 %                          Only applies if 'mc' is not nan or inf
0067 %                          Default: 0 dB (no knee)
0068 % mcMinPhase             - Boolean to design correction filters as minimum-phase filters. If set to false, correction filters are zero-phase filters
0069 %                          Default: true (minimum-phase filters) as this provides a better performance in terms of ITD errors in combination with classical SUpDEq processing
0070 %                          When applying other preprocessing methods, using zero-phase filters can be more appropriate.
0071 % limFade                - String to define whether to apply a linear fade upwards from aliasing frequency fA to fAt = fA + 1/3 Oct, or downwards, i.e., from fA to fAt = fA - 1/3 Oct
0072 %                          'fadeUp' - upwards
0073 %                          'fadeDown' - downwards
0074 %                           Default: 'fadeDown'
0075 %
0076 % Dependencies: SUpDEq toolbox, AKtools, SOFiA toolbox, SFS toolbox, TriangleRayIntersection
0077 %
0078 % References: (Pre-Processing methods)
0079 % Onset-Based Time-Alignment (OBTA)
0080 % [1] M. J. Evans, J. A. S. Angus, and A. I. Tew, "Analyzing head-related transfer function measurements
0081 % using surface spherical harmonics,"
0082 % J. Acoust. Soc. Am., vol. 104, no. 4, pp. 2400-2411, 1998.
0083 % [2] F. Brinkmann and S. Weinzierl, "Comparison of head-related transfer functions pre-processing
0084 % techniques for spherical harmonics decomposition,"
0085 % in Proceedings of the AES Conference on Audio for Virtual and Augmented Reality, 2018, pp. 1-10.
0086 %
0087 % Spatial Upsampling by Directional Equalization (SUpDEq)
0088 % [3] C. Pörschmann, J. M. Arend, and F. Brinkmann,
0089 % "Directional Equalization of Sparse Head-Related Transfer Function Sets for Spatial Upsampling,"
0090 % IEEE/ACM Trans. Audio, Speech, Lang. Process., vol. 27, no. 6, pp. 1060-1071, 2019.
0091 %
0092 % Phase-Correction (PC)
0093 % [4] Z. Ben-Hur, D. Lou Alon, R. Mehra, and B. Rafaely,
0094 % "Efficient Representation and Sparse Sampling of Head-Related Transfer Functions
0095 % Using Phase-Correction Based on Ear Alignment,"
0096 % IEEE/ACM Trans. Audio, Speech, Lang. Process., vol. 27, no. 12, pp. 2249-2262, 2019.
0097 %
0098 % Comparison of time-alignment methods & perceptual evaluation
0099 % [5] J. M. Arend, F. Brinkmann, and C. Pörschmann, “Assessing Spherical
0100 % Harmonics Interpolation of Time-Aligned Head-Related Transfer Functions,”
0101 % J. Audio Eng. Soc., vol. 69, no. 1/2, pp. 104–117, 2021.
0102 %
0103 % Spherical harmonics interpolation
0104 % [1-5]
0105 %
0106 % Natural-Neighbor Interpolation
0107 % [6] R. Sibson, “A brief description of natural neighbor interpolation,”
0108 % in Interpreting Multivariate Data, V. Barnett, Chichester, England: John Wiley & Sons, 1981, pp. 21–36.
0109 % Good read about NN interpolation: https://github.com/gwlucastrig/Tinfour/wiki/Introduction-to-Natural-Neighbor-Interpolation
0110 %
0111 % Barycentric Coordinates / Interpolation
0112 % [7] P. Shirley and S. Marschner, Fundamentals of Computer Graphics, 3rd ed. Boca Raton, FL: Taylor & Francis, 2009.
0113 % Good read about Barycentric interpolation: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/barycentric-coordinates
0114 %
0115 % Magnitude-Corrected and Time-Aligned Interpolation (MCA)
0116 % [8] J. M. Arend, C. Pörschmann, S. Weinzierl, F. Brinkmann,
0117 % "Magnitude-Corrected and Time-Aligned Interpolation of Head-Related Transfer Functions,"
0118 % (Manuscript submitted for publication).
0119 %
0120 % (C) 2020-2022 by JMA, Johannes M. Arend
0121 %               TU Berlin, Audio Communication Group
0122 %               TH Köln, Institute of Communications Engineering
0123 
0124 function [interpHRTFset, HRTF_L_ip, HRTF_R_ip] = supdeq_interpHRTF(HRTFset, ipSamplingGrid, ppMethod, ipMethod, mc, headRadius, tikhEps, limitMC, mcKnee, mcMinPhase, limFade) 
0125 
0126 
0127 if nargin < 3 || isempty(ppMethod)
0128     ppMethod = 'SUpDEq';
0129 end
0130 
0131 if nargin < 4 || isempty(ipMethod)
0132     ipMethod = 'SH';
0133 end
0134 
0135 if nargin < 5 || isempty(mc)
0136     mc = inf;
0137 end
0138 
0139 if nargin < 6 || isempty(headRadius)
0140     headRadius = 0.0875;
0141 end
0142 
0143 if nargin < 7 || isempty(tikhEps)
0144     tikhEps = 0;
0145 end
0146 
0147 if nargin < 8 || isempty(limitMC)
0148     limitMC = true;
0149 end
0150 
0151 if nargin < 9 || isempty(mcKnee)
0152     mcKnee = 0;
0153 end
0154 
0155 if nargin < 10 || isempty(mcMinPhase)
0156     mcMinPhase = true;
0157 end
0158 
0159 if nargin < 11 || isempty(limFade)
0160     limFade = 'fadeDown';
0161 end
0162    
0163 %% Get some required variables
0164 
0165 fs = HRTFset.f(end)*2;
0166 f = HRTFset.f;
0167 NFFT = length(HRTFset.f)*2-2;
0168 samplingGrid = HRTFset.samplingGrid;
0169 HRTF_L = HRTFset.HRTF_L;
0170 HRTF_R = HRTFset.HRTF_R;
0171 c = 343;
0172 
0173 %Get HRIRs - Required for threshold detection and mc
0174 %Get mirror spectrum
0175 HRIR_L = AKsingle2bothSidedSpectrum(HRTF_L.');
0176 HRIR_R = AKsingle2bothSidedSpectrum(HRTF_R.');
0177 %Transform back to time domain
0178 HRIR_L = real(ifft(HRIR_L));
0179 HRIR_R = real(ifft(HRIR_R));
0180 hrirLength = size(HRIR_L,1)/HRTFset.FFToversize;
0181 
0182 %% Perform preprocessing
0183 
0184 switch ppMethod
0185     case 'SUpDEq'
0186         disp('Preprocessing: SUpDEq')
0187         %Apply SUpDEq - Equalize HRTFs
0188 
0189         %Get eqDataset at N = 44
0190         eqDataset = supdeq_getEqDataset(44,2*headRadius,NFFT,fs);
0191 
0192         %Equalization
0193         [eqTF_L,eqTF_R] = supdeq_getEqTF(eqDataset,samplingGrid,'DEG',2,'ak',false);
0194         pHRTF_L = HRTF_L./eqTF_L;
0195         pHRTF_R = HRTF_R./eqTF_R;
0196         
0197     case 'SUpDEq_Lim'
0198         disp('Preprocessing: SUpDEq_Lim')
0199         %Apply SUpDEq - Equalize HRTFs
0200 
0201         %Get eqDataset at N = 44
0202         eqDataset = supdeq_getEqDataset(44,2*headRadius,NFFT,fs);
0203         
0204         %Limit eqDataset (Small improvement explained in [5] leading to better
0205         %results below the spatial aliasing frequency fA)
0206         eqDataset = supdeq_limitEqDataset(eqDataset,HRTFset.Nmax,headRadius);
0207 
0208         %Equalization
0209         [eqTF_L,eqTF_R] = supdeq_getEqTF(eqDataset,samplingGrid,'DEG',2,'ak',false);
0210         pHRTF_L = HRTF_L./eqTF_L;
0211         pHRTF_R = HRTF_R./eqTF_R;
0212 
0213     case 'SUpDEq_AP'
0214         disp('Preprocessing: SUpDEq_AP')
0215         %Apply SUpDEq_AP - Equalize HRTFs with rigid sphere allpass functions
0216 
0217         %Get eqDataset at N = 44
0218         eqDataset = supdeq_getEqDataset(44,2*headRadius,NFFT,fs);
0219 
0220         %Equalization
0221         [eqTF_L,eqTF_R] = supdeq_getEqTF(eqDataset,samplingGrid,'DEG',2,'ak',true);
0222         pHRTF_L = HRTF_L./eqTF_L;
0223         pHRTF_R = HRTF_R./eqTF_R;
0224         
0225     case 'SUpDEq_Lim_AP'
0226         disp('Preprocessing: SUpDEq_Lim_AP')
0227         %Apply SUpDEq_AP - Equalize HRTFs with limited rigid sphere allpass functions
0228 
0229         %Get eqDataset at N = 44
0230         eqDataset = supdeq_getEqDataset(44,2*headRadius,NFFT,fs);
0231         
0232         %Limit eqDataset (Small improvement explained in [5] leading to better
0233         %results below the spatial aliasing frequency fA)
0234         eqDataset = supdeq_limitEqDataset(eqDataset,HRTFset.Nmax,headRadius);
0235 
0236         %Equalization
0237         [eqTF_L,eqTF_R] = supdeq_getEqTF(eqDataset,samplingGrid,'DEG',2,'ak',true);
0238         pHRTF_L = HRTF_L./eqTF_L;
0239         pHRTF_R = HRTF_R./eqTF_R;
0240 
0241     case 'PC'
0242         disp('Preprocessing: PC')
0243         %Apply Phase-Correction - Equalize HRTFs with open sphere allpass functions
0244 
0245         %Get wavenumber k
0246         k  = 2*pi*f/c; k = k.';
0247 
0248         %Transform sampling grid to radiant
0249         sg = samplingGrid * pi / 180;
0250 
0251         %Get phase correction term for left/right ear according to Eq. 13 in [4]
0252         cosThetaL = cos(sg(:,2)')*cos(pi/2) + sin(sg(:,2)')*sin(pi/2) .* cos(sg(:,1)'-pi/2); %Left ear with -pi/2
0253         phaseCorL = exp(-1j*headRadius * k .* cosThetaL);
0254         cosThetaR = cos(sg(:,2)')*cos(pi/2) + sin(sg(:,2)')*sin(pi/2) .* cos(sg(:,1)'+pi/2); %Right ear with +pi/2
0255         phaseCorR = exp(-1j*headRadius * k .* cosThetaR);
0256 
0257         %Apply to HRTFs
0258         pHRTF_L = HRTF_L .* phaseCorL.'; %Just transpose, no conjugate complex
0259         pHRTF_R = HRTF_R .* phaseCorR.';
0260 
0261     case 'OBTA' 
0262         disp('Preprocessing: OBTA')
0263         %Apply Onset-Based Time-Alignment with fractional delay filters
0264 
0265         %Estimate TOA on low-passed and 10 times upsampled HRIRs according to [2]
0266         toa_L = AKonsetDetect(HRIR_L, 10, -20, 'rel', [3e3 fs]);
0267         toa_R = AKonsetDetect(HRIR_R, 10, -20, 'rel', [3e3 fs]);
0268 
0269         %Time-Align HRIRs (remove TOAs) with fractional delay
0270         pHRIR_L = AKfractionalDelayCyclic(HRIR_L, -toa_L);
0271         pHRIR_R = AKfractionalDelayCyclic(HRIR_R, -toa_R);
0272 
0273         %Transform to Fourier domain with same FFToversize as defined in struct
0274         pHRTF_L = AKboth2singleSidedSpectrum (fft(pHRIR_L)).';
0275         pHRTF_R = AKboth2singleSidedSpectrum (fft(pHRIR_R)).';
0276         
0277     case 'MagPhase'
0278         disp('Preprocessing: MagPhase')
0279         
0280         pHRTF_L = abs(HRTF_L);
0281         pHRTF_R = abs(HRTF_R);
0282         pHRTF_L_ph = unwrap(angle(HRTF_L).').';
0283         pHRTF_R_ph = unwrap(angle(HRTF_R).').';
0284         
0285     case 'None' 
0286         disp('Preprocessing: None')
0287 
0288         %Simply copy HRTF to pHRTF_L - Easier for further processing
0289         pHRTF_L = HRTF_L;
0290         pHRTF_R = HRTF_R;
0291 end
0292 
0293 %Save intermediate result (pre-processed HRTFs) in output struct
0294 interpHRTFset.p.pHRTF_L = pHRTF_L;
0295 interpHRTFset.p.pHRTF_R = pHRTF_R;
0296 if strcmp(ppMethod,'OBTA')
0297     interpHRTFset.p.toa_L = toa_L;
0298     interpHRTFset.p.toa_R = toa_R;
0299 end
0300 if strcmp(ppMethod,'MagPhase')
0301     interpHRTFset.p.pHRTF_L_ph = pHRTF_L_ph;
0302     interpHRTFset.p.pHRTF_R_ph = pHRTF_R_ph;
0303 end
0304 
0305 %% Perform interpolation
0306 
0307 switch ipMethod
0308     case 'SH'
0309         disp('HRTF Interpolation: SH')
0310         
0311         %Transform preprocessed HRTFs to SH domain
0312         %Tikhonov regularization can be applied if weights in sampling grid are erased and tikhEps ~= 0
0313         pHRTF_L_nm = AKsht(pHRTF_L.',false,samplingGrid,HRTFset.Nmax,'complex',fs,false,'complex',tikhEps);
0314         pHRTF_R_nm = AKsht(pHRTF_R.',false,samplingGrid,HRTFset.Nmax,'complex',fs,false,'complex',tikhEps);
0315         
0316         %Apply SH interpolation to ipSamplingGrid
0317         pHRTF_L_ip = AKisht(pHRTF_L_nm,false,ipSamplingGrid(:,1:2),'complex').';
0318         pHRTF_R_ip = AKisht(pHRTF_R_nm,false,ipSamplingGrid(:,1:2),'complex').';
0319         
0320         %Also interpolate TOAs if ppMethod = 'OBTA'
0321         if strcmp(ppMethod,'OBTA')
0322             
0323             %Transform TOAs to SH domain
0324             %Tikhonov regularization can be applied if weights in sampling grid are erased and tikhEps ~= 0
0325             toa_L_nm = AKsht(toa_L,false,samplingGrid,HRTFset.Nmax,'complex',fs,false,'real',tikhEps);
0326             toa_R_nm = AKsht(toa_R,false,samplingGrid,HRTFset.Nmax,'complex',fs,false,'real',tikhEps);
0327 
0328             %Apply SH interpolation to ipSamplingGrid
0329             toa_L_ip = AKisht(toa_L_nm, false, ipSamplingGrid(:,1:2), 'complex', false, false, 'real');
0330             toa_R_ip = AKisht(toa_R_nm, false, ipSamplingGrid(:,1:2), 'complex', false, false, 'real');
0331             
0332         end
0333         
0334         %Also interpolate Phase if ppMethod = 'MagPhase'
0335         if strcmp(ppMethod,'MagPhase')
0336             
0337             %Transform Phase to SH domain
0338             %Tikhonov regularization can be applied if weights in sampling grid are erased and tikhEps ~= 0
0339             pHRTF_L_ph_nm = AKsht(pHRTF_L_ph.',false,samplingGrid,HRTFset.Nmax,'complex',fs,false,'complex',tikhEps);
0340             pHRTF_R_ph_nm = AKsht(pHRTF_R_ph.',false,samplingGrid,HRTFset.Nmax,'complex',fs,false,'complex',tikhEps);
0341 
0342             %Apply SH interpolation to ipSamplingGrid
0343             pHRTF_L_ph_ip = AKisht(pHRTF_L_ph_nm,false,ipSamplingGrid(:,1:2),'complex').';
0344             pHRTF_R_ph_ip = AKisht(pHRTF_R_ph_nm,false,ipSamplingGrid(:,1:2),'complex').';
0345   
0346         end
0347         
0348     case 'NN'
0349         disp('HRTF Interpolation: NN')
0350         
0351         %Transform grids to cartesian coordinates
0352         [samplingGrid_cart(:,1), samplingGrid_cart(:,2), samplingGrid_cart(:,3)] = sph2cart(samplingGrid(:,1)/360*2*pi, pi/2-samplingGrid(:,2)/360*2*pi,1);
0353         [ipSamplingGrid_cart(:,1), ipSamplingGrid_cart(:,2), ipSamplingGrid_cart(:,3)] = sph2cart(ipSamplingGrid(:,1)/360*2*pi, pi/2-ipSamplingGrid(:,2)/360*2*pi,1);
0354         
0355         %NN interpolate preprocessed HRTFs to ipSamplingGrid
0356         pHRTF_L_ip = zeros(length(ipSamplingGrid),length(f));
0357         pHRTF_R_ip = zeros(length(ipSamplingGrid),length(f));
0358         
0359         for nPoint = 1:size(ipSamplingGrid,1)
0360 
0361             [idx_voronoi,w_voronoi] = findvoronoi(samplingGrid_cart,ipSamplingGrid_cart(nPoint,:));
0362 
0363             for n = 1:length(idx_voronoi) 
0364                 pHRTF_L_ip(nPoint,:) = pHRTF_L_ip(nPoint,:) + w_voronoi(n)*pHRTF_L(idx_voronoi(n),:);  
0365                 pHRTF_R_ip(nPoint,:) = pHRTF_R_ip(nPoint,:) + w_voronoi(n)*pHRTF_R(idx_voronoi(n),:);
0366             end
0367         end
0368         
0369         %Also interpolate TOAs if ppMethod = 'OBTA'
0370         if strcmp(ppMethod,'OBTA')
0371             
0372             toa_L_ip = zeros(1,length(ipSamplingGrid));
0373             toa_R_ip = zeros(1,length(ipSamplingGrid));
0374             
0375             for nPoint = 1:size(ipSamplingGrid,1)
0376 
0377                 [idx_voronoi,w_voronoi] = findvoronoi(samplingGrid_cart,ipSamplingGrid_cart(nPoint,:));
0378 
0379                 for n = 1:length(idx_voronoi) 
0380                     toa_L_ip(1,nPoint) = toa_L_ip(1,nPoint) + w_voronoi(n)*toa_L(1,idx_voronoi(n));  
0381                     toa_R_ip(1,nPoint) = toa_R_ip(1,nPoint) + w_voronoi(n)*toa_R(1,idx_voronoi(n));
0382                 end
0383             end
0384         end
0385         
0386         %Also interpolate Phase if ppMethod = 'MagPhase'
0387         if strcmp(ppMethod,'MagPhase')  
0388             
0389             pHRTF_L_ph_ip = zeros(length(ipSamplingGrid),length(f));
0390             pHRTF_R_ph_ip = zeros(length(ipSamplingGrid),length(f));
0391 
0392             for nPoint = 1:size(ipSamplingGrid,1)
0393 
0394                 [idx_voronoi,w_voronoi] = findvoronoi(samplingGrid_cart,ipSamplingGrid_cart(nPoint,:));
0395 
0396                 for n = 1:length(idx_voronoi) 
0397                     pHRTF_L_ph_ip(nPoint,:) = pHRTF_L_ph_ip(nPoint,:) + w_voronoi(n)*pHRTF_L_ph(idx_voronoi(n),:);  
0398                     pHRTF_R_ph_ip(nPoint,:) = pHRTF_R_ph_ip(nPoint,:) + w_voronoi(n)*pHRTF_R_ph(idx_voronoi(n),:);
0399                 end
0400             end 
0401         end
0402 
0403             
0404     case 'Bary'
0405         disp('HRTF Interpolation: Bary')
0406         
0407         %Transform grids to cartesian coordinates
0408         [samplingGrid_cart(:,1), samplingGrid_cart(:,2), samplingGrid_cart(:,3)] = sph2cart(samplingGrid(:,1)/360*2*pi, pi/2-samplingGrid(:,2)/360*2*pi,1);
0409         [ipSamplingGrid_cart(:,1), ipSamplingGrid_cart(:,2), ipSamplingGrid_cart(:,3)] = sph2cart(ipSamplingGrid(:,1)/360*2*pi, pi/2-ipSamplingGrid(:,2)/360*2*pi,1);
0410         
0411         %Get simplified convex hull (could also be done after delaunay-triangulation)
0412         convHullIdx = convhull(samplingGrid_cart(:,1), samplingGrid_cart(:,2), samplingGrid_cart(:,3), 'simplify', true);
0413         
0414         %Get HRTFs / directions assigned to convex hull triangles
0415         convHullHRTF_A = samplingGrid_cart(convHullIdx(:,1),:);
0416         convHullHRTF_B = samplingGrid_cart(convHullIdx(:,2),:);
0417         convHullHRTF_C = samplingGrid_cart(convHullIdx(:,3),:);
0418         
0419         %Interpolate with barycentric weights
0420         %Right triangle of convex hull picked by ray-triangle intersection test (intersectLinePolygon3d from geom3D toolbox should work too)
0421         %Function returns barycentric weights / coordinates u and v of the intersection point -> w = 1-u-v
0422         %P = w*A + u*B + v*C
0423         orig = [0 0 0];
0424         pHRTF_L_ip = zeros(length(ipSamplingGrid),length(f));
0425         pHRTF_R_ip = zeros(length(ipSamplingGrid),length(f));
0426         
0427         for nPoint = 1:size(ipSamplingGrid,1)
0428 
0429             [intersect, ~, u, v, ~] = TriangleRayIntersection(orig, ipSamplingGrid_cart(nPoint,:), convHullHRTF_A, convHullHRTF_B, convHullHRTF_C, 'border', 'inclusive');
0430             intersectIdx = find(intersect, 1, 'first'); %Evaluate first intersection point
0431             u = u(intersectIdx); v = v(intersectIdx); w = 1-u-v;
0432             bw = [w u v]; %Barycentric weights
0433             idx_bw = convHullIdx(intersectIdx,:); %Indizes of HRTFs of convex hull / triangles
0434 
0435             for n = 1:3 %Always 3 because of triangulization
0436                 pHRTF_L_ip(nPoint,:) = pHRTF_L_ip(nPoint,:) + bw(n)*pHRTF_L(idx_bw(n),:);  
0437                 pHRTF_R_ip(nPoint,:) = pHRTF_R_ip(nPoint,:) + bw(n)*pHRTF_R(idx_bw(n),:);
0438             end
0439         end
0440         
0441         %Also interpolate TOAs if ppMethod = 'OBTA'
0442         if strcmp(ppMethod,'OBTA')
0443             
0444             toa_L_ip = zeros(1,length(ipSamplingGrid));
0445             toa_R_ip = zeros(1,length(ipSamplingGrid));
0446             
0447             for nPoint = 1:size(ipSamplingGrid,1)
0448 
0449                 [intersect, ~, u, v, ~] = TriangleRayIntersection(orig, ipSamplingGrid_cart(nPoint,:), convHullHRTF_A, convHullHRTF_B, convHullHRTF_C, 'border', 'inclusive');
0450                 intersectIdx = find(intersect, 1, 'first'); %Evaluate first intersection point
0451                 u = u(intersectIdx); v = v(intersectIdx); w = 1-u-v;
0452                 bw = [w u v]; %Barycentric weights
0453                 idx_bw = convHullIdx(intersectIdx,:); %Indizes of HRTFs of convex hull / triangles
0454 
0455                 for n = 1:3 %Always 3 because of triangulization
0456                     toa_L_ip(1,nPoint) = toa_L_ip(1,nPoint) + bw(n)*toa_L(1,idx_bw(n));  
0457                     toa_R_ip(1,nPoint) = toa_R_ip(1,nPoint) + bw(n)*toa_R(1,idx_bw(n));
0458                 end
0459             end
0460         end
0461         
0462         %Also interpolate Phase if ppMethod = 'MagPhase'
0463         if strcmp(ppMethod,'MagPhase')  
0464             
0465             pHRTF_L_ph_ip = zeros(length(ipSamplingGrid),length(f));
0466             pHRTF_R_ph_ip = zeros(length(ipSamplingGrid),length(f));
0467 
0468             for nPoint = 1:size(ipSamplingGrid,1)
0469 
0470                 [intersect, ~, u, v, ~] = TriangleRayIntersection(orig, ipSamplingGrid_cart(nPoint,:), convHullHRTF_A, convHullHRTF_B, convHullHRTF_C, 'border', 'inclusive');
0471                 intersectIdx = find(intersect, 1, 'first'); %Evaluate first intersection point
0472                 u = u(intersectIdx); v = v(intersectIdx); w = 1-u-v;
0473                 bw = [w u v]; %Barycentric weights
0474                 idx_bw = convHullIdx(intersectIdx,:); %Indizes of HRTFs of convex hull / triangles
0475 
0476                 for n = 1:3 %Always 3 because of triangulization
0477                     pHRTF_L_ph_ip(nPoint,:) = pHRTF_L_ph_ip(nPoint,:) + bw(n)*pHRTF_L_ph(idx_bw(n),:);  
0478                     pHRTF_R_ph_ip(nPoint,:) = pHRTF_R_ph_ip(nPoint,:) + bw(n)*pHRTF_R_ph(idx_bw(n),:);
0479                 end
0480             end
0481         end
0482              
0483 end
0484 
0485 %Save intermediate result (interpolated pre-processed HRTFs) in output struct
0486 interpHRTFset.p.pHRTF_L_ip = pHRTF_L_ip;
0487 interpHRTFset.p.pHRTF_R_ip = pHRTF_R_ip;
0488 if strcmp(ppMethod,'OBTA')
0489     interpHRTFset.p.toa_L_ip = toa_L_ip;
0490     interpHRTFset.p.toa_R_ip = toa_R_ip;
0491 end
0492 if strcmp(ppMethod,'MagPhase')
0493     interpHRTFset.p.pHRTF_L_ph_ip = pHRTF_L_ph_ip;
0494     interpHRTFset.p.pHRTF_R_ph_ip = pHRTF_R_ph_ip;
0495 end
0496 
0497 %% Perform postprocessing
0498 
0499 switch ppMethod
0500     case 'SUpDEq'
0501         disp('Postprocessing: SUpDEq')
0502         %Apply SUpDEq - De-Equalize interpolated HRTFs
0503 
0504         %Get De-Equalization functions
0505         [deqTF_L,deqTF_R] = supdeq_getEqTF(eqDataset,ipSamplingGrid,'DEG',2,'ak',false);
0506         
0507         %De-Equalize
0508         HRTF_L_ip = pHRTF_L_ip.*deqTF_L;
0509         HRTF_R_ip = pHRTF_R_ip.*deqTF_R;
0510        
0511     case 'SUpDEq_Lim'
0512         disp('Postprocessing: SUpDEq_Lim')
0513         %Apply SUpDEq - De-Equalize interpolated HRTFs
0514 
0515         %Get De-Equalization functions of limited eqDataset
0516         [deqTF_L,deqTF_R] = supdeq_getEqTF(eqDataset,ipSamplingGrid,'DEG',2,'ak',false);
0517         
0518         %De-Equalize
0519         HRTF_L_ip = pHRTF_L_ip.*deqTF_L;
0520         HRTF_R_ip = pHRTF_R_ip.*deqTF_R;
0521         
0522     case 'SUpDEq_AP'
0523         disp('Postprocessing: SUpDEq_AP')
0524         %Apply SUpDEq_AP - De-Equalize interpolated HRTFs with rigid sphere allpass functions
0525 
0526         %Get De-Equalization functions
0527         [deqTF_L,deqTF_R] = supdeq_getEqTF(eqDataset,ipSamplingGrid,'DEG',2,'ak',true);
0528         
0529         %De-Equalize
0530         HRTF_L_ip = pHRTF_L_ip.*deqTF_L;
0531         HRTF_R_ip = pHRTF_R_ip.*deqTF_R;
0532         
0533     case 'SUpDEq_Lim_AP'
0534         disp('Postprocessing: SUpDEq_Lim_AP')
0535         %Apply SUpDEq_Lim_AP - De-Equalize interpolated HRTFs with limited rigid sphere allpass functions
0536 
0537         %Get De-Equalization functions of limited eqDataset
0538         [deqTF_L,deqTF_R] = supdeq_getEqTF(eqDataset,ipSamplingGrid,'DEG',2,'ak',true);
0539         
0540         %De-Equalize
0541         HRTF_L_ip = pHRTF_L_ip.*deqTF_L;
0542         HRTF_R_ip = pHRTF_R_ip.*deqTF_R;
0543 
0544     case 'PC'
0545         disp('Postprocessing: PC')
0546         %Apply Inverse Phase-Correction
0547 
0548         %Transform interpolation sampling grid to radiant
0549         sg = ipSamplingGrid * pi / 180;
0550 
0551         %Get inverse phase correction term for left/right ear according to Eq. 13 in [4]
0552         cosThetaL = cos(sg(:,2)')*cos(pi/2) + sin(sg(:,2)')*sin(pi/2) .* cos(sg(:,1)'-pi/2);
0553         %No minus before 1j to get inverse phase term. Could also be achieved by division instead of multiplication
0554         phaseCorL = exp(1j*headRadius * k .* cosThetaL);
0555         cosThetaR = cos(sg(:,2)')*cos(pi/2) + sin(sg(:,2)')*sin(pi/2) .* cos(sg(:,1)'+pi/2);
0556         %No minus before 1j to get inverse phase term. Could also be achieved by division instead of multiplication
0557         phaseCorR = exp(1j*headRadius * k .* cosThetaR);
0558 
0559         %Apply to interpolated HRTFs
0560         HRTF_L_ip = pHRTF_L_ip .* phaseCorL.'; %Just transpose, no conjugate complex
0561         HRTF_R_ip = pHRTF_R_ip .* phaseCorR.';
0562 
0563     case 'OBTA' 
0564         disp('Postprocessing: OBTA')
0565         %Re-Insert interpolated TOAs with fractional delays
0566         
0567         %Get interpolated HRIRs
0568         pHRIR_L_ip = AKsingle2bothSidedSpectrum(pHRTF_L_ip.');
0569         pHRIR_R_ip = AKsingle2bothSidedSpectrum(pHRTF_R_ip.');
0570         pHRIR_L_ip = real(ifft(pHRIR_L_ip));
0571         pHRIR_R_ip = real(ifft(pHRIR_R_ip));
0572         
0573         %Re-Insert TOAs with fractional delays
0574         HRIR_L_ip = AKfractionalDelayCyclic(pHRIR_L_ip, toa_L_ip);
0575         HRIR_R_ip = AKfractionalDelayCyclic(pHRIR_R_ip, toa_R_ip);
0576         
0577         %Transform back to frequency domain
0578         HRTF_L_ip = AKboth2singleSidedSpectrum(fft(HRIR_L_ip)).';
0579         HRTF_R_ip = AKboth2singleSidedSpectrum(fft(HRIR_R_ip)).';
0580         
0581     case 'MagPhase'
0582         disp('Postprocessing: MagPhase')
0583         
0584         %Combine magnitude and phase of interpolated HRTFs again
0585         HRTF_L_ip = pHRTF_L_ip.*exp(1i*pHRTF_L_ph_ip); 
0586         HRTF_R_ip = pHRTF_R_ip.*exp(1i*pHRTF_R_ph_ip); 
0587         
0588     case 'None' 
0589         disp('Postprocessing: None')
0590 
0591         %Simply copy pHRTF_L_ip to HRTF_L_ip - Easier for further processing
0592         HRTF_L_ip = pHRTF_L_ip;
0593         HRTF_R_ip = pHRTF_R_ip;
0594 end
0595 
0596 %% Correct magnitude if mc ~= nan
0597 
0598 if ~isnan(mc)
0599     
0600     disp('MC postprocessing');
0601     
0602     %Save intermediate result (interpolated HRTFs without mc) in output struct
0603     interpHRTFset.p.HRTF_L_ip_noMC = HRTF_L_ip;
0604     interpHRTFset.p.HRTF_R_ip_noMC = HRTF_R_ip;
0605     
0606     %Get input HRTFs in auditory bands (ERB filter)
0607     fLowErb = 50;
0608     [cl,ferb] = AKerbErrorPersistent(HRIR_L(1:hrirLength,:),AKdirac(hrirLength),[fLowErb fs/2],fs);
0609     [cr] = AKerbErrorPersistent(HRIR_R(1:hrirLength,:),AKdirac(hrirLength),[fLowErb fs/2],fs);
0610     
0611     %Interpolate ERB filters to ipSamplingGrid with respective ipMethod
0612     switch ipMethod
0613         case 'SH'
0614 
0615             %Transform ERB filters to SH domain
0616             %Tikhonov regularization can be applied if weights in sampling grid are erased and tikhEps ~= 0
0617             cl_nm = AKsht(cl, false, samplingGrid, HRTFset.Nmax, 'complex', fs, false, 'real',tikhEps);
0618             cr_nm = AKsht(cr, false, samplingGrid, HRTFset.Nmax, 'complex', fs, false, 'real',tikhEps);
0619 
0620             %Apply SH interpolation to ipSamplingGrid
0621             cl_ip = AKisht(cl_nm, false, ipSamplingGrid(:,1:2), 'complex', false, false, 'real');
0622             cr_ip = AKisht(cr_nm, false, ipSamplingGrid(:,1:2), 'complex', false, false, 'real');
0623 
0624         case 'NN'
0625             
0626             %Transform grids to cartesian coordinates
0627             [samplingGrid_cart(:,1), samplingGrid_cart(:,2), samplingGrid_cart(:,3)] = sph2cart(samplingGrid(:,1)/360*2*pi, pi/2-samplingGrid(:,2)/360*2*pi,1);
0628             [ipSamplingGrid_cart(:,1), ipSamplingGrid_cart(:,2), ipSamplingGrid_cart(:,3)] = sph2cart(ipSamplingGrid(:,1)/360*2*pi, pi/2-ipSamplingGrid(:,2)/360*2*pi,1);
0629 
0630             %NN interpolate ERB filters to ipSamplingGrid
0631             cl_ip = zeros(length(ferb),length(ipSamplingGrid));
0632             cr_ip = zeros(length(ferb),length(ipSamplingGrid));
0633 
0634             for nPoint = 1:size(ipSamplingGrid,1)
0635 
0636                 [idx_voronoi,w_voronoi] = findvoronoi(samplingGrid_cart,ipSamplingGrid_cart(nPoint,:));
0637 
0638                 for n = 1:length(idx_voronoi) 
0639                     cl_ip(:,nPoint) = cl_ip(:,nPoint) + w_voronoi(n)*cl(:,idx_voronoi(n));  
0640                     cr_ip(:,nPoint) = cr_ip(:,nPoint) + w_voronoi(n)*cr(:,idx_voronoi(n));
0641                 end
0642             end
0643 
0644         case 'Bary'
0645             
0646             %Transform grids to cartesian coordinates
0647             [samplingGrid_cart(:,1), samplingGrid_cart(:,2), samplingGrid_cart(:,3)] = sph2cart(samplingGrid(:,1)/360*2*pi, pi/2-samplingGrid(:,2)/360*2*pi,1);
0648             [ipSamplingGrid_cart(:,1), ipSamplingGrid_cart(:,2), ipSamplingGrid_cart(:,3)] = sph2cart(ipSamplingGrid(:,1)/360*2*pi, pi/2-ipSamplingGrid(:,2)/360*2*pi,1);
0649 
0650             %Get simplified convex hull (could also be done after delaunay-triangulation)
0651             convHullIdx = convhull(samplingGrid_cart(:,1), samplingGrid_cart(:,2), samplingGrid_cart(:,3), 'simplify', true);
0652 
0653             %Get HRTFs / directions assigned to convex hull triangles
0654             convHullHRTF_A = samplingGrid_cart(convHullIdx(:,1),:);
0655             convHullHRTF_B = samplingGrid_cart(convHullIdx(:,2),:);
0656             convHullHRTF_C = samplingGrid_cart(convHullIdx(:,3),:);
0657 
0658             %Interpolate with barycentric weights
0659             %Right triangle of convex hull picked by ray-triangle intersection test (intersectLinePolygon3d from geom3D toolbox should work too)
0660             %Function returns barycentric weights / coordinates u and v of the intersection point -> w = 1-u-v
0661             %P = w*A + u*B + v*C
0662             orig = [0 0 0];
0663             cl_ip = zeros(length(ferb),length(ipSamplingGrid));
0664             cr_ip = zeros(length(ferb),length(ipSamplingGrid));
0665 
0666             for nPoint = 1:size(ipSamplingGrid,1)
0667 
0668                 [intersect, ~, u, v, ~] = TriangleRayIntersection(orig, ipSamplingGrid_cart(nPoint,:), convHullHRTF_A, convHullHRTF_B, convHullHRTF_C, 'border', 'inclusive');
0669                 intersectIdx = find(intersect, 1, 'first'); %Evaluate first intersection point
0670                 u = u(intersectIdx); v = v(intersectIdx); w = 1-u-v;
0671                 bw = [w u v]; %Barycentric weights
0672                 idx_bw = convHullIdx(intersectIdx,:); %Indizes of HRTFs of convex hull / triangles
0673 
0674                 for n = 1:3 %Always 3 because of triangulization
0675                     cl_ip(:,nPoint) = cl_ip(:,nPoint) + bw(n)*cl(:,idx_bw(n));  
0676                     cr_ip(:,nPoint) = cr_ip(:,nPoint) + bw(n)*cr(:,idx_bw(n));
0677                 end
0678             end
0679     end
0680     
0681     %Save interpolated ERBs in 3D-array for further processing
0682     c_ip(:,:,1) = cl_ip;
0683     c_ip(:,:,2) = cr_ip;
0684     
0685     %Get interpolated HRTFs in auditory bands (ERB filter)
0686     HRIR_L_ip = AKsingle2bothSidedSpectrum(HRTF_L_ip.');
0687     HRIR_R_ip = AKsingle2bothSidedSpectrum(HRTF_R_ip.');
0688     HRIR_ip(:,:,1) = real(ifft(HRIR_L_ip));
0689     HRIR_ip(:,:,2) = real(ifft(HRIR_R_ip));
0690     HRIR_ip = HRIR_ip(1:hrirLength,:,:);
0691     %Save in 3D-array for further processing
0692     c_hrir_ip = AKerbErrorPersistent(HRIR_ip,AKdirac(size(HRIR_ip,1)),[fLowErb fs/2],fs);
0693     
0694     %Get correction filters for all HRTFs
0695     corrFilt = c_ip-c_hrir_ip;
0696     %Spline interpolate correction filters to 0-fs/2
0697     corrFilt_l = AKinterpolateSpectrum( squeeze(corrFilt(:,:,1)), ferb, NFFT, {'nearest' 'spline' 'nearest'}, fs);
0698     corrFilt_r = AKinterpolateSpectrum( squeeze(corrFilt(:,:,2)), ferb, NFFT, {'nearest' 'spline' 'nearest'}, fs);
0699     
0700     %Limit to f > fA (set to 0 below fA) if desired
0701     if limitMC
0702         
0703         %Get spatial alasing frequency
0704         fA = HRTFset.Nmax*c/2/pi/headRadius;
0705         
0706         disp(['MC postprocessing limited to f > fA, with fA = ',num2str(round(fA,2)),'Hz']);
0707         
0708         if strcmp(limFade,'fadeDown')
0709             
0710             disp('MC fade downwards');
0711             
0712             %Get third-octave below fA
0713             fAt = fA/2^(1/3);
0714             
0715             %Set to 0 (logarithmic) below fAt and apply linear fade from fAt to fA
0716             [~,fA_bin] = min(abs(HRTFset.f-fA));
0717             [~,fAt_bin] = min(abs(HRTFset.f-fAt));
0718             ramp = linspace(0,1,abs(fA_bin-fAt_bin)+1);
0719             corrFilt_l(1:fAt_bin-1,:) = 0;
0720             corrFilt_l(fAt_bin:fA_bin,:) = corrFilt_l(fAt_bin:fA_bin,:).*ramp.';
0721             corrFilt_r(1:fAt_bin-1,:) = 0;
0722             corrFilt_r(fAt_bin:fA_bin,:) = corrFilt_r(fAt_bin:fA_bin,:).*ramp.'; 
0723         end
0724         
0725         if strcmp(limFade,'fadeUp')
0726             
0727             disp('MC fade upwards');
0728             
0729             %Get third-octave above fA
0730             fAt = fA*2^(1/3);
0731             
0732             %Set to 0 (logarithmic) below fA and apply linear fade from fA to fAt
0733             [~,fA_bin] = min(abs(HRTFset.f-fA));
0734             [~,fAt_bin] = min(abs(HRTFset.f-fAt));
0735             ramp = linspace(0,1,abs(fAt_bin-fA_bin)+1);
0736             corrFilt_l(1:fA_bin-1,:) = 0;
0737             corrFilt_l(fA_bin:fAt_bin,:) = corrFilt_l(fA_bin:fAt_bin,:).*ramp.';
0738             corrFilt_r(1:fA_bin-1,:) = 0;
0739             corrFilt_r(fA_bin:fAt_bin,:) = corrFilt_r(fA_bin:fAt_bin,:).*ramp.'; 
0740         end
0741         
0742     end
0743     
0744     %Transform to linear values
0745     corrFilt_l = 10.^(corrFilt_l/20);
0746     corrFilt_r = 10.^(corrFilt_r/20);
0747     
0748     if ~isinf(mc)
0749         %Apply soft-limiting to correction filters according to mc if not inf
0750         disp(['MC maximum boost: ',num2str(mc),'dB'])
0751         disp(['MC soft-limiting knee: ',num2str(mcKnee),'dB'])
0752 
0753         corrFilt_l_lim = AKsoftLimit(corrFilt_l, mc, mcKnee,[0 fs/2], fs, true);
0754         corrFilt_r_lim = AKsoftLimit(corrFilt_r, mc, mcKnee,[0 fs/2], fs, true);
0755         
0756     else %No soft-limiting / Unlimited gain
0757         
0758         disp('MC maximum boost: inf')
0759         
0760         corrFilt_l_lim = corrFilt_l;
0761         corrFilt_r_lim = corrFilt_r;
0762         
0763     end
0764     
0765     %Design minimum phase filters and use for correction instead of zero
0766     %phase filters (if mcMinPhase = false)
0767     if mcMinPhase
0768         
0769         disp('MC phase: minimum');
0770         
0771         corrFilt_l_lim = AKsingle2bothSidedSpectrum(corrFilt_l_lim);
0772         corrFilt_r_lim = AKsingle2bothSidedSpectrum(corrFilt_r_lim);
0773 
0774         corrFilt_l_lim = real(ifft(corrFilt_l_lim));
0775         corrFilt_r_lim = real(ifft(corrFilt_r_lim));
0776 
0777         corrFilt_l_lim = AKphaseManipulation(corrFilt_l_lim,fs,'min',1,0);
0778         corrFilt_r_lim = AKphaseManipulation(corrFilt_r_lim,fs,'min',1,0);
0779 
0780         %Go back to frequency domain
0781         corrFilt_l_lim = AKboth2singleSidedSpectrum(fft(corrFilt_l_lim));
0782         corrFilt_r_lim = AKboth2singleSidedSpectrum(fft(corrFilt_r_lim));
0783         
0784     else
0785         
0786         disp('MC phase: zero');
0787         
0788     end
0789     
0790     %Write in 3D array
0791     corrFilt_lim(:,:,1) = corrFilt_l_lim;
0792     corrFilt_lim(:,:,2) = corrFilt_r_lim;
0793     
0794     %Save intermediate results (correction filter) in output struct
0795     interpHRTFset.p.corrFilt_lim = corrFilt_lim;    
0796         
0797     %Apply magnitude correction filters to HRTFs
0798     HRTF_L_ip = HRTF_L_ip.*corrFilt_lim(:,:,1).';
0799     HRTF_R_ip = HRTF_R_ip.*corrFilt_lim(:,:,2).';
0800     
0801 end
0802 
0803 %% Write result in struct
0804 
0805 %Get final HRIRs
0806 HRIR_L_ip = AKsingle2bothSidedSpectrum(HRTF_L_ip.');
0807 HRIR_R_ip = AKsingle2bothSidedSpectrum(HRTF_R_ip.');
0808 HRIR_L_ip = real(ifft(HRIR_L_ip));
0809 HRIR_R_ip = real(ifft(HRIR_R_ip));
0810 %Cut zeros depending on FFToversize
0811 HRIR_L_ip = HRIR_L_ip(1:hrirLength,:);
0812 HRIR_R_ip = HRIR_R_ip(1:hrirLength,:);
0813 
0814 interpHRTFset.HRTF_L = HRTF_L_ip;
0815 interpHRTFset.HRTF_R = HRTF_R_ip;
0816 interpHRTFset.HRIR_L = HRIR_L_ip;
0817 interpHRTFset.HRIR_R = HRIR_R_ip;
0818 interpHRTFset.f = HRTFset.f;
0819 interpHRTFset.fs = fs;
0820 interpHRTFset.FFToversize = HRTFset.FFToversize;
0821 interpHRTFset.samplingGrid = ipSamplingGrid;
0822 interpHRTFset.headRadius = headRadius;
0823 interpHRTFset.ppMethod = ppMethod;
0824 interpHRTFset.ipMethod = ipMethod;
0825 interpHRTFset.headRadius = headRadius;
0826 if tikhEps ~= 0
0827     interpHRTFset.tikhEps = tikhEps;
0828 end
0829 if ~isnan(mc)
0830     interpHRTFset.mc = mc;
0831     interpHRTFset.mcKnee = mcKnee;
0832     interpHRTFset.mcMinPhase = mcMinPhase;
0833     if limitMC
0834         interpHRTFset.limitMC = 1;
0835         interpHRTFset.limitMC_fA = fA;
0836         interpHRTFset.limitMC_fAt = fAt;
0837         interpHRTFset.limitMC_fade = limFade;
0838     end
0839 end
0840 disp('Done...');
0841 
0842 end
0843

Generated on Wed 30-Nov-2022 14:15:00 by m2html © 2005