【视音频编程学习】开源音频加速方案Sonic、SoundTouch对比及实时处理音频的实验分析

语音变速不变调是指保持音调和语义保持不变,语速变快或变慢。该过程表现为语谱图在时间轴上如手风琴般压缩或者扩展。那也就是说,基频值几乎不变,对应于音调不变;整个时间过程被压缩或者扩展,声门周期的数目减小或者增加,即声道运动速率发生改变,语速也随之变化。

在初步调查后,有两种方案可以实现变速不变调的功能:sonic, sountTouch,两个方案都是用来处理pcm音频文件,支持wav格式,适用于解码后的pcm数据处理。

方案一:sonic

  1. 处理后文件大小跟倍速值成反比。
  2. sonic处理单位为帧,存在丢失部分数据的可能,推测是算法内部导致的。
  3. 引入比较简单,主要函数为c语言编写,只需要引入一个c文件和.h头文件就可以使用。
  4. 加速后的音质比soundTouch更差。

Sonic实时处理音频流

sonic定义了一个stream流,首先创建一个stream,输入采样率和声道数。然后设置流的相关参数(speed, pitch, rate)。如果要采用实时的方式来处理音频流,便使用sonicWriteToStream()sonicReadShortFromStream()两个函数来输入输出。

问题:

  1. 音质损耗问题,通过实验可以发现本来放入2048字节的数据,加速2倍后应该为1024字节的数据,但是实际取出可能为480,甚至1380,数据不均等。

refrence:

  1. sonic源码分析

方案二:SoundTouch

  1. 处理后的文件大小跟倍速值成反比。
  2. 可以处理实时的音频数据但是输入/输出延时约为100ms;
  3. 引入比sonic更加复杂,需要引入库进行使用。
  4. 加速后的音质基本没有损耗,从听感上也比sonic的效果更好一些。

SoundTouch实时处理音频流

ST对音频的处理是输入函数putSamples()与输出函数receiveSamples()。实时处理音频流的思路就是,循环读取音频数据段,放入ST进行输出,输出处理后的数据段用于播放。

放入soundTouch的缓冲的数据需要经过处理后才能取出来,对于不同的sequence_ms、overLap_ms这个时间的长短不一致,但是要注意第一次进入soundTouch的数据会有一个判断,在TDStretch中截留的一部分数据。

SoundTouch:feature: Capable of real-time audio stream processing:

  • input/output latency max. ~ 100 ms.
  • Processing 44.1kHz/16bit stereo sound in realtime requires a 400 Mhz Intel Pentium processor or better.

How to use SoundTouch for realtime audio processing?

Create a processing function that is called by realtime system once realtime input samples are available, so that this function puts realtime input samples into SoundTouch pipeline with ‘putSamples’ function call, and use ‘receiveSamples’ function for extracting resulting output samples for realtime output processing.

问题:

  1. 重要的问题是并不是在取包间隔下每次取都能取到包,有一定的缓冲,它的已加速音频数据呈现间断性,如:0, 2048, 0, 2048, 0 … 这样的情况下在取已经加速的数据时可能得不到一一对应。
  2. 在修改soundTouch的采样率为输入音频的一半时,间断性消失,每次取出的数据呈现为:1024,1024,1024,1024,1024,… 并且声调无变化。
  3. 比较短的几个帧的加速可能需要进行平滑处理才会有更好的效果。

ref:

  1. SoundTouch Sound Processing Library (surina.net)

  2. owoudenberg/soundtouch.net: The SoundTouch Library is originally written by Olli Parviainen in C++. Although a .NET wrapper library is available, this library aims to be a complete rewrite in C#. (github.com)

  3. SoundTouch官网

思考:

  1. 是否可以对抖动和延迟进行判断,如果是在抖动延迟比较大时才使用音频加速,其他情况便不使用。
  2. soundTouch设置一半采样率的方式是否可行?还需要进一步测试。
  3. 是否需要寻找其他可行的方案,因为一一对应关系应该是一个硬性的指标

怎么确认放进去的数据和取出来的是对应的数据?

  • 通过计数来进行放入空数据进行加速,在当前计数值获取加速的音频同时将其写入文件,观察文件中的音频是否为空(表现为静音)

  • 结果:

    • 经过测试sonic的每次加速后的大小不一致,数据不一致的情况下做不到加速数据与原数据对应。
      • soundtouch每次加速后的数据大小稳定,但是存在间隔,经过观察和测试得间隔为一个包。通过相应的调整,使得soundTouch的数据对应,但是存在一个问题,在加速音频的前后的位置没有融合,而导致的杂音问题应该如何解决。
  • Note:

    调整SoundTouch的settings,可以在soundtouch的头文件SoundTouch.h中找到设置项,在官方网站上也对soundTouch的一些parameter进行了分析和描述。因为修改tempo只需要使用TimeStretch,所以可以根据需要修改sequence_ms、seekWindow_ms、overLap_ms来实现自己的需求,timeStretch本身也有一些参数可以修改,可以实现样本点精度。

缓冲完成的标志是什么?

  • 如果已经加速的数据不为空说明缓冲已经完成,可以取出数据。

补充:

变速不变调算法:

时域法 频域法 参量法
剪贴法 LSEE-MSTFTM 相位声码器
SOLA、SOLA-FS 正弦模型
TD-PSOLA
WSOLA

为了减小基音断裂和相位不连续问题,VerhelstRoelands 提出了波形相似叠加法(WSOLA),该方法计算量低于PSOLA,同时输出的语音质量高。Grofit对该方法进行了改进,使其也适用于音乐信号的变速处理。

SoundTouch使用的是WSOLA算法

结论:

由于sonic的数据不等问题,不适合于实时的加速情景,考虑到soundTouch会在缓冲中对所有帧进行波形相似叠加算法,取出的数据的头部会参考上一个数据的尾部进行叠加,这样在正常音频与加速音频交错的情况下就会出现基音断裂的情况。于是对其设置了三种方案如下:

  1. 只将要加速的音频帧放入soundTouch,取出后与正常音频一起放入播放队列中,这样会有基音断裂的现象

  2. 将所有音频放入soundTouch并加速,只取出需要的数据,还是存在基音断裂的现象,因为需要的数据参考的也是加速后的音频进行的融合,而写入的还是原文件,所以还是需要使用平滑处理。

  3. 将所有的音频放入soundTouch对需要的加速,不需要的tempo设置为1,基音断裂的现象消失,但是因为频繁修改tempo可能会降低音质。

方案1波形图:

请添加图片描述

方案1频谱频率:(如图所示中明显的竖条便是基音断裂的地方)

请添加图片描述

方案2波形图:

请添加图片描述

方案2频谱频率:

请添加图片描述

方案3波形图:

请添加图片描述

方案3频谱频率:

请添加图片描述

补充:

soundtouch使用的是TDStretch来进行变速,在TDStretch中可以看到在初始化时会有一个判断,将第一次输入soundtouch的数据的后面一部分(具体多少根据设置的tempo等参数决定)作为midBuffer,所以第一次输入时输出的样本点个数不等于期望的输出样本点个数,于是midBuffer作为后面一次输出的前半部分以此类推,修改了sequence_ms、seekwindow_ms、overlap_ms等参数后只能将midBuffer的大小减小,依然不能做到随放随取。

考虑改变tempo的方式是将所有的音频都经过soundTouch,正常的音频tempo设置为1,加速的音频设置为2。输出的时候因为刚开始的放进去1024个样本点,期望取出512个样本点,但是只取出了320个样本点,所以有192个样本点还在加速的缓存当中要下一次才能取出(并且会和下一次输入的数据切片进行波形相似算法叠加),如果刚开始是正常的音频,那么经过了soundTouch的正常音频的末尾便会缺失192个样本点,在加速音频的头部便会有这192个样本点,因为数据之间保持了连续性所以就不会出现基音断裂。但是在同一时刻放进去的数据和取出来的数据不是一一对应的关系,做不到随放随取,只能保证数据前后的连续。

猜你喜欢

转载自blog.csdn.net/Daibvly/article/details/121284226