MicrophoneArrayReadiness.java
package org.hammer.audio.acquisition;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Deterministic readiness result for starting localization with one profile and source mode.
*
* @param ready whether localization may start
* @param synchronization synchronization evidence used by the decision
* @param diagnostics ordered human-readable findings
*/
public record MicrophoneArrayReadiness(
boolean ready, SynchronizationAssessment synchronization, List<String> diagnostics) {
// Validate and defensively copy one readiness result.
public MicrophoneArrayReadiness {
Objects.requireNonNull(synchronization, "synchronization");
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
}
/** Evaluate a profile before simulation, replay or live localization starts. */
public static MicrophoneArrayReadiness assess(
MicrophoneArrayProfile profile,
LocalizationInputMode mode,
Instant observationTime,
double maximumTimingErrorSamples) {
Objects.requireNonNull(profile, "profile");
Objects.requireNonNull(mode, "mode");
Objects.requireNonNull(observationTime, "observationTime");
if (!Double.isFinite(maximumTimingErrorSamples) || maximumTimingErrorSamples <= 0.0) {
throw new IllegalArgumentException("maximumTimingErrorSamples must be finite and > 0");
}
List<String> diagnostics = new ArrayList<>();
if (!profile.supports(mode)) {
diagnostics.add("Profile does not support input mode " + mode + '.');
return new MicrophoneArrayReadiness(
false, SynchronizationAssessment.nominalSharedClock(), diagnostics);
}
if (mode != LocalizationInputMode.LIVE) {
diagnostics.add("Non-live source uses deterministic shared-clock timing evidence.");
return new MicrophoneArrayReadiness(
true, SynchronizationAssessment.nominalSharedClock(), diagnostics);
}
CaptureDeviceConfiguration capture = profile.liveCaptureConfiguration().orElseThrow();
if (capture.format().channels() != profile.array().channels()) {
diagnostics.add("Capture channel count does not match the microphone-array profile.");
return new MicrophoneArrayReadiness(
false, SynchronizationAssessment.nominalSharedClock(), diagnostics);
}
if (profile.calibration() == null) {
diagnostics.add("Live localization requires a calibration for this array mapping.");
return new MicrophoneArrayReadiness(
false, SynchronizationAssessment.nominalSharedClock(), diagnostics);
}
SynchronizationAssessment synchronization =
profile
.calibration()
.assess(observationTime, capture.format().sampleRate(), maximumTimingErrorSamples);
diagnostics.addAll(synchronization.diagnostics());
return new MicrophoneArrayReadiness(synchronization.usable(), synchronization, diagnostics);
}
}