Class VoiceSpeaker

java.lang.Object
com.hypixel.hytale.server.core.modules.voice.VoiceSpeaker
Direct Known Subclasses:
PositionalVoiceSpeaker

public abstract sealed class VoiceSpeaker extends Object permits PositionalVoiceSpeaker (not exhaustive)
A custom voice stream. Push raw Opus frames with 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

    Fields
    Modifier and Type
    Field
    Description
    static final int
    Maximum size of a single Opus frame, in bytes.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Closes the speaker and releases its routing state.
    id()
    Speaker id.
    boolean
    Whether the speaker is still open.
    play(List<byte[]> frames)
    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.
    void
    pushOpus(byte[] frame)
    Queues one Opus frame for delivery: 48kHz mono, roughly 20ms (about 960 samples), at most 512 bytes.

    Methods inherited from class Object

    equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • MAX_OPUS_FRAME_BYTES

      public static final int MAX_OPUS_FRAME_BYTES
      Maximum 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 before pushOpus(byte[]) or PlayerVoiceFrame.setOpus(byte[]).
      See Also:
  • Method Details

    • id

      @Nonnull public UUID 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 after close() 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 frame asynchronously 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

      @Nonnull public ClipPlayback play(@Nonnull List<byte[]> frames)
      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 (see pushOpus(byte[])) and are validated up front.

      Reads frames for 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 play on 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 - if frames is empty, or any frame is empty or larger than 512 bytes
      NullPointerException - if frames contains a null frame
    • close

      public void close()
      Closes the speaker and releases its routing state. Idempotent. Frames already queued by pushOpus(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.