VS语音信号处理(7) C语言调用SoundTouch进行变速不变调工程集成调试
前言
语音识别相关算法一般在MATLAB上进行仿真验证与实验,在工程上一般还是在VS中进行实现落地,本系列将介绍语音信号处理在C语言中的一系列应用,后期将以此为基础,再落地移植到嵌入式平台。
今天介绍SoundTouch语音变速不变调算法的工程应用。SoundTouch是一个支持音频倍速播放的开源库。支持变速(加速减速)、变调、变速同时变调等三类功能模块,能够对流媒体实时操作,也能对音频文件操作。采用 32 位浮点或者 16 位定点,支持单声道或者双声道,采样率范围为 8k~48k。
上一篇文章已经介绍了下载SoundTouch源码在VS2015平台进行调试测试的过程,由于自己过程中需要应用到变速不变调进行音频处理,源码直接编译处理,需要是三个工程,本文就介绍将源码的实例工程进行集成,这样就可以方便大家调用时,直接应用该集成工程进行自己的语音信号处理。话不多说,开干!
一. 源码下载即链接库准备
SoundTouch官网
SoundTouch地址:http://soundtouch.surina.net
SoundTouch 库适用于编写需要速度/音高控制功能的声音处理工具的应用程序开发人员,或者只是用于播放声音效果。SoundTouch 库源套件还包括一个示例实用程序 SoundStretch,用于从命令行界面处理 .wav 音频文件。
SoundTouch 库特点:
(1)开源实现;
(2)使用GNU C 编译器 (gcc)或Visual C++支持任何处理器和操作系统平台:Windows、Mac OS、Linux 和其他*nixes、Raspberry Pi、Android、Apple iOS等等;
(3)支持 16 位整数和 32 位浮点单声道/立体声/多声道音频格式;
(4)可以进行实时音频处理;
(5)最大输入/输出流延迟 ~ 100 毫秒;
(6)使用 133 Mhz Intel Pentium 处理器或更好的处理器可以实现 CD 质量立体声的真实处理;
(7)通过单个 C++ 类实现简单的编程接口;
(8)C++、C# 和 Java 示例应用程序;
(9)用于从 C#、Pascal/Delphi 和 Java 调用 SoundTouch 库例程的 API 接口模块;
(10)使用针对 x86 处理器的 MMX 和 SSE 指令集优化以及针对通用多核 CPU 的 OpenMP 优化的 C++ 实现。
点击官网上图中源码链接:https://codeberg.org/soundtouch/soundtouch
SoundTouch源码链接
进入源码网站,点击上图标记位置,进行下载
下载后解压到当前文件夹(下载文件为soundtouch-master.zip,解压为soundtouch)
根据调试成功的记录可知,所有的源码都在上述文件夹中,新建一个SoundTouchFile文件夹,将上述解压文件soundtouch中source目录SoundStretch文件中的以下选中文件复制到SoundTouchFile文件夹。
再将解压文件soundtouch中source目录SoundTouch文件中的以下选中文件复制到SoundTouchFile文件夹。
再将解压文件soundtouch中source目录SoundTouchDll文件中的以下文件复制到SoundTouchFile文件夹。
再将解压文件soundtouch中include目录中的以下文件复制到SoundTouchFile文件夹。
SoundTouchFile的文件夹如下图所示:
下面新建工程进行集成。
二. 工程编译准备
打开VS2015
点击新建一个项目(工程)
点击“确定”,创建项目
将SoundTouchFile文件夹复制到该工程目录下,如下图所示
左侧解决方案资源管理器视图中,头文件栏右键添加现有项,将SoundTouchFile中所有.h的头文件全部添加进来,如下图所示
左侧解决方案资源管理器视图中,源文件栏右键添加现有项,将SoundTouchFile中所有.cpp的源文件全部添加进来,如下图所示
选择编译平台为x64,点击“生成解决方案”
出现如下报错信息
1>------ 已启动全部重新生成: 项目: Project6, 配置: Debug x64 ------
1> WavFile.cpp
1>e:\project\vs\project6\project6\soundtouchfile\wavfile.cpp(175): error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1> c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\stdio.h(205): note: 参见“fopen”的声明
1>e:\project\vs\project6\project6\soundtouchfile\wavfile.cpp(709): error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1> c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\stdio.h(205): note: 参见“fopen”的声明
1> TDStretch.cpp
1> sse_optimized.cpp
1> SoundTouchDLL.cpp
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(86): error C2491: “soundtouch_createInstance”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(104): error C2491: “soundtouch_destroyInstance”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(117): error C2491: “soundtouch_getVersionString”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(125): error C2491: “soundtouch_getVersionString2”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(126): error C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1> c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\string.h(346): note: 参见“strncpy”的声明
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(133): error C2491: “soundtouch_getVersionId”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(140): error C2491: “soundtouch_setRate”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(151): error C2491: “soundtouch_setTempo”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(161): error C2491: “soundtouch_setRateChange”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(171): error C2491: “soundtouch_setTempoChange”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(181): error C2491: “soundtouch_setPitch”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(191): error C2491: “soundtouch_setPitchOctaves”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(201): error C2491: “soundtouch_setPitchSemiTones”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(211): error C2491: “soundtouch_setChannels”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(228): error C2491: “soundtouch_setSampleRate”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(251): error C2491: “soundtouch_flush”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(275): error C2491: “soundtouch_putSamples”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(298): error C2491: “soundtouch_putSamples_i16”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(329): error C2491: “soundtouch_clear”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(344): error C2491: “soundtouch_setSetting”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(358): error C2491: “soundtouch_getSetting”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(368): error C2491: “soundtouch_numUnprocessedSamples”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(385): error C2491: “soundtouch_receiveSamples”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(406): error C2491: “soundtouch_receiveSamples_i16”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(454): error C2491: “soundtouch_numSamples”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(464): error C2491: “soundtouch_isEmpty”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(473): error C2491: “bpm_createInstance”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(498): error C2491: “bpm_destroyInstance”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(513): error C2491: “bpm_putSamples”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(526): error C2491: “bpm_putSamples_i16”: 不允许 dllimport 函数 的定义
1>e:\project\vs\project6\project6\soundtouchfile\soundtouchdll.cpp(561): error C2491: “bpm_getBpm”: 不允许 dllimport 函数 的定义
1> SoundTouch.cpp
1> RunParameters.cpp
1> RateTransposer.cpp
1> PeakFinder.cpp
1> mmx_optimized.cpp
1> main.cpp
1> InterpolateShannon.cpp
1> InterpolateLinear.cpp
1> InterpolateCubic.cpp
1> FIRFilter.cpp
1> FIFOSampleBuffer.cpp
1> cpu_detect_x86.cpp
1> BPMDetect.cpp
1> AAFilter.cpp
1> 正在生成代码...
========== 全部重新生成: 成功 0 个,失败 1 个,跳过 0 个 ==========
根据报错提示可知宏定义未找到,进入项目属性页C/C++的预处理器定义中添加_CRT_SECURE_NO_WARNINGS与DLL_EXPORTS,
点击“确定”,重新生成解决方案
生成成功!将原压缩文件soundtouch中的lib文件夹复制到工程中,在项目属性VC++目录的库目录中添加lib文件夹如下
点击“确定”
点击“确定”,修改mian.cpp源码如下
///
/// DllTest.cpp : This is small app main routine used for testing sound processing
/// with SoundTouch.dll API
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
#include <string>
#include <iostream>
#include <fstream>
#include "SoundTouchFile/SoundTouchDLL.h"
#include "SoundTouchFile/WavFile.h"
using namespace std;
// DllTest main
int main(int argc, char *argv[])
{
// Check program arguments
/*if (argc < 4)
{
cout << "Too few arguments. Usage: DllTest [infile.wav] [outfile.wav] [sampletype]" << endl;
return -1;
}
const char *inFileName = argv[1];
const char *outFileName = argv[2];
string str_sampleType = argv[3];*/
const char *inFileName = "infile.wav";
const char *outFileName = "outfile2.wav";
string str_sampleType = "short";
bool floatSample;
if (str_sampleType.compare("float") == 0)
{
floatSample = true;
}
else if (str_sampleType.compare("short") == 0)
{
floatSample = false;
}
else
{
cerr << "Missing or invalid sampletype '" << str_sampleType << "'. Expected either short or float" << endl;
return -1;
}
try
{
// Open input & output WAV files
WavInFile inFile(inFileName);
int numChannels = inFile.getNumChannels();
int sampleRate = inFile.getSampleRate();
WavOutFile outFile(outFileName, sampleRate, inFile.getNumBits(), numChannels);
// Create SoundTouch DLL instance
HANDLE st = soundtouch_createInstance();
soundtouch_setChannels(st, numChannels);
soundtouch_setSampleRate(st, sampleRate);
//soundtouch_setPitchSemiTones(st, 2);
//soundtouch_setTempoChange(st, -50);
soundtouch_setTempo(st, 3);
cout << "processing with soundtouch.dll routines";
if (floatSample)
{
// Process file with SoundTouch.DLL float sample (default) API
float fbuffer[2048];
int nmax = 2048 / numChannels;
cout << " using float api ..." << endl;
while (inFile.eof() == false)
{
int n = inFile.read(fbuffer, nmax * numChannels) / numChannels;
soundtouch_putSamples(st, fbuffer, n);
do
{
n = soundtouch_receiveSamples(st, fbuffer, nmax);
outFile.write(fbuffer, n * numChannels);
} while (n > 0);
}
}
else
{
// Process file with SoundTouch.DLL int16 (short) sample API.
// Notice that SoundTouch.dll does internally processing using floating
// point routines so the int16 API is not any faster, but provided for
// convenience.
short i16buffer[2048];
int nmax = 2048 / numChannels;
cout << " using i16 api ..." << endl;
while (inFile.eof() == false)
{
int n = inFile.read(i16buffer, nmax * numChannels) / numChannels;
soundtouch_putSamples_i16(st, i16buffer, n);
do
{
n = soundtouch_receiveSamples_i16(st, i16buffer, nmax);
outFile.write(i16buffer, n * numChannels);
} while (n > 0);
}
}
soundtouch_destroyInstance(st);
cout << "done." << endl;
}
catch (const runtime_error &e)
{
cerr << e.what() << endl;
}
return 0;
}
导入一个语音文件infile.wav到工程目录下
三. 例程实现
点击“调试”运行,生成一个三倍速的语音文件outfile2.wav
生成语音如下图所示
cooledit查看两段语音
可以看到处理的非常不错,试听效果也比sonic确实好不少。
四. 小结
最近项目上需要对WAV语音进行处理,这个是对语音进行加速减速播放时需要保证语音不失真,上一篇文章介绍了实例工程调试跑通,本篇文章介绍的是将三个工程进行整合,合并到一个工程中,后续再整合到一个函数中,即可直接对语音进行变速不变调调用处理,目前根据调试结果来看确实效果不错。