变速不变调播放mp3-QT-QAudioOutput-lame-sonic

5 篇文章 0 订阅

基本原理

    有两种变速播放方法,一种是改变playback的频率,例如22050hz采样用44100hz播放。但是这种方法会导致声音快的同时,声调也相应变化,会比较难以接受。
第二种方法,也是本次讨论中用到的办法,通过某种方法(未深究其原理)重新计算声音采样,使其语速变快变慢而音调不变。

变速解决方案

    大概搜索了一下变速的库和方法,基于一篇经验分享选择了sonic。算法只有一对c和h文件,使用极其简单,据分享说效果不错不会破音
    git地址:https://github.com/waywardgeek/sonic.git
有ndk版的,主要区别是有jni,但是.c文件直接可以在安卓环境编译,用QT正合适

变速播放器

    有变速解决方案后,接下来需要一个能支持变速的播放方案。原来是QMediaPlayer可以很省心的播放,但是由于没有暴露音频解码和像播放设备喂数据的过程,没办法拿到数据喂给sonic进行变速处理。
    经过一番查找阅读确认,必须自己调用mp3lame解码,然后sonic处理,然后自行喂数据给QAudioOutput
    由于做录音热插拔支持的时候,用mp3lame编码,喂空白数据等行为,对mp3lame有一定的了解,做解码也比较顺利。而QAudioOutput没有用过,问题主要出在format的填写,对SampleRate、ChannelCount、SamepleSize的理解不够。
    SampleRate是采样频率,44100hz、32000hz、16000hz、8000hz等,代表每秒要采这么多份数据;
    SampleSize是采样数据的大小。采样数据的格式不同,有8bit的16bit的,有int也有float的。但是最常见的还是16bit的,那么SampleSize就是2.因为sizeof(short)=16
    ChannelCount是声道数量。如果双声道,那么一份数据里面有两个采样挨在一起,比如双通道44100hz的话,一秒钟有44100 x 2个 x 2字节每个 = 88200字节。
至于mp3
    lame里面的bitrate,是一毫秒对应的数据量,以bit为单位。单通道32000hz的话,bitrate=8bit每字节 x 1个通道 x 2字节每份 x 32份每毫秒 = 512
    lame里面的framesize,固定是1152或者576或者384,取决于mpeg版本和layer。流行的mp3是mpeg2的layer3,是1152。注意这里的1152单位是采样份数,代表mp3一帧里面包含的采样份数
    lame里面的nsamp是总的采样份数,只在VBR模式的mp3文件解码时赋值,totalframes是总的帧数。
    如果只是播放的话,以上不用操心太多,控制QAudioOutput缺数据前就驱动decoder解码获取待播放sample给sonic最后送往QAudioOutput,即可播放。
    但是作为一个取代QMediaPlayer播放的播放器功能,一个不可避免的问题是要支持播放位置的随机拖拽跳转。

拖拽随机播放

这个问题的难度来了。难题主要是:

  1. lame作为编解码器,理论上不需要支持解码位置的随机变更,所以它没有直接的支持
  2. mp3编码有VBR模式,提供变化的编码频率以节约某些片段的采样输出大小,很常见必须支持

研究过程中的一些情况

  1. lame是开源库,有部分作者为随机播放增加了几个不痛不痒的函数做辅助(仍然没有直接支持)
  2. 了解到一个libzplay及其解码的libmad好像能支持seek,却也在源码中看到了lame,一番小研究后放弃了
  3. 认为seek需要准确定位到mp3的frame头,通过其他讨论seek的文章,以及一段比较权威的代码发现,其实seek到的位置很草率
  4. 将seek方法尝试性用到了非vbr编码的mp3文件,居然可以用
  5. 强行让自己分析了lame的代码,原来它会根据mp3 frame头的特征,游走寻找它认为的frame头。(跳转前需要清除大部分状态和中间数据,这种情况下会触发它游走找头)
  6. vbr编码seek需要暴露toc信息,修改了lame库的代码。(check_vbr_header函数和mpstr_tag)
  7. 用vbr编码的mp3继续试验却大概率崩溃或异常音响,异常点有很多,和特定mp3的多个特定时间位置有关
  8. 经过分析后得知是seek后,游走找mp3 frame头的过程,误将其他数据当做了mp3头导致。
  9. 由于mp3 frame头的特征不太显著,代码用排除法判断是否帧头。但是由于跳转前清掉了layer=3的信息,导致未验证layer。1. 由于我们只考虑mp3,所以在head_check中固定要求验证layer=3
  10. 以上基本是主要的难点解决,代码中参考了lame自带的get_audio代码,主要抄袭文件头文件尾的相关处理。理论上我们已 经可以为lame贡献代码增加seek功能啦。

没代码说个屁啊

上传了代码 https://download.csdn.net/download/jinzeyu_cn/10312011 使用qt5.9.1可以直接打开编译运行
在windows、Android、Mac、IOS均测试通过。下载是为了在手机上测试方便,填入本地路径可以免去下载过程只测试播放。

这里写图片描述
这里写图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值