@4players/odin-nodejs 0.10.3 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/LICENSE +21 -0
  3. package/README.md +603 -44
  4. package/binding.gyp +29 -13
  5. package/cppsrc/binding.cpp +3 -6
  6. package/cppsrc/odinbindings.cpp +9 -45
  7. package/cppsrc/odincipher.cpp +92 -0
  8. package/cppsrc/odincipher.h +32 -0
  9. package/cppsrc/odinclient.cpp +19 -158
  10. package/cppsrc/odinclient.h +2 -5
  11. package/cppsrc/odinmedia.cpp +144 -186
  12. package/cppsrc/odinmedia.h +51 -18
  13. package/cppsrc/odinroom.cpp +675 -635
  14. package/cppsrc/odinroom.h +76 -26
  15. package/cppsrc/utilities.cpp +11 -81
  16. package/cppsrc/utilities.h +25 -140
  17. package/index.cjs +829 -0
  18. package/index.d.ts +3 -4
  19. package/libs/bin/linux/arm64/libodin.so +0 -0
  20. package/libs/bin/linux/arm64/libodin_crypto.so +0 -0
  21. package/libs/bin/linux/ia32/libodin.so +0 -0
  22. package/libs/bin/linux/ia32/libodin_crypto.so +0 -0
  23. package/libs/bin/linux/x64/libodin.so +0 -0
  24. package/libs/bin/linux/x64/libodin_crypto.so +0 -0
  25. package/{prebuilds/darwin-x64/node.napi.node → libs/bin/macos/universal/libodin.dylib} +0 -0
  26. package/libs/bin/macos/universal/libodin_crypto.dylib +0 -0
  27. package/libs/bin/windows/arm64/odin.dll +0 -0
  28. package/libs/bin/windows/arm64/odin.lib +0 -0
  29. package/libs/bin/windows/arm64/odin_crypto.dll +0 -0
  30. package/libs/bin/windows/arm64/odin_crypto.lib +0 -0
  31. package/libs/bin/windows/ia32/odin.dll +0 -0
  32. package/libs/bin/windows/ia32/odin.lib +0 -0
  33. package/libs/bin/windows/ia32/odin_crypto.dll +0 -0
  34. package/libs/bin/windows/ia32/odin_crypto.lib +0 -0
  35. package/libs/bin/windows/x64/odin.dll +0 -0
  36. package/libs/bin/windows/x64/odin.lib +0 -0
  37. package/libs/bin/windows/x64/odin_crypto.dll +0 -0
  38. package/libs/bin/windows/x64/odin_crypto.lib +0 -0
  39. package/libs/include/odin.h +665 -567
  40. package/libs/include/odin_crypto.h +46 -0
  41. package/odin.cipher.d.ts +31 -0
  42. package/odin.media.d.ts +69 -19
  43. package/odin.room.d.ts +348 -7
  44. package/package.json +5 -4
  45. package/prebuilds/{darwin-arm64/node.napi.node → darwin-x64+arm64/libodin.dylib} +0 -0
  46. package/prebuilds/darwin-x64+arm64/libodin_crypto.dylib +0 -0
  47. package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
  48. package/prebuilds/linux-x64/libodin.so +0 -0
  49. package/prebuilds/linux-x64/libodin_crypto.so +0 -0
  50. package/prebuilds/linux-x64/node.napi.node +0 -0
  51. package/prebuilds/win32-x64/node.napi.node +0 -0
  52. package/prebuilds/win32-x64/odin.dll +0 -0
  53. package/prebuilds/win32-x64/odin_crypto.dll +0 -0
  54. package/scripts/postbuild.cjs +133 -0
  55. package/tests/audio-recording/README.md +97 -12
  56. package/tests/audio-recording/index.js +238 -130
  57. package/tests/connection-test/README.md +97 -0
  58. package/tests/connection-test/index.js +273 -0
  59. package/tests/lifecycle/test-room-cycle.js +169 -0
  60. package/tests/sending-audio/README.md +178 -9
  61. package/tests/sending-audio/canBounce.mp3 +0 -0
  62. package/tests/sending-audio/index.js +250 -87
  63. package/tests/sending-audio/test-kiss-api.js +149 -0
  64. package/tests/sending-audio/test-loop-audio.js +142 -0
  65. package/CMakeLists.txt +0 -25
  66. package/libs/bin/linux/arm64/libodin_static.a +0 -0
  67. package/libs/bin/linux/ia32/libodin_static.a +0 -0
  68. package/libs/bin/linux/x64/libodin_static.a +0 -0
  69. package/libs/bin/macos/arm64/libodin_static.a +0 -0
  70. package/libs/bin/macos/x64/libodin_static.a +0 -0
  71. package/libs/bin/windows/arm64/odin_static.lib +0 -0
  72. package/libs/bin/windows/ia32/odin_static.lib +0 -0
  73. package/libs/bin/windows/x64/odin_static.lib +0 -0
package/README.md CHANGED
@@ -1,69 +1,628 @@
1
- # ODIN Node JS
1
+ # ODIN Node.js SDK
2
2
 
3
- Native Node JS bindings for the ODIN SDK. It is based on the [ODIN Native SDK](https://github.com/4Players/odin-sdk) and
4
- wraps the C++ code into a Node JS module.
3
+ [![npm version](https://img.shields.io/npm/v/@4players/odin-nodejs.svg)](https://www.npmjs.com/package/@4players/odin-nodejs)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Discord](https://img.shields.io/discord/XXXXXX?label=Discord&logo=discord)](https://4np.de/discord)
5
6
 
6
- Check out documentation and guides at [developers.4players.io](https://www.4players.io/odin/sdk/nodejs).
7
+ Native Node.js bindings for the [ODIN Voice SDK](https://github.com/4Players/odin-sdk). Build powerful voice chat applications, recording bots, AI integrations, and real-time audio processing tools.
7
8
 
8
- ## Beta!
9
+ 📖 **[Full Documentation](https://docs.4players.io/voice/nodejs/)** | 💬 **[Discord Community](https://4np.de/discord)** | 🎮 **[4Players ODIN](https://www.4players.io/odin)**
9
10
 
10
- **This SDK is currently in beta. This means that the API is not final and might change in the future. It's also not ready
11
- for production use as there might be bugs, memory leaks or other issues.** But it's good enough for testing AI
12
- integration, recording or other interesting use cases.
11
+ ---
13
12
 
14
- If you have any questions or feedback, please reach out to us at our [Discord Server](https://4np.de/discord).
13
+ ## Features
15
14
 
16
- ## ODIN Node JS compared to Web SDK
15
+ - 🎙️ **Real-time Voice Chat** - Low-latency voice communication
16
+ - 🔐 **End-to-End Encryption** - Built-in E2EE with OdinCipher
17
+ - 🤖 **Bot Integration** - Perfect for recording bots, AI assistants, and moderation tools
18
+ - 📊 **Raw Audio Access** - Get PCM audio data for processing, recording, or transcription
19
+ - 🌍 **Proximity Chat** - 3D positional audio support
20
+ - ⚡ **High Performance** - Native C++ bindings for maximum efficiency
21
+ - 📈 **Diagnostics** - Real-time connection and audio quality monitoring
17
22
 
18
- We have a JavaScript/TypeScript SDK that has fallback code for WebRTC and can be used in the browser. While this also
19
- works on NodeJS (to some extent) it is not as performant as the native bindings and also has some limitations.
20
- For bots and other advanced use cases we provide this native Node JS SDK.
23
+ ---
21
24
 
22
- While we try to keep the API as similar as possible, there are some differences. While wrapping things in objects in
23
- JavaScript and TypeScript for better separating doing so in native C/C++ is harder and error prone. Therefore we decided
24
- to only use C/C++ and objects where required. For example, the WebSDK provides an `OdinPeer` object that wraps various
25
- methods and properties. For bots and other NodeJS use cases you typically don't need that and therefore we decided to
26
- not make things more complex than required. You typically only get a `peerId` and `mediaId` and that's it. But for most
27
- use cases this is enough, and you are always free to build your own wrapper where required.
25
+ ## Installation
28
26
 
29
- The most prominent difference is, that the NodeJS SDK allows you to get the raw audio data from the ODIN server. This
30
- allows you to record audio or sending audio into the room. While the WebSDK provides a similar functionality it is based
31
- on Web Audio that is not available on NodeJS - therefore it does not make much sense to wrap and polyfill all of that while
32
- it's super easy (and fast) to wrap the ODIN native SDK into NodeJS bindings.
27
+ ```bash
28
+ npm install @4players/odin-nodejs
29
+ ```
33
30
 
34
- Having two libs also allows us to optimize for each platform without having to work through many use cases.
31
+ ### Prerequisites
35
32
 
36
- ## Prerequisites
33
+ This SDK includes prebuilt binaries for:
34
+ - **macOS** (x86_64 and arm64)
35
+ - **Windows** (x86_64)
36
+ - **Linux** (x86_64)
37
37
 
38
- This NodeJS SDK is based on the native ODIN SDK and is implemented via Native NodeJS bindings. This means that during
39
- `npm install` this module will be compiled, which means you may need to have a compiler installed.
38
+ For other platforms, you'll need a C++ compiler. See [node-gyp requirements](https://github.com/nodejs/node-gyp#installation).
40
39
 
41
- For some target systems, precompiled libraries are provided:
42
- - macOS (x86_64 and arm64)
43
- - Windows (x86_64)
44
- - Linux (x86_64)
40
+ ---
45
41
 
46
- Otherwise you need to have a C++ compiler installed. On macOS, you can install XCode and the command line tools. On Linux you need
47
- to have GCC installed. More information can be found here: [node-gyp](https://github.com/nodejs/node-gyp).
42
+ ## Quick Start
48
43
 
49
- ## Getting Started and compiling
44
+ ### 1. Get Your Access Key
50
45
 
51
- Add the package to your project:
46
+ Sign up at [4Players ODIN](https://www.4players.io/odin) to get your free access key.
52
47
 
53
- ```bash
54
- npm install --save @4players/odin-nodejs
48
+ ### 2. Basic Connection Example
49
+
50
+ ```javascript
51
+ import odin from '@4players/odin-nodejs';
52
+ const { OdinClient } = odin;
53
+
54
+ // Configuration - replace with your credentials
55
+ const accessKey = "__YOUR_ACCESS_KEY__";
56
+ const roomId = "my-room";
57
+ const userId = "user-123";
58
+
59
+ async function main() {
60
+ // Create client and generate token locally
61
+ const client = new OdinClient();
62
+ const token = client.generateToken(accessKey, roomId, userId);
63
+
64
+ // Create room using factory pattern
65
+ const room = client.createRoom(token);
66
+
67
+ // Set up event handlers
68
+ room.onJoined((event) => {
69
+ console.log(`Joined room: ${event.roomId}`);
70
+ console.log(`My peer ID: ${event.ownPeerId}`);
71
+ console.log(`Available media IDs: ${event.mediaIds}`);
72
+ });
73
+
74
+ room.onPeerJoined((event) => {
75
+ console.log(`Peer joined: ${event.peerId}`);
76
+ });
77
+
78
+ room.onPeerLeft((event) => {
79
+ console.log(`Peer left: ${event.peerId}`);
80
+ });
81
+
82
+ // Join the room
83
+ room.join("https://gateway.odin.4players.io");
84
+
85
+ // Keep connection alive
86
+ process.on('SIGINT', () => {
87
+ room.close();
88
+ process.exit(0);
89
+ });
90
+ }
91
+
92
+ main();
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Event Handlers
98
+
99
+ The SDK provides typed event handlers for easy integration:
100
+
101
+ ```javascript
102
+ // Connection events
103
+ room.onConnectionStateChanged((event) => {
104
+ console.log(`State: ${event.state}`); // Connecting, Joined, Disconnected, etc.
105
+ });
106
+
107
+ room.onJoined((event) => {
108
+ // { roomId, ownPeerId, room, mediaIds }
109
+ });
110
+
111
+ room.onLeft((event) => {
112
+ // { reason }
113
+ });
114
+
115
+ // Peer events
116
+ room.onPeerJoined((event) => {
117
+ // { peerId, userId, userData, peer }
118
+ });
119
+
120
+ room.onPeerLeft((event) => {
121
+ // { peerId }
122
+ });
123
+
124
+ // Media events
125
+ room.onMediaStarted((event) => {
126
+ // { peerId, media }
127
+ });
128
+
129
+ room.onMediaStopped((event) => {
130
+ // { peerId, mediaId }
131
+ });
132
+
133
+ room.onMediaActivity((event) => {
134
+ // { peerId, mediaId, state } - Voice Activity Detection
135
+ });
136
+
137
+ // Messages
138
+ room.onMessageReceived((event) => {
139
+ // { senderPeerId, message }
140
+ });
141
+
142
+ // Audio data (for recording/processing)
143
+ room.onAudioDataReceived((data) => {
144
+ // { peerId, mediaId, samples16, samples32 }
145
+ });
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Audio Recording Example
151
+
152
+ Record audio from peers to WAV files:
153
+
154
+ ```javascript
155
+ import odin from '@4players/odin-nodejs';
156
+ import wav from 'wav';
157
+
158
+ const { OdinClient } = odin;
159
+
160
+ // Configuration
161
+ const accessKey = "__YOUR_ACCESS_KEY__";
162
+ const roomId = "my-room";
163
+ const userId = "RecorderBot";
164
+
165
+ const recordings = {};
166
+
167
+ async function main() {
168
+ const client = new OdinClient();
169
+ const token = client.generateToken(accessKey, roomId, userId);
170
+ const room = client.createRoom(token);
171
+
172
+ room.onAudioDataReceived((data) => {
173
+ const { mediaId, peerId, samples16 } = data;
174
+
175
+ // Create recording file if needed
176
+ if (!recordings[mediaId]) {
177
+ recordings[mediaId] = new wav.FileWriter(`recording_${peerId}.wav`, {
178
+ channels: 2,
179
+ sampleRate: 48000,
180
+ bitDepth: 16
181
+ });
182
+ }
183
+
184
+ // Write audio samples
185
+ const buffer = Buffer.from(samples16.buffer, samples16.byteOffset, samples16.byteLength);
186
+ recordings[mediaId].write(buffer);
187
+ });
188
+
189
+ room.onMediaStopped((event) => {
190
+ if (recordings[event.mediaId]) {
191
+ recordings[event.mediaId].end();
192
+ delete recordings[event.mediaId];
193
+ }
194
+ });
195
+
196
+ room.join("https://gateway.odin.4players.io");
197
+ }
198
+
199
+ main();
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Sending Audio
205
+
206
+ The SDK provides two approaches for sending audio: a **high-level API** for convenience and a **low-level API** for full control.
207
+
208
+ ### High-Level API (Recommended)
209
+
210
+ The high-level API handles all the complexity automatically - media ID allocation, StartMedia RPC, and timing:
211
+
212
+ ```javascript
213
+ import odin from '@4players/odin-nodejs';
214
+ const { OdinClient } = odin;
215
+
216
+ // Configuration
217
+ const accessKey = "__YOUR_ACCESS_KEY__";
218
+ const roomId = "my-room";
219
+ const userId = "AudioBot";
220
+
221
+ async function main() {
222
+ const client = new OdinClient();
223
+ const token = client.generateToken(accessKey, roomId, userId);
224
+ const room = client.createRoom(token);
225
+
226
+ // Wait for room join
227
+ const joinPromise = new Promise(resolve => room.onJoined(resolve));
228
+ room.join("https://gateway.odin.4players.io");
229
+ await joinPromise;
230
+
231
+ // Create audio stream and send audio with one line!
232
+ const media = room.createAudioStream(44100, 2);
233
+
234
+ // Send an MP3 file (auto-decodes and streams with correct timing)
235
+ await media.sendMP3('./music.mp3');
236
+
237
+ // Or send a WAV file
238
+ await media.sendWAV('./audio.wav');
239
+
240
+ // Or send a decoded AudioBuffer
241
+ // await media.sendBuffer(audioBuffer);
242
+
243
+ media.close();
244
+ room.close();
245
+ }
246
+
247
+ main();
248
+ ```
249
+
250
+ ### Low-Level API
251
+
252
+ For full control over audio transmission, use the low-level API:
253
+
254
+ ```javascript
255
+ import odin from '@4players/odin-nodejs';
256
+ const { OdinClient } = odin;
257
+ import { encode } from '@msgpack/msgpack';
258
+
259
+ // Configuration
260
+ const accessKey = "__YOUR_ACCESS_KEY__";
261
+ const roomId = "my-room";
262
+ const userId = "AudioBot";
263
+
264
+ async function main() {
265
+ const client = new OdinClient();
266
+ const token = client.generateToken(accessKey, roomId, userId);
267
+ const room = client.createRoom(token);
268
+
269
+ room.onJoined(async (event) => {
270
+ // 1. Get media ID from the event
271
+ const mediaId = event.mediaIds[0];
272
+
273
+ // 2. Create audio stream
274
+ const media = room.createAudioStream(48000, 2);
275
+
276
+ // 3. Set the server-assigned media ID
277
+ media.setMediaId(mediaId);
278
+
279
+ // 4. Send StartMedia RPC to notify server
280
+ const rpc = encode([0, 1, "StartMedia", {
281
+ media_id: mediaId,
282
+ properties: { kind: "audio" }
283
+ }]);
284
+ room.sendRpc(new Uint8Array(rpc));
285
+
286
+ // 5. Send audio data in 20ms chunks
287
+ const chunkDurationMs = 20;
288
+ const samplesPerChunk = Math.floor(48000 * chunkDurationMs / 1000) * 2;
289
+
290
+ // Your audio data as Float32Array (interleaved stereo, range [-1, 1])
291
+ const audioChunk = new Float32Array(samplesPerChunk);
292
+ // ... fill with audio samples ...
293
+ media.sendAudioData(audioChunk);
294
+
295
+ // 6. When done, close
296
+ media.close();
297
+ });
298
+
299
+ room.join("https://gateway.odin.4players.io");
300
+ }
301
+
302
+ main();
303
+ ```
304
+
305
+ See [tests/sending-audio/](tests/sending-audio/) for complete examples of both APIs.
306
+
307
+ ---
308
+
309
+ ## End-to-End Encryption (E2EE)
310
+
311
+ Enable encryption for secure voice communication:
312
+
313
+ ```javascript
314
+ import odin from '@4players/odin-nodejs';
315
+ const { OdinClient, OdinCipher } = odin;
316
+
317
+ const client = new OdinClient();
318
+ const token = client.generateToken(accessKey, roomId, userId);
319
+ const room = client.createRoom(token);
320
+
321
+ // Create and configure cipher
322
+ const cipher = new OdinCipher();
323
+ cipher.setPassword(new TextEncoder().encode("shared-secret-password"));
324
+
325
+ // Apply cipher to room
326
+ room.setCipher(cipher);
327
+
328
+ room.join("https://gateway.odin.4players.io");
329
+ ```
330
+
331
+ > ⚠️ All participants in a room must use the same cipher password to communicate.
332
+
333
+ ### Verifying Peer Encryption Status
334
+
335
+ ```javascript
336
+ // Check if a peer's encryption matches ours
337
+ const status = cipher.getPeerStatus(peerId);
338
+ console.log(`Peer ${peerId} encryption: ${status}`);
339
+ // Possible values: "encrypted", "mismatch", "unencrypted", "unknown"
340
+ ```
341
+
342
+ ---
343
+
344
+ ## Proximity Chat (3D Audio)
345
+
346
+ Enable distance-based audio for spatial applications:
347
+
348
+ ```javascript
349
+ room.onJoined(() => {
350
+ // Set position scale (1 unit = 1 meter)
351
+ room.setPositionScale(1.0);
352
+
353
+ // Update your position
354
+ room.updatePosition(10.0, 0.0, 5.0); // x, y, z
355
+ });
55
356
  ```
56
357
 
57
- This will compile the NodeJS bindings for the "local" machine. We provide a few precompiled libraries so you don't need
58
- to have GCC and compilers installed. This is especially useful for CI/CD pipelines.
358
+ ---
359
+
360
+ ## Connection Diagnostics
361
+
362
+ Monitor connection quality and troubleshoot issues:
363
+
364
+ ```javascript
365
+ room.onJoined(() => {
366
+ // Get connection identifier
367
+ const connectionId = room.getConnectionId();
368
+ console.log(`Connection ID: ${connectionId}`);
369
+
370
+ // Get detailed connection statistics
371
+ const stats = room.getConnectionStats();
372
+ if (stats) {
373
+ console.log(`RTT: ${stats.rtt.toFixed(2)} ms`);
374
+ console.log(`TX Loss: ${(stats.udpTxLoss * 100).toFixed(2)}%`);
375
+ console.log(`RX Loss: ${(stats.udpRxLoss * 100).toFixed(2)}%`);
376
+ console.log(`TX Bytes: ${stats.udpTxBytes}`);
377
+ console.log(`RX Bytes: ${stats.udpRxBytes}`);
378
+ console.log(`Congestion Events: ${stats.congestionEvents}`);
379
+ }
380
+
381
+ // Get jitter statistics for an audio stream
382
+ const jitterStats = room.getJitterStats(mediaId);
383
+ if (jitterStats) {
384
+ console.log(`Packets Total: ${jitterStats.packetsTotal}`);
385
+ console.log(`Packets Lost: ${jitterStats.packetsLost}`);
386
+ console.log(`Packets Too Late: ${jitterStats.packetsArrivedTooLate}`);
387
+ }
388
+ });
389
+ ```
390
+
391
+ ---
392
+
393
+ ## API Reference
394
+
395
+ ### OdinClient
396
+
397
+ | Method | Description |
398
+ |--------|-------------|
399
+ | `generateToken(accessKey, roomId, userId)` | Generate a room token locally |
400
+ | `createRoom(token)` | Create a room instance (recommended) |
401
+ | `createRoomWithToken(token)` | Alias for createRoom |
402
+
403
+ ### OdinRoom
404
+
405
+ | Method | Description |
406
+ |--------|-------------|
407
+ | `join(gateway, userData?)` | Connect to the room |
408
+ | `close()` | Disconnect from the room |
409
+ | `sendMessage(data, peerIds?)` | Send a message to peers |
410
+ | `updatePosition(x, y, z)` | Update 3D position |
411
+ | `setPositionScale(scale)` | Set position scale factor |
412
+ | `setCipher(cipher)` | Enable E2EE |
413
+ | `createAudioStream(sampleRate, channels)` | Create audio output stream |
414
+ | `getConnectionId()` | Get connection identifier |
415
+ | `getConnectionStats()` | Get connection quality metrics |
416
+ | `getJitterStats(mediaId)` | Get audio jitter metrics |
417
+
418
+ ### OdinRoom Properties
419
+
420
+ | Property | Type | Description |
421
+ |----------|------|-------------|
422
+ | `ownPeerId` | `number` | Your peer ID |
423
+ | `connected` | `boolean` | Connection status |
424
+ | `availableMediaIds` | `number[]` | Available media IDs for audio streams |
425
+
426
+ ### OdinMedia (Audio Stream)
427
+
428
+ | Method | Description |
429
+ |--------|-------------|
430
+ | `setMediaId(mediaId)` | Set server-assigned media ID |
431
+ | `close()` | Release the stream |
432
+ | `sendAudioData(samples)` | Send raw audio samples |
433
+ | `sendMP3(filePath)` | Stream an MP3 file (convenience) |
434
+ | `sendWAV(filePath)` | Stream a WAV file (convenience) |
435
+ | `sendBuffer(audioBuffer)` | Stream AudioBuffer (convenience) |
436
+
437
+ ### OdinCipher (E2EE)
438
+
439
+ | Method | Description |
440
+ |--------|-------------|
441
+ | `setPassword(password)` | Set encryption password |
442
+ | `getPeerStatus(peerId)` | Get peer's encryption status |
59
443
 
60
- ## Dependencies
444
+ ### Events
61
445
 
62
- It has deps to the native ODIN SDK. A couple of libraries for typical use cases (i.e. macOS, Windows and Linux) is provided
63
- in the `libs` folder. If you have some fancy platform, you might need to compile the ODIN SDK and put the lib in the
64
- folder according this name scheme: `platform`-`arch`.
446
+ | Event | Payload |
447
+ |-------|---------|
448
+ | `ConnectionStateChanged` | `{ state, message }` |
449
+ | `Joined` | `{ roomId, ownPeerId, room, mediaIds }` |
450
+ | `Left` | `{ reason }` |
451
+ | `PeerJoined` | `{ peerId, userId, userData, peer }` |
452
+ | `PeerLeft` | `{ peerId }` |
453
+ | `MediaStarted` | `{ peerId, media }` |
454
+ | `MediaStopped` | `{ peerId, mediaId }` |
455
+ | `MediaActivity` | `{ peerId, mediaId, state }` |
456
+ | `MessageReceived` | `{ senderPeerId, message }` |
457
+ | `AudioDataReceived` | `{ peerId, mediaId, samples16, samples32 }` |
458
+
459
+ ---
460
+
461
+ ## Comparison with Web SDK
462
+
463
+ | Feature | Node.js SDK | Web SDK |
464
+ |---------|-------------|---------|
465
+ | Platform | Node.js (server) | Browser |
466
+ | Performance | Native C++ | WebRTC/JavaScript |
467
+ | Raw Audio Access | ✅ Full PCM data | ⚠️ Web Audio API |
468
+ | Use Cases | Bots, recording, AI | Client apps |
469
+ | E2EE | ✅ OdinCipher | ✅ OdinCipher |
470
+
471
+ The Node.js SDK is optimized for server-side use cases like:
472
+ - 🎙️ Audio recording bots
473
+ - 🤖 AI-powered voice assistants
474
+ - 📝 Speech-to-text transcription
475
+ - 🛡️ Content moderation
476
+ - 🔊 Audio processing pipelines
477
+
478
+ ---
65
479
 
66
480
  ## Examples
67
481
 
68
- We provide a few examples in the `tests` folder to get you started quickly.
482
+ Check the `tests/` folder for complete examples:
483
+
484
+ - **[connection-test](tests/connection-test/)** - Basic connection, events, and diagnostics
485
+ - **[audio-recording](tests/audio-recording/)** - Recording peer audio to WAV files
486
+ - **[sending-audio](tests/sending-audio/)** - Sending audio with both high-level and low-level APIs
487
+
488
+ ---
489
+
490
+ ## Troubleshooting
491
+
492
+ ### Build Errors
493
+
494
+ If you encounter build errors, ensure you have the required tools:
495
+
496
+ ```bash
497
+ # macOS
498
+ xcode-select --install
499
+
500
+ # Ubuntu/Debian
501
+ sudo apt-get install build-essential python3
502
+
503
+ # Windows
504
+ npm install --global windows-build-tools
505
+ ```
506
+
507
+ ### macOS Security Warnings
508
+
509
+ If you see "code signature not valid" errors:
510
+
511
+ ```bash
512
+ cd node_modules/@4players/odin-nodejs/build/Debug
513
+ xattr -cr *.dylib
514
+ codesign -f -s - *.dylib
515
+ ```
516
+
517
+ ### Connection Issues
518
+
519
+ 1. Verify your access key is correct
520
+ 2. Check your network allows WebSocket connections
521
+ 3. Ensure the token hasn't expired
522
+
523
+ ---
524
+
525
+ ## Development
526
+
527
+ ### Building for Other Platforms
528
+
529
+ This package includes prebuilt binaries for common platforms (macOS x64/arm64, Windows x64, Linux x64). If you need to build for a different platform or architecture, follow these steps:
530
+
531
+ #### 1. Install Build Requirements
532
+
533
+ You'll need a C++ compiler toolchain:
534
+
535
+ ```bash
536
+ # macOS
537
+ xcode-select --install
538
+
539
+ # Ubuntu/Debian
540
+ sudo apt-get install build-essential python3
541
+
542
+ # Windows
543
+ npm install --global windows-build-tools
544
+ ```
545
+
546
+ #### 2. Download ODIN SDK Libraries
547
+
548
+ Download the ODIN SDK libraries from the [official releases](https://github.com/4Players/odin-sdk/releases/tag/v1.8.2):
549
+
550
+ 1. Download the appropriate archive for your platform from the release assets
551
+ 2. Extract the libraries to the correct location:
552
+
553
+ | Platform | Architecture | Target Directory |
554
+ |----------|--------------|------------------|
555
+ | Linux | x64 | `libs/bin/linux/x64/` |
556
+ | Linux | arm64 | `libs/bin/linux/arm64/` |
557
+ | Linux | ia32 | `libs/bin/linux/ia32/` |
558
+ | macOS | Universal | `libs/bin/macos/universal/` |
559
+ | Windows | x64 | `libs/bin/windows/x64/` |
560
+ | Windows | ia32 | `libs/bin/windows/ia32/` |
561
+
562
+ The SDK archive contains these library files:
563
+ - **Linux**: `libodin_static.a`, `libodin.so`, `libodin_crypto_static.a`, `libodin_crypto.so`
564
+ - **macOS**: `libodin.dylib`, `libodin_crypto.dylib`, `libodin_static.a`, `libodin_crypto_static.a`
565
+ - **Windows**: `odin_static.lib`, `odin.dll`, `odin_crypto_static.lib`, `odin_crypto.dll`
566
+
567
+ #### 3. Build the Native Module
568
+
569
+ ```bash
570
+ # Build in debug mode
571
+ npm run build:debug
572
+
573
+ # Build in release mode
574
+ npm run build:release
575
+ ```
576
+
577
+ #### 4. Verify the Build
578
+
579
+ ```bash
580
+ node -e "const odin = require('./index.cjs'); console.log('ODIN SDK loaded:', !!odin.OdinClient);"
581
+ ```
582
+
583
+ ### Project Structure
584
+
585
+ ```
586
+ ├── cppsrc/ # C++ native bindings source code
587
+ ├── libs/
588
+ │ ├── bin/ # ODIN SDK binaries (all platforms)
589
+ │ │ ├── linux/ # Linux binaries (x64, arm64, ia32)
590
+ │ │ ├── macos/ # macOS binaries (arm64, x64, universal)
591
+ │ │ └── windows/ # Windows binaries (x64, ia32)
592
+ │ └── include/ # ODIN SDK headers (odin.h, odin_crypto.h)
593
+ ├── index.cjs # JavaScript wrapper
594
+ ├── *.d.ts # TypeScript type definitions
595
+ └── tests/ # Example scripts
596
+ ```
597
+
598
+ ---
599
+
600
+ ## Contributing
601
+
602
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
603
+
604
+ 1. Fork the repository
605
+ 2. Create a feature branch
606
+ 3. Make your changes
607
+ 4. Submit a pull request
608
+
609
+ ---
610
+
611
+ ## Support
612
+
613
+ - 📖 **Documentation**: [docs.4players.io](https://docs.4players.io/voice/nodejs/)
614
+ - 💬 **Discord**: [Join our community](https://4np.de/discord)
615
+ - 📧 **Email**: support@4players.io
616
+ - 🐛 **Issues**: [GitHub Issues](https://github.com/4Players/odin-nodejs/issues)
617
+
618
+ ---
619
+
620
+ ## License
621
+
622
+ MIT License - see [LICENSE](LICENSE) for details.
623
+
624
+ ---
69
625
 
626
+ <p align="center">
627
+ Made with ❤️ by <a href="https://www.4players.io">4Players GmbH</a>
628
+ </p>