文章目录

  • 前言
    • 视频变速
    • 音频变速
      • TSM算法(时域压扩算法)
        • OLA(Overlap-and-Add)
        • WSOLA(Waveform Similarity Overlap-Add)
  • 编译
  • 使用
  • 参考

前言

视频变速

视频比较简单,直接控制视频帧的显示时间就行了,减速就延长显示时间,加速就缩减显示时间。问题就是如果加速过快解码速度跟不上的话就会出现音视频不同步(这个貌似无解,除非限制速度强制音频同步视频,毕竟机器性能是有极限的[大笑]。顺便吐槽一下qq浏览器的视频加速到不同步后好像就恢复不过来了,只能seek同步一下。)

音频变速

音频的话就麻烦了一点,我曾天真的以为固定播放器的采样率,改变音频的采样率就能实现音频变速。变速是实现变速了,但是想象一下一个直径一米的半圆,y轴不变,x轴乘二,他在一米里面会是怎样的,在两米里又是怎样的。可见单纯的改变采样率会导致音调发生改变,失去声音原来的形状。
下面简单介绍下相关算法(个人理解),想具体深入了解的另行查找资料(建议认真读一下第一个参考链接的论文),另外如果有错漏还望指出。

TSM算法(时域压扩算法)

变速不变调的经典算法叫TSM(Time-Scale Modifacaiton)
主要流程是分帧,合帧。分帧一来是无法处理无限长信号,二来是音频在短时域上是周期性的。帧长度一般为20ms~50ms。分帧操作中帧与帧之间会有重叠(overlap),也就是每一帧的起点间隔不等于帧长度(两帧的起点间隔称为帧移,比如重叠75%,那帧移就是帧长度的25%)。合帧就是把分出来的帧重新连接还原成连续信号,而TSM的基本原理就是固定合帧时的重叠比例(一般是50%或者75%),改变分帧时的重叠比例从而实现时域压扩。如下图。

但是会造成一个问题就是两帧连接处波形不连续(基音断裂),如下图。

OLA(Overlap-and-Add)

OLA(Overlap-and-Add, OLA)重叠相加算是音频变速算法中最简单的时域方法,它是后续时域算法(SOLA, SOLA-FS, TD-PSOLA, WSOLA)的基础。OLA算法通过加窗解决波形不连续问题。
加窗可以让一帧信号的幅度在两端渐变到 0,获得周期性信号,减轻频谱泄漏。加窗有矩形窗,汉宁窗(Hanning),海明窗(Hamming)等,一般应用海明窗。一段信号经过海明窗处理如下。

加窗就是信号与一个「窗函数」相乘,从上图可以看到信号两边渐变到0,呈现周期性,方便傅里叶变换。

OLA流程如上图,x是原信号,y是处理(加速)后的信号,XmX_{m}Xm​是第一帧,红色w是窗函数,图(b)就是第一帧加窗,Xm+1X_{m+1}Xm+1​就是第二帧,HaH_{a}Ha​就是分帧的帧移,图(d)就是第二帧加窗然后与第一帧叠加,HSH_{S}HS​就是合帧的帧移。两帧之间的信号被忽略掉了,从而实现了加速。OLA算法通过加窗然后重叠部分相加解决了波形不连续的问题,但是无法保证每一个帧都能覆盖完整周期并保证其相位对齐,这种失真也叫相位跳跃失真(phase jump artifacts),如下图,原本周期性的信号变得不周期了。

WSOLA(Waveform Similarity Overlap-Add)

相似帧叠加算法,是OLA算法的改进版,不是直接暴力叠加帧,而是在一定范围内寻找最相似的帧叠加。

如上图,蓝色虚线框是正常不变速处理时的第二个分帧,Xm+1X_{m+1}Xm+1​就是压扩(加速)处理时第二帧,蓝色实线框就是范围,在这个范围内找与蓝色虚线框最相似的一帧,图(c)中x信号上的第二个红色实线框就是在范围内找到的最相似帧。图(d)就是相似帧加窗然后与第一帧重叠相加。
WSOLA通过寻找相似帧解决了信号失真问题,WSOLA处理结果如下图,减速后的信号与原信号一致。

WSOLA的应用主要有两个开源库,一个是sonic(使用定位基音周期法寻找相似帧),一个是soundTouch(使用寻找相关峰法寻找相似帧)。sonic在处理纯人声时效果比较好,soundTouch在处理环境等综合性声音时效果比较好,不是特殊要求的人声处理一般采用soundTouch。
算法的介绍到此为止。
顺口提一句音频变调就是通过改变重采样实现变调变速,然后再通过上面的变速算法恢复速度。

编译

源码地址
我编译的时候是基于2.3.1版本的。下载源码下来后把source/SoundTouch目录下的所有文件复制到工程的cpp/soundtouch目录下,如下

将include文件夹下的所有文件复制到工程的include/soundtouch目录下,如下

然后将上面两步复制过来的源文件和头文件配置进CMakeLists.txt里,关键部分示例如下

aux_source_directory(. DIR_SRC)
aux_source_directory(./soundtouch DIR_SRC_SOUND_TOUCH)
add_library(native-libSHARED${DIR_SRC} ${DIR_SRC_SOUND_TOUCH})set(DIR ${CMAKE_SOURCE_DIR}/../../../libs)
include_directories(${DIR}/include/soundtouch)

完成上面的配置后我们就能在工程里面使用soundTouch库了。

使用

官方有个demo在source/Android-lib目录下,具体用法可以参考其中的soundtouch-jni.cpp。大体的步骤如下。
1.初始化对象

    soundtouch::SoundTouch *soundtouch = new soundtouch::SoundTouch();//输入音频数据的采样率soundtouch->setSampleRate(OUT_SAMPLE_RATE);//输入音频的通道数soundtouch->setChannels(OUT_CHANNEL_NUMBER);

2.配置,下面的这些配置是可以在输入输出数据时更改的即时生效。

//变调
//    soundtouch->setPitch(playSpeed);
//变速变调
//    soundtouch->setRate(playSpeed);
//变速soundtouch->setTempo(speed);

3.输入输出数据

         //ffmpeg的音频转化(比如重采样)int outSample = swr_convert(audioSwrContext, &audioBuffer, 4096 * 2,(const uint8_t **) (audioFrame->data),audioFrame->nb_samples);int size = 0;//外层其实有个While循环不停的读取audioFramesoundtouch->putSamples(reinterpret_cast<const soundtouch::SAMPLETYPE *>(audioBuffer),outSample);int soundOutSample = 0;do {soundOutSample = soundtouch->receiveSamples(reinterpret_cast<soundtouch::SAMPLETYPE *>(audioBuffer + size),OUT_SAMPLE_RATE / OUT_CHANNEL_NUMBER);size += soundOutSample * OUT_CHANNEL_NUMBER * BYTES_PER_SAMPLE;} while (soundOutSample != 0);if (size == 0) {av_frame_free(&audioFrame);continue;}

就是通过putSamples传入数据,receiveSamples读取数据。其实如果receiveSamples的第二个参数足够大,并不需要do-while循环。需要注意的是soundtouch::SAMPLETYPE,代表声音信号的数据类型,有整形和浮点形,由SOUNDTOUCH_FLOAT_SAMPLES和SOUNDTOUCH_INTEGER_SAMPLES控制,具体查看STTypes.h。因为我输出的音频格式是ffmpeg的AV_SAMPLE_FMT_S16和我使用的soundTouch的SAMPLETYPE一致所以不需要转换数据,如果数据格式不一样可能(也许)需要转换,贴下ijkplayer的代码

         //将8位转16位for (int i = 0; i < (resampled_data_size / 2); i++){is->audio_new_buf[i] = (is->audio_buf1[i * 2] | (is->audio_buf1[i * 2 + 1] << 8));}

4.冲出残留数据。在音频流的最后需要冲出残留的缓存数据,我没有处理,贴下官方的示例代码

    pSoundTouch->flush();do{nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);outFile.write(sampleBuffer, nSamples * nChannels);} while (nSamples != 0);

参考

A Review of Time-Scale Modification of Music Signals
你真的懂语音特征背后的原理吗?
音频变速变调原理及 soundtouch 代码分析

音视频系列九 使用soundTouch实现音视频变速相关推荐

  1. Android音视频学习系列(九) — Android端实现rtmp推流

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  2. datagrid 重载本地数据_音视频系列3:使用ffmpeg + nginx搭建本地转发服务器

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/104893135,欢迎关注,点赞,评论. 前言 音视频系列: HectoorZ ...

  3. 音视频系列2:ffmpeg将H.264解码为RGB

    音视频系列2:ffmpeg将H.264解码为RGB 前言 源码 前言 喜大普奔,终于更新啦,上期说到,如何使用ffmpeg+rtmp进行拉流,不熟悉的小伙伴们,可以先看上一期.今天我们要实现的是使用f ...

  4. android 拍摄视频后返回九宫格,抖音九宫格不同视频怎么拍 九个不同的视频在一个画面播放...

    关于抖音怎么合拍这个问题,小编觉得这些类似抖音怎么无痕合拍?无缝视频合拍怎么弄?抖音合拍怎么弄三个画面?抖音视频上下互动怎么合拍等问题,都可以直接在抖音APP中实现痘印怎么合拍的问题,没有啥挑战性哦, ...

  5. ffmpeg rtmp 不清晰_音视频系列3:使用ffmpeg + nginx搭建本地转发服务器

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/104893135,欢迎关注,点赞,评论. 前言 音视频系列: HectoorZ ...

  6. ffmpeg rtmp 花屏_音视频系列6:ffmpeg多线程拉流

    本文与csdn博客同步:https://blog.csdn.net/Hanghang_/article/details/105302384,欢迎关注,点赞,评论. 前言 本篇博客是音视频系列的续集与改 ...

  7. 音视频系列--H264编解码总结

    一.概述 H264,通常也被称之为H264/AVC(或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC) 对摄像头采集的每一帧视频需要进行编码,由于视频中存在空间和时间的冗余,需要 ...

  8. 音视频系列---最强播放器推荐

    PotPlayer 地址:https://daumpotplayer.com/download/ 基础功能: Daum PotPlayer -完全免费的多功能多媒体播放器,具有内置编解码器和灵活的设置 ...

  9. 音视频面试必备:一文搞懂视频相关的基础概念

    1.引言 随着移动互联网的普及,实时音视频技术已经在越来越多的场景下发挥重要作用,已经不再局限于IM中的实时视频聊天.实时视频会议这种功能,在远程医疗.远程教育.智能家居等等场景也司空见惯. 虽然实时 ...

最新文章

  1. 三周第三次课(11月1日) 3.7 su命令 3.8 sudo命令 3.9 限制root远程登录
  2. 用ldap方式访问AD域的的错误解释
  3. ContentType 列表
  4. 条件随机场——时间序列(句子单词序列也算),其特征函数必须要考虑前一刻的数据...
  5. sq工程师是做什么的_算法工程师为什么成天做数据,都做哪些数据?
  6. st link v2引脚连接_ST-Link资料02_ST-Link固件介绍,及固件命名规则
  7. vxlan 资料及其在 neutron中的应用
  8. 混合云架构,如何实现混合云落地?(上)
  9. 《从零开始学Swift》学习笔记(Day 45)——重写方法
  10. NLP 语料库 大全
  11. java中的displaytag类_java-如何在displaytag中以xlsx扩展名导出Excel文...
  12. 疯狂的程序员 71-80
  13. Dex.top“50计划”新玩法,能否让行业格局重新洗牌?
  14. C盘Windows XP,D盘Windows7,双系统安装纪录
  15. 华为平板电池损耗用什么软件测试,平板怎么看电池损耗
  16. PS人物换装--白色换纯色
  17. Sqlmap Tamper编写
  18. 为什么需要制定计划?
  19. MFRC53101TOFE小知识
  20. PAT--1051 复数乘法

热门文章

  1. python简单小游戏代码-零基础python教程-用Python设计你的第一个小游戏
  2. java如何写出简洁代码
  3. IOS UIView详解
  4. DT_MACHINE_START 板级信息初始化匹配调用机制实现
  5. 用 JS 原生方法实现 jQuery 的 append, prepend, before, after
  6. 嵌入式 linux yum命令详解
  7. android10桌面布局好看,让你的Android手机界面布局更好看
  8. 【特征检测】HOG特征算法
  9. Java开发插件及工具
  10. dev hdb2在linux中表示,Linux磁盘分区基础