音频特效实践
Posted blueberry_mu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了音频特效实践相关的知识,希望对你有一定的参考价值。
音频特效
基础概念
波形图
波形图横轴是时间,纵轴为强度。
频谱图
在波形图中选取一段时间的音频,快速傅立叶变换(FFT)可以得到频谱图。
频谱图的横轴为频率,纵轴为强度。
语谱图
语谱图横轴为时间,纵轴为频率,颜色表示强度。
它是由STFT(短时傅里叶变换)得到。可以理解它是将时域的数据按固定的窗口分段进行FFT变换,然后叠加在一起。
可以把它理解为一个三位图形,但是用二维的形式呈现给了用户,即x轴为时间,y轴为频率,z轴为强度。
以上图形是使用音频处理软件Audacity得到。
傅立叶变换
- 傅立叶变换可以将音频的时域数据转为频域数据。
- 数字信号则使用DFT(离散傅立叶变换)来处理。
- 但是因为DFT的计算量较大,实现一般会用FFT(快速傅立叶变换)。
- 快速傅立叶变换的实现。
- https://rosettacode.org/wiki/Fast_Fourier_transform#C.2B.2B
- 一个android实现:https://github.com/paramsen/noise
效果器
均衡器(Equalizer)
均衡器可以提高/降低一段音频信号中某段频率的强度。比如我想比较喜欢一首歌的低频部分。我想将它的低频部分强度放大。则可以通过均衡器来调节低频区域的强度。
它的原理是将音频从时域进行FFT转换为频域,然后根据用户选择的中心频率,调节对应频率的强度,之后再进行FFT逆变换回时域信号。
软件中也会内置一些均衡器。
压缩效果器(Compressor)
压缩效果器,是指在时域上对声音强度所进行的一个处理。当音频的音量剧增的时候,会自动将音量调小一点。
- 阈值,当声音强度大于这个值时,之后的声音没增加 x 强度,则输出的强度只会增加 x / 比率。 比如压缩比为2:1 ,没当声音增加 2,经过压缩则只增加 1.
- 比率,压缩的比率。
- 建立时间,用于决定压缩器在超过阈值多久后触发压缩器来工作。
- 释放时间,当声音低于阈值多长时间后,触发器停止工作。
混响效果器(Reverb)
实际生活中人听到的声音很多都是经过多次反射进入人耳的。你在一间教室,一个山谷、或者其他场所听到的声音都会有所不同。混响效果就是模拟这种环境。
效果器的实现
sox
- pc上的编译及使用
- 下载源码
git clone git://git.code.sf.net/p/sox/code sox
- 配置
autoreconf -i
编写sh脚本
#!/bin/bash
CWD=`pwd`
LOCAL=$CWD
./configure \\
--prefix="$LOCAL/pc_lib" \\
--enable-static \\
--disable-shared \\
--disable-openmp \\
--without-libltdl \\
--without-coreaudio
- 运行shell脚本
- 构建及安装
make && make install
- 使用sox
构建完成之后可以在pc_lib目下下找到可执行文件sox.
如执行均衡器:
sox audio.wav audio_output.wav equalizer 100.1 1.5q 3 equalizer 120 2.0q -19
上面的命令可以将audio.wav以100.1 位中心频率,宽为1.5q,强度提升3. 以及 120为中心频率宽度为 2.0q,强度降低19.
宽度为 $xq 表示的频宽。根据q可以计算出一个O(八度),一个O体现在频率上是2倍的频率。比如一个音调比另一个音调高一个八度,在频率上就是这个音调是另一个音调的2倍。
- android上使用sox。
- 交叉编译
- 配置文件
#!/bin/bash
CWD=`pwd`
LOCAL=$CWD
NDK=~/Library/Android/sdk/ndk/22.1.7171670
NDK_SYSROOT=$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
export TARGET=armv7a-linux-androideabi
# Set this to your minSdkVersion.
export API=28
# Configure and build.
export AR=$TOOLCHAIN/bin/llvm-ar
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export AS=$CC
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip
./configure \\
--prefix="$LOCAL/android_lib" \\
--host $TARGET \\
--with-pic \\
--disable-openmp \\
--without-libltdl
make
- cmake 配置
cmake_minimum_required(VERSION 3.18)
project(audioeffect)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(PREBUILT_DIR $CMAKE_SOURCE_DIR/../../../prebuilt/armv7a)
message(STATUS "include $PREBUILT_DIR/include")
file(GLOB SOURCES "*.cpp" "*.h" )
add_library(audioeffect SHARED
$SOURCES
)
add_library(sox STATIC IMPORTED GLOBAL)
set_target_properties(sox PROPERTIES IMPORTED_LOCATION $PREBUILT_DIR/lib/libsox.a)
set_target_properties(sox PROPERTIES INTERFACE_INCLUDE_DIRECTORIES $PREBUILT_DIR/include)
target_link_libraries(audioeffect PRIVATE log PRIVATE sox)
- 实现一个均衡器
#include "Equalizer.h"
#include "logger.h"
#include <utility>
#include <fstream>
void Equalizer::setInput(string input)
logi("%s,%s", "setInput", input.c_str());
input_ = std::move(input);
void Equalizer::setOutput(string output)
logi("%s,%s", "setOutput", output.c_str());
output_ = std::move(output);
void Equalizer::start()
assert(sox_init() == SOX_SUCCESS);
in_ = sox_open_read(input_.c_str(), nullptr, nullptr, nullptr);
out_ = sox_open_write(output_.c_str(), &in_->signal, nullptr, nullptr, nullptr, nullptr);
chain_ = sox_create_effects_chain(&in_->encoding, &out_->encoding);
// 输入
sox_effect_t *e = sox_create_effect(sox_find_effect("input"));
char *args[10];
args[0] = (char *) in_;
logi("in paths is %s:", args[0]);
int result = sox_effect_options(e, 1, args);
logi("effect input result is %d", result);
assert(result == SOX_SUCCESS);
sox_add_effect(chain_, e, &in_->signal, &in_->signal);
free(e);
// 均衡器
e = sox_create_effect(sox_find_effect("equalizer"));
char *frequency = "300";
char *bandwidth = "1.25q";
char *gain = "3";
char *equalizer_chars[] = frequency, bandwidth, gain;
result = sox_effect_options(e, 3, equalizer_chars);
if (result != SOX_SUCCESS)
logi("option equalizer failed : error code is %d", result);
assert(result == SOX_SUCCESS);
sox_add_effect(chain_, e, &in_->signal, &out_->signal);
free(e);
e = sox_create_effect(sox_find_effect("equalizer"));
frequency = "300";
bandwidth = "1.25q";
gain = "30";
equalizer_chars[0] = frequency;
equalizer_chars[1] = bandwidth;
equalizer_chars[2] = gain;
result = sox_effect_options(e, 3, equalizer_chars);
if (result != SOX_SUCCESS)
logi("option equalizer failed : error code is %d", result);
assert(result == SOX_SUCCESS);
sox_add_effect(chain_, e, &in_->signal, &out_->signal);
free(e);
// 输出
e = sox_create_effect(sox_find_effect("output"));
args[0] = (char *) out_;
result = sox_effect_options(e, 1, args);
if (result != SOX_SUCCESS)
logi("option output effect failed : error code is %d", result);
assert(result == SOX_SUCCESS);
sox_add_effect(chain_, e, &out_->signal, &out_->signal);
free(e);
sox_flow_effects(chain_, nullptr, nullptr);
sox_delete_effects_chain(chain_);
sox_close(out_);
sox_close(in_);
sox_quit();
sox的设计是一个责任链。使用时,先创建出一个chain,然后创建出需要的effect添加到chain中。上述的例子中为模式为:
input ----> equalizer1 -------> equalizer2 ----------> output
最终可以对比处理过后的音频和原音频的差异。
我这个例子中原音频的频谱为:
处理后为:
遇到的一些问题记录
- 编译器不支持Rewind-Pipe,根据注释提示,删掉#error FIX NEEDED HERE就行
formats.c:425:4: error: FIX NEEDED HERE
#error FIX NEEDED HERE
^
6 warnings and 1 error generated.
make[1]: *** [libsox_la-formats.lo] Error 1
改法:
#elif defined _MSC_VER || defined _WIN32 || defined _WIN64 || \\
defined _ISO_STDIO_ISO_H || defined __sgi
fp->_ptr = fp->_base;
#else
/* To fix this #error, either simply remove the #error line and live without
* file-type detection with pipes, or add support for your compiler in the
* lines above. Test with cat monkey.wav | ./sox --info - */
// #error FIX NEEDED HERE
#define NO_REWIND_PIPE
(void)fp;
#endif
- 找不到glob、globfree符号。这2个文件是在api28之后引入的,所有需要讲android api 调整为28及以上
copying selected object files to avoid basename conflicts...
CCLD sox
ld: error: undefined symbol: glob
>>> referenced by sox.c:2606
>>> sox.o:(main)
ld: error: undefined symbol: globfree
>>> referenced by sox.c:2612
>>> sox.o:(main)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[1]: *** [sox] Error 1
make: *** [all-recursive] Error 1
参考
https://zhuanlan.zhihu.com/p/19763358
http://sox.sourceforge.net/
https://github.com/paramsen/noise
https://howtoeq.wordpress.com/2010/10/07/q-factor-and-bandwidth-in-eq-what-it-all-means/
https://developer.android.com/ndk/guides/other_build_systems
以上是关于音频特效实践的主要内容,如果未能解决你的问题,请参考以下文章