DatasetAudioLoader.java
package org.hammer.audio.experimental.acoustic.dataset;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Objects;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import org.hammer.audio.capture.SampleDecoder;
import org.hammer.audio.core.AudioBlock;
import org.hammer.audio.core.AudioFormatDescriptor;
/** Loads local dataset audio files into normalized {@link AudioBlock} instances. */
public final class DatasetAudioLoader {
/**
* Inspect one audio file without exposing the decoded samples.
*
* @param audioPath local audio file
* @return basic audio metadata
* @throws IOException when the audio file cannot be opened
*/
public static AudioFileInfo inspect(Path audioPath) throws IOException {
Objects.requireNonNull(audioPath, "audioPath");
try (AudioInputStream stream = AudioSystem.getAudioInputStream(audioPath.toFile())) {
AudioFormat format = stream.getFormat();
double sampleRate = format.getSampleRate();
long frameLength = stream.getFrameLength();
double duration = frameLength > 0 && sampleRate > 0.0 ? frameLength / sampleRate : 0.0;
return new AudioFileInfo(
sampleRate, duration, format.getChannels(), format.getSampleSizeInBits());
} catch (UnsupportedAudioFileException ex) {
throw new IOException("Unsupported audio file: " + audioPath, ex);
}
}
/**
* Decode one audio file into a normalized {@link AudioBlock}.
*
* @param audioPath local audio file
* @return decoded audio block
* @throws IOException when the audio file cannot be decoded
*/
public AudioBlock load(Path audioPath) throws IOException {
Objects.requireNonNull(audioPath, "audioPath");
try (AudioInputStream sourceStream = AudioSystem.getAudioInputStream(audioPath.toFile())) {
AudioFormat baseFormat = sourceStream.getFormat();
AudioFormat targetFormat = pcmFormat(baseFormat);
try (AudioInputStream pcmStream =
needsConversion(baseFormat)
? AudioSystem.getAudioInputStream(targetFormat, sourceStream)
: sourceStream) {
AudioFormat effectiveFormat = pcmStream.getFormat();
AudioFormatDescriptor descriptor =
new AudioFormatDescriptor(
effectiveFormat.getSampleRate(),
effectiveFormat.getChannels(),
effectiveFormat.getSampleSizeInBits());
SampleDecoder decoder =
new SampleDecoder(
descriptor,
AudioFormat.Encoding.PCM_SIGNED.equals(effectiveFormat.getEncoding()),
effectiveFormat.isBigEndian());
byte[] bytes = pcmStream.readAllBytes();
int frames = decoder.framesIn(bytes.length);
float[][] samples = new float[descriptor.channels()][frames];
decoder.decode(bytes, bytes.length, samples);
return AudioBlock.wrap(descriptor, samples, 0L, 0L);
}
} catch (UnsupportedAudioFileException ex) {
throw new IOException("Unsupported audio file: " + audioPath, ex);
}
}
private static boolean needsConversion(AudioFormat format) {
AudioFormat.Encoding encoding = format.getEncoding();
return !AudioFormat.Encoding.PCM_SIGNED.equals(encoding)
&& !AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding);
}
private static AudioFormat pcmFormat(AudioFormat format) {
if (!needsConversion(format)) {
return format;
}
int channels = Math.max(1, format.getChannels());
return new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
format.getSampleRate(),
16,
channels,
channels * 2,
format.getSampleRate(),
false);
}
/**
* Basic metadata for one inspected audio file.
*
* @param sampleRateHz audio sample rate in hertz
* @param durationSeconds decoded or header-derived duration in seconds
* @param channelCount number of audio channels
* @param sampleSizeBits source sample size in bits
*/
public record AudioFileInfo(
double sampleRateHz, double durationSeconds, int channelCount, int sampleSizeBits) {
public AudioFileInfo {
if (!Double.isFinite(sampleRateHz) || sampleRateHz <= 0.0) {
throw new IllegalArgumentException("sampleRateHz must be finite and > 0");
}
if (!Double.isFinite(durationSeconds) || durationSeconds < 0.0) {
throw new IllegalArgumentException("durationSeconds must be finite and >= 0");
}
if (channelCount < 1) {
throw new IllegalArgumentException("channelCount must be >= 1");
}
if (sampleSizeBits < 1) {
throw new IllegalArgumentException("sampleSizeBits must be >= 1");
}
}
}
}