moved to dissonance
parent
61c7469cb2
commit
c701bf4ed5
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: efa29f73ec755d14eac2a560bddce4b0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -10,13 +10,8 @@ public class NetworkGUI : MonoBehaviour
|
|||
public InputField roomInput;
|
||||
public Text messages;
|
||||
public List<string> messageBuffer;
|
||||
public Dropdown microphones;
|
||||
public string microphone;
|
||||
public velmicrophone mic;
|
||||
public void onMicrophoneChanged(string mic)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void handleSend()
|
||||
{
|
||||
if(sendInput.text != "")
|
||||
|
|
@ -35,7 +30,7 @@ public class NetworkGUI : MonoBehaviour
|
|||
{
|
||||
if(roomInput.text != "")
|
||||
{
|
||||
mic.startMicrophone(microphones.options[microphones.value].text);
|
||||
|
||||
networkManager.join(roomInput.text);
|
||||
}
|
||||
|
||||
|
|
@ -44,8 +39,6 @@ public class NetworkGUI : MonoBehaviour
|
|||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
List<string> temp = new List<string>(Microphone.devices);
|
||||
microphones.AddOptions(temp);
|
||||
networkManager.messageReceived += (m) => {
|
||||
string s = m.type + ":" + m.sender +":" + m.text;
|
||||
messageBuffer.Add(s);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using System.Net.Sockets;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
using Dissonance;
|
||||
public class NetworkManager : MonoBehaviour
|
||||
{
|
||||
public string host;
|
||||
|
|
@ -19,8 +19,8 @@ public class NetworkManager : MonoBehaviour
|
|||
public GameObject playerPrefab;
|
||||
public Dictionary<int, NetworkPlayer> players = new Dictionary<int, NetworkPlayer>();
|
||||
|
||||
public velmicrophone mic; //to hand to the player
|
||||
|
||||
public Action<NetworkPlayer> onJoinedRoom = delegate { };
|
||||
public Action<NetworkPlayer> onPlayerJoined = delegate { };
|
||||
#endregion
|
||||
// Use this for initialization
|
||||
public class Message
|
||||
|
|
@ -71,12 +71,13 @@ public class NetworkManager : MonoBehaviour
|
|||
if (m.text != "")
|
||||
{
|
||||
NetworkPlayer player = GameObject.Instantiate<GameObject>(playerPrefab).GetComponent<NetworkPlayer>();
|
||||
player.attachMic(mic); //gets a microphone to send voice
|
||||
|
||||
player.isLocal = true;
|
||||
player.userid = m.sender;
|
||||
players.Add(userid, player);
|
||||
player.room = m.text;
|
||||
player.manager = this;
|
||||
onJoinedRoom(player);
|
||||
}
|
||||
}
|
||||
else //not for me, a player is joining or leaving
|
||||
|
|
@ -98,6 +99,7 @@ public class NetworkManager : MonoBehaviour
|
|||
player.userid = m.sender;
|
||||
player.manager = this;
|
||||
players.Add(m.sender, player);
|
||||
onPlayerJoined(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ public class NetworkPlayer : MonoBehaviour
|
|||
{
|
||||
public int userid;
|
||||
public string username;
|
||||
public string dissonanceID;
|
||||
public string room;
|
||||
public NetworkManager manager;
|
||||
Vector3 networkPosition;
|
||||
|
|
@ -22,11 +23,14 @@ public class NetworkPlayer : MonoBehaviour
|
|||
int lastPlayTime = 0;
|
||||
int totalbuffered = 0;
|
||||
int totalPlayed = 0;
|
||||
uint lastAudioId = 0;
|
||||
// Start is called before the first frame update
|
||||
public Dissonance.VelCommsNetwork commsNetwork;
|
||||
bool isSpeaking = false;
|
||||
void Start()
|
||||
{
|
||||
source = GetComponent<AudioSource>();
|
||||
speechClip = AudioClip.Create("speechclip", 160000, 1, 16100, false);
|
||||
speechClip = AudioClip.Create("speechclip", 160000, 1, 16000, false);
|
||||
source.clip = speechClip;
|
||||
source.Stop();
|
||||
source.loop = true;
|
||||
|
|
@ -37,16 +41,13 @@ public class NetworkPlayer : MonoBehaviour
|
|||
}
|
||||
}
|
||||
|
||||
public void attachMic(velmicrophone mic)
|
||||
{
|
||||
this.mic = mic;
|
||||
this.mic.encodedFrameAvailable += this.sendAudioData;
|
||||
}
|
||||
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
|
||||
|
||||
if (userid != manager.userid) //not me, I move to wherever I should
|
||||
{
|
||||
transform.position = Vector3.Lerp(transform.position, networkPosition, .1f);
|
||||
|
|
@ -57,24 +58,16 @@ public class NetworkPlayer : MonoBehaviour
|
|||
float v = Input.GetAxis("Vertical");
|
||||
Vector3 delta = h * Vector3.right + v * Vector3.up;
|
||||
transform.position = transform.position + delta * Time.deltaTime;
|
||||
}
|
||||
|
||||
//we also need to deal with the audio source
|
||||
|
||||
int numSamplesPlayedSinceLast = source.timeSamples - lastPlayTime;
|
||||
|
||||
if(numSamplesPlayedSinceLast < 0) //looped
|
||||
//if we're not speaking, and the comms say we are, send a speaking event, which will be received on other network players and sent to their comms accordingly
|
||||
if(commsNetwork.comms.FindPlayer(dissonanceID).IsSpeaking != isSpeaking)
|
||||
{
|
||||
numSamplesPlayedSinceLast = source.clip.samples - lastPlayTime + source.timeSamples;
|
||||
}
|
||||
totalPlayed += numSamplesPlayedSinceLast;
|
||||
if(totalPlayed >= totalbuffered)
|
||||
{
|
||||
int left = numSamplesPlayedSinceLast - totalbuffered;
|
||||
source.Pause();
|
||||
isSpeaking = !isSpeaking;
|
||||
manager.sendTo(1, "4," + (isSpeaking?1:0) + ";");
|
||||
|
||||
}
|
||||
lastPlayTime = source.timeSamples;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -112,8 +105,32 @@ public class NetworkPlayer : MonoBehaviour
|
|||
{
|
||||
|
||||
byte[] data = Convert.FromBase64String(sections[1]);
|
||||
uint sequenceNumber = uint.Parse(sections[2]);
|
||||
commsNetwork.voiceReceived(dissonanceID,data,sequenceNumber);
|
||||
|
||||
this.receiveAudioData(mic.decodeOpusData(data));
|
||||
break;
|
||||
}
|
||||
case "3": //dissonance id
|
||||
{
|
||||
if (dissonanceID == "")
|
||||
{
|
||||
dissonanceID = sections[1];
|
||||
//tell the comms network that this player joined the channel
|
||||
commsNetwork.playerJoined(dissonanceID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "4": //speaking state
|
||||
{
|
||||
if(sections[1] == "0")
|
||||
{
|
||||
commsNetwork.playedStoppedSpeaking(dissonanceID);
|
||||
lastAudioId = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
commsNetwork.playerStartedSpeaking(dissonanceID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -121,20 +138,8 @@ public class NetworkPlayer : MonoBehaviour
|
|||
|
||||
}
|
||||
|
||||
public void receiveAudioData(float[] data)
|
||||
{
|
||||
|
||||
speechClip.SetData(data, lastSample);
|
||||
lastSample += data.Length;
|
||||
lastSample %= speechClip.samples;
|
||||
totalbuffered += data.Length;
|
||||
if (!source.isPlaying && !buffering)
|
||||
{
|
||||
StartCoroutine(startSoundIn(2));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
IEnumerator startSoundIn(int frames)
|
||||
{
|
||||
buffering = true;
|
||||
|
|
@ -145,9 +150,15 @@ public class NetworkPlayer : MonoBehaviour
|
|||
source.Play();
|
||||
buffering = false;
|
||||
}
|
||||
public void sendAudioData(FixedArray a)
|
||||
public void sendAudioData(ArraySegment<byte> data)
|
||||
{
|
||||
string b64_data = Convert.ToBase64String(a.array,0,a.count);
|
||||
manager.sendTo(1, "2,"+b64_data + ";");
|
||||
string b64_data = Convert.ToBase64String(data.Array,data.Offset,data.Count);
|
||||
manager.sendTo(1, "2,"+b64_data + ","+lastAudioId +";");
|
||||
}
|
||||
|
||||
public void setDissonanceID(string id)
|
||||
{
|
||||
dissonanceID = id;
|
||||
manager.sendTo(1, "3," + id+";");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!244 &-1719467165466355418
|
||||
AudioMixerEffectController:
|
||||
m_ObjectHideFlags: 3
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_EffectID: 6362d3ceb713f4c3fa42448a3e29217e
|
||||
m_EffectName: Dissonance Echo Cancellation
|
||||
m_MixLevel: a240a2d1f057e4cf9bbc59f6cfbec367
|
||||
m_Parameters: []
|
||||
m_SendTarget: {fileID: 0}
|
||||
m_EnableWetMix: 0
|
||||
m_Bypass: 0
|
||||
--- !u!241 &24100000
|
||||
AudioMixerController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: NewAudioMixer
|
||||
m_OutputGroup: {fileID: 0}
|
||||
m_MasterGroup: {fileID: 24300002}
|
||||
m_Snapshots:
|
||||
- {fileID: 24500006}
|
||||
m_StartSnapshot: {fileID: 24500006}
|
||||
m_SuspendThreshold: -80
|
||||
m_EnableSuspend: 1
|
||||
m_UpdateMode: 1
|
||||
m_ExposedParameters: []
|
||||
m_AudioMixerGroupViews:
|
||||
- guids:
|
||||
- b8e40716c8c1442ab9ef14e149b2423c
|
||||
name: View
|
||||
m_CurrentViewIndex: 0
|
||||
m_TargetSnapshot: {fileID: 24500006}
|
||||
--- !u!243 &24300002
|
||||
AudioMixerGroupController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Master
|
||||
m_AudioMixer: {fileID: 24100000}
|
||||
m_GroupID: b8e40716c8c1442ab9ef14e149b2423c
|
||||
m_Children: []
|
||||
m_Volume: 3d9590ea0314643bd94e1616b88508ef
|
||||
m_Pitch: 31f1ddadf3a5d47caab0e64b81ced2ae
|
||||
m_Send: 00000000000000000000000000000000
|
||||
m_Effects:
|
||||
- {fileID: 24400004}
|
||||
- {fileID: -1719467165466355418}
|
||||
m_UserColorIndex: 0
|
||||
m_Mute: 0
|
||||
m_Solo: 0
|
||||
m_BypassEffects: 0
|
||||
--- !u!244 &24400004
|
||||
AudioMixerEffectController:
|
||||
m_ObjectHideFlags: 3
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_EffectID: f79719d1f394d4b4783ede937d403602
|
||||
m_EffectName: Attenuation
|
||||
m_MixLevel: 8d97d9fd1e8d446eb9244b5dead78e90
|
||||
m_Parameters: []
|
||||
m_SendTarget: {fileID: 0}
|
||||
m_EnableWetMix: 0
|
||||
m_Bypass: 0
|
||||
--- !u!245 &24500006
|
||||
AudioMixerSnapshotController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Snapshot
|
||||
m_AudioMixer: {fileID: 24100000}
|
||||
m_SnapshotID: 86ea5b9fedb76448594a5ab2119e34a1
|
||||
m_FloatValues: {}
|
||||
m_TransitionOverrides: {}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fa1da19d935e241119cdd522ceae772c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 24100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -6,11 +6,15 @@ using System;
|
|||
using System.IO;
|
||||
public class OutputCapture : MonoBehaviour
|
||||
{
|
||||
|
||||
float[] buffer = new float[100000];
|
||||
int curBufferPos = 0;
|
||||
public velmicrophone mic;
|
||||
public float[] buffer = new float[100000]; //represents the last 100000 samples (roughly)
|
||||
public int curBufferPos = 0;
|
||||
public double outputTime; //time of curBufferPos
|
||||
int sampleRate;
|
||||
bool isPlaying = false;
|
||||
public long sampleNumber = 0;
|
||||
short[] lastPlayed = new short[512];
|
||||
byte[] lastPlayedBytes = new byte[1024];
|
||||
// Start is called before the first frame update
|
||||
void Awake()
|
||||
{
|
||||
|
|
@ -30,6 +34,22 @@ public class OutputCapture : MonoBehaviour
|
|||
|
||||
private void OnAudioFilterRead(float[] data, int channels)
|
||||
{
|
||||
|
||||
for(int i = 0; i < data.Length; i+=2)
|
||||
{
|
||||
lastPlayed[i / 2] = (short)(((data[i] + data[i + 1])/2.0f)*short.MaxValue);
|
||||
}
|
||||
|
||||
if(mic.filter != null)
|
||||
{
|
||||
Buffer.BlockCopy(lastPlayed, 0, lastPlayedBytes, 0, 1024);
|
||||
lock (mic.filter)
|
||||
{
|
||||
mic.filter.RegisterFramePlayed(lastPlayedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!isPlaying)
|
||||
{
|
||||
return;
|
||||
|
|
@ -48,9 +68,20 @@ public class OutputCapture : MonoBehaviour
|
|||
System.Array.Copy(data, buffer.Length - curBufferPos, buffer, 0, data.Length - numLeft);
|
||||
curBufferPos = numLeft;
|
||||
}
|
||||
|
||||
outputTime = AudioSettings.dspTime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private float getSampleAtTime(double t)
|
||||
{
|
||||
lock (buffer)
|
||||
{
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -0,0 +1,33 @@
|
|||
fileFormatVersion: 2
|
||||
guid: da4654b5ef2894d40a4b26c9d23c0a0b
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8adda10125f861c4ab834bb9bd2056b5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -14,7 +14,6 @@ GameObject:
|
|||
- component: {fileID: 5845716565458182149}
|
||||
- component: {fileID: 5713671764962751473}
|
||||
- component: {fileID: 419667223}
|
||||
- component: {fileID: 8439520723839566278}
|
||||
m_Layer: 0
|
||||
m_Name: PlayerPrefab
|
||||
m_TagString: Untagged
|
||||
|
|
@ -211,15 +210,3 @@ AudioSource:
|
|||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
--- !u!114 &8439520723839566278
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6139051692386484099}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ba77e7f5139014c4b8c9eab0e405d0f0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,122 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Dissonance;
|
||||
using Dissonance.Extensions;
|
||||
using Dissonance.Networking;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dissonance
|
||||
{
|
||||
public class VelCommsNetwork : MonoBehaviour, ICommsNetwork
|
||||
{
|
||||
public ConnectionStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
return manager.connected?ConnectionStatus.Connected:ConnectionStatus.Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
public NetworkMode Mode
|
||||
{
|
||||
get
|
||||
{
|
||||
return NetworkMode.Client;
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<NetworkMode> ModeChanged;
|
||||
public event Action<string, CodecSettings> PlayerJoined;
|
||||
public event Action<string> PlayerLeft;
|
||||
public event Action<VoicePacket> VoicePacketReceived;
|
||||
public event Action<TextMessage> TextPacketReceived;
|
||||
public event Action<string> PlayerStartedSpeaking;
|
||||
public event Action<string> PlayerStoppedSpeaking;
|
||||
public event Action<RoomEvent> PlayerEnteredRoom;
|
||||
public event Action<RoomEvent> PlayerExitedRoom;
|
||||
|
||||
ConnectionStatus _status = ConnectionStatus.Disconnected;
|
||||
CodecSettings initSettings;
|
||||
public string dissonanceId;
|
||||
List<VoicePacket> packets = new List<VoicePacket>();
|
||||
bool loopBackSound = false;
|
||||
public DissonanceComms comms;
|
||||
public NetworkManager manager;
|
||||
NetworkPlayer myPlayer;
|
||||
public void Initialize(string playerName, Rooms rooms, PlayerChannels playerChannels, RoomChannels roomChannels, CodecSettings codecSettings)
|
||||
{
|
||||
dissonanceId = playerName;
|
||||
initSettings = codecSettings;
|
||||
Debug.Log("Initializing dissonance");
|
||||
|
||||
manager.onJoinedRoom += (player) =>
|
||||
{
|
||||
myPlayer = player;
|
||||
myPlayer.commsNetwork = this;
|
||||
myPlayer.setDissonanceID(playerName); //need to let that new player know my dissonance id (tell everyone again)
|
||||
|
||||
};
|
||||
|
||||
manager.onPlayerJoined += (player) =>
|
||||
{
|
||||
myPlayer.setDissonanceID(playerName); //need to let that new player know my dissonance id (tell everyone again)
|
||||
|
||||
player.commsNetwork = this; //this will tell us when various things happen of importance
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public void voiceReceived(string sender,byte[] data,uint sequenceNumber)
|
||||
{
|
||||
VoicePacket vp = new VoicePacket(sender, ChannelPriority.Default, 1, true, new ArraySegment<byte>(data), sequenceNumber);
|
||||
VoicePacketReceived(vp);
|
||||
}
|
||||
|
||||
public void SendText(string data, ChannelType recipientType, string recipientId)
|
||||
{
|
||||
Debug.Log("sending text");
|
||||
}
|
||||
|
||||
public void SendVoice(ArraySegment<byte> data)
|
||||
{
|
||||
myPlayer?.sendAudioData(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
_status = ConnectionStatus.Connected;
|
||||
comms = GetComponent<DissonanceComms>();
|
||||
|
||||
}
|
||||
|
||||
public void playerJoined(string id)
|
||||
{
|
||||
PlayerJoined(id, initSettings);
|
||||
RoomEvent re = new RoomEvent();
|
||||
re.Joined = true;
|
||||
re.Room = "Global";
|
||||
re.PlayerName = id;
|
||||
PlayerEnteredRoom(re);
|
||||
}
|
||||
|
||||
public void playerStartedSpeaking(string id)
|
||||
{
|
||||
PlayerStartedSpeaking(id);
|
||||
}
|
||||
public void playedStoppedSpeaking(string id)
|
||||
{
|
||||
PlayerStoppedSpeaking(id);
|
||||
}
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d2009d8e264649749c0315d48765749
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e4e43899246c941c78acfc59ce2f664a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -5,26 +5,27 @@ using System.IO;
|
|||
using Concentus.Structs;
|
||||
using System.Threading;
|
||||
using System;
|
||||
using AudioProcessingModuleCs.Media.Dsp.WebRtc;
|
||||
using AudioProcessingModuleCs.Media;
|
||||
public class velmicrophone : MonoBehaviour
|
||||
{
|
||||
OpusEncoder opusEncoder;
|
||||
OpusDecoder opusDecoder;
|
||||
public OutputCapture oc = null;
|
||||
//StreamWriter sw;
|
||||
AudioClip clip;
|
||||
float[] tempData;
|
||||
float[] encoderBuffer;
|
||||
List<float[]> frameBuffer;
|
||||
short[] encoderBuffer;
|
||||
List<short[]> frameBuffer;
|
||||
|
||||
List<FixedArray> sendQueue = new List<FixedArray>();
|
||||
List<float[]> encoderArrayPool = new List<float[]>();
|
||||
List<short[]> encoderArrayPool = new List<short[]>();
|
||||
List<FixedArray> decoderArrayPool = new List<FixedArray>();
|
||||
int lastUsedEncoderPool = 0;
|
||||
int lastUsedDecoderPool = 0;
|
||||
int encoderBufferIndex=0;
|
||||
int size = 0;
|
||||
int lastPosition = 0;
|
||||
string device = "";
|
||||
int sampleNumber = 0;
|
||||
int encoder_frame_size = 640;
|
||||
double micSampleTime = 1 / 44100.0;
|
||||
double encodeTime = 1 / 16000.0;
|
||||
|
|
@ -37,14 +38,18 @@ public class velmicrophone : MonoBehaviour
|
|||
double averageVolume = 0;
|
||||
Thread t;
|
||||
public Action<FixedArray> encodedFrameAvailable = delegate { };
|
||||
|
||||
double micTime = 0;
|
||||
long sampleNumber = 0;
|
||||
public int offset = 512;
|
||||
public double gain = 2.0f;
|
||||
public WebRtcFilter filter = null;
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
opusEncoder = new OpusEncoder(16000, 1, Concentus.Enums.OpusApplication.OPUS_APPLICATION_VOIP);
|
||||
opusDecoder = new OpusDecoder(16000, 1);
|
||||
encoderBuffer = new float[16000];
|
||||
frameBuffer = new List<float[]>();
|
||||
encoderBuffer = new short[16000];
|
||||
frameBuffer = new List<short[]>();
|
||||
//string path = Application.persistentDataPath + "/" + "mic.csv"; //this was for writing mic samples
|
||||
//sw = new StreamWriter(path, false);
|
||||
|
||||
|
|
@ -53,7 +58,7 @@ public class velmicrophone : MonoBehaviour
|
|||
|
||||
for(int i = 0; i < 100; i++) //pre allocate a bunch of arrays for microphone frames (probably will only need 1 or 2)
|
||||
{
|
||||
encoderArrayPool.Add(new float[encoder_frame_size]);
|
||||
encoderArrayPool.Add(new short[encoder_frame_size]);
|
||||
decoderArrayPool.Add(new FixedArray(1000));
|
||||
|
||||
}
|
||||
|
|
@ -76,6 +81,9 @@ public class velmicrophone : MonoBehaviour
|
|||
Debug.Log("Frequency:" + clip.frequency);
|
||||
tempData = new float[clip.samples * clip.channels];
|
||||
Debug.Log("channels: " + clip.channels);
|
||||
|
||||
filter = new WebRtcFilter(256, 640, new AudioFormat(16000), new AudioFormat(48000), false, false, false);
|
||||
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
|
|
@ -87,7 +95,7 @@ public class velmicrophone : MonoBehaviour
|
|||
|
||||
}
|
||||
|
||||
float[] getNextEncoderPool()
|
||||
short[] getNextEncoderPool()
|
||||
{
|
||||
lastUsedEncoderPool = (lastUsedEncoderPool + 1) % encoderArrayPool.Count;
|
||||
return encoderArrayPool[lastUsedEncoderPool];
|
||||
|
|
@ -104,14 +112,15 @@ public class velmicrophone : MonoBehaviour
|
|||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
if (clip != null)
|
||||
{
|
||||
|
||||
int micPosition = Microphone.GetPosition(device);
|
||||
if(micPosition == lastPosition)
|
||||
{
|
||||
return; //sometimes the microphone will not advance
|
||||
}
|
||||
|
||||
int numSamples = 0;
|
||||
float[] temp;
|
||||
if (micPosition > lastPosition)
|
||||
|
|
@ -123,33 +132,50 @@ public class velmicrophone : MonoBehaviour
|
|||
//whatever was left
|
||||
numSamples = (tempData.Length - lastPosition) + micPosition;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Debug.Log(micPosition);
|
||||
temp = new float[numSamples]; //this has to be dynamically allocated because of the way clip.GetData works (annoying...maybe use native mic)
|
||||
|
||||
clip.GetData(temp, lastPosition);
|
||||
lastPosition = micPosition;
|
||||
|
||||
|
||||
|
||||
//this code does 2 things. 1) it samples the microphone data to be exactly what the encoder wants, 2) it forms encoder packets
|
||||
|
||||
|
||||
|
||||
for (int i = 0; i < temp.Length; i++) //iterate through temp, which contans that mic samples at 44.1khz
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sampleTimer += micSampleTime;
|
||||
if(sampleTimer > encodeTime)
|
||||
if(sampleTimer >= encodeTime)
|
||||
{
|
||||
|
||||
//take a sample between the last sample and the current sample
|
||||
|
||||
|
||||
double diff = sampleTimer - encodeTime; //this represents how far past this sample actually is
|
||||
double t = diff / micSampleTime; //this should be between 0 and 1
|
||||
double v = lastMicSample * (1 - t) + temp[i] * t;
|
||||
|
||||
|
||||
sampleTimer -= encodeTime;
|
||||
|
||||
encoderBuffer[encoderBufferIndex++] = (float)v;
|
||||
encoderBuffer[encoderBufferIndex++] = (short)(v*short.MaxValue);
|
||||
averageVolume += v > 0 ? v : -v;
|
||||
if(encoderBufferIndex > encoder_frame_size) //this is when a new packet gets created
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
averageVolume = averageVolume / encoder_frame_size;
|
||||
|
||||
if(averageVolume < silenceThreshold)
|
||||
|
|
@ -165,19 +191,53 @@ public class velmicrophone : MonoBehaviour
|
|||
if (numSilent < minSilencePacketsToStop)
|
||||
{
|
||||
|
||||
float[] frame = getNextEncoderPool(); //these are predefined sizes, so we don't have to allocate a new array
|
||||
short[] frame = getNextEncoderPool(); //these are predefined sizes, so we don't have to allocate a new array
|
||||
//lock the frame buffer
|
||||
|
||||
System.Array.Copy(encoderBuffer, frame, encoder_frame_size); //nice and fast
|
||||
|
||||
|
||||
|
||||
byte[] recorded = new byte[frame.Length * 2];
|
||||
Buffer.BlockCopy(frame, 0, recorded, 0, frame.Length * 2);
|
||||
|
||||
lock (filter)
|
||||
{
|
||||
filter.Write(recorded);
|
||||
}
|
||||
|
||||
/*
|
||||
bool moreFrames;
|
||||
|
||||
do
|
||||
{
|
||||
short[] cancelBuffer = new short[encoder_frame_size];
|
||||
lock (filter)
|
||||
{
|
||||
if (filter.Read(cancelBuffer, out moreFrames))
|
||||
{
|
||||
lock (frameBuffer)
|
||||
{
|
||||
|
||||
frameBuffer.Add(cancelBuffer);
|
||||
waiter.Set(); //signal the encode frame
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (moreFrames);
|
||||
*/
|
||||
|
||||
lock (frameBuffer)
|
||||
{
|
||||
|
||||
frameBuffer.Add(frame);
|
||||
waiter.Set(); //signal the encode frame
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
encoderBufferIndex = 0;
|
||||
|
||||
}
|
||||
|
|
@ -198,9 +258,9 @@ public class velmicrophone : MonoBehaviour
|
|||
|
||||
}
|
||||
|
||||
public float[] decodeOpusData(byte[] data)
|
||||
public short[] decodeOpusData(byte[] data)
|
||||
{
|
||||
float[] t = getNextEncoderPool();
|
||||
short[] t = getNextEncoderPool();
|
||||
opusDecoder.Decode(data, 0, data.Length, t, 0, encoder_frame_size);
|
||||
return t;
|
||||
}
|
||||
|
|
@ -211,18 +271,18 @@ public class velmicrophone : MonoBehaviour
|
|||
while (waiter.WaitOne(Timeout.Infinite)) //better to wait on signal
|
||||
{
|
||||
|
||||
List<float[]> toEncode = new List<float[]>();
|
||||
List<short[]> toEncode = new List<short[]>();
|
||||
|
||||
|
||||
lock (frameBuffer)
|
||||
{
|
||||
foreach (float[] frame in frameBuffer) {
|
||||
foreach (short[] frame in frameBuffer) {
|
||||
toEncode.Add(frame);
|
||||
}
|
||||
frameBuffer.Clear();
|
||||
}
|
||||
|
||||
foreach(float[] frame in toEncode)
|
||||
foreach(short[] frame in toEncode)
|
||||
{
|
||||
FixedArray a = getNextDecoderPool();
|
||||
int out_data_size = opusEncoder.Encode(frame, 0, encoder_frame_size, a.array, 0, 1000);
|
||||
|
|
|
|||
|
|
@ -580,7 +580,22 @@ PlayerSettings:
|
|||
webGLLinkerTarget: 1
|
||||
webGLThreadsSupport: 0
|
||||
webGLDecompressionFallback: 0
|
||||
scriptingDefineSymbols: {}
|
||||
scriptingDefineSymbols:
|
||||
1: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
4: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
7: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
13: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
14: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
19: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
21: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
25: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
27: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
28: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
29: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
30: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
31: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
32: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
33: PHOTON_UNITY_NETWORKING;PUN_2_0_OR_NEWER;PUN_2_OR_NEWER;PUN_2_19_OR_NEWER
|
||||
additionalCompilerArguments: {}
|
||||
platformArchitecture: {}
|
||||
scriptingBackend: {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
{
|
||||
"templatePinStates": [],
|
||||
"dependencyTypeInfos": [
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.AnimationClip",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEditor.Animations.AnimatorController",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.AnimatorOverrideController",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEditor.Audio.AudioMixerController",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.ComputeShader",
|
||||
"ignore": true,
|
||||
"defaultInstantiationMode": 1,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Cubemap",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.GameObject",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEditor.LightingDataAsset",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": false
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.LightingSettings",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Material",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEditor.MonoScript",
|
||||
"ignore": true,
|
||||
"defaultInstantiationMode": 1,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.PhysicMaterial",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.PhysicsMaterial2D",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Rendering.PostProcessing.PostProcessResources",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Rendering.VolumeProfile",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEditor.SceneAsset",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": false
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Shader",
|
||||
"ignore": true,
|
||||
"defaultInstantiationMode": 1,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.ShaderVariantCollection",
|
||||
"ignore": true,
|
||||
"defaultInstantiationMode": 1,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Texture",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Texture2D",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
},
|
||||
{
|
||||
"userAdded": false,
|
||||
"type": "UnityEngine.Timeline.TimelineAsset",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 0,
|
||||
"supportsModification": true
|
||||
}
|
||||
],
|
||||
"defaultDependencyTypeInfo": {
|
||||
"userAdded": false,
|
||||
"type": "<default_scene_template_dependencies>",
|
||||
"ignore": false,
|
||||
"defaultInstantiationMode": 1,
|
||||
"supportsModification": true
|
||||
},
|
||||
"newSceneOverride": 0
|
||||
}
|
||||
Loading…
Reference in New Issue