Class WavAudioSourceProvider
- All Implemented Interfaces:
com.tino1b2be.dtmf.io.AudioSourceProvider
AudioSourceProvider implementation for the
RIFF/WAVE (and RF64/WAVE) container formats. This is
the public entry point of the dtmf-io-wav module, discovered
by ServiceLoader through the
META-INF/services/com.tino1b2be.dtmf.io.AudioSourceProvider
registration (Requirement 9.2) and normally invoked indirectly via
AudioSources.open(...).
Design
The provider is a clean-room RIFF parser: it reads the container byte-by-byte throughRiffReader and decodes samples
through the shared com.tino1b2be.dtmf.io.internal.SampleConversion
helper. It does not import anything under javax.sound.sampled
(Requirement 9.12) and ships with zero external runtime dependencies
beyond dtmf-io itself. The actual frame-level decoding lives
inside WavAudioSource (returned from open(Path) and
open(InputStream, String)); this class is exclusively
responsible for detecting a WAV input and parsing its header.
Detection (canOpen)
Both canOpen overloads check the same twelve-byte magic pattern
(Requirements 9.5, 9.6): bytes 0..3 must be "RIFF" or
"RF64", bytes 4..7 are the top-level size field and are
not validated at this stage, and bytes 8..11 must be
"WAVE". A match returns a score of 100; anything else,
including an input shorter than twelve bytes, returns -1. The
Path overload opens a fresh FileChannel and closes it
before returning; the InputStream overload uses
InputStream.mark(int) and InputStream.reset() so the
caller's stream is left positioned exactly where it started
(Requirement 4.6). Non-markable streams are declined with -1
without consuming any bytes, because reading a header from a
non-markable stream would leave it in a state no downstream provider
could recover from (Requirement 4.7); the AudioSources facade
wraps non-markable inputs in a BufferedInputStream before
scoring, so this branch mostly protects direct callers.
Full parse (open)
When a caller proceeds to open(Path) or
open(InputStream, String), the provider walks the RIFF form
with RiffReader, skipping any LIST / bext /
junk / PEAK / unknown chunks and locating the
mandatory fmt and data chunks (Requirement 9.11).
RF64 files additionally require a ds64 chunk before
the fmt chunk: the outer 32-bit size fields are pinned to
0xFFFFFFFF and the real 64-bit sizes are pulled from
ds64's riffSize64 and dataSize64. The parser
recognises exactly three wFormatTag values
(Requirements 9.7, 9.8, 9.9):
0x0001WAVE_FORMAT_PCM— signed integer PCM at16,24, or32bits.0x0003WAVE_FORMAT_IEEE_FLOAT— IEEE 754 float at32or64bits.0xFFFEWAVE_FORMAT_EXTENSIBLE— dispatches on the 16-byteSubFormatGUID; onlyKSDATAFORMAT_SUBTYPE_PCMandKSDATAFORMAT_SUBTYPE_IEEE_FLOATare accepted.
wFormatTag (including 0x0006 A-law,
0x0007 µ-law, 0x0011 IMA ADPCM) is rejected with
UnsupportedAudioFormatException identifying the code
(Requirement 9.10). Structural defects — a missing fmt ,
a missing data, a bogus outer magic, or a chunk whose declared
size runs off the end of the form — are rejected with
IOException describing the defect (Requirement 9.11), so
callers can tell "not a valid WAV" from "valid WAV with a compression
we do not support" (Requirement 12.4).
Stream ownership
Theopen(Path) branch opens a FileChannel that the
returned AudioSource owns and closes on
AudioSource.close(). The open(InputStream, String)
branch never closes the caller's stream
(Requirement 4.10); it returns a stream-backed WavAudioSource
whose AudioSource.close() transitions the source into the
closed state but leaves the caller's InputStream untouched.- Since:
- 2.1.0
- See Also:
-
WavAudioSourceAudioSourceProviderAudioSource
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionintcanOpen(InputStream stream, String hint) intcom.tino1b2be.dtmf.io.AudioSourceopen(InputStream stream, String hint) com.tino1b2be.dtmf.io.AudioSourceintpriority()
-
Constructor Details
-
WavAudioSourceProvider
public WavAudioSourceProvider()ServiceLoaderrequires a public no-argument constructor (Requirement 4.1). Instances are stateless and cheap to construct; the provider caches no data across calls.
-
-
Method Details
-
formatName
- Specified by:
formatNamein interfacecom.tino1b2be.dtmf.io.AudioSourceProvider
-
priority
public int priority()- Specified by:
priorityin interfacecom.tino1b2be.dtmf.io.AudioSourceProvider
-
canOpen
Opens a read-only
FileChannelonpath, reads the first twelve bytes, and returns100when they match the RIFF/WAVE or RF64/WAVE pattern (Requirements 9.5, 9.6). The channel is closed via try-with-resources before returning so the detection call does not leak a file handle.Any
IOExceptionraised while opening or reading the file propagates to the caller;com.tino1b2be.dtmf.io.AudioSourcescatches such exceptions during scoring, records the provider as having returned-1, logs a warning, and continues.- Specified by:
canOpenin interfacecom.tino1b2be.dtmf.io.AudioSourceProvider- Parameters:
path- file to score; must be non-null- Returns:
100on a magic-byte match,-1otherwise- Throws:
NullPointerException- ifpathisnullIOException- on I/O failure while reading the file
-
canOpen
When
streamsupportsmark/reset, marks up toOUTER_HEADER_BYTESbytes, reads exactly twelve bytes viaInputStream.readNBytes(int), scores against the RIFF pattern, and resets the stream in afinallyblock so the caller's position is restored on both the success and failure paths (Requirement 4.6). Short reads (fewer than twelve bytes) return-1.Non-markable streams are declined with
-1without consuming any bytes (Requirement 4.7); theAudioSourcesfacade wraps such streams in aBufferedInputStreambefore scoring, so in normal use this branch is defensive.- Specified by:
canOpenin interfacecom.tino1b2be.dtmf.io.AudioSourceProvider- Parameters:
stream- the stream to score; must be non-nullhint- optional caller-supplied hint; may benulland is ignored by this provider (content-based detection)- Returns:
100on a magic-byte match,-1otherwise- Throws:
NullPointerException- ifstreamisnullIOException- on I/O failure while reading the header prefix
-
open
Opens a read-only
FileChanneland drives the RIFF parser over it to build aWaveFormat, then hands the channel (still open, positioned at the first byte of thedatapayload) toWavAudioSource.fromChannel(WaveFormat, FileChannel). The returned source owns the channel; itsAudioSource.close()closes it.On any parse failure this method closes the channel before re-throwing so partially-parsed files do not leak file handles.
- Specified by:
openin interfacecom.tino1b2be.dtmf.io.AudioSourceProvider- Parameters:
path- file to open; must be non-null- Returns:
- an opened
WavAudioSourcein channel-backed mode - Throws:
NullPointerException- ifpathisnullcom.tino1b2be.dtmf.io.UnsupportedAudioFormatException- if the file's magic matched but thefmtchunk declares an unsupported encoding (Requirement 9.10)IOException- on any other failure, including structural defects (Requirement 9.11) and underlying I/O errors
-
open
Drives the RIFF parser directly over the caller's
InputStreamand hands it toWavAudioSource.fromCallerStream(WaveFormat, InputStream)with the stream positioned at the first byte of thedatapayload. The returned source is stream-backed, soAudioSource.canSeek()isfalse(Requirement 3.9).This method never closes the caller's stream (Requirement 4.10), on either the success path or the failure path: the stream is the caller's to manage. The returned
WavAudioSource'sclose()likewise leaves the stream untouched.- Specified by:
openin interfacecom.tino1b2be.dtmf.io.AudioSourceProvider- Parameters:
stream- the caller-supplied stream; must be non-null. Callers that come in throughAudioSources.open(InputStream, String)will always receive a markable stream thanks to the facade's buffering (Requirement 5.12); callers invoking the provider directly must supply a stream whose bytes can be consumed forward-only starting from the current position.hint- optional caller-supplied hint (file name, URL path segment, MIME type); may benulland is ignored by this provider (content-based detection wins regardless of the hint).- Returns:
- an opened
WavAudioSourcein stream-backed mode - Throws:
NullPointerException- ifstreamisnullcom.tino1b2be.dtmf.io.UnsupportedAudioFormatException- on unsupported encodings (Requirement 9.10)IOException- on structural defects (Requirement 9.11) or underlying stream errors
-