created centralized UDP Listener
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Convai.Scripts.Runtime.Core;
|
||||
@ -37,7 +36,6 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
public int ListenPort => listenPort;
|
||||
|
||||
// Network components
|
||||
private UdpClient _udpListener;
|
||||
private IPEndPoint _remoteEndPoint;
|
||||
private bool _isListening = false;
|
||||
private int listenPort;
|
||||
@ -47,24 +45,19 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
private bool _isReceivingAudio = false;
|
||||
private int _expectedSequence = 0;
|
||||
private const uint MAGIC_NUMBER = 0xC0A1; // Simple magic number for packet validation
|
||||
private const uint ACK_MAGIC = 0xC0A2; // ACK magic to confirm START control
|
||||
|
||||
// Timing for auto-stop
|
||||
private float _lastPacketTime;
|
||||
private const float AUTO_STOP_DELAY = 1.0f; // Stop listening after 1 second of no packets
|
||||
|
||||
|
||||
// Packet structure (matching ConvaiSimpleUDPAudioSender)
|
||||
// SIMPLIFIED Packet structure (matching ConvaiSimpleUDPAudioSender)
|
||||
private struct AudioPacketData
|
||||
{
|
||||
public uint magicNumber;
|
||||
public int sequence;
|
||||
public int sampleCount;
|
||||
public int microphonePosition;
|
||||
public bool isEndSignal;
|
||||
public bool isStartSignal;
|
||||
public short[] audioSamples;
|
||||
public long timestamp;
|
||||
}
|
||||
|
||||
[Header("Recording Storage")]
|
||||
@ -234,20 +227,22 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
|
||||
try
|
||||
{
|
||||
// Create UDP client with port reuse to allow sharing with UDPPeerDiscovery
|
||||
_udpListener = new UdpClient();
|
||||
_udpListener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
_udpListener.Client.Bind(new IPEndPoint(IPAddress.Any, listenPort));
|
||||
// Wait for shared listener to be ready
|
||||
if (SharedUDPListener.Instance == null)
|
||||
{
|
||||
ConvaiLogger.Error("SharedUDPListener not found! Make sure it's in the scene.", ConvaiLogger.LogCategory.Character);
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to shared listener
|
||||
SharedUDPListener.Instance.OnPacketReceived += HandlePacketReceived;
|
||||
_isListening = true;
|
||||
|
||||
ConvaiLogger.Info($"Simple UDP Audio Receiver V2 listening on port {listenPort} (shared)", ConvaiLogger.LogCategory.Character);
|
||||
|
||||
// Start listening for incoming packets
|
||||
_ = ListenForAudioPackets(_cancellationTokenSource.Token);
|
||||
ConvaiLogger.Info($"✅ Audio Receiver subscribed to shared listener, listening for magic 0x{MAGIC_NUMBER:X}", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConvaiLogger.Error($"Failed to start UDP listener: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||
ConvaiLogger.Error($"❌ FAILED to subscribe Audio Receiver: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||
ConvaiLogger.Error($"Stack trace: {ex.StackTrace}", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
@ -258,9 +253,12 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
return;
|
||||
|
||||
_isListening = false;
|
||||
_udpListener?.Close();
|
||||
_udpListener?.Dispose();
|
||||
_udpListener = null;
|
||||
|
||||
// Unsubscribe from shared listener
|
||||
if (SharedUDPListener.Instance != null)
|
||||
{
|
||||
SharedUDPListener.Instance.OnPacketReceived -= HandlePacketReceived;
|
||||
}
|
||||
|
||||
// Stop any ongoing simulation
|
||||
StopTalkingSimulation();
|
||||
@ -268,26 +266,19 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
ConvaiLogger.Info("Stopped UDP Audio Receiver V2", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
|
||||
private async Task ListenForAudioPackets(CancellationToken cancellationToken)
|
||||
private void HandlePacketReceived(byte[] data, IPEndPoint senderEndPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (_isListening && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var result = await _udpListener.ReceiveAsync();
|
||||
_remoteEndPoint = result.RemoteEndPoint;
|
||||
|
||||
await ProcessReceivedPacket(result.Buffer, result.RemoteEndPoint);
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Normal when stopping
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConvaiLogger.Error($"Error in UDP listener: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
// Check if this is an audio packet (by magic number)
|
||||
if (data.Length < 12) return;
|
||||
|
||||
uint magic = BitConverter.ToUInt32(data, 0);
|
||||
if (magic != MAGIC_NUMBER) return;
|
||||
|
||||
// Update remote endpoint
|
||||
_remoteEndPoint = senderEndPoint;
|
||||
|
||||
// Process audio packet
|
||||
_ = ProcessReceivedPacket(data, senderEndPoint);
|
||||
}
|
||||
|
||||
private Task ProcessReceivedPacket(byte[] data, IPEndPoint sender)
|
||||
@ -305,28 +296,17 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
_totalPacketsReceived++;
|
||||
_lastPacketReceivedTime = DateTime.UtcNow;
|
||||
|
||||
if (enableDebugLogging)
|
||||
{
|
||||
if (packet.isEndSignal)
|
||||
ConvaiLogger.DebugLog($"Received end signal from {sender}", ConvaiLogger.LogCategory.Character);
|
||||
else
|
||||
ConvaiLogger.DebugLog($"Received audio packet {packet.sequence} with {packet.sampleCount} samples", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
// SIMPLIFIED: Check for end signal (sampleCount == -1)
|
||||
bool isEndSignal = (packet.sampleCount == -1);
|
||||
|
||||
// Handle START control: acknowledge and begin simulation
|
||||
if (packet.isStartSignal)
|
||||
if (enableDebugLogging && packet.sequence % 50 == 0)
|
||||
{
|
||||
SendStartAck(sender);
|
||||
if (!_isReceivingAudio)
|
||||
{
|
||||
StartTalkingSimulation();
|
||||
}
|
||||
OnAudioReceiving?.Invoke(true);
|
||||
return Task.CompletedTask;
|
||||
ConvaiLogger.DebugLog($"📥 Received audio packet #{packet.sequence} (magic: 0x{packet.magicNumber:X}) from {sender}, {packet.sampleCount} samples", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
|
||||
if (packet.isEndSignal)
|
||||
if (isEndSignal)
|
||||
{
|
||||
ConvaiLogger.Info($"📥 Received END signal from {sender}", ConvaiLogger.LogCategory.Character);
|
||||
StopTalkingSimulation();
|
||||
OnAudioReceiving?.Invoke(false);
|
||||
}
|
||||
@ -335,6 +315,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
// If this is the first packet, start the talking simulation
|
||||
if (packet.sequence == 0 && !_isReceivingAudio)
|
||||
{
|
||||
ConvaiLogger.Info($"📥 Received FIRST audio packet from {sender}, starting simulation", ConvaiLogger.LogCategory.Character);
|
||||
StartTalkingSimulation();
|
||||
}
|
||||
|
||||
@ -418,7 +399,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
|
||||
private AudioPacketData? ParseSimpleAudioPacket(byte[] data)
|
||||
{
|
||||
if (data.Length < 17) // Minimum header size to match sender
|
||||
if (data.Length < 12) // SIMPLIFIED: Minimum header size (4+4+4)
|
||||
return null;
|
||||
|
||||
try
|
||||
@ -430,27 +411,22 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
offset += 4;
|
||||
|
||||
if (magic != MAGIC_NUMBER)
|
||||
{
|
||||
if (enableDebugLogging)
|
||||
ConvaiLogger.Warn($"❌ Invalid magic number: expected 0x{MAGIC_NUMBER:X}, got 0x{magic:X}", ConvaiLogger.LogCategory.Character);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Read header (matching sender's 17-byte format)
|
||||
// SIMPLIFIED header (12 bytes total)
|
||||
int sequence = BitConverter.ToInt32(data, offset);
|
||||
offset += 4;
|
||||
|
||||
int sampleCount = BitConverter.ToInt32(data, offset);
|
||||
offset += 4;
|
||||
|
||||
int microphonePosition = BitConverter.ToInt32(data, offset);
|
||||
offset += 4;
|
||||
|
||||
byte flags = data[offset];
|
||||
offset += 1;
|
||||
|
||||
bool isEndSignal = (flags == 1);
|
||||
bool isStartSignal = (flags == 2);
|
||||
|
||||
// Read audio data
|
||||
// Read audio data (if not end signal)
|
||||
short[] audioSamples = null;
|
||||
if (!isEndSignal && !isStartSignal && sampleCount > 0)
|
||||
if (sampleCount > 0)
|
||||
{
|
||||
int audioDataSize = sampleCount * sizeof(short);
|
||||
if (data.Length >= offset + audioDataSize)
|
||||
@ -458,6 +434,10 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
audioSamples = new short[sampleCount];
|
||||
Buffer.BlockCopy(data, offset, audioSamples, 0, audioDataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvaiLogger.Warn($"⚠️ Packet too small: expected {offset + audioDataSize} bytes, got {data.Length}", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
|
||||
return new AudioPacketData
|
||||
@ -465,11 +445,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
magicNumber = magic,
|
||||
sequence = sequence,
|
||||
sampleCount = sampleCount,
|
||||
microphonePosition = microphonePosition,
|
||||
isEndSignal = isEndSignal,
|
||||
isStartSignal = isStartSignal,
|
||||
audioSamples = audioSamples,
|
||||
timestamp = 0 // Not provided in sender format
|
||||
audioSamples = audioSamples
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -479,26 +455,6 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
private void SendStartAck(IPEndPoint sender)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_udpListener == null || sender == null)
|
||||
return;
|
||||
|
||||
byte[] ack = new byte[8];
|
||||
BitConverter.GetBytes(ACK_MAGIC).CopyTo(ack, 0);
|
||||
BitConverter.GetBytes(-1).CopyTo(ack, 4);
|
||||
_udpListener.SendAsync(ack, ack.Length, sender);
|
||||
|
||||
if (enableDebugLogging)
|
||||
ConvaiLogger.DebugLog($"Sent START ACK to {sender}", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConvaiLogger.Warn($"Failed to send START ACK: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
|
||||
private void BufferAudioPacket(int sequence, short[] samples)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user