RecordingPreflight.java
package org.hammer.audio;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Objects;
import org.hammer.audio.core.AudioFormatDescriptor;
import org.hammer.audio.recording.runtime.FileStoreRecordingStorageProbe;
import org.hammer.audio.recording.runtime.RecordingStorageLevel;
import org.hammer.audio.recording.runtime.RecordingStorageProbe;
import org.hammer.audio.recording.runtime.RecordingStorageStatus;
/** Evaluates recording storage readiness before a target or partial file is created. */
public final class RecordingPreflight {
private RecordingPreflight() {
// utility class
}
/**
* Inspect the selected destination using the configured source format and production filesystem
* probe.
*
* @param service configured audio source
* @param destination requested final recording path
* @return immutable preflight capacity snapshot
* @throws IOException if the source format or destination filesystem cannot be inspected
*/
public static RecordingStorageStatus inspect(AudioCaptureService service, Path destination)
throws IOException {
return inspect(service, destination, new FileStoreRecordingStorageProbe(), Instant.now());
}
static RecordingStorageProbe productionProbe() {
return new FileStoreRecordingStorageProbe();
}
static RecordingStorageStatus inspect(
AudioCaptureService service,
Path destination,
RecordingStorageProbe storageProbe,
Instant checkedAt)
throws IOException {
Objects.requireNonNull(service, "service");
Objects.requireNonNull(destination, "destination");
Objects.requireNonNull(storageProbe, "storageProbe");
Objects.requireNonNull(checkedAt, "checkedAt");
AudioFormatDescriptor descriptor = service.getDescriptor();
if (descriptor == null) {
throw new IOException("Audio source has no format descriptor; start/configure it first.");
}
return storageProbe.probe(destination, 0L, 0.0, expectedBytesPerSecond(descriptor), checkedAt);
}
static void requireReady(RecordingStorageStatus preflight) throws IOException {
Objects.requireNonNull(preflight, "preflight");
if (!preflight.writable()) {
throw new IOException("Recording destination is not writable: " + preflight.destination());
}
if (preflight.level() == RecordingStorageLevel.CRITICAL) {
throw new IOException(
"Recording destination has insufficient safe capacity: "
+ preflight.usableBytes()
+ " usable bytes");
}
}
private static double expectedBytesPerSecond(AudioFormatDescriptor descriptor) {
double samplePayload = descriptor.sampleRate() * descriptor.channels() * Float.BYTES;
return samplePayload * 1.02;
}
}