com.adrenak.univoice 2.0.1 → 3.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [2.0.2](https://github.com/adrenak/univoice/compare/v2.0.1...v2.0.2) (2022-11-02)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * remove comment about possible PeerID in IAudioOutput ([2246bb2](https://github.com/adrenak/univoice/commit/2246bb2bef234b84bdf6cd55a9faf87e09133953))
7
+
8
+ ## [1.2.1](https://github.com/adrenak/univoice/compare/v1.2.0...v1.2.1) (2022-10-10)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * PeersOutputs gets filled as peers join, not when they start ([42eaeb9](https://github.com/adrenak/univoice/commit/42eaeb95eef4eea7ee559da9fdbcac78ceb941b4))
14
+
1
15
  # [1.2.0](https://github.com/adrenak/univoice/compare/v1.1.4...v1.2.0) (2022-09-20)
2
16
 
3
17
 
@@ -1,12 +1,17 @@
1
1
  using System;
2
2
  using System.Linq;
3
3
  using System.Collections.Generic;
4
+ using System.Diagnostics;
5
+
6
+ using Debug = UnityEngine.Debug;
4
7
 
5
8
  namespace Adrenak.UniVoice {
6
9
  /// <summary>
7
10
  /// Provides the means to host or connect to a chatroom.
8
11
  /// </summary>
9
12
  public class ChatroomAgent : IDisposable {
13
+ const string TAG = "ChatroomAgent";
14
+
10
15
  // ====================================================================
11
16
  #region PROPERTIES
12
17
  // ====================================================================
@@ -33,14 +38,29 @@ namespace Adrenak.UniVoice {
33
38
  /// There is a <see cref="IAudioOutput"/> for each peer that gets
34
39
  /// created using the provided <see cref="AudioOutputFactory"/>
35
40
  /// The <see cref="IAudioOutput"/> instance corresponding to a peer is
36
- /// responsible for playing the audio that we receive that peer.
41
+ /// responsible for playing the audio that we receive from that peer.
37
42
  /// </summary>
38
43
  public Dictionary<short, IAudioOutput> PeerOutputs;
39
44
 
45
+ /// <summary>
46
+ /// Fired when the <see cref="CurrentMode"/> changes.
47
+ /// </summary>
48
+ public Action<ChatroomAgentMode> OnModeChanged;
49
+
40
50
  /// <summary>
41
51
  /// The current <see cref="ChatroomAgentMode"/> of this agent
42
52
  /// </summary>
43
- public ChatroomAgentMode CurrentMode { get; private set; }
53
+ public ChatroomAgentMode CurrentMode {
54
+ get => _currentMode;
55
+ private set {
56
+ if(_currentMode != value) {
57
+ _currentMode = value;
58
+ OnModeChanged?.Invoke(value);
59
+ Debug.unityLogger.Log(TAG, "Current Mode set to " + value);
60
+ }
61
+ }
62
+ }
63
+ ChatroomAgentMode _currentMode = ChatroomAgentMode.Unconnected;
44
64
 
45
65
  /// <summary>
46
66
  /// Mutes all the peers. If set to true, no incoming audio from other
@@ -69,13 +89,14 @@ namespace Adrenak.UniVoice {
69
89
  #endregion
70
90
 
71
91
  // ====================================================================
72
- #region CONSTRUCTION / DISPOSAL
92
+ #region CONSTRUCTION AND DISPOSAL
73
93
  // ====================================================================
74
94
  /// <summary>
75
95
  /// Creates and returns a new agent using the provided dependencies.
96
+ /// The instance then makes the dependencies work together.
76
97
  /// </summary>
77
98
  ///
78
- /// <param name="chatroomNetwork">The chatroom network implementation
99
+ /// <param name="chatroomNetwork">The chatroom network implementation
79
100
  /// for chatroom access and sending data to peers in a chatroom.
80
101
  /// </param>
81
102
  ///
@@ -105,7 +126,8 @@ namespace Adrenak.UniVoice {
105
126
  PeerSettings = new Dictionary<short, ChatroomPeerSettings>();
106
127
  PeerOutputs = new Dictionary<short, IAudioOutput>();
107
128
 
108
- Init();
129
+ Debug.unityLogger.Log(TAG, "Created");
130
+ SetupEventListeners();
109
131
  }
110
132
 
111
133
  /// <summary>
@@ -115,6 +137,7 @@ namespace Adrenak.UniVoice {
115
137
  /// instances and/or using them outside this instance.
116
138
  /// </summary>
117
139
  public void Dispose() {
140
+ Debug.unityLogger.Log(TAG, "Disposing");
118
141
  AudioInput.Dispose();
119
142
 
120
143
  RemoveAllPeers();
@@ -122,54 +145,62 @@ namespace Adrenak.UniVoice {
122
145
  PeerOutputs.Clear();
123
146
 
124
147
  Network.Dispose();
148
+ Debug.unityLogger.Log(TAG, "Disposed");
125
149
  }
126
150
  #endregion
127
151
 
128
152
  // ====================================================================
129
153
  #region INTERNAL
130
154
  // ====================================================================
131
- void Init() {
132
- // Node server events
133
- Network.OnCreatedChatroom += () =>
155
+ void SetupEventListeners() {
156
+ Debug.unityLogger.Log(TAG, "Setting up events.");
157
+
158
+ // Network events
159
+ Network.OnCreatedChatroom += () => {
160
+ Debug.unityLogger.Log(TAG, "Chatroom created.");
134
161
  CurrentMode = ChatroomAgentMode.Host;
162
+ };
135
163
  Network.OnClosedChatroom += () => {
136
- CurrentMode = ChatroomAgentMode.Unconnected;
164
+ Debug.unityLogger.Log(TAG, "Chatroom closed.");
137
165
  RemoveAllPeers();
166
+ CurrentMode = ChatroomAgentMode.Unconnected;
138
167
  };
139
-
140
- // Node client events
141
168
  Network.OnJoinedChatroom += id => {
169
+ Debug.unityLogger.Log(TAG, "Joined chatroom.");
142
170
  CurrentMode = ChatroomAgentMode.Guest;
143
171
  };
144
- Network.OnLeftChatroom += () =>
172
+ Network.OnLeftChatroom += () => {
173
+ Debug.unityLogger.Log(TAG, "Left chatroom.");
145
174
  RemoveAllPeers();
146
- Network.OnPeerJoinedChatroom += id =>
175
+ CurrentMode = ChatroomAgentMode.Unconnected;
176
+ };
177
+ Network.OnPeerJoinedChatroom += id => {
178
+ Debug.unityLogger.Log(TAG, "New peer joined: " + id);
147
179
  AddPeer(id);
148
- Network.OnPeerLeftChatroom += id =>
180
+ };
181
+ Network.OnPeerLeftChatroom += id => {
182
+ Debug.unityLogger.Log(TAG, "Peer left: " + id);
149
183
  RemovePeer(id);
184
+ };
150
185
 
151
186
  // Stream the incoming audio data using the right peer output
152
187
  Network.OnAudioReceived += (peerID, data) => {
153
- // if we're muting all, no point continuing.
188
+ // if we're muting all, do nothing.
154
189
  if (MuteOthers) return;
155
190
 
156
- var index = data.segmentIndex;
157
- var frequency = data.frequency;
158
- var channels = data.channelCount;
159
- var samples = data.samples;
160
-
161
- if (HasSettingsForPeer(peerID) && !PeerSettings[peerID].muteThem)
162
- PeerOutputs[peerID].Feed(index, frequency, channels, samples);
191
+ if (AllowIncomingAudioFromPeer(peerID))
192
+ PeerOutputs[peerID].Feed(data);
163
193
  };
164
194
 
165
195
  AudioInput.OnSegmentReady += (index, samples) => {
196
+ if (CurrentMode == ChatroomAgentMode.Unconnected) return;
197
+
198
+ // If we're muting ourselves to all, do nothing.
166
199
  if (MuteSelf) return;
167
200
 
168
201
  // Get all the recipients we haven't muted ourselves to
169
202
  var recipients = Network.PeerIDs
170
- .Where(x => {
171
- return HasSettingsForPeer(x) && !PeerSettings[x].muteSelf;
172
- });
203
+ .Where(id => AllowOutgoingAudioToPeer(id));
173
204
 
174
205
  // Send the audio segment to every deserving recipient
175
206
  foreach (var recipient in recipients)
@@ -180,35 +211,50 @@ namespace Adrenak.UniVoice {
180
211
  samples = samples
181
212
  });
182
213
  };
214
+ Debug.unityLogger.Log(TAG, "Event setup completed.");
183
215
  }
184
216
 
185
217
  void AddPeer(short id) {
186
- if (!PeerSettings.ContainsKey(id))
187
- PeerSettings.Add(id, new ChatroomPeerSettings());
188
- if(!PeerOutputs.ContainsKey(id)) {
189
- var output = AudioOutputFactory.Create(
190
- AudioInput.Frequency,
191
- AudioInput.ChannelCount,
192
- AudioInput.Frequency * AudioInput.ChannelCount / AudioInput.SegmentRate
193
- );
194
- output.ID = id.ToString();
195
- PeerOutputs.Add(id, output);
196
- }
218
+ // Ensure no old settings or outputs exist for this ID.
219
+ RemovePeer(id);
220
+
221
+ PeerSettings.Add(id, new ChatroomPeerSettings());
222
+
223
+ var output = AudioOutputFactory.Create(
224
+ AudioInput.Frequency,
225
+ AudioInput.ChannelCount,
226
+ AudioInput.Frequency * AudioInput.ChannelCount / AudioInput.SegmentRate
227
+ );
228
+ output.ID = id.ToString();
229
+ PeerOutputs.Add(id, output);
230
+ Debug.unityLogger.Log(TAG, "Added peer " + id);
197
231
  }
198
232
 
199
233
  void RemovePeer(short id) {
200
- if (PeerSettings.ContainsKey(id))
234
+ if (PeerSettings.ContainsKey(id)) {
201
235
  PeerSettings.Remove(id);
236
+ Debug.unityLogger.Log(TAG, "Removed peer settings for ID " + id);
237
+ }
202
238
  if (PeerOutputs.ContainsKey(id)) {
203
239
  PeerOutputs[id].Dispose();
204
240
  PeerOutputs.Remove(id);
241
+ Debug.unityLogger.Log(TAG, "Removed peer output for ID " + id);
205
242
  }
206
243
  }
207
244
 
208
- void RemoveAllPeers() =>
209
- PeerSettings.Keys.ToList().ForEach(x => RemovePeer(x));
245
+ bool AllowIncomingAudioFromPeer(short id) {
246
+ return PeerSettings.ContainsKey(id) && !PeerSettings[id].muteThem;
247
+ }
248
+
249
+ bool AllowOutgoingAudioToPeer(short id) {
250
+ return PeerSettings.ContainsKey(id) && !PeerSettings[id].muteSelf;
251
+ }
210
252
 
211
- bool HasSettingsForPeer(short id) => PeerSettings.ContainsKey(id);
253
+ void RemoveAllPeers() {
254
+ Debug.unityLogger.Log(TAG, "Removing all peers");
255
+ foreach(var peer in Network.PeerIDs)
256
+ RemovePeer(peer);
257
+ }
212
258
  #endregion
213
259
  }
214
260
  }
@@ -26,7 +26,7 @@ namespace Adrenak.UniVoice {
26
26
  /// <summary>
27
27
  /// The number of segments (a segment is a sequence of audio samples)
28
28
  /// that are emitted from the source every second.
29
- /// A 16000 Hz source with one channel at a rate of 10
29
+ /// Eg. A 16000 Hz source with one channel at a rate of 10
30
30
  /// will output an array of 1600 samples every 100 milliseconds.
31
31
  /// A 44000 Hz source with two channels at a rate of 10
32
32
  /// will output an array of 8800 samples every 100 milliseconds.
@@ -5,13 +5,11 @@ namespace Adrenak.UniVoice {
5
5
  /// Responsible for playing audio that is sent to it.
6
6
  /// You'd normally want a <see cref="UnityEngine.AudioSource"/>
7
7
  /// based implementation to play the audio in Unity. But this class can
8
- /// be used in other ways just as streaming the received audio to a server
8
+ /// be used in other ways such as streaming the received audio to a server
9
9
  /// or writing it to a local file. It's just an audio output and the
10
- /// destination doesn't matter.
10
+ /// destination depends on your implementation.
11
11
  /// </summary>
12
12
  public interface IAudioOutput : IDisposable {
13
- // TODO: Maybe introduce a PeerID property?
14
-
15
13
  /// <summary>
16
14
  /// An ID associated with this audio output
17
15
  /// </summary>
@@ -36,10 +34,12 @@ namespace Adrenak.UniVoice {
36
34
  /// <param name="audioSamples">
37
35
  /// The audio samples/segment being fed
38
36
  /// </param>
39
- void Feed(int segmentIndex,
40
- int frequency,
41
- int channelCount,
42
- float[] audioSamples
43
- );
37
+ void Feed(int segmentIndex, int frequency, int channelCount, float[] audioSamples);
38
+
39
+ /// <summary>
40
+ /// Feeds a <see cref="ChatroomAudioSegment"/> object to the audio output.
41
+ /// </summary>
42
+ /// <param name="segment">The audio data to be sent.</param>
43
+ void Feed(ChatroomAudioSegment segment);
44
44
  }
45
45
  }
@@ -1,11 +1,11 @@
1
- fileFormatVersion: 2
2
- guid: 095ec1523c98b7f4997c105a06856823
3
- MonoImporter:
4
- externalObjects: {}
5
- serializedVersion: 2
6
- defaultReferences: []
7
- executionOrder: 0
8
- icon: {instanceID: 0}
9
- userData:
10
- assetBundleName:
11
- assetBundleVariant:
1
+ fileFormatVersion: 2
2
+ guid: 095ec1523c98b7f4997c105a06856823
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -46,8 +46,10 @@ namespace Adrenak.UniVoice {
46
46
  /// <summary>
47
47
  /// Fired when a peer joins the chatroom.
48
48
  /// Provides the ID of the peer as event data.
49
- /// This action also MUST be called for all previously
50
- /// existing peers when we connect to a network.
49
+ /// NOTE: This action also MUST be called for all previously
50
+ /// existing peers when a local user connects to a network.
51
+ /// This allows the local user to know about the users that
52
+ /// were in the chatroom before they joined.
51
53
  /// </summary>
52
54
  event Action<short> OnPeerJoinedChatroom;
53
55
 
@@ -59,11 +61,15 @@ namespace Adrenak.UniVoice {
59
61
 
60
62
  /// <summary>
61
63
  /// Fired when the network receives audio data from a peer.
64
+ /// The first argument is the ID of the user the audio came from.
65
+ /// The second is the audio segment.
62
66
  /// </summary>
63
67
  event Action<short, ChatroomAudioSegment> OnAudioReceived;
64
68
 
65
69
  /// <summary>
66
70
  /// Fired when the local user sets audio data to a peer.
71
+ /// The first argument is the ID of the user the audio was sent to.
72
+ /// The second is the audio segment.
67
73
  /// </summary>
68
74
  event Action<short, ChatroomAudioSegment> OnAudioSent;
69
75
  #endregion
@@ -77,7 +83,7 @@ namespace Adrenak.UniVoice {
77
83
  short OwnID { get; }
78
84
 
79
85
  /// <summary>
80
- /// IDs of all the peers in the current chatroom
86
+ /// IDs of all the peers in the current chatroom (excluding <see cref="OwnID"/>)
81
87
  /// </summary>
82
88
  List<short> PeerIDs { get; }
83
89
  #endregion
@@ -88,25 +94,25 @@ namespace Adrenak.UniVoice {
88
94
  /// <summary>
89
95
  /// Creates a chatroom
90
96
  /// </summary>
91
- /// <param name="data">Name of the chatroom</param>
97
+ /// <param name="data">Any arguments for hosting a chatroom</param>
92
98
  void HostChatroom(object data = null);
93
99
 
94
100
  /// <summary>
95
101
  /// Closes a chatroom that the local user is hosting
96
102
  /// </summary>
97
- /// <param name="data">Any arguments for closing the room</param>
103
+ /// <param name="data">Any arguments used for closing the chatroom</param>
98
104
  void CloseChatroom(object data = null);
99
105
 
100
106
  /// <summary>
101
107
  /// Joins a chatroom
102
108
  /// </summary>
103
- /// <param name="data">The name of the chatroom to join</param>
109
+ /// <param name="data">Any arguments used to join a chatroom</param>
104
110
  void JoinChatroom(object data = null);
105
111
 
106
112
  /// <summary>
107
113
  /// Leaves the chatroom the local user is currently in, if any
108
114
  /// </summary>
109
- /// <param name="data">Any arguments for leaving the room</param>
115
+ /// <param name="data">Any arguments used to leave a chatroom</param>
110
116
  void LeaveChatroom(object data = null);
111
117
 
112
118
  /// <summary>
@@ -1,11 +1,11 @@
1
- fileFormatVersion: 2
2
- guid: e3c9ef93e574acf4094f05ab7346bfd5
3
- MonoImporter:
4
- externalObjects: {}
5
- serializedVersion: 2
6
- defaultReferences: []
7
- executionOrder: 0
8
- icon: {instanceID: 0}
9
- userData:
10
- assetBundleName:
11
- assetBundleVariant:
1
+ fileFormatVersion: 2
2
+ guid: e3c9ef93e574acf4094f05ab7346bfd5
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
package/package.json CHANGED
@@ -1,20 +1,29 @@
1
1
  {
2
- "name": "com.adrenak.univoice",
3
- "version": "2.0.1",
4
- "displayName": "Adrenak.UniVoice",
5
- "description": "Voice chat/VoIP solution for Unity.",
6
- "unity": "2019.4",
7
- "author": "Vatsal Ambastha <ambastha.vatsal@gmail.com> (http://www.vatsalambastha.com)",
8
- "license": "MIT",
9
- "keywords": [
10
- "Peer To Peer",
11
- "WebRTC",
12
- "P2P",
13
- "Networking",
14
- "Voice Chat",
15
- "voip"
16
- ],
17
- "publishConfig": {
18
- "registry": "https://registry.npmjs.org"
19
- }
2
+ "name": "com.adrenak.univoice",
3
+ "version": "3.0.0",
4
+ "displayName": "Adrenak.UniVoice",
5
+ "description": "Voice chat/VoIP solution for Unity.",
6
+ "unity": "2019.4",
7
+ "author": {
8
+ "name": "Vatsal Ambastha",
9
+ "email": "ambastha.vatsal@gmail.com",
10
+ "url": "http://www.vatsalambastha.com"
11
+ },
12
+ "license": "MIT",
13
+ "keywords": [
14
+ "Peer To Peer",
15
+ "WebRTC",
16
+ "P2P",
17
+ "Networking",
18
+ "Voice Chat",
19
+ "voip",
20
+ "Unity",
21
+ "Unity3d",
22
+ "VR",
23
+ "AR",
24
+ "Metaverse"
25
+ ],
26
+ "publishConfig": {
27
+ "registry": "https://registry.npmjs.org"
28
+ }
20
29
  }
@@ -1,39 +0,0 @@
1
- using System.Collections.Generic;
2
-
3
- using UnityEngine;
4
-
5
- namespace Adrenak.UniVoice {
6
- public static class Extensions {
7
- /// <summary>
8
- /// Returns the normalized position of the AudioSource on its AudioClip
9
- /// </summary>
10
- public static float Position(this AudioSource source) {
11
- return (float)source.timeSamples / source.clip.samples;
12
- }
13
-
14
- /// <summary>
15
- /// Adds the given key to the dictionary with a value if the key
16
- /// is not present before.
17
- /// </summary>
18
- /// <param name="t">The key to check and add</param>
19
- /// <param name="k">The value if the key is added</param>
20
- public static void EnsureKey<T, K>
21
- (this Dictionary<T, K> dict, T t, K k) {
22
- if (!dict.ContainsKey(t))
23
- dict.Add(t, k);
24
- }
25
-
26
- /// <summary>
27
- /// Ensures a key and value pair are in the dictionary
28
- /// </summary>
29
- /// <param name="t">The key to ensure</param>
30
- /// <param name="k">The value associated with the key</param>
31
- public static void EnsurePair<T, K>
32
- (this Dictionary<T, K> dict, T t, K k) {
33
- if (dict.ContainsKey(t))
34
- dict[t] = k;
35
- else
36
- dict.Add(t, k);
37
- }
38
- }
39
- }
@@ -1,12 +0,0 @@
1
- fileFormatVersion: 2
2
- guid: 646602ab062d77543b3742e51d16658f
3
- timeCreated: 1547895114
4
- licenseType: Free
5
- MonoImporter:
6
- serializedVersion: 2
7
- defaultReferences: []
8
- executionOrder: 0
9
- icon: {instanceID: 0}
10
- userData:
11
- assetBundleName:
12
- assetBundleVariant: