Я работаю над пользовательской системой голосового реле в Unity, используя NetCode для GameObjects и голосовой чат диссонанса. Система предназначена для трансляции голоса игрока А через другой аудиозаучитель независимо от расстояния, так что звук на основе близости работает правильно через 3D-аудио, в то же время позволяя слушать в рамках доступа к получению голоса.
The audio has a 1–2 second delay
The voice sounds choppy and robotic
There’s frequent crackling or stuttering
Increasing the frame size above 300+ breaks everything (no sound gets through)
Все, что я хочу, это то, чтобы голос был передан гладко, с минимальной задержкой, и в синхронизации с другими игроками, слышащими его с правильного аудиора.
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}");
}
}
}
< /code>
Я не использую VoiceProximityBroadcastTrigger только VoiceProximityReceiptTrigger. Пользовательский RelaySpeaker, который я создал вместо VoiceProximityBroadcastTrigger, использует настройки AudioSource по умолчанию, с его диапазоном, установленным 500. VoiceProximityReceipttrigger установлен на 3. Таким образом, голос может быть передан из любого места, но только те, кто находится в диапазоне AudioSource, могут услышать его. Единственная проблема заключается в том, что переданный голос искажен.How can I eliminate the delay and crackling?
Is there a better way to synchronize voice data for near real-time relay via AudioSource?
How do I match Dissonance’s own processing quality (VAD, buffering, etc.)?
Я работаю над пользовательской системой голосового реле в Unity, используя NetCode для GameObjects и голосовой чат диссонанса. Система предназначена для трансляции голоса игрока А через другой аудиозаучитель независимо от расстояния, так что звук на основе близости работает правильно через 3D-аудио, в то же время позволяя слушать в рамках доступа к получению голоса.[code]The audio has a 1–2 second delay
The voice sounds choppy and robotic
There’s frequent crackling or stuttering
Increasing the frame size above 300+ breaks everything (no sound gets through) [/code] Все, что я хочу, это то, чтобы голос был передан гладко, с минимальной задержкой, и в синхронизации с другими игроками, слышащими его с правильного аудиора.[code]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;
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; } } [/code] miclaysender: [/b] [code]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;
if (serializer.IsReader) samples = new float[length];
for (int i = 0; i < length; i++) serializer.SerializeValue(ref samples[i]); } } [/code] [b] unitymicrophonecapturewithptt: [/b] [code]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; }
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; }
var playerObj = NetworkManager.Singleton.ConnectedClients[clientId].PlayerObject; var relayNetId = netObj.NetworkObjectId; var playerNetId = playerObj.NetworkObjectId;
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();
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}"); } } } < /code> Я не использую VoiceProximityBroadcastTrigger только VoiceProximityReceiptTrigger. Пользовательский RelaySpeaker, который я создал вместо VoiceProximityBroadcastTrigger, использует настройки AudioSource по умолчанию, с его диапазоном, установленным 500. VoiceProximityReceipttrigger установлен на 3. Таким образом, голос может быть передан из любого места, но только те, кто находится в диапазоне AudioSource, могут услышать его. Единственная проблема заключается в том, что переданный голос искажен.How can I eliminate the delay and crackling?
Is there a better way to synchronize voice data for near real-time relay via AudioSource?
How do I match Dissonance’s own processing quality (VAD, buffering, etc.)? [/code]
Я работаю над пользовательской системой голосовой реле в Unity, используя NetCode для GameObjects и голосовой чат диссонанса. UnityMicrophoneCaptureWithPTT ). Система предназначена для трансляции голоса игрока А через другие аудиоуправления...
В настоящее время я обновляю проект React Native с версии 0.68 до 0.75. Хотя я успешно обновил все остальные пакеты, у меня возникли проблемы с @react-native-voice/voice.
Приложение работает отлично, когда этот пакет удален, но как только я добавьте...
Я пытаюсь записать пользовательский голос и преобразовать его в текст в приложении React-Native, используя пакет Expo , typescript и @React-C-Voice/Voice .
Я импортировал библиотеку в свой проект, но когда я пытаюсь начать начало или остановка , я...
Я внимательно следил за серией Articulated Robotics по ROS и решил использовать его код ros_arduino_bridge для управления моими двигателями через последовательный порт к моему Arduino Mega 2560 из ROS2. В любом случае, мой робот использует...