背景原因:
多年前曾做过sip客户端,测试过几个软件,其中包含有音频的简单混音,有兴趣可以看一下。
做法仅仅把音频相加。
代码:
/
#include "resource.h" // main symbols
#include "windows.h"
#include "mmsystem.h"
#ifdef WIN32
#define WAVDATA BYTE
#else
#define WAVDATA BYTE _huge
#define WAVEFORMATEX PCMWAVEFORMAT
#endif
#ifdef WIN32
#define WAV16DATA WORD
#else
#define WAV16DATA WORD _huge
#endif
class MWAVE
{
private:
BOOL OpenFlage;
DWORD DataSize;
HGLOBAL hData;
WAVDATA* lpData;
PCMWAVEFORMAT pFormat;
WAVEHDR WaveHead;
HWAVEOUT hWaveOut;
public:
MWAVE(){memset(this,0,sizeof(MWAVE));};
~MWAVE(){Close();};
int Open(char*); //打开一个WAV文件
int Play(HWND); //播放一个WAV文件
int Add(char*); //往正在播放的WAV设备中添加WAV 文件
int Stop(); //停止播放
int Close(); //关闭设备
};
#include "stdafx.h"
#include "wavemix.h"
#include "wavemixDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///
// wavemix Class
//
int MWAVE::Open(char* name)
{
HMMIO hMmio;
MMCKINFO pinfo;
MMCKINFO cinfo;
// if(hMmio)Close();
//打开WAV文件,返回一个HMMIO句柄
hMmio=mmioOpen(name,NULL,MMIO_READ);
if(!hMmio)return FALSE;
OpenFlage=1;
//查找父块"wave";
pinfo.fccType=mmioFOURCC('W','A','V','E');
if(mmioDescend(hMmio,&pinfo,NULL,MMIO_FINDRIFF))goto FALSE_END;
//查找子块"fmt" parent"riff";
cinfo.ckid=mmioFOURCC('f','m','t',' ');
if(mmioDescend(hMmio,&cinfo,&pinfo,MMIO_FINDCHUNK))
goto FALSE_END;
mmioRead(hMmio,(LPSTR)&pFormat,sizeof(PCMWAVEFORMAT));//cinfo.cksize);
if(pFormat.wf.wFormatTag!=WAVE_FORMAT_PCM)
goto FALSE_END;
//跳入块"FMT"
mmioAscend(hMmio,&cinfo,0);
//查找数据块
cinfo.ckid=mmioFOURCC('d','a','t','a');
if(mmioDescend(hMmio,&cinfo,&pinfo,MMIO_FINDCHUNK))
goto FALSE_END;
DataSize=cinfo.cksize;
//读取数据
hData=GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, DataSize);
lpData=(WAVDATA*)GlobalLock(hData);
if( !hData || !lpData ) goto FALSE_END;
if(mmioRead(hMmio,(HPSTR)lpData,DataSize)!=(LRESULT)DataSize)
goto FALSE_END;
//close and return
mmioClose(hMmio,MMIO_FHOPEN);
return TRUE;
FALSE_END:
if(hMmio)mmioClose(hMmio,MMIO_FHOPEN);
if(lpData)LocalUnlock(hData);
if(hData)GlobalFree(hData);
memset(this,0,sizeof(MWAVE));
return 0;
}
int MWAVE::Play(HWND hP)
{
if(!OpenFlage)return FALSE;
//检测系统播放功能
if(waveOutOpen(NULL,WAVE_MAPPER,
(WAVEFORMATEX*)&pFormat,NULL,
NULL,WAVE_FORMAT_QUERY))
return Close();
if(waveOutOpen(&hWaveOut,WAVE_MAPPER,
( WAVEFORMATEX*)&pFormat,(DWORD)hP,
0,CALLBACK_WINDOW))
return Close();
WaveHead.lpData=(LPSTR)lpData;
WaveHead.dwBufferLength=DataSize;
WaveHead.dwFlags=0L;
WaveHead.dwLoops=0L;
往WAV设备中添加数据
if(waveOutPrepareHeader(hWaveOut,&WaveHead,
sizeof(WAVEHDR)))
return Close();
if(waveOutWrite(hWaveOut,&WaveHead,sizeof(WAVEHDR)))
return Close();
return TRUE;
}
//#define min(a, b) (((a) < (b)) ? (a) : (b))
int MWAVE::Add(char* name)
{
register int x;
if(!OpenFlage)return Open(name);
MWAVE wav;
if(!wav.Open(name))return FALSE;
MMTIME time;
//获得WAV文件当前播放位置
time.wType=TIME_BYTES;
if(waveOutGetPosition(hWaveOut,&time,sizeof(MMTIME)))
time.u.cb=0;
DWORD start=((time.u.cb>>1)<<1);
DWORD end=min(DataSize_start,wav.DataSize);//混音长度
register WAVDATA* lpd=lpData+start;
for(register DWORD i=0;i<end;i++)
{
//将两组WAV文件数据相加,并检测数据大小是否合法,如果//数据大小越界,则分别取最大值和最小值
x=(((*(lpd+i))+(*(wav.lpData+i))))_128;
if(x<0)x=0;
if(x>255)x=255;
*(lpd+i)=(BYTE)(x);
}
return TRUE;
}
int MWAVE::Stop()
{return !waveOutReset(hWaveOut);}
int MWAVE::Close()
{
if(hWaveOut)
{
waveOutReset(hWaveOut);
waveOutClose(hWaveOut);
waveOutUnprepareHeader(hWaveOut,&WaveHead, sizeof(WAVEHDR));
}
if(lpData)LocalUnlock(hData);
if(hData)GlobalFree(hData);
memset(this,0,sizeof(MWAVE));
return 0;
}
混合器中:
这是程序中的混音设置,计算方法没什么特别;
int
PCMMixer::TransformAudioSamples(std::vector<std::pair<AudioSample *, AudioSource *> > &dataIn, AudioSample **ppAudioSample)
{
tracer.tracef(DET, "TransformAudioSamples\n");
int result = 0;
AudioSample *outSample = NULL;
AudioSample *inSample = NULL;
std::vector<std::pair<AudioSample *, AudioSource *> > data;
// create a new input vector with only the non-null valid audio samples passed in the input
for (int i=0; i<dataIn.size(); i++)
{
if (dataIn[i].first)
{
data.push_back(std::make_pair(dataIn[i].first, dataIn[i].second));
}
}
// make inSample the first (if any) non-null audioSample in the vector. it is used later for audio format stuff
if (data.size() == 1)//一路输入
{
inSample = data[0].first;
outSample = inSample;
inSample->AddRef(this);
}
else if (data.size() > 1)//多路输入
{
// create new audio sample
inSample = data[0].first;
result = AudioSampleManager::GetInstance()->GetAudioSample(&outSample, this);
WAVEFORMATEX sampleFormat;
inSample->GetFormat(&sampleFormat);
outSample->SetFormat(sampleFormat);
short *dataOut = (short *)(outSample->Data());//输入第一路
// find out how many need to be mixed
int maxInputSize(0);
int maxSilenceDuration(0);
for (int i=0; i< data.size(); i++)
{
int sampleDataSize = (data[i].first)->DataSize();
if (sampleDataSize > maxInputSize)//获得最大数据量
{
maxInputSize = sampleDataSize;
}
else if (sampleDataSize == 0)//最大静音时间
{
maxSilenceDuration = max(maxSilenceDuration, (data[i].first)->GetSilenceDuration());
}
}
outSample->SetSilenceDuration(maxSilenceDuration); // in case all are silence packets
int samplesToMix = (outSample->BufferSize() > maxInputSize ? maxInputSize : outSample->BufferSize()) / 2;
//要混合的样本数量
// mix samples
for (i=0; i<samplesToMix; i++)
{
double total = 0;
// first add the samples
for (int j=0; j<data.size(); j++)
{
AudioSample *audioSample = data[j].first;//获得第j路输入的数据
short *dataIn = (short *)(audioSample->Data());//数据值
if (audioSample->DataSize() >= (i+1) * 2) // sample #0 means datasize has to be = (0+1) * 2
{//第j路样本的数据大小,必须在以混合的音频的后面,不能搅乱前面的部分。
//输出总大小为samplesToMix * 2
total += dataIn[i];//把各路音频值相加
}
}
// limit the volume
if (total > 32767.0)
{
total = 32767.0;
}
else if (total < -32768.0)
{
total = -32768.0;
}
dataOut[i] = (short)total;
}
outSample->SetDataSize(samplesToMix * 2);//设置输出大小为要混合的样本数*2
tracer.tracef(DET, "TransformAudioSamples : outSample->DataSize = %d\n", samplesToMix * 2);
}
*ppAudioSample = outSample;
tracer.tracef(DET, "~TransformAudioSamples\n");
return 0;
}