怎样制作基于Cocos2d-x的SLGslg网页游戏制作软件

怎样制作基于Cocos2d-x的SLG游戏_百度知道
怎样制作基于Cocos2d-x的SLG游戏
提问者采纳
Sprite* maskSprite) { &#47/a static method Sprite* HelloWorld, maskSprite-&&#47. 1 RenderTexture * rt = RenderTexture:.:create( maskSprite-&gt::maskedSpriteWithSprite(Sprite* textureSgetContentSize().;getContentSize().&#47
来自团队:
其他类似问题
为您推荐:
cocos2d的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁怎样制作基于Cocos2d-x的SLG游戏-第1章 - 推酷
怎样制作基于Cocos2d-x的SLG游戏-第1章
原创:@涵紫任珊
在前两年《QQ农场》比较流行的时候,想必大家都曾被它虐过千百遍吧。如今,虽然《QQ农场》的热潮已经告一段落,但随着手机游戏的日益发展,更多的农场手游开始兴起,比如同为腾讯旗下的《全民农场》,它便是以其更丰富的内容、更贴近现实的社交互动玩法,将QQ农场取而代之。所以竟然大家都这么热爱农场游戏,那我们接下来就来自己制作一款类似的SLG游戏吧。
SLG游戏既策略模拟类游戏,它可以分很多类型,像农场游戏这样的模拟经营游戏只是其中的一种。农场游戏可以让玩家扮演一个农场的经营者,玩家通过购买种子,耕种、浇水、施肥、除草、收获果实,最后出售给市场来实现整个经营过程,它让玩家在经营农场的同时也可以感受到“作物养成” 带来的乐趣。
本款游戏我们将使用目前最新的Cocos2d-x 3.2引擎,同时结合Tiledmap以及Cocos Studio来制作。所以,在此之前请大家先下载好引擎,TiledMap编辑器和Cocos Studio编辑器。
如果要说到环境搭建,那又该是一篇长篇大论了,所以这里我就不说了,不清楚的同学到网上问度娘吧,会有很多不同系统不同版本搭建的结果的。
Cocos2d-x创建项目的方式一直都在不停的改动,所以这里我觉得很有必要给大家介绍下如何创建项目。
创建3.2的项目其实很简单,打开终端(Windows是cmd)进入到引擎文件夹目录,然后输入以下命令就可以创建。
cocos new SLG -p com.cocos2dx.rs -l cpp -d /Users/cocos2d-x/workspace/cocos2dx/cocos2d-x-3.2/projects
new:new后是项目名
-p :-p后是包名
-l :-l后是语言(cpp指c++)
-d :-d后是项目生成路径
如果一切无误,那等待几分钟以后你就可以在给定的目录下找到新建的项目了。打开项目工程后,下面我们就可以开始游戏的制作了。
打开工程后,运行程序,你会发现它不是一个空的项目,在Classes和Resource文件夹下Cocos2d-x已经给出了一些实质性的东西,当然这些不是一定都有用的,它们存在的目的是为了给我们展示一个典型的Cocos2d-x的例子。
除了AppDelegate.h 和 AppDelegate.cpp文件,这两个文件夹下其他的东西都是可被删除的(不过在删除之前,可以先看一下HelloWorld类,了解下它的类结构、类方法,以便对Cocos2d-x进行初步的学习,也方便初学者依葫芦画瓢再写一个类似的场景)。AppDelegate类是创建项目时自动生成的一个类,它控制着游戏的生命周期,是Cocos2d-x游戏的通用入口文件,类似于一般 Windows 工程中main函数所在的文件。
打开AppDelegate.cpp文件,在游戏加载期的最后一个applicationDidFinishLaunching()函数中我们可以设置第一个启动的游戏场景,如下:
auto scene = GameScene::createScene();
director-&runWithScene(scene);
GameScene是我们新建的一个游戏场景,下面会讲解。
分辨率适配
在我的游戏中,首先第一件事还是做分辨率适配,这是个恒古不变的定律。现如今市场中各种屏幕尺寸和分辨率的移动设备层出不穷,为了更好地适应这些设备,游戏的分辨率适配是十分有必要的。
在农场游戏中,很最要的一点就是实现大地图背景的拖动、放大缩小等操作,所以可想而知,我们的地图不是全部都显示在屏幕内的,也就是说,我们不能像之前的方式那样把整个显示内容做适配,我们应该留有一定的边距供玩家拖动。如下图所示:
同样是在applicationDidFinishLaunching函数中,我们添加如下一段代码对游戏做分辨率适配,以便它能更好的适应不同的运行环境。
glview-&setDesignResolutionSize(480.0f, 320.0f, ResolutionPolicy::FIXED_HEIGHT);
std::vector searchP
searchPath.push_back(&H_1920&);
FileUtils::getInstance()-&setSearchPaths(searchPath);
director-&setContentScaleFactor(1440.0f / 320.0f);
分辨率适配的原理,建议大家阅读一下:
这篇文章,虽然它不是针对最新版Cocos2dx引擎,但它还是能很清楚的告诉你分辨率适配的原理和方法。
还有要说明的一点是,我们的游戏地图的高为1920,分辨率适配时则只设为了1440,意思就是说,我们本该全在屏幕内的内容留出了4分之一的高度在屏幕外。
编辑游戏地图
模拟经营游戏中,游戏地图多为拼接而成,这里我们用瓦片地图编辑器(Tiled Map Editor)来制作游戏的地图,它可以把编辑后的地图文件保存为TMX格式的文件,能被Cocos2d-x很好的支持。瓦片地图(Tile Map)不但生成简单,而且可以灵活的用于引擎中。关于瓦片地图的介绍可参考
接下来我们开始创建地图。
运行TiledMap编辑器,新建一个地图文件。填写如下对话框:
在地图方向选项内,可以选择正常、45度(传说中的2.5D)和45度交错,这里我们选择45度。接下来需要设置地图大小,这里的数值是指有多少格tile元件,并不是像素,这里我们选择30&30的地图。
最后是确定tile元件的大小,根据美工提供的地面元件大小设置,这个教程里,我们使用128&64的大小。
在地图大小一栏中,你可以看到最终的地图大小为3840 * 1920。
点击确认之后,你可能已经发现了,这个游戏地图它是菱形的,如下图所示。
这里你可能会想,为什么要菱形的啦,选择45度交错建一个接近矩形的不行吗?呵呵,其实这样也是可以的,只不过啦,Cocos2d-x引擎中默认是不支持45度交错的,如果需要在引擎中加载这种交错的地图必须自己修改引擎代码(看到这,是不是整个人都不好了),而且在修改过后还不能正确的得到地图的大小,需要自己编写计算大小的公式代码。鉴于这一点,我在想,难道《全名农场》、《请叫我海盗》等游戏地图的四个角都不能被点击操作都是因为这个原因吗? 哈哈,就当是我想多了吧。其实菱形就菱形吧,其他的游戏也都这样,可以在菱形地图的下层贴一层背景来掩盖这一现象。
接下来,我们还是回到正题开始拼接地图吧。选择地图-》新图块,然后填写如下所示的对话框。
选择浏览按钮,将准备好的图块文件载入编辑器。接着设置图块的宽度和高度(默认情况下是一个tile元件的大小,但),根据图块文件中图块的大小来设置。边距、间距什么的,可不做修改,0就好。
最后选中相应的图块,拖动到渲染区拼一个理想的地图。
暂时我们就只简单的拼一个地图就可以了,后面再根据游戏需要,设置一些必要的对象和属性。
加载地图资源
新建一个GameScene场景,加载游戏地图。不过在此之前,请把编辑好的tmx和图块文件拷贝到Resource文件夹下。GameScene的结构和HelloWorld差不多,照着HelloWorld类依葫芦画瓢就可以建一个。你可以先看看它是如何实现的,再实现自己的类。这里就不多说了。
在Cocos2d-x中使用TMX,有以下流程供你参考:
首先用地图编辑器编辑你的地图,导出成TMX 格式。
将导出的TMX 文件和相关图片放在工程的Resoure文件夹下。
使用Cocos2d-x中TMXTileMap 类的create方法创建地图对象,TMX 文件的解析是引擎内部完成的,所以我们不需要担心。TMXTileMap 是Node 的子类,因此只要添加到场景中即可。
通过TMXTileMap,可以获得其他相关对象,比如单个瓦片(属Sprite类),比如对象组(ObjectGroup类),比如层(TMXLayer类)等;你可以通过TMXLayer类修改,删除或者添加某个网格位置的瓦片,这样可以动态的修改地图了,你还可以进行其他的操作,相关的API 我们后面使用到了再做讲解。
在GameScene类的init函数中添加如下的代码创建游戏地图:
mapLayer = LayerColor::create(Color4B(78,127,41,255));
this-&addChild(mapLayer,-1);
auto map = TMXTiledMap::create(&mymap4.tmx&);
mapLayer-&setContentSize(map-&getContentSize());
mapLayer-&addChild(map, 10);
auto treeSprite = Sprite::create(&1.png&);
treeSprite-&setAnchorPoint(Vec2(0, 0));
treeSprite-&setPosition(Vec2(0, 0));
treeSprite-&setScale(2);
mapLayer-&addChild(treeSprite, 11);
代码中新建了一个带颜色的背景层,背景层的尺寸等于加载的TMX地图大小。接着把地图和如下的一个遮盖层(其实就是为了防止菱形的地图看起来那么突兀而添加的一层)依次添加到层上。
运行程序,你将看到如下图所示的游戏场景:
本教程的美术资源不是很完善,大家就将就看吧,见谅!
下章我们将实现游戏背景的单指拖动,双指缩放的功能,以及利用Cocos Studio来制作一个支持多分辨率的UI系统。
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
排版有问题
没有分页内容
视频无法显示
图片无法显示cocos2d-x三国策略战争游戏源码
dotawar-master
SceneSetting.cpp
AppController.mm
RootViewController.mm
constraints
constraints
CMakeLists.txt
chipmunk-docs.html
LICENSE.txt
README.txt
base_nodes
draw_nodes
CMakeLists.txt
keypad_dispatcher
label_nodes
layers_scenes_transitions_nodes
menu_nodes
misc_nodes
particle_nodes
Simulation
AccelerometerDelegateWrapper.mm
CCAccelerometer.mm
CCApplication.mm
CCCommon.mm
CCDevice.mm
CCDirectorCaller.mm
CCEGLView.mm
CCFileUtilsIOS.mm
CCImage.mm
CCThread.mm
EAGLView.mm
third_party
script_support
sprite_nodes
data_support
image_support
user_default
CCUserDefault.mm
zip_support
text_input_node
tilemap_parallax_nodes
touch_dispatcher
CocosDenshion
SimpleAudioEngine.mm
CocosDenshion.xcodeproj
project.pbxproj
extensions
AssetsManager
Components
CCControlExtension
CCEditBoxImplIOS.mm
CCEditBoxImplMac.mm
CCScrollView
LocalStorage
physics_nodes
libwebsockets
cocos2dx_support
CCLuaObjcBridge.mm
AudioEngine.lua
CCBReaderLoad.lua
ActorsPack1.plist
ActorsPack1.png
CloseNormal.png
CloseSelected.png
Default.png
fps_images-hd.png
fps_images-ipadhd.png
fps_images.png
GameDate.plist
GameUI01.plist
GameUI01.png
GameUI02.plist
GameUI02.png
grass_ground.png
health_bar_green.png
health_bar_red.png
HelloWorld.png
Icon-72.png
Icon-Small-50.png
Icon-Small.png
Icon-Small@2x.png
Icon@2x.png
Info.plist
iTunesArtwork
Level0.tmx
actorData.lua
Prefix.pch
涓夊浗war.xcodeproj
project.xcworkspace
xcshareddata
涓夊浗war.xccheckout
xcuserdata
ericwang.xcuserdatad
UserInterfaceState.xcuserstate
sev.xcuserdatad
UserInterfaceState.xcuserstate
contents.xcworkspacedata
xcuserdata
ericwang.xcuserdatad
xcschememanagement.plist
涓夊浗war.xcscheme
sev.xcuserdatad
xcdebugger
Breakpoints_v2.xcbkptlist
xcschememanagement.plist
涓夊浗war.xcscheme
project.pbxproj
Level0.tmx
dotawar-master
._AppController.mm
._RootViewController.mm
constraints
._constraints
._chipmunk
constraints
._CMakeLists.txt
._constraints
._chipmunk-docs.html
._LICENSE.txt
._README.txt
base_nodes
draw_nodes
._ChangeLog
._CMakeLists.txt
keypad_dispatcher
label_nodes
layers_scenes_transitions_nodes
menu_nodes
misc_nodes
particle_nodes
Simulation
._AccelerometerDelegateWrapper.mm
._CCAccelerometer.mm
._CCApplication.mm
._CCCommon.mm
._CCDevice.mm
._CCDirectorCaller.mm
._CCEGLView.mm
._CCFileUtilsIOS.mm
._CCImage.mm
._CCThread.mm
._EAGLView.mm
._Simulation
third_party
._libraries
._third_party
script_support
sprite_nodes
data_support
image_support
user_default
._CCUserDefault.mm
zip_support
._component
._data_support
._image_support
._tinyxml2
._user_default
._zip_support
text_input_node
tilemap_parallax_nodes
touch_dispatcher
._base_nodes
._draw_nodes
._keypad_dispatcher
._label_nodes
._layers_scenes_transitions_nodes
._menu_nodes
._misc_nodes
._particle_nodes
._platform
._script_support
._sprite_nodes
._textures
._text_input_node
._tilemap_parallax_nodes
._touch_dispatcher
CocosDenshion
._SimpleAudioEngine.mm
CocosDenshion.xcodeproj
._project.pbxproj
._CocosDenshion.xcodeproj
._proj.ios
extensions
AssetsManager
Components
CCControlExtension
._CCEditBoxImplIOS.mm
._CCEditBoxImplMac.mm
CCScrollView
._CCControlExtension
._CCEditBox
._CCScrollView
LocalStorage
physics_nodes
._AssetsManager
._CCBReader
._Components
._LocalStorage
._physics_nodes
libwebsockets
cocos2dx_support
._CCLuaObjcBridge.mm
._platform
._AudioEngine.lua
._CCBReaderLoad.lua
._cocos2dx_support
._chipmunk
._cocos2dx
._CocosDenshion
._extensions
._libwebsockets
._ActorsPack1.plist
._ActorsPack1.png
._CloseNormal.png
._CloseSelected.png
._Default.png
._fps_images-hd.png
._fps_images-ipadhd.png
._fps_images.png
._GameDate.plist
._GameUI01.plist
._GameUI01.png
._GameUI02.plist
._GameUI02.png
._grass_ground.png
._health_bar_green.png
._health_bar_red.png
._HelloWorld.png
._Icon-72.png
._Icon-Small-50.png
._Icon-Small.png
._Icon-Small@2x.png
._Icon.png
._Icon@2x.png
._Info.plist
._iTunesArtwork
._Level0.tmx
._actor.lua
._actorData.lua
._Prefix.pch
._Resources
涓夊浗war.xcodeproj
project.xcworkspace
xcshareddata
._涓夊浗war.xccheckout
xcuserdata
sev.xcuserdatad
._UserInterfaceState.xcuserstate
._sev.xcuserdatad
._contents.xcworkspacedata
._xcshareddata
._xcuserdata
xcuserdata
sev.xcuserdatad
xcdebugger
._Breakpoints_v2.xcbkptlist
._xcschememanagement.plist
._涓夊浗war.xcscheme
._xcdebugger
._xcschemes
._sev.xcuserdatad
._project.xcworkspace
._xcuserdata
._.DS_Store
._Level0.tmx
._README.md
._涓夊浗war
._涓夊浗war.xcodeproj
._dotawar-master
源代码说明.txt
Copyright (c) 2010 Steve Oldmeadow
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &Software&), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &AS IS&, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
#import &CocosDenshion.h&
alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
alcMacOSXMixerOutputRateProc(const ALdouble value);
typedef ALvoid
AL_APIENTRY
(*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
alBufferDataStaticProcPtr
proc = NULL;
if (proc == NULL) {
proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) &alBufferDataStatic&);
proc(bid, format, data, size, freq);
typedef ALvoid
AL_APIENTRY
(*alcMacOSXMixerOutputRateProcPtr) (const ALdouble value);
alcMacOSXMixerOutputRateProc(const ALdouble value)
alcMacOSXMixerOutputRateProcPtr
proc = NULL;
if (proc == NULL) {
proc = (alcMacOSXMixerOutputRateProcPtr) alcGetProcAddress(NULL, (const ALCchar*) &alcMacOSXMixerOutputRate&);
proc(value);
NSString * const kCDN_BadAlContext = @&kCDN_BadAlContext&;
NSString * const kCDN_AsynchLoadComplete = @&kCDN_AsynchLoadComplete&;
float const kCD_PitchDefault = 1.0f;
float const kCD_PitchLowerOneOctave = 0.5f;
float const kCD_PitchHigherOneOctave = 2.0f;
float const kCD_PanDefault = 0.0f;
float const kCD_PanFullLeft = -1.0f;
float const kCD_PanFullRight = 1.0f;
float const kCD_GainDefault = 1.0f;
@interface CDSoundEngine (PrivateMethods)
-(BOOL) _initOpenAL;
-(void) _testGetG
-(void) _dumpSourceGroupsI
-(void) _getSourceIndexForSourceG
-(void) _freeSourceG
-(BOOL) _setUpSourceGroups:(int[]) definitions total:(NSUInteger)
#pragma mark -
#pragma mark CDUtilities
@implementation CDUtilities
+(NSString*) fullPathFromRelativePath:(NSString*) relPath
// do not convert an absolute path (starting with '/')
if(([relPath length] & 0) && ([relPath characterAtIndex:0] == '/'))
return relP
NSMutableArray *imagePathComponents = [NSMutableArray arrayWithArray:[relPath pathComponents]];
NSString *file = [imagePathComponents lastObject];
[imagePathComponents removeLastObject];
NSString *imageDirectory = [NSString pathWithComponents:imagePathComponents];
NSString *fullpath = [[NSBundle mainBundle] pathForResource:file ofType:nil inDirectory:imageDirectory];
if (fullpath == nil)
fullpath = relP
#pragma mark -
#pragma mark CDSoundEngine
@implementation CDSoundEngine
static Float32 _mixerSampleR
static BOOL _mixerRateSet = NO;
@synthesize lastErrorCode = lastErrorCode_;
@synthesize functioning = functioning_;
@synthesize asynchLoadProgress = asynchLoadProgress_;
@synthesize getGainWorks = getGainWorks_;
@synthesize sourceTotal = sourceTotal_;
+ (void) setMixerSampleRate:(Float32) sampleRate {
_mixerRateSet = YES;
_mixerSampleRate = sampleR
- (void) _testGetGain {
float testValue = 0.7f;
ALuint testSourceId = _sources[0].sourceId;
alSourcef(testSourceId, AL_GAIN, 0.0f);//Start from know value
alSourcef(testSourceId, AL_GAIN, testValue);
ALfloat gainV
alGetSourcef(testSourceId, AL_GAIN, &gainVal);
getGainWorks_ = (gainVal == testValue);
//Generate sources one at a time until we fail
-(void) _generateSources {
_sources = (sourceInfo*)malloc( sizeof(_sources[0]) * CD_SOURCE_LIMIT);
BOOL hasFailed = NO;
sourceTotal_ = 0;
alGetError();//Clear error
while (!hasFailed && sourceTotal_ & CD_SOURCE_LIMIT) {
alGenSources(1, &(_sources[sourceTotal_].sourceId));
if (alGetError() == AL_NO_ERROR) {
//Now try attaching source to null buffer
alSourcei(_sources[sourceTotal_].sourceId, AL_BUFFER, 0);
if (alGetError() == AL_NO_ERROR) {
_sources[sourceTotal_].usable =
sourceTotal_++;
hasFailed = YES;
_sources[sourceTotal_].usable =
hasFailed = YES;
//Mark the rest of the sources as not usable
for (int i=sourceTotal_; i & CD_SOURCE_LIMIT; i++) {
_sources[i].usable =
-(void) _generateBuffers:(int) startIndex endIndex:(int) endIndex {
if (_buffers) {
alGetError();
for (int i=startI i &= endI i++) {
alGenBuffers(1, &_buffers[i].bufferId);
_buffers[i].bufferData = NULL;
if (alGetError() == AL_NO_ERROR) {
_buffers[i].bufferState = CD_BS_EMPTY;
_buffers[i].bufferState = CD_BS_FAILED;
CDLOG(@&Denshion::CDSoundEngine - buffer creation failed %i&,i);
* Internal method called during init
- (BOOL) _initOpenAL
context = NULL;
*newDevice = NULL;
//Set the mixer rate for the audio mixer
if (!_mixerRateSet) {
_mixerSampleRate = CD_SAMPLE_RATE_DEFAULT;
alcMacOSXMixerOutputRateProc(_mixerSampleRate);
CDLOGINFO(@&Denshion::CDSoundEngine - mixer output rate set to %0.2f&,_mixerSampleRate);
// Create a new OpenAL Device
// Pass NULL to specify the system's default output device
newDevice = alcOpenDevice(NULL);
if (newDevice != NULL)
// Create a new OpenAL Context
// The new context will render to the OpenAL Device just created
context = alcCreateContext(newDevice, 0);
if (context != NULL)
// Make the new context the Current OpenAL Context
alcMakeContextCurrent(context);
// Create some OpenAL Buffer Objects
[self _generateBuffers:0 endIndex:bufferTotal-1];
// Create some OpenAL Source Objects
[self _generateSources];
return FALSE;//No device
alGetError();//Clear error
return TRUE;
- (void) dealloc {
ALCcontext
*currentContext = NULL;
*device = NULL;
[self stopAllSounds];
CDLOGINFO(@&Denshion::CDSoundEngine - Deallocing sound engine.&);
[self _freeSourceGroups];
// Delete the Sources
CDLOGINFO(@&Denshion::CDSoundEngine - deleting sources.&);
for (int i=0; i & sourceTotal_; i++) {
alSourcei(_sources[i].sourceId, AL_BUFFER, 0);//Detach from current buffer
alDeleteSources(1, &(_sources[i].sourceId));
if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
CDLOG(@&Denshion::CDSoundEngine - Error deleting source! %x\n&, lastErrorCode_);
// Delete the Buffers
CDLOGINFO(@&Denshion::CDSoundEngine - deleting buffers.&);
for (int i=0; i & bufferT i++) {
alDeleteBuffers(1, &_buffers[i].bufferId);
#ifdef CD_USE_STATIC_BUFFERS
if (_buffers[i].bufferData) {
free(_buffers[i].bufferData);
CDLOGINFO(@&Denshion::CDSoundEngine - free buffers.&);
free(_buffers);
currentContext = alcGetCurrentContext();
//Get device for active context
device = alcGetContextsDevice(currentContext);
//Release context
CDLOGINFO(@&Denshion::CDSoundEngine - destroy context.&);
alcDestroyContext(currentContext);
//Close device
CDLOGINFO(@&Denshion::CDSoundEngine - close device.&);
alcCloseDevice(device);
CDLOGINFO(@&Denshion::CDSoundEngine - free sources.&);
free(_sources);
//Release mutexes
[_mutexBufferLoad release];
[super dealloc];
-(NSUInteger) sourceGroupTotal {
return _sourceGroupT
-(void) _freeSourceGroups
CDLOGINFO(@&Denshion::CDSoundEngine freeing source groups&);
if(_sourceGroups) {
for (int i=0; i & _sourceGroupT i++) {
if (_sourceGroups[i].sourceStatuses) {
free(_sourceGroups[i].sourceStatuses);
CDLOGINFO(@&Denshion::CDSoundEngine freed source statuses %i&,i);
free(_sourceGroups);
-(BOOL) _redefineSourceGroups:(int[]) definitions total:(NSUInteger) total
if (_sourceGroups) {
//Stop all sounds
[self stopAllSounds];
//Need to free source groups
[self _freeSourceGroups];
return [self _setUpSourceGroups:definitions total:total];
-(BOOL) _setUpSourceGroups:(int[]) definitions total:(NSUInteger) total
_sourceGroups = (sourceGroup *)malloc( sizeof(_sourceGroups[0]) * total);
if(!_sourceGroups) {
CDLOG(@&Denshion::CDSoundEngine - source groups memory allocation failed&);
return NO;
_sourceGroupTotal =
int sourceCount = 0;
for (int i=0; i & _sourceGroupT i++) {
_sourceGroups[i].startIndex = 0;
_sourceGroups[i].currentIndex = _sourceGroups[i].startI
_sourceGroups[i].enabled =
_sourceGroups[i].nonInterruptible =
_sourceGroups[i].totalSources = definitions[i];
_sourceGroups[i].sourceStatuses = malloc(sizeof(_sourceGroups[i].sourceStatuses[0]) * _sourceGroups[i].totalSources);
if (_sourceGroups[i].sourceStatuses) {
for (int j=0; j & _sourceGroups[i].totalS j++) {
//First bit is used to indicate whether source is locked, index is shifted back 1 bit
_sourceGroups[i].sourceStatuses[j] = (sourceCount + j) && 1;
sourceCount += definitions[i];
return YES;
-(void) defineSourceGroups:(int[]) sourceGroupDefinitions total:(NSUInteger) total {
[self _redefineSourceGroups:sourceGroupDefinitions total:total];
-(void) defineSourceGroups:(NSArray*) sourceGroupDefinitions {
CDLOGINFO(@&Denshion::CDSoundEngine - source groups defined by NSArray.&);
NSUInteger totalDefs = [sourceGroupDefinitions count];
int* defs = (int *)malloc( sizeof(int) * totalDefs);
int currentIndex = 0;
for (id currentDef in sourceGroupDefinitions) {
if ([currentDef isKindOfClass:[NSNumber class]]) {
defs[currentIndex] = (int)[(NSNumber*)currentDef integerValue];
CDLOGINFO(@&Denshion::CDSoundEngine - found definition %i.&,defs[currentIndex]);
CDLOG(@&Denshion::CDSoundEngine - warning, did not understand source definition.&);
defs[currentIndex] = 0;
currentIndex++;
[self _redefineSourceGroups:defs total:totalDefs];
free(defs);
- (id)init
if ((self = [super init])) {
//Create mutexes
_mutexBufferLoad = [[NSObject alloc] init];
asynchLoadProgress_ = 0.0f;
bufferTotal = CD_BUFFERS_START;
_buffers = (bufferInfo *)malloc( sizeof(_buffers[0]) * bufferTotal);
// Initialize our OpenAL environment
if ([self _initOpenAL]) {
//Set up the default source group - a single group that contains all the sources
int sourceDefs[1];
sourceDefs[0] = self.sourceT
[self _setUpSourceGroups:sourceDefs total:1];
functioning_ = YES;
//Synchronize premute gain
_preMuteGain = self.masterG
mute_ = NO;
enabled_ = YES;
//Test whether get gain works for sources
[self _testGetGain];
//Something went wrong with OpenAL
functioning_ = NO;
* Delete the buffer identified by soundId
* @return true if buffer deleted successfully, otherwise false
- (BOOL) unloadBuffer:(int) soundId
//Ensure soundId is within array bounds otherwise memory corruption will occur
if (soundId & 0 || soundId &= bufferTotal) {
CDLOG(@&Denshion::CDSoundEngine - soundId is outside array bounds, maybe you need to increase CD_MAX_BUFFERS&);
return FALSE;
//Before a buffer can be deleted any sources that are attached to it must be stopped
for (int i=0; i & sourceTotal_; i++) {
//Note: tried getting the AL_BUFFER attribute of the source instead but doesn't
//appear to work on a device - just returned zero.
if (_buffers[soundId].bufferId == _sources[i].attachedBufferId) {
CDLOG(@&Denshion::CDSoundEngine - Found attached source %i %i %i&,i,_buffers[soundId].bufferId,_sources[i].sourceId);
#ifdef CD_USE_STATIC_BUFFERS
//When using static buffers a crash may occur if a source is playing with a buffer that is about
//to be deleted even though we stop the source and successfully delete the buffer. Crash is confirmed
//on 2.2.1 and 3.1.2, however, it will only occur if a source is used rapidly after having its prior
//data deleted. To avoid any possibility of the crash we wait for the source to finish playing.
alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state);
if (state == AL_PLAYING) {
CDLOG(@&Denshion::CDSoundEngine - waiting for source to complete playing before removing buffer data&);
alSourcei(_sources[i].sourceId, AL_LOOPING, FALSE);//Turn off looping otherwise loops will never end
while (state == AL_PLAYING) {
alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state);
usleep(10000);
//Stop source and detach
alSourceStop(_sources[i].sourceId);
if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
CDLOG(@&Denshion::CDSoundEngine - error stopping source: %x\n&, lastErrorCode_);
alSourcei(_sources[i].sourceId, AL_BUFFER, 0);//Attach to &NULL& buffer to detach
if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
CDLOG(@&Denshion::CDSoundEngine - error detaching buffer: %x\n&, lastErrorCode_);
//Record that source is now attached to nothing
_sources[i].attachedBufferId = 0;
alDeleteBuffers(1, &_buffers[soundId].bufferId);
if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
CDLOG(@&Denshion::CDSoundEngine - error deleting buffer: %x\n&, lastErrorCode_);
_buffers[soundId].bufferState = CD_BS_FAILED;
return FALSE;
#ifdef CD_USE_STATIC_BUFFERS
//Free previous data, if alDeleteBuffer has returned without error then no
if (_buffers[soundId].bufferData) {
CDLOGINFO(@&Denshion::CDSoundEngine - freeing static data for soundId %i @ %i&,soundId,_buffers[soundId].bufferData);
free(_buffers[soundId].bufferData);//Free the old data
_buffers[soundId].bufferData = NULL;
alGenBuffers(1, &_buffers[soundId].bufferId);
if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
CDLOG(@&Denshion::CDSoundEngine - error regenerating buffer: %x\n&, lastErrorCode_);
_buffers[soundId].bufferState = CD_BS_FAILED;
return FALSE;
//We now have an empty buffer
_buffers[soundId].bufferState = CD_BS_EMPTY;
CDLOGINFO(@&Denshion::CDSoundEngine - buffer %i successfully unloaded\n&,soundId);
return TRUE;
* Load buffers asynchronously
* Check asynchLoadProgress for progress. asynchLoadProgress represents fraction of completion. When it equals 1.0 loading
* is complete. NB: asynchLoadProgress is simply based on the number of load requests, it does not take into account
* file sizes.
* @param An array of CDBufferLoadRequest objects
- (void) loadBuffersAsynchronously:(NSArray *) loadRequests {
@synchronized(self) {
asynchLoadProgress_ = 0.0f;
CDAsynchBufferLoader *loaderOp = [[[CDAsynchBufferLoader alloc] init:loadRequests soundEngine:self] autorelease];
NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease];
[opQ addOperation:loaderOp];
-(BOOL) _resizeBuffers:(int) increment {
void * tmpBufferInfos = realloc( _buffers, sizeof(_buffers[0]) * (bufferTotal + increment) );
if(!tmpBufferInfos) {
free(tmpBufferInfos);
return NO;
_buffers = tmpBufferI
int oldBufferTotal = bufferT
bufferTotal = bufferTotal +
[self _generateBuffers:oldBufferTotal endIndex:bufferTotal-1];
return YES;
-(BOOL) loadBufferFromData:(int) soundId soundData:(ALvoid*) soundData format:(ALenum) format size:(ALsizei) size freq:(ALsizei) freq {
@synchronized(_mutexBufferLoad) {
if (!functioning_) {
//OpenAL initialisation has previously failed
CDLOG(@&Denshion::CDSoundEngine - Loading buffer failed because sound engine state != functioning&);
return FALSE;
//Ensure soundId is within array bounds otherwise memory corruption will occur
if (soundId & 0) {
CDLOG(@&Denshion::CDSoundEngine - soundId is negative&);
return FALSE;
if (soundId &= bufferTotal) {
//Need to resize the buffers
int requiredIncrement = CD_BUFFERS_INCREMENT;
while (bufferTotal + requiredIncrement & soundId) {
requiredIncrement += CD_BUFFERS_INCREMENT;
CDLOGINFO(@&Denshion::CDSoundEngine - attempting to resize buffers by %i for sound %i&,requiredIncrement,soundId);
if (![self _resizeBuffers:requiredIncrement]) {
CDLOG(@&Denshion::CDSoundEngine - buffer resize failed&);
return FALSE;
if (soundData)
if (_buffers[soundId].bufferState != CD_BS_EMPTY) {
CDLOGINFO(@&Denshion::CDSoundEngine - non empty buffer, regenerating&);
if (![self unloadBuffer:soundId]) {
//Deletion of buffer failed, delete buffer routine has set buffer state and lastErrorCode
return NO;
#ifdef CD_DEBUG
//Check that sample rate matches mixer rate and warn if they do not
if (freq != (int)_mixerSampleRate) {
CDLOGINFO(@&Denshion::CDSoundEngine - WARNING sample rate does not match mixer sample rate performance may not be optimal.&);
#ifdef CD_USE_STATIC_BUFFERS
alBufferDataStaticProc(_buffers[soundId].bufferId, format, soundData, size, freq);
_buffers[soundId].bufferData =//Save the pointer to the new data
alBufferData(_buffers[soundId].bufferId, format, soundData, size, freq);
if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
CDLOG(@&Denshion::CDSoundEngine -
error attaching audio to buffer: %x&, lastErrorCode_);
_buffers[soundId].bufferState = CD_BS_FAILED;
return FALSE;
CDLOG(@&Denshion::CDSoundEngine Buffer data is null!&);
_buffers[soundId].bufferState = CD_BS_FAILED;
return FALSE;
_buffers[soundId].format =
_buffers[soundId].sizeInBytes =
_buffers[soundId].frequencyInHertz =
_buffers[soundId].bufferState = CD_BS_LOADED;
CDLOGINFO(@&Denshion::CDSoundEngine Buffer %i loaded format:%i freq:%i size:%i&,soundId,format,freq,size);
return TRUE;
}//end mutex
* Load sound data for later play back.
* @return TRUE if buffer loaded okay for play back otherwise false
- (BOOL) loadBuffer:(int) soundId filePath:(NSString*) filePath
CDLOGINFO(@&Denshion::CDSoundEngine - Loading openAL buffer %i %@&, soundId, filePath);
CFURLRef fileURL =
NSString *path = [CDUtilities fullPathFromRelativePath:filePath];
if (path) {
fileURL = (CFURLRef)[[NSURL fileURLWithPath:path] retain];
if (fileURL)
data = CDGetOpenALAudioData(fileURL, &size, &format, &freq);
CFRelease(fileURL);
BOOL result = [self loadBufferFromData:soundId soundData:data format:format size:size freq:freq];
#ifndef CD_USE_STATIC_BUFFERS
free(data);//Data can be freed here because alBufferData performs a memcpy
CDLOG(@&Denshion::CDSoundEngine Could not find file!\n&);
//Don't change buffer state here as it will be the same as before method was called
return FALSE;
-(BOOL) validateBufferId:(int) soundId {
if (soundId & 0 || soundId &= bufferTotal) {
CDLOGINFO(@&Denshion::CDSoundEngine - validateBufferId buffer outside range %i&,soundId);
return NO;
} else if (_buffers[soundId].bufferState != CD_BS_LOADED) {
CDLOGINFO(@&Denshion::CDSoundEngine - validateBufferId invalide buffer state %i&,soundId);
return NO;
return YES;
-(float) bufferDurationInSeconds:(int) soundId {
if ([self validateBufferId:soundId]) {
float factor = 0.0f;
switch (_buffers[soundId].format) {
case AL_FORMAT_MONO8:
factor = 1.0f;
case AL_FORMAT_MONO16:
factor = 0.5f;
case AL_FORMAT_STEREO8:
factor = 0.5f;
case AL_FORMAT_STEREO16:
factor = 0.25f;
return (float)_buffers[soundId].sizeInBytes/(float)_buffers[soundId].frequencyInHertz *
return -1.0f;
-(ALsizei) bufferSizeInBytes:(int) soundId {
if ([self validateBufferId:soundId]) {
return _buffers[soundId].sizeInB
return -1.0f;
-(ALsizei) bufferFrequencyInHertz:(int) soundId {
if ([self validateBufferId:soundId]) {
return _buffers[soundId].frequencyInH
return -1.0f;
- (ALfloat) masterGain {
if (mute_) {
//When mute the real gain will always be 0 therefore return the preMuteGain value
return _preMuteG
alGetListenerf(AL_GAIN, &gain);
* Overall gain setting multiplier. e.g 0.5 is half the gain.
- (void) setMasterGain:(ALfloat) newGainValue {
if (mute_) {
_preMuteGain = newGainV
alListenerf(AL_GAIN, newGainValue);
#pragma mark CDSoundEngine AudioInterrupt protocol
- (BOOL) mute {
return mute_;
* Setting mute silences all sounds but playing sounds continue to advance playback
- (void) setMute:(BOOL) newMuteValue {
if (newMuteValue == mute_) {
mute_ = newMuteV
if (mute_) {
//Remember what the gain was
_preMuteGain = self.masterG
//Set gain to 0 - do not use the property as this will adjust preMuteGain when muted
alListenerf(AL_GAIN, 0.0f);
//Restore gain to what it was before being muted
self.masterGain = _preMuteG
- (BOOL) enabled {
return enabled_;
- (void) setEnabled:(BOOL)enabledValue
if (enabled_ == enabledValue) {
enabled_ = enabledV
if (enabled_ == NO) {
[self stopAllSounds];
-(void) _lockSource:(int) sourceIndex lock:(BOOL) lock {
BOOL found = NO;
for (int i=0; i & _sourceGroupTotal && ! i++) {
if (_sourceGroups[i].sourceStatuses) {
for (int j=0; j & _sourceGroups[i].totalSources && ! j++) {
//First bit is used to indicate whether source is locked, index is shifted back 1 bit
if((_sourceGroups[i].sourceStatuses[j] && 1)==sourceIndex) {
if (lock) {
//Set first bit to lock this source
_sourceGroups[i].sourceStatuses[j] |= 1;
//Unset first bit to unlock this source
_sourceGroups[i].sourceStatuses[j] &= ~1;
found = YES;
-(int) _getSourceIndexForSourceGroup:(int)sourceGroupId
//Ensure source group id is valid to prevent memory corruption
if (sourceGroupId & 0 || sourceGroupId &= _sourceGroupTotal) {
CDLOG(@&Denshion::CDSoundEngine invalid source group id %i&,sourceGroupId);
return CD_NO_SOURCE;
int sourceIndex = -1;//Using -1 to indicate no source found
BOOL complete = NO;
ALint sourceState = 0;
sourceGroup *thisSourceGroup = &_sourceGroups[sourceGroupId];
thisSourceGroup-&currentIndex = thisSourceGroup-&startI
while (!complete) {
//Iterate over sources looking for one that is not locked, first bit indicates if source is locked
if ((thisSourceGroup-&sourceStatuses[thisSourceGroup-&currentIndex] & 1) == 0) {
//This source is not locked
sourceIndex = thisSourceGroup-&sourceStatuses[thisSourceGroup-&currentIndex] && 1;//shift back to get the index
if (thisSourceGroup-&nonInterruptible) {
//Check if this source is playing, if so it can't be interrupted
alGetSourcei(_sources[sourceIndex].sourceId, AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_PLAYING) {
//complete = YES;
//Set start index so next search starts at the next position
thisSourceGroup-&startIndex = thisSourceGroup-&currentIndex + 1;
sourceIndex = -1;//The source index was no good because the source was playing
//complete = YES;
//Set start index so next search starts at the next position
thisSourceGroup-&startIndex = thisSourceGroup-&currentIndex + 1;
thisSourceGroup-&currentIndex++;
if (thisSourceGroup-&currentIndex &= thisSourceGroup-&totalSources) {
//Reset to the beginning
thisSourceGroup-&currentIndex = 0;
if (thisSourceGroup-&currentIndex == thisSourceGroup-&startIndex) {
//We have looped around and got back to the start
complete = YES;
//Reset start index to beginning if beyond bounds
if (thisSourceGroup-&startIndex &= thisSourceGroup-&totalSources) {
thisSourceGroup-&startIndex = 0;
if (sourceIndex &= 0) {
return sourceI
return CD_NO_SOURCE;
* Play a sound.
* @param soundId the id of the sound to play (buffer id).
* @param SourceGroupId the source group that will be used to play the sound.
* @param pitch pitch multiplier. e.g 1.0 is unaltered, 0.5 is 1 octave lower.
* @param pan stereo position. -1 is fully left, 0 is centre and 1 is fully right.
* @param gain gain multiplier. e.g. 1.0 is unaltered, 0.5 is half the gain
* @param loop should the sound be looped or one shot.
* @return the id of the source being used to play the sound or CD_MUTE if the sound engine is muted or non functioning
* or CD_NO_SOURCE if a problem occurs setting up the source
- (ALuint)playSound:(int) soundId sourceGroupId:(int)sourceGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop {
#ifdef CD_DEBUG
//Sanity check parameters - only in DEBUG
NSAssert(soundId &= 0, @&soundId can not be negative&);
NSAssert(soundId & bufferTotal, @&soundId exceeds limit&);
NSAssert(sourceGroupId &= 0, @&sourceGroupId can not be negative&);
NSAssert(sourceGroupId & _sourceGroupTotal, @&sourceGroupId exceeds limit&);
NSAssert(pitch & 0, @&pitch must be greater than zero&);
NSAssert(pan &= -1 && pan &= 1, @&pan must be between -1 and 1&);
NSAssert(gain &= 0, @&gain can not be negative&);
//If mute or initialisation has failed or buffer is not loaded then do nothing
if (!enabled_ || !functioning_ || _buffers[soundId].bufferState != CD_BS_LOADED || _sourceGroups[sourceGroupId].enabled) {
#ifdef CD_DEBUG
if (!functioning_) {
CDLOGINFO(@&Denshion::CDSoundEngine - sound playback aborted because sound engine is not functioning&);
} else if (_buffers[soundId].bufferState != CD_BS_LOADED) {
CDLOGINFO(@&Denshion::CDSoundEngine - sound playback aborted because buffer %i is not loaded&, soundId);
return CD_MUTE;
int sourceIndex = [self _getSourceIndexForSourceGroup:sourceGroupId];//This method ensures sourceIndex is valid
if (sourceIndex != CD_NO_SOURCE) {
ALuint source = _sources[sourceIndex].sourceId;
ALuint buffer = _buffers[soundId].bufferId;
alGetError();//Clear the error code
alGetSourcei(source, AL_SOURCE_STATE, &state);
if (state == AL_PLAYING) {
alSourceStop(source);
alSourcei(source, AL_BUFFER, buffer);//Attach to sound
alSourcef(source, AL_PITCH, pitch);//Set pitch
alSourcei(source, AL_LOOPING, loop);//Set looping
alSourcef(source, AL_GAIN, gain);//Set gain/volume
float sourcePosAL[] = {pan, 0.0f, 0.0f};//Set position - just using left and right panning
alSourcefv(source, AL_POSITION, sourcePosAL);
alGetError();//Clear the error code
alSourcePlay(source);
if((lastErrorCode_ = alGetError()) == AL_NO_ERROR) {
//Everything was okay
_sources[sourceIndex].attachedBufferId =
if (alcGetCurrentContext() == NULL) {
CDLOGINFO(@&Denshion::CDSoundEngine - posting bad OpenAL context message&);
[[NSNotificationCenter defaultCenter] postNotificationName:kCDN_BadAlContext object:nil];
return CD_NO_SOURCE;
return CD_NO_SOURCE;
-(BOOL) _soundSourceAttachToBuffer:(CDSoundSource*) soundSource soundId:(int) soundId
//Attach the source to the buffer
ALuint source = soundSource-&_sourceId;
ALuint buffer = _buffers[soundId].bufferId;
alGetSourcei(source, AL_SOURCE_STATE, &state);
if (state == AL_PLAYING) {
alSourceStop(source);
alGetError();//Clear the error code
alSourcei(source, AL_BUFFER, buffer);//Attach to sound data
if((lastErrorCode_ = alGetError()) == AL_NO_ERROR) {
_sources[soundSource-&_sourceIndex].attachedBufferId =
//_sourceBufferAttachments[soundSource-&_sourceIndex] =//Keep track of which
soundSource-&_soundId = soundId;
return YES;
return NO;
* Get a sound source for the specified sound in the specified source group
-(CDSoundSource *) soundSourceForSound:(int) soundId sourceGroupId:(int) sourceGroupId
if (!functioning_) {
//Check if a source is available
int sourceIndex = [self _getSourceIndexForSourceGroup:sourceGroupId];
if (sourceIndex != CD_NO_SOURCE) {
CDSoundSource *result = [[CDSoundSource alloc] init:_sources[sourceIndex].sourceId sourceIndex:sourceIndex soundEngine:self];
[self _lockSource:sourceIndex lock:YES];
//Try to attach to the buffer
if ([self _soundSourceAttachToBuffer:result soundId:soundId]) {
//Set to a known state
result.pitch = 1.0f;
result.pan = 0.0f;
result.gain = 1.0f;
result.looping = NO;
return [result autorelease];
//Release the sound source we just created, this will also unlock the source
[result release];
//No available source within that source group
-(void) _soundSourcePreRelease:(CDSoundSource *) soundSource {
CDLOGINFO(@&Denshion::CDSoundEngine _soundSourcePreRelease %i&,soundSource-&_sourceIndex);
//Unlock the sound source's source
[self _lockSource:soundSource-&_sourceIndex lock:NO];
* Stop all sounds playing within a source group
- (void) stopSourceGroup:(int) sourceGroupId {
if (!functioning_ || sourceGroupId &= _sourceGroupTotal || sourceGroupId & 0) {
int sourceCount = _sourceGroups[sourceGroupId].totalS
for (int i=0; i & sourceC i++) {
int sourceIndex = _sourceGroups[sourceGroupId].sourceStatuses[i] && 1;
alSourceStop(_sources[sourceIndex].sourceId);
alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
* Stop a sound playing.
* @param sourceId an OpenAL source identifier i.e. the return value of playSound
- (void)stopSound:(ALuint) sourceId {
if (!functioning_) {
alSourceStop(sourceId);
alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
- (void) stopAllSounds {
for (int i=0; i & sourceTotal_; i++) {
alSourceStop(_sources[i].sourceId);
alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
- (void) pauseSound:(ALuint) sourceId {
if (!functioning_) {
alSourcePause(sourceId);
alGetError();//Clear error in case we pause any sounds that couldn't be paused
- (void) pauseAllSounds {
for (int i = 0; i & sourceTotal_; i++) {
[self pauseSound:_sources[i].sourceId];
alGetError();//Clear error in case we stopped any sounds that couldn't be paused
- (void) resumeSound:(ALuint) soundId {
if (!functioning_) {
// only resume a sound id that is paused
alGetSourcei(soundId, AL_SOURCE_STATE, &state);
if (state != AL_PAUSED)
alSourcePlay(soundId);
alGetError();//Clear error in case we stopped any sounds that couldn't be resumed
- (void) resumeAllSounds {
for (int i = 0; i & sourceTotal_; i++) {
[self resumeSound:_sources[i].sourceId];
alGetError();//Clear error in case we stopped any sounds that couldn't be resumed
* Set a source group as non interruptible.
Default is that source groups are interruptible.
* Non interruptible means that if a request to play a sound is made for a source group and there are
* no free sources available then the play request will be ignored and CD_NO_SOURCE will be returned.
- (void) setSourceGroupNonInterruptible:(int) sourceGroupId isNonInterruptible:(BOOL) isNonInterruptible {
//Ensure source group id is valid to prevent memory corruption
if (sourceGroupId & 0 || sourceGroupId &= _sourceGroupTotal) {
CDLOG(@&Denshion::CDSoundEngine setSourceGroupNonInterruptible invalid source group id %i&,sourceGroupId);
if (isNonInterruptible) {
_sourceGroups[sourceGroupId].nonInterruptible =
_sourceGroups[sourceGroupId].nonInterruptible =
* Set the mute property for a source group. If mute is turned on any sounds in that source group
* will be stopped and further sounds in that source group will play. However, turning mute off
* will not restart any sounds that were playing when mute was turned on. Also the mute setting
* for the sound engine must be taken into account. If the sound engine is mute no sounds will play
* no matter what the source group mute setting is.
- (void) setSourceGroupEnabled:(int) sourceGroupId enabled:(BOOL) enabled {
//Ensure source group id is valid to prevent memory corruption
if (sourceGroupId & 0 || sourceGroupId &= _sourceGroupTotal) {
CDLOG(@&Denshion::CDSoundEngine setSourceGroupEnabled invalid source group id %i&,sourceGroupId);
if (enabled) {
_sourceGroups[sourceGroupId].enabled =
[self stopSourceGroup:sourceGroupId];
_sourceGroups[sourceGroupId].enabled =
* Return the mute property for the source group identified by sourceGroupId
- (BOOL) sourceGroupEnabled:(int) sourceGroupId {
return _sourceGroups[sourceGroupId].
-(ALCcontext *) openALContext {
- (void) _dumpSourceGroupsInfo {
#ifdef CD_DEBUG
CDLOGINFO(@&-------------- source Group Info --------------&);
for (int i=0; i & _sourceGroupT i++) {
CDLOGINFO(@&Group: %i start:%i total:%i&,i,_sourceGroups[i].startIndex, _sourceGroups[i].totalSources);
CDLOGINFO(@&----- mute:%i nonInterruptible:%i&,_sourceGroups[i].enabled, _sourceGroups[i].nonInterruptible);
CDLOGINFO(@&----- Source statuses ----&);
for (int j=0; j & _sourceGroups[i].totalS j++) {
CDLOGINFO(@&Source status:%i index=%i locked=%i&,j,_sourceGroups[i].sourceStatuses[j] && 1, _sourceGroups[i].sourceStatuses[j] & 1);
///////////////////////////////////////////////////////////////////////////////////////
@implementation CDSoundSource
@synthesize lastE
//Macro for handling the al error code
#define CDSOUNDSOURCE_UPDATE_LAST_ERROR (lastError = alGetError())
#define CDSOUNDSOURCE_ERROR_HANDLER ( CDSOUNDSOURCE_UPDATE_LAST_ERROR == AL_NO_ERROR)
-(id)init:(ALuint) theSourceId sourceIndex:(int) index soundEngine:(CDSoundEngine*) engine {
if ((self = [super init])) {
_sourceId = theSourceId;
_sourceIndex =
enabled_ = YES;
mute_ = NO;
_preMuteGain = self.
-(void) dealloc
CDLOGINFO(@&Denshion::CDSoundSource deallocated %i&,self-&_sourceIndex);
//Notify sound engine we are about to release
[_engine _soundSourcePreRelease:self];
[super dealloc];
- (void) setPitch:(float) newPitchValue {
alSourcef(_sourceId, AL_PITCH, newPitchValue);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
- (void) setGain:(float) newGainValue {
if (!mute_) {
alSourcef(_sourceId, AL_GAIN, newGainValue);
_preMuteGain = newGainV
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
- (void) setPan:(float) newPanValue {
float sourcePosAL[] = {newPanValue, 0.0f, 0.0f};//Set position - just using left and right panning
alSourcefv(_sourceId, AL_POSITION, sourcePosAL);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
- (void) setLooping:(BOOL) newLoopingValue {
alSourcei(_sourceId, AL_LOOPING, newLoopingValue);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
- (BOOL) isPlaying {
alGetSourcei(_sourceId, AL_SOURCE_STATE, &state);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
return (state == AL_PLAYING);
- (float) pitch {
ALfloat pitchV
alGetSourcef(_sourceId, AL_PITCH, &pitchVal);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
return pitchV
- (float) pan {
ALfloat sourcePosAL[] = {0.0f,0.0f,0.0f};
alGetSourcefv(_sourceId, AL_POSITION, sourcePosAL);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
return sourcePosAL[0];
- (float) gain {
if (!mute_) {
alGetSourcef(_sourceId, AL_GAIN, &val);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
return _preMuteG
- (BOOL) looping {
alGetSourcef(_sourceId, AL_LOOPING, &val);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
-(BOOL) stop {
alSourceStop(_sourceId);
return CDSOUNDSOURCE_ERROR_HANDLER;
-(BOOL) play {
if (enabled_) {
alSourcePlay(_sourceId);
CDSOUNDSOURCE_UPDATE_LAST_ERROR;
if (lastError != AL_NO_ERROR) {
if (alcGetCurrentContext() == NULL) {
CDLOGINFO(@&Denshion::CDSoundSource - posting bad OpenAL context message&);
[[NSNotificationCenter defaultCenter] postNotificationName:kCDN_BadAlContext object:nil];
return NO;
return YES;
return NO;
-(BOOL) pause {
alSourcePause(_sourceId);
return CDSOUNDSOURCE_ERROR_HANDLER;
-(BOOL) rewind {
alSourceRewind(_sourceId);
return CDSOUNDSOURCE_ERROR_HANDLER;
-(void) setSoundId:(int) soundId {
[_engine _soundSourceAttachToBuffer:self soundId:soundId];
-(int) soundId {
return _soundId;
-(float) durationInSeconds {
return [_engine bufferDurationInSeconds:_soundId];
#pragma mark CDSoundSource AudioInterrupt protocol
- (BOOL) mute {
return mute_;
* Setting mute silences all sounds but playing sounds continue to advance playback
- (void) setMute:(BOOL) newMuteValue {
if (newMuteValue == mute_) {
if (newMuteValue) {
//Remember what the gain was
_preMuteGain = self.
self.gain = 0.0f;
mute_ = newMuteV//Make sure this is done after setting the gain property as the setter behaves differently depending on mute value
//Restore gain to what it was before being muted
mute_ = newMuteV
self.gain = _preMuteG
- (BOOL) enabled {
return enabled_;
- (void) setEnabled:(BOOL)enabledValue
if (enabled_ == enabledValue) {
enabled_ = enabledV
if (enabled_ == NO) {
[self stop];
////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDAudioInterruptTargetGroup
@implementation CDAudioInterruptTargetGroup
-(id) init {
if ((self = [super init])) {
children_ = [[NSMutableArray alloc] initWithCapacity:32];
enabled_ = YES;
mute_ = NO;
-(void) addAudioInterruptTarget:(NSObject&CDAudioInterruptProtocol&*) interruptibleTarget {
//Synchronize child
[interruptibleTarget setMute:mute_];
[interruptibleTarget setEnabled:enabled_];
[children_ addObject:interruptibleTarget];
-(void) removeAudioInterruptTarget:(NSObject&CDAudioInterruptProtocol&*) interruptibleTarget {
[children_ removeObjectIdenticalTo:interruptibleTarget];
- (BOOL) mute {
return mute_;
* Setting mute silences all sounds but playing sounds continue to advance playback
- (void) setMute:(BOOL) newMuteValue {
if (newMuteValue == mute_) {
for (NSObject&CDAudioInterruptProtocol&* target in children_) {
[target setMute:newMuteValue];
- (BOOL) enabled {
return enabled_;
- (void) setEnabled:(BOOL)enabledValue
if (enabledValue == enabled_) {
for (NSObject&CDAudioInterruptProtocol&* target in children_) {
[target setEnabled:enabledValue];
////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDAsynchBufferLoader
@implementation CDAsynchBufferLoader
-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine {
if ((self = [super init])) {
_loadRequests = loadR
[_loadRequests retain];
_soundEngine = theSoundE
[_soundEngine retain];
-(void) main {
CDLOGINFO(@&Denshion::CDAsynchBufferLoader - loading buffers&);
[super main];
_soundEngine.asynchLoadProgress = 0.0f;
if ([_loadRequests count] & 0) {
float increment = 1.0f / [_loadRequests count];
//Iterate over load request and load
for (CDBufferLoadRequest *loadRequest in _loadRequests) {
[_soundEngine loadBuffer:loadRequest.soundId filePath:loadRequest.filePath];
_soundEngine.asynchLoadProgress +=
//Completed
_soundEngine.asynchLoadProgress = 1.0f;
[[NSNotificationCenter defaultCenter] postNotificationName:kCDN_AsynchLoadComplete object:nil];
-(void) dealloc {
[_loadRequests release];
[_soundEngine release];
[super dealloc];
///////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDBufferLoadRequest
@implementation CDBufferLoadRequest
@synthesize filePath, soundId;
-(id) init:(int) theSoundId filePath:(const NSString *) theFilePath {
if ((self = [super init])) {
soundId = theSoundId;
filePath = [theFilePath copy];
-(void) dealloc {
[filePath release];
[super dealloc];
///////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDFloatInterpolator
@implementation CDFloatInterpolator
@synthesize start,end,interpolationT
-(float) interpolate:(float) t {
if (t & 1.0f) {
switch (interpolationType) {
case kIT_Linear:
//Linear interpolation
return ((end - start) * t) +
case kIT_SCurve:
//Cubic s curve t^2 * (3 - 2t)
return ((float)(t * t * (3.0 - (2.0 * t))) * (end - start)) +
case kIT_Exponential:
//Formulas taken from EaseAction
if (end & start) {
float logDelta = (t==0) ? 0 : powf(2, 10 * (t/1 - 1)) - 1 * 0.001f;
return ((end - start) * logDelta) +
//Fade Out
float logDelta = (-powf(2, -10 * t/1) + 1);
return ((end - start) * logDelta) +
return 0.0f;
-(id) init:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal {
if ((self = [super init])) {
start = startV
end = endV
interpolationType =
///////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDPropertyModifier
@implementation CDPropertyModifier
@synthesize stopTargetWhenC
-(id) init:(id) theTarget interpolationType:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal {
if ((self = [super init])) {
if (target) {
//Release the previous target if there is one
[target release];
target = theT
#if CD_DEBUG
//Check target is of the required type
if (![theTarget isMemberOfClass:[self _allowableType]] ) {
CDLOG(@&Denshion::CDPropertyModifier target is not of type %@&,[self _allowableType]);
NSAssert([theTarget isKindOfClass:[CDSoundEngine class]], @&CDPropertyModifier target not of required type&);
[target retain];
startValue = startV
endValue = endV
if (interpolator) {
//Release previous interpolator if there is one
[interpolator release];
interpolator = [[CDFloatInterpolator alloc] init:type startVal:startVal endVal:endVal];
stopTargetWhenComplete = NO;
-(void) dealloc {
CDLOGINFO(@&Denshion::CDPropertyModifier deallocated %@&,self);
[target release];
[interpolator release];
[super dealloc];
-(void) modify:(float) t {
if (t & 1.0) {
[self _setTargetProperty:[interpolator interpolate:t]];
//At the end
[self _setTargetProperty:endValue];
if (stopTargetWhenComplete) {
[self _stopTarget];
-(float) startValue {
return startV
-(void) setStartValue:(float) startVal
startValue = startV
interpolator.start = startV
-(float) endValue {
return startV
-(void) setEndValue:(float) endVal
endValue = endV
interpolator.end = endV
-(tCDInterpolationType) interpolationType {
return interpolator.interpolationT
-(void) setInterpolationType:(tCDInterpolationType) interpolationType {
interpolator.interpolationType = interpolationT
-(void) _setTargetProperty:(float) newVal {
-(float) _getTargetProperty {
return 0.0f;
-(void) _stopTarget {
-(Class) _allowableType {
return [NSObject class];
///////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDSoundSourceFader
@implementation CDSoundSourceFader
-(void) _setTargetProperty:(float) newVal {
((CDSoundSource*)target).gain = newV
-(float) _getTargetProperty {
return ((CDSoundSource*)target).
-(void) _stopTarget {
[((CDSoundSource*)target) stop];
-(Class) _allowableType {
return [CDSoundSource class];
///////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDSoundSourcePanner
@implementation CDSoundSourcePanner
-(void) _setTargetProperty:(float) newVal {
((CDSoundSource*)target).pan = newV
-(float) _getTargetProperty {
return ((CDSoundSource*)target).
-(void) _stopTarget {
[((CDSoundSource*)target) stop];
-(Class) _allowableType {
return [CDSoundSource class];
///////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDSoundSourcePitchBender
@implementation CDSoundSourcePitchBender
-(void) _setTargetProperty:(float) newVal {
((CDSoundSource*)target).pitch = newV
-(float) _getTargetProperty {
return ((CDSoundSource*)target).
-(void) _stopTarget {
[((CDSoundSource*)target) stop];
-(Class) _allowableType {
return [CDSoundSource class];
///////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CDSoundEngineFader
@implementation CDSoundEngineFader
-(void) _setTargetProperty:(float) newVal {
((CDSoundEngine*)target).masterGain = newV
-(float) _getTargetProperty {
return ((CDSoundEngine*)target).masterG
-(void) _stopTarget {
[((CDSoundEngine*)target) stopAllSounds];
-(Class) _allowableType {
return [CDSoundEngine class];
Copyright(C)
OKBASE.NET All Rights Reserved 好库网 版权所有

我要回帖

更多关于 slg游戏制作软件 的文章

 

随机推荐