第十三节 利用SoundTouch实现变速、变调

2018-12-06  本文已影响55人  最美下雨天
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;
}






上一篇下一篇

猜你喜欢

热点阅读