第十三节 利用SoundTouch实现变速、变调
2018-12-06 本文已影响55人
最美下雨天
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
我们将include和SoundTouch目录下的文件考入到工程
image.png
修改CMakeList.txt
cmake_minimum_required(VERSION 3.4.1)
#导入include路径
include_directories(src/main/cpp/include)
include_directories(src/main/cpp/soundtouch/include)
include_directories(src/main/cpp/soundtouch/source)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp
src/main/cpp/CallBackJava.cpp
src/main/cpp/HFFmpeg.cpp
src/main/cpp/HAudio.cpp
src/main/cpp/HQueue.cpp
src/main/cpp/HPlayStatus.cpp
src/main/cpp/soundtouch/source/AAFilter.cpp
src/main/cpp/soundtouch/source/FIFOSampleBuffer.cpp
src/main/cpp/soundtouch/source/FIRFilter.cpp
src/main/cpp/soundtouch/source/cpu_detect_x86.cpp
src/main/cpp/soundtouch/source/sse_optimized.cpp
src/main/cpp/soundtouch/source/RateTransposer.cpp
src/main/cpp/soundtouch/source/SoundTouch.cpp
src/main/cpp/soundtouch/source/InterpolateCubic.cpp
src/main/cpp/soundtouch/source/InterpolateLinear.cpp
src/main/cpp/soundtouch/source/InterpolateShannon.cpp
src/main/cpp/soundtouch/source/TDStretch.cpp
src/main/cpp/soundtouch/source/PeakFinder.cpp
)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#添加动态库
add_library( avcodec-57 SHARED IMPORTED)
#设置动态库路径
set_target_properties( avcodec-57
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec-57.so)
add_library( avdevice-57 SHARED IMPORTED)
set_target_properties( avdevice-57
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavdevice-57.so)
add_library( avfilter-6 SHARED IMPORTED)
set_target_properties( avfilter-6
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavfilter-6.so)
add_library( avformat-57 SHARED IMPORTED)
set_target_properties( avformat-57
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavformat-57.so)
add_library( avutil-55 SHARED IMPORTED)
set_target_properties( avutil-55
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavutil-55.so)
add_library( postproc-54 SHARED IMPORTED)
set_target_properties( postproc-54
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libpostproc-54.so)
add_library( swresample-2 SHARED IMPORTED)
set_target_properties( swresample-2
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libswresample-2.so)
add_library( swscale-4 SHARED IMPORTED)
set_target_properties( swscale-4
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libswscale-4.so)
target_link_libraries( # Specifies the target library.
native-lib
avcodec-57
avdevice-57
avfilter-6
avformat-57
avutil-55
postproc-54
swresample-2
swscale-4
OpenSLES
# Links the target library to the log library
# included in the NDK.
${log-lib} )
/**
* 设置音速
* @param speed
*/
void HAudio::setSpeed(jfloat speed) {
this->speed=speed;
if(soundTouch!=NULL)
{
soundTouch->setTempo(speed);
}
}
/**
* 设置音调
* @param tonal
*/
void HAudio::setTonal(jfloat tonal) {
this->pitch=tonal;
if(soundTouch!=NULL)
{
soundTouch->setPitch(tonal);
}
}
int HAudio::getSoundTouchData() {
while(hPlayStatus != NULL && !hPlayStatus->exit)
{
out_buffer = NULL;
if(finished)
{
finished = false;
data_size = resampleAudio((void **) &out_buffer);
if(data_size > 0)
{
for(int i = 0; i < data_size / 2 + 1; i++)
{
sampleBuffer[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1]) << 8));
}
soundTouch->putSamples(sampleBuffer, nb);
num = soundTouch->receiveSamples(sampleBuffer, nb);
} else{
soundTouch->flush();
}
}
if(num == 0)
{
finished = true;
continue;
} else{
if(out_buffer == NULL)
{
num = soundTouch->receiveSamples(sampleBuffer, nb);
if(num == 0)
{
finished = true;
continue;
}
}
return num;
}
}
return 0;
}
OpenSL ES的播放回调方法中
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
{
HAudio *wlAudio = (HAudio *) context;
if(wlAudio != NULL)
{
if(LOG_DEBUG)
{
LOGI("循环调用重采样");
}
int buffersize = wlAudio->getSoundTouchData();
//int buffersize = wlAudio->resampleAudio((void **) &wlAudio->out_buffer);;
if(buffersize > 0)
{
int out_channels=av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
int totalBytes=buffersize*out_channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
wlAudio->clock+=totalBytes/(double)(wlAudio->sample_rate*2*2);
//我们0.1s回调一次
if(wlAudio->clock-wlAudio->last_time>0.1)
{
wlAudio->last_time=wlAudio->clock;
//回调Java层显示时间
wlAudio->callBackJava->onShowTime(CHILD_THREAD,wlAudio->duration,wlAudio->clock);
}
//(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> buffer, buffersize);
//注意这儿的代码变化
//(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> sampleBuffer, buffersize*out_channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> sampleBuffer, totalBytes);
}
}
}
粘贴一下HAudio.cpp的完整代码
//
// Created by 霍振鹏 on 2018/10/19.
//
#include "HAudio.h"
HAudio::HAudio(HPlayStatus *hPlayStatus,int sample_rate,CallBackJava *callBackJava) {
this->hPlayStatus=hPlayStatus;
this->hQueue=new HQueue(hPlayStatus);
this->sample_rate=sample_rate;
this->callBackJava=callBackJava;
buffer = (uint8_t *) av_malloc(sample_rate * 2 * 2);
//SoundTouch
sampleBuffer= (SAMPLETYPE *) av_malloc(sample_rate * 2 * 2);
soundTouch=new SoundTouch();
soundTouch->setSampleRate(sample_rate);
soundTouch->setChannels(2);
soundTouch->setPitch(pitch);
soundTouch->setTempo(speed);
}
HAudio::~HAudio() {
}
void *writeData(void *data)
{
HAudio *hAudio= (HAudio *) data;
hAudio->initOpenSLES();
pthread_exit(&hAudio->pthread);
}
void HAudio::play() {
//创建一个线程,准备写入pcm数据
pthread_create(&pthread,NULL,writeData,this);
}
//FILE *outFile=fopen("/storage/emulated/0/voiceplayer.pcm","w");
int HAudio::resampleAudio(void **pcmbuf) {
if(LOG_DEBUG)
{
LOGI("开始写入pcm数据");
}
while(hPlayStatus!=NULL&&!hPlayStatus->exit)
{
if(LOG_DEBUG)
{
LOGI("进入循环");
}
if(hPlayStatus->seek)
{
continue;
}
AVPacket *avPacket=av_packet_alloc();
if(hQueue->getAvPacket(avPacket)!=0)
{
av_packet_free(&avPacket);
av_free(avPacket);
avPacket=NULL;
continue;
} else
{
}
//return 0 on success
int ret=0;
ret=avcodec_send_packet(avCodecContext,avPacket);
if(ret!=0)
{
av_packet_free(&avPacket);
av_free(avPacket);
avPacket=NULL;
continue;
}
AVFrame *avFrame=av_frame_alloc();
ret =avcodec_receive_frame(avCodecContext,avFrame);
if(ret==0)
{
if(avFrame->channels>0&&avFrame->channel_layout==0)
{
avFrame->channel_layout=av_get_default_channel_layout(avFrame->channels);
}
else if(avFrame->channels==0&&avFrame->channel_layout>0)
{
avFrame->channel_layout=av_get_channel_layout_nb_channels(avFrame->channel_layout);
}
SwrContext *swrContext;
swrContext=swr_alloc_set_opts(
NULL,
AV_CH_LAYOUT_STEREO,
AV_SAMPLE_FMT_S16,
avFrame->sample_rate,
avFrame->channel_layout,
(AVSampleFormat) avFrame->format,
avFrame->sample_rate,
NULL,NULL
);
if(!swrContext||swr_init(swrContext)<0)
{
av_packet_free(&avPacket);
av_free(avPacket);
avPacket = NULL;
av_frame_free(&avFrame);
av_free(avFrame);
avFrame = NULL;
swr_free(&swrContext);
continue;
}
//return number of samples output per channel
//返回每个通道输出的样本数,立体声就是2个通道
nb=swr_convert(
swrContext,
&buffer,
avFrame->nb_samples,
(const uint8_t **) avFrame->data,
avFrame->nb_samples
);
int out_channels=av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
//每个通道输出的样本数*通道数*每个样本占用的字节,感觉加上了SoundTouch后不好理解,换种写法,跟不加之前统一,主要是Enqueue这儿的代码统一
data_size=nb*out_channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
// fwrite(buffer,1,data_size,outFile);
//当前AVFrame中存放的时间(比如说该Frame出现在2分钟的时候,那么它的值就是2分钟)
now_time=avFrame->pts*av_q2d(time_base);
//clock表示的是从开始播放到现在已经播放的时长
if(now_time<clock)
{
now_time=clock;
}
clock=now_time;
*pcmbuf=buffer;
if(LOG_DEBUG)
{
LOGI("总时长duration:%d 当前时长:%d ",duration,now_time);
}
//LOGI("data_size is %d", data_size);
av_packet_free(&avPacket);
av_free(avPacket);
avPacket = NULL;
av_frame_free(&avFrame);
av_free(avFrame);
avFrame = NULL;
swr_free(&swrContext);
break;
} else{
av_packet_free(&avPacket);
av_free(avPacket);
avPacket=NULL;
av_frame_free(&avFrame);
av_free(avFrame);
avFrame=NULL;
continue;
}
}
// fclose(outFile);
return data_size;
}
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
{
HAudio *wlAudio = (HAudio *) context;
if(wlAudio != NULL)
{
if(LOG_DEBUG)
{
LOGI("循环调用重采样");
}
int buffersize = wlAudio->getSoundTouchData();
//int buffersize = wlAudio->resampleAudio((void **) &wlAudio->out_buffer);;
if(buffersize > 0)
{
int out_channels=av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
int totalBytes=buffersize*out_channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
wlAudio->clock+=totalBytes/(double)(wlAudio->sample_rate*2*2);
//我们0.1s回调一次
if(wlAudio->clock-wlAudio->last_time>0.1)
{
wlAudio->last_time=wlAudio->clock;
//回调Java层显示时间
wlAudio->callBackJava->onShowTime(CHILD_THREAD,wlAudio->duration,wlAudio->clock);
}
//(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> buffer, buffersize);
//注意这儿的代码变化
//(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> sampleBuffer, buffersize*out_channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> sampleBuffer, totalBytes);
}
}
}
void HAudio::initOpenSLES() {
SLresult result;
result = slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
//第二步,创建混音器
const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
(void)result;
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
(void)result;
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);
if (SL_RESULT_SUCCESS == result) {
result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
outputMixEnvironmentalReverb, &reverbSettings);
(void)result;
}
SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&outputMix, 0};
// 第三步,配置PCM格式信息
SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
SLDataFormat_PCM pcm={
SL_DATAFORMAT_PCM,//播放pcm格式的数据
2,//2个声道(立体声)
getCurrentSampleRateForOpensles(sample_rate),//44100hz的频率
SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)
SL_BYTEORDER_LITTLEENDIAN//结束标志
};
SLDataSource slDataSource = {&android_queue, &pcm};
const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE,SL_IID_VOLUME,SL_IID_MUTESOLO};
const SLboolean req[3] = {SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE};
(*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 3, ids, req);
//初始化播放器
(*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);
// 得到接口后调用 获取Player接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmVolumePlay);
(*pcmPlayerObject)->GetInterface(
pcmPlayerObject,
SL_IID_MUTESOLO,
&pcmPlayPlayerMuteSolo);
// 注册回调缓冲区 获取缓冲队列接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
//缓冲接口回调
(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
// 获取播放状态接口
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
pcmBufferCallBack(pcmBufferQueue, this);
}
int HAudio::getCurrentSampleRateForOpensles(int sample_rate) {
int rate = 0;
switch (sample_rate)
{
case 8000:
rate = SL_SAMPLINGRATE_8;
break;
case 11025:
rate = SL_SAMPLINGRATE_11_025;
break;
case 12000:
rate = SL_SAMPLINGRATE_12;
break;
case 16000:
rate = SL_SAMPLINGRATE_16;
break;
case 22050:
rate = SL_SAMPLINGRATE_22_05;
break;
case 24000:
rate = SL_SAMPLINGRATE_24;
break;
case 32000:
rate = SL_SAMPLINGRATE_32;
break;
case 44100:
rate = SL_SAMPLINGRATE_44_1;
break;
case 48000:
rate = SL_SAMPLINGRATE_48;
break;
case 64000:
rate = SL_SAMPLINGRATE_64;
break;
case 88200:
rate = SL_SAMPLINGRATE_88_2;
break;
case 96000:
rate = SL_SAMPLINGRATE_96;
break;
case 192000:
rate = SL_SAMPLINGRATE_192;
break;
default:
rate = SL_SAMPLINGRATE_44_1;
}
return rate;
}
void HAudio::pause() {
if(pcmPlayerPlay!=NULL)
{
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay,SL_PLAYSTATE_PAUSED);
}
}
void HAudio::resume() {
if(pcmPlayerPlay!=NULL)
{
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay,SL_PLAYSTATE_PLAYING);
}
}
void HAudio::setVolume(int percent) {
volumePercent = percent;
if(pcmVolumePlay != NULL)
{
if(percent > 30)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -20);
}
else if(percent > 25)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -22);
}
else if(percent > 20)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -25);
}
else if(percent > 15)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -28);
}
else if(percent > 10)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -30);
}
else if(percent > 5)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -34);
}
else if(percent > 3)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -37);
}
else if(percent > 0)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -40);
}
else{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -100);
}
}
}
void HAudio::setStereoVolume() {
(*pcmPlayPlayerMuteSolo)->SetChannelMute(
pcmPlayPlayerMuteSolo,
1, //0右声道1左声道
false //声道是否开启
);
(*pcmPlayPlayerMuteSolo)->SetChannelMute(
pcmPlayPlayerMuteSolo,
0, //0右声道1左声道
false //声道是否开启
);
}
void HAudio::setRightVolume() {
(*pcmPlayPlayerMuteSolo)->SetChannelMute(
pcmPlayPlayerMuteSolo,
1, //0右声道1左声道
false //声道是否开启
);
(*pcmPlayPlayerMuteSolo)->SetChannelMute(
pcmPlayPlayerMuteSolo,
0, //0右声道1左声道
true //声道是否开启
);
}
void HAudio::setLeftVolume() {
(*pcmPlayPlayerMuteSolo)->SetChannelMute(
pcmPlayPlayerMuteSolo,
0, //0右声道1左声道
false //声道是否开启
);
(*pcmPlayPlayerMuteSolo)->SetChannelMute(
pcmPlayPlayerMuteSolo,
1, //0右声道1左声道
true //声道是否开启
);
}
/**
* 设置音速
* @param speed
*/
void HAudio::setSpeed(jfloat speed) {
this->speed=speed;
if(soundTouch!=NULL)
{
soundTouch->setTempo(speed);
}
}
/**
* 设置音调
* @param tonal
*/
void HAudio::setTonal(jfloat tonal) {
this->pitch=tonal;
if(soundTouch!=NULL)
{
soundTouch->setPitch(tonal);
}
}
int HAudio::getSoundTouchData() {
while(hPlayStatus != NULL && !hPlayStatus->exit)
{
out_buffer = NULL;
if(finished)
{
finished = false;
data_size = resampleAudio((void **) &out_buffer);
if(data_size > 0)
{
for(int i = 0; i < data_size / 2 + 1; i++)
{
sampleBuffer[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1]) << 8));
}
soundTouch->putSamples(sampleBuffer, nb);
num = soundTouch->receiveSamples(sampleBuffer, nb);
} else{
soundTouch->flush();
}
}
if(num == 0)
{
finished = true;
continue;
} else{
if(out_buffer == NULL)
{
num = soundTouch->receiveSamples(sampleBuffer, nb);
if(num == 0)
{
finished = true;
continue;
}
}
return num;
}
}
return 0;
}