@4players/odin-nodejs 0.7.3 → 0.8.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/README.md CHANGED
@@ -3,7 +3,15 @@
3
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
4
  wraps the C++ code into a Node JS module.
5
5
 
6
- Please check out the typings in `index.d.ts` for more information on the API.
6
+ Check out documentation and guides at [developers.4players.io](https://www.4players.io/odin/sdk/nodejs).
7
+
8
+ ## Beta!
9
+
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.
13
+
14
+ If you have any questions or feedback, please reach out to us at our [Discord Server](https://4np.de/discord).
7
15
 
8
16
  ## ODIN Node JS compared to Web SDK
9
17
 
package/binding.gyp CHANGED
@@ -1,61 +1,63 @@
1
1
  {
2
- "targets": [{
3
- "target_name": "odin",
4
- "sources": [
5
- "cppsrc/binding.cpp",
6
- "cppsrc/odinbindings.cpp",
7
- "cppsrc/odinclient.cpp",
8
- "cppsrc/odinroom.cpp",
9
- "cppsrc/odinmedia.cpp",
10
- "cppsrc/utilities.cpp"
11
- ],
12
- 'dependencies': [
13
- "<!(node -p \"require('node-addon-api').gyp\")"
14
- ],
15
- 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
16
- 'cflags!': [ '-fno-exceptions' ],
17
- 'cflags_cc!': [ '-fno-exceptions' ],
18
- 'conditions': [
19
- ["OS=='linux'", {
20
- 'libraries': [
21
- "<(module_root_dir)/libs/bin/linux/<(target_arch)/libodin_static.a"
22
- ],
23
- 'include_dirs': [
24
- "<!@(node -p \"require('node-addon-api').include\")",
25
- "<(module_root_dir)/libs/include"
26
- ],
27
- }],
28
- ["OS=='win'", {
29
- "defines": [
30
- "_HAS_EXCEPTIONS=1"
31
- ],
32
- "msvs_settings": {
33
- "VCCLCompilerTool": {
34
- "ExceptionHandling": 1
35
- },
36
- },
37
- 'runtime_library': [
38
- "<(module_root_dir)/libs/bin/windows/<(target_arch)/odin_static.lib"
39
- ],
40
- 'include_dirs': [
41
- "<!@(node -p \"require('node-addon-api').include\")",
42
- "<(module_root_dir)/libs/include"
43
- ],
44
- }],
45
- ["OS=='mac'", {
46
- 'xcode_settings': {
47
- 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
48
- 'CLANG_CXX_LIBRARY': 'libc++',
49
- 'MACOSX_DEPLOYMENT_TARGET': '10.12',
50
- },
51
- 'libraries': [
52
- "<(module_root_dir)/libs/bin/macos/<(target_arch)/libodin_static.a"
53
- ],
54
- 'include_dirs': [
55
- "<!@(node -p \"require('node-addon-api').include\")",
56
- "<(module_root_dir)/libs/include"
57
- ],
58
- }],
2
+ "targets": [
3
+ {
4
+ "target_name": "odin",
5
+ "sources": [
6
+ "cppsrc/binding.cpp",
7
+ "cppsrc/odinbindings.cpp",
8
+ "cppsrc/odinclient.cpp",
9
+ "cppsrc/odinroom.cpp",
10
+ "cppsrc/odinmedia.cpp",
11
+ "cppsrc/utilities.cpp"
12
+ ],
13
+ "include_dirs": [
14
+ "<!@(node -p \"require('node-addon-api').include\")",
15
+ "<(module_root_dir)/libs/include"
16
+ ],
17
+ "dependencies": [
18
+ "<!(node -p \"require('node-addon-api').gyp\")"
19
+ ],
20
+ "conditions": [
21
+ ["OS=='linux'", {
22
+ "libraries": [
23
+ "-L<(module_root_dir)/libs/bin/linux/<(target_arch)",
24
+ "-lodin_static"
59
25
  ]
60
- }]
26
+ }],
27
+ ["OS=='mac'", {
28
+ "libraries": [
29
+ "-L<(module_root_dir)/libs/bin/macos/<(target_arch)",
30
+ "-lodin_static",
31
+ ]
32
+ }],
33
+ ["OS=='win'", {
34
+ "msvs_settings": {
35
+ "VCCLCompilerTool": {
36
+ "AdditionalOptions": [ "/MD" ],
37
+ },
38
+ "VCLinkerTool": {
39
+
40
+ },
41
+ },
42
+ "libraries": [
43
+ "<(module_root_dir)/libs/bin/windows/<(target_arch)/odin_static.lib",
44
+ "-lws2_32",
45
+ "-lbcrypt",
46
+ "-lucrt",
47
+ "-lvcruntime",
48
+ "-lwinmm",
49
+ "-lntdll"
50
+ ]
51
+ }]
52
+ ],
53
+ "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
54
+ "cflags!": [ "-fno-exceptions" ],
55
+ "cflags_cc!": [ "-fno-exceptions" ],
56
+ "xcode_settings": {
57
+ "GCC_ENABLE_CPP_EXCEPTIONS": "NO",
58
+ "CLANG_CXX_LIBRARY": "libc++",
59
+ "MACOSX_DEPLOYMENT_TARGET": "10.12",
60
+ },
61
+ }
62
+ ]
61
63
  }
@@ -72,7 +72,12 @@ OdinClient::OdinClient(const Napi::CallbackInfo& info) : Napi::ObjectWrap<OdinCl
72
72
  printf("Odin NodeJS Addon: Initializing Odin runtime (%s) with sample rate %f and %d channels\n", ODIN_VERSION, _sampleRate, _numChannels);
73
73
  #endif
74
74
 
75
- odin_startup_ex(ODIN_VERSION, _sampleRate, _numChannels == 1 ? OdinChannelLayout_Mono : OdinChannelLayout_Stereo);
75
+ // Create a OdinAudioStreamConfig struct to configure the audio stream
76
+ OdinAudioStreamConfig config;
77
+ config.sample_rate = _sampleRate;
78
+ config.channel_count = _numChannels;
79
+
80
+ odin_startup_ex(ODIN_VERSION, config);
76
81
  }
77
82
 
78
83
  /**
@@ -141,6 +146,8 @@ Napi::Value OdinClient::CreateRoom(const Napi::CallbackInfo &info) {
141
146
  if (!generator)
142
147
  {
143
148
  Napi::TypeError::New(env, "Failed to initialize token generator, invalid access key").ThrowAsJavaScriptException();
149
+ odin_token_generator_destroy(generator);
150
+ return env.Undefined();
144
151
  }
145
152
 
146
153
  /*
@@ -151,6 +158,8 @@ Napi::Value OdinClient::CreateRoom(const Napi::CallbackInfo &info) {
151
158
  if (odin_is_error(error))
152
159
  {
153
160
  Napi::TypeError::New(env, "Creating access token failed").ThrowAsJavaScriptException();
161
+ odin_token_generator_destroy(generator);
162
+ return env.Undefined();
154
163
  }
155
164
 
156
165
  /*
@@ -232,7 +232,7 @@ void OdinMedia::SendData(const Napi::CallbackInfo &info) {
232
232
  Napi::Float32Array array = info[0].As<Napi::Float32Array>();
233
233
 
234
234
  #ifdef DEBUG
235
- printf("Received %d bytes of audio data\n", (int)array.ElementLength());
235
+ printf("Sent %d bytes of audio data\n", (int)array.ElementLength());
236
236
  #endif
237
237
 
238
238
  OdinReturnCode result = odin_audio_push_data(_mediaStreamHandle, array.Data(), array.ElementLength());
@@ -331,7 +331,6 @@ Napi::Object OdinRoom::Init(Napi::Env env, Napi::Object exports) {
331
331
  Napi::Function func = DefineClass(env, "OdinRoom", {
332
332
  InstanceMethod<&OdinRoom::Join>("join", static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
333
333
  InstanceMethod<&OdinRoom::UpdatePeerUserData>("updateOwnUserData", static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
334
- InstanceMethod<&OdinRoom::UpdateRoomUserData>("updateRoomUserData", static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
335
334
  InstanceMethod<&OdinRoom::SendMessage>("sendMessage", static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
336
335
  InstanceMethod<&OdinRoom::SetEventListener>("setEventListener", static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
337
336
  InstanceMethod<&OdinRoom::AddEventListener>("addEventListener", static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
@@ -381,7 +380,7 @@ void OdinRoom::Join(const Napi::CallbackInfo &info) {
381
380
 
382
381
  if (info.Length() > 1) {
383
382
  Napi::Uint8Array data = info[1].As<Napi::Uint8Array>();
384
- OdinReturnCode error = odin_room_update_user_data(_roomHandle, OdinUserDataTarget_Peer, data.Data(), data.ByteLength());
383
+ OdinReturnCode error = odin_room_update_peer_user_data(_roomHandle, data.Data(), data.ByteLength());
385
384
  if (odin_is_error(error))
386
385
  {
387
386
  Napi::TypeError::New(env, "Setting initial room peer data failed").ThrowAsJavaScriptException();
@@ -475,7 +474,7 @@ void OdinRoom::UpdatePeerUserData(const Napi::CallbackInfo &info) {
475
474
 
476
475
  Napi::Uint8Array data = info[0].As<Napi::Uint8Array>();
477
476
 
478
- OdinReturnCode error = odin_room_update_user_data(_roomHandle, OdinUserDataTarget_Peer, data.Data(), data.ByteLength());
477
+ OdinReturnCode error = odin_room_update_peer_user_data(_roomHandle, data.Data(), data.ByteLength());
479
478
 
480
479
  if (odin_is_error(error))
481
480
  {
@@ -483,28 +482,6 @@ void OdinRoom::UpdatePeerUserData(const Napi::CallbackInfo &info) {
483
482
  }
484
483
  }
485
484
 
486
- /**
487
- * Updates the room user data. Requires an array of bytes as a parameter.
488
- * @param info
489
- * @return
490
- */
491
- void OdinRoom::UpdateRoomUserData(const Napi::CallbackInfo &info) {
492
- Napi::Env env = info.Env();
493
-
494
- if (info.Length() != 1 || !info[0].IsTypedArray()) {
495
- Napi::TypeError::New(env, "Data as byte array expected").ThrowAsJavaScriptException();
496
- }
497
-
498
- Napi::Uint8Array data = info[0].As<Napi::Uint8Array>();
499
-
500
- OdinReturnCode error = odin_room_update_user_data(_roomHandle, OdinUserDataTarget_Room, data.Data(), data.ByteLength());
501
-
502
- if (odin_is_error(error))
503
- {
504
- OdinUtilities::ThrowNapiException(env, error, "Failed to update room user data");
505
- }
506
- }
507
-
508
485
  /**
509
486
  * Sends a message to the room. Requires an array of bytes as a parameter and optinally a list of peer ids to send to.
510
487
  * Please note: If the list is NULL the message will be sent to all peers in the room. If the list is not NULL the message
@@ -544,7 +521,7 @@ void OdinRoom::SendMessage(const Napi::CallbackInfo &info) {
544
521
 
545
522
  if (odin_is_error(error))
546
523
  {
547
- OdinUtilities::ThrowNapiException(env, error, "Failed to update room user data");
524
+ OdinUtilities::ThrowNapiException(env, error, "Failed to send message");
548
525
  }
549
526
  }
550
527
 
package/cppsrc/odinroom.h CHANGED
@@ -47,7 +47,6 @@ private:
47
47
  void Close(const Napi::CallbackInfo& info);
48
48
  void Join(const Napi::CallbackInfo& info);
49
49
  void UpdatePeerUserData(const Napi::CallbackInfo &info);
50
- void UpdateRoomUserData(const Napi::CallbackInfo &info);
51
50
  void SendMessage(const Napi::CallbackInfo &info);
52
51
  void SetEventListener(const Napi::CallbackInfo &info);
53
52
  void AddEventListener(const Napi::CallbackInfo &info);
package/index.d.ts CHANGED
@@ -2,4 +2,10 @@ export * from "./odin.client";
2
2
  export * from "./odin.room";
3
3
  export * from "./odin.media";
4
4
 
5
+ /**
6
+ * Generates an access token for a room and user ID. You can use this to join a room with a custom access token.
7
+ * @param accessKey - The access key to use for this client. You can get one from https://developers.4players.io
8
+ * @param roomId - The ID of the room to create.
9
+ * @param userId - The ID of the user
10
+ */
5
11
  export declare function generateAccessToken(accessKey: string, roomId: string, userId: string): string;
Binary file
Binary file
@@ -10,23 +10,7 @@
10
10
  #include <stdint.h>
11
11
  #include <stdlib.h>
12
12
 
13
- #define ODIN_VERSION "1.4.0"
14
-
15
- #define Frame_SAMPLE_RATE 48000
16
-
17
- /**
18
- * Supported channel layouts in audio functions.
19
- */
20
- typedef enum OdinChannelLayout {
21
- /**
22
- * Samples are sequential
23
- */
24
- OdinChannelLayout_Mono,
25
- /**
26
- * Channels are interleaved
27
- */
28
- OdinChannelLayout_Stereo,
29
- } OdinChannelLayout;
13
+ #define ODIN_VERSION "1.6.4"
30
14
 
31
15
  /**
32
16
  * Known types of a media stream.
@@ -134,25 +118,11 @@ typedef enum OdinTokenAudience {
134
118
  } OdinTokenAudience;
135
119
 
136
120
  /**
137
- * Supported targets for user data updates.
138
- */
139
- typedef enum OdinUserDataTarget {
140
- /**
141
- * Individual user data for your own peer
142
- */
143
- OdinUserDataTarget_Peer,
144
- /**
145
- * Global user data for the room
146
- */
147
- OdinUserDataTarget_Room,
148
- } OdinUserDataTarget;
149
-
150
- /**
151
- * A pointer to a local ODIN token generator used to generate signed room tokens based based on an
152
- * access key. Please note, that access keys are your the unique authentication keys to be used to
153
- * generate room tokens for accessing the ODIN server network. For your own security, we strongly
154
- * recommend that you _NEVER_ put an access key in your client code and generate room tokens on a
155
- * server.
121
+ * A pointer to a local ODIN token generator, employed for generating signed room tokens predicated
122
+ * on an access key. Be aware that access keys serve as your unique authentication keys, requisite
123
+ * for generating room tokens to access the ODIN server network. To ensure your security, it's
124
+ * strongly recommended that you _NEVER_ embed an access key within your client code, and instead
125
+ * generate room tokens on a server.
156
126
  */
157
127
  typedef struct OdinTokenGenerator OdinTokenGenerator;
158
128
 
@@ -164,6 +134,20 @@ typedef struct OdinTokenGenerator OdinTokenGenerator;
164
134
  */
165
135
  typedef uint32_t OdinReturnCode;
166
136
 
137
+ /**
138
+ * Audio stream configuration.
139
+ */
140
+ typedef struct OdinAudioStreamConfig {
141
+ /**
142
+ * The number of samples per second in hertz (between 8000 and 192000)
143
+ */
144
+ uint32_t sample_rate;
145
+ /**
146
+ * The number of channels for the new audio stream (between 1 and 2)
147
+ */
148
+ uint8_t channel_count;
149
+ } OdinAudioStreamConfig;
150
+
167
151
  /**
168
152
  * Internal handle identifier for an ODIN room to interact with.
169
153
  */
@@ -491,20 +475,6 @@ typedef struct OdinApmConfig {
491
475
  bool gain_controller;
492
476
  } OdinApmConfig;
493
477
 
494
- /**
495
- * Audio stream configuration.
496
- */
497
- typedef struct OdinAudioStreamConfig {
498
- /**
499
- * The number of samples per second in hertz (between 8000 and 192000)
500
- */
501
- uint32_t sample_rate;
502
- /**
503
- * The number of channels for the new audio stream (between 1 and 2)
504
- */
505
- uint8_t channel_count;
506
- } OdinAudioStreamConfig;
507
-
508
478
  /**
509
479
  * Audio stream statistics.
510
480
  */
@@ -583,30 +553,30 @@ size_t odin_error_format(OdinReturnCode error, char *buf, size_t buf_len);
583
553
  bool odin_is_error(OdinReturnCode code);
584
554
 
585
555
  /**
586
- * Starts the internal ODIN client runtime using recommended settings for audio output and verifies
587
- * that the correct API header file is used. This is ref-counted so you need matching calls of startup
588
- * and shutdown in your application. A lot of the functions in the API require a running ODIN runtime.
589
- * With the only exception being the `access_key` and `token_generator` related functions.
556
+ * Initializes the internal ODIN client runtime with optimized settings for audio output, ensuring
557
+ * the correct API header file is employed. This operation is ref-counted, necessitating paired
558
+ * invocations of startup and shutdown within your application. The majority of the API functions
559
+ * hinge on an active ODIN runtime, with the sole exception of `access_key` and `token_generator`
560
+ * related functions.
590
561
  *
591
- * Note: Use `ODIN_VERSION` to pass the `version` argument.
562
+ * Note: Utilize `ODIN_VERSION` to supply the `version` argument.
592
563
  */
593
564
  bool odin_startup(const char *version);
594
565
 
595
566
  /**
596
- * Starts the internal ODIN client runtime and allows passing the sample rate and channel layout
597
- * for audio output. This is ref-counted so you need matching calls of startup and shutdown in your
598
- * application.
567
+ * Initializes the internal ODIN client runtime, permitting the specification of sample rate and
568
+ * channel layout for audio output. This operation is ref-counted, necessitating paired invocations
569
+ * of startup and shutdown within your application.
599
570
  *
600
- * Note: Make sure to use the same settings on consecutive calls of this function.
571
+ * Note: Ensure consistent settings are used on successive invocations of this function.
601
572
  */
602
- bool odin_startup_ex(const char *version,
603
- uint32_t output_sample_rate,
604
- enum OdinChannelLayout output_channel_layout);
573
+ bool odin_startup_ex(const char *version, struct OdinAudioStreamConfig output_config);
605
574
 
606
575
  /**
607
- * Terminates the internal ODIN runtime. This function _should_ be called before shutting down
608
- * the application. After calling this function all `odin_*` methods will fail immediately.
609
- * (Given the internal ref-count reached zero. See `odin_startup` for more information)
576
+ * Shuts down the internal ODIN runtime. It is advisable to invoke this function prior to
577
+ * terminating the application. Post invocation, all `odin_*` methods will cease to function
578
+ * immediately, provided the internal ref-count has descended to zero. Refer to `odin_startup`
579
+ * for additional details.
610
580
  */
611
581
  void odin_shutdown(void);
612
582
 
@@ -639,13 +609,13 @@ OdinReturnCode odin_room_set_event_callback(OdinRoomHandle room,
639
609
  void *extra_data);
640
610
 
641
611
  /**
642
- * Sets the scaling used for all coordinates passed to `odin_room_update_position`. This allows
643
- * adapting to the individual needs of your game coordinate system if necessary. Only peers within
644
- * a unit circle with a radius of `1.0` are able to 'see' each other. When changing the position
645
- * of a peer, the position must be scaled such as that the maximum distance is one or less. The
646
- * scaling can be done either manually or by setting the multiplicative scale here.
612
+ * Sets the scaling factor for coordinates supplied to `odin_room_update_position`, facilitating
613
+ * adaptation to your game's unique coordinate system requirements. Peers are visible to each other
614
+ * only within a unit circle of radius `1.0`. When altering a peer's position, ensure the position
615
+ * is scaled such that the maximum distance remains one or less. This scaling can be performed
616
+ * manually or by specifying the multiplicative scale here.
647
617
  *
648
- * Note: Please make sure that all of your client apps use the same scaling.
618
+ * Note: It's crucial to maintain consistent scaling across all client applications.
649
619
  */
650
620
  OdinReturnCode odin_room_set_position_scale(OdinRoomHandle room, float scale);
651
621
 
@@ -679,28 +649,26 @@ OdinReturnCode odin_room_peer_id(OdinRoomHandle room, uint64_t *out_peer_id);
679
649
  OdinReturnCode odin_room_connection_stats(OdinRoomHandle room, struct OdinConnectionStats *stats);
680
650
 
681
651
  /**
682
- * Updates the custom user data for either your own peer or the specified `OdinRoomHandle` itself.
683
- * All user data is synced automatically, which allows storing of arbitrary information for each
684
- * individual peer and even globally for the room if needed.
652
+ * Updates the custom user data for your own peer. All user data is synced automatically, which
653
+ * allows storing of arbitrary information for each individual peer.
685
654
  *
686
655
  * Note: Use this before calling `odin_room_join` to set initial peer user data upon connect.
687
656
  */
688
- OdinReturnCode odin_room_update_user_data(OdinRoomHandle room,
689
- enum OdinUserDataTarget target,
690
- const uint8_t *user_data,
691
- size_t user_data_length);
657
+ OdinReturnCode odin_room_update_peer_user_data(OdinRoomHandle room,
658
+ const uint8_t *user_data,
659
+ size_t user_data_length);
692
660
 
693
661
  /**
694
- * Updates the two-dimensional position of your own peer in the given `OdinRoomHandle`. The server
695
- * will use the specified coordinates for each peer in the same room to apply automatic culling
696
- * based on unit circles with a radius of `1.0`. This is ideal for any scenario, where you want to
697
- * put a very large number of peers into the same room and make them only 'see' each other while
698
- * being in proximity. Additionally, you can use `odin_room_set_position_scale` to adjust the
699
- * distance multiplier for position updates if needed.
662
+ * Updates the three-dimensional position of the current peer within the specified `OdinRoomHandle`.
663
+ * The server utilizes the provided coordinates to perform automatic culling among peers in the same
664
+ * room, based on unit circles with a radius of `1.0`. This feature is particularly beneficial in
665
+ * scenarios involving a large number of peers within the same room, enabling peers to interact or
666
+ * 'see' each other only when in close proximity. To modify the distance sensitivity for position
667
+ * updates, use `odin_room_set_position_scale`.
700
668
  *
701
669
  * Note: Use this before calling `odin_room_join` to set the initial peer position upon connect.
702
670
  */
703
- OdinReturnCode odin_room_update_position(OdinRoomHandle room, float x, float y);
671
+ OdinReturnCode odin_room_update_position(OdinRoomHandle room, float x, float y, float z);
704
672
 
705
673
  /**
706
674
  * Sends arbitrary data to a list of target peers over the ODIN server. If `NULL` is specified, the
@@ -725,12 +693,12 @@ OdinReturnCode odin_room_add_media(OdinRoomHandle room, OdinMediaStreamHandle me
725
693
  OdinReturnCode odin_room_configure_apm(OdinRoomHandle room, struct OdinApmConfig config);
726
694
 
727
695
  /**
728
- * Creates a new audio stream, which can be added to a room and send data over it.
696
+ * Creates a new audio input stream, which can be added to a room and send data over it.
729
697
  */
730
698
  OdinMediaStreamHandle odin_audio_stream_create(struct OdinAudioStreamConfig config);
731
699
 
732
700
  /**
733
- * Creates a new video stream, which can be added to a room and send data over it.
701
+ * Creates a new video input stream, which can be added to a room and send data over it.
734
702
  *
735
703
  * Note: Video streams are not supported yet.
736
704
  */
@@ -760,21 +728,45 @@ OdinReturnCode odin_media_stream_peer_id(OdinMediaStreamHandle stream, uint64_t
760
728
  */
761
729
  enum OdinMediaStreamType odin_media_stream_type(OdinMediaStreamHandle stream);
762
730
 
731
+ /**
732
+ * Instructs the server to pause the specified `OdinMediaStreamHandle`, ceasing the reception of
733
+ * data. This operation essentially communicates a server-side mute request from the client, thus
734
+ * indicating a desire to halt packet reception for this media stream.
735
+ */
736
+ OdinReturnCode odin_media_stream_pause(OdinMediaStreamHandle stream);
737
+
738
+ /**
739
+ * Instructs the server to resume the specified output `OdinMediaStreamHandle`, re-initiating the
740
+ * reception of data. This operation essentially communicates a server-side unmute request from the
741
+ * client, indicating a desire to restart packet reception for this media stream.
742
+ */
743
+ OdinReturnCode odin_media_stream_resume(OdinMediaStreamHandle stream);
744
+
763
745
  /**
764
746
  * Sends data to the audio stream. The data has to be interleaved [-1, 1] float data.
765
747
  */
766
748
  OdinReturnCode odin_audio_push_data(OdinMediaStreamHandle stream, const float *buf, size_t buf_len);
767
749
 
768
750
  /**
769
- * Reads audio data from the specified `OdinMediaStreamHandle`. This will return audio data in
770
- * 48kHz interleaved.
771
- *
772
- * Note: `out_channel_layout` is reserved for future use.
751
+ * Reads audio data from the specified `OdinMediaStreamHandle`. This will return audio data in the
752
+ * format specified when calling `odin_startup_ex` or 48 kHz interleaved by default.
773
753
  */
774
754
  OdinReturnCode odin_audio_read_data(OdinMediaStreamHandle stream,
775
755
  float *out_buffer,
776
756
  size_t out_buffer_len);
777
757
 
758
+ /**
759
+ * Returns the number of samples available in the audio buffer of an output `OdinMediaStreamHandle`.
760
+ */
761
+ OdinReturnCode odin_audio_data_len(OdinMediaStreamHandle stream);
762
+
763
+ /**
764
+ * Resets the specified `OdinMediaStreamHandle` to its initial state, restoring it to its default
765
+ * configuration. This operation resets the internal Opus encoder/decoder, ensuring a clean state.
766
+ * Additionally, it clears internal buffers, providing a fresh start.
767
+ */
768
+ OdinReturnCode odin_audio_reset(OdinMediaStreamHandle stream);
769
+
778
770
  /**
779
771
  * Retrieves statistics for the specified `OdinMediaStreamHandle`.
780
772
  *
@@ -784,9 +776,9 @@ OdinReturnCode odin_audio_stats(OdinMediaStreamHandle stream, struct OdinAudioSt
784
776
 
785
777
  /**
786
778
  * Reads up to `out_buffer_len` samples from the given streams and mixes them into the `out_buffer`.
787
- * All audio streams will be read based on a 48khz sample rate so make sure to allocate the buffer
788
- * accordingly. After the call the `out_buffer_len` will contain the amount of samples that have
789
- * actually been read and mixed into `out_buffer`.
779
+ * All audio streams will be read based on the sample rate you chose when initializing the ODIN runtime
780
+ * so make sure to allocate the buffer accordingly. After the call the `out_buffer_len` will contain
781
+ * the amount of samples that have actually been read and mixed into `out_buffer`.
790
782
  *
791
783
  * If enabled this will also apply any audio processing to the output stream and feed back required
792
784
  * data to the internal audio processing pipeline which requires a final mix.
@@ -803,6 +795,17 @@ OdinReturnCode odin_audio_mix_streams(OdinRoomHandle room,
803
795
  */
804
796
  OdinReturnCode odin_audio_process_reverse(OdinRoomHandle room, float *buffer, size_t buffer_len);
805
797
 
798
+ /**
799
+ * Sets the delay estimate for the reverse stream used in the ODIN echo cancellation. This function
800
+ * is important in scenarios where the audio output and the audio input are not synchronized. An
801
+ * accurate delay value ensures that the echo canceller can correctly align the two audio streams,
802
+ * resulting in effective echo cancellation.
803
+ *
804
+ * Improper delay values may lead to poor echo cancellation and thus degrade the quality of the
805
+ * audio communication.
806
+ */
807
+ OdinReturnCode odin_audio_set_stream_delay(OdinRoomHandle room, uint64_t delay_ms);
808
+
806
809
  /**
807
810
  * Creates a new ODIN resampler instance. This is intended for situations where your audio pipeline
808
811
  * doesn't support 48 kHz.
@@ -834,28 +837,29 @@ OdinReturnCode odin_resampler_process(OdinResamplerHandle resampler,
834
837
  OdinReturnCode odin_resampler_destroy(OdinResamplerHandle resampler);
835
838
 
836
839
  /**
837
- * Creates a new access key required to access the ODIN network. An access key is a 44 character
838
- * long Base64-String, which consists of a version, random bytes and a checksum.
840
+ * Creates a new access key crucial for signing tokens, facilitating access to an ODIN server. An
841
+ * access key is a 44-character long Base64 String, embodying a version identifier, random bytes,
842
+ * and a checksum.
839
843
  */
840
844
  OdinReturnCode odin_access_key_generate(char *buf, size_t buf_len);
841
845
 
842
846
  /**
843
- * Retrieves the key ID from a specified access key. The key ID is included in room tokens,
844
- * making it possible to identify which public key must be used for verification.
847
+ * Extracts the key ID from a specified access key. The key ID is embedded in room tokens, enabling
848
+ * the identification of the corresponding public key required for verification.
845
849
  */
846
850
  OdinReturnCode odin_access_key_id(const char *access_key, char *out_key_id, size_t out_key_id_len);
847
851
 
848
852
  /**
849
- * Retrieves the public key from a specified access key. The public key is based on the Ed25519
850
- * curve and must be submitted to _4Players_ so that a generated room token can be verified.
853
+ * Extracts the public key from a specified access key. The public key, derived from the Ed25519
854
+ * curve, must be shared with _4Players_ to enable verification of a generated room token.
851
855
  */
852
856
  OdinReturnCode odin_access_key_public_key(const char *access_key,
853
857
  char *out_public_key,
854
858
  size_t out_public_key_len);
855
859
 
856
860
  /**
857
- * Retrieves the secret key from a specified access key. The secret key is based on the Ed25519
858
- * curve and used to sign a generated room token to access the ODIN network.
861
+ * Extracts the private key from a specified access key. The private key, rooted in the Ed25519
862
+ * curve, is utilized to sign a generated room token for accessing the ODIN network.
859
863
  */
860
864
  OdinReturnCode odin_access_key_secret_key(const char *access_key,
861
865
  char *out_secret_key,
package/odin.client.d.ts CHANGED
@@ -7,16 +7,16 @@ import {OdinRoom} from "./odin.room";
7
7
  export declare class OdinClient {
8
8
  /**
9
9
  * Creates a new instance of a client with an access key.
10
- * @param accessKey {string} The access key to use for this client. You can get one from https://developers.4players.io
11
- * @param sampleRate {number} The sample rate of the audio stream (between 8000 and 48000)
12
- * @param channelCount {number} The number of channels of the audio stream (1 or 2)
10
+ * @param accessKey - The access key to use for this client. You can get one from https://developers.4players.io
11
+ * @param sampleRate - The sample rate of the audio stream (between 8000 and 48000)
12
+ * @param channelCount - The number of channels of the audio stream (1 or 2)
13
13
  */
14
14
  constructor(accessKey: string, sampleRate?: number, channelCount?: number);
15
15
 
16
16
  /**
17
17
  * Creates a new local room instance with the given ID and user ID. Use join to connect to that room.
18
- * @param roomId {string} The ID of the room to create.
19
- * @param userId {string} The ID of the user to create the room for.
18
+ * @param roomId - The ID of the room to create.
19
+ * @param userId - The ID of the user to create the room for.
20
20
  */
21
21
  createRoom(roomId: string, userId: string): OdinRoom;
22
22
  }
package/odin.media.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {OdinRoom} from "./odin.room";
1
+ import {OdinAPMSettings, OdinRoom} from "./odin.room";
2
2
 
3
3
  /**
4
4
  * The OdinMedia class. Represents a local media stream added to the room - i.e. a microphone, another audio stream like files.
@@ -7,11 +7,12 @@ import {OdinRoom} from "./odin.room";
7
7
  export declare class OdinMedia {
8
8
  /**
9
9
  * Creates a new instance of a media object. Don't create OdinMedia directly, use `createAudioStream` from OdinRoom instead.
10
- * @param room {OdinRoom} The room to add the media to.
11
- * @param sampleRate {number} The sample rate of the audio stream (between 8000 and 48000)
12
- * @param channelCount {number} The number of channels of the audio stream (1 or 2)
10
+ * @param room - The room to add the media to.
11
+ * @param sampleRate - The sample rate of the audio stream (between 8000 and 48000)
12
+ * @param channelCount - The number of channels of the audio stream (1 or 2)
13
+ * @param options - Optional configuration options for Odin Audio Processing Module (APM).
13
14
  */
14
- constructor(room: OdinRoom, sampleRate: number, channelCount: number);
15
+ constructor(room: OdinRoom, sampleRate: number, channelCount: number, options?: OdinAPMSettings);
15
16
 
16
17
  /**
17
18
  * Closes the local audio stream and removed the media from the room
@@ -22,12 +23,13 @@ export declare class OdinMedia {
22
23
  * Sends audio data to the room. The data must be in the format specified when creating the media as a 32-bit float array.
23
24
  * Samples need to be between -1 and 1. Audio data needs to be sent in regular intervals, otherwise the audio will be sound
24
25
  * interrupted. See the example for more details.
25
- * @param data
26
+ * @param data - A 32-bit float array containing the audio data.
26
27
  */
27
28
  sendAudioData(data: Float32Array): void;
28
29
 
29
30
  /**
30
31
  * Gets the ID of the media.
32
+ * @returns The ID of the media.
31
33
  */
32
34
  get id(): string;
33
35
  }
package/odin.room.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import {OdinMedia} from "./odin.media";
2
2
 
3
+ /**
4
+ * Defines available Odin events
5
+ */
3
6
  declare interface OdinEvents {
4
7
  /**
5
8
  * Fired when the local user connected to the room (successfully joined).
@@ -399,55 +402,49 @@ export declare interface OdinAPMSettings {
399
402
  export declare class OdinRoom {
400
403
  /**
401
404
  * Creates a new instance of a room with a token. Use OdinClient if you don't want to manage tokens yourself.
402
- * @param token {string} The token to use for this room.
405
+ * @param token - The token to use for this room.
403
406
  */
404
407
  constructor(token: string);
405
408
 
406
409
  /**
407
410
  * Joins the room with the given gateway URL and optional user data.
408
- * @param gatewayUrl {string} The gateway URL to connect to. Use gateway.odin.4players.io if you are unsure.
409
- * @param userData {Uint8Array} The user data to send to the room. This can be used to identify the user.
411
+ * @param gatewayUrl - The gateway URL to connect to. Use gateway.odin.4players.io if you are unsure.
412
+ * @param userData - The user data to send to the room. This can be used to identify the user.
410
413
  */
411
414
  join(gatewayUrl: string, userData?: Uint8Array): void;
412
415
 
413
416
  /**
414
417
  * Sends a message to the room.
415
- * @param message {Uint8Array} The message to send as a byte array.
416
- * @param peerIdList {number[]} The list of peer IDs to send the message to. If this is undefined, the message will be sent to all peers. If the list is defined and empty an error will be thrown.
418
+ * @param message - The message to send as a byte array.
419
+ * @param peerIdList - The list of peer IDs to send the message to. If this is undefined, the message will be sent to all peers. If the list is defined and empty an error will be thrown.
417
420
  */
418
421
  sendMessage(message: Uint8Array, peerIdList?: number[]): void;
419
422
 
420
423
  /**
421
424
  * Adds an event listener to the room for specific events.
422
- * @param event {Event} The event to listen for (see keys of OdinEvents for possible values)
423
- * @param callback {(data: any) => void} The callback to call when the event is fired.
425
+ * @param eventName - The event to listen for (see keys of OdinEvents for possible values)
426
+ * @param handler - The callback to call when the event is fired.
424
427
  */
425
428
  addEventListener<Event extends keyof OdinEvents>(eventName: Event, handler: OdinEvents[Event]): void;
426
429
 
427
430
  /**
428
431
  * Removes an event listener from the room for specific events.
429
- * @param event {Event} The event to remove the listener from (see keys of OdinEvents for possible values)
432
+ * @param eventName - The event to remove the listener from (see keys of OdinEvents for possible values)
430
433
  */
431
434
  removeEventListener<Event extends keyof OdinEvents>(eventName: Event): void;
432
435
 
433
436
  /**
434
437
  * Sets a global event listener that received all events, this can be helpful for debugging. Please use addEventListener instead for production code.
435
- * @param callback {(data: any) => void} The callback to call when the event is fired.
438
+ * @param callback - The callback to call when the event is fired.
436
439
  */
437
440
  setEventListener(callback: (data: OdinEventPayload) => void): void;
438
441
 
439
442
  /**
440
443
  * Updates the peer user data of the local peer
441
- * @param userData {Uint8Array} The new user data to set.
444
+ * @param userData - The new user data to set.
442
445
  */
443
446
  updateOwnUserData(userData: Uint8Array): void;
444
447
 
445
- /**
446
- * Updates the room user data (for all peers)
447
- * @param userData {Uint8Array} The new user data to set.
448
- */
449
- updateRoomUserData(userData: Uint8Array): void;
450
-
451
448
  /**
452
449
  * Closes the room and disconnects from the server.
453
450
  */
@@ -466,8 +463,9 @@ export declare class OdinRoom {
466
463
  /**
467
464
  * Creates a local audio stream and adds it to the room. An OdinMedia object will be returned that allows you to send
468
465
  * audio data.
469
- * @param sampleRate {number} The sample rate of the audio stream. Can be between 8000 and 48000.
470
- * @param channels {number} The number of channels of the audio stream. Can be 1 or 2.
466
+ * @param sampleRate - The sample rate of the audio stream. Can be between 8000 and 48000.
467
+ * @param channels - The number of channels of the audio stream. Can be 1 or 2.
468
+ * @returns The OdinMedia object that allows you to send audio data.
471
469
  */
472
470
  createAudioStream(sampleRate: number, channels: number, apmSettings?: OdinAPMSettings): OdinMedia;
473
471
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@4players/odin-nodejs",
3
- "version": "0.7.3",
4
- "description": "",
3
+ "version": "0.8.0",
4
+ "description": "NodeJS bindings for the ODIN SDK. Use for AI enhanced human interactions, content moderation and audio processing features in a backend.",
5
5
  "main": "index.cjs",
6
6
  "types": "index.d.ts",
7
7
  "scripts": {
@@ -32,7 +32,8 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "node-gyp": "^9.3.1",
35
- "prebuildify": "^5.0.1"
35
+ "prebuildify": "^5.0.1",
36
+ "typedoc": "^0.23.28"
36
37
  },
37
38
  "engines": {
38
39
  "node": ">=18"
Binary file
@@ -21,7 +21,7 @@ const userData = {
21
21
  version: "0.1"
22
22
  }
23
23
  const data = new TextEncoder().encode(JSON.stringify(userData));
24
- const odinClient = new OdinClient(accessKey, 44100, 1);
24
+ const odinClient = new OdinClient(accessKey, 48000, 2);
25
25
  const room = odinClient.createRoom(roomName, userName);
26
26
 
27
27
  // Join the room
@@ -30,9 +30,7 @@ room.join("gateway.odin.4players.io", data);
30
30
  // Send a message to the room
31
31
  const message = {
32
32
  kind: 'message',
33
- payload: {
34
- text: 'Hello, I am a music bot and will stream some music to you.'
35
- }
33
+ payload: 'Hello, I am a music bot and will stream some music to you.'
36
34
  }
37
35
  room.sendMessage(new TextEncoder().encode(JSON.stringify(message)));
38
36
 
@@ -41,29 +39,48 @@ const sendMusic = async (media) => {
41
39
  // Prepare our MP3 decoder and load the sample file
42
40
  const audioBuffer = await decode(fs.readFileSync('./santa.mp3'));
43
41
 
44
- // Print our some info about the audio file
45
- console.log(audioBuffer);
46
-
47
42
  // Create a stream that will match the settings of the file
48
- const audioBufferStream = new AudioBufferStream({channels: 1, sampleRate: 44100, float: true, bitDepth: 32});
43
+ const audioBufferStream = new AudioBufferStream({
44
+ channels: audioBuffer.numberOfChannels,
45
+ sampleRate: audioBuffer.sampleRate,
46
+ float: true,
47
+ bitDepth: 32,
48
+ chunkLength: 960 // 960 bytes every 20ms - might be doubled (1920) depending on the sample rate
49
+ });
49
50
 
50
- // Whenever the stream has data, send it to the media stream
51
+ // Create a queue to store the chunks of audio data
52
+ const queue = [];
53
+
54
+ // Whenever the stream has data, add it to the queue
51
55
  audioBufferStream.on('data', (data) => {
52
- const floats = new Float32Array(new Uint8Array(data).buffer)
53
- media.sendAudioData(floats);
56
+ const floats = new Float32Array(new Uint8Array(data).buffer);
57
+ queue.push(floats);
54
58
  });
55
59
 
60
+ // Start a timer to send audio data at regular intervals
61
+ const interval = setInterval(() => {
62
+ if (queue.length > 0) {
63
+ const chunk = queue.shift();
64
+ media.sendAudioData(chunk);
65
+ } else {
66
+ // If there's no more data to send, stop the timer
67
+ clearInterval(interval);
68
+ audioBufferStream.end();
69
+ console.log("Audio finished");
70
+ }
71
+ }, 20); // Send a chunk every 20ms
72
+
56
73
  audioBufferStream.write(audioBuffer);
57
74
  }
58
75
 
59
76
  // Create a media stream in the room - it will return an OdinMedia instance that we can use to send data to ODIN
60
- const media = room.createAudioStream(44100, 1);
77
+ const media = room.createAudioStream(48000, 2);
61
78
  console.log(media);
62
79
  console.log("MEDIA-ID:", media.id);
63
80
 
64
81
  // Start the stream and send the music to ODIN
65
82
  sendMusic(media).then(() => {
66
- console.log("Finished sending song");
83
+ console.log("Started sending audio");
67
84
  });
68
85
 
69
86
  // Wait until the user presses a key to stop
Binary file