用于使用openAL播放3D实时音效的封装类,针对openAL的功能进行了简化封装。每个音源只支持一个wav文件的播放,总数也不高,但是接口十分简洁,基本一看就知道怎么用。最多可以播放24路3D音频,根据设定的位置和物理参数计算左右声道的音量。
作者:yinan Miao from Beihang Univ(本人)
包含.h与.cpp文件一套遵守BSD开发协议(可以随意转载使用,可以应用于任何项目,但是必须注明来源。)
依赖环境:openAL,MGLTools
alManager.h
#ifndef ALMANAGER_H
#define ALMANAGER_H
#include
#include
#include
#include
//用于使用openAL播放3D实时音效的封装类
//针对openAL的功能进行了简化封装
//每个音源只支持一个wav文件的播放,总数也不高,但是接口十分简洁
//最多可以播放24路3D音频
//根据设定的位置和物理参数计算左右声道的音量
//作者:yinan Miao from Beihang Univ
//共计.h与.cpp一套,遵守BSD开发协议。(可应用于任何领域但是需要在源码和手册注明作者)
using namespace std;
#define MAX_BUFFER_SIZE 24//最多支持24路,可以修改
//---------------------------------------------------------------
//WAV数据类型相关,从而无关化alut,减少外引模块
struct WAVE_Data {//Wav文件数据体模块
char subChunkID[4]; //should contain the word data
long subChunk2Size; //Stores the size of the data block
};
struct WAVE_Format {//wav文件数据参数类型
char subChunkID[4];
long subChunkSize;
short audioFormat;
short numChannels;
long sampleRate;
long byteRate;
short blockAlign;
short bitsPerSample;
};
struct RIFF_Header {//RIFF块标准模型
char chunkID[4];
long chunkSize;//size not including chunkSize or chunkID
char format[4];
};
//----------------------------------------------------------------
class alManager
{
private:
ALCdevice *device;//设备指针
ALCcontext *context;//上下文指针
ALCboolean bEAX;//环境音效扩展(声卡标准)
ALuint buffers[MAX_BUFFER_SIZE+1];//用于储存音频数据的缓存ID
ALuint sources[MAX_BUFFER_SIZE+1];//用于储存音源ID
bool loadWavFile(const string filename, ALuint buffer,ALsizei* size, ALsizei* frequency,ALenum* format);//根据文件名载入wav文件到指定的bufferID当中。
/*
Copyright (c) 2018, yinan Miao;
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the Beihang Univ. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public:
alManager();//构造函数,没有实际作用。(因为在openAL的设定中,只能启动一个OPENAL的上下文,因此在本类的设定中不能重复定义或初始化)
void loadWav2Buffer(ALuint bufferI,const char *fileName);//根据文件名载入指定wav文件,并且绑定到指定ID的buffer和音源。
void setListenerPos(vertex pos,vertex vel,vertex at,vertex up);//设置听者的位置、速度、脸朝向向量以及头顶朝向向量。
void setSourcePos(int sI,vertex pos,vertex vel,vertex at);//设置指定ID音源的位置、速度、朝向向量。
void setSourcePhy(int sI,float Gain,float maxDis,float halfDistance,float rollOff = 1.0);//设置指定ID音源的物理参数:缩放系数、最大传播距离、衰减参数以及半衰参数。实际信号= (GAIN-20*log10(1+ROLLOFF*(距离-半衰距离)/半衰距离))原始信号
void play(int sI);//播放指定ID的音源
int getSourceId(int sI);//得到指定ID的实际音源ID,懂openAL的朋友可以使用其进行自定义操作
void init();//初始化openAL
void end();//关闭openAL
};
#endif // ALMANAGER_H
alManager.cpp
#include "almanager.h"
//以下文件导入函数从开源社区无共享协议修改而来,可以读取文件的信息并且绑定指定bufferID。
bool alManager::loadWavFile(const string filename, ALuint buffer, ALsizei *size, ALsizei *frequency, ALenum *format)
{
FILE* soundFile = NULL;
WAVE_Format wave_format;
RIFF_Header riff_header;
WAVE_Data wave_data;
unsigned char* data;
cout<<"reading wav:"<<filename<<endl;
try {
soundFile = fopen(filename.c_str(), "rb");
if (!soundFile)
throw (filename);
// Read in the first chunk into the struct
fread(&riff_header, sizeof(RIFF_Header), 1, soundFile);
//check for RIFF and WAVE tag in memeory
if ((riff_header.chunkID[0] != 'R' ||
riff_header.chunkID[1] != 'I' ||
riff_header.chunkID[2] != 'F' ||
riff_header.chunkID[3] != 'F') ||
(riff_header.format[0] != 'W' ||
riff_header.format[1] != 'A' ||
riff_header.format[2] != 'V' ||
riff_header.format[3] != 'E'))
{
throw ("Invalid RIFF or WAVE Header");
}
//Read in the 2nd chunk for the wave info
fread(&wave_format, sizeof(WAVE_Format), 1, soundFile);
//check for fmt tag in memory
if (wave_format.subChunkID[0] != 'f' ||
wave_format.subChunkID[1] != 'm' ||
wave_format.subChunkID[2] != 't' ||
wave_format.subChunkID[3] != ' ')
{
throw ("Invalid Wave Format");
}
//check for extra parameters;
if (wave_format.subChunkSize > 16)
fseek(soundFile, sizeof(short), SEEK_CUR);
//Read in the the last byte of data before the sound file
fread(&wave_data, sizeof(WAVE_Data), 1, soundFile);
//check for data tag in memory
if (wave_data.subChunkID[0] != 'd' ||
wave_data.subChunkID[1] != 'a' ||
wave_data.subChunkID[2] != 't' ||
wave_data.subChunkID[3] != 'a')
{
cout<<wave_data.subChunkID[0]<<wave_data.subChunkID[1]<<wave_data.subChunkID[2]<<wave_data.subChunkID[3]<<endl;
throw ("Invalid data header");
}
//Allocate memory for data
data = new unsigned char[wave_data.subChunk2Size];
// Read in the sound data into the soundData variable
if (!fread(data, wave_data.subChunk2Size, 1, soundFile))
throw ("error loading WAVE data into struct!");
//Now we set the variables that we passed in with the
//data from the structs
*size = wave_data.subChunk2Size;
*frequency = wave_format.sampleRate;
//The format is worked out by looking at the number of
//channels and the bits per sample.
if (wave_format.numChannels == 1)
{
if (wave_format.bitsPerSample == 8)
*format = AL_FORMAT_MONO8;
else if (wave_format.bitsPerSample == 16)
*format = AL_FORMAT_MONO16;
}
else if (wave_format.numChannels == 2)
{
if (wave_format.bitsPerSample == 8)
*format = AL_FORMAT_STEREO8;
else if (wave_format.bitsPerSample == 16)
*format = AL_FORMAT_STEREO16;
}
//now we put our data into the openAL buffer and
//check for success
alBufferData(buffer, *format, (void*)data,*size, *frequency);
//errorCheck();
//clean up and return true if successful
fclose(soundFile);
return true;
}
catch(char* error)
{
cout << error << " : trying to load "<< filename << std::endl;
if (soundFile != NULL)
fclose(soundFile);
system("pause");
return false;
}
}
alManager::alManager()
{
//撒也没有
}
void alManager::loadWav2Buffer(ALuint bufferI, const char *fileName)
{
ALsizei size;
ALsizei freq;
ALenum format;
loadWavFile(fileName,buffers[bufferI],&size,&freq,&format);//读取到bufferID。
alSourcei(sources[bufferI],AL_BUFFER,buffers[bufferI]);//绑定bufferID到音源ID。
}
void alManager::setListenerPos(vertex pos,vertex vel,vertex at,vertex up)
{
//设置位置、速度、朝向向量以及头顶向量。
ALfloat listenerPos[]= {pos.x,pos.y,pos.z};
ALfloat listenerVel[]= {vel.x,vel.y,vel.z};
ALfloat listenerOri[]= {at.x,at.y,at.z, up.x,up.y,up.z};
alListenerfv(AL_POSITION,listenerPos);
alListenerfv(AL_VELOCITY,listenerVel);
alListenerfv(AL_ORIENTATION,listenerOri);
}
void alManager::setSourcePos(int sI, vertex pos, vertex vel, vertex at)
{
//设置位置、速度以及朝向向量
ALfloat sPos[]= {pos.x,pos.y,pos.z};
ALfloat sVel[]= {vel.x,vel.y,vel.z};
ALfloat sOri[]= {at.x,at.y,at.z};
alSourcefv(sources[sI],AL_POSITION,sPos);
alSourcefv(sources[sI],AL_VELOCITY,sVel);
alSourcefv(sources[sI],AL_DIRECTION,sOri);
}
void alManager::setSourcePhy(int sI, float Gain, float maxDis, float halfDistance, float rollOff)
{
//设置放大系数、最大距离、半衰距离以及衰减参数
alSourcef(sources[sI],AL_GAIN,Gain);
alSourcef(sources[sI],AL_MAX_DISTANCE,maxDis);
alSourcef(sources[sI],AL_ROLLOFF_FACTOR,rollOff);
alSourcef(sources[sI],AL_REFERENCE_DISTANCE,halfDistance);
}
void alManager::play(int sI)
{
alSourcePlay(sources[sI]);//播放指定音源
}
int alManager::getSourceId(int sI)
{
return sources[sI];//返回音源ID
}
/*
Copyright (c) 2018, yinan Miao;
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the Beihang Univ. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
void alManager::init()
{
device = alcOpenDevice(NULL);//打开默认设备
if(device)
{
context = alcCreateContext(device,NULL);//打开默认上下文
alcMakeContextCurrent(context);
}
bEAX = alIsExtensionPresent("EAX2.0");//检查EAX
alGetError();
alGenBuffers(MAX_BUFFER_SIZE,buffers);//分配足够多的bufferID
alGenSources(MAX_BUFFER_SIZE,sources);//分配足够多的音源ID
//设置默认的位置
ALfloat listenerPos[]={0.0,0.0,0.0};
ALfloat listenerVel[]={0.0,0.0,0.0};
ALfloat listenerOri[]={0.0,0.0,-1.0, 0.0,1.0,0.0};
alListenerfv(AL_POSITION,listenerPos);
alListenerfv(AL_VELOCITY,listenerVel);
alListenerfv(AL_ORIENTATION,listenerOri);
}
void alManager::end()
{
for(int i=0;i<MAX_BUFFER_SIZE;i++)
{
alSourceStop(sources[i]);//循环关闭所有的音源
}
alDeleteBuffers(MAX_BUFFER_SIZE,buffers);//注销bufferID
alDeleteSources(MAX_BUFFER_SIZE,sources);//注销音源ID
context = alcGetCurrentContext();//获取上下文
device = alcGetContextsDevice(context);//获取设备
alcMakeContextCurrent(NULL);//释放内存
alcDestroyContext(context);//关闭上下文
alcCloseDevice(device);//关闭设备
}
在程序开始时声明一个ALManager类,并且调用init()函数初始化。
alManager al;
al.init();
在需要载入声音时载入声音并且设置基础参数。
al.loadWav2Buffer(0,"summer.wav");
al.play(0);
al.setListenerPos(VERTEX(0,0,0),VERTEX(0,0,0),VERTEX(0,0,0),VERTEX(0,0,0));
al.setSourcePos(0,VERTEX(0,0,20),VERTEX(0,0,0),VERTEX(0,0,1));
al.setSourcePhy(0,1.0,50000,1.0,15);
可以在任何地方调用函数改变参数
void unKonwn()
{
......
al.setListenerPos(VERTEX(headx,heady,headz),VERTEX(0,0,0),at,up);
......
}