ios 直播变声功能

最近有做iOS直播变声的需求,于是去网上搜索了可用的第三方变声库,最终选定了SoundTouch,SoundTouch是C++的一套库,对iOS项目来说也是比较容易进行集成的,具体的可以参考一下 iOS下使用SoundTouch实现变声

要了解变声原理的同学,可以参考一下变声语音相关知识

在使用过程中遇到的最大问题应该是需要SoundTouch进行实时音频流的处理,而目前网上存在的例子,多数都是对音频文件进行处理,于是花了一些时间来适配,这也是今天要说明的重点。

目前在自己的项目中使用音频采集库数据返回的格式是 CMSampleBufferRef,我需要做的是对CMSampleBufferRef进行处理并返回给上层,而看SoundTouch的数据接口 putSamples 能接受的是一个 SAMPLETYPE 类型,继续跟踪查看会发现 SAMPLETYPE 是由以下两个宏定义来决定的

#defineSOUNDTOUCH_INTEGER_SAMPLES1//< 16bit integer samples//#define SOUNDTOUCH_FLOAT_SAMPLES1//< 32bit float samples

因为声音采集的数据格式会有不同,SoundTouch能支持的是16位的int值和32位的float值,接下来我们就需要知道自己的声音采样数据格式是什么样的,这些数据可以从CMSampleBufferRef中来寻找,以下为直接在XCode中打印的一个CMSampleBuffer的值,里面我们可以看到一个 mFormatFlags,目前这个值是0x29,去找AudioFormatFlags的定义可以发现 0x29 = kAudioFormatFlagIsFloat|kAudioFormatFlagIsBigEndian|kAudioFormatFlagIsNonInterleaved,因此可以断定音频数据格式是float类型的,从mBytesPerFrame中可以判断是32位的,这样我们就知道要在代码中打开SOUNDTOUCH_FLOAT_SAMPLES1的定义了

CMSampleBuffer0x14bd9df00retainCount:1allocator:0x1aa381bb8invalid = NO    dataReady = YES    makeDataReadyCallback =0x0makeDataReadyRefcon =0x0formatDescription = {    mediaType:'soun'mediaSubType:'lpcm'mediaSpecific: {        ASBD: {            mSampleRate:44100.000000mFormatID:'lpcm'mFormatFlags:0x29mBytesPerPacket:4mFramesPerPacket:1mBytesPerFrame:4mChannelsPerFrame:1mBitsPerChannel:32}        cookie: {(null)}        ACL: {(null)}        FormatListArray: {(null)}    }    extensions: {(null)}}    sbufToTrackReadiness =0x0numSamples =941sampleTimingArray[1] = {        {PTS = {10672774120541/1000000000=10672.774}, DTS = {INVALID}, duration = {1/44100=0.000}},    }    dataBuffer =0x170113b00

接下来需要做的是将数据传给SoundTouch的putSamples,这需要我们从CMSampleBufferRef中来提取音频的数据

voidSoundTouch::putSamples(constSAMPLETYPE*samples,uint nSamples)

下面先贴出原码

- (CMSampleBufferRef)pitchSoundBuffer:(CMSampleBufferRef)ref {    AudioBufferList audioBufferList;CMBlockBufferRefblockBuffer;CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(ref,NULL, &audioBufferList,sizeof(audioBufferList),NULL,NULL,0, &blockBuffer);    AudioBuffer audioBuffer = audioBufferList.mBuffers[0];    Float32 *frame = (Float32*)audioBuffer.mData;NSMutableData*audioData=[[NSMutableDataalloc] init];    [audioData appendBytes:frame length:audioBuffer.mDataByteSize];char*pcmData = (char*)audioData.bytes;intpcmSize = (int)audioData.length;intnSamples = pcmSize /4;    mSoundTouch->putSamples((Float32 *)pcmData, nSamples);if(audioData.length ==0) {returnref;    }NSMutableData*soundTouchDatas = [[NSMutableDataalloc] init];    Float32 *samples = new Float32[pcmSize];intnumSamples =0;    memset(samples,0, pcmSize);    numSamples = mSoundTouch->receiveSamples(samples,nSamples);    [soundTouchDatas appendBytes:samples length:numSamples*4];    delete [] samples;CMItemCounttimingCount;CMSampleBufferGetSampleTimingInfoArray(ref,0,nil, &timingCount);CMSampleTimingInfo* pInfo = (CMSampleTimingInfo*)malloc(sizeof(CMSampleTimingInfo) * timingCount);CMSampleBufferGetSampleTimingInfoArray(ref, timingCount, pInfo, &timingCount);if(soundTouchDatas.length ==0) {returnref;    }void*touchData = (void*)[soundTouchDatas bytes];CMSampleBufferReftouchSampleBufferRef = [selfcreateAudioSample:touchData frames:(int)[soundTouchDatas length] timing:*pInfo];returntouchSampleBufferRef;}

下面为创建一个CMSampleBufferRef

- (CMSampleBufferRef)createAudioSample:(void*)audioData frames:(UInt32)len timing:(CMSampleTimingInfo)timing{intchannels =1;    AudioBufferList audioBufferList;    audioBufferList.mNumberBuffers =1;    audioBufferList.mBuffers[0].mNumberChannels=channels;    audioBufferList.mBuffers[0].mDataByteSize=len;    audioBufferList.mBuffers[0].mData = audioData;    AudioStreamBasicDescription asbd;    asbd.mSampleRate =44100;    asbd.mFormatID = kAudioFormatLinearPCM;    asbd.mFormatFlags =0x29;    asbd.mBytesPerPacket =4;    asbd.mFramesPerPacket =1;    asbd.mBytesPerFrame =4;    asbd.mChannelsPerFrame =1;    asbd.mBitsPerChannel =32;    asbd.mReserved =0;CMSampleBufferRefbuff =NULL;staticCMFormatDescriptionRefformat =NULL;    OSStatus error =0;    error =CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &asbd,0,NULL,0,NULL,NULL, &format);if(error) {returnNULL;    }    error =CMSampleBufferCreate(kCFAllocatorDefault,NULL,false,NULL,NULL, format, len/4,1, &timing,0,NULL, &buff);if(error) {returnNULL;    }    error =CMSampleBufferSetDataBufferFromAudioBufferList(buff, kCFAllocatorDefault, kCFAllocatorDefault,0, &audioBufferList);if(error){returnNULL;    }returnbuff;}

本文出自:https://www.jianshu.com/p/c96a2dd80866

你可能感兴趣的:(ios 直播变声功能)