GSM音频编码的优化和写入wav文件

GSM是voip中较为常见的一种编码,压缩率比很高,写到wav文件每秒只占用1.6k字节(接近于g729),是普通g711格式的五分之一,对录音来说可节省大量磁盘空间。生成的wav文件,可能是不牵涉到专利的原因,在各种操作系统下都能够播放。

1、使用IPP的codec

GSM的编解码通常使用开源C代码,入口文件是gsm_encode.c和gsm_decode.c,能用,但不够优化。

我使用Intel公司著名的IPP库,这个库经过高度优化,cpu占用很小。

实际使用中发现用IPP进行encode后rtp发给对方,对方软电话无声,解不出来。经过跟踪,IPP编码出来的33个字节,第1个字节的前半个字节为0,应该为gsm“魔术字”:0xd,加上这么几句即可:

IppEncode(... ..., outRawBuff);

unsigned char ch = outRawBuff[0];

ch = ch | 0xd0;

outRawBuff[0] = ch;

2、写wav文件头

关于gsm格式的wav文件格式,网上的文章很少,大多语焉不详。

其实掌握要这2个要点就好:

首先,voip一般都是单声道,8000采样率,这个是基本。

然后,GSM encode后的一帧数据是32.5个字节,wav文件当然不能存储半个字节,所以2帧组成一个数据块,即65字节,剩下的都是枝节了:

	... ...
	WAVEFORMATEX m_wfExt;
	int fmtLen = 20;  // fmt块长20字节
	memcpy(FileHeader.dwRiff, "RIFF", 4);
	memcpy(FileHeader.dwWave, "WAVE", 4);
	memcpy(FileHeader.dwFormat, "fmt ", 4);
	FileHeader.dwFormatLength = fmtLen;  // 20
	FileHeader.dwFileSize = contentLen + 20 + fmtLen;
	wavFp.write((char*)&FileHeader, 20);

	m_wfExt.nChannels = 1;  // 单声道
	m_wfExt.nSamplesPerSec = 8000;  // 采样率
	m_wfExt.wFormatTag = WAVE_FORMAT_GSM610;
	m_wfExt.wBitsPerSample = 0;  // 每个采样的位数,gsm编码显然无法凑成整数,所以填0
	m_wfExt.nBlockAlign = 65;  // 块对齐,即一个块的字节数:1个块就是2数据帧65字节
	m_wfExt.nAvgBytesPerSec = 1625; // 评价每秒的字节数,1秒有50个采样周期,2个采样周期为65字节,所以65*25=1625字节
	m_wfExt.cbSize = 2;  // 扩展字段长度,即占2个字节
	WORD wSamplesPerBlock = 320;  // 扩展字段:每个块的采样数,对于GSM来说两帧数据为1块,所以此处填320,即160x2

	char fmtBuff[300];
	memset(fmtBuff, 0, 300);
	memcpy(fmtBuff, &m_wfExt, 18);
	memcpy(fmtBuff + 18, &wSamplesPerBlock, 2);

	wavFp.write(fmtBuff, fmtLen);  // 写入fmt块

3、数据如何写入wav文件?

上面说了,用gsm codec编码或rtp收取到的一帧gsm数据是33字节,有效数据为32.5字节,第一个字节的前4位是没什么用的GSM“魔术字”0xd,但如何将两帧数据合并成65字节的块呢?

最容易想到的办法是将第一帧数据全部前移4bit,剩下半个字节,填充到下一帧的第一个字节,经试验证明不行,播放出来是噪音,后来读gsm源代码,才发现wav文件存储的字节高低位和普通我们认知的高低位是相反的。1个字节8位,按顺序0-7位,我们平常认为最左边是0bit,最右边是7bit,但wav存储的相反,最左边是最高位7bit,最右边是最低位0bit。

要把颠倒了的顺序重新颠倒过来,甚为复杂,此处就不贴代码了,详细可参考gsm的开源代码。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Python中写入WAV文件有多种方法。一种方法是使用soundfile库的`write()`函数。你可以通过导入soundfile库并使用`write()`函数来实现写入WAV文件。以下是一个示例代码: ```python import soundfile as sf # 将音频数据写入WAV文件 sf.write('output.wav', audio, sr, 'PCM_24') ``` 其中,`audio`是音频数据,`sr`是采样率。你可以根据需要修改文件名和参数。 另一种方法是使用librosa库。你可以使用`librosa.load()`函数读取WAV文件,然后使用其他方法将音频数据写入WAV文件。以下是一个示例代码: ```python import librosa # 读取WAV文件 audio, sr = librosa.load('record.wav', sr=44100) # 将音频数据写入WAV文件 librosa.output.write_wav('output.wav', audio, sr) ``` 请注意,如果你使用的是librosa库的0.8.0版本之后的版本,你需要将`librosa.output.write_wav()`函数替换为`soundfile.write()`函数,因为在0.8.0版本后,librosa库移除了`write_wav()`函数。 除此之外,还可以使用Wave模块的`wave.open()`函数和`writeframes()`方法来写入WAV文件。你可以参考Wave模块的官方文档来了解更多细节。 总结起来,你可以选择使用soundfile库的`write()`函数、librosa库的`output.write_wav()`函数(0.8.0版本之前),或者Wave模块的`wave.open()`函数和`writeframes()`方法来写入WAV文件。具体使用哪种方法取决于你的需求和项目环境。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【音频】python读取写入wav文件](https://blog.csdn.net/weixin_41932970/article/details/130713881)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [python:wave --- 读写WAV格式文件](https://blog.csdn.net/weixin_39145520/article/details/129404885)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值