[HGE]-源码分析-11 sound
音效部分。
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
[HGE]-源码分析-11 sound相关推荐
- Bochs源码分析 - 11:《X86探索及编程》代码初步解释
前言 现在我们来阅读邓志老师的<x86/x64体系探索及编程>,边阅读边将其代码放到bochs中运行,然后观察bochs的CPU是如何来模拟的.现在我们来详细分析ex3-2 ...
- spring源码分析11
refresh包括applicationContext的所有功能 刷新上下文环境 初始化beanFactory,对xml文件进行读取 对beanFactory进行各种功能填充 子类覆盖方法做额外处理 ...
- tcp/ip 协议栈Linux内核源码分析11 邻居子系统分析二 arp协议的实现处理
内核版本:3.4.39 内核邻居子系统定义了一个基本的框架,使得不同的邻居协议可以共用一套代码.比起其它的内核模块,邻居子系统框架代码还是比较简单易懂的.邻居子系统位于网络层和流量控制子系统中间,它提 ...
- idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(一)
课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...
- apache dubbo 源码分析系列汇总
Dubbo(读音[ˈdʌbəʊ])是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成.后面捐献给了知名的开源社区 ...
- red5源码分析---12
red5源码分析-服务器处理视频数据 接着<red5源码分析-11>,本章假设客户端发来的是视频数据,下面就分析服务器如何处理这些数据的. 根据前面几章的分析,基于mina框架,数据到达服 ...
- Java源码详解六:ConcurrentHashMap源码分析--openjdk java 11源码
文章目录 注释 类的继承与实现 数据的存储 构造函数 哈希 put get 扩容 本系列是Java详解,专栏地址:Java源码分析 ConcurrentHashMap 官方文档:ConcurrentH ...
- Java源码详解四:String源码分析--openjdk java 11源码
文章目录 注释 类的继承 数据的存储 构造函数 charAt函数 equals函数 hashCode函数 indexOf函数 intern函数 本系列是Java详解,专栏地址:Java源码分析 Str ...
- Java源码详解五:ArrayList源码分析--openjdk java 11源码
文章目录 注释 类的继承与实现 构造函数 add操作 扩容函数 remove函数 subList函数 总结 本系列是Java详解,专栏地址:Java源码分析 ArrayList 官方文档:ArrayL ...
最新文章
- 使用钩子函数[4] - 钩子链和 CallNextHookEx 的返回值
- Vieri离开国际米兰
- SpringBoot中oauth2.0学习之服务端配置快速上手
- pyplot输出的绘图界面中文乱码的解决方案
- Python面试真题第二节
- 都是远程办公惹的祸!搜狗输入法为错误推送地震预警信息致歉
- Python项目实践:国家财政数据趋势演算
- MOSS搜索的爬网错误【访问被拒绝。请验证默认内容访问账户是否有权访问此库】...
- 圈圈教你学USB第一章
- python编写摇骰子游戏_Python使用tkinter实现摇骰子小游戏功能的代码
- MacOS 连接网络打印机后一直不能打印
- Flask基础--思维导图
- openssl建立证书和私钥方法
- 八、Web 的攻击技术
- 装完docker电脑黑屏无法启动_教你电脑启动后黑屏该怎么办
- jquery中的$()是什么
- python数据分析优势-用Python做数据分析有哪些优势?
- UINCODE字符串和安全字符串函数(好)
- java struts2模板,Struts2 主题和模板
- Ubuntu外接显示器设置开机镜像显示
热门文章
- 为什么N型MOS管占据主导地位
- dds提取工具_多媒体资源提取工具(MultiExtractor)
- 应届毕业生转行java_应届毕业生、转行人必看!2019最新薪酬报告
- [乱七八糟][转]这不是你想象中的软件产业
- python编程用什么软件-python开发工具有哪些(初学python用什么软件)
- 学术研究科研工具推荐,共9个,拿走不谢!
- Codex Popup List
- OpenAI Codex、DeepMind AlphaCode论文精读阅读笔记
- 思迈特软件Smartbi:大数据分析平台的搭建和BI平台的区别
- 算力网络 — 算力中心