using XiaoZhiSharp.Utils; using NAudio.Wave; using OpusSharp.Core; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace XiaoZhiSharp.Services { public class AudioWaveService : IAudioService, IDisposable { // NAudio 音频输出相关组件 private IWavePlayer? _waveOut; private BufferedWaveProvider? _waveOutProvider = null; // NAudio 音频输入相关组件 private WaveInEvent? _waveIn; public event IAudioService.PcmAudioEventHandler? OnPcmAudioEvent; // 音频参数 public int SampleRate { get; set; } = Global.SampleRate_WaveOut; public int SampleRate_WaveIn { get; set; } = Global.SampleRate_WaveIn; public int Bitrate { get; set; } = 16; public int Channels { get; set; } = 1; public int FrameDuration { get; set; } = 60; public int FrameSize { get { return SampleRate * FrameDuration / 1000; // 帧大小 } } public bool IsPlaying { get; private set; } public bool IsRecording { get; private set; } = false; public int VadCounter { get; private set; } = 0; // 用于语音活动检测的计数器 public AudioWaveService() { Initialize(); } public void Initialize() { // 初始化音频输出相关组件 var waveFormat = new WaveFormat(SampleRate, Bitrate, Channels); _waveOut = new WaveOutEvent(); _waveOutProvider = new BufferedWaveProvider(waveFormat); _waveOut.Init(_waveOutProvider); // 增大缓冲区大小,例如设置为 10 秒的音频数据 _waveOutProvider.BufferLength = SampleRate * Channels * 2 * 10; // 初始化音频输入相关组件 _waveIn = new WaveInEvent(); _waveIn.WaveFormat = new WaveFormat(48000, Bitrate, Channels); //_waveIn.WaveFormat = new WaveFormat(SampleRate, Bitrate, Channels); _waveIn.DataAvailable += waveIn_DataAvailable; _waveIn.RecordingStopped += waveIn_RecordingStopped; // 启动音频播放线程 Thread threadWave = new Thread(() => { while (true) { if (!IsPlaying) { if (_waveOutProvider.BufferedDuration > TimeSpan.FromSeconds(1)) { StartPlaying(); } } while (IsPlaying) { // 可以添加更多逻辑,如缓冲区检查等 Thread.Sleep(10); } StopPlaying(); } }); threadWave.Start(); } public void StartRecording() { if (_waveIn != null) { if (!IsRecording) { _waveIn.StartRecording(); IsRecording = true; VadCounter = 0; //LogConsole.WriteLine("开始录音"); } } } public void StopRecording() { if (_waveIn != null) { if (IsRecording) { _waveIn.StopRecording(); //LogConsole.WriteLine("结束录音"); IsRecording = false; VadCounter = 0; } } } /// /// 静音检测 /// private bool IsAudioMute(byte[] buffer, int bytesRecorded) { double rms = 0; int sampleCount = bytesRecorded / 2; // 每个样本 2 字节 for (int i = 0; i < sampleCount; i++) { short sample = BitConverter.ToInt16(buffer, i * 2); rms += sample * sample; } rms = Math.Sqrt(rms / sampleCount); rms /= short.MaxValue; // 归一化到 0 - 1 范围 double MuteThreshold = 0.01; // 静音阈值 return rms < MuteThreshold; } private void waveIn_RecordingStopped(object? sender, StoppedEventArgs e) { } private float[] ConvertBytesToFloats(byte[] byteData) { int sampleCount = byteData.Length / 2; // 假设是 16 位音频 float[] floatData = new float[sampleCount]; for (int i = 0; i < sampleCount; i++) { short sample = BitConverter.ToInt16(byteData, i * 2); floatData[i] = sample / (float)short.MaxValue; } return floatData; } private void waveIn_DataAvailable(object? sender, WaveInEventArgs e) { Task.Run(() => { byte[] pcmBytes48000 = e.Buffer; if (!IsAudioMute(pcmBytes48000, e.BytesRecorded)) { if(Global.IsDebug) Console.Title = "录音-" + VadCounter; byte[] pcmBytes = ConvertPcmSampleRate(pcmBytes48000, 48000, SampleRate_WaveIn, Channels, Bitrate); if (OnPcmAudioEvent != null) { OnPcmAudioEvent(pcmBytes); } } else { VadCounter ++; if (Global.IsDebug) Console.Title = "静音-" + VadCounter; } }); } private byte[] ConvertPcmSampleRate(byte[] pcmData, int originalSampleRate, int targetSampleRate, int channels, int bitsPerSample) { // 创建原始音频格式 WaveFormat originalFormat = new WaveFormat(originalSampleRate, bitsPerSample, channels); // 将 byte[] 数据包装成 MemoryStream using (MemoryStream memoryStream = new MemoryStream(pcmData)) { // 创建原始音频流 using (RawSourceWaveStream originalStream = new RawSourceWaveStream(memoryStream, originalFormat)) { // 创建目标音频格式 WaveFormat targetFormat = new WaveFormat(targetSampleRate, bitsPerSample, channels); // 进行重采样 using (MediaFoundationResampler resampler = new MediaFoundationResampler(originalStream, targetFormat)) { resampler.ResamplerQuality = 60; // 设置重采样质量 // 计算重采样后数据的大致长度 long estimatedLength = (long)(pcmData.Length * (double)targetSampleRate / originalSampleRate); byte[] resampledData = new byte[estimatedLength]; int totalBytesRead = 0; int bytesRead; byte[] buffer = new byte[resampler.WaveFormat.AverageBytesPerSecond]; while ((bytesRead = resampler.Read(buffer, 0, buffer.Length)) > 0) { Array.Copy(buffer, 0, resampledData, totalBytesRead, bytesRead); totalBytesRead += bytesRead; } // 调整数组长度到实际读取的字节数 Array.Resize(ref resampledData, totalBytesRead); return resampledData; } } } } public void StartPlaying() { if (!IsPlaying) { _waveOut?.Play(); IsPlaying = true; } } public void StopPlaying() { if (IsPlaying) { _waveOut?.Stop(); IsPlaying = false; } } public void AddOutSamples(byte[] pcmData) { if (_waveOutProvider != null) { // 添加样本数据 _waveOutProvider.AddSamples(pcmData, 0, pcmData.Length); } } public void AddOutSamples(float[] pcmData) { if (_waveOutProvider != null) { byte[] byteAudioData = FloatArrayToByteArray(pcmData); // 检查缓冲区可用空间 while (_waveOutProvider.BufferedBytes + byteAudioData.Length > _waveOutProvider.BufferLength) { // 等待一段时间,让缓冲区有足够空间 System.Threading.Thread.Sleep(10); } // 添加样本数据 _waveOutProvider.AddSamples(byteAudioData, 0, byteAudioData.Length); } } private static byte[] FloatArrayToByteArray(float[] floatArray) { // 初始化一个与 float 数组长度两倍的 byte 数组,因为每个 short 占 2 个字节 byte[] byteArray = new byte[floatArray.Length * 2]; for (int i = 0; i < floatArray.Length; i++) { // 将 float 类型的值映射到 short 类型的范围 short sample = (short)(floatArray[i] * short.MaxValue); // 将 short 类型的值拆分为两个字节 byteArray[i * 2] = (byte)(sample & 0xFF); byteArray[i * 2 + 1] = (byte)(sample >> 8); } return byteArray; } private static float[] ByteArrayToFloatArray(byte[] byteArray) { // 检查字节数组的长度是否是 4 的倍数 if (byteArray.Length % 4 != 0) { throw new ArgumentException("字节数组的长度必须是 4 的倍数。"); } // 计算浮点数数组的长度 int floatCount = byteArray.Length / 4; float[] floatArray = new float[floatCount]; // 循环遍历字节数组,每次取 4 个字节转换为一个浮点数 for (int i = 0; i < floatCount; i++) { floatArray[i] = BitConverter.ToSingle(byteArray, i * 4); } return floatArray; } public void Dispose() { IsPlaying = false; IsRecording = false; _waveIn?.Dispose(); _waveOut?.Dispose(); } } }