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.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(); } }