Код: Выделить всё
UnityMicrophoneCaptureWithPTT
Голос звучит чучело и роботизированно < /li>
Частое потрескивание или заикание < /li>
Увеличение размера кадра выше 300+ разрывает все (нет звука) < /li>
< /ul>
Все, что я хочу, чтобы быть голосом, с голосова В синхронизации с другими игроками, которые слышат его из правильного AudioSource .
Вот текущая настройка:
audiorelayTarget:
Код: Выделить всё
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
[RequireComponent(typeof(AudioSource))]
public class AudioRelayTarget : NetworkBehaviour
{
private Queue buffer = new();
private const int MaxBuffer = 48000;
private AudioSource source;
public ulong OwnerClientId => GetComponent().OwnerClientId;
private void Awake()
{
source = GetComponent();
source.spatialBlend = 1;
source.loop = true;
source.clip = AudioClip.Create("RelayStream", MaxBuffer, 1, 48000, true, OnAudioRead);
source.Play();
}
public override void OnNetworkSpawn()
{
AudioRelayTargetRegistry.Register(OwnerClientId, this);
}
public void ReceiveAudio(float[] data)
{
lock (buffer)
{
foreach (var f in data)
{
buffer.Enqueue(f);
if (buffer.Count > MaxBuffer)
buffer.Dequeue();
}
}
}
private void OnAudioRead(float[] data)
{
lock (buffer)
{
for (int i = 0; i < data.Length; i++)
data[i] = buffer.Count > 0 ? buffer.Dequeue() : 0f;
}
}
}
[b] audioreLayTargetRegistry:
using System.Collections.Generic;
public static class AudioRelayTargetRegistry
{
private static readonly Dictionary _registry = new();
public static void Register(ulong ownerId, AudioRelayTarget target)
{
if (!_registry.ContainsKey(ownerId))
_registry.Add(ownerId, target);
}
public static AudioRelayTarget Get(ulong ownerId)
{
_registry.TryGetValue(ownerId, out var t);
return t;
}
}
Код: Выделить всё
using System;
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
using Dissonance;
using Dissonance.Audio.Capture;
using NAudio.Wave;
public class MicRelaySender : NetworkBehaviour, IMicrophoneSubscriber
{
private WaveFormat _format;
private readonly Queue micQueue = new();
private VoiceRelayManager _relayManager;
[SerializeField] int FrameSize = 240;
public override void OnNetworkSpawn()
{
if (IsOwner || IsServer)
{
var comms = FindObjectOfType();
comms.SubscribeToRecordedAudio(this);
_relayManager = FindObjectOfType();
if (IsOwner)
InvokeRepeating(nameof(SendMicFrame), 0f, 0.02f);
}
}
private void SendMicFrame()
{
if (!IsOwner) return;
float[] audio = null;
lock (micQueue)
{
if (micQueue.Count > 0)
audio = micQueue.Dequeue();
}
if (audio != null)
SendMicDataServerRpc(audio);
}
public void ReceiveMicrophoneData(ArraySegment buffer, WaveFormat format)
{
_format = format;
if (IsOwner && buffer.Count >= FrameSize)
{
float[] shortBuffer = new float[FrameSize];
Array.Copy(buffer.Array, buffer.Offset, shortBuffer, 0, FrameSize);
lock (micQueue)
{
micQueue.Enqueue(shortBuffer);
}
}
}
public void Reset()
{
lock (micQueue)
{
micQueue.Clear();
}
}
private void Update()
{
if (!IsOwner) return;
lock (micQueue)
{
while (micQueue.Count > 0)
{
var audio = micQueue.Dequeue();
SendMicDataServerRpc(audio);
}
}
}
[ServerRpc(Delivery = RpcDelivery.Unreliable)]
private void SendMicDataServerRpc(float[] audioSamples)
{
if (_relayManager == null) return;
ulong relayClientId = _relayManager.GetRelayTargetFor(OwnerClientId);
RelayAudioPacket packet = new RelayAudioPacket
{
relayOwnerClientId = relayClientId,
samples = audioSamples
};
SendToRelayClientRpc(packet);
}
[ClientRpc]
private void SendToRelayClientRpc(RelayAudioPacket packet)
{
var target = AudioRelayTargetRegistry.Get(packet.relayOwnerClientId);
target?.ReceiveAudio(packet.samples);
}
}
Код: Выделить всё
using Unity.Netcode;
public struct RelayAudioPacket : INetworkSerializable
{
public ulong relayOwnerClientId;
public float[] samples;
public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref relayOwnerClientId);
int length = samples == null ? 0 : samples.Length;
serializer.SerializeValue(ref length);
if (serializer.IsReader)
samples = new float[length];
for (int i = 0; i < length; i++)
serializer.SerializeValue(ref samples[i]);
}
}
Код: Выделить всё
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using Dissonance.Audio.Capture;
using NAudio.Wave;
namespace Dissonance.Audio.Capture
{
public class UnityMicrophoneCaptureWithPTT : MonoBehaviour, IMicrophoneCapture
{
public KeyCode pushToTalkKey = KeyCode.V;
public bool IsRecording { get; private set; }
public string Device { get; private set; }
public TimeSpan Latency { get; private set; }
private readonly List _subscribers = new();
private AudioClip _micClip;
private const int SampleRate = 48000;
private const int FrameSize = 9600;
private float[] _frameBuffer = new float[FrameSize];
private int _micPosition = 0;
private float _updateTimer;
public WaveFormat StartCapture(string name)
{
if (Microphone.devices.Length == 0)
{
Debug.LogError("[MicCapture] No microphone devices found!");
return null;
}
if (!string.IsNullOrEmpty(name) && !Microphone.devices.Contains(name))
{
Debug.LogWarning($"[MicCapture] Microphone '{name}' not found. Using default.");
name = null;
}
Device = name ?? Microphone.devices[0];
_micClip = Microphone.Start(Device, true, 1, SampleRate);
if (_micClip == null)
{
Debug.LogError("[MicCapture] Failed to start microphone.");
return null;
}
IsRecording = true;
Latency = TimeSpan.Zero;
_micPosition = 0;
return new WaveFormat(SampleRate, 1);
}
private void Update()
{
if (IsRecording && Input.GetKey(pushToTalkKey))
{
UpdateSubscribers();
}
}
public void StopCapture()
{
if (_micClip != null)
{
Microphone.End(Device);
_micClip = null;
}
IsRecording = false;
}
public void Subscribe(IMicrophoneSubscriber listener)
{
if (!_subscribers.Contains(listener))
_subscribers.Add(listener);
}
public bool Unsubscribe(IMicrophoneSubscriber listener)
{
return _subscribers.Remove(listener);
}
public bool UpdateSubscribers()
{
if (!IsRecording || _micClip == null || !Input.GetKey(pushToTalkKey))
return false;
_updateTimer += Time.unscaledDeltaTime;
while (_updateTimer >= 0.02f)
{
_updateTimer -= 0.02f;
int micPos = Microphone.GetPosition(Device);
int totalSamples = _micClip.samples;
if (micPos < _micPosition)
_micPosition = 0;
if (micPos - _micPosition < FrameSize)
return false;
_micClip.GetData(_frameBuffer, _micPosition);
_micPosition = (_micPosition + FrameSize) % totalSamples;
foreach (var subscriber in _subscribers)
{
subscriber.ReceiveMicrophoneData(new ArraySegment(_frameBuffer), new WaveFormat(SampleRate, 1));
}
}
return false;
}
}
}
< /code>
voicerelaymanager: < /p>
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
public class VoiceRelayManager : NetworkBehaviour
{
[Header("Prefab to spawn for each player")]
public GameObject relaySpeakerPrefab;
private Dictionary _relaySpeakers = new();
private Dictionary _speakerToRelay = new();
public override void OnNetworkSpawn()
{
if (IsServer)
NetworkManager.OnClientConnectedCallback += OnClientConnected;
}
private void OnClientConnected(ulong clientId)
{
SpawnRelaySpeaker(clientId);
}
private void SpawnRelaySpeaker(ulong clientId)
{
GameObject relayObj = Instantiate(relaySpeakerPrefab);
var netObj = relayObj.GetComponent();
netObj.SpawnWithOwnership(clientId);
_relaySpeakers[clientId] = relayObj;
var playerObj = NetworkManager.Singleton.ConnectedClients[clientId].PlayerObject;
var relayNetId = netObj.NetworkObjectId;
var playerNetId = playerObj.NetworkObjectId;
StartCoroutine(AssignFollowTargetWhenReady(relayNetId, playerNetId));
}
public ulong GetRelayTargetFor(ulong speakerClientId)
{
if (_speakerToRelay.TryGetValue(speakerClientId, out var relayClientId))
return relayClientId;
return speakerClientId;
}
public AudioRelayTarget FindRelayTargetInstance(ulong relayOwnerId)
{
if (_relaySpeakers.TryGetValue(relayOwnerId, out var go))
return go.GetComponent();
return null;
}
private System.Collections.IEnumerator AssignFollowTargetWhenReady(ulong relayId, ulong playerId)
{
yield return new WaitForSeconds(0.1f);
if (NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(relayId, out var relay) &&
NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(playerId, out var player))
{
var follow = relay.GetComponent();
if (follow != null)
follow.SetTarget(player.transform);
}
else
{
Debug.LogWarning($"[VoiceRelay] Relay or Player not found: {relayId}, {playerId}");
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/796 ... rent-setup