Unity中AudioClip.PCMReaderCallback写入audioBuffer后未触发求助
问题分析与修复方案
1. BinaryReader关闭底层MemoryStream导致后续读取失效
你在PcmReader中用using包裹BinaryReader,默认情况下BinaryReader会关闭底层的MemoryStream,第一次回调执行后,audioBuffer就被关闭,后续无法再写入或读取数据,回调自然无法正常触发。
修复:
创建BinaryReader时指定不关闭底层流,手动释放BinaryReader而非底层流:
private void PcmReader(float[] data) { // ... 其他逻辑 ... var reader = new BinaryReader(audioBuffer, Encoding.Default, leaveOpen: true); try { Debug.Log("audioBuffer is read"); for (int i = 0; i < data.Length; i++) { if (audioBuffer.Position < audioBuffer.Length) { short sample = reader.ReadInt16(); data[i] = sample / 32768f; } else { data[i] = 0f; } } } finally { reader.Dispose(); // 释放BinaryReader,但保留底层流 } // ... 其他逻辑 ... }
2. AudioClip.Create参数错误
AudioClip.Create的第二个参数是总样本数,你当前设置的44100意味着音频仅1秒时长(44100样本 ÷ 44100采样率),如果WebSocket返回数据的速度慢于音频播放速度,可能还没收到数据,音频就已播放完毕,回调停止触发。
修复:
设置足够大的总样本数适配流式音频场景:
audioClip = AudioClip.Create("ElevenLabsTTS", int.MaxValue, 1, 44100, true, PcmReader);
3. 线程安全问题:WebSocket回调跨线程操作MemoryStream
WebSocket的OnMessage回调运行在非Unity主线程,直接写入MemoryStream会和主线程的PcmReader回调产生线程冲突,导致数据损坏或读取异常。
修复:
添加锁保护audioBuffer的读写操作:
private readonly object bufferLock = new object(); // PcmReader中读取时加锁 lock (bufferLock) { if (audioBuffer == null || audioBuffer.Length == 0) return; // ... 读取buffer的逻辑 ... } // WebSocket回调中写入时加锁 websocket.OnMessage += (bytes) => { string message = Encoding.UTF8.GetString(bytes); var data = JsonUtility.FromJson<MessageData>(message); if (data.audio != null) { byte[] audioData = System.Convert.FromBase64String(data.audio); lock (bufferLock) { audioBuffer.Write(audioData); } Debug.Log("Added audio data"); } };
4. 优化MemoryStream重置逻辑
读取完所有数据后,不仅要清空长度,还要重置Position到0,避免后续写入位置错误:
lock (bufferLock) { if (audioBuffer.Position >= audioBuffer.Length) { audioBuffer.SetLength(0); audioBuffer.Position = 0; } }
完整修复后的关键代码片段
Start方法
private MemoryStream audioBuffer; private AudioClip audioClip; private AudioSource audioSource; private readonly object bufferLock = new object(); async void Start() { Debug.Log("Start"); audioBuffer = new MemoryStream(); audioClip = AudioClip.Create("ElevenLabsTTS", int.MaxValue, 1, 44100, true, PcmReader); await ConnectToWebSocket(); audioSource = GetComponent<AudioSource>(); audioSource.clip = audioClip; audioSource.Play(); Debug.Log("Playing audio clip"); }
PcmReader回调
private void PcmReader(float[] data) { lock (bufferLock) { if (audioBuffer == null || audioBuffer.Length == 0) { Array.Fill(data, 0f); return; } var reader = new BinaryReader(audioBuffer, Encoding.Default, leaveOpen: true); try { Debug.Log("audioBuffer is read"); for (int i = 0; i < data.Length; i++) { if (audioBuffer.Position < audioBuffer.Length) { short sample = reader.ReadInt16(); data[i] = sample / 32768f; } else { data[i] = 0f; } } } finally { reader.Dispose(); } if (audioBuffer.Position >= audioBuffer.Length) { audioBuffer.SetLength(0); audioBuffer.Position = 0; } } }
内容的提问来源于stack exchange,提问作者user9319865
相关产品推荐
相关产品推荐

