Class VoiceSpeaker
- Direct Known Subclasses:
PositionalVoiceSpeaker
pushOpus(byte[]) (the handle stamps the
sequence number and timestamp; callers only supply 48kHz mono frame bytes and their own pacing)
and close() when done. Obtained from VoiceModule.openEntityVoice(Ref),
VoiceModule.openPositionalVoice(World, Vector3d) (which returns a PositionalVoiceSpeaker), or
VoiceModule.openDirectVoice(Collection).
Thread-safe: pushOpus(byte[]) may be called from any thread and hands off to the speaker's
voice executor, preserving per-speaker frame ordering without blocking the caller.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intMaximum size of a single Opus frame, in bytes. -
Method Summary
Modifier and TypeMethodDescriptionvoidclose()Closes the speaker and releases its routing state.id()Speaker id.booleanisOpen()Whether the speaker is still open.Queues a multi-frame clip and paces it one frame per 20Lms, so the client jitter buffer is not overrun the way pushing every frame at once would.voidpushOpus(byte[] frame) Queues one Opus frame for delivery: 48kHz mono, roughly 20ms (about 960 samples), at most 512 bytes.
-
Field Details
-
MAX_OPUS_FRAME_BYTES
public static final int MAX_OPUS_FRAME_BYTESMaximum size of a single Opus frame, in bytes. The voice protocol caps a relayed frame at this size, so a larger frame cannot be sent. Plugins can reference this to size or validate frames beforepushOpus(byte[])orPlayerVoiceFrame.setOpus(byte[]).- See Also:
-
-
Method Details
-
id
Speaker id. Stable for the life of the handle; clients key playback on it. -
isOpen
public boolean isOpen()Whether the speaker is still open. False afterclose()or after a source entity despawns. -
pushOpus
public void pushOpus(@Nonnull byte[] frame) Queues one Opus frame for delivery: 48kHz mono, roughly 20ms (about 960 samples), at most 512 bytes. The server does not encode audio, so frames must already be Opus-encoded (for example from a baked clip). The handle stamps the sequence number and 48kHz timestamp; the caller only supplies the frame bytes and paces them at roughly 50 per second. Pushing faster just fills the client's jitter buffer, which drops the excess.The server reads
frameasynchronously on the routing path and fans the same array out to every recipient without copying, and there is no send-completion signal. Treat the array as owned by the speaker once pushed: hand off a fresh array per frame rather than reusing a scratch buffer, or a recipient's encode could read a half-overwritten frame. A valid frame pushed after the speaker is closed is dropped; argument validation still applies.- Parameters:
frame- the Opus frame, 1 to 512 bytes- Throws:
IllegalArgumentException- if the frame is empty or larger than 512 bytes
-
play
Queues a multi-frame clip and paces it one frame per 20Lms, so the client jitter buffer is not overrun the way pushing every frame at once would. Frames must be valid Opus frames (seepushOpus(byte[])) and are validated up front.Reads
framesfor the life of the clip: do not structurally modify the list or mutate a frame's bytes until the clip completes. The frames are only read, so the same list may be replayed or played on several speakers at once. Pass a copy if you need to keep modifying your own list.A speaker carries one ordered Opus stream. Overlapping clips on the same speaker, or a clip plus a concurrent
pushOpus(byte[]), interleave into a single sequence and decode as garbled audio; open a separate speaker per concurrent stream.The clip stops on its own when the frames run out or the speaker closes (including the entity-despawn auto-close of an entity speaker). Calling
playon an already-closed speaker returns a handle that completes without sending any frame.- Parameters:
frames- the ordered Opus frames, at least one; each 1 to 512 bytes- Returns:
- a handle to cancel the clip early or observe its completion
- Throws:
IllegalArgumentException- ifframesis empty, or any frame is empty or larger than 512 bytesNullPointerException- ifframescontains a null frame
-
close
public void close()Closes the speaker and releases its routing state. Idempotent. Frames already queued bypushOpus(byte[])still route: the teardown runs behind them on the speaker's voice executor, so the documented "push a clip then close" pattern delivers the tail rather than dropping it.
-