mirror of
https://github.com/Dadechin/Unity-WebSocket.git
synced 2025-07-17 10:44:33 +00:00
148 lines
4.5 KiB
C#
148 lines
4.5 KiB
C#
|
using UnityEngine;
|
|||
|
using Concentus.Structs;
|
|||
|
using Concentus.Enums;
|
|||
|
|
|||
|
public class OpusMicRecorder : MonoBehaviour
|
|||
|
{
|
|||
|
[Header("Audio Settings")]
|
|||
|
public int sampleRate = 16000;
|
|||
|
public int frameSizeMs = 20; // 20 میلیثانیه
|
|||
|
|
|||
|
[Header("Bandpass Filter Frequencies (Hz)")]
|
|||
|
[Range(50f, 1000f)]
|
|||
|
public float lowFreq = 300f;
|
|||
|
|
|||
|
[Range(1000f, 8000f)]
|
|||
|
public float highFreq = 3400f;
|
|||
|
|
|||
|
private AudioClip micClip;
|
|||
|
private int frameSizeSamples;
|
|||
|
private int lastSamplePos = 0;
|
|||
|
|
|||
|
private OpusEncoder encoder;
|
|||
|
private BandpassFilter voiceFilter;
|
|||
|
|
|||
|
public delegate void EncodedAudioReady(byte[] data);
|
|||
|
public event EncodedAudioReady OnEncodedAudio;
|
|||
|
|
|||
|
void Start()
|
|||
|
{
|
|||
|
frameSizeSamples = (sampleRate / 1000) * frameSizeMs;
|
|||
|
|
|||
|
encoder = new OpusEncoder(sampleRate, 1, OpusApplication.OPUS_APPLICATION_VOIP);
|
|||
|
encoder.Bitrate = 16000;
|
|||
|
|
|||
|
micClip = Microphone.Start(null, true, 1, sampleRate);
|
|||
|
|
|||
|
// اعتبارسنجی ورودی فرکانس ها
|
|||
|
if (lowFreq >= highFreq)
|
|||
|
{
|
|||
|
Debug.LogWarning("lowFreq باید کمتر از highFreq باشد. تنظیم به مقادیر پیشفرض.");
|
|||
|
lowFreq = 300f;
|
|||
|
highFreq = 3400f;
|
|||
|
}
|
|||
|
|
|||
|
voiceFilter = new BandpassFilter(sampleRate, lowFreq, highFreq);
|
|||
|
}
|
|||
|
|
|||
|
void Update()
|
|||
|
{
|
|||
|
int micPos = Microphone.GetPosition(null);
|
|||
|
int available = micPos - lastSamplePos;
|
|||
|
if (available < 0) available += micClip.samples;
|
|||
|
if (available < frameSizeSamples) return;
|
|||
|
|
|||
|
float[] samples = new float[frameSizeSamples];
|
|||
|
micClip.GetData(samples, lastSamplePos);
|
|||
|
lastSamplePos = (lastSamplePos + frameSizeSamples) % micClip.samples;
|
|||
|
|
|||
|
// آپدیت فیلتر اگر کاربر در Inspector مقادیر را تغییر داد
|
|||
|
if (voiceFilter.LowFreq != lowFreq || voiceFilter.HighFreq != highFreq)
|
|||
|
{
|
|||
|
voiceFilter.SetFrequencies(lowFreq, highFreq);
|
|||
|
}
|
|||
|
|
|||
|
voiceFilter.Process(samples);
|
|||
|
|
|||
|
short[] pcm = new short[frameSizeSamples];
|
|||
|
for (int i = 0; i < frameSizeSamples; i++)
|
|||
|
pcm[i] = (short)Mathf.Clamp(samples[i] * short.MaxValue, short.MinValue, short.MaxValue);
|
|||
|
|
|||
|
byte[] encoded = new byte[1275];
|
|||
|
int encodedLength = encoder.Encode(pcm, 0, frameSizeSamples, encoded, 0, encoded.Length);
|
|||
|
|
|||
|
byte[] packet = new byte[encodedLength];
|
|||
|
System.Buffer.BlockCopy(encoded, 0, packet, 0, encodedLength);
|
|||
|
OnEncodedAudio?.Invoke(packet);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// کلاس فیلتر میانگذر (Bandpass) با قابلیت تنظیم داینامیک فرکانسها
|
|||
|
public class BandpassFilter
|
|||
|
{
|
|||
|
private float a0, a1, a2, b1, b2;
|
|||
|
private float z1, z2;
|
|||
|
|
|||
|
private float sampleRate;
|
|||
|
public float LowFreq { get; private set; }
|
|||
|
public float HighFreq { get; private set; }
|
|||
|
|
|||
|
public BandpassFilter(float sampleRate, float lowFreq, float highFreq)
|
|||
|
{
|
|||
|
this.sampleRate = sampleRate;
|
|||
|
SetFrequencies(lowFreq, highFreq);
|
|||
|
z1 = 0;
|
|||
|
z2 = 0;
|
|||
|
}
|
|||
|
|
|||
|
public void SetFrequencies(float lowFreq, float highFreq)
|
|||
|
{
|
|||
|
// اعتبارسنجی ساده
|
|||
|
if (lowFreq <= 0) lowFreq = 50f;
|
|||
|
if (highFreq >= sampleRate / 2f) highFreq = sampleRate / 2f - 100;
|
|||
|
if (lowFreq >= highFreq)
|
|||
|
{
|
|||
|
Debug.LogWarning("BandpassFilter: lowFreq must be less than highFreq");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
LowFreq = lowFreq;
|
|||
|
HighFreq = highFreq;
|
|||
|
|
|||
|
float omegaL = 2.0f * Mathf.PI * LowFreq / sampleRate;
|
|||
|
float omegaH = 2.0f * Mathf.PI * HighFreq / sampleRate;
|
|||
|
|
|||
|
float centerFreq = (omegaL + omegaH) / 2.0f;
|
|||
|
float bandwidth = omegaH - omegaL;
|
|||
|
float Q = centerFreq / bandwidth;
|
|||
|
|
|||
|
float alpha = Mathf.Sin(centerFreq) / (2.0f * Q);
|
|||
|
float cosW0 = Mathf.Cos(centerFreq);
|
|||
|
float norm = 1.0f + alpha;
|
|||
|
|
|||
|
a0 = alpha / norm;
|
|||
|
a1 = 0;
|
|||
|
a2 = -alpha / norm;
|
|||
|
b1 = -2.0f * cosW0 / norm;
|
|||
|
b2 = (1.0f - alpha) / norm;
|
|||
|
|
|||
|
// ریست فیلتر برای جلوگیری از artifact بعد تغییر فرکانس
|
|||
|
z1 = 0;
|
|||
|
z2 = 0;
|
|||
|
}
|
|||
|
|
|||
|
public void Process(float[] samples)
|
|||
|
{
|
|||
|
for (int i = 0; i < samples.Length; i++)
|
|||
|
{
|
|||
|
float input = samples[i];
|
|||
|
float output = a0 * input + a1 * z1 + a2 * z2 - b1 * z1 - b2 * z2;
|
|||
|
|
|||
|
z2 = z1;
|
|||
|
z1 = output;
|
|||
|
|
|||
|
samples[i] = output;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|