final fixes for audio + installed parallelsync

This commit is contained in:
tom.hempel
2025-09-26 16:40:21 +02:00
parent 7d65d1b799
commit 40fd408908
10 changed files with 365 additions and 50 deletions

View File

@ -9,6 +9,7 @@ using UnityEngine;
using UnityEngine.XR;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.XR;
using System.IO;
namespace Convai.Scripts.Runtime.Multiplayer
{
@ -70,6 +71,16 @@ namespace Convai.Scripts.Runtime.Multiplayer
public event Action<bool> OnRecordingStateChanged;
[Header("Recording Storage")]
[SerializeField] private bool saveLocalAudio = true;
[SerializeField] private int localSampleRate = 16000;
[SerializeField] private string localFilePrefix = "sender_audio";
private readonly object _localAudioLock = new object();
private readonly System.Collections.Generic.List<short> _localSamples = new System.Collections.Generic.List<short>(128 * 1024);
private bool _localSaveInProgress = false;
private DateTime _localSessionStart;
private string _persistentDataPath;
private void Start()
{
// Apply global config if enabled
@ -84,6 +95,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
}
InitializeNetwork();
InitializeAudio();
_persistentDataPath = Application.persistentDataPath;
_cancellationTokenSource = new CancellationTokenSource();
_ackCancellationTokenSource = new CancellationTokenSource();
@ -423,6 +435,11 @@ namespace Convai.Scripts.Runtime.Multiplayer
_lastMicrophonePosition = 0;
_packetSequence = 0;
_startAckReceived = false;
_localSessionStart = DateTime.UtcNow;
lock (_localAudioLock)
{
_localSamples.Clear();
}
ConvaiLogger.Info("Started recording for UDP transmission (Simple)", ConvaiLogger.LogCategory.Character);
OnRecordingStateChanged?.Invoke(true);
@ -454,6 +471,11 @@ namespace Convai.Scripts.Runtime.Multiplayer
// Send end-of-recording signal
SendEndOfRecordingSignal();
if (saveLocalAudio)
{
TrySaveLocalAudioAsync();
}
}
catch (Exception ex)
{
@ -542,6 +564,12 @@ namespace Convai.Scripts.Runtime.Multiplayer
// Create a simple packet structure
byte[] packet = CreateSimpleAudioPacket(audioData, processedSamples, currentChunkSamples);
// Buffer locally for saving
if (saveLocalAudio)
{
AppendLocalAudio(audioData, processedSamples, currentChunkSamples);
}
// Send the packet
await _udpClient.SendAsync(packet, packet.Length, _targetEndPoint);
@ -649,6 +677,100 @@ namespace Convai.Scripts.Runtime.Multiplayer
}
}
private void AppendLocalAudio(float[] source, int startIndex, int count)
{
if (source == null || count <= 0)
return;
lock (_localAudioLock)
{
for (int i = 0; i < count; i++)
{
float sample = source[startIndex + i];
short shortSample = (short)(Mathf.Clamp(sample, -1f, 1f) * short.MaxValue);
_localSamples.Add(shortSample);
}
}
}
private void TrySaveLocalAudioAsync()
{
if (_localSaveInProgress)
return;
short[] dataToSave;
DateTime sessionStart;
lock (_localAudioLock)
{
if (_localSamples.Count == 0)
{
if (enableDebugLogging)
ConvaiLogger.Info("No local audio to save.", ConvaiLogger.LogCategory.Character);
return;
}
dataToSave = _localSamples.ToArray();
_localSamples.Clear();
sessionStart = _localSessionStart;
}
_localSaveInProgress = true;
Task.Run(async () =>
{
try
{
// Small delay to allow any final chunks to enqueue
await Task.Delay(100);
string timestamp = sessionStart.ToLocalTime().ToString("yyyyMMdd_HHmmss");
string fileName = $"{localFilePrefix}_{timestamp}.wav";
string dir = _persistentDataPath;
string path = Path.Combine(dir, fileName);
WriteWav(path, dataToSave, localSampleRate, 1);
ConvaiLogger.Info($"Saved local audio to: {path}", ConvaiLogger.LogCategory.Character);
}
catch (Exception ex)
{
ConvaiLogger.Error($"Failed to save local audio: {ex.Message}", ConvaiLogger.LogCategory.Character);
}
finally
{
_localSaveInProgress = false;
}
});
}
private void WriteWav(string path, short[] samples, int sampleRate, int channels)
{
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
using (var writer = new BinaryWriter(fs))
{
int bitsPerSample = 16;
int byteRate = sampleRate * channels * (bitsPerSample / 8);
int blockAlign = channels * (bitsPerSample / 8);
int dataSize = samples.Length * (bitsPerSample / 8);
int fileSize = 44 - 8 + dataSize;
writer.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));
writer.Write(fileSize);
writer.Write(System.Text.Encoding.ASCII.GetBytes("WAVE"));
writer.Write(System.Text.Encoding.ASCII.GetBytes("fmt "));
writer.Write(16);
writer.Write((short)1);
writer.Write((short)channels);
writer.Write(sampleRate);
writer.Write(byteRate);
writer.Write((short)blockAlign);
writer.Write((short)bitsPerSample);
writer.Write(System.Text.Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
for (int i = 0; i < samples.Length; i++)
{
writer.Write(samples[i]);
}
}
}
private async Task SendStartOfRecordingSignalAndAwaitAck()
{
try