StereoDelaySnapshot.java

package org.hammer.audio.localization;

import java.util.Objects;
import org.hammer.audio.analysis.AnalysisSnapshot;

/** Immutable result of stereo inter-channel delay analysis. */
public final class StereoDelaySnapshot implements AnalysisSnapshot {

  private final long sourceFrameIndex;
  private final long sourceTimestampNanos;
  private final StereoDelayStatus status;
  private final int delaySamples;
  private final double delayMillis;
  private final double pathLengthDifferenceMeters;
  private final double angleDegrees;
  private final double confidence;
  private final double microphoneSpacingMeters;
  private final double speedOfSoundMetersPerSecond;
  private final int minCorrelationLagSamples;
  private final float[] correlationByLag;

  /**
   * Create a stereo delay snapshot.
   *
   * @param sourceFrameIndex source block frame index
   * @param sourceTimestampNanos source block timestamp
   * @param status validity status
   * @param delaySamples estimated right-channel delay relative to left, in samples
   * @param delayMillis estimated delay in milliseconds
   * @param pathLengthDifferenceMeters path-length difference implied by delay
   * @param angleDegrees approximate angle of arrival from broadside, or NaN
   * @param confidence normalized cross-correlation confidence
   * @param microphoneSpacingMeters microphone spacing used for physical checks
   * @param speedOfSoundMetersPerSecond speed of sound used for conversion
   * @param minCorrelationLagSamples lag represented by index zero of correlationByLag
   * @param correlationByLag normalized cross-correlation curve, defensively copied
   */
  public StereoDelaySnapshot(
      long sourceFrameIndex,
      long sourceTimestampNanos,
      StereoDelayStatus status,
      int delaySamples,
      double delayMillis,
      double pathLengthDifferenceMeters,
      double angleDegrees,
      double confidence,
      double microphoneSpacingMeters,
      double speedOfSoundMetersPerSecond,
      int minCorrelationLagSamples,
      float[] correlationByLag) {
    this.sourceFrameIndex = sourceFrameIndex;
    this.sourceTimestampNanos = sourceTimestampNanos;
    this.status = Objects.requireNonNull(status, "status");
    this.delaySamples = delaySamples;
    this.delayMillis = delayMillis;
    this.pathLengthDifferenceMeters = pathLengthDifferenceMeters;
    this.angleDegrees = angleDegrees;
    this.confidence = confidence;
    this.microphoneSpacingMeters = microphoneSpacingMeters;
    this.speedOfSoundMetersPerSecond = speedOfSoundMetersPerSecond;
    this.minCorrelationLagSamples = minCorrelationLagSamples;
    this.correlationByLag = correlationByLag == null ? new float[0] : correlationByLag.clone();
  }

  @Override
  public long sourceFrameIndex() {
    return sourceFrameIndex;
  }

  @Override
  public long sourceTimestampNanos() {
    return sourceTimestampNanos;
  }

  public StereoDelayStatus status() {
    return status;
  }

  public boolean valid() {
    return status == StereoDelayStatus.VALID;
  }

  public int delaySamples() {
    return delaySamples;
  }

  public double delayMillis() {
    return delayMillis;
  }

  public double pathLengthDifferenceMeters() {
    return pathLengthDifferenceMeters;
  }

  public double angleDegrees() {
    return angleDegrees;
  }

  public double confidence() {
    return confidence;
  }

  public double microphoneSpacingMeters() {
    return microphoneSpacingMeters;
  }

  public double speedOfSoundMetersPerSecond() {
    return speedOfSoundMetersPerSecond;
  }

  public int minCorrelationLagSamples() {
    return minCorrelationLagSamples;
  }

  public int maxCorrelationLagSamples() {
    return minCorrelationLagSamples + correlationByLag.length - 1;
  }

  public int correlationLagForIndex(int index) {
    return minCorrelationLagSamples + index;
  }

  public float[] correlationByLag() {
    return correlationByLag.clone();
  }
}