com.adrenak.univoice 4.0.0 → 4.1.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
@@ -12,11 +12,13 @@ Some features of UniVoice:
12
12
  * 🔊 __Configurable Audio Output__: UniVoice is audio output agnostic. Out of the box is supports playing peer audio using Unity AudioSource. You can divert incoming audio to anywhere you want by implementing the `IAudioOutput` interface.
13
13
 
14
14
  * 🌐 __Configurable Network__: UniVoice is network agnostic and supports Mirror out of the box. You can implement the `IAudioClient` and `IAudioServer` interfaces using the networking plugin of your choice to make it compatible with it.
15
+
16
+ * ✏️ __Audio Filters__: Modify outgoing and incoming audio by implementing the `IAudioFilter` interface. Gaussian blurring for denoising and Opus (Concentus) encoding & decoding for lower bandwidth consumption are provided out of the box.
15
17
 
16
18
  ## Installation
17
19
  ⚠️ [OpenUPM](https://openupm.com/packages/com.adrenak.univoice/?subPage=versions) may not have up to date releases. Install using NPM registry instead 👇
18
20
 
19
- Ensure you have the NPM registry in the `packages.json` file of your Unity project with the following scopes:
21
+ Ensure you have the NPM registry in the `manifest.json` file of your Unity project with the following scopes:
20
22
  ```
21
23
  "scopedRegistries": [
22
24
  {
@@ -27,11 +29,12 @@ Ensure you have the NPM registry in the `packages.json` file of your Unity proje
27
29
  "com.adrenak.univoice",
28
30
  "com.adrenak.brw",
29
31
  "com.adrenak.unimic",
30
- "com.adrenak.unityopus"
32
+ "com.adrenak.concentus-unity"
31
33
  ]
32
34
  }
33
35
  }
34
36
  ```
37
+ Then add `com.adrenak.univoice:x.y.z` to the `dependencies` in your `manifest.json` file (where x.y.z is the version you wish to install). The list of versions is available on [the UniVoice NPM page](https://www.npmjs.com/package/com.adrenak.univoice?activeTab=versions).
35
38
 
36
39
  ## Docs
37
40
  Am API reference is available: http://www.vatsalambastha.com/univoice
@@ -42,11 +45,11 @@ This repository contains a sample scene for the Mirror network, which is the bes
42
45
  To try the sample, import Mirror and add the `UNIVOICE_MIRROR_NETWORK` compilation symbol to your project.
43
46
 
44
47
  ## Dependencies
45
- [com.adrenak.brw](https://www.github.com/adrenak/brw)`@1.0.1` for reading and writing messages for communication. See `MirrorServer.cs` and `MirrorClient.cs` where they're used.
48
+ [com.adrenak.brw](https://www.github.com/adrenak/brw) for reading and writing messages for communication. See `MirrorServer.cs` and `MirrorClient.cs` where they're used.
46
49
 
47
- [com.adrenak.unimic](https://www.github.com/adrenak/unimic)`@3.2.1` for easily capturing audio from any connected mic devices. See `UniMicInput.cs` for usage.
50
+ [com.adrenak.unimic](https://www.github.com/adrenak/unimic) for easily capturing audio from any connected mic devices. See `UniMicInput.cs` for usage. Also used for streaming audio playback. See `StreamedAudioSourceOutput.cs` for usage.
48
51
 
49
- [com.adrenak.unityopus](https://www.github.com/adrenak/unityopus)`@1.0.0` for Opus encoding and decoding. See `OpusFilter.cs` for usage
52
+ [com.adrenak.concentus-unity](https://www.github.com/adrenak/concentus-unity) for Opus encoding and decoding. See `ConcentusEncodeFilter.cs` and `ConcentusDecodeFilter.cs` for usage
50
53
 
51
54
  ## License and Support
52
55
  This project is under the [MIT license](https://github.com/adrenak/univoice/blob/master/LICENSE).
@@ -60,3 +63,4 @@ The developer can be reached at the following links:
60
63
  [LinkedIn](https://www.linkedin.com/in/vatsalAmbastha)
61
64
  [GitHub](https://www.github.com/adrenak)
62
65
  [Twitter](https://www.twitter.com/vatsalAmbastha)
66
+ Discord: `adrenak#1934`
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "Adrenak.UniVoice.Runtime",
3
+ "rootNamespace": "",
3
4
  "references": [
4
5
  "GUID:f87ecb857e752164ab814a3de8eb0262",
5
6
  "GUID:1f776cd02c03a7b4280b6b649d7758e2",
6
7
  "GUID:30817c1a0e6d646d99c048fc403f5979",
7
8
  "GUID:725ee7191c021de4dbf9269590ded755",
8
- "GUID:34d15e3fb5fa4b541b5a93a6dc182cc5"
9
+ "GUID:b118fd5a40c85ad4e9b38e8c4a42bbb1"
9
10
  ],
10
11
  "includePlatforms": [],
11
12
  "excludePlatforms": [],
@@ -77,8 +77,8 @@ namespace Adrenak.UniVoice {
77
77
  foreach (var filter in OutputFilters)
78
78
  audioFrame = filter.Run(audioFrame);
79
79
  }
80
-
81
- PeerOutputs[id].Feed(audioFrame);
80
+ if(audioFrame.samples.Length > 0)
81
+ PeerOutputs[id].Feed(audioFrame);
82
82
  };
83
83
  }
84
84
  }
@@ -99,7 +99,8 @@ namespace Adrenak.UniVoice {
99
99
  frame = filter.Run(frame);
100
100
  }
101
101
 
102
- Client.SendAudioFrame(frame);
102
+ if(frame.samples.Length > 0)
103
+ Client.SendAudioFrame(frame);
103
104
  };
104
105
  }
105
106
  }
@@ -0,0 +1,80 @@
1
+ using Concentus;
2
+ using System;
3
+
4
+ /*
5
+ * Opus encoding and decoding are VERY important for any real world use of UniVoice as without
6
+ * encoding the size of audio data is much (over 10x) larger.
7
+ * For more info see https://www.github.com/adrenak/concentus-unity
8
+ */
9
+ namespace Adrenak.UniVoice.Filters {
10
+ /// <summary>
11
+ /// Decodes Opus encoded audio. Use this as a filter for incoming client audio.
12
+ /// </summary>
13
+ public class ConcentusDecodeFilter : IAudioFilter {
14
+ IOpusDecoder decoder;
15
+ float[] decodeBuffer;
16
+ int inputChannelCount;
17
+ int inputFrequency;
18
+ byte[] floatsToBytes;
19
+
20
+ /// <summary>
21
+ /// Creates a Concentus decode filter.
22
+ /// </summary>
23
+ /// <param name="decodeBufferLength">
24
+ /// The length of the decode buffer. Default is 11520 to fit a large sample
25
+ /// with frequency 48000, duration 120ms and 2 channels. This should be enough
26
+ /// for almost all scenarios. Increase if you need more.
27
+ /// </param>
28
+ public ConcentusDecodeFilter(int decodeBufferLength = 11520) {
29
+ decodeBuffer = new float[decodeBufferLength];
30
+ }
31
+
32
+ public AudioFrame Run(AudioFrame input) {
33
+ inputChannelCount = input.channelCount;
34
+ inputFrequency = input.frequency;
35
+
36
+ CreateNewDecoderIfNeeded();
37
+
38
+ var decodeResult = Decode(input.samples, out Span<float> decoded);
39
+ if (decodeResult > 0) {
40
+ floatsToBytes = Utils.Bytes.FloatsToBytes(decoded.ToArray());
41
+ return new AudioFrame {
42
+ timestamp = input.timestamp,
43
+ samples = floatsToBytes,
44
+ channelCount = inputChannelCount,
45
+ frequency = inputFrequency
46
+ };
47
+ }
48
+ else {
49
+ return new AudioFrame {
50
+ timestamp = input.timestamp,
51
+ samples = new byte[0],
52
+ channelCount = inputChannelCount,
53
+ frequency = inputFrequency
54
+ };
55
+ }
56
+ }
57
+
58
+ int Decode(Span<byte> toDecode, out Span<float> decoded) {
59
+ // Decode the Opus packet into preallocated buffer
60
+ int samplesPerChannel = decoder.Decode(toDecode, decodeBuffer, decodeBuffer.Length);
61
+
62
+ if (samplesPerChannel > 0) {
63
+ int totalSamples = samplesPerChannel * inputChannelCount; // Total samples across all channels
64
+ decoded = decodeBuffer.AsSpan(0, totalSamples); // Trim to valid samples
65
+ }
66
+ else {
67
+ decoded = Span<float>.Empty;
68
+ }
69
+
70
+ return samplesPerChannel; // Return number of samples per channel or 0 on failure
71
+ }
72
+
73
+ void CreateNewDecoderIfNeeded() {
74
+ if (decoder == null || decoder.SampleRate != inputFrequency || decoder.NumChannels != inputChannelCount) {
75
+ decoder?.Dispose();
76
+ decoder = OpusCodecFactory.CreateDecoder(inputFrequency, inputChannelCount);
77
+ }
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,156 @@
1
+ using Concentus;
2
+ using Concentus.Enums;
3
+ using System;
4
+
5
+ /*
6
+ * Opus encoding and decoding are VERY important for any real world use of UniVoice as without
7
+ * encoding the size of audio data is much (over 10x) larger.
8
+ * For more info see https://www.github.com/adrenak/concentus-unity
9
+ */
10
+ namespace Adrenak.UniVoice.Filters {
11
+ /// <summary>
12
+ /// A filter that encodes audio using Opus. Use this as an output filter
13
+ /// to reduce the size of outgoing client audio
14
+ /// </summary>
15
+ public class ConcentusEncodeFilter : IAudioFilter {
16
+ public ConcentusFrequencies SamplingFrequency { get; private set; }
17
+ IOpusEncoder encoder;
18
+ IResampler resampler;
19
+ byte[] encodeBuffer;
20
+ float[] resampleBuffer;
21
+ int inputDuration;
22
+ int inputChannels;
23
+ int inputFrequency;
24
+ int resamplerChannelCount;
25
+ float[] bytesToFloats;
26
+ byte[] floatsToBytes;
27
+ int resamplerQuality;
28
+ int encoderComplexity;
29
+ int encoderBitrate;
30
+
31
+ /// <summary>
32
+ /// Creates a Concentus encode filter
33
+ /// </summary>
34
+ /// <param name="encodeFrequency">
35
+ /// The frequency the encoder runs at.
36
+ /// If the input audio frequency is different from this value, it will be resampled before encode.
37
+ /// </param>
38
+ /// <param name="resamplerQuality">Resampler quality [1, 10]</param>
39
+ /// <param name="encoderComplexity">Encoder complexity [1, 10]</param>
40
+ /// <param name="encoderBitrate">Encoder bitrate [16000, 256000]. Set to -1 to enable variable bitrate.</param>
41
+ /// <param name="encodeBufferLength">
42
+ /// The length of the encode buffer. Default is 46080 to fit a large sample
43
+ /// with frequency 48000, duration 120ms and 2 channels. This should be enough
44
+ /// for almost all scenarios. Increase if you need more.
45
+ /// </param>
46
+ public ConcentusEncodeFilter(
47
+ ConcentusFrequencies encodeFrequency = ConcentusFrequencies.Frequency_16000,
48
+ int resamplerQuality = 2,
49
+ int encoderComplexity = 3,
50
+ int encoderBitrate = 64000,
51
+ int encodeBufferLength = 46080
52
+ ) {
53
+ SamplingFrequency = encodeFrequency;
54
+ this.resamplerQuality = Math.Clamp(resamplerQuality, 1, 10);
55
+ this.encoderComplexity = Math.Clamp(encoderComplexity, 1, 10);
56
+ this.encoderBitrate = Math.Clamp(encoderBitrate, 16000, 256000);
57
+ encodeBuffer = new byte[encodeBufferLength];
58
+ }
59
+
60
+ public AudioFrame Run(AudioFrame input) {
61
+ inputChannels = input.channelCount;
62
+ inputFrequency = input.frequency;
63
+ inputDuration = ((input.samples.Length / 4) * 1000) / (input.frequency * input.channelCount);
64
+
65
+ CreateNewResamplerAndEncoderIfNeeded();
66
+
67
+ Span<float> toEncode;
68
+ bytesToFloats = Utils.Bytes.BytesToFloats(input.samples);
69
+ toEncode = bytesToFloats;
70
+
71
+ if (inputFrequency != (int)SamplingFrequency)
72
+ toEncode = Resample(bytesToFloats);
73
+
74
+ var encodeResult = Encode(toEncode, out Span<byte> encoded);
75
+ if (encodeResult > 0) {
76
+ floatsToBytes = encoded.ToArray();
77
+ return new AudioFrame {
78
+ timestamp = input.timestamp,
79
+ channelCount = inputChannels,
80
+ samples = floatsToBytes,
81
+ frequency = (int)SamplingFrequency
82
+ };
83
+ }
84
+ else {
85
+ return new AudioFrame {
86
+ timestamp = input.timestamp,
87
+ channelCount = inputChannels,
88
+ samples = new byte[0],
89
+ frequency = (int)SamplingFrequency
90
+ };
91
+ }
92
+ }
93
+
94
+ void CreateNewResamplerAndEncoderIfNeeded() {
95
+ if (resampleBuffer == null || resampleBuffer.Length != (int)SamplingFrequency * inputDuration * inputChannels / 1000)
96
+ resampleBuffer = new float[(int)SamplingFrequency * inputDuration * inputChannels / 1000];
97
+
98
+ if (resampler == null) {
99
+ resamplerChannelCount = inputChannels;
100
+ resampler = ResamplerFactory.CreateResampler(inputChannels, inputFrequency, (int)SamplingFrequency, resamplerQuality);
101
+ }
102
+ else {
103
+ resampler.GetRates(out int in_rate, out int out_rate);
104
+ if (in_rate != inputFrequency || out_rate != (int)SamplingFrequency || resamplerChannelCount != inputChannels) {
105
+ resampler.Dispose();
106
+ resamplerChannelCount = inputChannels;
107
+ resampler = ResamplerFactory.CreateResampler(inputChannels, inputFrequency, (int)SamplingFrequency, resamplerQuality);
108
+ }
109
+ }
110
+
111
+ if (encoder == null || encoder.SampleRate != (int)SamplingFrequency || encoder.NumChannels != inputChannels) {
112
+ encoder?.Dispose();
113
+ encoder = OpusCodecFactory.CreateEncoder((int)SamplingFrequency, inputChannels, OpusApplication.OPUS_APPLICATION_VOIP);
114
+ encoder.Complexity = encoderComplexity;
115
+ if(encoderBitrate == -1)
116
+ encoder.UseVBR = true;
117
+ else {
118
+ encoder.UseVBR = false;
119
+ encoder.Bitrate = encoderBitrate;
120
+ }
121
+ }
122
+ }
123
+
124
+ Span<float> Resample(Span<float> samples) {
125
+ // Calculate input and output lengths
126
+ int in_len = samples.Length / inputChannels; // Input samples per channel
127
+ int out_len = (int)SamplingFrequency * inputDuration / 1000; // Output samples per channel
128
+
129
+ // Perform resampling into preallocated buffer
130
+ resampler.ProcessInterleaved(samples, ref in_len, resampleBuffer, ref out_len);
131
+
132
+ // Return only the valid portion of resampled data
133
+ return resampleBuffer.AsSpan(0, out_len * inputChannels); // Trim to valid samples
134
+ }
135
+
136
+ int Encode(Span<float> toEncode, out Span<byte> encoded) {
137
+ int frameSize = (int)SamplingFrequency * inputDuration / 1000; // Samples per channel
138
+ int totalSamples = frameSize * inputChannels; // Total interleaved samples
139
+
140
+ if (toEncode.Length < totalSamples) {
141
+ encoded = Span<byte>.Empty;
142
+ return 0;
143
+ }
144
+
145
+ // Use preallocated encodeBuffer
146
+ int result = encoder.Encode(toEncode.Slice(0, totalSamples), frameSize, encodeBuffer, encodeBuffer.Length);
147
+
148
+ if (result > 0)
149
+ encoded = encodeBuffer.AsSpan(0, result); // Trim to actual encoded size
150
+ else
151
+ encoded = Span<byte>.Empty;
152
+
153
+ return result; // Return number of bytes written or 0 on failure
154
+ }
155
+ }
156
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 7e548eaedfb8c1b49812a12b3fba2c8a
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -0,0 +1,31 @@
1
+ namespace Adrenak.UniVoice.Filters {
2
+ /// <summary>
3
+ /// Defines supported sampling frequencies for the Opus codec via Concentus.
4
+ /// </summary>
5
+ public enum ConcentusFrequencies : int {
6
+ /// <summary>
7
+ /// 8 kHz sampling frequency, typically used for narrowband audio.
8
+ /// </summary>
9
+ Frequency_8000 = 8000,
10
+
11
+ /// <summary>
12
+ /// 12 kHz sampling frequency, suitable for medium-band audio.
13
+ /// </summary>
14
+ Frequency_12000 = 12000,
15
+
16
+ /// <summary>
17
+ /// 16 kHz sampling frequency, commonly used for wideband speech.
18
+ /// </summary>
19
+ Frequency_16000 = 16000,
20
+
21
+ /// <summary>
22
+ /// 24 kHz sampling frequency, providing good quality for music and audio.
23
+ /// </summary>
24
+ Frequency_24000 = 24000,
25
+
26
+ /// <summary>
27
+ /// 48 kHz sampling frequency, offering the highest audio quality.
28
+ /// </summary>
29
+ Frequency_48000 = 48000,
30
+ }
31
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 53ba54c62b14cf4439d3b71fe7b1d08c
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: d3ce124588cebfb4e812cb9b91e46525
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
@@ -11,14 +11,24 @@ namespace Adrenak.UniVoice.Inputs {
11
11
  /// </summary>
12
12
  public class UniMicInput : IAudioInput {
13
13
  const string TAG = "UniMicInput";
14
-
15
14
  public event Action<AudioFrame> OnFrameReady;
16
15
 
17
- public Mic.Device Device { get; private set; }
16
+ public Mic.Device device;
17
+ public Mic.Device Device {
18
+ get => device;
19
+ set {
20
+ if (device == value)
21
+ return;
22
+ if(device != null)
23
+ device.OnFrameCollected -= OnFrameCollected;
24
+ device = value;
25
+ if(device != null)
26
+ device.OnFrameCollected += OnFrameCollected;
27
+ }
28
+ }
18
29
 
19
30
  public UniMicInput(Mic.Device device) {
20
31
  Device = device;
21
- device.OnFrameCollected += OnFrameCollected;
22
32
  }
23
33
 
24
34
  private void OnFrameCollected(int frequency, int channels, float[] samples) {
@@ -32,7 +42,8 @@ namespace Adrenak.UniVoice.Inputs {
32
42
  }
33
43
 
34
44
  public void Dispose() {
35
- Device.OnFrameCollected -= OnFrameCollected;
45
+ if(Device != null)
46
+ Device.OnFrameCollected -= OnFrameCollected;
36
47
  Debug.unityLogger.Log(TAG, "Disposed");
37
48
  }
38
49
  }
@@ -43,7 +43,7 @@ RenderSettings:
43
43
  --- !u!157 &3
44
44
  LightmapSettings:
45
45
  m_ObjectHideFlags: 0
46
- serializedVersion: 11
46
+ serializedVersion: 12
47
47
  m_GIWorkflowMode: 0
48
48
  m_GISettings:
49
49
  serializedVersion: 2
@@ -98,7 +98,8 @@ LightmapSettings:
98
98
  m_TrainingDataDestination: TrainingData
99
99
  m_LightProbeSampleCountMultiplier: 4
100
100
  m_LightingDataAsset: {fileID: 0}
101
- m_UseShadowmask: 1
101
+ m_LightingSettings: {fileID: 4890085278179872738, guid: ad4b002903bb1474ca81b5d0f5e89dd8,
102
+ type: 2}
102
103
  --- !u!196 &4
103
104
  NavMeshSettings:
104
105
  serializedVersion: 2
@@ -118,6 +119,8 @@ NavMeshSettings:
118
119
  manualTileSize: 0
119
120
  tileSize: 256
120
121
  accuratePlacement: 0
122
+ maxJobWorkers: 0
123
+ preserveTilesOutsideBounds: 0
121
124
  debug:
122
125
  m_Flags: 0
123
126
  m_NavMeshData: {fileID: 0}
@@ -149,6 +152,7 @@ RectTransform:
149
152
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
150
153
  m_LocalPosition: {x: 0, y: 0, z: 0}
151
154
  m_LocalScale: {x: 1, y: 1, z: 1}
155
+ m_ConstrainProportionsScale: 0
152
156
  m_Children:
153
157
  - {fileID: 1116173713}
154
158
  m_Father: {fileID: 1726461497}
@@ -174,6 +178,7 @@ MonoBehaviour:
174
178
  m_Material: {fileID: 0}
175
179
  m_Color: {r: 1, g: 1, b: 1, a: 1}
176
180
  m_RaycastTarget: 1
181
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
177
182
  m_Maskable: 1
178
183
  m_OnCullStateChanged:
179
184
  m_PersistentCalls:
@@ -241,6 +246,7 @@ Transform:
241
246
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
242
247
  m_LocalPosition: {x: 153, y: 294.1, z: 0}
243
248
  m_LocalScale: {x: 1, y: 1, z: 1}
249
+ m_ConstrainProportionsScale: 0
244
250
  m_Children: []
245
251
  m_Father: {fileID: 0}
246
252
  m_RootOrder: 3
@@ -273,6 +279,7 @@ RectTransform:
273
279
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
274
280
  m_LocalPosition: {x: 0, y: 0, z: 0}
275
281
  m_LocalScale: {x: 1, y: 1, z: 1}
282
+ m_ConstrainProportionsScale: 0
276
283
  m_Children:
277
284
  - {fileID: 983083073}
278
285
  m_Father: {fileID: 1726461497}
@@ -298,6 +305,7 @@ MonoBehaviour:
298
305
  m_Material: {fileID: 0}
299
306
  m_Color: {r: 1, g: 1, b: 1, a: 1}
300
307
  m_RaycastTarget: 1
308
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
301
309
  m_Maskable: 1
302
310
  m_OnCullStateChanged:
303
311
  m_PersistentCalls:
@@ -399,6 +407,7 @@ Transform:
399
407
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
400
408
  m_LocalPosition: {x: 0, y: 1, z: -10}
401
409
  m_LocalScale: {x: 1, y: 1, z: 1}
410
+ m_ConstrainProportionsScale: 0
402
411
  m_Children: []
403
412
  m_Father: {fileID: 0}
404
413
  m_RootOrder: 0
@@ -566,6 +575,7 @@ RectTransform:
566
575
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
567
576
  m_LocalPosition: {x: 0, y: 0, z: 0}
568
577
  m_LocalScale: {x: 1, y: 1, z: 1}
578
+ m_ConstrainProportionsScale: 0
569
579
  m_Children:
570
580
  - {fileID: 1917147245}
571
581
  m_Father: {fileID: 965416053}
@@ -605,6 +615,7 @@ RectTransform:
605
615
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
606
616
  m_LocalPosition: {x: 0, y: 0, z: 0}
607
617
  m_LocalScale: {x: 1, y: 1, z: 1}
618
+ m_ConstrainProportionsScale: 0
608
619
  m_Children:
609
620
  - {fileID: 1790122419}
610
621
  m_Father: {fileID: 909505741}
@@ -629,6 +640,7 @@ MonoBehaviour:
629
640
  m_EditorClassIdentifier:
630
641
  m_Navigation:
631
642
  m_Mode: 3
643
+ m_WrapAround: 0
632
644
  m_SelectOnUp: {fileID: 0}
633
645
  m_SelectOnDown: {fileID: 0}
634
646
  m_SelectOnLeft: {fileID: 0}
@@ -678,6 +690,7 @@ MonoBehaviour:
678
690
  m_Material: {fileID: 0}
679
691
  m_Color: {r: 1, g: 1, b: 1, a: 1}
680
692
  m_RaycastTarget: 1
693
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
681
694
  m_Maskable: 1
682
695
  m_OnCullStateChanged:
683
696
  m_PersistentCalls:
@@ -758,6 +771,7 @@ MonoBehaviour:
758
771
  m_FallbackScreenDPI: 96
759
772
  m_DefaultSpriteDPI: 96
760
773
  m_DynamicPixelsPerUnit: 1
774
+ m_PresetInfoIsWorld: 0
761
775
  --- !u!223 &877991846
762
776
  Canvas:
763
777
  m_ObjectHideFlags: 0
@@ -789,6 +803,7 @@ RectTransform:
789
803
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
790
804
  m_LocalPosition: {x: 0, y: 0, z: 0}
791
805
  m_LocalScale: {x: 0, y: 0, z: 0}
806
+ m_ConstrainProportionsScale: 0
792
807
  m_Children:
793
808
  - {fileID: 1796628514}
794
809
  - {fileID: 1726461497}
@@ -829,6 +844,7 @@ RectTransform:
829
844
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
830
845
  m_LocalPosition: {x: 0, y: 0, z: 0}
831
846
  m_LocalScale: {x: 1, y: 1, z: 1}
847
+ m_ConstrainProportionsScale: 0
832
848
  m_Children:
833
849
  - {fileID: 1798840738}
834
850
  - {fileID: 965416053}
@@ -886,6 +902,7 @@ MonoBehaviour:
886
902
  m_Material: {fileID: 0}
887
903
  m_Color: {r: 1, g: 1, b: 1, a: 0.392}
888
904
  m_RaycastTarget: 1
905
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
889
906
  m_Maskable: 1
890
907
  m_OnCullStateChanged:
891
908
  m_PersistentCalls:
@@ -937,6 +954,7 @@ RectTransform:
937
954
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
938
955
  m_LocalPosition: {x: 0, y: 0, z: 0}
939
956
  m_LocalScale: {x: 1, y: 1, z: 1}
957
+ m_ConstrainProportionsScale: 0
940
958
  m_Children:
941
959
  - {fileID: 741068687}
942
960
  m_Father: {fileID: 909505741}
@@ -961,6 +979,7 @@ MonoBehaviour:
961
979
  m_EditorClassIdentifier:
962
980
  m_Navigation:
963
981
  m_Mode: 3
982
+ m_WrapAround: 0
964
983
  m_SelectOnUp: {fileID: 0}
965
984
  m_SelectOnDown: {fileID: 0}
966
985
  m_SelectOnLeft: {fileID: 0}
@@ -989,7 +1008,7 @@ MonoBehaviour:
989
1008
  m_TargetGraphic: {fileID: 1917147246}
990
1009
  m_HandleRect: {fileID: 1917147245}
991
1010
  m_Direction: 0
992
- m_Value: 1
1011
+ m_Value: 0
993
1012
  m_Size: 0.99999994
994
1013
  m_NumberOfSteps: 0
995
1014
  m_OnValueChanged:
@@ -1010,6 +1029,7 @@ MonoBehaviour:
1010
1029
  m_Material: {fileID: 0}
1011
1030
  m_Color: {r: 1, g: 1, b: 1, a: 1}
1012
1031
  m_RaycastTarget: 1
1032
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1013
1033
  m_Maskable: 1
1014
1034
  m_OnCullStateChanged:
1015
1035
  m_PersistentCalls:
@@ -1060,6 +1080,7 @@ RectTransform:
1060
1080
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1061
1081
  m_LocalPosition: {x: 0, y: 0, z: 0}
1062
1082
  m_LocalScale: {x: 1, y: 1, z: 1}
1083
+ m_ConstrainProportionsScale: 0
1063
1084
  m_Children: []
1064
1085
  m_Father: {fileID: 667953547}
1065
1086
  m_RootOrder: 0
@@ -1084,6 +1105,7 @@ MonoBehaviour:
1084
1105
  m_Material: {fileID: 0}
1085
1106
  m_Color: {r: 1, g: 1, b: 1, a: 1}
1086
1107
  m_RaycastTarget: 1
1108
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1087
1109
  m_Maskable: 1
1088
1110
  m_OnCullStateChanged:
1089
1111
  m_PersistentCalls:
@@ -1138,6 +1160,7 @@ RectTransform:
1138
1160
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1139
1161
  m_LocalPosition: {x: 0, y: 0, z: 0}
1140
1162
  m_LocalScale: {x: 1, y: 1, z: 1}
1163
+ m_ConstrainProportionsScale: 0
1141
1164
  m_Children: []
1142
1165
  m_Father: {fileID: 1726461497}
1143
1166
  m_RootOrder: 2
@@ -1162,6 +1185,7 @@ MonoBehaviour:
1162
1185
  m_Material: {fileID: 0}
1163
1186
  m_Color: {r: 1, g: 1, b: 1, a: 1}
1164
1187
  m_RaycastTarget: 1
1188
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1165
1189
  m_Maskable: 1
1166
1190
  m_OnCullStateChanged:
1167
1191
  m_PersistentCalls:
@@ -1216,6 +1240,7 @@ RectTransform:
1216
1240
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1217
1241
  m_LocalPosition: {x: 0, y: 0, z: 0}
1218
1242
  m_LocalScale: {x: 1, y: 1, z: 1}
1243
+ m_ConstrainProportionsScale: 0
1219
1244
  m_Children: []
1220
1245
  m_Father: {fileID: 159342650}
1221
1246
  m_RootOrder: 0
@@ -1240,6 +1265,7 @@ MonoBehaviour:
1240
1265
  m_Material: {fileID: 0}
1241
1266
  m_Color: {r: 1, g: 1, b: 1, a: 1}
1242
1267
  m_RaycastTarget: 1
1268
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1243
1269
  m_Maskable: 1
1244
1270
  m_OnCullStateChanged:
1245
1271
  m_PersistentCalls:
@@ -1294,6 +1320,7 @@ RectTransform:
1294
1320
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1295
1321
  m_LocalPosition: {x: 0, y: 0, z: 0}
1296
1322
  m_LocalScale: {x: 1, y: 1, z: 1}
1323
+ m_ConstrainProportionsScale: 0
1297
1324
  m_Children: []
1298
1325
  m_Father: {fileID: 1798840738}
1299
1326
  m_RootOrder: 0
@@ -1342,6 +1369,7 @@ MonoBehaviour:
1342
1369
  m_ChildControlHeight: 0
1343
1370
  m_ChildScaleWidth: 0
1344
1371
  m_ChildScaleHeight: 0
1372
+ m_ReverseArrangement: 0
1345
1373
  --- !u!1001 &1206482348
1346
1374
  PrefabInstance:
1347
1375
  m_ObjectHideFlags: 0
@@ -1507,6 +1535,7 @@ RectTransform:
1507
1535
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1508
1536
  m_LocalPosition: {x: 0, y: 0, z: 0}
1509
1537
  m_LocalScale: {x: 1, y: 1, z: 1}
1538
+ m_ConstrainProportionsScale: 0
1510
1539
  m_Children: []
1511
1540
  m_Father: {fileID: 1790122419}
1512
1541
  m_RootOrder: 0
@@ -1531,6 +1560,7 @@ MonoBehaviour:
1531
1560
  m_Material: {fileID: 0}
1532
1561
  m_Color: {r: 1, g: 1, b: 1, a: 1}
1533
1562
  m_RaycastTarget: 1
1563
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1534
1564
  m_Maskable: 1
1535
1565
  m_OnCullStateChanged:
1536
1566
  m_PersistentCalls:
@@ -1581,6 +1611,7 @@ RectTransform:
1581
1611
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
1582
1612
  m_LocalPosition: {x: 0, y: 0, z: 0}
1583
1613
  m_LocalScale: {x: 1, y: 1, z: 1}
1614
+ m_ConstrainProportionsScale: 0
1584
1615
  m_Children: []
1585
1616
  m_Father: {fileID: 1726461497}
1586
1617
  m_RootOrder: 1
@@ -1605,6 +1636,7 @@ MonoBehaviour:
1605
1636
  m_Material: {fileID: 0}
1606
1637
  m_Color: {r: 0.8962264, g: 0.8962264, b: 0.8962264, a: 1}
1607
1638
  m_RaycastTarget: 1
1639
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1608
1640
  m_Maskable: 1
1609
1641
  m_OnCullStateChanged:
1610
1642
  m_PersistentCalls:
@@ -1657,6 +1689,7 @@ RectTransform:
1657
1689
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
1658
1690
  m_LocalPosition: {x: 0, y: 0, z: 0}
1659
1691
  m_LocalScale: {x: 1, y: 1, z: 1}
1692
+ m_ConstrainProportionsScale: 0
1660
1693
  m_Children:
1661
1694
  - {fileID: 909505741}
1662
1695
  - {fileID: 1480403820}
@@ -1699,6 +1732,7 @@ RectTransform:
1699
1732
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1700
1733
  m_LocalPosition: {x: 0, y: 0, z: 0}
1701
1734
  m_LocalScale: {x: 1, y: 1, z: 1}
1735
+ m_ConstrainProportionsScale: 0
1702
1736
  m_Children:
1703
1737
  - {fileID: 1423776684}
1704
1738
  m_Father: {fileID: 789642335}
@@ -1737,6 +1771,7 @@ RectTransform:
1737
1771
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
1738
1772
  m_LocalPosition: {x: 0, y: 0, z: 0}
1739
1773
  m_LocalScale: {x: 1, y: 1, z: 1}
1774
+ m_ConstrainProportionsScale: 0
1740
1775
  m_Children: []
1741
1776
  m_Father: {fileID: 877991847}
1742
1777
  m_RootOrder: 0
@@ -1761,6 +1796,7 @@ MonoBehaviour:
1761
1796
  m_Material: {fileID: 0}
1762
1797
  m_Color: {r: 0, g: 0, b: 0, a: 1}
1763
1798
  m_RaycastTarget: 1
1799
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1764
1800
  m_Maskable: 1
1765
1801
  m_OnCullStateChanged:
1766
1802
  m_PersistentCalls:
@@ -1812,6 +1848,7 @@ RectTransform:
1812
1848
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1813
1849
  m_LocalPosition: {x: 0, y: 0, z: 0}
1814
1850
  m_LocalScale: {x: 1, y: 1, z: 1}
1851
+ m_ConstrainProportionsScale: 0
1815
1852
  m_Children:
1816
1853
  - {fileID: 1188574985}
1817
1854
  m_Father: {fileID: 909505741}
@@ -1850,6 +1887,7 @@ MonoBehaviour:
1850
1887
  m_Material: {fileID: 0}
1851
1888
  m_Color: {r: 1, g: 1, b: 1, a: 1}
1852
1889
  m_RaycastTarget: 1
1890
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1853
1891
  m_Maskable: 1
1854
1892
  m_OnCullStateChanged:
1855
1893
  m_PersistentCalls:
@@ -1900,6 +1938,7 @@ RectTransform:
1900
1938
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1901
1939
  m_LocalPosition: {x: 0, y: 0, z: 0}
1902
1940
  m_LocalScale: {x: 1, y: 1, z: 1}
1941
+ m_ConstrainProportionsScale: 0
1903
1942
  m_Children: []
1904
1943
  m_Father: {fileID: 741068687}
1905
1944
  m_RootOrder: 0
@@ -1924,6 +1963,7 @@ MonoBehaviour:
1924
1963
  m_Material: {fileID: 0}
1925
1964
  m_Color: {r: 1, g: 1, b: 1, a: 1}
1926
1965
  m_RaycastTarget: 1
1966
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
1927
1967
  m_Maskable: 1
1928
1968
  m_OnCullStateChanged:
1929
1969
  m_PersistentCalls:
@@ -2008,6 +2048,7 @@ Transform:
2008
2048
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
2009
2049
  m_LocalPosition: {x: 0, y: 0, z: 0}
2010
2050
  m_LocalScale: {x: 1, y: 1, z: 1}
2051
+ m_ConstrainProportionsScale: 0
2011
2052
  m_Children: []
2012
2053
  m_Father: {fileID: 0}
2013
2054
  m_RootOrder: 2
@@ -2039,7 +2080,7 @@ MonoBehaviour:
2039
2080
  m_PrefabInstance: {fileID: 0}
2040
2081
  m_PrefabAsset: {fileID: 0}
2041
2082
  m_GameObject: {fileID: 2117923849}
2042
- m_Enabled: 0
2083
+ m_Enabled: 1
2043
2084
  m_EditorHideFlags: 0
2044
2085
  m_Script: {fileID: 11500000, guid: 6b0fecffa3f624585964b0d0eb21b18e, type: 3}
2045
2086
  m_Name:
@@ -2095,7 +2136,7 @@ MonoBehaviour:
2095
2136
  offlineScene:
2096
2137
  onlineScene:
2097
2138
  offlineSceneLoadDelay: 0
2098
- transport: {fileID: 2117923854}
2139
+ transport: {fileID: 2117923850}
2099
2140
  networkAddress: localhost
2100
2141
  maxConnections: 100
2101
2142
  disconnectInactiveConnections: 0
@@ -2130,6 +2171,7 @@ Transform:
2130
2171
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
2131
2172
  m_LocalPosition: {x: 1489.682, y: 663.6068, z: 178.25}
2132
2173
  m_LocalScale: {x: 1, y: 1, z: 1}
2174
+ m_ConstrainProportionsScale: 0
2133
2175
  m_Children: []
2134
2176
  m_Father: {fileID: 0}
2135
2177
  m_RootOrder: 4
@@ -2141,7 +2183,7 @@ MonoBehaviour:
2141
2183
  m_PrefabInstance: {fileID: 0}
2142
2184
  m_PrefabAsset: {fileID: 0}
2143
2185
  m_GameObject: {fileID: 2117923849}
2144
- m_Enabled: 1
2186
+ m_Enabled: 0
2145
2187
  m_EditorHideFlags: 0
2146
2188
  m_Script: {fileID: 11500000, guid: c7424c1070fad4ba2a7a96b02fbeb4bb, type: 3}
2147
2189
  m_Name:
@@ -7,12 +7,11 @@ using UnityEngine;
7
7
  using UnityEngine.UI;
8
8
  using UnityEngine.Android;
9
9
 
10
+ using Adrenak.UniMic;
10
11
  using Adrenak.UniVoice.Networks;
11
12
  using Adrenak.UniVoice.Outputs;
12
13
  using Adrenak.UniVoice.Inputs;
13
14
  using Adrenak.UniVoice.Filters;
14
- using Adrenak.UniMic;
15
- using Adrenak.UnityOpus;
16
15
 
17
16
  namespace Adrenak.UniVoice.Samples {
18
17
  public class GroupVoiceCallMirrorSample : MonoBehaviour {
@@ -83,35 +82,14 @@ namespace Adrenak.UniVoice.Samples {
83
82
  // We add some filters to the input audio
84
83
  // - The first is audio blur, so that the audio that's been captured by this client
85
84
  // has lesser noise
86
- var outgoingBlur = new GaussianAudioBlur();
87
-
85
+ session.InputFilters.Add(new GaussianAudioBlur());
88
86
  // - The next one is the Opus encoder filter. This is VERY important. Without this the
89
87
  // outgoing data would be very large, usually by a factor of 10 or more.
90
- // To do this we first create the encoder, then use it to create an instance of
91
- // of the OpusEncodeFilter class
92
- var encoder = new Encoder(
93
- (SamplingFrequency)mic.SamplingFrequency,
94
- (NumChannels)mic.ChannelCount,
95
- OpusApplication.VoIP
96
- );
97
- var outgoingEncode = new OpusEncodeFilter(encoder);
98
-
99
- // Finally, we add both the input filters to the session. Note that session.InputFilters
100
- // is a list and the filters are run in the order they are added to it.
101
- session.InputFilters.Add(outgoingBlur);
102
- session.InputFilters.Add(outgoingEncode);
103
-
104
- // Next, for incoming audio we create some filters.
105
- // - first is the decoder, because the audio that we are expecting would be encoded.
106
- // So we create a decoder, use that to create a OpusDecodeFilter and add it to the sessions
107
- // output filters
108
- var decoder = new Decoder(
109
- (SamplingFrequency)mic.SamplingFrequency,
110
- (NumChannels)mic.ChannelCount
111
- );
112
- var incomingDecode = new OpusDecodeFilter(decoder);
113
-
114
- session.OutputFilters.Add(incomingDecode);
88
+ session.InputFilters.Add(new ConcentusEncodeFilter());
89
+
90
+ // Next, for incoming audio we register the Concentus decode filter as the audio we'd
91
+ // receive from other clients would be encoded and not readily playable
92
+ session.OutputFilters.Add(new ConcentusDecodeFilter());
115
93
 
116
94
  // Subscribe to some server events
117
95
  server.OnServerStart += () => {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "com.adrenak.univoice",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "displayName": "Adrenak.UniVoice",
5
- "description": "Network agnostic voice chat/VoIP framework for Unity.",
6
- "unity": "2019.4",
5
+ "description": "Voice chat/VoIP framework for Unity.",
6
+ "unity": "2021.2",
7
7
  "author": {
8
8
  "name": "Vatsal Ambastha",
9
9
  "email": "ambastha.vatsal@gmail.com",
@@ -19,7 +19,10 @@
19
19
  "Unity3d",
20
20
  "VR",
21
21
  "AR",
22
- "Metaverse"
22
+ "Metaverse",
23
+ "mic",
24
+ "opus",
25
+ "concentus"
23
26
  ],
24
27
  "samples": [
25
28
  {
@@ -28,12 +31,9 @@
28
31
  "path": "Samples~"
29
32
  }
30
33
  ],
31
- "publishConfig": {
32
- "registry": "https://registry.npmjs.org"
33
- },
34
34
  "dependencies": {
35
35
  "com.adrenak.brw": "1.0.1",
36
- "com.adrenak.unimic": "3.2.1",
37
- "com.adrenak.unityopus": "1.0.0"
36
+ "com.adrenak.unimic": "3.2.2",
37
+ "com.adrenak.concentus-unity": "1.0.1"
38
38
  }
39
39
  }
@@ -1,85 +0,0 @@
1
- using Adrenak.UnityOpus;
2
-
3
- using System;
4
-
5
- /*
6
- * Opus encoding and decoding are VERY important for any real world use of UniVoice as without
7
- * encoding the size of audio data is much (over 10x) larger.
8
- * For more info see https://www.github.com/adrenak/UnityOpus
9
- */
10
- namespace Adrenak.UniVoice.Filters {
11
- /// <summary>
12
- /// A filter that encodes audio using Opus. Use this as an output filter
13
- /// to reduce the size of outgoing client audio
14
- /// </summary>
15
- public class OpusEncodeFilter : IAudioFilter {
16
- Encoder encoder;
17
- byte[] outputBuffer;
18
-
19
- public OpusEncodeFilter(Encoder encoder) {
20
- this.encoder = encoder;
21
- }
22
-
23
- public AudioFrame Run(AudioFrame input) {
24
- if(outputBuffer == null)
25
- outputBuffer = new byte[input.samples.Length * 4];
26
- else if(input.samples.Length != outputBuffer.Length * 4)
27
- outputBuffer = new byte[input.samples.Length * 4];
28
-
29
- int encodeResult = encoder.Encode(Utils.Bytes.BytesToFloats(input.samples), outputBuffer);
30
- if (encodeResult > 0) {
31
- byte[] encodedBytes = new byte[encodeResult];
32
- Array.Copy(outputBuffer, encodedBytes, encodedBytes.Length);
33
- return new AudioFrame {
34
- timestamp = 0,
35
- frequency = input.frequency,
36
- channelCount = input.channelCount,
37
- samples = encodedBytes
38
- };
39
- }
40
- else {
41
- return new AudioFrame {
42
- timestamp = 0,
43
- frequency = input.frequency,
44
- channelCount = input.channelCount,
45
- samples = null
46
- };
47
- }
48
- }
49
- }
50
-
51
- /// <summary>
52
- /// Decodes Opus encoded audio. Use this as a filter for incoming client audio.
53
- /// </summary>
54
- public class OpusDecodeFilter : IAudioFilter {
55
- Decoder decoder;
56
- float[] outputBuffer;
57
-
58
- public OpusDecodeFilter(Decoder decoder, int outputBufferLength = 48000) {
59
- this.decoder = decoder;
60
- outputBuffer = new float[outputBufferLength];
61
- }
62
-
63
- public AudioFrame Run(AudioFrame input) {
64
- var decodeResult = decoder.Decode(input.samples, input.samples.Length, outputBuffer);
65
- if(decodeResult > 0) {
66
- float[] decoded = new float[decodeResult];
67
- Array.Copy(outputBuffer, decoded, decoded.Length);
68
- return new AudioFrame {
69
- timestamp = 0,
70
- frequency = input.frequency,
71
- channelCount = input.channelCount,
72
- samples = Utils.Bytes.FloatsToBytes(decoded)
73
- };
74
- }
75
- else {
76
- return new AudioFrame {
77
- timestamp = 0,
78
- frequency = input.frequency,
79
- channelCount = input.channelCount,
80
- samples = null
81
- };
82
- }
83
- }
84
- }
85
- }