Interface AudioSource
- All Superinterfaces:
AutoCloseable,Closeable
- All Known Implementing Classes:
RawPcmAudioSource
AudioSourceProvider
and by RawPcmAudioSource. Samples read through this interface are
always normalised to [-1.0, 1.0], regardless of the underlying
format's native encoding (Requirement 3.6). The shape deliberately
mirrors InputStream and
javax.sound.sampled.AudioInputStream: callers invoke
read(double[], int, int) and receive either a frame count or
-1 at end of stream.
This is the one interface that unifies WAV, MP3, raw PCM, and any
future provider module. Format-specific decoding logic lives entirely
inside the implementation; callers see the same
(sampleRate, channelCount, bitDepth, totalFrames) metadata and
the same interleaved normalised double[] output regardless of
which provider served them.
Metadata invariants
sampleRate()is strictly positive (Requirement 3.2).channelCount()is strictly positive (Requirement 3.3).bitDepth()is one of{16, 24, 32, 64}for PCM integer sources,{32}for 32-bit IEEE float,{64}for 64-bit IEEE double (Requirement 3.4). MP3 sources report16because the JLayer-based provider decodes to 16-bit PCM.totalFrames()returns a non-negative frame count, or-1Lwhen the total is not known up front (typical for forward-only streams such as MP3) (Requirement 3.5).
Channel interleaving
WhenchannelCount() is 1, the output buffer holds one
sample per frame. When it is 2, frame k lives at
buffer[offset + 2k] (left) and buffer[offset + 2k + 1]
(right). For more than two channels, the file's native channel order
follows right (Requirement 3.7). This matches the shape
com.tino1b2be.dtmf.DtmfDecoder already consumes for
ChannelMode.STEREO_INDEPENDENT.
Return-code conventions
read(double[], int, int) returns a non-negative frame count on
success, or -1 once the source is exhausted and no more samples
will ever be produced (Requirement 3.6). A return value of 0 is
not end of stream: it means "no samples available right now,
try again." Callers should treat 0 as a retry signal and only
treat -1 as terminal.
Buffer ownership
read(double[], int, int) writes into the caller-supplied
double[] and must not retain a reference to it after the call
returns (Requirement 3.15). Callers are free to reuse, reallocate, or
overwrite the buffer between reads; implementations that cache samples
internally must copy into their own storage.
Stream ownership on close
close() releases any resources the source owns — file channels
it opened, decoder state, native handles — and is idempotent: a second
call is a no-op (Requirement 3.14 implies this; the stream contract on
Closeable codifies it). A source obtained via
AudioSources.open(java.io.InputStream, String) only closes
streams the provider wrapped or opened itself; it does not
close caller-supplied InputStreams (Requirement 4.10).
A source obtained via AudioSources.open(java.nio.file.Path) or
AudioSources.open(java.net.URL) owns the stream it opened and
will close it on close().
Thread safety
Instances are not thread-safe. A singleAudioSource
must be driven by at most one thread at a time; callers that need
concurrent access must either serialise externally or open one source
per thread. Provider implementations are free to assume single-threaded
access to their internal state.
Lifecycle after close
Onceclose() has returned, any subsequent call to
read(double[], int, int), read(double[]), or
seek(long) throws IOException identifying the source
as closed (Requirement 3.14). close() itself remains callable
and is a no-op on subsequent invocations.- Since:
- 2.1.0
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionintbitDepth()Native sample bit depth of the source.booleancanSeek()Whether random-access seeking is supported on this source.intChannel count of the source.voidclose()Release any resources the source owns.longZero-based index of the next frame that will be returned byread(double[], int, int)(Requirement 3.13).default intread(double[] buffer) Read up tobuffer.length / channelCount()frames intobuffer, starting at index0.intread(double[] buffer, int offset, int length) Read up tolengthsample frames intobufferstarting atoffset.intSample rate of the source in Hertz.voidseek(long frameIndex) Reposition the read cursor so the nextread(double[], int, int)returns frames starting atframeIndexfrom the start of the source (Requirement 3.10).longTotal number of sample frames available from the source, or-1Lif the total is unknown.
-
Method Details
-
sampleRate
int sampleRate()Sample rate of the source in Hertz.- Returns:
- sample rate in Hz; always strictly positive (Requirement 3.2)
-
channelCount
int channelCount()Channel count of the source.- Returns:
- channel count; always strictly positive
(Requirement 3.3).
1for mono,2for stereo, and so on.
-
bitDepth
int bitDepth()Native sample bit depth of the source.The value is one of
{16, 24, 32, 64}for PCM integer sources,{32}for 32-bit IEEE float sources, and{64}for 64-bit IEEE double sources (Requirement 3.4). MP3 sources report16because the JLayer-based decoder emits 16-bit signed PCM.Normalisation to the
[-1.0, 1.0]doublesamples returned fromread(double[], int, int)is performed by the source: integer samples are divided by2^(bitDepth - 1); IEEE float samples are widened without scaling.- Returns:
- native sample bit depth; one of the values listed above
-
totalFrames
long totalFrames()Total number of sample frames available from the source, or-1Lif the total is unknown.A seekable source (see
canSeek()) that knows its length up front returns a non-negative count here; a forward-only stream whose length is not recoverable without scanning the whole input (a VBR MP3 without a Xing header, for example) returns-1L(Requirement 3.5).- Returns:
- total frame count, or
-1Lif unknown
-
read
Read up tolengthsample frames intobufferstarting atoffset.When
channelCount()is greater than1, channels are interleaved in the output buffer in the order left, right, then any additional channels in the source's native channel order (Requirement 3.7). Exactlyn * channelCount()samples are written when this call returnsn >= 0.Samples are normalised to
[-1.0, 1.0]: integer samples are divided by2^(bitDepth - 1); IEEE float samples are widened todoublewithout scaling (Requirement 3.6).Return conventions:
- A non-negative return value is the number of frames
actually written; it is always in
[0, length]. -1means the source is exhausted and no further frames will ever be produced (Requirement 3.6).0is not end of stream. It means "no frames available right now" and is a valid, retry-able return value; callers should re-invoke rather than treating it as terminal.
The caller owns
buffer. Implementations do not retain a reference to it after the call returns (Requirement 3.15); callers are free to reuse, reallocate, or overwrite it between reads.- Parameters:
buffer- destination buffer; non-nulloffset- starting index intobuffer;0 <= offset <= buffer.lengthlength- maximum number of frames to write; the buffer must hold at leastlength * channelCount()samples starting atoffset- Returns:
- number of frames actually read (
0 <= n <= length), or-1at end of stream - Throws:
IOException- if the source has been closed (Requirement 3.14) or an underlying I/O error occurs
- A non-negative return value is the number of frames
actually written; it is always in
-
read
Read up tobuffer.length / channelCount()frames intobuffer, starting at index0. Behaves identically toread(buffer, 0, buffer.length)(Requirement 3.8); kept on the interface as a default so implementations only have to supply the three-argument form.- Parameters:
buffer- destination buffer; non-null- Returns:
- number of frames actually read, or
-1at end of stream - Throws:
IOException- if the source has been closed or an underlying I/O error occursNullPointerException- ifbufferisnull
-
canSeek
boolean canSeek()Whether random-access seeking is supported on this source.Seekable sources (file-backed WAV,
RawPcmAudioSource) returntrue; forward-only sources (MP3, anyInputStream-backed source) returnfalse(Requirement 3.9).- Returns:
trueifseek(long)is supported
-
seek
Reposition the read cursor so the nextread(double[], int, int)returns frames starting atframeIndexfrom the start of the source (Requirement 3.10).- Parameters:
frameIndex- zero-based frame index to reposition to- Throws:
UnsupportedOperationException- ifcanSeek()returnsfalse; the exception message identifies the implementing class (Requirement 3.11)IllegalArgumentException- ifframeIndex < 0, or iftotalFrames()is non-negative andframeIndex > totalFrames(); the exception message identifies the offending value and the valid range (Requirement 3.12)IOException- if the source has been closed (Requirement 3.14) or an underlying I/O error occurs
-
currentFrame
long currentFrame()Zero-based index of the next frame that will be returned byread(double[], int, int)(Requirement 3.13).On a freshly opened source the return value is
0. After a successfulreadthat returnednframes, it increases byn. After a successfulseek(long)tof, it equalsf. At end of stream it equalstotalFrames()when that value is known.- Returns:
- zero-based frame index of the next frame to be read
-
close
Release any resources the source owns.Closing a source obtained via
AudioSources.open(java.nio.file.Path)orAudioSources.open(java.net.URL)also closes the underlying stream thatAudioSourcesopened on the caller's behalf. Closing a source obtained viaAudioSources.open(java.io.InputStream, String)does not close the caller-supplied stream (Requirement 4.10); the caller retains ownership of anyInputStreamthey handed in.This method is idempotent: a second and subsequent invocation is a no-op. After
close()has returned, any call toread(double[], int, int),read(double[]), orseek(long)throwsIOExceptionidentifying the source as closed (Requirement 3.14).- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceCloseable- Throws:
IOException- if releasing the underlying resources fails
-