关于声音的变速不变调,或者只是单纯的变调,现在很多视频播放器或者音频播放器或者移动端APP都实现了此功能。诸如腾讯视频,爱奇艺视频,优酷视频。。。。。。而我们主要是以SoundTouch开源源码来实现:SoundTouch是一种非常优秀的音频处理算法,大名鼎鼎的安卓端开发开源源码ijkplayer就是用的SoundTouch的算法实现变速不变调。
但是如果同时对音视频实现变速不变调呢,这个其实说复杂也复杂,说简单也简单,如果播放器源码是自己做的,我们可以让视频去同步音频,音频速度加快,证明音频数据减少,那么视频帧也会丢弃。如果音频速度变慢,证明音频数据增多,那么视频帧间切换会变慢即可。
直接贴源码吧:
下面是我封装的头文件:
#pragma once
#include "SoundTouch.h"
using namespace soundtouch;
class CSoundTouchEx
{
public:
CSoundTouchEx(void);
~CSoundTouchEx(void);
private:
int m_nChannels;
float m_fTempo;
float m_fRate;
SoundTouch m_soundtouch;
public:
float GetTempo()
{
return m_fTempo;
}
bool SetTempo(float nTempo);
float GetRate()
{
return m_fRate;
}
bool SetRate(float nRate);
int TempoProcess(char *pOutBuf,char *pInBuf,int nInDataCount);
void SetTempoFormat(int sampleRate=44100,int channels=2)
{
m_soundtouch.setSampleRate(sampleRate);
m_soundtouch.setChannels(channels);
m_nChannels=channels;
m_soundtouch.setSetting(SETTING_USE_QUICKSEEK, 0);
m_soundtouch.setSetting(SETTING_USE_AA_FILTER, 1);
}
bool shortTOfloat(const short *pShort,int nNum,float *pFloat)
{
double fscale = 1.0 / 32768.0;
// convert to floats, scale to range [-1..+1[
for (int i = 0; i < nNum; i ++)
{
pFloat[i] = (float)(fscale * (double)pShort[i]);
}
return true;
}
bool floatTOshort(const float *pFloat,int nNum,short *pShort)
{
int iTemp=0;
// convert to 16 bit integer
for (int i = 0; i < nNum; i ++)
{
// convert to integer
iTemp = (int)(32768.0f * pFloat[i]);
// saturate
if (iTemp < -32768) iTemp = -32768;
if (iTemp > 32767) iTemp = 32767;
pShort[i] = (short)iTemp;
}
return true;
}
};
接下来是实现的CPP源码:
#include "StdAfx.h"
#include ".\soundtouchex.h"
CSoundTouchEx::CSoundTouchEx(void)
{
m_fRate=1;
m_fTempo=0;
m_nChannels=2;
}
CSoundTouchEx::~CSoundTouchEx(void)
{
}
bool CSoundTouchEx::SetRate(float nRate)
{
//if(nRate<-50)
// return false;
//if(nRate>900)
// return false;
//m_soundtouch.setRateChange(nRate);
m_soundtouch.setPitch(nRate);
m_fRate=nRate;
return true;
}
bool CSoundTouchEx::SetTempo(float nTempo)
{
if(nTempo<-50)
return false;
if(nTempo>900)
return false;
//m_soundtouch.setRateChange(nTempo);
m_soundtouch.setTempoChange(nTempo);
m_fTempo=nTempo;
return true;
}
int CSoundTouchEx::TempoProcess(char *pOutBuf,char *pInBuf,int nInDataCount)
{
int nChannels=m_nChannels;
short *pOutBuf1=(short*)pOutBuf;
int nOut=0;
DWORD time1=::timeGetTime();
//m_soundtouch.flush();
const int tempoBUFSIZE=(1024*2);
//SAMPLETYPE *pNewBuf=new SAMPLETYPE[nInDataCount];
SAMPLETYPE pNewBuf[1024*64];
//memset(pNewBuf,0,sizeof(SAMPLETYPE)*nInDataCount);
shortTOfloat((short*)pInBuf,nInDataCount,pNewBuf);
int nAllSize=0;
int nPointer=0;
SAMPLETYPE *pBuf=0;
for(;;)
{
int nTrueLen=0;
SAMPLETYPE *pBuf=pNewBuf+nPointer;
nPointer+=tempoBUFSIZE;
nTrueLen=tempoBUFSIZE;
if(nPointer>=nInDataCount)
{
nTrueLen=nInDataCount-nPointer+tempoBUFSIZE;
nPointer+=nTrueLen;
}
int buffSizeSamples=nTrueLen/nChannels;
// Feed the samples into SoundTouch processor
m_soundtouch.putSamples((SAMPLETYPE*)pBuf, buffSizeSamples);
int nSamples=0;
do
{
nSamples = m_soundtouch.receiveSamples((SAMPLETYPE*)pBuf, buffSizeSamples);
floatTOshort(pBuf,nSamples*nChannels,(short*)(pOutBuf1+nAllSize));
nAllSize+=nSamples*nChannels;
} while (nSamples != 0);
if(nPointer>=nInDataCount)
break;
}
//if(pNewBuf)
// delete []pNewBuf;
DWORD time2=::timeGetTime()-time1;
return nAllSize*2;//因为是char
}
调用代码段:
int CXiaoPlayer::OutAudio(BYTE *pPCM,int nlen,int c,int s,int b,int pts)
{
m_soundtouch.SetTempoFormat(s,c);
//变速不变调
BYTE *TempoBuf=new BYTE[1024*200];
int nBytes=nlen;
if(m_soundtouch.GetTempo()!=0||m_soundtouch.GetRate()!=1)
{
try
{
nBytes=m_soundtouch.TempoProcess((char*)TempoBuf,(char*)pPCM,nlen/2);
}
catch(...)
{
TRACE0("CAudioPlay:TempoProcess错误\n");
m_soundtouch.SetTempo(0);
}
}
else
{
memcpy(TempoBuf,pPCM,nlen);
nBytes=nlen;
}
if(nBytes>0)
{
WAVEFORMATEX waveformat;
memset(&waveformat, 0, sizeof(WAVEFORMATEX));
waveformat.cbSize = sizeof(WAVEFORMATEX);
waveformat.wFormatTag = WAVE_FORMAT_PCM; // pcm
waveformat.nChannels = c; //
waveformat.nSamplesPerSec = s; //
waveformat.wBitsPerSample = b; //
waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
PlayAudio(TempoBuf,nBytes,(BYTE*)&waveformat,pts);
}
delete []TempoBuf;
return nBytes;
}
OK!今天就到此为止!
如果大家有合作需求:请联系QQ35744025,10多年专业音视频开发经验,欢迎骚扰。