Steam Voice
Steam records and compresses the player's microphone for you. What it hands over is compressed audio: send it to the other players as it is, and decode it on the way out.
Steam handles the microphone permission and the player's own push-to-talk settings; the game does not have to.
Methods
startVoiceRecording()
Starts recording. It keeps recording until stopVoiceRecording().
stopVoiceRecording()
Stops. Steam usually still has a little audio buffered, so one more getVoice() after this is worth doing.
getAvailableVoice()
How much compressed voice is waiting: { result, compressedSize }. result is Steam's EVoiceResult — 0 there is data, 2 not recording, 3 nothing yet.
getVoice(maxSize?)
Reads up to maxSize bytes (8192 by default), or null when there is none.
javascript
const chunk = await steam.getVoice();
if (chunk) send(chunk.buffer); // plain bytesdecompressVoice(data, sampleRate?)
Turns compressed voice back into PCM — 16-bit mono at sampleRate, which defaults to 11025.
Examples
Push to talk
javascript
let talking = false;
async function pressTalk() {
if (talking) return;
talking = await steam.startVoiceRecording();
}
async function releaseTalk() {
if (!talking) return;
await steam.stopVoiceRecording();
await drainVoice(); // whatever is still buffered
talking = false;
}
async function drainVoice() {
let chunk;
while ((chunk = await steam.getVoice())) {
network.send({ type: 'voice', bytes: chunk.buffer });
}
}
// while talking, read often — Steam's buffer is not large
setInterval(() => { if (talking) drainVoice(); }, 50);Playing what arrives
javascript
const ctx = new AudioContext();
async function onVoicePacket(bytes) {
const pcm = await steam.decompressVoice(bytes, 11025);
if (!pcm) return;
// 16-bit signed mono, little endian
const view = new DataView(new Uint8Array(pcm.buffer).buffer);
const samples = new Float32Array(pcm.written / 2);
for (let i = 0; i < samples.length; i++) {
samples[i] = view.getInt16(i * 2, true) / 32768;
}
const buffer = ctx.createBuffer(1, samples.length, 11025);
buffer.getChannelData(0).set(samples);
const source = ctx.createBufferSource();
source.buffer = buffer;
source.connect(ctx.destination);
source.start();
}Notes
- Read while recording, not only at the end: Steam's capture buffer is small and overruns quietly.
- The bytes are Steam's own codec. Do not try to play them directly and do not re-encode them — pass them through and decode at the other end.
- Without Steam running,
getVoice()answersnulland recording never starts.