使用NAudio生成的WAV文件时长为0但文件大小增加的问题排查
代码实现
string path = string.Empty; ConcurrentQueue<string> recQue = new ConcurrentQueue<string>(); private async void SaveAudioRecording() { timer.Dispose(); if (string.IsNullOrEmpty(path)) { path = Directory.GetCurrentDirectory() + "\AudioRecordings_" + clientSession.clientInfo[0]; Directory.CreateDirectory(path); } using (MemoryStream audioDataStream = new MemoryStream()) { MixingSampleProvider mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(41100, 1)); while (recQue.TryDequeue(out string first)) { string firstDel = first.Substring(first.IndexOf('§'), 4); if (recQue.TryDequeue(out string second)) { string secondDel = second.Substring(second.IndexOf('§'), 4); first = first.Replace(firstDel, string.Empty); second = second.Replace(secondDel, string.Empty); if (firstDel != secondDel) { mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(41100, 1)); first = first.Replace(firstDel, string.Empty); second = second.Replace(secondDel, string.Empty); byte[] firstAudioData = Convert.FromBase64String(first); byte[] secondAudioData = Convert.FromBase64String(second); MemoryStream[] memoryStreams = { new MemoryStream(), new MemoryStream() }; await memoryStreams[0].WriteAsync(firstAudioData, 0, firstAudioData.Length); await memoryStreams[1].WriteAsync(secondAudioData, 0, secondAudioData.Length); foreach (var stream in memoryStreams) { await stream.FlushAsync(); var reader = new RawSourceWaveStream(stream, new WaveFormat(41100, 1)); var waveProvider = reader.ToSampleProvider(); mixer.AddMixerInput(waveProvider); var tempBuffer = new float[mixer.WaveFormat.SampleRate * mixer.WaveFormat.Channels * 5]; // 5-second buffer int samplesRead; while ((samplesRead = mixer.Read(tempBuffer, 0, tempBuffer.Length)) > 0) { var byteBuffer = new byte[samplesRead * sizeof(float)]; Buffer.BlockCopy(tempBuffer, 0, byteBuffer, 0, byteBuffer.Length); await audioDataStream.WriteAsync(byteBuffer, 0, byteBuffer.Length); } } } else { first = first.Replace(firstDel, string.Empty); second = second.Replace(secondDel, string.Empty); byte[] firstAudioData = Convert.FromBase64String(first); byte[] secondAudioData = Convert.FromBase64String(second); await audioDataStream.WriteAsync(firstAudioData, 0, firstAudioData.Length); await audioDataStream.WriteAsync(secondAudioData, 0, secondAudioData.Length); await audioDataStream.FlushAsync(); } } else { first = first.Replace(firstDel, string.Empty); byte[] firstAudioData = Convert.FromBase64String(first); await audioDataStream.WriteAsync(firstAudioData, 0, firstAudioData.Length); await audioDataStream.FlushAsync(); } } WaveFileWriter writer = new WaveFileWriter(System.IO.Path.Combine(path, "Audio_recording_" + Directory.EnumerateFiles(path).Count().ToString() + ".wav"),mixer.WaveFormat); WriteWavHeader(writer, Convert.ToInt32(audioDataStream.Length)); audioDataStream.Seek(0, SeekOrigin.Begin); await audioDataStream.CopyToAsync(writer); await writer.FlushAsync(); } recQue = new ConcurrentQueue<string>(); recordButton.IsEnabled = true; } private void WriteWavHeader(Stream stream, int audioDataSize) { using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) { writer.Write(Encoding.ASCII.GetBytes("RIFF")); writer.Write(audioDataSize + 36); // Total file size - 8 bytes writer.Write(Encoding.ASCII.GetBytes("WAVE")); writer.Write(Encoding.ASCII.GetBytes("fmt ")); writer.Write(16); // Length of format data writer.Write((ushort)1); // PCM format writer.Write((ushort)1); // Not Stereo writer.Write(41100); // Sample rate writer.Write(82200); // Byte rate (Sample rate * Channels * BitsPerSample / 8) writer.Write((ushort)2); // Block align (Channels * BitsPerSample / 8) writer.Write((ushort)16); // Bits per sample writer.Write(Encoding.ASCII.GetBytes("data")); writer.Write(audioDataSize); // Size of audio data } }
问题描述
已确认WriteWavHeader方法可正常工作,但生成的WAV文件无论文件大小如何,时长始终显示为0。逻辑上,当firstDel与secondDel不匹配时混合两个原始音频流,否则将它们拼接至内存流,最终生成WAV文件。预期添加更多音频数据时文件时长会增加,但仅文件大小有所变化,请问该问题的可能原因是什么?
可能的原因分析
音频格式与WAV头不匹配:
混合音频时使用的是32位浮点格式(WaveFormat.CreateIeeeFloatWaveFormat(41100, 1)),但WriteWavHeader硬编码了16位PCM的参数(如BitsPerSample=16、BlockAlign=2),且覆盖了WaveFileWriter自动生成的对应格式头。播放器会根据WAV头的参数计算时长,实际数据格式与头信息不符时,会导致解析错误,显示时长为0。内存流位置未重置导致混合数据无效:
写入音频数据到memoryStreams后,未将流的Position重置为0,导致后续RawSourceWaveStream读取不到任何音频数据,混合后的输出为空。即使文件大小因拼接部分的数据增加,混合部分的无效数据也会破坏整体文件的可解析性。音频数据格式混杂:
拼接和单个音频处理时直接写入的是原始16位PCM数据,而混合部分写入的是32位浮点数据,两种格式混杂在同一个文件中,播放器无法识别统一的音频格式,进而无法正确计算时长。WaveFileWriter的使用错误:
代码中先通过WaveFileWriter初始化文件(使用混合器的浮点格式),随后调用WriteWavHeader覆盖了正确的头信息,导致文件头与实际数据格式完全不匹配,播放器无法解析出有效时长。
内容的提问来源于stack exchange,提问作者Alexander Jansson

