303 lines
7.7 KiB
C#
303 lines
7.7 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.Audio;
|
||
using UnityEngine.UI;
|
||
/// <summary>
|
||
/// 麦克风实时聊天
|
||
/// </summary>
|
||
public class RTSpeechHandler : MonoBehaviour
|
||
{
|
||
/// <summary>
|
||
/// 麦克风名称
|
||
/// </summary>
|
||
public string m_MicrophoneName = null;
|
||
/// <summary>
|
||
/// 音量大于这个值,就开始录制
|
||
/// </summary>
|
||
public float m_SilenceThreshold = 0.01f;
|
||
/// <summary>
|
||
/// 沉默限制时长
|
||
/// </summary>
|
||
[Header("设置几秒没声音,就停止录制")]
|
||
public float m_RecordingTimeLimit = 2.0f;
|
||
/// <summary>
|
||
/// 对话状态保持时长
|
||
/// </summary>
|
||
[Header("设置对话状态保持时间")]
|
||
public float m_LossAwakeTimeLimit = 10f;
|
||
/// <summary>
|
||
/// 锁定状态下,不记录静默时间
|
||
/// </summary>
|
||
[SerializeField]private bool m_LockState = false;
|
||
/// <summary>
|
||
/// 音频
|
||
/// </summary>
|
||
private AudioClip m_RecordedClip;
|
||
/// <summary>
|
||
/// 唤醒关键词
|
||
/// </summary>
|
||
[SerializeField]private string m_AwakeKeyWord=string.Empty;
|
||
/// <summary>
|
||
/// 唤醒状态
|
||
/// </summary>
|
||
[Header("标识当前是否处于唤醒状态")]
|
||
[SerializeField]private bool m_AwakeState = false;
|
||
/// <summary>
|
||
/// 监听状态
|
||
/// </summary>
|
||
[SerializeField] private bool m_ListeningState = false;
|
||
/// <summary>
|
||
/// 录制状态
|
||
/// </summary>
|
||
[SerializeField] private bool m_IsRecording = false;
|
||
/// <summary>
|
||
/// 沉默计时器
|
||
/// </summary>
|
||
[SerializeField]private float m_SilenceTimer = 0.0f;
|
||
|
||
/// <summary>
|
||
/// 聊天脚本
|
||
/// </summary>
|
||
[SerializeField]private RTChatSample m_ChatSample;
|
||
/// <summary>
|
||
/// 语音唤醒
|
||
/// </summary>
|
||
[SerializeField] private WOV m_VoiceAWake;
|
||
|
||
private void Awake()
|
||
{
|
||
OnInit();
|
||
}
|
||
|
||
private void OnInit()
|
||
{
|
||
//AI回复结束回调
|
||
m_ChatSample.OnAISpeakDone += SpeachDoneCallBack;
|
||
|
||
//绑定唤醒回调
|
||
m_VoiceAWake.OnBindAwakeCallBack(AwakeCallBack);
|
||
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
|
||
if (m_MicrophoneName == null)
|
||
{
|
||
// 如果没有指定麦克风名称,则使用系统默认麦克风
|
||
m_MicrophoneName = Microphone.devices[0];
|
||
}
|
||
|
||
// 确保麦克风准备好
|
||
if (Microphone.IsRecording(m_MicrophoneName))
|
||
{
|
||
Microphone.End(m_MicrophoneName);
|
||
}
|
||
|
||
// 启动麦克风监听
|
||
m_RecordedClip = Microphone.Start(m_MicrophoneName, false,30, 16000);
|
||
|
||
while (Microphone.GetPosition(null) <= 0) { }
|
||
|
||
// 启动录制状态检测协程
|
||
StartCoroutine(DetectRecording());
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始检测声音
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private IEnumerator DetectRecording()
|
||
{
|
||
while (true)
|
||
{
|
||
float[] samples = new float[128]; // 选择合适的样本大小
|
||
int position = Microphone.GetPosition(null);
|
||
if (position < samples.Length)
|
||
{
|
||
yield return null;
|
||
continue;
|
||
}
|
||
|
||
|
||
|
||
try { m_RecordedClip.GetData(samples, position - samples.Length); } catch { }
|
||
|
||
float rms = 0.0f;
|
||
foreach (float sample in samples)
|
||
{
|
||
rms += sample * sample;
|
||
}
|
||
|
||
rms = Mathf.Sqrt(rms / samples.Length);
|
||
|
||
if (rms > m_SilenceThreshold)
|
||
{
|
||
m_SilenceTimer = 0.0f; // 重置静默计时器
|
||
|
||
//启动关键词唤醒监听
|
||
if (!m_AwakeState&&!m_ListeningState)
|
||
{
|
||
StartVoiceListening();
|
||
}
|
||
//已唤醒,启动录制
|
||
if (m_AwakeState&&!m_IsRecording)
|
||
{
|
||
StartRecording();
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
|
||
if (!m_LockState)
|
||
{
|
||
m_SilenceTimer += Time.deltaTime;
|
||
}
|
||
|
||
//结束唤醒词监听
|
||
if (m_ListeningState&&!m_AwakeState && m_SilenceTimer >= m_RecordingTimeLimit)
|
||
{
|
||
StopVoiceListening();
|
||
}
|
||
|
||
//唤醒状态,结束说话
|
||
if (m_AwakeState&&m_IsRecording && m_SilenceTimer >= m_RecordingTimeLimit)
|
||
{
|
||
StopRecording();
|
||
}
|
||
|
||
//沉默时间过长,结束对话状态,j进入等待唤醒
|
||
if (m_AwakeState && !m_IsRecording && m_SilenceTimer >= m_LossAwakeTimeLimit)
|
||
{
|
||
m_AwakeState=false;
|
||
PrintLog("Loss->对话连接已丢失");
|
||
}
|
||
|
||
}
|
||
|
||
yield return null;
|
||
|
||
}
|
||
}
|
||
|
||
[SerializeField]private AudioSource m_Greeting;
|
||
[SerializeField] private AudioClip m_GreatingVoice;
|
||
/// <summary>
|
||
/// 关键词监听回调
|
||
/// </summary>
|
||
/// <param name="_msg"></param>
|
||
private void AwakeCallBack(string _msg)
|
||
{
|
||
if (_msg == m_AwakeKeyWord&&!m_AwakeState)
|
||
{
|
||
m_AwakeState = true;
|
||
Debug.Log("识别到关键词:" + _msg);
|
||
PrintLog("Link->已建立对话连接");
|
||
if (m_Greeting)
|
||
{
|
||
m_Greeting.clip = m_GreatingVoice;
|
||
m_Greeting.Play();
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 开始唤醒监听
|
||
/// </summary>
|
||
private void StartVoiceListening()
|
||
{
|
||
m_ListeningState = true;
|
||
m_VoiceAWake.StartRecognizer();
|
||
PrintLog("开始->识别唤醒关键词");
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止唤醒监听
|
||
/// </summary>
|
||
private void StopVoiceListening()
|
||
{
|
||
m_ListeningState = false;
|
||
m_VoiceAWake.StopRecognizer();
|
||
PrintLog("结束->唤醒关键词识别");
|
||
//StartCoroutine(WaitAndStopListen());
|
||
}
|
||
|
||
private IEnumerator WaitAndStopListen()
|
||
{
|
||
yield return new WaitForSeconds(1);
|
||
m_ListeningState = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始监听说话声音
|
||
/// </summary>
|
||
private void StartRecording()
|
||
{
|
||
m_SilenceTimer = 0.0f; // 重置静默计时器
|
||
m_IsRecording = true;
|
||
PrintLog("正在录制对话...");
|
||
//停止监听,并重新开始录制,会导致唤醒的那一帧声音丢失
|
||
Microphone.End(m_MicrophoneName);
|
||
m_RecordedClip = Microphone.Start(m_MicrophoneName, false, 30, 16000);
|
||
}
|
||
/// <summary>
|
||
/// 结束说话
|
||
/// </summary>
|
||
private void StopRecording()
|
||
{
|
||
m_IsRecording = false;
|
||
|
||
PrintLog("会话录制结束...");
|
||
|
||
// 停止麦克风监听
|
||
Microphone.End(m_MicrophoneName);
|
||
|
||
//处理音频数据
|
||
SetRecordedAudio();
|
||
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始录制监听
|
||
/// </summary>
|
||
public void ReStartRecord()
|
||
{
|
||
m_RecordedClip = Microphone.Start(m_MicrophoneName, true, 30, 16000);
|
||
|
||
m_LockState = false;
|
||
}
|
||
|
||
|
||
private void SetRecordedAudio()
|
||
{
|
||
m_LockState = true;
|
||
|
||
m_ChatSample.AcceptClip(m_RecordedClip);
|
||
//AudioSource.clip = m_RecordedClip;
|
||
//AudioSource.Play();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 对话结束回调,启动麦克风检测
|
||
/// </summary>
|
||
private void SpeachDoneCallBack()
|
||
{
|
||
ReStartRecord();
|
||
}
|
||
|
||
[SerializeField] private Text m_PrintText;
|
||
/// <summary>
|
||
/// 打印日志
|
||
/// </summary>
|
||
/// <param name="_log"></param>
|
||
private void PrintLog(string _log)
|
||
{
|
||
m_PrintText.text = _log;
|
||
//Debug.Log(_log);
|
||
}
|
||
|
||
}
|