beamforming.py 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. from __future__ import annotations
  2. import numpy as np
  3. SPEED_OF_SOUND = 343.0
  4. def _fractional_delay(signal: np.ndarray, delay_samples: float) -> np.ndarray:
  5. """Apply fractional sample delay using linear interpolation."""
  6. if signal.size == 0:
  7. return signal.astype(np.float32, copy=False)
  8. x = np.arange(signal.size, dtype=np.float32)
  9. shifted_x = x - np.float32(delay_samples)
  10. delayed = np.interp(shifted_x, x, signal, left=0.0, right=0.0)
  11. return delayed.astype(np.float32)
  12. def beamform_delay_and_sum(
  13. mic1_data: np.ndarray,
  14. mic2_data: np.ndarray,
  15. angle_deg: float,
  16. sample_rate: int,
  17. mic_spacing: float,
  18. speed_of_sound: float = SPEED_OF_SOUND,
  19. ) -> np.ndarray:
  20. """Delay-and-sum beamforming for two microphones arranged in a line."""
  21. if mic1_data.shape != mic2_data.shape:
  22. raise ValueError("mic1_data and mic2_data must have the same shape")
  23. mic1 = mic1_data.astype(np.float32, copy=False)
  24. mic2 = mic2_data.astype(np.float32, copy=False)
  25. angle_rad = np.deg2rad(float(angle_deg))
  26. delay_seconds = float(mic_spacing) * np.sin(angle_rad) / float(speed_of_sound)
  27. delay_samples = delay_seconds * float(sample_rate)
  28. aligned_mic1 = _fractional_delay(mic1, delay_samples / 2.0)
  29. aligned_mic2 = _fractional_delay(mic2, -delay_samples / 2.0)
  30. output = (aligned_mic1 + aligned_mic2) * 0.5
  31. return np.clip(output, -1.0, 1.0).astype(np.float32)