语音PCM

语音PCM

脉冲编码调制(Pulse Code Modulation, PCM)是语音信号的重要编码方式之一。语音编码是将模拟信号转为数字信号的语音通信技术,分为波形编码、参量编码和混合编码等类型。波形编码针对语音波形进行,在降低量化样本比特数的同时保持了良好的语音质量。
PCM编码就是一种波形编码方法,通过每隔一段时间对模拟语音信号采样,将其取整量化,用二进制码表示抽样量化的幅值,实现将语音数字化的编码调制。
PCM是现代数字传输系统普遍采用的调制方式。PCM可以向用户提供多种业务,包括2M~155 Mbit/s速率的数字数据专线业务和语音、图像、远程教学等业务,适用于传输速率要求高、需要更高带宽的应用,在语音信号处理中有着广泛的运用。
PCM分为抽样、量化和编码三个步骤,如下图所示。下面对此过程进行介绍
在这里插入图片描述

1.抽样

语音信号具有频率和振幅特征,频率对应于时间轴,振幅对应于电平轴,而由于存储空间有限,数字编码过程中,必须对正弦信号进行抽样。抽样是把模拟信号以其信号带宽2倍以上的频率提取样值,变为在时间轴上离散的抽样信号的过程。对一个正弦信号进行抽样获得的抽样信号是一个脉冲幅度调制信号,对抽样信号进行检波和平滑滤波,即可还原出原来的模拟信号。
抽样过程就是抽取某点的频率值的过程。显然,在1s内抽取的点越多,获取的频率信息越丰富。为了复原波形,一次振动中必须有2个点的采样,人耳能够感觉到的最高频率为20kHz,因此要满足人耳的听觉要求,则需要至少每秒进行40000次采样,即采样率为44. 1 kHz。光有频率信息是不够的,还必须获得该频率的能量值并量化,用于表示信号强度。量化电平数为2的整数次幂,常用16 bit的采样大小即26。例如对一个语音信号进行8次采样,采样点分别对应的能量值分别为Al~A8,使用2bit的采样大小只能保留Al ~A8中4个点,而进行3bit的采样则刚好记录下8点的所有信息。采样率和采样大小的值越大,记录的波形越接近原始信号。
抽样可以看作是周期性单位冲激脉冲和语音模拟信号的相乘,结果为一系列的周期性冲激脉冲,脉冲的高度与模拟信号的取值成正比。若抽样速率足够大,则离散的冲激脉冲可以完全代替模拟信号,即由这些离散信号可恢复出原信号。
抽样定理表述为:设有最高频率小于 f h f_h fh的信号 m ( t ) m(t) m(t),将周期为 T ≤ 1 / ( 2 f h ) T\le 1/(2f_h) T1/(2fh)的冲激脉冲信号 δ T ( t ) \delta_T(t) δT(t)与其相乘进行抽样,则 m ( t ) m(t) m(t)被抽样信号完全确定。
m s ( t ) = m ( t ) δ T ( t ) m_s(t)=m(t)\delta_T(t) ms(t)=m(t)δT(t)

m ( t ) m(t) m(t)的傅里叶变换:
M s ( f ) = M ( f ) ∗ Δ Ω ( f ) M_s(f)=M(f)*\Delta_{\Omega}(f) Ms(f)=M(f)ΔΩ(f)
其中,
Δ Ω ( f ) = 1 T ∑ n = − ∞ ∞ δ ( f − n f s ) , f s = 1 T M s ( f ) = 1 T [ M ( f ) ∗ ∑ n = − ∞ ∞ δ ( f − n f s ) ] = ∑ n = − ∞ ∞ M ( f − n f s ) \Delta_{\Omega}(f)=\frac{1}{T}\sum \limits_{n=-\infty}^{\infty}\delta(f-nf_s),f_s=\frac{1}{T} \\ M_s(f)=\frac{1}{T}[M(f)*\sum \limits_{n=-\infty}^{\infty}\delta(f-nf_s)]=\sum \limits_{n=-\infty}^{\infty}M(f-nf_s) ΔΩ(f)=T1n=δ(fnfs),fs=T1Ms(f)=T1[M(f)n=δ(fnfs)]=n=M(fnfs)
由上可见,当频率间隔 f s ≥ 2 f H f_s\ge 2f_H fs2fH时, M s ( f ) M_s(f) Ms(f)包含的每个原信号频谱间不重叠,这样就能用低通滤波器从抽样信号中恢复原信号。
由于实际的滤波器不够理想,边缘不够陡峭,因此实际抽样频率需要比 2 f H 2f_H 2fH大些,如典型的电话信号最高频率限制在3400 Hz,而抽样频率一般取为8kHz

2.量化

抽样信号虽然是时间轴上离散的信号,但仍是模拟信号,其值在一定的取值范围内可有无限多个值。显然,对无限个样值给出数字码组来对应是不可能的。为了实现以数字码表示样值,必须采用四舍五入的方法把样值分级取整,使一定取值范围内的样值由无限多个值变为有限个值。这一过程称为量化。
量化后的抽样信号与量化前相比较有所失真,且不再是模拟信号。这种量化失真在接收端还原模拟信号时表现为噪声,称为量化噪声。量化噪声的大小取决于把样值分级取整的方式,分的级数越多,即量化级差或间隔越小,量化噪声也越小。
设模拟抽样信号为 m ( k T ) m(kT) m(kT) ,抽样值仍为取值连续的变量。用 N N N个不同的二进制数字码元来表示抽样值大小,则共有 M = 2 N M=2^N M=2N个不同的抽样值。
将抽样范围划分为 M M M个区间,每个区间用一个电平表示。这样,共有 M M M个离散电平,成为量化电平。M个抽样区间等间隔划分,为均匀量化,否则为非均匀量化。量化信号表示为:
m q ( k T ) = q i , m i − 1 ≤ m ( k T ) < m i m_q(kT)=q_i,m_{i-1}\le m(kT)\lt m_i mq(kT)=qi,mi1m(kT)<mi
若为均匀量化,量化值可取量化间隔的中点:
i = ( m i + m i − 1 ) / 2 , i = 0 , 1 , . . . , M _i=(m_i+m_{i-1})/2,i=0,1,...,M i=(mi+mi1)/2,i=0,1,...,M

3.编码

量化后的抽样信号在一定范围内仅有有限个可取的样值,且信号正负幅度分布的对称性使正负样值个数相等,正负向的量化级对称分布。若将有限个量化样值绝对值从小到大排列,依次赋予十进制数字,以 + + + − - 号为前缀,则量化后的抽样信号就转化为按时序排列的十进制数字码流。将数字转换为二进制编码,根据十进制代码总个数确定二进制位数,即字长。这样把量化的抽样信号变换成给定字长的二进制码流的过程称为编码。
二进制码可经受较高的噪声电平的干扰,并且易于再生,因此PCM一般采用二进制码。
对于 Q Q Q个量化电平,可以用 k k k位二进制码表示,其中每一种组合为一个码字。在点对点通信或短距离通信中,采用 k = 7 k=7 k=7位码已基本满足质量要求,而对于干线远程的全网通信,一般需要经过多次转接,有较高的质量要求,目前多采用8位编码PCM设备。

量化及代码

语音信号的特点就是小信号较重要,需要提高小信号的量化信噪比。所以需要对信号进行非线性压缩,改变大信号与小信号比例关系。
在这里插入图片描述
在这里插入图片描述
此时, c i c_i ci就是[1/128,1/64,1/32,1/16,…,1/2]每段间隔都有一个重建值 s ^ ′ \hat{s}^{'} s^

正负(1bit)段落码(3bit)段内电平码(4bit)

比如:输入x (-4096<x<4096)

正负(1bit):
大于零,1;小于零,0;
在这里插入图片描述

%实验要求一:PCM编解码实验
clear all;
close all;
[x fs ]= audioread('C6_1_y.wav');  
v=1;
xx=x/v;
sxx=floor(xx*4096);
y=pcm_encode(sxx);
yy=pcm_decode(y,v)';

nq=sum((x-yy).*(x-yy))/length(x);
sq=mean(yy.^2);
snr=(sq/nq);
t=(1:length(x))/fs;
subplot(211)
plot(t,x/max(abs(x)))
axis tight
title('(a)编码前语音')
xlabel('时间/s')
ylabel('幅度')
subplot(212)
plot(t,yy/max(abs(yy)))
axis tight
title('(b)解码后语音')
xlabel('时间/s')
ylabel('幅度')
snrq=10*log10(mean(snr))
figure;
plot(t,abs(x-yy));
title('编码前后误差')

encoder

%PCM编码函数
function[out]=pcm_encode(x)
n=length(x);                                                     %-4096<x<4096
for i=1:n
    if x(i)>0
       out(i,1)=1;                                               %根据符号输出第1位量化结果
    else
       out(i,1)=0;
    end

    if abs(x(i))>=0 & abs(x(i))<32                               %根据输入范围输出后2-4out(i,2)=0; out(i,3)=0; out(i,4)=0; step=2;st=0; 
    elseif 32<=abs(x(i)) & abs(x(i))<64
         out(i,2)=0; out(i,3)=0; out(i,4)=1; step=2;st=32; 
    elseif 64<=abs(x(i)) & abs(x(i))<128
         out(i,2)=0; out(i,3)=1; out(i,4)=0; step=4;st=64;
    elseif 128<=abs(x(i)) & abs(x(i))<256
         out(i,2)=0; out(i,3)=1; out(i,4)=1; step=8;st=128;
    elseif 256<=abs(x(i)) & abs(x(i))<512
         out(i,2)=1; out(i,3)=0; out(i,4)=0; step=16;st=256;
    elseif 512<=abs(x(i)) & abs(x(i))<1024
         out(i,2)=1; out(i,3)=0; out(i,4)=1; step=32;st=512;
    elseif 1024<=abs(x(i)) & abs(x(i))<2048
         out(i,2)=1; out(i,3)=1; out(i,4)=0; step=64;st=1024;
    elseif 2048<=abs(x(i)) & abs(x(i))<4096
         out(i,2)=1; out(i,3)=1; out(i,4)=1; step=128;st=2048;

    else
         out(i,2)=1; out(i,3)=1; out(i,4)=1; step=128;st=2048;
    end

    if(abs(x(i))>=4096)                                            %超出最大幅值的量化结果
       out(i,2:8)=[1 1 1 1 1 1 1];
    else                                                           %未超出,计算后四位
        tmp=floor((abs(x(i))-st)/step);
        t=dec2bin(tmp,4)-48;                                       %十进制转为4位二进制字符串
        out(i,5:8)=t(1:4);
    end
end
out=reshape(out',1,8*n);                                           %调整为长为8n的行向量

decoder

%PCM解码函数
function[out]=pcm_decode(ins,v)
n=length(ins);                         %输入为8位PCM采样信号

in=reshape(ins',8,n/8)';               %调整矩阵行列数
slot(1)=0;                             %量化幅值
slot(2)=32;
slot(3)=64;
slot(4)=128;
slot(5)=256;
slot(6)=512;
slot(7)=1024;
slot(8)=2048;

step(1)=2;                              %步长
step(2)=2;
step(3)=4;
step(4)=8;
step(5)=16;
step(6)=32;
step(7)=64;
step(8)=128;


for i=1:n/8
    ss=2*in(i,1)-1;                     %解码符号位
    tmp=in(i,2)*4+in(i,3)*2+in(i,4)+1;  %解码2-4位
    st=slot(tmp);
    dt=(in(i,5)*8+in(i,6)*4+in(i,7)*2+in(i,8))*step(tmp)+0.5*step(tmp);   %解码5-8out(i)=ss*(st+dt)/4096*v;               %解码结果相加,乘以量化电平
end

在这里插入图片描述
在这里插入图片描述

Reference:梁瑞宇等. 语音信号处理实验教程[M]. 北京: 机械工业出版社, 2016.2.

在 Qt 中,可以使用 Qt Multimedia 模块中的 QAudioOutput 和 QAudioFormat 类来生成 PCM 格式的音频。而要将文字转换为语音,则需要使用第三方语音合成 API,例如科大讯飞、百度语音等。 以下是一个简单的示例代码,使用科大讯飞的语音合成 API 将输入的文字转换为 PCM 格式的音频: ```cpp #include <QCoreApplication> #include <QUrl> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QJsonDocument> #include <QJsonObject> #include <QAudioOutput> #include <QAudioFormat> QString appId = "your app id"; // 替换为你的 AppId QString apiKey = "your api key"; // 替换为你的 API Key QString apiSecret = "your api secret"; // 替换为你的 API Secret void textToSpeech(const QString& text, const QString& filePath) { QString url = "http://api.xfyun.cn/v1/service/v1/tts"; QString param = "{\"auf\":\"audio/L16;rate=16000\",\"aue\":\"raw\",\"voice_name\":\"xiaoyan\",\"engine_type\":\"intp65\",\"text\":\"" + text + "\"}"; QString currentTime = QString::number(QDateTime::currentDateTime().toTime_t()); QString checksum = QCryptographicHash::hash((apiKey + currentTime + param).toUtf8(), QCryptographicHash::Md5).toHex(); QUrl apiUrl(url); apiUrl.addQueryItem("auf", "audio/L16;rate=16000"); apiUrl.addQueryItem("aue", "raw"); apiUrl.addQueryItem("voice_name", "xiaoyan"); apiUrl.addQueryItem("engine_type", "intp65"); apiUrl.addQueryItem("text", text); QNetworkRequest request(apiUrl); request.setRawHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); request.setRawHeader("X-Appid", appId.toUtf8()); request.setRawHeader("X-CurTime", currentTime.toUtf8()); request.setRawHeader("X-Param", param.toUtf8()); request.setRawHeader("X-CheckSum", checksum.toUtf8()); QNetworkAccessManager manager; QNetworkReply* reply = manager.post(request, QByteArray()); QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); QByteArray responseData = reply->readAll(); reply->deleteLater(); QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData); QJsonObject jsonObj = jsonDoc.object(); if (jsonObj.contains("code") && jsonObj.value("code").toInt() == 0) { QByteArray audioData = QByteArray::fromBase64(jsonObj.value("data").toString().toUtf8()); QFile file(filePath); if (file.open(QIODevice::WriteOnly)) { file.write(audioData); file.close(); } } } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QString text = "你好,Qt!"; QString filePath = "output.pcm"; textToSpeech(text, filePath); QAudioFormat format; format.setSampleRate(16000); format.setChannelCount(1); format.setSampleSize(16); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::SignedInt); QAudioOutput output(format); output.start(&QFile(filePath)); return a.exec(); } ``` 在上面的代码中,`textToSpeech` 函数使用科大讯飞的语音合成 API 将输入的 `text` 转换为 PCM 格式的音频,并保存到指定的文件 `filePath` 中。然后,定义了一个 `QAudioFormat` 对象来设置音频格式,使用 `QAudioOutput` 类来播放生成的 PCM 音频。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值