使用FileStream生成WAV可正常播放,改用MemoryStream时触发波形头损坏异常
核心问题根因
这个报错和MemoryStream的内存更新机制没有关系,本质是两个容易被忽略的框架行为叠加导致的:
System.Media.SoundPlayer读取传入的流对象时,默认从流的当前游标位置开始读,不会自动把游标重置到流的开头。- 你用到的WaveWriter(NAudio组件)在Dispose执行WAV文件收尾逻辑时,完成WAV头的大小字段回写后,会把底层流的游标停在整个流的末尾位置。这种状态下的流传给SoundPlayer,它从末尾位置开始读,自然找不到合法的RIFF/WAV标识头,就会抛出“The wave header is corrupt”的异常。
你之前观测到的ChunkSize字段的写入延迟,其实是WaveWriter的正常工作逻辑:它在构造实例时会先写入带占位值的WAV头(RIFF块大小字段先填固定值36),后续写入采样数据时只做末尾追加,不会回头修改头部;直到执行Dispose收尾时,才会把流游标倒回头部,把真实的文件总大小写入ChunkSize字段,再把游标停到流的末尾。这个流程在FileStream和MemoryStream上的表现完全一致,根本不存在“MemoryStream缓冲区没完成更新”的问题。
你的实现存在的具体疏漏
- 缺失了流游标重置的关键操作。不管底层用的是FileStream还是MemoryStream,WaveWriter完成Dispose收尾后,流的游标都会停在流的末尾位置,必须手动把流的
Position属性设为0,才能让SoundPlayer从合法的WAV头位置开始读取。 - 对流生命周期的认知存在偏差。SoundPlayer在构造阶段就会同步把传入流的全部有效内容加载到自己的内部缓存中,只要构造时流的内容完整、位置正确,后续就算释放流对象,也不会影响音频播放,完全不存在“提前释放导致流内容失效”的问题。
- 你之前的FileStream版本能正常跑通,是因为最终是用文件路径字符串构造的SoundPlayer,相当于SoundPlayer自己重新打开文件流、自动定位到文件头读取,根本没用到你之前操作过的那个FileStream对象的当前位置,所以一直没暴露游标偏移的问题。
修正后的MemoryStream实现代码
Friend Sub New(buffer() As Byte, nBytes As Integer) ' 按WAV文件预估总大小预分配内存,避免MemoryStream动态扩容产生额外开销 Dim wavStream As New MemoryStream(CInt(nBytes + 44)) Dim waveFormat As New WaveFormat(44100, 16, 2) Dim Wwriter As New WaveWriter(wavStream, waveFormat) ' 写入原始采样数据 Wwriter.Write(buffer, 0, nBytes) ' 触发WaveWriter完成WAV头的真实大小字段回写 CType(Wwriter, IDisposable).Dispose() ' 之前完全漏掉的核心步骤:把流游标强制拉回流的起始位置 wavStream.Position = 0 ' 传入游标复位后的流给SoundPlayer Dim soundPlayer As New System.Media.SoundPlayer(wavStream) soundPlayer.Play() soundPlayer.Dispose() ' SoundPlayer构造完成时已经读取完全部流内容,此时可安全释放流 wavStream.Dispose() End Sub
补充说明:不要在SoundPlayer构造完成后、播放过程中把MemoryStream的内部缓冲区清空或者截断,否则会触发播放异常;但正常Dispose流对象、释放内存操作,不会对已经完成加载的SoundPlayer造成影响。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

