Voice deprecated
Microphone capture + per-peer playback. Opus-encoded 20 ms frames carry over your transport (Steam P2P, the Net library, anything). Ruzit handles the audio plumbing, you decide how packets travel.
local Voice = import("Voice")
local mic = Voice.StartCapture()
mic.OnPacket:Connect(function(packet)
send_to_peers(packet)
end)
local chan = Voice.CreateChannel()
chan:Play()
on_peer_packet:Connect(function(packet) chan:Push(packet) end)
Voice API
Open the default microphone. Encodes 20 ms Opus frames; fires
OnPacket for each one. Stop with
:Stop() when done.
Per-peer playback channel. Each remote speaker gets one. Push received Opus packets in; Ruzit decodes and plays them.
Fire-and-forget: spin up a transient channel, decode + play the
packet, optionally pinned to a world position. Best for one-off
received messages. opts:
{ x?, y?, z?, falloff?, min_falloff? }
(falloff is MaxFalloff).
For ongoing peer voice prefer CreateChannel.
Empty packet collector. Hook to mic.OnPacket OR to
packets you receive from a peer, the recorder doesn't
care where they came from.
Inverse of recorder:Serialize().
One-shot mic recording. Opens the default input, captures for
duration seconds (clamped to 1 hour), stops
automatically, and fires the returned Signal once with
(recording, serialized) — the recording handle for
local playback (recording:PlayInto(channel) or
Voice.PlayRecording(recording)) and the serialized
string for sending over the network / saving to disk / reloading
via Voice.LoadRecording.
local Voice = import("Voice")
Voice.Record(3):Connect(function(recording, serialized)
-- Local playback:
Voice.PlayRecording(recording)
-- Or send to peers and have them Voice.LoadRecording(s):PlayInto(ch):
lobby:Broadcast(serialized)
end)
Fire-and-forget recording playback. data is either
a VoiceRecording handle or a serialized string
(the same form Voice.LoadRecording takes).
Spins up a transient channel, plays the whole recording, and
returns the channel so you can apply shaders, watch
IsPlaying, or stop it early.
opts: { loop?: boolean, speed?: number }
(speed clamps to 0.25..4).
Shader factories
Build a VoiceShader userdata you can stack on a channel
via chan:ApplyShader(shader). For per-channel modifiers
you only need one of (Volume / Speed / Position), prefer the
properties on VoiceChannel below.
Voice.Volume(factor) | Linear gain. 1.0 unchanged, 0 silence, >1 boost. |
Voice.Speed(factor) | Time + pitch. Clamps to 0.25..4. |
Voice.Spatial(x, y, z, max_falloff?) | Anchor to a world point with distance falloff. Listener = active Renderable.Camera. |
Voice.ListenerPosition(x, y, z) | Override the listener position globally. Pass (0, 0, 0) to revert to the camera. |
VoiceCapture
Returned by Voice.StartCapture().
Fires with each Opus-encoded packet (~20 ms = 50/sec). Pass
packet to peers via Steam P2P / Net /
whatever transport.
Stop and release the mic. After Stop, every other method is a no-op.
True between StartCapture and Stop.
Push-to-talk: stop emitting packets without closing the mic. Toggling is cheap (one atomic).
Alias for Pause/Resume that reads naturally with a state variable.
Voice activation: peak amplitude (0..1). 0 = always send (default). Typical: 0.02–0.05 just above ambient noise. Frames whose peak amplitude is below the threshold are dropped before they hit the Opus encoder.
VoiceChannel
Returned by Voice.CreateChannel() /
Voice.PlayPacket(...). One per remote speaker.
True while at least one packet is queued for playback.
Feed an Opus packet (received from a peer). Decoded and queued for playback. Auto-clamps backlog to ~1 second to prevent runaway latency.
Begin the audio mixer for this channel.
After Stop, new packets queue silently until you Play again.
Stack a VoiceShader built via the factories above.
Convenience setter, equivalent to assigning
chan.Position. Pass nil (or no args) to clear.
Same as SetPosition but also installs / replaces
the Spatial shader with this distance falloff in one call.
Modifier properties
Live, settable properties on the channel. Same idea as SFX Sound, but the Voice mixer only has Volume / Speed / Spatial under the hood, so the property list is the portable subset. Assigning replaces the prior value (no stacking), reading returns the current value.
chan.Volume | number | Linear gain. 1.0 unchanged, 0 silence, >1 boost. |
chan.Speed | number | Time + pitch. Clamps to 0.25..4. |
chan.Pitch | number | Alias for Speed. |
chan.Position | Vector? | World-space anchor. Set to nil to revert to non-positional. |
chan.MinFalloff | number | Inner radius. Distances ≤ this play at full volume. Default 0. |
chan.MaxFalloff | number | Outer radius. Distances ≥ this are silent. Default 8. |
-- A peer joins, their channel; squeeze them through a radio.
local chan = Voice.CreateChannel()
chan.Volume = 0.8
chan.Position = Vector(12, 0, 3)
chan.MinFalloff = 2
chan.MaxFalloff = 25
chan:Play()
Tweening
A VoiceChannel is a valid target for
TweenService.new.
Tweenable properties: Volume, Pitch /
Speed, Position, MinFalloff,
MaxFalloff.
local Tween = import("TweenService")
-- Smooth volume duck while a system alert plays.
Tween.new(chan, 0.25, "Sine", { Volume = 0.2 }):Play()
VoiceRecorder
Captures Opus packets into an in-memory buffer. Packets are opaque , the recorder doesn't care if they came from your own mic or a peer.
Append one Opus packet.
Drop everything captured so far.
Total packet count.
Total recorded duration in seconds.
Length-prefixed binary serialization. Save with
IO.write or to Steam Cloud, send over the
network, etc. Reload with Voice.LoadRecording.
Snapshot the current packet list as an immutable VoiceRecording.
VoiceRecording
Immutable snapshot. Built from
recorder:ToRecording() or loaded via
Voice.LoadRecording(bytes).
Stream packets into channel at the original
~50 packets/second pacing. Returns immediately, playback
is dripped by the heart pump. opts:
{ loop?: boolean, speed?: 0.25..4.0 }.
VoiceShader
Returned by every Voice.Volume / Speed / Spatial / ...
factory. Opaque handle, pass it to
chan:ApplyShader(shader). Multiple channels can share
the same shader instance, and one channel can stack many shaders.