FrequencyCluster.java

package org.hammer.audio.experimental.acoustic.tracking;

import java.util.List;
import java.util.Objects;

/**
 * A cluster of {@link DetectedPeak detected peaks} from different channels that share a common
 * frequency, representing one acoustic source observed across the microphone array in a single
 * frame.
 *
 * <p>The {@link #centerFrequencyHz()} is the magnitude-weighted average of the contributing peaks.
 * The {@link #totalMagnitude()} sums their magnitudes and is used as a rough activity estimate
 * downstream. The {@link #peaks()} list is immutable.
 */
public record FrequencyCluster(
    double centerFrequencyHz, double totalMagnitude, List<DetectedPeak> peaks) {

  /** Validate and defensively copy the peaks list. */
  public FrequencyCluster {
    if (!Double.isFinite(centerFrequencyHz) || centerFrequencyHz < 0.0) {
      throw new IllegalArgumentException("centerFrequencyHz must be finite and >= 0");
    }
    if (!Double.isFinite(totalMagnitude) || totalMagnitude < 0.0) {
      throw new IllegalArgumentException("totalMagnitude must be finite and >= 0");
    }
    Objects.requireNonNull(peaks, "peaks");
    if (peaks.isEmpty()) {
      throw new IllegalArgumentException("peaks must not be empty");
    }
    peaks = List.copyOf(peaks);
  }

  /** Number of channels that contributed a peak to this cluster. */
  public int channelCount() {
    return peaks.size();
  }
}