GeneratorCalibrationService.java

package org.hammer.audio.experimental.acoustic.simulation.calibration;

import java.util.List;
import java.util.Objects;
import org.hammer.audio.experimental.acoustic.feature.comparison.SyntheticRealComparisonReport;
import org.hammer.audio.experimental.acoustic.simulation.WingbeatSignalParameters;
import org.hammer.audio.experimental.acoustic.wingbeat.WingbeatFeatureVector;

/**
 * Orchestrates the full generator calibration workflow.
 *
 * <p>Workflow:
 *
 * <pre>
 *   Real Dataset
 *       ↓
 *   Feature Statistics
 *       ↓
 *   Parameter Estimation ({@link SyntheticParameterEstimator})
 *       ↓
 *   Synthetic Generator Configuration ({@link CalibrationResult})
 * </pre>
 *
 * <p>The service:
 *
 * <ol>
 *   <li>Analyses the baseline generator against the real corpus to obtain the before-calibration
 *       deviation report.
 *   <li>Estimates improved parameters from the real corpus using {@link
 *       SyntheticParameterEstimator}.
 *   <li>Analyses the calibrated generator against the real corpus for the after-calibration
 *       deviation report.
 *   <li>Returns a {@link CalibrationResult} containing both reports and the calibrated parameters.
 * </ol>
 *
 * <p>The calibrated {@link WingbeatSignalParameters} can be saved and reused directly with {@link
 * org.hammer.audio.experimental.acoustic.simulation.WingbeatSignalGenerator}.
 *
 * <p>This service is stateless and may be called concurrently.
 */
public final class GeneratorCalibrationService {

  private final SyntheticCalibrationAnalysis analysis;
  private final SyntheticParameterEstimator estimator;

  /** Create a calibration service with default analysis and estimator instances. */
  public GeneratorCalibrationService() {
    this.analysis = new SyntheticCalibrationAnalysis();
    this.estimator = new SyntheticParameterEstimator();
  }

  /**
   * Run the full calibration pipeline using the given baseline parameters and real corpus.
   *
   * @param baseline baseline generator parameters to compare against; must not be {@code null}
   * @param real real recording feature vectors; must not be {@code null} or empty
   * @return calibration result including before/after reports and calibrated parameters; never
   *     {@code null}
   */
  public CalibrationResult calibrate(
      WingbeatSignalParameters baseline, List<WingbeatFeatureVector> real) {
    Objects.requireNonNull(baseline, "baseline");
    Objects.requireNonNull(real, "real");
    if (real.isEmpty()) {
      throw new IllegalArgumentException("real corpus must not be empty");
    }

    SyntheticRealComparisonReport before = analysis.analyse(baseline, real);
    WingbeatSignalParameters calibrated = estimator.estimate(real);
    SyntheticRealComparisonReport after = analysis.analyse(calibrated, real);

    return new CalibrationResult(baseline, calibrated, before, after);
  }
}