ffmpeg播放器声音效果2-变速不变调及变调

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xjb2006/article/details/86063307

关于声音的变速不变调,或者只是单纯的变调,现在很多视频播放器或者音频播放器或者移动端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多年专业音视频开发经验,欢迎骚扰。

猜你喜欢

转载自blog.csdn.net/xjb2006/article/details/86063307