--- license: mit tags: - audio - binaural - hrtf - hrir - spatial-audio - asmr language: - en - ja --- # Binaural HRIR Spatial Map Head-Related Impulse Response (HRIR) lookup table for real-time binaural audio rendering. ## Grid Specification | Parameter | Value | |---|---| | Azimuth | 0°–355° (Δ5.0°, 72 points) | | Distance | 0.10m–2.00m (26 points) | | Elevation | 0.0° (fixed) | | Total points | 1,872 | | IR length | 512 samples (10.7 ms) | | Sample rate | 48000 Hz | | Dtype | float32 | ### Distance Grid (near-field dense) ``` 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.22, 0.25, 0.28, 0.30, 0.33, 0.36, 0.40, 0.45, 0.50, 0.60, 0.75, 1.00, 1.25, 1.50, 2.00 ``` - 10–20 cm: 1 cm step (11 points) — near-field, high HRTF variation - 20–40 cm: 2–3 cm step (7 points) - 40 cm–1 m: 5–15 cm step (4 points) - 1–2 m: 25–50 cm step (4 points) ## HRTF Model Rigid sphere model with: - Spherical wave expansion (near-field) - Ear canal resonance (angle/distance dependent) - Pinna diffraction - Interaural Time Difference (ITD) - Torso reflection - Air absorption ## Usage ```python import numpy as np data = np.load("hrir_spatial_map.npz") azimuths = data["azimuths"] # (72,) float32 — [0, 5, 10, ..., 355] distances = data["distances"] # (26,) float32 — [0.10, 0.11, ..., 2.00] hrir_L = data["hrir_L"] # (72, 26, 512) float32 hrir_R = data["hrir_R"] # (72, 26, 512) float32 # Look up HRIR for azimuth=270° (right), distance=0.10m az_idx = int(270 / 5) # = 54 dist_idx = 0 # = 0.10m ir_L = hrir_L[az_idx, dist_idx] # (512,) float32 ir_R = hrir_R[az_idx, dist_idx] # (512,) float32 # Binaural rendering via direct convolution out_L = np.convolve(mono_audio, ir_L, mode="full")[:len(mono_audio)] out_R = np.convolve(mono_audio, ir_R, mode="full")[:len(mono_audio)] stereo = np.stack([out_L, out_R], axis=-1) ``` ### Interpolation for arbitrary positions ```python def interpolate_hrir(hrir_L, hrir_R, azimuths, distances, az_deg, dist_m): az_step = azimuths[1] - azimuths[0] az_idx_f = (az_deg % 360) / az_step az_i0 = int(az_idx_f) % len(azimuths) az_i1 = (az_i0 + 1) % len(azimuths) az_alpha = az_idx_f - int(az_idx_f) dist_m = np.clip(dist_m, distances[0], distances[-1]) d_idx = np.searchsorted(distances, dist_m, side="right") - 1 d_idx = np.clip(d_idx, 0, len(distances) - 2) d_alpha = (dist_m - distances[d_idx]) / (distances[d_idx + 1] - distances[d_idx]) L00 = hrir_L[az_i0, d_idx] L01 = hrir_L[az_i0, d_idx + 1] L10 = hrir_L[az_i1, d_idx] L11 = hrir_L[az_i1, d_idx + 1] L = (1 - az_alpha) * ((1 - d_alpha) * L00 + d_alpha * L01) \ + az_alpha * ((1 - d_alpha) * L10 + d_alpha * L11) R00 = hrir_R[az_i0, d_idx] R01 = hrir_R[az_i0, d_idx + 1] R10 = hrir_R[az_i1, d_idx] R11 = hrir_R[az_i1, d_idx + 1] R = (1 - az_alpha) * ((1 - d_alpha) * R00 + d_alpha * R01) \ + az_alpha * ((1 - d_alpha) * R10 + d_alpha * R11) return L.astype(np.float32), R.astype(np.float32) ``` ## License MIT