语音的压缩、存储与回放
一、 设计要求与目标
(1)使用DSP实现语音压缩和解压缩的基本算法,算法类型自定,例如可以采用G.711、G.729等语音压缩算法。
(2)采用A/D转换器从MIC输入口实时采集语音信号,进行压缩后存储到DSP的片内和片外RAM存储器中,存储时间不小于10秒。
(3)存储器存满之后,使用DSP进行实时解压缩,并从SPEAKER输出口进行回放输出。
(4)使用指示灯对语音存储和回放过程进行指示。
发挥部分:
使用多种算法进行语音的压缩、存储和解压缩,比较它们之间的优缺点。
二、 实验目的
1、通过本实验掌握5402DSP片上外设多通道缓冲串行口mcbsp。
2、学习掌握tlc320ad50CODEC编译码器的内部结构、工作原理。
3、学习A律语音压缩以及C语言下的编程方法。
三、实验原理
1.语音采集与输出模块
语音采集与输出模块采用的是TI公司推出的一款高性能的立体声音频Codec芯片TLC320AD50C,内置耳机输出放大器,支持MIC和LINE IN两种输入方式(二选一),且对输入和输出都具有可编程增益调节。AD50的模数转换(ADCs)和数模转换(DACs)部件高度集成在芯片内部,采用了先进的Sigma-delta过采样技术,可以在8K到96K的频率范围内提供16bit、20bit、24bit和32bit的采样,ADC和DAC的输出信噪比分别可以达到90dB和100dB。与此同时,AD50还具有很低的能耗,回放模式下功率仅为23mW,省电模式下更是小于15uW。由于具有上述优点,使得AD50是一款非常理想的音频模拟I/O器件,可以很好的应用在随声听(如CD,MP3??)、录音机等数字音频领域[2]。由TLC320AD50C组成的语音输入与输出模块不仅采样率高最高可达96K,且外围电路简单,性价比高。
2.语音编码
(1) 概念:
语音编码一般分为两类:一类是波形编码,一类是被称为“声码器技术”的编码。 PCM编码即脉冲编码调制。
波形编码的最简单形式就是脉冲编码调制(Pulse code modulation),这种方式将语音变换成与其幅度成正比的二进制序列,而二进制数值往往采用脉冲表示,并用脉冲对采样幅 度进行编码,所以叫做脉冲编码调制。
脉冲编码调制没有考虑语音的性质,所以信号没有得到压缩。
(2)量化:
脉冲编码调制用同等的量化级数进行量化,即采用均匀量化,而均匀量化是基本的量化方 式。但是均匀量化有缺点,在信号动态范围较大而方差较小的时候,其信噪比会下降 。 国际上有两种非均匀量化的方法:A律和u律,u律是最常用的一种。在美国,7位u律是长途电话质量的标准。 而我国采用的是A律压缩,而且有标准的A律PCM编码芯片。
(3)DPCM&ADPCM:
降低传输比特率的方法之一是减少编码的信息量,这要消除语音信号中的冗余度。相邻的语音样本之间存在明显的相关性,因此对相邻样本间的差信号进行编码,便可使信息量得
到压缩。因为差分信号比原语音信号的动态范围和平均能量都小。这种编码叫Differential PCM,简称DPCM,即差分脉冲编码调制。
ADPCM即自适应差分脉冲编码调制,是包括短时预测的编码系统。CCITT(国际电报电话咨询委员会)在19xx年提出的32 kbit/s的编码器建议就是采用ADPCM作为长途传输中的国际通用语音编码方案。这种ADPCM编码方案达到64 kbit/s PCM的语音传输质量,并具有很好的抗误码性能。
(4)a律压缩
a律压缩示意图
A律压缩编码表
采用DSP可以直接对PCM编码后的语音信号进行μ律和A律压缩。图1是DSP硬件实现数据压缩解压的简单流程,DSP将传输来的压缩后的数据进行解压成16位或者32位,然后对解压后的数据进行分析、处理,最后将处理后的数据按照要求压缩成8位的数据格式输出到相应设备,供其他设备读取。 2
图1 数据压缩解压流程
图2是DSP将数据解压的值,DSP将压缩的8位数据解压成16位的DSP通用数据格式,其中高13位为解压后的数据,低3位补0。这是因为6.711的A律压缩只能对13位数据操作。DSP将解压后的数据放在缓冲串口的发送寄存器中,只要运行发送指令,缓冲串口就会将数据发送出去。缓冲串口对接收数据的解压过程和压缩过程完全相反。图3是μ律数据解压的示意图。
图2 A律数据解压
图3 μ律数据解压
DSP内部的缓冲串口(McBSPs)带有硬件实现的μ律/A律压缩解压,用户只需要在相应寄存器中进行设置就可以了。
在进行A律压缩时,采样后的12位数据,默认其最高位为符号位,压缩时要保持最高位即符号位不变,原数据的后11位要压缩成7位。这7位码由3位段落码和4位段内码组成。具体的压缩变换后的数据根据后11位数据大小决定。
表2 A律数据压缩表
3
除对串行口数据实现压扩处理外,这套硬件在McBSP不使用时还可以当作一个特殊的处理单元对内部数据实现压扩处理,他有两种实现方法。
法一:当串行口的发送和接受部分都处于复位状态时,DRR1和DXR1内部通过压扩逻辑连接在一起,数据从DXR1写入并根据XCOMPAND处理,然后根据RCOMPAND再处理,在4个CPU时钟后从DRR1中读出数据。该处理比软件实现快,不利之处在于处理完后没有同步信息通知CPU和DMA。
法二:在数据环回模式下,McBSP也实现了一种内连。数据处理与第一种方法相同,但它可以提供中断信号(或同步事件)给CPU(或DMA)。这里数据处理的时间是根据串行口的比特律确定的。
另外,在通常情况下McBSP先传输信号的高位后传输低位,但是在字长为8比特的数据传输时,McBSP提供了比特倒序的功能,即可以先传输低位后传输高位。
在本实验中,我们通过软件编程来完成线性码转换成A律。
语音信号通常是小信号概率大,大信号出现的概率小,为提高小信号时的量化信躁比,压缩比特速率,可为非线性量化。语音压缩是把16位的数据比特转化为8位数据比特,从而到达语音压缩的目的。
在主程序中通过A/D抽样量化,可以得到16位的线性编码,再由编码表通过软件计算得到8位A律编码,其中最高位为符号位,第6位到第4位为段落码,低4位为段内码。将8位的压缩结果存储到系统RAM中进行缓存,根据抽样率、语音存储时间以及系统RAM的容量设置语音存储缓冲区的大小,待缓冲区存满后,将缓冲区内的数据进行解压缩,然后输出到SPEAKER接口输出端。
若使用A/D转换器,必须首先对A/D转换器进行初始化设置,即设置A/D转换器的工作模式、输入增益以及抽样频率等。
3.程序设计
(1)、程序流程
4
存储器的分配(5402.cmd)
MEMORY {
PAGE 0: VECS: origin = 0080h, length = 0080h /* Internal Program RAM */ PRAM: origin = 7600h, length = 8000h /* Internal Program RAM */
PAGE 1: SCRATCH: origin = 0060h, length = 0020h /* Scratch Pad Data RAM */ DMARAM: origin = 0C00h, length = 0300h /* DMA buffer */
DATA: origin = 1100h, length = 0080h /* Internal Data RAM */ STACK: origin = 1180h, length = 0560h /* Stack Memory Space */ INRAM: origin = 1900h, length = 0100h /* Internal Data RAM */ HPRAM0: origin = 1A00h, length = 0002h /* HPI memory accessible by Host and DSP */
HPRAM1: origin = 1A02h, length = 0280h /* HPI memory accessible by Host and DSP */
HPRAM2: origin = 1C82h, length = 0280h /* HPI memory accessible by Host and DSP */
EXRAM: origin = 1F10h, length = 9000h /* External Data RAM */
5
}
SECTIONS
{
.cinit > PRAM PAGE 0
.text > PRAM PAGE 0
.vectors > VECS PAGE 0
init_var > PRAM PAGE 0
detect > PRAM PAGE 0
vrcprg
matprg
.stack > STACK PAGE 1
.trap > SCRATCH PAGE 1
.const > EXRAM PAGE 1
.data > EXRAM PAGE 1
.bss > EXRAM PAGE 1
.cio > EXRAM PAGE 1
.switch > EXRAM PAGE 1
tables > EXRAM PAGE 1
var > EXRAM PAGE 1
svctab > EXRAM PAGE 1 /* SS_V LSP table */
vctab > EXRAM PAGE 1 /* V LSP table */
uvctab > EXRAM PAGE 1 /* UV LSP table */
cuvtab > EXRAM PAGE 1 /* Stochastic codebook */
cdbktab > EXRAM PAGE 1 /* various codebook tables*/
logtab > EXRAM PAGE 1 /* table for log2 */
powtab > EXRAM PAGE 1 /* table for pow2 */
hamtab > EXRAM PAGE 1 /* table for hamming */
lgwtab > EXRAM PAGE 1 /* table for lag window */
acostab > EXRAM PAGE 1 /* table for arccos */
sqrtab > EXRAM PAGE 1 /* table for square root */
acbtab > EXRAM PAGE 1 /* table for thresholds in acb */ pm03tab > EXRAM PAGE 1 /* table for x^(-0.3) computation */ costab > EXRAM PAGE 1 /* table for cosine */
V23 > INRAM PAGE 1
FSK > INRAM PAGE 1
6 > PRAM PAGE 0 > PRAM PAGE 0
hpibuff0 > HPRAM0 PAGE 1
hpibuff1 > HPRAM1 PAGE 1
hpibuff2 > HPRAM2 PAGE 1
dma_buff > DMARAM PAGE 1
}
/*主程序设计*/
/*语音采集及回放程序*/
/*用A律进行压缩及解压*/
/*采用AD50进行A/D,D/A转换 */ /*灯循环闪烁程序开始*/
/*L0:录音*/
/*L1:放音*/
#include <type.h> /* 头文件*/
#include <board.h>
#include <codec.h>
#include <mcbsp54.h>
/* 宏定义 */
#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ #define QUANT_MASK (0xf) /* Quantization field mask. */ #define NSEGS (8) /* Number of A-law segments. */
#define SEG_SHIFT (4) /* Left shift for segment number. */ #define SEG_MASK (0x70) /* Segment field mask. */
/* 函数声明 */
void delay(s16 period);
void led(s16 cnt);
void initcodec(void);
void flashenable(void);
unsigned char data2alaw(s16 pcm_val);
int alaw2data(unsigned char a_val);
static int search(int val,short *table,int size);
/* 全局变量 */
HANDLE hHandset;
s16 data;
s16 data1;
u16 i=0;
7
u16 temp1;
u16 j=0;
u16 k,l=0;
u8 temp2;
u16 buffer[20000];
static short seg_end[8]={0x1F,0x3F,0x7F,0xFF,0x1FF,0x3FF,0x7FF,0xFFF};
/* 主函数 */
void main()
{
if (brd_init(100))
return;
led(2); //闪灯两次
initcodec(); //初始化codec
flashenable(); //选择片外FLASH为片外存储器
/*
delay(100);
brd_led_toggle(BRD_LED0);
for(i=0x9000;i<0xefff;i++)
{
REG_WRITE(i,*(volatile u16*)DRR1_ADDR(HANDSET_CODEC));
delay(20);
}
brd_led_toggle(BRD_LED1);
delay(200);
for(i=0x9000;i<0xefff;i++)
{
*(volatile u16*)DXR1_ADDR(HANDSET_CODEC)=REG_READ(i);
delay(20);
}
brd_led_toggle(BRD_LED2);
*/
while (1)
{
while (!MCBSP_RRDY(HANDSET_CODEC)) {}; //等待接收handset处的采样
brd_led_toggle(BRD_LED0);
data = *(volatile u16*)DRR1_ADDR(HANDSET_CODEC); //从handset处读取采样
temp1=data2alaw(data); //对采样进行a律压缩
8
/* 把低地址数据放在高八位 高地址数据放在低八位 */
i=i+1;
if(i%2==1)
{
buffer[j]=(temp1<<=8);
/*奇数数据左移8位 temp1=abcdefgh00000000
buffer[j]=temp1*/
}
else
{
buffer[j]=(buffer[j]|temp1);
/*偶数数据与temp1取或 组成新的数据
buffer[j]=abcdefghiabcdefghi*/
j++; //j加1
}
if(i>=40000)
{
i=0;
}
if(j>=20000)
{
j=0;
brd_led_disable(BRD_LED0);
brd_led_toggle(BRD_LED1);
//点亮二极管1 表示放音开始
/* 放音部分 */
for(k=0;k<40000;k++)
{
if(k%2==0)
{
temp2=(buffer[l]>>8)&0x0ff;
}
else
{
temp2=buffer[l]&0x0ff;
l++;
}
if(l>=20000)
9
l=0;
data1=alaw2data(temp2); // a律解压 while (!MCBSP_XRDY(HANDSET_CODEC)) {}; *(volatile u16*)DXR1_ADDR(HANDSET_CODEC) = data1; //将数据写入
D/A转换器
}
/* 放音结束 */
brd_led_toggle(BRD_LED0);
brd_led_toggle(BRD_LED1);
}
}
} //主程序结束
/* 子函数 */
/*******延时******/
void delay(s16 period)
{
int i, j;
for(i=0; i<period; i++)
{
for(j=0; j<period>>1; j++);
}
}
/*******闪灯******/
void led(s16 cnt)
{
while ( cnt-- )
{
brd_led_toggle(BRD_LED0); //切换LED指示灯0的显示状态
delay(1000);
10
}
} brd_led_toggle(BRD_LED1); delay(1000); brd_led_toggle(BRD_LED2); delay(1000);
/*****初始化codec**/
void initcodec(void)
{
/* Open Handset Codec 获取设置codec的句柄*/
hHandset = codec_open(HANDSET_CODEC); / Acquire handle to codec /* Set codec parameters */
codec_dac_mode(hHandset, CODEC_DAC_15BIT); // DAC in 15-bit mode codec_adc_mode(hHandset, CODEC_ADC_15BIT); // ADC in 15-bit mode
codec_ain_gain(hHandset, CODEC_AIN_6dB); // 6dB gain on analog input to ADC codec_aout_gain(hHandset, CODEC_AOUT_MINUS_6dB);
// -6dB gain on analog output from DAC codec_sample_rate(hHandset,SR_8000); // 8KHz sampling rate }
/*****设置flash****/
void flashenable(void)
{
CPLD_CTRL2_REG|=0x0010;
CPLD_DMCTRL_REG|=0x0040;
}
/*****a律压缩******/
unsigned char data2alaw(s16 pcm_val)
{
int mask; aval; int seg; unsigned char if (pcm_val >= 0) { mask = 0xD5; } else { } mask = 0x55; // 标记 bit = 0 pcm_val = -pcm_val; // 标记 (7th) bit = 1
11
// Convert the scaled magnitude to segment number.
seg = search(pcm_val, seg_end, 8);
// Combine the sign, segment, and quantization bits.
if (seg >= 8) // out of range, 返回最大数. return (0x7F ^ mask);
else
{
aval = seg << SEG_SHIFT;
if (seg < 2)
aval |= (pcm_val >> 1) & QUANT_MASK;
else
aval |= (pcm_val >>seg) & QUANT_MASK;
return (aval ^ mask);
}
}
/****alaw的子程序**/
static int search(int val,short *table,int size)
{
int i;
for (i = 0; i < size; i++)
{
if (val <= *table++)
return (i);
}
return (size);
}
/*****a律解压******/
int alaw2data(unsigned char a_val)
{
int t;
int seg;
a_val ^= 0x55;
t = (a_val & QUANT_MASK) << 4;
seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
if(seg==0)
{
t += 8;
t=(t>>3);
12
} if((seg<4)&&(seg>0)) { t +=0x108; t=(t>>(4-seg)); } if(seg>3) { } t+=0x108; t=(t<<=(seg-4));
return ((a_val & SIGN_BIT) ? t : -t);
}
/* 结束 */
四.操作过程
1.将工程文件夹放入C盘要求目录下。
2. 打开CCS,Project → open,打开工程文件。
3.进行编译,连接。
4.根据错误提示,对build option进行正确的设置,修改头文件路径。
5.再编译,连接,显示没有错误。
6.load program---run,此时三盏灯循环亮,之后程序开始,第一盏灯亮开始录音,一段时间后第二盏灯亮开始放音,然后又开始录音。依次循环。
五.调试过程与运行结果
1.观察数据结果
View——watch window
我们输入buffer,显示此数组的值,如图D所示。
2.观察数据图形
View——Graph——Time Frequency
可以加入断点,然后按Animate 动态显示。
分别观察语音输入(存放在变量data中)波形,压缩后波形(temp1),解压后输出波形(data1),图形分别为A,B,C。
3.观察存储器中数据
View——Memory
经过多次调试,使录音时间在保持音质的前提下达到5-6秒。
13
A.输入波形
(data)
B.压缩后波形
(temp1)
C.解压缩后输出波形(data1)
14
D.watch window中的值
五.实验总结
经历了这次实验,我想我们充分认识到了压力带给人的巨大动力。确定做语音压缩,存
15
储与回放实验后,查了大量A律,u律压缩方法的资料,并学习了CCS环境下的操作、调试与运行。由于之前对DSP很不了解,这方面学习我们去实验室操作了不少时间,终于熟悉了它的操作步骤与简单调试方法。
然后具体针对语音压缩回放实验,我们经历了比较大的挫折。首先借了板子后怎么都不能和计算机连接,换了好几台电脑都不行,换了板子后终于可以了,但问题是语音不能录入,很疑惑,编译下载都没问题,最后发现是耳机的问题。终于能录入后,我们很激动,接下来就是观察波形与数据了。分别看了输入与经压缩后波形,发现没什么区别,而且有连续整段波形全在横轴上方或下方,经分析后我们修改了显示点数,将256改为128,则波形变疏,同时也能看出压缩后的变化,只是不太明显,说明A律压缩不会有很大失真,效果较好。对比输入输出波形,发现很相近,达到了实验要求。至于为何有连续整段波形全在横轴上方或下方,我想是因为没有信号输入导致,但具体还有待自己去查找资料。
在调试过程中我们还学习了断点的设置与利用,还有在watch window 中对输入变量data 设置不同的值可分析波形的变化情况,同时还可查看buffer 中存储内容。
我们自己试验的扩展部分是将录音与放音时间调长,经改大buffer容量,并相应加大i, j 的循环次数则可。
由于时间比较紧,我们只是查了u律压缩的实现方法,没有将其与A律压缩做具体比较,觉得比较遗憾。我觉得这是挺有意思的实验,让我自己去摸索验证理论性的东西。
王颖 通信0607 06282022
通过这次的DSP实验,让我充分认识到了我的不足。我虽然学习过C语言的编程,但一旦将它应用到实际的比较专业的问题上,我所学的知识就变得完全不够用了,为了这次实验我和我的搭档查询了大量的资料,无论是C语言方面的还是压缩算法方面的都让我们花费了很多时间,查询到了需要的资料我们又对它们进行了整理,提取出我们需要的a律压缩和μ律压缩的算法和原理,并且努力去理解其中的知识,使其能为我们所用,尽管如此我们仍然没能成功的编出属于我们自己的程序,因此我们找到了一份编好的程序,用我们已经理解的知识去分析它,理解这份程序的原理,并根据我们的实际对它进行一些修改,从而我们才得出我们现在的程序。虽然程序编写成功了但我们在调试上又遇到了很多的问题,比如在CCS环境下我们要如何观察波形,波形有代表什么等等,为此我们又不断的翻阅课件和资料,才最终解决了所有的问题,我们的实验才算基本完成了。
整个实验过程中,我在不断的查阅资料和开动脑筋过程中,锻炼了我的资料采集能力,也锻炼了我的分析整理能力,为以后作毕业设计打下了基础;同样在实验过程中我遇到很多困难,而这些困难则锻炼了我的耐心和分析解决问题的能力;通过编程锻炼了我的编程能力,使我对C语言,汇编语言和MATLAB语言更加熟悉,让我能更好的运用它们;而且两人一组的实验,也锻炼了我们的团结协作能力,为将来走入社会能更好的工作打下了基础。
总之通过这次的实验,让我认识到了自己的不足,同时又通过这次的实验让我们学到了让我们受用一生的知识。
王燕 自动化0601 06212016
六.参考资料:[1] 高海林 钱满义 DSP技术及其应用 自编讲义,2005
[2] 谭浩强 C程序设计(第二版)清华大学出版社,2003
[3]李颖 李文海 现代通信技术 人民邮电出版社 2002.3
[4]赵勇、甘泉 DSP应用系统设计 电子工业出版社 2002.10
16
实验报告二实验名称低碳钢和铸铁的压缩实验实验目的1测定在压缩时低碳钢的屈服极限S及铸铁的强度限b2观察它们的破坏现象并比较这两种材…
材料的拉伸压缩实验一实验目的1观察试件受力和变形之间的相互关系2观察低碳钢在拉伸过程中表现出的弹性屈服强化颈缩断裂等物理现象观察铸…
上海建桥学院实验报告实验项目名称静态图像和动态图像压缩实验类型验证型同组学生姓名项冠芳指导老师李玮莹实验地点7514实验日期20x…
材料的拉伸压缩实验一实验目的1观察试件受力和变形之间的相互关系2观察低碳钢在拉伸过程中表现出的弹性屈服强化颈缩断裂等物理现象观察铸…
实验三压缩实验一实验目的1测定压缩时低碳钢的屈服极限s和铸铁的强度极限b2观察低碳钢和铸铁压缩时的变形和破坏现象并进行比较和分析原…
姓名报名编号学习中心层次专业实验名称土的压缩试验一实验目的通过土的压缩实验得到试样在侧限与轴向排水条件下的孔隙比和压力的关系即压缩…
水利工程学院土力学综合实训报告专业水利水电建筑工程组员常帅陈龙波史行行王迪赵牛牛赵文明第五小组1前言土力学基础试验是土木工程专业的…
南华大学城市建设学院土力学实验报告20xx0521实验一土的重度密度含水率试验实验名称土的重度含水量实验实验成绩实验同组人田小旭易…
姓名陈炳余报名编号C06576xxxxxxxxxxxx0015学习中心浙江玉环奥鹏学习中心层次专升本专业大连理工土木工程实验一水泥…
实验一拉伸实验一实验目的1测定低碳钢Q235的屈服点s强度极限b延伸率断面收缩率2测定铸铁的强度极限b3观察低碳钢拉伸过程中的各种…
《汉语语音》多媒体软件科学总结报告一、软件开发的背景和目的计算机应用事业的迅速发展,给语言教学带来了全新的观念、开辟了新的思路。多…