[HGE]-源码分析-11 sound

来源:互联网 发布:寒冷队长 知乎 编辑:程序博客网 时间:2024/05/14 05:00

    音效部分。

HGE的音效底层接口用的BASS库,做的事情就是把BASS库里的操作封装了一下。

这个文件里面函数挺多,外部的接口分为,Effect,music,Stream,Channel

先上图看看这些函数。





在这里感谢easis童鞋的翻译~~

这些函数都不难,只要明白函数做的事情是什么,再去看源码就特别容易了


对了对了,在这里顺便提一下Effect,Music,Stream,Channel的区别

Effect是音效,比如开枪的声音,点击鼠标的声音等等

Music是更长的音乐,比如游戏场景的背景音,过场动画时放的音乐

Stream是音效的压缩格式,一套相关的音效合在一起,同时载入和释放

至于Channel,BASS里的音乐控制都是由通道控制的,通道打开就相当于播放,其他的调整通道参数播放的效果也会改变

控制播放都是通过控制通道完成的




代码走起:


Effect:

HEFFECT CALL HGE_Impl::Effect_Load(const char *filename, DWORD size)//size不为0,filename就是指向内存块的指针{                                                                   //size是这块内存的大小,也就是音效的大小DWORD _size, length, samples;HSAMPLE hs;HSTREAM hstrm;BASS_CHANNELINFO info;void *buffer, *data;if(hBass)//句柄型数据类型,相当于装载带内存的资源的ID{if(bSilent) return 1;//判断资源从内存载入,还是从文件载入if(size) { data=(void *)filename; _size=size; }else{data=Resource_Load(filename, &_size);if(!data) return NULL;}    //获得可以被Effect_Play使用的句柄hs=BASS_SampleLoad(TRUE, data, 0, _size, 4, BASS_SAMPLE_OVER_VOL);if(!hs) {   //无法直接载入,换流句柄,用流的格式载入hstrm=BASS_StreamCreateFile(TRUE, data, 0, _size, BASS_STREAM_DECODE);if(hstrm) {length=(DWORD)BASS_ChannelGetLength(hstrm);BASS_ChannelGetInfo(hstrm, &info);samples=length;if(info.chans < 2) samples>>=1;if((info.flags & BASS_SAMPLE_8BITS) == 0) samples>>=1;buffer=BASS_SampleCreate(samples, info.freq, 2, 4, info.flags | BASS_SAMPLE_OVER_VOL);if(!buffer){BASS_StreamFree(hstrm);_PostError("Can't create sound effect: Not enough memory");}else{//把流转成音效句柄BASS_ChannelGetData(hstrm, buffer, length);hs=BASS_SampleCreateDone();BASS_StreamFree(hstrm);if(!hs)_PostError("Can't create sound effect");}}}if(!size) Resource_Free(data);return hs;}else return 0;}

首先是音效载入,成功载入返回一个音效句柄(实际是个WORD),可以交给其他函数使用


HCHANNEL CALL HGE_Impl::Effect_Play(HEFFECT eff){if(hBass){    //获取播放通道并播放HCHANNEL chn;chn=BASS_SampleGetChannel(eff, FALSE);BASS_ChannelPlay(chn, TRUE);return chn;}else return 0;}HCHANNEL CALL HGE_Impl::Effect_PlayEx(HEFFECT eff, int volume, int pan, float pitch, bool loop){    //含有详细播放控制的播放方法                      音量    音效平衡       音高系数     循环(true循环到通道关闭)if(hBass){BASS_SAMPLE info;HCHANNEL chn;BASS_SampleGetInfo(eff, &info);chn=BASS_SampleGetChannel(eff, FALSE);BASS_ChannelSetAttributes(chn, (int)(pitch*info.freq), volume, pan);info.flags &= ~BASS_SAMPLE_LOOP;if(loop) info.flags |= BASS_SAMPLE_LOOP;BASS_ChannelSetFlags(chn, info.flags);BASS_ChannelPlay(chn, TRUE);return chn;}else return 0;}void CALL HGE_Impl::Effect_Free(HEFFECT eff){if(hBass) BASS_SampleFree(eff);}
这3个函数就是使用刚才生成的那个句柄的,其他具体的都写在注释里了。


Music:

HMUSIC CALL HGE_Impl::Music_Load(const char *filename, DWORD size){   //和effect的load差不多,不同的是这里没有流载入格式,如果载入失败就直接贴出错误void *data;DWORD _size;HMUSIC hm;if(hBass){if(size){data=(void *)filename;_size=size;}else{data=Resource_Load(filename, &_size);if(!data) return 0;}hm=BASS_MusicLoad(TRUE, data, 0, 0, BASS_MUSIC_PRESCAN | BASS_MUSIC_POSRESETEX | BASS_MUSIC_RAMP, 0);if(!hm)_PostError("Can't load music");if(!size) Resource_Free(data);return hm;//成功返回music句柄}else return 0;}HCHANNEL CALL HGE_Impl::Music_Play(HMUSIC mus, bool loop, int volume, int order, int row){                                 //按播放控制播放一段音乐if(hBass){DWORD pos = BASS_MusicGetOrderPosition(mus);if(order == -1) order = LOWORD(pos);if(row == -1) row = HIWORD(pos);BASS_ChannelSetPosition(mus, MAKEMUSICPOS(order, row));BASS_CHANNELINFO info;BASS_ChannelGetInfo(mus, &info);BASS_ChannelSetAttributes(mus, info.freq, volume, 0);info.flags &= ~BASS_SAMPLE_LOOP;//默认是不循环的if(loop) info.flags |= BASS_SAMPLE_LOOP;BASS_ChannelSetFlags(mus, info.flags);BASS_ChannelPlay(mus, FALSE);return mus;//音乐句柄和通道句柄都是WORD,这里的播放音乐其实就是打开通道}else return 0;}void CALL HGE_Impl::Music_Free(HMUSIC mus){if(hBass) BASS_MusicFree(mus);}

音乐的载入和播放,和音效只有一些细微的区别,所以一起贴,细节依然写在注释里

和Effect不同的是,由于音乐的播放时间比较长,因此可能需要一些调节函数

void CALL HGE_Impl::Music_SetAmplification(HMUSIC music, int ampl)//设置振幅{if(hBass) BASS_MusicSetAttribute(music, BASS_MUSIC_ATTRIB_AMPLIFY, ampl);}int CALL HGE_Impl::Music_GetAmplification(HMUSIC music)//获取振幅{if(hBass) return BASS_MusicGetAttribute(music, BASS_MUSIC_ATTRIB_AMPLIFY);else return -1;}int CALL HGE_Impl::Music_GetLength(HMUSIC music){//获取进入音乐列表时的编号if(hBass){return BASS_MusicGetOrders(music);}else return -1;}void CALL HGE_Impl::Music_SetPos(HMUSIC music, int order, int row){    //设置音乐播放位置                      音乐中实体编号   列编号if(hBass){BASS_ChannelSetPosition(music, MAKEMUSICPOS(order, row));}}bool CALL HGE_Impl::Music_GetPos(HMUSIC music, int *order, int *row){    //获取播放位置if(hBass){DWORD pos;pos = BASS_MusicGetOrderPosition(music);if(pos == -1) return false;*order = LOWORD(pos);*row = HIWORD(pos);return true;}else return false;}void CALL HGE_Impl::Music_SetInstrVolume(HMUSIC music, int instr, int volume){    //设定乐器音量if(hBass){BASS_MusicSetAttribute(music, BASS_MUSIC_ATTRIB_VOL_INST + instr, volume);}}int CALL HGE_Impl::Music_GetInstrVolume(HMUSIC music, int instr){if(hBass){return BASS_MusicGetAttribute(music, BASS_MUSIC_ATTRIB_VOL_INST + instr);}else return -1;}void CALL HGE_Impl::Music_SetChannelVolume(HMUSIC music, int channel, int volume){     //通道音量if(hBass){BASS_MusicSetAttribute(music, BASS_MUSIC_ATTRIB_VOL_CHAN + channel, volume);}}int CALL HGE_Impl::Music_GetChannelVolume(HMUSIC music, int channel){if(hBass){return BASS_MusicGetAttribute(music, BASS_MUSIC_ATTRIB_VOL_CHAN + channel);}else return -1;}

这些调节函数都是直接掉BASS就可以了,本身就是一些SET和GET操作,青一色的判断一下BASS句柄是否存在然后直接调函数



Stream:

HSTREAM CALL HGE_Impl::Stream_Load(const char *filename, DWORD size){      //压缩音频流载入,前面的Effect_Load里有类似的操作,在普通的无法载入时,尝试流载入格式void *data;DWORD _size;HSTREAM hs;CStreamList *stmItem;if(hBass){if(bSilent) return 1;if(size) { data=(void *)filename; _size=size; }else{data=Resource_Load(filename, &_size);if(!data) return 0;}hs=BASS_StreamCreateFile(TRUE, data, 0, _size, 0);if(!hs){_PostError("Can't load stream");if(!size) Resource_Free(data);return 0;}if(!size){    //加入音频流列表,而且加在列表头stmItem=new CStreamList;stmItem->hstream=hs;stmItem->data=data;stmItem->next=streams;streams=stmItem;}return hs;}else return 0;}void CALL HGE_Impl::Stream_Free(HSTREAM stream){CStreamList *stmItem=streams, *stmPrev=0;//遍历列表去掉要释放的if(hBass){while(stmItem){if(stmItem->hstream==stream){if(stmPrev) stmPrev->next=stmItem->next;else streams=stmItem->next;Resource_Free(stmItem->data);delete stmItem;break;}stmPrev=stmItem;stmItem=stmItem->next;}BASS_StreamFree(stream);}}HCHANNEL CALL HGE_Impl::Stream_Play(HSTREAM stream, bool loop, int volume){    //播放if(hBass){BASS_CHANNELINFO info;BASS_ChannelGetInfo(stream, &info);BASS_ChannelSetAttributes(stream, info.freq, volume, 0);info.flags &= ~BASS_SAMPLE_LOOP;if(loop) info.flags |= BASS_SAMPLE_LOOP;BASS_ChannelSetFlags(stream, info.flags);BASS_ChannelPlay(stream, TRUE);return stream;}else return 0;}

流的载入和播放,和前两个的很像,都是一个流程。


Channel:

void CALL HGE_Impl::Channel_SetPanning(HCHANNEL chn, int pan){if(hBass) BASS_ChannelSetAttributes(chn, -1, -1, pan);}void CALL HGE_Impl::Channel_SetVolume(HCHANNEL chn, int volume){if(hBass) BASS_ChannelSetAttributes(chn, -1, volume, -101);}void CALL HGE_Impl::Channel_SetPitch(HCHANNEL chn, float pitch){if(hBass){BASS_CHANNELINFO info;BASS_ChannelGetInfo(chn, &info);BASS_ChannelSetAttributes(chn, (int)(pitch*info.freq), -1, -101);}}void CALL HGE_Impl::Channel_Pause(HCHANNEL chn){if(hBass) BASS_ChannelPause(chn);}void CALL HGE_Impl::Channel_Resume(HCHANNEL chn){if(hBass) BASS_ChannelPlay(chn, FALSE);}void CALL HGE_Impl::Channel_Stop(HCHANNEL chn){if(hBass) BASS_ChannelStop(chn);}void CALL HGE_Impl::Channel_PauseAll(){if(hBass) BASS_Pause();}void CALL HGE_Impl::Channel_ResumeAll(){if(hBass) BASS_Start();}void CALL HGE_Impl::Channel_StopAll(){if(hBass){BASS_Stop();BASS_Start();}}bool CALL HGE_Impl::Channel_IsPlaying(HCHANNEL chn){if(hBass){if(BASS_ChannelIsActive(chn)==BASS_ACTIVE_PLAYING) return true;else return false;}else return false;}float CALL HGE_Impl::Channel_GetLength(HCHANNEL chn) {if(hBass){return BASS_ChannelBytes2Seconds(chn, BASS_ChannelGetLength(chn));}else return -1;}float CALL HGE_Impl::Channel_GetPos(HCHANNEL chn) {if(hBass){return BASS_ChannelBytes2Seconds(chn, BASS_ChannelGetPosition(chn));}else return -1;}void CALL HGE_Impl::Channel_SetPos(HCHANNEL chn, float fSeconds) {if(hBass){BASS_ChannelSetPosition(chn, BASS_ChannelSeconds2Bytes(chn, fSeconds));}}void CALL HGE_Impl::Channel_SlideTo(HCHANNEL channel, float time, int volume, int pan, float pitch){if(hBass){BASS_CHANNELINFO info;BASS_ChannelGetInfo(channel, &info);int freq;if(pitch == -1) freq = -1;else freq = (int)(pitch*info.freq);BASS_ChannelSlideAttributes(channel, freq, volume, pan, DWORD(time*1000));}}bool CALL HGE_Impl::Channel_IsSliding(HCHANNEL channel){if(hBass){if(BASS_ChannelIsSliding(channel)) return true;else return false;}else return false;}

这一组函数和music的控制函数很像,BASS的声音控制的源头就是控制channel,依然是调BASS函数


剩下的几个:

内部的函数

bool HGE_Impl::_SoundInit(){if(!bUseSound || hBass) return true;hBass=LoadLibrary("bass.dll");if (!hBass){_PostError("Can't load BASS.DLL");return false;}LOADBASSFUNCTION(BASS_GetVersion);if (HIWORD(BASS_GetVersion()) != BASSVERSION){_PostError("Incorrect BASS.DLL version");return false;}LOADBASSFUNCTION(BASS_GetDeviceDescription);LOADBASSFUNCTION(BASS_Init);LOADBASSFUNCTION(BASS_Free);LOADBASSFUNCTION(BASS_Start);LOADBASSFUNCTION(BASS_Pause);LOADBASSFUNCTION(BASS_Stop);LOADBASSFUNCTION(BASS_SetConfig);//LOADBASSFUNCTION(BASS_ErrorGetCode);LOADBASSFUNCTION(BASS_SampleLoad);LOADBASSFUNCTION(BASS_SampleCreate);LOADBASSFUNCTION(BASS_SampleCreateDone);LOADBASSFUNCTION(BASS_SampleGetInfo);LOADBASSFUNCTION(BASS_SampleGetChannel);LOADBASSFUNCTION(BASS_SampleFree);LOADBASSFUNCTION(BASS_MusicLoad);LOADBASSFUNCTION(BASS_MusicFree);LOADBASSFUNCTION(BASS_MusicGetOrders);LOADBASSFUNCTION(BASS_MusicGetOrderPosition);LOADBASSFUNCTION(BASS_MusicSetAttribute);LOADBASSFUNCTION(BASS_MusicGetAttribute);LOADBASSFUNCTION(BASS_StreamCreateFile);LOADBASSFUNCTION(BASS_StreamFree);LOADBASSFUNCTION(BASS_ChannelGetInfo);LOADBASSFUNCTION(BASS_ChannelGetAttributes);LOADBASSFUNCTION(BASS_ChannelSetAttributes);LOADBASSFUNCTION(BASS_ChannelSlideAttributes);LOADBASSFUNCTION(BASS_ChannelIsSliding);LOADBASSFUNCTION(BASS_ChannelSetFlags);LOADBASSFUNCTION(BASS_ChannelGetData);LOADBASSFUNCTION(BASS_ChannelPlay);LOADBASSFUNCTION(BASS_ChannelPause);LOADBASSFUNCTION(BASS_ChannelStop);LOADBASSFUNCTION(BASS_ChannelIsActive);LOADBASSFUNCTION(BASS_ChannelGetLength);LOADBASSFUNCTION(BASS_ChannelGetPosition);LOADBASSFUNCTION(BASS_ChannelSetPosition);LOADBASSFUNCTION(BASS_ChannelSeconds2Bytes);LOADBASSFUNCTION(BASS_ChannelBytes2Seconds);bSilent=false;if (!BASS_Init(-1,nSampleRate,0,hwnd,NULL)){System_Log("BASS Init failed, using no sound");BASS_Init(0,nSampleRate,0,hwnd,NULL);bSilent=true;}else{System_Log("Sound Device: %s",BASS_GetDeviceDescription(1));System_Log("Sample rate: %ld\n", nSampleRate);}//BASS_SetConfig(BASS_CONFIG_BUFFER, 5000);//BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 50);_SetFXVolume(nFXVolume);_SetMusVolume(nMusVolume);_SetStreamVolume(nStreamVolume);return true;}

很长的BASS初始化过程,BASS很有意思的一点是需要用到的函数在初始化的时候要LOAD一下,还有一个bSilent标志变量,它为false表示BASS可用


void HGE_Impl::_SoundDone(){CStreamList *stmItem=streams, *stmNext;if(hBass){BASS_Stop();BASS_Free();//int err = BASS_ErrorGetCode(); FreeLibrary(hBass);hBass=0;while(stmItem){stmNext=stmItem->next;Resource_Free(stmItem->data);delete stmItem;stmItem=stmNext;}streams=0;}}

同样有来就有走,做完事之后就要释放


void HGE_Impl::_SetMusVolume(int vol){if(hBass) BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, vol);}void HGE_Impl::_SetStreamVolume(int vol){if(hBass) BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, vol);}void HGE_Impl::_SetFXVolume(int vol){if(hBass) BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, vol);}

这三个只在初始化和system里被调用过,用法很像DX的SetRanderState,不过这里是设定BASS的配置,设定全局音量


这里Sound就完结了

关于BASS更多内容可以查阅其他童鞋分享的BASS文档 http://download.csdn.net/detail/jiejiejieppp/4999688

还有一个中文的HGE文档,http://download.csdn.net/detail/chenzhizs/3271592






原创粉丝点击