vivox-sdk-node 1.0.0

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.
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "vivox-sdk-node",
3
+ "version": "1.0.0",
4
+ "description": "High-performance, type-safe Node.js wrapper for the Vivox SDK.",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "scripts": {
8
+ "install": "node-gyp rebuild",
9
+ "build": "node-gyp rebuild",
10
+ "test": "node example_test.js",
11
+ "compile": "npx tsc"
12
+ },
13
+ "keywords": [
14
+ "vivox",
15
+ "voice",
16
+ "chat",
17
+ "sdk",
18
+ "unity",
19
+ "node-addon",
20
+ "typescript"
21
+ ],
22
+ "author": "",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "node-addon-api": "^8.7.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^25.7.0",
29
+ "node-gyp": "^12.3.0",
30
+ "typescript": "^6.0.3"
31
+ },
32
+ "files": [
33
+ "index.js",
34
+ "index.d.ts",
35
+ "binding.gyp",
36
+ "src/vivox_addon.cpp",
37
+ "vivox_sdk/include/*.h",
38
+ "vivox_sdk/lib/vivoxsdk.lib",
39
+ "vivox_sdk/lib/vivoxsdk.dll"
40
+ ]
41
+ }
@@ -0,0 +1,443 @@
1
+ #include <napi.h>
2
+ #include <Vxc.h>
3
+ #include <VxcRequests.h>
4
+ #include <VxcResponses.h>
5
+ #include <VxcEvents.h>
6
+ #include <thread>
7
+ #include <atomic>
8
+ #include <string>
9
+ #include <vector>
10
+
11
+ class VivoxAddon : public Napi::Addon<VivoxAddon> {
12
+ public:
13
+ VivoxAddon(Napi::Env env, Napi::Object exports) {
14
+ DefineAddon(exports, {
15
+ InstanceMethod("initialize", &VivoxAddon::Initialize),
16
+ InstanceMethod("uninitialize", &VivoxAddon::Uninitialize),
17
+ InstanceMethod("getVersion", &VivoxAddon::GetVersion),
18
+ InstanceMethod("setEventCallback", &VivoxAddon::SetEventCallback),
19
+ InstanceMethod("connectorCreate", &VivoxAddon::ConnectorCreate),
20
+ InstanceMethod("login", &VivoxAddon::Login),
21
+ InstanceMethod("joinChannel", &VivoxAddon::JoinChannel),
22
+ InstanceMethod("setCaptureDevice", &VivoxAddon::SetCaptureDevice),
23
+ InstanceMethod("getCaptureDevices", &VivoxAddon::GetCaptureDevices),
24
+ InstanceMethod("setRenderDevice", &VivoxAddon::SetRenderDevice),
25
+ InstanceMethod("getRenderDevices", &VivoxAddon::GetRenderDevices),
26
+ InstanceMethod("loginAnonymous", &VivoxAddon::LoginAnonymous),
27
+ InstanceMethod("setLocalMicVolume", &VivoxAddon::SetLocalMicVolume),
28
+ InstanceMethod("setLocalSpeakerVolume", &VivoxAddon::SetLocalSpeakerVolume),
29
+ InstanceMethod("set3DPosition", &VivoxAddon::Set3DPosition),
30
+ InstanceMethod("sendMessage", &VivoxAddon::SendMessage),
31
+ InstanceMethod("setParticipantMuteForMe", &VivoxAddon::SetParticipantMuteForMe),
32
+ InstanceMethod("setParticipantVolumeForMe", &VivoxAddon::SetParticipantVolumeForMe),
33
+ InstanceMethod("kickUser", &VivoxAddon::KickUser),
34
+ InstanceMethod("channelMuteUser", &VivoxAddon::ChannelMuteUser),
35
+ InstanceMethod("startAudioInjection", &VivoxAddon::StartAudioInjection),
36
+ InstanceMethod("stopAudioInjection", &VivoxAddon::StopAudioInjection),
37
+ InstanceMethod("muteLocalMic", &VivoxAddon::MuteLocalMic)
38
+ });
39
+ m_running = false;
40
+ }
41
+
42
+ private:
43
+ std::atomic<bool> m_running;
44
+ std::thread m_messageThread;
45
+ Napi::ThreadSafeFunction m_tsfn;
46
+
47
+ Napi::Value MuteLocalMic(const Napi::CallbackInfo& info) {
48
+ Napi::Env env = info.Env();
49
+ std::string connectorHandle = info[0].As<Napi::String>();
50
+ int mute = info[1].As<Napi::Number>().Int32Value();
51
+
52
+ vx_req_connector_mute_local_mic_t *req;
53
+ vx_req_connector_mute_local_mic_create(&req);
54
+ req->connector_handle = vx_strdup(connectorHandle.c_str());
55
+ req->mute_level = mute;
56
+
57
+ return Napi::Number::New(env, vx_issue_request(&req->base));
58
+ }
59
+
60
+ Napi::Value StartAudioInjection(const Napi::CallbackInfo& info) {
61
+ Napi::Env env = info.Env();
62
+ std::string accountHandle = info[0].As<Napi::String>();
63
+ std::string filename = info[1].As<Napi::String>();
64
+
65
+ vx_req_sessiongroup_control_audio_injection_t *req;
66
+ vx_req_sessiongroup_control_audio_injection_create(&req);
67
+
68
+ req->sessiongroup_handle = vx_strdup((accountHandle + "_group").c_str());
69
+ req->audio_injection_control_type = vx_sessiongroup_audio_injection_control_start;
70
+ req->filename = vx_strdup(filename.c_str());
71
+
72
+ return Napi::Number::New(env, vx_issue_request(&req->base));
73
+ }
74
+
75
+ Napi::Value StopAudioInjection(const Napi::CallbackInfo& info) {
76
+ Napi::Env env = info.Env();
77
+ std::string accountHandle = info[0].As<Napi::String>();
78
+
79
+ vx_req_sessiongroup_control_audio_injection_t *req;
80
+ vx_req_sessiongroup_control_audio_injection_create(&req);
81
+
82
+ req->sessiongroup_handle = vx_strdup((accountHandle + "_group").c_str());
83
+ req->audio_injection_control_type = vx_sessiongroup_audio_injection_control_stop;
84
+
85
+ return Napi::Number::New(env, vx_issue_request(&req->base));
86
+ }
87
+
88
+ Napi::Value KickUser(const Napi::CallbackInfo& info) {
89
+ Napi::Env env = info.Env();
90
+ std::string accountHandle = info[0].As<Napi::String>();
91
+ std::string channelUri = info[1].As<Napi::String>();
92
+ std::string participantUri = info[2].As<Napi::String>();
93
+
94
+ vx_req_channel_kick_user_t *req;
95
+ vx_req_channel_kick_user_create(&req);
96
+ req->account_handle = vx_strdup(accountHandle.c_str());
97
+ req->channel_uri = vx_strdup(channelUri.c_str());
98
+ req->participant_uri = vx_strdup(participantUri.c_str());
99
+
100
+ return Napi::Number::New(env, vx_issue_request(&req->base));
101
+ }
102
+
103
+ Napi::Value ChannelMuteUser(const Napi::CallbackInfo& info) {
104
+ Napi::Env env = info.Env();
105
+ std::string accountHandle = info[0].As<Napi::String>();
106
+ std::string channelUri = info[1].As<Napi::String>();
107
+ std::string participantUri = info[2].As<Napi::String>();
108
+ int mute = info[3].As<Napi::Number>().Int32Value();
109
+
110
+ vx_req_channel_mute_user_t *req;
111
+ vx_req_channel_mute_user_create(&req);
112
+ req->account_handle = vx_strdup(accountHandle.c_str());
113
+ req->channel_uri = vx_strdup(channelUri.c_str());
114
+ req->participant_uri = vx_strdup(participantUri.c_str());
115
+ req->set_muted = mute;
116
+
117
+ return Napi::Number::New(env, vx_issue_request(&req->base));
118
+ }
119
+
120
+ Napi::Value SendMessage(const Napi::CallbackInfo& info) {
121
+ Napi::Env env = info.Env();
122
+ std::string sessionHandle = info[0].As<Napi::String>();
123
+ std::string messageBody = info[1].As<Napi::String>();
124
+
125
+ vx_req_session_send_message_t *req;
126
+ vx_req_session_send_message_create(&req);
127
+ req->session_handle = vx_strdup(sessionHandle.c_str());
128
+ req->message_body = vx_strdup(messageBody.c_str());
129
+
130
+ int status = vx_issue_request(&req->base);
131
+ return Napi::Number::New(env, status);
132
+ }
133
+
134
+ Napi::Value SetParticipantMuteForMe(const Napi::CallbackInfo& info) {
135
+ Napi::Env env = info.Env();
136
+ std::string sessionHandle = info[0].As<Napi::String>();
137
+ std::string participantUri = info[1].As<Napi::String>();
138
+ int mute = info[2].As<Napi::Number>().Int32Value();
139
+
140
+ vx_req_session_set_participant_mute_for_me_t *req;
141
+ vx_req_session_set_participant_mute_for_me_create(&req);
142
+ req->session_handle = vx_strdup(sessionHandle.c_str());
143
+ req->participant_uri = vx_strdup(participantUri.c_str());
144
+ req->mute = mute;
145
+
146
+ int status = vx_issue_request(&req->base);
147
+ return Napi::Number::New(env, status);
148
+ }
149
+
150
+ Napi::Value SetParticipantVolumeForMe(const Napi::CallbackInfo& info) {
151
+ Napi::Env env = info.Env();
152
+ std::string sessionHandle = info[0].As<Napi::String>();
153
+ std::string participantUri = info[1].As<Napi::String>();
154
+ int volume = info[2].As<Napi::Number>().Int32Value();
155
+
156
+ vx_req_session_set_participant_volume_for_me_t *req;
157
+ vx_req_session_set_participant_volume_for_me_create(&req);
158
+ req->session_handle = vx_strdup(sessionHandle.c_str());
159
+ req->participant_uri = vx_strdup(participantUri.c_str());
160
+ req->volume = volume;
161
+
162
+ int status = vx_issue_request(&req->base);
163
+ return Napi::Number::New(env, status);
164
+ }
165
+
166
+ Napi::Value GetVersion(const Napi::CallbackInfo& info) {
167
+ return Napi::String::New(info.Env(), vx_get_sdk_version_info_ex());
168
+ }
169
+
170
+ Napi::Value Initialize(const Napi::CallbackInfo& info) {
171
+ Napi::Env env = info.Env();
172
+ vx_sdk_config_t config;
173
+ vx_get_default_config3(&config, sizeof(config));
174
+
175
+ config.use_access_tokens = 1;
176
+ config.never_rtp_timeout_ms = 15000;
177
+ config.lost_rtp_timeout_ms = 40000;
178
+
179
+ int status = vx_initialize3(&config, sizeof(config));
180
+ if (status == 0 && !m_running) {
181
+ m_running = true;
182
+ m_messageThread = std::thread(&VivoxAddon::MessageLoop, this);
183
+ }
184
+ return Napi::Number::New(env, status);
185
+ }
186
+
187
+ Napi::Value ConnectorCreate(const Napi::CallbackInfo& info) {
188
+ Napi::Env env = info.Env();
189
+ std::string server = info[0].As<Napi::String>();
190
+ std::string handle = info.Length() > 1 ? info[1].As<Napi::String>().Utf8Value() : "";
191
+
192
+ vx_req_connector_create_t *req;
193
+ vx_req_connector_create_create(&req);
194
+ req->acct_mgmt_server = vx_strdup(server.c_str());
195
+ if (!handle.empty()) {
196
+ req->connector_handle = vx_strdup(handle.c_str());
197
+ }
198
+
199
+ int status = vx_issue_request(&req->base);
200
+ return Napi::Number::New(env, status);
201
+ }
202
+
203
+ Napi::Value Login(const Napi::CallbackInfo& info) {
204
+ Napi::Env env = info.Env();
205
+ std::string connectorHandle = info[0].As<Napi::String>();
206
+ std::string accountUri = info[1].As<Napi::String>();
207
+ std::string token = info[2].As<Napi::String>();
208
+
209
+ vx_req_account_authtoken_login_t *req;
210
+ vx_req_account_authtoken_login_create(&req);
211
+
212
+ req->connector_handle = vx_strdup(connectorHandle.c_str());
213
+ req->authtoken = vx_strdup(token.c_str());
214
+ req->account_handle = vx_strdup(accountUri.c_str());
215
+
216
+ int status = vx_issue_request(&req->base);
217
+ return Napi::Number::New(env, status);
218
+ }
219
+
220
+ Napi::Value LoginAnonymous(const Napi::CallbackInfo& info) {
221
+ Napi::Env env = info.Env();
222
+ std::string connectorHandle = info[0].As<Napi::String>();
223
+ std::string accountUri = info[1].As<Napi::String>();
224
+ std::string token = info[2].As<Napi::String>();
225
+
226
+ vx_req_account_anonymous_login_t *req;
227
+ vx_req_account_anonymous_login_create(&req);
228
+
229
+ req->connector_handle = vx_strdup(connectorHandle.c_str());
230
+ req->access_token = vx_strdup(token.c_str());
231
+ req->account_handle = vx_strdup(accountUri.c_str());
232
+
233
+ size_t sipPos = accountUri.find("sip:");
234
+ size_t atPos = accountUri.find("@");
235
+ if (sipPos != std::string::npos && atPos != std::string::npos) {
236
+ std::string acctName = accountUri.substr(sipPos + 4, atPos - (sipPos + 4));
237
+ req->acct_name = vx_strdup(acctName.c_str());
238
+ }
239
+
240
+ int status = vx_issue_request(&req->base);
241
+ return Napi::Number::New(env, status);
242
+ }
243
+
244
+ Napi::Value JoinChannel(const Napi::CallbackInfo& info) {
245
+ Napi::Env env = info.Env();
246
+ std::string accountHandle = info[0].As<Napi::String>();
247
+ std::string channelUri = info[1].As<Napi::String>();
248
+ std::string token = info[2].As<Napi::String>();
249
+
250
+ vx_req_sessiongroup_add_session_t *req;
251
+ vx_req_sessiongroup_add_session_create(&req);
252
+
253
+ req->account_handle = vx_strdup(accountHandle.c_str());
254
+ req->sessiongroup_handle = vx_strdup((accountHandle + "_group").c_str());
255
+ req->session_handle = vx_strdup((channelUri + "_sess").c_str());
256
+ req->uri = vx_strdup(channelUri.c_str());
257
+ req->access_token = vx_strdup(token.c_str());
258
+
259
+ req->connect_audio = 1;
260
+ req->connect_text = 1;
261
+
262
+ int status = vx_issue_request(&req->base);
263
+ return Napi::Number::New(env, status);
264
+ }
265
+
266
+ Napi::Value SetCaptureDevice(const Napi::CallbackInfo& info) {
267
+ Napi::Env env = info.Env();
268
+ std::string deviceId = info[0].As<Napi::String>();
269
+ vx_req_aux_set_capture_device_t *req;
270
+ vx_req_aux_set_capture_device_create(&req);
271
+ req->capture_device_specifier = vx_strdup(deviceId.c_str());
272
+ return Napi::Number::New(env, vx_issue_request(&req->base));
273
+ }
274
+
275
+ Napi::Value GetCaptureDevices(const Napi::CallbackInfo& info) {
276
+ Napi::Env env = info.Env();
277
+ vx_req_aux_get_capture_devices_t *req;
278
+ vx_req_aux_get_capture_devices_create(&req);
279
+ return Napi::Number::New(env, vx_issue_request(&req->base));
280
+ }
281
+
282
+ Napi::Value SetRenderDevice(const Napi::CallbackInfo& info) {
283
+ Napi::Env env = info.Env();
284
+ std::string deviceId = info[0].As<Napi::String>();
285
+ vx_req_aux_set_render_device_t *req;
286
+ vx_req_aux_set_render_device_create(&req);
287
+ req->render_device_specifier = vx_strdup(deviceId.c_str());
288
+ return Napi::Number::New(env, vx_issue_request(&req->base));
289
+ }
290
+
291
+ Napi::Value GetRenderDevices(const Napi::CallbackInfo& info) {
292
+ Napi::Env env = info.Env();
293
+ vx_req_aux_get_render_devices_t *req;
294
+ vx_req_aux_get_render_devices_create(&req);
295
+ return Napi::Number::New(env, vx_issue_request(&req->base));
296
+ }
297
+
298
+ Napi::Value SetLocalMicVolume(const Napi::CallbackInfo& info) {
299
+ Napi::Env env = info.Env();
300
+ int volume = info[0].As<Napi::Number>().Int32Value();
301
+ vx_req_connector_set_local_mic_volume_t *req;
302
+ vx_req_connector_set_local_mic_volume_create(&req);
303
+ req->volume = volume;
304
+ return Napi::Number::New(env, vx_issue_request(&req->base));
305
+ }
306
+
307
+ Napi::Value SetLocalSpeakerVolume(const Napi::CallbackInfo& info) {
308
+ Napi::Env env = info.Env();
309
+ int volume = info[0].As<Napi::Number>().Int32Value();
310
+ vx_req_connector_set_local_speaker_volume_t *req;
311
+ vx_req_connector_set_local_speaker_volume_create(&req);
312
+ req->volume = volume;
313
+ return Napi::Number::New(env, vx_issue_request(&req->base));
314
+ }
315
+
316
+ Napi::Value Set3DPosition(const Napi::CallbackInfo& info) {
317
+ Napi::Env env = info.Env();
318
+ double posX = info[1].As<Napi::Number>().DoubleValue();
319
+ double posY = info[2].As<Napi::Number>().DoubleValue();
320
+ double posZ = info[3].As<Napi::Number>().DoubleValue();
321
+ std::string sessionHandle = info[4].As<Napi::String>();
322
+
323
+ vx_req_session_set_3d_position_t *req;
324
+ vx_req_session_set_3d_position_create(&req);
325
+ req->session_handle = vx_strdup(sessionHandle.c_str());
326
+
327
+ req->speaker_position[0] = posX;
328
+ req->speaker_position[1] = posY;
329
+ req->speaker_position[2] = posZ;
330
+
331
+ req->listener_position[0] = posX;
332
+ req->listener_position[1] = posY;
333
+ req->listener_position[2] = posZ;
334
+
335
+ req->speaker_at_orientation[2] = -1.0;
336
+ req->speaker_up_orientation[1] = 1.0;
337
+ req->speaker_left_orientation[0] = -1.0;
338
+
339
+ req->listener_at_orientation[2] = -1.0;
340
+ req->listener_up_orientation[1] = 1.0;
341
+ req->listener_left_orientation[0] = -1.0;
342
+
343
+ return Napi::Number::New(env, vx_issue_request(&req->base));
344
+ }
345
+
346
+ Napi::Value Uninitialize(const Napi::CallbackInfo& info) {
347
+ m_running = false;
348
+ if (m_messageThread.joinable()) m_messageThread.join();
349
+ vx_uninitialize();
350
+ if (m_tsfn) {
351
+ m_tsfn.Release();
352
+ m_tsfn = nullptr;
353
+ }
354
+ return info.Env().Undefined();
355
+ }
356
+
357
+ Napi::Value SetEventCallback(const Napi::CallbackInfo& info) {
358
+ if (m_tsfn) {
359
+ m_tsfn.Release();
360
+ }
361
+ m_tsfn = Napi::ThreadSafeFunction::New(info.Env(), info[0].As<Napi::Function>(), "VivoxCB", 0, 1);
362
+ return info.Env().Undefined();
363
+ }
364
+
365
+ void MessageLoop() {
366
+ while (m_running) {
367
+ vx_message_base_t *msg = nullptr;
368
+ if (vx_get_message(&msg) == 0 && msg != nullptr) {
369
+ m_tsfn.BlockingCall(msg, [](Napi::Env env, Napi::Function jsCb, vx_message_base_t* m) {
370
+ Napi::HandleScope scope(env);
371
+ Napi::Object obj = Napi::Object::New(env);
372
+ obj.Set("type", (double)m->type);
373
+
374
+ if (m->type == msg_response) {
375
+ vx_resp_base_t *resp = (vx_resp_base_t*)m;
376
+ obj.Set("resp_type", (double)resp->type);
377
+ obj.Set("status", (double)resp->status_code);
378
+ if (resp->status_string) obj.Set("status_string", resp->status_string);
379
+
380
+ if (resp->type == resp_aux_get_capture_devices) {
381
+ vx_resp_aux_get_capture_devices_t *dResp = (vx_resp_aux_get_capture_devices_t*)m;
382
+ Napi::Array devices = Napi::Array::New(env, dResp->count);
383
+ for (int i = 0; i < dResp->count; ++i) {
384
+ Napi::Object dev = Napi::Object::New(env);
385
+ if (dResp->capture_devices[i]->display_name) dev.Set("name", dResp->capture_devices[i]->display_name);
386
+ if (dResp->capture_devices[i]->device) dev.Set("id", dResp->capture_devices[i]->device);
387
+ devices.Set(i, dev);
388
+ }
389
+ obj.Set("devices", devices);
390
+ } else if (resp->type == resp_aux_get_render_devices) {
391
+ vx_resp_aux_get_render_devices_t *dResp = (vx_resp_aux_get_render_devices_t*)m;
392
+ Napi::Array devices = Napi::Array::New(env, dResp->count);
393
+ for (int i = 0; i < dResp->count; ++i) {
394
+ Napi::Object dev = Napi::Object::New(env);
395
+ if (dResp->render_devices[i]->display_name) dev.Set("name", dResp->render_devices[i]->display_name);
396
+ if (dResp->render_devices[i]->device) dev.Set("id", dResp->render_devices[i]->device);
397
+ devices.Set(i, dev);
398
+ }
399
+ obj.Set("devices", devices);
400
+ }
401
+ } else if (m->type == msg_event) {
402
+ vx_evt_base_t *evt = (vx_evt_base_t*)m;
403
+ obj.Set("evt_type", (double)evt->type);
404
+
405
+ if (evt->type == evt_account_login_state_change) {
406
+ vx_evt_account_login_state_change_t *lEvt = (vx_evt_account_login_state_change_t*)m;
407
+ obj.Set("state", (double)lEvt->state);
408
+ if (lEvt->account_handle) obj.Set("account_handle", lEvt->account_handle);
409
+ } else if (evt->type == evt_participant_added) {
410
+ vx_evt_participant_added_t *pEvt = (vx_evt_participant_added_t*)m;
411
+ if (pEvt->participant_uri) obj.Set("participant_uri", pEvt->participant_uri);
412
+ obj.Set("is_current_user", (double)pEvt->is_current_user);
413
+ } else if (evt->type == evt_participant_removed) {
414
+ vx_evt_participant_removed_t *pEvt = (vx_evt_participant_removed_t*)m;
415
+ if (pEvt->participant_uri) obj.Set("participant_uri", pEvt->participant_uri);
416
+ obj.Set("reason", (double)pEvt->reason);
417
+ } else if (evt->type == evt_participant_updated) {
418
+ vx_evt_participant_updated_t *pEvt = (vx_evt_participant_updated_t*)m;
419
+ if (pEvt->participant_uri) obj.Set("participant_uri", pEvt->participant_uri);
420
+ obj.Set("is_speaking", (double)pEvt->is_speaking);
421
+ obj.Set("energy", pEvt->energy);
422
+ } else if (evt->type == evt_connection_state_changed) {
423
+ vx_evt_connection_state_changed_t *cEvt = (vx_evt_connection_state_changed_t*)m;
424
+ obj.Set("connection_state", (double)cEvt->connection_state);
425
+ } else if (evt->type == evt_message) {
426
+ vx_evt_message_t *mEvt = (vx_evt_message_t*)m;
427
+ if (mEvt->participant_uri) obj.Set("participant_uri", mEvt->participant_uri);
428
+ if (mEvt->message_body) obj.Set("message", mEvt->message_body);
429
+ if (mEvt->session_handle) obj.Set("session_handle", mEvt->session_handle);
430
+ }
431
+ }
432
+
433
+ jsCb.Call({obj});
434
+ vx_destroy_message(m);
435
+ });
436
+ } else {
437
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
438
+ }
439
+ }
440
+ }
441
+ };
442
+
443
+ NODE_API_ADDON(VivoxAddon)