☰
PESQ语音质量评估工具源码编译实战:Linux与Windows全流程指南
2026/10/22 14:05:15 网站建设 项目流程

简介:PESQ作为ITU-T P.862标准指定的语音质量客观评价工具,广泛用于通信系统测试、音频编码评估与语音增强效果验证。这份资源打包了PESQ编译源码与配套教程,适合需要离线搭建评估环境、研究心理声学模型或开展语音质量测试的工程师与研究者。

资源共88个文件,压缩包仅5.53MB,以C源码、头文件、编译脚本为主,附带PDF标准文档、编译教程和66个wav语音样本,可辅助理解算法输入输出及评分流程。目前已有655人学习下载。

通过阅读教程,读者可在GCC/MinGW环境下完成编译,生成pesq可执行程序;结合内置wav样本,可对比参考语音与待测语音的MOS得分,深入验证PESQ的频域失真与掩蔽效应计算逻辑。资源结构清晰,兼顾源码学习与实际测试,是一份便于上手、可二次开发的语音质量评估工具包。

1. PESQ 工具编译源码:与其等现成二进制,不如自己动手

前阵子给某个语音增强项目做效果评估,我需要给增强后的语音打一个客观的 MOS 分数。去网上找 PESQ 的可执行文件,结果要么是别人编译好的老版本,换台机器就报缺库,要么运行参数和文档对不上,折腾半天还没跑通。后来我放弃现成二进制,直接拿了源码来编译,前后半小时就把工具跑起来了,而且后续批量评估都靠它。这篇笔记就是按那个过程整理的,先讲 PESQ 在评什么,再给出 Linux 和 Windows 下从源码编译的完整步骤,最后把运行中的高频翻车点按「现象、原因、解决」列清楚。新手可以照命令一步步走,熟手可以直接跳到避坑章节看边界条件。

2. PESQ 的原理与编译选型:搞清楚它在算什么,才知道怎么编译

2.1 PESQ 在算什么:从听感差异到一张 MOS 表

PESQ 是语音质量评估领域里被用得最多的一种客观算法,全称是 Perceptual Evaluation of Speech Quality。它的核心思路是拿一段原始干净语音作为参考,再把经过编解码、网络传输或语音增强处理的退化语音和参考语音做对比,最终输出一个从 -0.5 到 4.5 的分数,这个分数模拟的是人耳对这段语音的主观 MOS 评分。

要理解 PESQ,关键是知道它不是简单地在波形上做信噪比。它内部先对参考信号和退化信号做时间对齐,补偿掉编解码和网络抖动带来的时延差异;然后把信号变换到感知域,模拟人耳听觉的频响和非线性响度;最后把感知差异映射成 MOS-LQO(Listening Quality Objective,客观听感质量分)。这也是为什么 PESQ 的分数比 PESQ 的早期版本更接近主观试听结果,也是语音增强、降噪、音频编解码项目里拿它当默认指标的原因。

从编译的角度看,PESQ 是一个纯 C 语言实现的标准算法,没有外部第三方库依赖,连数学库的调用都很少。它不依赖深度学习框架,不需要 CUDA,也不需要 Python 环境。这就意味着只要有一个能编译 C 的编译器,就能把源码变成可执行文件。Linux 下的 gcc、Windows 下的 MinGW 或 Visual Studio 都可以,这也是它适合自己编译的根本原因:代码量不大,依赖极少,编译过程几乎不会因为环境问题卡住。

2.2 源码里到底有哪些文件:常见 C 代码结构与依赖

拿到 PESQ 源码包之后,很大概率会看到这些类型的文件:以 pesq 开头的核心代码、若干 DSP 工具代码、读 WAV 文件的代码,以及一个说明文档。源码中一般会包含一个 Makefile 或者可以直接用命令行编译的文件列表。

常见做法是,核心计算部分放在类似 pesq.c 和 dsp.c 的文件里;pesq.c 负责主流程、命令行参数解析、分数映射;dsp.c 负责滤波、FFT、感知系数计算;还有一部分代码负责从 WAV 文件里读取采样数据,这里可能叫 wav_io.c 或者 readwav.c。文件名在不同流传版本里有差异,这很正常,但只要整个文件夹里能找到这些文件,编译思路都是通的。

编译前要确认依赖,其实依赖只有标准 C 库和数学库。数学库在链接时需要加 -lm,这是最容易漏的一个参数。如果源码包里自带 Makefile,通常 Makefile 已经写好了。如果只有散落的 .c 文件,手动编译时要把所有源文件一起交给 gcc,不能只编译核心文件,否则链接阶段会提示找不到函数定义。

选择自编译还有一个非常现实的原因:很多网上流传的现成 PESQ 工具来自多年前的某网盘或某个镜像站,这些二进制文件是否加了额外处理、是否支持 16kHz 宽带语音、命令行参数是否带 -ref 和 -deg 前缀,都没法保证。自己编译源码至少能保证你拿到的是最接近参考实现的版本,出了问题也能对着源码排查。

2.3 为什么自己编译而不是找现成二进制:版本、许可、可移植性

PESQ 的参考实现是以源码形式发布的,标准文本和源码在一致性上经过了很多年的交叉验证。你自己编译出来的程序,在行为上会严格遵守参考实现的默认配置,例如采样率处理、抖动容忍窗口、分数映射表。而网上那些已经打包好的可执行文件,你看不到它的编译选项,也不知道它是否修改过内部常数。

从可移植性来说,自己编译能让你拿到一个和当前系统匹配的本地可执行文件。在服务器上做离线批量评估时,只需要把这个文件放到固定目录,就能重复运行。相比之下,从网上下载二进制往往带有 glibc 版本依赖,复制到新机器上常见的报错就是“找不到某个共享库”或“版本 GLIBC_X 不存在”,这类问题在语音评估环境里尤其烦人。

许可角度也要提一句:PESQ 参考实现来源于电信标准组织发布的标准代码,虽然源码开放,但商用和再分发有明确的授权边界。自己下载源码在内部编译使用,是最稳妥的方式。如果你只是做项目评估,不在自己的产品里重新分发这份代码,几乎不会碰到法律问题。这也是我建议走源码编译而不是到处找别人编译好的二进制的原因之一。你至少要知道自己拿的是什么、代码边界在哪儿。

3. 在 Linux 下编译 PESQ 源码:从解压到跑通第一个样本

3.1 准备环境:确认 gcc 和 make 是否可用

Linux 下编译 PESQ 需要两样东西:gcc 编译器和 make 工具。绝大多数服务器或开发机上都已经预装了,但有些精简镜像里只有运行库没有编译器。在动手之前,先用两条命令确认一下:

which gcc gcc --version | head -n 1 which make make --version | head -n 1

第一条命令能输出 gcc 的路径,第二条命令通过版本号确认编译器可用。如果哪一条返回了 “command not found”,就需要先用系统的包管理安装 build-essential 或者其他等价工具组。在 Debian 系环境里通常是 sudo apt-get install build-essential,在 Red Hat 系环境里是 sudo yum groupinstall "Development Tools"。这个操作完成后,再重新执行一次上面的命令确认。

安装编译器本身是一个很常规的操作,不值得卡住。PESQ 这份代码是几十年前的 C 语言风格,现代 gcc 编译它通常只会给几个警告,不会产生致命错误。唯一要注意的是 gcc 版本偏旧的老系统,比如某些重要环境还在用 gcc 4.8,那也没问题,因为 PESQ 没有用到高版本 C11 特性。

3.2 编译命令与 Makefile:从源码包到 pesq 可执行文件

假设你手头的源码包已经解压到了某个目录,比如 ~/pesq-src。进入目录后,先看看里面有没有已经写好的 Makefile。如果有,直接执行 make 就行。如果没有 Makefile 或者 Makefile 是针对老环境的,我一般会自己写一个非常小的 Makefile,内容如下:

# Makefile 示例:PESQ 源码编译 # 把需要编译的源文件列在这里,没有外部依赖 CC = gcc CFLAGS = -O2 -Wall LDFLAGS = -lm # 常见文件列表。如果源码包里有额外的 wav 读取文件,请加进来 OBJS = pesq.o dsp.o wav_io.o pesq: $(OBJS) $(CC) $(CFLAGS) -o pesq $(OBJS) $(LDFLAGS) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f *.o pesq

把这份内容保存成 Makefile 文件后,在命令行里执行 make,几秒钟内就能生成一个名为 pesq 的可执行文件。这里的关键是 OBJS 这个变量,它必须包含源码包里所有的 .c 文件对应的 .o 文件。如果源码包的 WAV 读取代码叫 readwav.c,那就要把 OBJS 改成“pesq.o dsp.o readwav.o”。names 可能不同,以你自己实际解压出的文件为准。

为什么 LDFLAGS 要写 -lm?因为 PESQ 的代码里用到了平方根、对数等数学函数,这些函数放在 libm 里,链接时如果不加 -lm,最后一步会报“undefined reference to `sqrt'”一类的错误。CFLAGS 里的 -O2 是优化选项,加上它能让处理速度更快一些;-Wall 打开警告信息,方便你在编译时发现可疑代码。后面如果看到一堆警告,只要没有出现错误,都不影响使用。

3.3 第一次运行:用两个 wav 文件验证输出

编译通过后,需要准备一对测试文件来验证工具能正常打分。PESQ 这种标准工具需要两个输入:参考文件(ref)和退化文件(deg)。参考文件是干净语音,退化文件是经过处理的语音。两者必须是相同采样率、相同格式、时长接近的 PCM 数据。

常见的运行命令是:

# 用法:pesq 采样率 参考.wav 退化.wav ./pesq +8000 ref.wav deg.wav

如果命令没报错,会在终端打印一个类似“PESQ MOS = 4.123”的输出,同时在当前目录生成一个以 deg 文件名开头的结果文件,里面详细记录了每帧的得分和最终 MOS-LQO。这里的 +8000 表示参考信号采样率是 8000Hz。如果你的语音是 16kHz 宽带,就要改成 +16000,否则结果不会可信。

我自己的经验是第一次跑通时,用一段 8 秒左右的干净中文语音,加上一个轻微压缩的副本,得到的分数一般在 3.5 到 4.2 区间。如果打出来 4.5 或 -0.5,那说明输入的文件格式或者参数配对有问题,不是工具坏了。后面第 5 章会专门讲这些坑。

生成的结果文件里,每一行是一个窗口的判决结果,最后一行的 PESQ MOS 才是最终值。批量评估或者写脚本时,可以用 grep 或 awk 从这个文件里提取最终分数。这一步验证通过之后,这个编译出来的工具就能用于后续所有评估了。

4. 在 Windows 下编译 PESQ 源码:MinGW 或 Visual Studio 的两种常见做法

4.1 MinGW 环境下用一条 gcc 命令完成编译

Windows 下最常见的方式是装一个 MinGW-w64 环境,然后在命令行里像 Linux 一样使用 gcc 编译。这个方式最省事,不需要新建工程,也避开了 Visual Studio 对预编译头和多字节字符的折腾。

假设源码解压在 D:\pesq-src,打开 cmd 或 PowerShell,先确认 gcc 在 PATH 里:

gcc -v

如果系统提示找不到命令,就要把安装好的 MinGW 的 bin 目录加到 PATH 环境变量。这是 Windows 环境里最容易卡住的一步。加好 PATH 后,在源码目录执行下面这条编译命令:

gcc -O2 -o pesq.exe pesq.c dsp.c wav_io.c -lm

如果源码包里文件名不叫 wav_io.c,改成实际文件名即可。这条命令把三个源文件一次性编译并链接成 pesq.exe。需要注意 -lm 在 MinGW 下通常能正常工作,因为 MinGW 自带数学库。如果链接时提示找不到 libm,就把 -lm 去掉,但这种情况很少见。

编译完成后,运行方式与 Linux 一致:

pesq.exe +16000 ref.wav deg.wav

Windows 的控制台默认代码页和源码里的字符串输出可能不一致,导致终端里中文乱码,这是正常的,不影响最终生成的结果文件。如果你在乎终端显示,可以在 PowerShell 里先执行 chcp 65001 切到 UTF-8,但分数提取不要依赖终端,直接读结果文件更稳定。

4.2 Visual Studio 工程方式:新建工程、拖入源码、关掉预编译头

有些开发者不熟悉命令行,更习惯用 Visual Studio 编译。这块也不难。新建一个空的 C++ 控制台工程,然后把源码包里的 .c 文件全部拖进“源文件”目录,把 .h 文件拖进“头文件”目录。

这里有几个必须处理的配置项:

1. 关闭预编译头:项目属性 -> C/C++ -> 预编译头 -> 不使用预编译头。 2. 字符集设为“使用多字节字符集”:项目属性 -> 常规 -> 字符集。 3. C/C++ -> 语言 -> 符合模式,设为“否”。

第一项是关键。PESQ 是纯 C 代码,没有 pch.h 这种头文件,Visual Studio 默认开启预编译头会直接报“无法打开包含文件 pch.h”。第二项是因为老代码里大量使用 char 数组处理文件名和字符串,用 Unicode 字符集会导致函数版本不匹配。第三项是为了避免新版 MSVC 对老式 C 代码的严格语法检查,否则某些隐式转换会变成编译错误。

配置完成后,按 F7 编译,生成的 exe 会放在工程输出目录。Visual Studio 编译器的数学库是默认链接的,所以不需要像 gcc 那样手动加 -lm。如果你的工程里同时包含多余的文件,比如原作者留下的测试文件,只要没有引用它们,就不影响链接。

4.3 音频文件准备:16-bit PCM、单声道、时长对齐

Windows 下编译成功只是第一步,最容易被忽视的是 WAV 文件的格式要求。PESQ 读 WAV 文件的代码通常只支持标准的 PCM 编码,不支持 A-law、Mu-law、浮点 WAV 或压缩格式。如果输入文件不满足要求,运行时会报错或者读出一堆无意义数据,最终打出一个离谱分数。

在准备样本时,我一般会先用工具把语音统一转成这个格式:

编码格式:PCM 16-bit signed integer 采样率:8000 Hz 或 16000 Hz,按 PESQ 命令参数匹配 声道数:单声道 位深:16bit,小端序

如果你的源文件是 48kHz 或 24bit,需要先降采样、转成单声道。这个过程可以用某个开源音频解码器完成,也可以用 Python 的 wave 模块写个小脚本处理。注意,PESQ 不会自动做重采样,命令行写 +8000 就表示它默认两个文件都是 8kHz,如果喂进去一个 16kHz 的文件,分数就完全不可信。

时长匹配方面,参考和退化可以不等长,但差异不能太大。PESQ 内部有时间对齐和补偿机制,能容忍几百毫秒的时延差异。如果退化文件因为丢包或裁剪比参考短了很多,它会对齐失败并输出一个不可信的低分。最好在喂给工具之前先对两个文件做静音裁剪和长度归一化,让它们保持同一个数量级的时长。

5. PESQ 编译与使用中的避坑指南:5 个高频翻车现场

5.1 现象:编译报错 “storage size of 'xxx' isn't known”

编译时突然报这样的错误,看起来像某个结构体定义缺失。原因通常是指定编译的源文件不完整,比如只编了 pesq.c 而没有编 dsp.c,或者头文件没有被正确找到。也有一些情况下是某些老代码依赖一个未定义的全局结构体,需要你手动补上。

解决方法是先确认你编译命令里包含了所有 .c 文件,再检查头文件路径。把编译命令改成 gcc -O2 -o pesq pesq.c dsp.c wav_io.c -lm 这种把全部源文件列出的形式,错误一般立刻消失。如果仍然报错,去源码目录里执行 gcc -O2 -c pesq.c,单独编译看是不是头文件缺失。

5.2 现象:运行报错 “Error opening file” 但文件确实存在

这个坑非常多见,尤其 Windows 下。路径里带了中文或空格还好说,真正的问题是文件名被写成了带扩展名的字符串,但当前目录和文件实际所在目录不一致。PESQ 的老代码会把命令行参数当作文件名直接 fopen,不会自动拼路径。

解决方法是把待测试的 wav 文件和 pesq 可执行文件放在同一个目录下,然后在命令行里切到那个目录再运行。在 Windows 下还可以用 \ 分隔路径,但建议别折腾路径,统一把文件复制到当前目录最省事。另一个隐蔽原因是 WAV 文件本身损坏,读取函数打开成功但读头失败,这时它也会报出类似拿不到文件内容的错误。用播放器确认文件能正常打开后再跑 PESQ。

5.3 现象:结果分数接近 4.5 或小于 0,明显不合理

如果你跑出来的 MOS 分数是 4.5 或 -0.5,几乎可以断定是抽样率参数和实际文件的采样率不匹配。PESQ 对带宽很敏感,内部有固定滤波器组设计,8k 和 16k 的模型参数差异很大。你给 +8000 却塞 16kHz 文件进去,它会因为频谱超出预期而产生严重偏差,最终输出贴近极端的分数。

另一个原因是参考和退化文件搞反了。PESQ 是可以接受参考和退化互换的,但顺序不同会导致不同的对齐策略,分数可能会差零点几。尤其在做增强评估时,如果把退化文件当成参考,输出分数通常会偏高。解决方法是严格检查命令行参数顺序,参考信号在前,退化信号在后。

5.4 现象:参考语音与退化语音长度差超过容忍范围

PESQ 内部有一个时间对齐模块,它能补偿时延和抖动,但这个补偿范围是有限的。如果退化语音因为网络丢包或切边,比参考短了一半,或者前面静音被切掉了好几秒,PESQ 会对齐到一个错误的位置,然后输出一个完全不可信的低分。

这个问题的标准解法是先做 VAD 或静音检测,去掉两端的大段静音,再尽量让两个文件从同一语音内容开始。还要检查退化信号开头是否有明显的延迟,比如几十毫秒到几百毫秒是可以接受的,如果超过 500ms,建议先做时间对齐预处理。PESQ 对齐能力不是无限的,不要拿它当全自动对齐工具用。

5.5 现象:编译通过但运行后没有生成结果文件

有些版本的 PESQ 只向终端打印结果,不写文件,或者它会根据退化文件名生成一个附加扩展名的输出文件,但没打印出路径。如果没看到文件,先不要觉得工具坏了。有些 fork 版本里,退出前把结果写到一个名为 results.txt 的文件里,有些则直接丢弃。

解决方法是运行命令后立刻看终端完整输出。如果输出里有类似 “Writing result to: deg.wav_pesq.txt” 的字样,那结果就在当前目录下。如果终端直接打印分数,没有写文件,那你可以用重定向把输出保存下来:

./pesq +16000 ref.wav deg.wav > result.txt

这样终端输出和最终分数都会存进 result.txt。无论哪种实现,至少不要让结果丢失。顺便提一句,如果 run 完了终端一行输出都没有,检查是否把参数写错导致程序提前退出,这种情况我也会先用成品 wav 对验证工具本身没问题。

6. 把 PESQ 变成能批量评估的工具:一个最小脚本与验证方法

编译和单次运行都跑通之后,下一步自然是批量评估。真实项目中不可能只有一对语音,我通常会准备一个目录,里面按测试集命名 wav 文件:reference 子目录放干净语音,degraded 子目录放处理后的语音,文件名一一对应。

下面这个 bash 脚本是常见做法,它会把每一对文件跑一遍,并把分数汇总到一个 CSV 文件里:

#!/bin/bash # 批量跑 PESQ 的脚本示例 # 用法: ./batch_pesq.sh ref_dir deg_dir 16000 REF_DIR=$1 DEG_DIR=$2 FS=$3 OUT_FILE=pesq_results.csv # 输出表头 echo "file,pesq_mos" > $OUT_FILE for ref in $REF_DIR/*.wav; do base=$(basename "$ref") deg="$DEG_DIR/$base" if [ -f "$deg" ]; then # 运行 PESQ 并把最后一行分数写入临时文件 ./pesq +$FS "$ref" "$deg" > tmp_output.txt score=$(grep -oP 'PESQ MOS = \K[0-9.]+' tmp_output.txt) echo "$base,$score" >> $OUT_FILE else echo "$base,missing_deg" >> $OUT_FILE fi done rm -f tmp_output.txt

脚本里最核心的是 grep 提取分数那一行。很多版本输出格式是固定的,但如果你想确认,可以先跑一个样本看输出格式,再把 grep 的匹配规则换成实际格式。参数 $FS 传入的是 8000 或 16000,这要和语音实际采样率一致。脚本会自动跳过缺失的退化文件,并把状态标注出来,这样就能一眼看出哪些样本没对齐。

验证这个分数是否可信,最直接的方法是拿同一对语音用人类试听打分做比对。比如你找几个人听三份文件:原始参考、退化版本、PESQ 输出对应的主观分。如果主观评分在 3.5 附近,而 PESQ 打出 4.4,那大概率是输入采样率或者文件格式有问题。这不是 PESQ 的 bug,而是你喂给它的数据和它内部假设不一致。

还要说一点后期进阶。PESQ 虽然几乎是语音质量评估的事实标准,但它对 48kHz 超宽带语音、对噪声背景下音乐类信号支持有限。近年来的项目如果遇到这些问题,我一般会关注那些支持全频带的客观评估算法,比如 POLQA 或 ViSQOL。它们的原理与 PESQ 类似,但源码和许可模式又不太一样。如果你只是需要快速搭一个评估基线,PESQ 编译版仍然是最低成本的起点;如果业务场景已经明确是超宽带或语音增强的极致调优,那就应该把预算投向更新一代的模型。

我用这个编译版 PESQ 已经处理过几个语音增强和去噪项目,在批量评估时我最养成的一个习惯是:每次跑完都把输入文件的格式参数记录在结果文件名里,比如 ref_8000_ref.wav_deg_8000.wav_pesq.txt,这样过了两周回看数据,还能知道当时用的是窄带还是宽带模型。这个习惯帮我避免过不止一次误用结果。希望这篇笔记能让你少踩几个我踩过的坑,也希望你自己动手编译 PESQ 的过程顺利,等你跑通第一个样本后,就会发现这个“老古董”工具依然很能打。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询