Class RawPcmAudioSource
- All Implemented Interfaces:
AudioSource,Closeable,AutoCloseable
AudioSource wrapping an in-memory byte[] of raw linear PCM
bytes with caller-supplied format metadata.
This is the escape hatch for callers who already hold PCM bytes in
memory (from an in-process codec, a network stream, a unit-test fixture,
or a custom recorder) and want to feed them through the same
AudioSource pipeline as file-based callers. It bypasses the
AudioSourceProvider SPI; just construct it directly. For the
overwhelmingly common PCM16 little-endian signed case, the
fromPcm16LittleEndian(byte[], int, int) factory removes the
endianness and encoding arguments (Requirement 7.11).
Supported PCM tuples
The constructor accepts any(bitDepth, byteOrder, encoding)
triple supported by the shared SampleConversion helper
(Requirements 7.7, 7.8):
bitDepth ∈ {16, 24, 32, 64}byteOrder ∈ {LITTLE_ENDIAN, BIG_ENDIAN}encoding ∈ {SIGNED_INT, UNSIGNED_INT, IEEE_FLOAT}, with the additional constraint thatIEEE_FLOATrequiresbitDepth ∈ {32, 64}.
Normalisation
Integer samples are divided by2^(bitDepth - 1) so that full-scale
positive samples map just below +1.0 and full-scale negative
samples map exactly to -1.0. IEEE float samples are widened to
double without scaling (Requirements 3.6, 7.12). The conversion
formulas live in
SampleConversion and are shared
verbatim with the WAV and MP3 providers.
Buffer ownership — the backing array is NOT copied
The caller-supplieddata array is referenced, not copied
(Requirement 7.13). The source reads from the array on every
read(double[], int, int) call for the lifetime of this object.
Mutating data after construction will change the output of
subsequent reads and yields undefined behaviour; callers who need to
reuse or recycle their byte buffer must pass a defensive copy
(typically data.clone()) into the constructor. The cost of
zero-copy is entirely on the caller's side; the source pays nothing.
Seekability
canSeek() always returns true (Requirement 7.10);
seeking is an O(1) update of the internal frame cursor since the whole
buffer is already resident.
Thread safety
Instances are not thread-safe (shared with the generalAudioSource contract). The mutable state is the frame cursor and
the closed flag; callers that need concurrent access must serialise
externally or construct one source per thread sharing the same
underlying byte[] (which is safe provided no one mutates the
bytes).- Since:
- 2.1.0
- See Also:
-
Constructor Summary
ConstructorsConstructorDescriptionRawPcmAudioSource(byte[] data, int sampleRate, int bitDepth, ByteOrder byteOrder, int channelCount, PcmEncoding encoding) Wrap a caller-supplied byte buffer of raw linear PCM bytes. -
Method Summary
Modifier and TypeMethodDescriptionintbitDepth()Native sample bit depth of the source.Return the byte order this source was constructed with.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 byAudioSource.read(double[], int, int)(Requirement 3.13).encoding()Return the PCM numeric encoding this source was constructed with.static RawPcmAudioSourcefromPcm16LittleEndian(byte[] data, int sampleRate, int channelCount) Convenience factory for the overwhelmingly common case of 16-bit little-endian signed PCM (Requirement 7.11).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 nextAudioSource.read(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.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface com.tino1b2be.dtmf.io.AudioSource
read
-
Constructor Details
-
RawPcmAudioSource
public RawPcmAudioSource(byte[] data, int sampleRate, int bitDepth, ByteOrder byteOrder, int channelCount, PcmEncoding encoding) Wrap a caller-supplied byte buffer of raw linear PCM bytes.The
dataarray is referenced, not copied (Requirement 7.13). See the class Javadoc for the full buffer-ownership contract; mutatingdataafter construction yields undefinedread(double[], int, int)output.- Parameters:
data- raw PCM byte buffer; length must be an exact multiple of(bitDepth / 8) * channelCountsampleRate- source sample rate in Hz;1 <= sampleRate <= 384000(Req 7.5)bitDepth- native sample bit depth;bitDepth ∈ {16, 24, 32, 64}(Req 7.7)byteOrder- byte order of multi-byte samples;ByteOrder.LITTLE_ENDIANorByteOrder.BIG_ENDIANchannelCount- number of interleaved channels;1 <= channelCount <= 8(Req 7.6)encoding- PCM numeric encoding (Req 7.3); with the additional constraint thatPcmEncoding.IEEE_FLOATrequiresbitDepth ∈ {32, 64}(Req 7.8)- Throws:
NullPointerException- ifdata,byteOrder, orencodingisnull; the message identifies the offending parameter (Req 7.4)IllegalArgumentException- if any numeric parameter is outside its accepted range, if the(encoding, bitDepth)pair is invalid, or ifdata.lengthis not an exact multiple of the frame size (Req 7.5, 7.6, 7.7, 7.8, 7.9); the message identifies the offending value and the valid range/set
-
-
Method Details
-
fromPcm16LittleEndian
public static RawPcmAudioSource fromPcm16LittleEndian(byte[] data, int sampleRate, int channelCount) Convenience factory for the overwhelmingly common case of 16-bit little-endian signed PCM (Requirement 7.11). Equivalent tonew RawPcmAudioSource(data, sampleRate, 16, ByteOrder.LITTLE_ENDIAN, channelCount, PcmEncoding.SIGNED_INT)The
dataarray is referenced, not copied; the same buffer-ownership contract as the primary constructor applies (Requirement 7.13).- Parameters:
data- raw PCM16 byte buffer; length must be an exact multiple of2 * channelCountsampleRate- source sample rate in HzchannelCount- number of interleaved channels- Returns:
- a new
RawPcmAudioSourceconfigured for PCM16 LE signed int - Throws:
NullPointerException- ifdataisnullIllegalArgumentException- if any constructor validation rule is violated
-
sampleRate
public int sampleRate()Description copied from interface:AudioSourceSample rate of the source in Hertz.- Specified by:
sampleRatein interfaceAudioSource- Returns:
- sample rate in Hz; always strictly positive (Requirement 3.2)
-
channelCount
public int channelCount()Description copied from interface:AudioSourceChannel count of the source.- Specified by:
channelCountin interfaceAudioSource- Returns:
- channel count; always strictly positive
(Requirement 3.3).
1for mono,2for stereo, and so on.
-
bitDepth
public int bitDepth()Description copied from interface:AudioSourceNative 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 fromAudioSource.read(double[], int, int)is performed by the source: integer samples are divided by2^(bitDepth - 1); IEEE float samples are widened without scaling.- Specified by:
bitDepthin interfaceAudioSource- Returns:
- native sample bit depth; one of the values listed above
-
totalFrames
public long totalFrames()Description copied from interface:AudioSourceTotal number of sample frames available from the source, or-1Lif the total is unknown.A seekable source (see
AudioSource.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).- Specified by:
totalFramesin interfaceAudioSource- Returns:
- total frame count, or
-1Lif unknown
-
canSeek
public boolean canSeek()Description copied from interface:AudioSourceWhether 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).- Specified by:
canSeekin interfaceAudioSource- Returns:
trueifAudioSource.seek(long)is supported
-
currentFrame
public long currentFrame()Description copied from interface:AudioSourceZero-based index of the next frame that will be returned byAudioSource.read(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 successfulAudioSource.seek(long)tof, it equalsf. At end of stream it equalsAudioSource.totalFrames()when that value is known.- Specified by:
currentFramein interfaceAudioSource- Returns:
- zero-based frame index of the next frame to be read
-
encoding
Return the PCM numeric encoding this source was constructed with. Informational; the decoded samples handed back fromread(double[], int, int)are always normaliseddoubleregardless.- Returns:
- the
PcmEncodingthe source decodes from
-
byteOrder
Return the byte order this source was constructed with. Informational.- Returns:
- the
ByteOrderthe source decodes from
-
read
Read up tolengthsample frames intobufferstarting atoffset.When
AudioSource.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.Implementation notes:
- Reads up to
lengthframes (not samples) intobufferstarting atoffset. Exactlyn * channelCountsamples are written when this call returnsn >= 0, with channels interleaved per theAudioSourcecontract (Requirement 3.7). - Returns
-1when the source is already exhausted at entry (Requirement 3.6). - Reads are served directly out of the backing byte array; no intermediate buffer allocations occur.
- Specified by:
readin interfaceAudioSource- 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)NullPointerException- ifbufferisnullIndexOutOfBoundsException- ifoffset < 0,length < 0, oroffset + length * channelCountexceedsbuffer.length
- A non-negative return value is the number of frames
actually written; it is always in
-
seek
Reposition the read cursor so the nextAudioSource.read(double[], int, int)returns frames starting atframeIndexfrom the start of the source (Requirement 3.10).RawPcmAudioSourcealways supports seeking (Requirement 7.10);canSeek()istrue. The valid range is[0, totalFrames()]; seeking tototalFrames()positions the cursor at end-of-stream so the nextread(...)returns-1.- Specified by:
seekin interfaceAudioSource- Parameters:
frameIndex- zero-based frame index to reposition to- Throws:
IOException- if the source has been closed (Requirement 3.14)IllegalArgumentException- ifframeIndexis outside[0, totalFrames()]; the message identifies the offending value and the valid range (Requirement 3.12)
-
close
public void 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 toAudioSource.read(double[], int, int),AudioSource.read(double[]), orAudioSource.seek(long)throwsIOExceptionidentifying the source as closed (Requirement 3.14).RawPcmAudioSourceowns no native resources;close()simply flips an internal flag that causes subsequentread(double[], int, int)orseek(long)calls to throwIOException(Requirement 3.14). It does not touch the caller-supplied backing byte array; callers remain free to reuse or discard it afterwards.This method is idempotent: a second and subsequent invocation is a no-op.
- Specified by:
closein interfaceAudioSource- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceCloseable
-