com.adrenak.univoice 4.5.1 β 4.7.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 +10 -1
- package/Runtime/ClientSession.cs +173 -40
- package/Runtime/Impl/Networks/FakeNetwork.cs +6 -0
- package/Runtime/Impl/Networks/FakeNetwork.cs.meta +11 -0
- package/Runtime/Impl/Networks/Mirror/MirrorClient.cs +4 -0
- package/Runtime/Impl/Networks/Mirror/MirrorServer.cs +47 -16
- package/Runtime/Impl/Outputs/StreamedAudioSourceOutput.cs +1 -1
- package/Runtime/Types/VoiceSettings.cs +20 -0
- package/Samples~/Basic Setup Scripts/UniVoiceMirrorSetupSample.cs +1 -1
- package/Samples~/Group Chat Sample/Scripts/GroupVoiceCallMirrorSample.cs +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,16 @@ UniVoice is a voice chat/VoIP solution for Unity.
|
|
|
4
4
|
Some features of UniVoice:
|
|
5
5
|
- π₯ Group voice chat. Multiple peers can join a chatroom and exchange audio.
|
|
6
6
|
|
|
7
|
-
- β
|
|
7
|
+
- β Fine control over audio data flow.
|
|
8
|
+
* Don't want to listen to a peer? Mute them. Don't want someone listening to you? Deafen them.
|
|
9
|
+
* Group players using tags and control audio flow between them. For example:
|
|
10
|
+
- "red", "blue" and "spectator" tags for two teams playing against each other.
|
|
11
|
+
- Red and Blue teams can only hear each other
|
|
12
|
+
- Spectators can hear everyone
|
|
13
|
+
- clients with "contestant", "judge" and "audience" tags for a virtual talent show.
|
|
14
|
+
- Contestant can be heard by everyone, but don't hear anyone else (for focus)
|
|
15
|
+
- Judges can talk to and hear each other for discussions. They can hear the contestant. But not the audience (for less noise)
|
|
16
|
+
- Audience can hear and talk to each other. They can hear the performer. But they cannot hear the judges.
|
|
8
17
|
|
|
9
18
|
- π¨ Customize your audio input, output and networking layers.
|
|
10
19
|
* π __Configurable Network__:
|
package/Runtime/ClientSession.cs
CHANGED
|
@@ -1,21 +1,92 @@
|
|
|
1
1
|
ο»Ώusing System;
|
|
2
2
|
using System.Collections.Generic;
|
|
3
|
+
using System.Linq;
|
|
3
4
|
|
|
4
5
|
using UnityEngine;
|
|
5
6
|
|
|
6
7
|
namespace Adrenak.UniVoice {
|
|
7
8
|
/// <summary>
|
|
8
9
|
/// Handles a client session.
|
|
9
|
-
/// Requires
|
|
10
|
-
///
|
|
10
|
+
/// Requires implementations of <see cref="IAudioClient{T}"/>, <see cref="IAudioInput"/> and <see cref="IAudioOutput"/>.
|
|
11
|
+
/// Handles input, output along with filters over the entire client lifecycle.
|
|
12
|
+
/// Adjusts to changes in configuration at runtime.
|
|
11
13
|
/// </summary>
|
|
12
14
|
/// <typeparam name="T"></typeparam>
|
|
13
15
|
public class ClientSession<T> : IDisposable {
|
|
16
|
+
/// <summary>
|
|
17
|
+
/// Represents a filter registered in the session.
|
|
18
|
+
/// Currently used only for output filters.
|
|
19
|
+
/// </summary>
|
|
20
|
+
class FilterFactoryEntry {
|
|
21
|
+
public Type FilterType { get; }
|
|
22
|
+
public Func<IAudioFilter> Factory { get; }
|
|
23
|
+
|
|
24
|
+
public FilterFactoryEntry(Type filterType, Func<IAudioFilter> factory) {
|
|
25
|
+
FilterType = filterType;
|
|
26
|
+
Factory = factory;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#region AUDIO OUTPUT
|
|
31
|
+
|
|
32
|
+
List<FilterFactoryEntry> outputFilterFactories = new List<FilterFactoryEntry>();
|
|
33
|
+
Dictionary<T, List<IAudioFilter>> peerOutputFilters = new Dictionary<T, List<IAudioFilter>>();
|
|
34
|
+
|
|
35
|
+
/// <summary>
|
|
36
|
+
/// Whether any incoming audio from peers would be processed. If set to false, all incoming peer audio is ignored, and would
|
|
37
|
+
/// neither be processed by the <see cref="OutputFilters"/> nor output to the <see cref="IAudioOutput"/> of any peer.
|
|
38
|
+
/// This can be used to easily mute all the peers on the network.
|
|
39
|
+
/// Note that this doesn't stop the audio data from arriving and would consume bandwidth. To stop reception completely
|
|
40
|
+
/// by telling the server to not send audio, use <see cref="IAudioClient{T}.YourVoiceSettings"/>
|
|
41
|
+
/// </summary>
|
|
42
|
+
public bool OutputsEnabled { get; set; } = true;
|
|
43
|
+
|
|
14
44
|
/// <summary>
|
|
15
45
|
/// The <see cref="IAudioOutput"/> instances of each peer in the session
|
|
16
46
|
/// </summary>
|
|
17
47
|
public Dictionary<T, IAudioOutput> PeerOutputs { get; private set; } = new Dictionary<T, IAudioOutput>();
|
|
18
48
|
|
|
49
|
+
/// <summary>
|
|
50
|
+
/// Adds an filter to the output audio. Note: it is possible to register the same
|
|
51
|
+
/// filter type more than once, this can be used to create some effects but can also cause
|
|
52
|
+
/// errors.
|
|
53
|
+
/// </summary>
|
|
54
|
+
/// <typeparam name="TFilter">The type of the filter to be added</typeparam>
|
|
55
|
+
/// <param name="filterFactory">A lambda method that returns an instance of the filter type</param>
|
|
56
|
+
public void AddOutputFilter<TFilter>(Func<IAudioFilter> filterFactory)
|
|
57
|
+
where TFilter : IAudioFilter {
|
|
58
|
+
outputFilterFactories.Add(new FilterFactoryEntry(typeof(TFilter), filterFactory));
|
|
59
|
+
|
|
60
|
+
foreach (var peerFilters in peerOutputFilters.Values)
|
|
61
|
+
peerFilters.Add(filterFactory());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// <summary>
|
|
65
|
+
/// Checks if an output audio filter of a specific type has been registered.
|
|
66
|
+
/// </summary>
|
|
67
|
+
/// <typeparam name="TFilter">The type of the filter to check</typeparam>
|
|
68
|
+
/// <returns>True if the filter is registered, false otherwise</returns>
|
|
69
|
+
public bool HasOutputFilter<TFilter>()
|
|
70
|
+
where TFilter : IAudioFilter {
|
|
71
|
+
return outputFilterFactories.Any(entry => entry.FilterType == typeof(TFilter));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// <summary>
|
|
75
|
+
/// Removes a previously registered output audio filter
|
|
76
|
+
/// </summary>
|
|
77
|
+
/// <typeparam name="TFilter">The type of the filter to be removed</typeparam>
|
|
78
|
+
public void RemoveOutputFilter<TFilter>()
|
|
79
|
+
where TFilter : IAudioFilter {
|
|
80
|
+
outputFilterFactories.RemoveAll(entry => entry.FilterType == typeof(TFilter));
|
|
81
|
+
|
|
82
|
+
foreach (var peerFilters in peerOutputFilters.Values)
|
|
83
|
+
peerFilters.RemoveAll(f => f.GetType() == typeof(TFilter));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#endregion
|
|
87
|
+
|
|
88
|
+
#region AUDIO INPUT
|
|
89
|
+
|
|
19
90
|
/// <summary>
|
|
20
91
|
/// Whether input audio will be processed. If set to false, any input audio captured by
|
|
21
92
|
/// <see cref="Input"/> would be ignored and would neither be processed by the <see cref="InputFilters"/> nor send via the <see cref="Client"/>
|
|
@@ -24,25 +95,28 @@ namespace Adrenak.UniVoice {
|
|
|
24
95
|
public bool InputEnabled { get; set; } = true;
|
|
25
96
|
|
|
26
97
|
/// <summary>
|
|
27
|
-
///
|
|
28
|
-
/// neither be processed by the <see cref="OutputFilters"/> nor outputted to the <see cref="IAudioOutput"/> of any peer.
|
|
29
|
-
/// This can be used to easily mute all the peers on the network.
|
|
30
|
-
/// Note that this doesn't stop the audio data from arriving and would consume bandwidth. Do stop reception completely
|
|
31
|
-
/// use <see cref="IAudioClient{T}.YourVoiceSettings"/>
|
|
32
|
-
/// </summary>
|
|
33
|
-
public bool OutputsEnabled { get; set; } = true;
|
|
34
|
-
|
|
35
|
-
/// <summary>
|
|
36
|
-
/// The input <see cref="IAudioFilter"/> that will be applied to the outgoing audio for all the peers.
|
|
98
|
+
/// The <see cref="IAudioFilter"/> that will be applied to the outgoing audio for all the peers.
|
|
37
99
|
/// Note that filters are executed in the order they are present in this list
|
|
38
100
|
/// </summary>
|
|
39
101
|
public List<IAudioFilter> InputFilters { get; set; } = new List<IAudioFilter>();
|
|
40
102
|
|
|
41
103
|
/// <summary>
|
|
42
|
-
///
|
|
43
|
-
/// Note that filters are executed in the order they are present in this list.
|
|
104
|
+
/// Checks if an input audio filter of a specific type has been registered.
|
|
44
105
|
/// </summary>
|
|
45
|
-
|
|
106
|
+
/// <typeparam name="TFilter">The type of the filter to check</typeparam>
|
|
107
|
+
/// <returns>True if the filter is registered, false otherwise</returns>
|
|
108
|
+
public bool HasInputFilter<TFilter>()
|
|
109
|
+
where TFilter : IAudioFilter {
|
|
110
|
+
return InputFilters.Any(filter => filter.GetType() == typeof(TFilter));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#endregion
|
|
114
|
+
|
|
115
|
+
public ClientSession(IAudioClient<T> client, IAudioInput input, Func<IAudioOutput> outputProvider) {
|
|
116
|
+
Client = client;
|
|
117
|
+
Input = input;
|
|
118
|
+
OutputProvider = outputProvider;
|
|
119
|
+
}
|
|
46
120
|
|
|
47
121
|
public ClientSession(IAudioClient<T> client, IAudioInput input, IAudioOutputFactory outputFactory) {
|
|
48
122
|
Client = client;
|
|
@@ -57,7 +131,7 @@ namespace Adrenak.UniVoice {
|
|
|
57
131
|
public IAudioClient<T> Client {
|
|
58
132
|
get => client;
|
|
59
133
|
set {
|
|
60
|
-
if(client != null)
|
|
134
|
+
if (client != null)
|
|
61
135
|
client.Dispose();
|
|
62
136
|
client = value;
|
|
63
137
|
|
|
@@ -65,12 +139,24 @@ namespace Adrenak.UniVoice {
|
|
|
65
139
|
foreach (var output in PeerOutputs)
|
|
66
140
|
output.Value.Dispose();
|
|
67
141
|
PeerOutputs.Clear();
|
|
142
|
+
peerOutputFilters.Clear();
|
|
68
143
|
};
|
|
69
144
|
|
|
70
145
|
Client.OnPeerJoined += id => {
|
|
71
146
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
147
|
+
if (OutputProvider != null) {
|
|
148
|
+
var output = OutputProvider();
|
|
149
|
+
PeerOutputs.Add(id, output);
|
|
150
|
+
}
|
|
151
|
+
else if (OutputFactory != null) {
|
|
152
|
+
var output = OutputFactory.Create();
|
|
153
|
+
PeerOutputs.Add(id, output);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
var filters = outputFilterFactories
|
|
157
|
+
.Select(entry => entry.Factory())
|
|
158
|
+
.ToList();
|
|
159
|
+
peerOutputFilters.Add(id, filters);
|
|
74
160
|
}
|
|
75
161
|
catch (Exception e) {
|
|
76
162
|
Debug.LogException(e);
|
|
@@ -78,26 +164,27 @@ namespace Adrenak.UniVoice {
|
|
|
78
164
|
};
|
|
79
165
|
|
|
80
166
|
Client.OnPeerLeft += id => {
|
|
81
|
-
if (
|
|
82
|
-
|
|
167
|
+
if (PeerOutputs.ContainsKey(id)) {
|
|
168
|
+
PeerOutputs[id].Dispose();
|
|
169
|
+
PeerOutputs.Remove(id);
|
|
170
|
+
}
|
|
83
171
|
|
|
84
|
-
|
|
85
|
-
|
|
172
|
+
if (peerOutputFilters.ContainsKey(id)) {
|
|
173
|
+
peerOutputFilters.Remove(id);
|
|
174
|
+
}
|
|
86
175
|
};
|
|
87
176
|
|
|
88
177
|
Client.OnReceivedPeerAudioFrame += (id, audioFrame) => {
|
|
89
|
-
if (!OutputsEnabled)
|
|
178
|
+
if (!OutputsEnabled || !PeerOutputs.ContainsKey(id))
|
|
90
179
|
return;
|
|
91
180
|
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (OutputFilters != null) {
|
|
96
|
-
foreach (var filter in OutputFilters)
|
|
181
|
+
if (peerOutputFilters.TryGetValue(id, out var filters)) {
|
|
182
|
+
foreach (var filter in filters)
|
|
97
183
|
audioFrame = filter.Run(audioFrame);
|
|
98
184
|
}
|
|
99
|
-
|
|
100
|
-
|
|
185
|
+
|
|
186
|
+
if (audioFrame.samples.Length > 0)
|
|
187
|
+
PeerOutputs[id]?.Feed(audioFrame);
|
|
101
188
|
};
|
|
102
189
|
}
|
|
103
190
|
}
|
|
@@ -109,11 +196,11 @@ namespace Adrenak.UniVoice {
|
|
|
109
196
|
public IAudioInput Input {
|
|
110
197
|
get => input;
|
|
111
198
|
set {
|
|
112
|
-
if(input != null)
|
|
199
|
+
if (input != null)
|
|
113
200
|
input.Dispose();
|
|
114
201
|
input = value;
|
|
115
202
|
input.OnFrameReady += frame => {
|
|
116
|
-
if (!InputEnabled)
|
|
203
|
+
if (!InputEnabled)
|
|
117
204
|
return;
|
|
118
205
|
|
|
119
206
|
if (InputFilters != null) {
|
|
@@ -121,12 +208,44 @@ namespace Adrenak.UniVoice {
|
|
|
121
208
|
frame = filter.Run(frame);
|
|
122
209
|
}
|
|
123
210
|
|
|
124
|
-
if(frame.samples.Length > 0)
|
|
211
|
+
if (frame.samples.Length > 0)
|
|
125
212
|
Client.SendAudioFrame(frame);
|
|
126
213
|
};
|
|
127
214
|
}
|
|
128
215
|
}
|
|
129
216
|
|
|
217
|
+
Func<IAudioOutput> outputProvider;
|
|
218
|
+
/// <summary>
|
|
219
|
+
/// The provider of IAudioOutput objects for peers.
|
|
220
|
+
/// If this value is being set while peers already exist,
|
|
221
|
+
/// the old outputs would be cleared and new onces will
|
|
222
|
+
/// be created.
|
|
223
|
+
/// </summary>
|
|
224
|
+
public Func<IAudioOutput> OutputProvider {
|
|
225
|
+
get => outputProvider;
|
|
226
|
+
set {
|
|
227
|
+
outputProvider = value;
|
|
228
|
+
outputFactory = null;
|
|
229
|
+
|
|
230
|
+
foreach (var output in PeerOutputs)
|
|
231
|
+
output.Value.Dispose();
|
|
232
|
+
PeerOutputs.Clear();
|
|
233
|
+
|
|
234
|
+
if(outputProvider != null) {
|
|
235
|
+
foreach (var id in Client.PeerIDs) {
|
|
236
|
+
try {
|
|
237
|
+
var output = outputProvider();
|
|
238
|
+
PeerOutputs.Add(id, output);
|
|
239
|
+
}
|
|
240
|
+
catch (Exception e) {
|
|
241
|
+
Debug.LogException(e);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
130
249
|
IAudioOutputFactory outputFactory;
|
|
131
250
|
/// <summary>
|
|
132
251
|
/// The <see cref="IAudioOutputFactory"/> that creates the <see cref="IAudioOutput"/> of peers
|
|
@@ -135,18 +254,21 @@ namespace Adrenak.UniVoice {
|
|
|
135
254
|
get => outputFactory;
|
|
136
255
|
set {
|
|
137
256
|
outputFactory = value;
|
|
257
|
+
outputProvider = null;
|
|
138
258
|
|
|
139
259
|
foreach (var output in PeerOutputs)
|
|
140
260
|
output.Value.Dispose();
|
|
141
261
|
PeerOutputs.Clear();
|
|
142
262
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
263
|
+
if (outputFactory != null) {
|
|
264
|
+
foreach (var id in Client.PeerIDs) {
|
|
265
|
+
try {
|
|
266
|
+
var output = outputFactory.Create();
|
|
267
|
+
PeerOutputs.Add(id, output);
|
|
268
|
+
}
|
|
269
|
+
catch (Exception e) {
|
|
270
|
+
Debug.LogException(e);
|
|
271
|
+
}
|
|
150
272
|
}
|
|
151
273
|
}
|
|
152
274
|
}
|
|
@@ -156,5 +278,16 @@ namespace Adrenak.UniVoice {
|
|
|
156
278
|
Client.Dispose();
|
|
157
279
|
Input.Dispose();
|
|
158
280
|
}
|
|
281
|
+
|
|
282
|
+
#region OBSOLETE
|
|
283
|
+
|
|
284
|
+
/// <summary>
|
|
285
|
+
/// The output <see cref="IAudioFilter"/> that will be applied to the incoming audio for all the peers.
|
|
286
|
+
/// Note that filters are executed in the order they are present in this list.
|
|
287
|
+
/// </summary>
|
|
288
|
+
[Obsolete("OutputFilters has been removed. Use AddOutputFilter and RemoveOutputFilter instead.", true)]
|
|
289
|
+
public List<IAudioFilter> OutputFilters { get; set; } = new List<IAudioFilter>();
|
|
290
|
+
|
|
291
|
+
#endregion
|
|
159
292
|
}
|
|
160
293
|
}
|
|
@@ -171,6 +171,10 @@ namespace Adrenak.UniVoice.Networks {
|
|
|
171
171
|
writer.WriteIntArray(YourVoiceSettings.mutedPeers.ToArray());
|
|
172
172
|
writer.WriteInt(YourVoiceSettings.deafenAll ? 1 : 0);
|
|
173
173
|
writer.WriteIntArray(YourVoiceSettings.deafenedPeers.ToArray());
|
|
174
|
+
writer.WriteString(string.Join(",", YourVoiceSettings.myTags));
|
|
175
|
+
writer.WriteString(string.Join(",", YourVoiceSettings.mutedTags));
|
|
176
|
+
writer.WriteString(string.Join(",", YourVoiceSettings.deafenedTags));
|
|
177
|
+
|
|
174
178
|
var message = new MirrorMessage {
|
|
175
179
|
data = writer.Bytes
|
|
176
180
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// Notes:
|
|
2
2
|
// In Mirror 89.11.0, the OnServerConnectedWithAddress event was added
|
|
3
3
|
// https://github.com/MirrorNetworking/Mirror/releases/tag/v89.11.0
|
|
4
4
|
// OnServerConnected no longer seems to work?
|
|
@@ -106,37 +106,61 @@ namespace Adrenak.UniVoice.Networks {
|
|
|
106
106
|
var reader = new BytesReader(message.data);
|
|
107
107
|
var tag = reader.ReadString();
|
|
108
108
|
|
|
109
|
+
// Server forwards the received audio from a client to other clients based on voice settings.
|
|
110
|
+
// Client can mute or deafen each other using IDs as well as tags
|
|
109
111
|
if (tag.Equals(MirrorMessageTags.AUDIO_FRAME)) {
|
|
110
112
|
// We start with all the peers except the one that's
|
|
111
113
|
// sent the audio
|
|
112
114
|
var peersToForwardAudioTo = ClientIDs
|
|
113
115
|
.Where(x => x != clientId);
|
|
114
116
|
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
+
// Check the voice settings of the sender and eliminate any peers the sender
|
|
118
|
+
// may have deafened
|
|
117
119
|
if (ClientVoiceSettings.TryGetValue(clientId, out var senderSettings)) {
|
|
118
|
-
// If the client sending the audio has deafened everyone
|
|
119
|
-
//
|
|
120
|
+
// If the client sending the audio has deafened everyone,
|
|
121
|
+
// we simply return. Sender's audio should not be forwarded to anyone.
|
|
120
122
|
if (senderSettings.deafenAll)
|
|
121
123
|
return;
|
|
122
124
|
|
|
123
|
-
//
|
|
124
|
-
// deafened
|
|
125
|
+
// Filter the recipient list by removing all peers that the sender has
|
|
126
|
+
// deafened using ID
|
|
125
127
|
peersToForwardAudioTo = peersToForwardAudioTo
|
|
126
128
|
.Where(x => !senderSettings.deafenedPeers.Contains(x));
|
|
129
|
+
|
|
130
|
+
// Further filter the recipient list by removing peers that the sender has
|
|
131
|
+
// deafened using tags
|
|
132
|
+
peersToForwardAudioTo = peersToForwardAudioTo.Where(peer => {
|
|
133
|
+
// Get the voice settings of the peer
|
|
134
|
+
if (ClientVoiceSettings.TryGetValue(peer, out VoiceSettings peerVoiceSettings)) {
|
|
135
|
+
// Check if sender has not deafened peer using tag
|
|
136
|
+
var hasDeafenedPeer = senderSettings.deafenedTags.Intersect(peerVoiceSettings.myTags).Count() > 0;
|
|
137
|
+
return !hasDeafenedPeer;
|
|
138
|
+
}
|
|
139
|
+
// If peer doesn't have voice settings, we can keep the peer in the list
|
|
140
|
+
else {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
127
144
|
}
|
|
128
145
|
|
|
129
|
-
// We iterate through each recipient peer that the sender wants to send
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (ClientVoiceSettings.TryGetValue(
|
|
134
|
-
|
|
146
|
+
// We iterate through each recipient peer that the sender wants to send audio to, checking if
|
|
147
|
+
// they have muted the sender, before forwarding the audio to them.
|
|
148
|
+
foreach (var recipient in peersToForwardAudioTo) {
|
|
149
|
+
// Get the settings of a potential recipient
|
|
150
|
+
if (ClientVoiceSettings.TryGetValue(recipient, out var recipientSettings)) {
|
|
151
|
+
// If a peer has muted everyone, don't send audio
|
|
152
|
+
if (recipientSettings.muteAll)
|
|
135
153
|
continue;
|
|
136
|
-
|
|
154
|
+
|
|
155
|
+
// If the peers has muted the sender using ID, skip sending audio
|
|
156
|
+
if (recipientSettings.mutedPeers.Contains(clientId))
|
|
157
|
+
continue;
|
|
158
|
+
|
|
159
|
+
// If the peer has muted the sender using tag, skip sending audio
|
|
160
|
+
if (recipientSettings.mutedTags.Intersect(senderSettings.myTags).Count() > 0)
|
|
137
161
|
continue;
|
|
138
162
|
}
|
|
139
|
-
SendToClient(
|
|
163
|
+
SendToClient(recipient, message.data, Channels.Unreliable);
|
|
140
164
|
}
|
|
141
165
|
}
|
|
142
166
|
else if (tag.Equals(MirrorMessageTags.VOICE_SETTINGS)) {
|
|
@@ -147,11 +171,18 @@ namespace Adrenak.UniVoice.Networks {
|
|
|
147
171
|
var mutedPeers = reader.ReadIntArray().ToList();
|
|
148
172
|
var deafenAll = reader.ReadInt() == 1 ? true : false;
|
|
149
173
|
var deafenedPeers = reader.ReadIntArray().ToList();
|
|
174
|
+
var myTags = reader.ReadString().Split(",").ToList();
|
|
175
|
+
var mutedTags = reader.ReadString().Split(",").ToList();
|
|
176
|
+
var deafenedTags = reader.ReadString().Split(",").ToList();
|
|
177
|
+
|
|
150
178
|
var voiceSettings = new VoiceSettings {
|
|
151
179
|
muteAll = muteAll,
|
|
152
180
|
mutedPeers = mutedPeers,
|
|
153
181
|
deafenAll = deafenAll,
|
|
154
|
-
deafenedPeers = deafenedPeers
|
|
182
|
+
deafenedPeers = deafenedPeers,
|
|
183
|
+
myTags = myTags,
|
|
184
|
+
mutedTags = mutedTags,
|
|
185
|
+
deafenedTags = deafenedTags
|
|
155
186
|
};
|
|
156
187
|
if (ClientVoiceSettings.ContainsKey(clientId))
|
|
157
188
|
ClientVoiceSettings[clientId] = voiceSettings;
|
|
@@ -33,7 +33,7 @@ namespace Adrenak.UniVoice.Outputs {
|
|
|
33
33
|
/// </summary>
|
|
34
34
|
/// <param name="frame"></param>
|
|
35
35
|
public void Feed(AudioFrame frame) {
|
|
36
|
-
Stream.Feed(frame.frequency, frame.channelCount, Utils.Bytes.BytesToFloats(frame.samples)
|
|
36
|
+
Stream.Feed(frame.frequency, frame.channelCount, Utils.Bytes.BytesToFloats(frame.samples));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/// <summary>
|
|
@@ -22,6 +22,26 @@ namespace Adrenak.UniVoice {
|
|
|
22
22
|
/// </summary>
|
|
23
23
|
public List<int> deafenedPeers = new List<int>();
|
|
24
24
|
|
|
25
|
+
/// <summary>
|
|
26
|
+
/// The tags associated with this client.
|
|
27
|
+
/// DO NOT USE COMMAS (,)
|
|
28
|
+
/// </summary>
|
|
29
|
+
public List<string> myTags = new List<string>();
|
|
30
|
+
|
|
31
|
+
/// <summary>
|
|
32
|
+
/// The tags, which if associated with a peer, would cause
|
|
33
|
+
/// those the audio of that peer to not be send to this peer
|
|
34
|
+
/// DO NOT USE COMMAS (,)
|
|
35
|
+
/// </summary>
|
|
36
|
+
public List<string> mutedTags = new List<string>();
|
|
37
|
+
|
|
38
|
+
/// <summary>
|
|
39
|
+
/// The tags, which if associated with a peer, would cause
|
|
40
|
+
/// those peers to not receive audio from this client
|
|
41
|
+
/// DO NOT USE COMMAS (,)
|
|
42
|
+
/// </summary>
|
|
43
|
+
public List<string> deafenedTags = new List<string>();
|
|
44
|
+
|
|
25
45
|
/// <summary>
|
|
26
46
|
/// Sets the deaf status of a peer
|
|
27
47
|
/// </summary>
|
|
@@ -185,7 +185,7 @@ namespace Adrenak.UniVoice.Samples {
|
|
|
185
185
|
Debug.unityLogger.Log(LogType.Log, TAG, "Registered ConcentusEncodeFilter as an input filter");
|
|
186
186
|
|
|
187
187
|
// For incoming audio register the ConcentusDecodeFilter to decode the encoded audio received from other clients
|
|
188
|
-
ClientSession.
|
|
188
|
+
ClientSession.AddOutputFilter<ConcentusDecodeFilter>(() => new ConcentusDecodeFilter());
|
|
189
189
|
Debug.unityLogger.Log(LogType.Log, TAG, "Registered ConcentusDecodeFilter as an output filter");
|
|
190
190
|
}
|
|
191
191
|
|
|
@@ -89,7 +89,7 @@ namespace Adrenak.UniVoice.Samples {
|
|
|
89
89
|
|
|
90
90
|
// Next, for incoming audio we register the Concentus decode filter as the audio we'd
|
|
91
91
|
// receive from other clients would be encoded and not readily playable
|
|
92
|
-
session.
|
|
92
|
+
session.AddOutputFilter<ConcentusDecodeFilter>(() => new ConcentusDecodeFilter());
|
|
93
93
|
|
|
94
94
|
// Subscribe to some server events
|
|
95
95
|
server.OnServerStart += () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "com.adrenak.univoice",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"displayName": "Adrenak.UniVoice",
|
|
5
5
|
"description": "Voice chat/VoIP framework for Unity.",
|
|
6
6
|
"unity": "2021.2",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"com.adrenak.brw": "1.0.1",
|
|
36
|
-
"com.adrenak.unimic": "3.
|
|
36
|
+
"com.adrenak.unimic": "3.3.0",
|
|
37
37
|
"com.adrenak.concentus-unity": "1.0.1"
|
|
38
38
|
}
|
|
39
39
|
}
|