Skip to main content
Version: 0.11.x

Streaming Audio Files

With the rise of AI, we are seeing more and more applications that require server-side audio streaming capabilities. In this article, we will show you how to stream audio files into an ODIN room using our Node.js SDK.

Use Cases

There are many use cases for audio streaming:

  • AI Voice Assistants: Stream AI-generated audio responses (from OpenAI, AWS Polly, etc.) back to users
  • Music Bots: Play background music or sound effects in a room
  • Text-to-Speech: Convert text to speech and stream it to room participants
  • Audio Processing: Apply effects or transformations to audio before streaming

Our Node.js SDK is perfect for these use cases. It allows you to receive audio streams from users and send audio streams back into the room.

The simplest way to stream audio files is using the high-level API. It handles all the complexity automatically - media ID allocation, StartMedia RPC, and correct timing:

import odin from '@4players/odin-nodejs';
const { OdinClient } = odin;

const accessKey = "__YOUR_ACCESS_KEY__";
const roomId = "Lobby";
const userId = "MusicBot";

async function main() {
const client = new OdinClient();
const token = client.generateToken(accessKey, roomId, userId);
const room = client.createRoom(token);

// Configure user data (optional but recommended)
const userData = {
name: "Music Bot",
avatar: "https://example.com/bot-avatar.png",
userId: "Bot007",
outputMuted: 1, // Bot doesn't need to receive audio
inputMuted: 0, // Bot will send audio
platform: "ODIN Node.js SDK",
version: "0.11.0"
};
const data = new TextEncoder().encode(JSON.stringify(userData));

// Wait for room join
const joinPromise = new Promise(resolve => room.onJoined(resolve));
room.join("https://gateway.odin.4players.io", data);
await joinPromise;

// Create audio stream (sample rate and channels match your audio files)
const media = room.createAudioStream(48000, 2);

// Stream an MP3 file - just one line!
console.log("Streaming MP3 file...");
await media.sendMP3('./music.mp3');
console.log("MP3 streaming complete!");

// Or stream a WAV file
// await media.sendWAV('./audio.wav');

// Clean up - close() sends StopMedia RPC and releases resources
media.close();
room.close();
}

main();
tip

The high-level API (sendMP3, sendWAV, sendBuffer) automatically handles:

  • Media ID allocation from the room
  • Sending the StartMedia RPC to the server
  • Decoding audio files to raw PCM
  • Sending audio chunks at the correct 20ms intervals

Low-Level API (Full Control)

For advanced use cases where you need full control over audio transmission, use the low-level API:

import odin from '@4players/odin-nodejs';
const { OdinClient } = odin;
import fs from 'fs';
import decode from 'audio-decode';
import AudioBufferStream from 'audio-buffer-stream';
import { encode } from '@msgpack/msgpack';

const accessKey = "__YOUR_ACCESS_KEY__";
const roomId = "Lobby";
const userId = "My Bot";

// Configure user data for the bot
const userData = {
name: "Music Bot",
avatar: "https://avatars.dicebear.com/api/bottts/123.svg",
userId: "Bot007",
outputMuted: 1,
inputMuted: 0,
platform: "ODIN Node.js SDK",
version: "0.11.0"
};

async function main() {
const client = new OdinClient();
const token = client.generateToken(accessKey, roomId, userId);
const room = client.createRoom(token);

const data = new TextEncoder().encode(JSON.stringify(userData));

room.onJoined(async (event) => {
console.log("Joined room:", event.roomId);
console.log("My peer ID:", event.ownPeerId);
console.log("Available media IDs:", event.mediaIds);

// Get a media ID from the server
const mediaId = event.mediaIds[0];

// Create audio stream
const media = room.createAudioStream(48000, 2);

// Set the server-assigned media ID
media.setMediaId(mediaId);

// Send StartMedia RPC to notify server
const rpc = encode([0, 1, "StartMedia", {
media_id: mediaId,
properties: { kind: "audio" }
}]);
room.sendRpc(new Uint8Array(rpc));

// Now stream audio using the low-level approach
await sendMusicLowLevel(media);

// Clean up - close() sends StopMedia RPC and releases resources
media.close();
room.close();
});

room.join("https://gateway.odin.4players.io", data);

// Send a welcome message
const message = {
kind: 'message',
payload: 'Hello, I am a music bot and will stream some music to you.'
};
room.sendMessage(new TextEncoder().encode(JSON.stringify(message)));
}

async function sendMusicLowLevel(media) {
return new Promise(async (resolve) => {
// Decode the MP3 file
const audioBuffer = await decode(fs.readFileSync('./music.mp3'));

// ODIN requires 20ms chunks (50 times per second)
const chunkLength = audioBuffer.sampleRate / 50;

// Create a stream that matches the audio file's settings
const audioBufferStream = new AudioBufferStream({
channels: audioBuffer.numberOfChannels,
sampleRate: audioBuffer.sampleRate,
float: true,
bitDepth: 32,
chunkLength: chunkLength
});

// Queue to store audio chunks
const queue = [];

// Whenever the stream has data, add it to the queue
audioBufferStream.on('data', (data) => {
const floats = new Float32Array(new Uint8Array(data).buffer);
queue.push(floats);
});

// Send audio data at regular 20ms intervals
const interval = setInterval(() => {
if (queue.length > 0) {
const chunk = queue.shift();
media.sendAudioData(chunk);
} else {
// No more data to send
clearInterval(interval);
audioBufferStream.end();
console.log("Audio finished");
resolve();
}
}, 20);

// Write the audio buffer to the stream
audioBufferStream.write(audioBuffer);
});
}

main();

Streaming Decoded AudioBuffers

If you already have decoded audio (e.g., from an AI text-to-speech service), you can stream it directly:

import odin from '@4players/odin-nodejs';
const { OdinClient } = odin;
import decode from 'audio-decode';
import fs from 'fs';

async function streamDecodedAudio() {
const client = new OdinClient();
const token = client.generateToken(accessKey, roomId, userId);
const room = client.createRoom(token);

const joinPromise = new Promise(resolve => room.onJoined(resolve));
room.join("https://gateway.odin.4players.io");
await joinPromise;

// Decode audio file
const audioBuffer = await decode(fs.readFileSync('./speech.mp3'));

// Create stream and send the buffer
const media = room.createAudioStream(audioBuffer.sampleRate, audioBuffer.numberOfChannels);
await media.sendBuffer(audioBuffer);

// close() sends StopMedia RPC and releases resources
media.close();
room.close();
}

Dependencies

For this example, you'll need the following npm packages:

npm install @4players/odin-nodejs audio-decode audio-buffer-stream @msgpack/msgpack
PackageDescription
@4players/odin-nodejsODIN Node.js SDK
audio-decodeDecodes MP3, WAV, and other audio formats to raw PCM
audio-buffer-streamConverts AudioBuffer to a readable stream
@msgpack/msgpackMessagePack encoder (for low-level RPC calls)
tip

When using the high-level API (sendMP3, sendWAV), you only need @4players/odin-nodejs and audio-decode. The SDK handles the rest internally.

Next Steps

If you can send audio, you might also be interested in receiving audio and transcribing it to text for content moderation or AI interaction. Check out our Transcribe Audio guide.

ODIN Bot SDK

This example is just a starting point. We have built an ODIN Bot SDK in TypeScript on top of the ODIN Node.js SDK that provides simple interfaces for capturing and sending audio streams. Find it on npm: @4players/odin-bot-sdk.