Golang从串口流转换单声道PCM至WAV文件遇异常音频问题求助
串口PCM转WAV杂音问题的解决方法
核心问题分析
- 编码器被提前关闭:循环内部每次调用
e.Close(),会直接终止WAV文件的写入流程,第一次循环后文件就被写入结束标记,后续写入无效,重复的头信息还会破坏文件结构。 - 16位PCM样本解析错误:你设置的是16位采样深度,但代码把每个字节单独当作一个样本处理。16位PCM每个样本占2字节,需要将两个字节合并成一个16位整数(通常为小端字节序),而非拆分单个字节。
- 忽略串口实际读取字节数:
s.Read(buf)返回的读取长度被丢弃,可能导致缓冲区残留旧数据,引入杂音。
修正后的代码
func readAudio() { flag.Parse() // Output file. out, err := os.Create("output.wav") if err != nil { log.Fatal(err) } defer out.Close() // 8 kHz, 16 bit, 1 channel, WAV. e := wav.NewEncoder(out, 8000, 16, 1, 1) defer func() { // 仅在程序结束时关闭编码器,保证文件结构完整 if err := e.Close(); err != nil { log.Fatal(err) } }() // Program Info log.Printf("Starting with port %s at baud rate %d", com, baud) // Setup Serial Port c := &serial.Config{Name: com, Baud: 115200} s, err := serial.OpenPort(c) if err != nil { log.Fatal(err) } defer s.Close() for { log.Print("Reading Bytes in...") buf := make([]byte, 320) // 记录实际读取的字节数 n, err := s.Read(buf) if err != nil { log.Fatal(err) } // 仅处理实际读取到的数据 buf = buf[:n] // Print Bytes Read log.Println(buf) audioBuf, err := newAudioIntBuf(buf) if err != nil { log.Fatal(err) } // 写入缓冲区到WAV文件 if err := e.Write(audioBuf); err != nil { log.Fatal(err) } } } func newAudioIntBuf(byteBuf []byte) (*audio.IntBuffer, error) { buf := audio.IntBuffer{ Format: &audio.Format{ NumChannels: 1, SampleRate: 8000, }, } // 16位PCM每个样本占2字节,按小端序解析 for i := 0; i < len(byteBuf); i += 2 { if i+1 >= len(byteBuf) { break // 避免越界,跳过不完整的样本 } // 合并两个字节为16位整数(小端) sample := int(int16(byteBuf[i]) | int16(byteBuf[i+1])<<8) buf.Data = append(buf.Data, sample) } return &buf, nil }
额外说明
- 串口每次发送320字节是合理的:8kHz采样率、16位单声道下,每秒产生16000字节(8000*2),320字节对应20ms音频数据,属于标准小数据块传输,不会影响结果。
- 需确保串口发送的PCM数据格式为8kHz、16位、单声道、小端字节序,若发送端格式与接收端解析不一致,仍会出现杂音。
内容的提问来源于stack exchange,提问作者vinniyo
相关产品推荐
相关产品推荐

