FrameSchedule.java
package org.hammer.audio.experimental.acoustic.tracking;
/**
* Bounded per-frame timing budget for a real-time tracking pipeline.
*
* <p>{@code FrameSchedule} captures the contract that the tracking pipeline imposes on its host:
* the host must deliver one {@link org.hammer.audio.core.AudioBlock} of {@code blockFrames} every
* {@code blockFrames / sampleRate} seconds, and the pipeline must complete its processing within a
* fraction ({@code maxLoadFraction}) of that period to stay real-time.
*
* <p>This record is informational; it does not schedule anything itself. It exists so callers have
* a single, validated source of truth for the timing budget that documentation, monitoring and
* {@link ProcessingBudget} can rely on.
*
* @param sampleRate sample rate in Hz, > 0
* @param blockFrames frames per processed block, > 0
* @param maxLoadFraction maximum fraction of one block period that pipeline processing may consume
* (between 0 exclusive and 1 inclusive); typical values are 0.5 for a comfortable budget and
* 0.8 for an aggressive one
*/
public record FrameSchedule(double sampleRate, int blockFrames, double maxLoadFraction) {
/** Validate sample rate, block size and load fraction. */
public FrameSchedule {
if (!(sampleRate > 0.0) || !Double.isFinite(sampleRate)) {
throw new IllegalArgumentException("sampleRate must be finite and > 0");
}
if (blockFrames <= 0) {
throw new IllegalArgumentException("blockFrames must be > 0");
}
if (!(maxLoadFraction > 0.0) || maxLoadFraction > 1.0 || !Double.isFinite(maxLoadFraction)) {
throw new IllegalArgumentException("maxLoadFraction must be in (0,1]");
}
}
/** Duration of one block in seconds. */
public double blockDurationSeconds() {
return blockFrames / sampleRate;
}
/** Maximum allowed processing time per block in nanoseconds. */
public long maxProcessingNanos() {
return Math.round(blockDurationSeconds() * 1.0e9 * maxLoadFraction);
}
}