com.adrenak.univoice 3.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 +36 -54
- package/Runtime/Adrenak.UniVoice.Runtime.asmdef +20 -3
- package/Runtime/ClientSession.cs +138 -0
- package/Runtime/{Interfaces/IChatroomNetwork.cs.meta → ClientSession.cs.meta} +1 -1
- package/Runtime/Common/Utils.cs +55 -0
- package/Runtime/{Types/ChatroomAgentMode.cs.meta → Common/Utils.cs.meta} +1 -1
- package/Runtime/Common.meta +8 -0
- package/Runtime/Impl/Filters/Concentus Opus Filters/ConcentusDecodeFilter.cs +80 -0
- package/Runtime/{ChatroomAgent.cs.meta → Impl/Filters/Concentus Opus Filters/ConcentusDecodeFilter.cs.meta } +1 -1
- package/Runtime/Impl/Filters/Concentus Opus Filters/ConcentusEncodeFilter.cs +156 -0
- package/Runtime/Impl/Filters/Concentus Opus Filters/ConcentusEncodeFilter.cs.meta +11 -0
- package/Runtime/Impl/Filters/Concentus Opus Filters/ConcentusFrequencies.cs +31 -0
- package/Runtime/Impl/Filters/Concentus Opus Filters/ConcentusFrequencies.cs.meta +11 -0
- package/Runtime/Impl/Filters/Concentus Opus Filters.meta +8 -0
- package/Runtime/Impl/Filters/GaussianAudioBlur.cs +90 -0
- package/Runtime/Impl/Filters/GaussianAudioBlur.cs.meta +11 -0
- package/Runtime/Impl/Filters.meta +8 -0
- package/Runtime/Impl/Inputs/UniMicInput.cs +50 -0
- package/Runtime/Impl/Inputs/UniMicInput.cs.meta +11 -0
- package/Runtime/Impl/Inputs.meta +8 -0
- package/Runtime/Impl/Networks/Mirror/MirrorClient.cs +161 -0
- package/Runtime/Impl/Networks/Mirror/MirrorClient.cs.meta +11 -0
- package/Runtime/Impl/Networks/Mirror/MirrorMessage.cs +21 -0
- package/Runtime/Impl/Networks/Mirror/MirrorMessage.cs.meta +11 -0
- package/Runtime/Impl/Networks/Mirror/MirrorMessageTags.cs +16 -0
- package/Runtime/Impl/Networks/Mirror/MirrorMessageTags.cs.meta +11 -0
- package/Runtime/Impl/Networks/Mirror/MirrorModeObserver.cs +43 -0
- package/Runtime/Impl/Networks/Mirror/MirrorModeObserver.cs.meta +11 -0
- package/Runtime/Impl/Networks/Mirror/MirrorServer.cs +234 -0
- package/Runtime/Impl/Networks/Mirror/MirrorServer.cs.meta +11 -0
- package/Runtime/Impl/Networks/Mirror.meta +8 -0
- package/Runtime/Impl/Networks.meta +8 -0
- package/Runtime/Impl/Outputs/StreamedAudioSourceOutput.cs +56 -0
- package/Runtime/Impl/Outputs/StreamedAudioSourceOutput.cs.meta +11 -0
- package/Runtime/Impl/Outputs.meta +8 -0
- package/Runtime/Impl.meta +8 -0
- package/Runtime/Interfaces/IAudioClient.cs +77 -0
- package/Runtime/Interfaces/IAudioClient.cs.meta +11 -0
- package/Runtime/Interfaces/IAudioFilter.cs +10 -0
- package/Runtime/Interfaces/IAudioFilter.cs.meta +11 -0
- package/Runtime/Interfaces/IAudioInput.cs +1 -24
- package/Runtime/Interfaces/IAudioOutput.cs +4 -30
- package/Runtime/Interfaces/IAudioOutputFactory.cs +2 -7
- package/Runtime/Interfaces/IAudioServer.cs +41 -0
- package/Runtime/Interfaces/IAudioServer.cs.meta +11 -0
- package/Runtime/Types/{ChatroomAudioSegment.cs → AudioFrame.cs} +7 -6
- package/Runtime/Types/VoiceSettings.cs +53 -0
- package/Runtime/Types/VoiceSettings.cs.meta +11 -0
- package/Samples~/Group Chat Sample/Prefabs/Mic Toggle.prefab +235 -0
- package/{CHANGELOG.md.meta → Samples~/Group Chat Sample/Prefabs/Mic Toggle.prefab.meta } +2 -2
- package/Samples~/Group Chat Sample/Prefabs/Peer View.prefab +851 -0
- package/Samples~/Group Chat Sample/Prefabs/Peer View.prefab.meta +7 -0
- package/Samples~/Group Chat Sample/Prefabs.meta +8 -0
- package/Samples~/Group Chat Sample/Scenes/GroupVoiceCallSample-Mirror.unity +2202 -0
- package/Samples~/Group Chat Sample/Scenes/GroupVoiceCallSample-Mirror.unity.meta +7 -0
- package/Samples~/Group Chat Sample/Scenes.meta +8 -0
- package/Samples~/Group Chat Sample/Scripts/GroupVoiceCallMirrorSample.cs +195 -0
- package/Samples~/Group Chat Sample/Scripts/GroupVoiceCallMirrorSample.cs.meta +11 -0
- package/Samples~/Group Chat Sample/Scripts/PeerView.cs +74 -0
- package/Samples~/Group Chat Sample/Scripts/PeerView.cs.meta +11 -0
- package/Samples~/Group Chat Sample/Scripts.meta +8 -0
- package/Samples~/Group Chat Sample/Sprites/mic.png +0 -0
- package/Samples~/Group Chat Sample/Sprites/mic.png.meta +88 -0
- package/Samples~/Group Chat Sample/Sprites/off.png +0 -0
- package/Samples~/Group Chat Sample/Sprites/off.png.meta +88 -0
- package/Samples~/Group Chat Sample/Sprites/on.png +0 -0
- package/Samples~/Group Chat Sample/Sprites/on.png.meta +88 -0
- package/Samples~/Group Chat Sample/Sprites/speaker.png +0 -0
- package/Samples~/Group Chat Sample/Sprites/speaker.png.meta +88 -0
- package/Samples~/Group Chat Sample/Sprites.meta +8 -0
- package/Samples~/Group Chat Sample.meta +8 -0
- package/package.json +19 -9
- package/CHANGELOG.md +0 -67
- package/Runtime/ChatroomAgent.cs +0 -260
- package/Runtime/Interfaces/IChatroomNetwork.cs +0 -125
- package/Runtime/Types/ChatroomAgentMode.cs +0 -22
- package/Runtime/Types/ChatroomPeerSettings.cs +0 -18
- package/Runtime/Types/ChatroomPeerSettings.cs.meta +0 -11
- /package/Runtime/Types/{ChatroomAudioSegment.cs.meta → AudioFrame.cs.meta} +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
Note: Inbuilt implementations and samples have been removed from this repository. They'll be added to separate repositories soon.
|
|
2
|
-
|
|
3
1
|
# UniVoice
|
|
4
2
|
UniVoice is a voice chat/VoIP solution for Unity.
|
|
5
3
|
|
|
@@ -9,76 +7,60 @@ Some features of UniVoice:
|
|
|
9
7
|
- ⚙ Peer specific settings. Don't want to listen to a peer? Mute them. Don't want someone listening to you? Mute yourself against them.
|
|
10
8
|
|
|
11
9
|
- 🎨 Customize your audio input, output and networking layer.
|
|
12
|
-
* 🎤 __Configurable Audio Input__:
|
|
10
|
+
* 🎤 __Configurable Audio Input__: UniVoice is audio input agnostic. It supports mic audio input out of the box and you can change the source of outgoing audio by implementing the `IAudioInput` interrace.
|
|
13
11
|
|
|
14
|
-
* 🔊 __Configurable Audio Output__:
|
|
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.
|
|
15
13
|
|
|
16
|
-
* 🌐 __Configurable Network__:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Manuals and sample projects are not available yet. For the API reference, please visit http://www.vatsalambastha.com/univoice
|
|
20
|
-
|
|
21
|
-
# Usage
|
|
22
|
-
## Creating a chatroom agent
|
|
23
|
-
- To be able to host and join voice chatrooms, you need a `ChatroomAgent` instance.
|
|
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.
|
|
24
17
|
|
|
18
|
+
## Installation
|
|
19
|
+
⚠️ [OpenUPM](https://openupm.com/packages/com.adrenak.univoice/?subPage=versions) may not have up to date releases. Install using NPM registry instead 👇
|
|
20
|
+
|
|
21
|
+
Ensure you have the NPM registry in the `manifest.json` file of your Unity project with the following scopes:
|
|
25
22
|
```
|
|
26
|
-
|
|
23
|
+
"scopedRegistries": [
|
|
24
|
+
{
|
|
25
|
+
"name": "npmjs",
|
|
26
|
+
"url": "https://registry.npmjs.org",
|
|
27
|
+
"scopes": [
|
|
28
|
+
"com.npmjs",
|
|
29
|
+
"com.adrenak.univoice",
|
|
30
|
+
"com.adrenak.brw",
|
|
31
|
+
"com.adrenak.unimic",
|
|
32
|
+
"com.adrenak.concentus-unity"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
27
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).
|
|
28
38
|
|
|
29
|
-
##
|
|
39
|
+
## Docs
|
|
40
|
+
Am API reference is available: http://www.vatsalambastha.com/univoice
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- To get your ID
|
|
34
|
-
`agent.Network.OwnID;`
|
|
42
|
+
## Samples
|
|
43
|
+
This repository contains a sample scene for the Mirror network, which is the best place to see how UniVoice can be integrated into your project.
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
`agent.Network.PeersIDs`
|
|
38
|
-
|
|
39
|
-
`agent.Network` also provides methods to host or join a chatroom. Here is how you use them:
|
|
45
|
+
To try the sample, import Mirror and add the `UNIVOICE_MIRROR_NETWORK` compilation symbol to your project.
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
agent.Network.HostChatroom(optional_data);
|
|
44
|
-
|
|
45
|
-
// Join an existing chatroom using a name
|
|
46
|
-
agent.Network.JoinChatroom(optional_data);
|
|
47
|
+
## Dependencies
|
|
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.
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
agent.Network.LeaveChatroom(optional_data);
|
|
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.
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
agent.Network.CloseChatroom(optional_data);
|
|
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
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
## Muting Audio
|
|
56
|
-
To mute everyone in the chatroom, use `agent.MuteOthers = true;` or set it to `false` to unmute them all.
|
|
57
|
-
|
|
58
|
-
To mute yourself use `agent.MuteSelf = true;` or set it to `false` to unmute yourself. This will stop sending your audio to all the peers in the chatroom.
|
|
59
|
-
|
|
60
|
-
For muting a specific peer, first get the peers settings object using this:
|
|
61
|
-
```
|
|
62
|
-
agent.PeerSettings[id].muteThem = true; // where id belongs to the peer in question
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
If you want to mute yourself towards a specific peer, use this:
|
|
66
|
-
`agent.PeerSettings[id].muteSelf = true; // where id belongs to the peer in question`
|
|
67
|
-
|
|
68
|
-
## Events
|
|
69
|
-
`agent.Network` provides several network related events. Refer to the [API reference](http://www.vatsalambastha.com/univoice/api/Adrenak.UniVoice.ChatroomAgent.html) for them.
|
|
70
|
-
|
|
71
|
-
# License and Support
|
|
54
|
+
## License and Support
|
|
72
55
|
This project is under the [MIT license](https://github.com/adrenak/univoice/blob/master/LICENSE).
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
__Commercial consultation and development can be arranged__ but is subject to schedule and availability.
|
|
57
|
+
Community contributions are welcome.
|
|
77
58
|
|
|
78
|
-
|
|
59
|
+
## Contact
|
|
79
60
|
The developer can be reached at the following links:
|
|
80
61
|
|
|
81
62
|
[Website](http://www.vatsalambastha.com)
|
|
82
63
|
[LinkedIn](https://www.linkedin.com/in/vatsalAmbastha)
|
|
83
64
|
[GitHub](https://www.github.com/adrenak)
|
|
84
65
|
[Twitter](https://www.twitter.com/vatsalAmbastha)
|
|
66
|
+
Discord: `adrenak#1934`
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "Adrenak.UniVoice.Runtime",
|
|
3
|
+
"rootNamespace": "",
|
|
4
|
+
"references": [
|
|
5
|
+
"GUID:f87ecb857e752164ab814a3de8eb0262",
|
|
6
|
+
"GUID:1f776cd02c03a7b4280b6b649d7758e2",
|
|
7
|
+
"GUID:30817c1a0e6d646d99c048fc403f5979",
|
|
8
|
+
"GUID:725ee7191c021de4dbf9269590ded755",
|
|
9
|
+
"GUID:b118fd5a40c85ad4e9b38e8c4a42bbb1"
|
|
10
|
+
],
|
|
11
|
+
"includePlatforms": [],
|
|
12
|
+
"excludePlatforms": [],
|
|
13
|
+
"allowUnsafeCode": false,
|
|
14
|
+
"overrideReferences": false,
|
|
15
|
+
"precompiledReferences": [],
|
|
16
|
+
"autoReferenced": true,
|
|
17
|
+
"defineConstraints": [],
|
|
18
|
+
"versionDefines": [],
|
|
19
|
+
"noEngineReferences": false
|
|
20
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
|
|
4
|
+
using UnityEngine;
|
|
5
|
+
|
|
6
|
+
namespace Adrenak.UniVoice {
|
|
7
|
+
/// <summary>
|
|
8
|
+
/// Handles a client session.
|
|
9
|
+
/// Requires an implementation of <see cref="IAudioClient{T}"/>, <see cref="IAudioInput"/> and <see cref="IAudioOutputFactory"/> each.
|
|
10
|
+
/// Allows adding input and output filters and handles their execution.
|
|
11
|
+
/// </summary>
|
|
12
|
+
/// <typeparam name="T"></typeparam>
|
|
13
|
+
public class ClientSession<T> : IDisposable {
|
|
14
|
+
/// <summary>
|
|
15
|
+
/// The <see cref="IAudioOutput"/> instances of each peer in the session
|
|
16
|
+
/// </summary>
|
|
17
|
+
public Dictionary<T, IAudioOutput> PeerOutputs { get; private set; } = new Dictionary<T, IAudioOutput>();
|
|
18
|
+
|
|
19
|
+
/// <summary>
|
|
20
|
+
/// The input <see cref="IAudioFilter"/> that will be applied to the outgoing audio for all the peers.
|
|
21
|
+
/// Note that filters are executed in the order they are present in this list
|
|
22
|
+
/// </summary>
|
|
23
|
+
public List<IAudioFilter> InputFilters { get; set; } = new List<IAudioFilter>();
|
|
24
|
+
|
|
25
|
+
/// <summary>
|
|
26
|
+
/// The output <see cref="IAudioFilter"/> that will be applied to the incoming audio for all the peers.
|
|
27
|
+
/// Note that filters are executed in the order they are present in this list.
|
|
28
|
+
/// </summary>
|
|
29
|
+
public List<IAudioFilter> OutputFilters { get; set; } = new List<IAudioFilter>();
|
|
30
|
+
|
|
31
|
+
public ClientSession(IAudioClient<T> client, IAudioInput input, IAudioOutputFactory outputFactory) {
|
|
32
|
+
Client = client;
|
|
33
|
+
Input = input;
|
|
34
|
+
OutputFactory = outputFactory;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// <summary>
|
|
38
|
+
/// The <see cref="IAudioClient{T}"/> that's used for networking
|
|
39
|
+
/// </summary>
|
|
40
|
+
IAudioClient<T> client;
|
|
41
|
+
public IAudioClient<T> Client {
|
|
42
|
+
get => client;
|
|
43
|
+
set {
|
|
44
|
+
if(client != null)
|
|
45
|
+
client.Dispose();
|
|
46
|
+
client = value;
|
|
47
|
+
|
|
48
|
+
Client.OnLeft += () => {
|
|
49
|
+
foreach (var output in PeerOutputs)
|
|
50
|
+
output.Value.Dispose();
|
|
51
|
+
PeerOutputs.Clear();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
Client.OnPeerJoined += id => {
|
|
55
|
+
try {
|
|
56
|
+
var output = outputFactory.Create();
|
|
57
|
+
PeerOutputs.Add(id, output);
|
|
58
|
+
}
|
|
59
|
+
catch (Exception e) {
|
|
60
|
+
Debug.LogException(e);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
Client.OnPeerLeft += id => {
|
|
65
|
+
if (!PeerOutputs.ContainsKey(id))
|
|
66
|
+
return;
|
|
67
|
+
|
|
68
|
+
PeerOutputs[id].Dispose();
|
|
69
|
+
PeerOutputs.Remove(id);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
client.OnReceivedPeerAudioFrame += (id, audioFrame) => {
|
|
73
|
+
if (!PeerOutputs.ContainsKey(id))
|
|
74
|
+
return;
|
|
75
|
+
|
|
76
|
+
if (OutputFilters != null) {
|
|
77
|
+
foreach (var filter in OutputFilters)
|
|
78
|
+
audioFrame = filter.Run(audioFrame);
|
|
79
|
+
}
|
|
80
|
+
if(audioFrame.samples.Length > 0)
|
|
81
|
+
PeerOutputs[id].Feed(audioFrame);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
IAudioInput input;
|
|
87
|
+
/// <summary>
|
|
88
|
+
/// The <see cref="IAudioInput"/> that's used for sourcing outgoing audio
|
|
89
|
+
/// </summary>
|
|
90
|
+
public IAudioInput Input {
|
|
91
|
+
get => input;
|
|
92
|
+
set {
|
|
93
|
+
if(input != null)
|
|
94
|
+
input.Dispose();
|
|
95
|
+
input = value;
|
|
96
|
+
input.OnFrameReady += frame => {
|
|
97
|
+
if (InputFilters != null) {
|
|
98
|
+
foreach (var filter in InputFilters)
|
|
99
|
+
frame = filter.Run(frame);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if(frame.samples.Length > 0)
|
|
103
|
+
Client.SendAudioFrame(frame);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
IAudioOutputFactory outputFactory;
|
|
109
|
+
/// <summary>
|
|
110
|
+
/// The <see cref="IAudioOutputFactory"/> that creates the <see cref="IAudioOutput"/> of peers
|
|
111
|
+
/// </summary>
|
|
112
|
+
public IAudioOutputFactory OutputFactory {
|
|
113
|
+
get => outputFactory;
|
|
114
|
+
set {
|
|
115
|
+
outputFactory = value;
|
|
116
|
+
|
|
117
|
+
foreach (var output in PeerOutputs)
|
|
118
|
+
output.Value.Dispose();
|
|
119
|
+
PeerOutputs.Clear();
|
|
120
|
+
|
|
121
|
+
foreach (var id in Client.PeerIDs) {
|
|
122
|
+
try {
|
|
123
|
+
var output = outputFactory.Create();
|
|
124
|
+
PeerOutputs.Add(id, output);
|
|
125
|
+
}
|
|
126
|
+
catch (Exception e) {
|
|
127
|
+
Debug.LogException(e);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public void Dispose() {
|
|
134
|
+
Client.Dispose();
|
|
135
|
+
Input.Dispose();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
using System.IO;
|
|
2
|
+
using System.IO.Compression;
|
|
3
|
+
using System.Net.Sockets;
|
|
4
|
+
using System.Net;
|
|
5
|
+
using System.Runtime.Serialization.Formatters.Binary;
|
|
6
|
+
using System;
|
|
7
|
+
using UnityEngine;
|
|
8
|
+
|
|
9
|
+
namespace Adrenak.UniVoice {
|
|
10
|
+
public class Utils {
|
|
11
|
+
public class Bytes {
|
|
12
|
+
public static byte[] FloatsToBytes(float[] floats) {
|
|
13
|
+
int byteCount = sizeof(float) * floats.Length;
|
|
14
|
+
byte[] byteArray = new byte[byteCount];
|
|
15
|
+
|
|
16
|
+
Buffer.BlockCopy(floats, 0, byteArray, 0, byteCount);
|
|
17
|
+
|
|
18
|
+
return byteArray;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public static float[] BytesToFloats(byte[] bytes) {
|
|
22
|
+
int floatCount = bytes.Length / sizeof(float);
|
|
23
|
+
float[] floatArray = new float[floatCount];
|
|
24
|
+
|
|
25
|
+
Buffer.BlockCopy(bytes, 0, floatArray, 0, bytes.Length);
|
|
26
|
+
|
|
27
|
+
return floatArray;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static class Audio {
|
|
32
|
+
static float[] audioF;
|
|
33
|
+
static float sumOfSquares;
|
|
34
|
+
public static float CalculateRMS(byte[] audio) {
|
|
35
|
+
audioF = Bytes.BytesToFloats(audio);
|
|
36
|
+
|
|
37
|
+
foreach(var x in audioF)
|
|
38
|
+
sumOfSquares += x * x;
|
|
39
|
+
return Mathf.Sqrt(sumOfSquares / audioF.Length);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static class Network {
|
|
44
|
+
public static string LocalIPv4Address {
|
|
45
|
+
get {
|
|
46
|
+
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0)) {
|
|
47
|
+
socket.Connect("8.8.8.8", 65530);
|
|
48
|
+
IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
|
|
49
|
+
return endPoint.Address.ToString();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -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,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
|
+
}
|