Unity-WebSocket/Assets/OpusVoiceReceiverWithBuffer.cs

102 lines
3.3 KiB
C#
Raw Normal View History

2025-06-28 07:58:54 +00:00
using UnityEngine;
using WebSocketSharp;
using System.Threading;
using Concentus.Structs;
public class OpusVoiceReceiverWithBuffer : MonoBehaviour
{
private WebSocket ws;
private AudioSource audioSource;
private OpusDecoder decoder;
private const int sampleRate = 16000;
private const int channels = 1;
private const int clipLengthSeconds = 5; // طول کل AudioClip به ثانیه
private AudioClip audioClip;
private float[] audioBuffer;
private int bufferWritePos = 0;
private int bufferReadPos = 0;
private int bufferSize;
private object bufferLock = new object();
void Start()
{
audioSource = gameObject.AddComponent<AudioSource>();
audioSource.loop = true;
decoder = new OpusDecoder(sampleRate, channels);
bufferSize = sampleRate * clipLengthSeconds; // اندازه بافر به تعداد نمونه ها
audioBuffer = new float[bufferSize];
audioClip = AudioClip.Create("VoiceStream", bufferSize, channels, sampleRate, true, OnAudioRead, OnAudioSetPosition);
audioSource.clip = audioClip;
audioSource.Play();
ws = new WebSocket("ws://192.168.31.10:8765");
ws.OnMessage += OnMessageReceived;
ws.Connect();
}
private void OnMessageReceived(object sender, MessageEventArgs e)
{
// دیکد کردن داده اوپوس
short[] pcm = new short[960]; // 20ms frame at 16kHz
int decodedSamples = decoder.Decode(e.RawData, 0, e.RawData.Length, pcm, 0, pcm.Length, false);
float[] floatSamples = new float[decodedSamples];
for (int i = 0; i < decodedSamples; i++)
floatSamples[i] = pcm[i] / (float)short.MaxValue;
// نوشتن در بافر حلقه‌ای امن در برابر چند Thread
lock (bufferLock)
{
for (int i = 0; i < decodedSamples; i++)
{
audioBuffer[bufferWritePos] = floatSamples[i];
bufferWritePos = (bufferWritePos + 1) % bufferSize;
// در صورت پر شدن بافر، خواندن را جلو می‌بریم (داده‌های قدیمی حذف می‌شوند)
if (bufferWritePos == bufferReadPos)
{
bufferReadPos = (bufferReadPos + 1) % bufferSize;
}
}
}
}
// این تابع توسط AudioSource برای خواندن داده‌های صوتی فراخوانی می‌شود
void OnAudioRead(float[] data)
{
lock (bufferLock)
{
for (int i = 0; i < data.Length; i++)
{
if (bufferReadPos != bufferWritePos)
{
data[i] = audioBuffer[bufferReadPos];
bufferReadPos = (bufferReadPos + 1) % bufferSize;
}
else
{
// اگر بافر خالی باشد، سکوت پخش می‌کنیم
data[i] = 0f;
}
}
}
}
void OnAudioSetPosition(int newPosition)
{
// این تابع باید باشد ولی در اینجا کاری انجام نمی‌دهیم
}
private void OnDestroy()
{
ws?.Close();
}
}