first run through for sound on iOS platforms. Currently works only for simulator unless the music files are extracted from the zip file onto the filesystem.

This commit is contained in:
techdragon.nguyen@gmail.com
2012-02-03 11:23:27 +00:00
parent 52f2435c48
commit 78c08af9f5
8 changed files with 1546 additions and 14 deletions

View File

@@ -21,6 +21,7 @@
#ifdef ANDROID
#include <SLES/OpenSLES.h>
#include "SLES/OpenSLES_Android.h"
#elif defined USE_PHONON
#include <phonon/AudioOutput>
#include <phonon/MediaObject>
@@ -48,6 +49,8 @@
#endif
//------------------------------------------------------------------------------------------------
using namespace std;
#ifdef USE_PHONON
class JMusic : public QObject
{
@@ -69,6 +72,10 @@ public:
void seekAtTheBegining();
#elif defined (PSP)
JMP3* mTrack;
#elif defined (IOS)
std::string filename;
std::string key;
std::string ext;
#elif defined WITH_FMOD
FSOUND_SAMPLE* mTrack; // MP3 needed to be of "sample" type for FMOD, FMUSIC_MODULE is for MODs
#elif defined ANDROID
@@ -82,7 +89,7 @@ public:
};
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
class JSample
{
public:
@@ -93,6 +100,12 @@ class JSample
#if defined (PSP)
WAVDATA *mSample;
#elif defined (IOS)
std::string filename;
std::string key;
std::string ext;
void* mSample;
#elif defined (WITH_FMOD)
FSOUND_SAMPLE *mSample;
#elif defined (USE_PHONON)

175
JGE/src/iOS/JSfx.cpp Normal file
View File

@@ -0,0 +1,175 @@
//
// JSfx.cpp
// wagic
//
// Created by Michael Nguyen on 2/3/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#include <iostream>
#include "JFileSystem.h"
#include "JSoundSystem.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#import "SoundManager.h"
//////////////////////////////////////////////////////////////////////////
JMusic::JMusic()
{
}
void JMusic::Update(){
}
int JMusic::getPlayTime(){
return 0;
}
JMusic::~JMusic()
{
}
//////////////////////////////////////////////////////////////////////////
JSample::JSample()
{
}
JSample::~JSample()
{
}
unsigned long JSample::fileSize()
{
return 0;
}
//////////////////////////////////////////////////////////////////////////
JSoundSystem* JSoundSystem::mInstance = NULL;
JSoundSystem* JSoundSystem::GetInstance()
{
if (mInstance == NULL)
{
mInstance = new JSoundSystem();
mInstance->InitSoundSystem();
}
return mInstance;
}
void JSoundSystem::Destroy()
{
if (mInstance)
{
mInstance->DestroySoundSystem();
delete mInstance;
mInstance = NULL;
}
}
JSoundSystem::JSoundSystem()
{
mVolume = 0;
mSampleVolume = 0;
}
JSoundSystem::~JSoundSystem()
{
}
void JSoundSystem::InitSoundSystem()
{
NSLog(@"InitSoundSystem enter");
[SoundManager sharedSoundManager];
NSLog(@"InitSoundSystem leave");
}
void JSoundSystem::DestroySoundSystem()
{
}
JMusic *JSoundSystem::LoadMusic(const char *fileName)
{
JMusic* music = new JMusic();
string fullpath = JFileSystem::GetInstance()->GetResourceFile(fileName);
music->filename = fullpath;
NSString *filename = [NSString stringWithCString: fileName encoding:NSUTF8StringEncoding];
NSString *key = [[filename componentsSeparatedByString: @"."] objectAtIndex: 0];
NSString *fileType = [[key componentsSeparatedByString: @"."] lastObject];
NSString *path = [NSString stringWithCString: fullpath.c_str() encoding:NSUTF8StringEncoding];
music->key = [key cStringUsingEncoding:NSUTF8StringEncoding];
music->ext = [fileType cStringUsingEncoding: NSUTF8StringEncoding];
[[SoundManager sharedSoundManager] loadBackgroundMusicWithKey:key musicFile:path];
return music;
}
void JSoundSystem::PlayMusic(JMusic *music, bool looping)
{
NSString *key = [NSString stringWithCString: music->key.c_str() encoding: NSUTF8StringEncoding];
[[SoundManager sharedSoundManager] playMusicWithKey: key timesToRepeat: looping? -1 : 1];
}
void JSoundSystem::StopMusic(JMusic *music)
{
[[SoundManager sharedSoundManager] stopMusic];
}
void JSoundSystem::SetVolume(int volume)
{
SetMusicVolume(volume);
SetSfxVolume(volume);
}
void JSoundSystem::SetMusicVolume(int volume)
{
mVolume = volume;
[[SoundManager sharedSoundManager] setMusicVolume: (float ) volume];
}
void JSoundSystem::SetSfxVolume(int volume){
mSampleVolume = volume;
[[SoundManager sharedSoundManager] setFxVolume: (float ) volume];
SetMusicVolume(mVolume);
}
JSample *JSoundSystem::LoadSample(const char *fileName)
{
JSample* sample = new JSample();
if (sample)
{
NSArray *components = [[NSString stringWithCString:fileName encoding:NSUTF8StringEncoding] componentsSeparatedByString:@"."];
string fullpath = JFileSystem::GetInstance()->GetResourceFile(fileName);
sample->filename = fullpath;
sample->ext = [[components lastObject] cStringUsingEncoding: NSUTF8StringEncoding];
NSString *key = [components objectAtIndex:0];
NSString *musicFile = [NSString stringWithCString: fullpath.c_str() encoding:NSUTF8StringEncoding];
[[SoundManager sharedSoundManager] loadSoundWithKey: key musicFile: musicFile];
}
return sample;
}
void JSoundSystem::PlaySample(JSample *sample)
{
SoundManager *soundManager = [SoundManager sharedSoundManager];
NSString *key = [NSString stringWithCString: sample->key.c_str() encoding:NSUTF8StringEncoding];
[soundManager playSoundWithKey: key gain: 1.0f pitch: 1.0f location:CGPointZero shouldLoop: NO sourceID: -1];
}

View File

@@ -0,0 +1,140 @@
/*
File: MyOpenALSupport.c
Abstract: OpenAL-related support functions
Version: 1.4
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2009 Apple Inc. All Rights Reserved.
*/
#include "MyOpenALSupport.h"
ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
{
static alBufferDataStaticProcPtr proc = NULL;
if (proc == NULL) {
proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic");
}
if (proc)
proc(bid, format, data, size, freq);
return;
}
void* MyGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate)
{
OSStatus err = noErr;
SInt64 theFileLengthInFrames = 0;
AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);
ExtAudioFileRef extRef = NULL;
void* theData = NULL;
AudioStreamBasicDescription theOutputFormat;
// Open a file with ExtAudioFileOpen()
err = ExtAudioFileOpenURL(inFileURL, &extRef);
if(err) { printf("MyGetOpenALAudioData: ExtAudioFileOpenURL FAILED, Error = %ld\n", err); goto Exit; }
// Get the audio data format
err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat);
if(err) { printf("MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %ld\n", err); goto Exit; }
if (theFileFormat.mChannelsPerFrame > 2) { printf("MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); goto Exit;}
// Set the client format to 16 bit signed integer (native-endian) data
// Maintain the channel count and sample rate of the original source format
theOutputFormat.mSampleRate = theFileFormat.mSampleRate;
theOutputFormat.mChannelsPerFrame = theFileFormat.mChannelsPerFrame;
theOutputFormat.mFormatID = kAudioFormatLinearPCM;
theOutputFormat.mBytesPerPacket = 2 * theOutputFormat.mChannelsPerFrame;
theOutputFormat.mFramesPerPacket = 1;
theOutputFormat.mBytesPerFrame = 2 * theOutputFormat.mChannelsPerFrame;
theOutputFormat.mBitsPerChannel = 16;
theOutputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
// Set the desired client (output) data format
err = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(theOutputFormat), &theOutputFormat);
if(err) { printf("MyGetOpenALAudioData: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) FAILED, Error = %ld\n", err); goto Exit; }
// Get the total frame count
thePropertySize = sizeof(theFileLengthInFrames);
err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames);
if(err) { printf("MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileLengthFrames) FAILED, Error = %ld\n", err); goto Exit; }
// Read all the data into memory
UInt32 dataSize = theFileLengthInFrames * theOutputFormat.mBytesPerFrame;;
theData = malloc(dataSize);
if (theData)
{
AudioBufferList theDataBuffer;
theDataBuffer.mNumberBuffers = 1;
theDataBuffer.mBuffers[0].mDataByteSize = dataSize;
theDataBuffer.mBuffers[0].mNumberChannels = theOutputFormat.mChannelsPerFrame;
theDataBuffer.mBuffers[0].mData = theData;
// Read the data into an AudioBufferList
err = ExtAudioFileRead(extRef, (UInt32*)&theFileLengthInFrames, &theDataBuffer);
if(err == noErr)
{
// success
*outDataSize = (ALsizei)dataSize;
*outDataFormat = (theOutputFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
*outSampleRate = (ALsizei)theOutputFormat.mSampleRate;
}
else
{
// failure
free (theData);
theData = NULL; // make sure to return NULL
printf("MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", err); goto Exit;
}
}
Exit:
// Dispose the ExtAudioFileRef, it is no longer needed
if (extRef) ExtAudioFileDispose(extRef);
return theData;
}

View File

@@ -0,0 +1,60 @@
/*
File: MyOpenALSupport.h
Abstract: OpenAL-related support functions
Version: 1.4
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2009 Apple Inc. All Rights Reserved.
*/
#import <OpenAL/al.h>
#import <OpenAL/alc.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AudioToolbox/ExtendedAudioFile.h>
typedef ALvoid AL_APIENTRY (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid *data, ALsizei size, ALsizei freq);
ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid *data, ALsizei size, ALsizei freq);
void* MyGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei *outSampleRate);

View File

@@ -0,0 +1,167 @@
//
// SoundManager.h
// SLQTSOR
//
// Created by Michael Daley on 22/05/2009.
// Copyright 2009 Michael Daley. All rights reserved.
//
#import <OpenAL/al.h>
#import <OpenAL/alc.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#define kFadeInterval (1.0f/60) // Causes the music volume to be updated 60 times per second when music is fading
// SoundManager provides a basic wrapper for OpenAL and AVAudioPlayer. It is a singleton
// class that allows sound clips to be loaded and cached with a key and then played back
// using that key. It also allows for music tracks to be played, stopped and paused
//
@interface SoundManager : NSObject <AVAudioPlayerDelegate, AVAudioSessionDelegate> {
//////////////////// Sound Setup
ALCcontext *context; // Context in which all sounds will be played
ALCdevice *device; // Reference to the device to use when playing sounds
ALenum alError; // Any OpenAL errors that are rasied
CGPoint listenerPosition; // Location of the OpenAL Listener
NSString *soundCategory; // The sound category to be used by the sound manager
NSMutableArray *soundSources; // Mutable array of all sound sources
AVAudioSession *audioSession; // Reference to an audio session
NSError *audioSessionError; // Audiosession errors are placed in this ivar
//////////////////// Sound dicionaries
NSMutableDictionary *soundLibrary; // Dictionary of all sounds loaded and their keys
NSMutableDictionary *musicLibrary; // Dictionary of all music/ambient sounds loaded and their keys
NSMutableDictionary *musicPlaylists; // Dictionary of playlists
NSMutableArray *currentPlaylistTracks; // Array of tracks for the current play list
//////////////////// Music
AVAudioPlayer *musicPlayer; // AVAudioPlayer instance for the music
//////////////////// Volume
float currentMusicVolume; // Volume of music/ambient sounds played through AVAudioPlayer
float fxVolume; // Volume of OpenAL sound effects
float musicVolume; // The master music volume. This value is not affected by fading music
//////////////////// Fading sound
NSTimer *timer; // Timer used fade the music volume up or down
float fadeAmount; // Amount the volume should be faded each timer call
float fadeDuration; // The amount of time the fade has been running
float targetFadeDuration; // The duration the current fade should run for
//////////////////// Flags
BOOL isExternalAudioPlaying; // YES if music was playing before the sound engine was initialized i.e.
BOOL isFading; // YES if the sound manager is currently fading music
BOOL isMusicPlaying; // YES if music is currently playing
BOOL stopMusicAfterFade; // YES if music is to be stopped once fading has finished
BOOL usePlaylist; // YES if tracks in the playlist should be played one after the other
BOOL loopPlaylist; // YES if the playlist should loop when it reaches the end
BOOL loopLastPlaylistTrack; // YES if you want the last track of the playlist to be looped forever
//////////////////// Playlist tracking
int playlistIndex; // Current index being played in the playlist
NSString *currentPlaylistName; // Holds the name of the currently playing play list
}
@property (nonatomic, assign) float currentMusicVolume;
@property (nonatomic, assign) float fxVolume;
@property (nonatomic, assign) BOOL isExternalAudioPlaying;
@property (nonatomic, assign) BOOL isMusicPlaying;
@property (nonatomic, assign) BOOL usePlaylist;
@property (nonatomic, assign) BOOL loopLastPlaylistTrack;
@property (nonatomic, assign) float musicVolume;
// Returns as instance of the SoundManager class. If an instance has already been created
// then this instance is returned, otherwise a new instance is created and returned.
+ (SoundManager *)sharedSoundManager;
// Designated initializer.
- (id)init;
// Plays the sound which is found with |aSoundKey| using the provided |aGain| and |aPitch|.
// |aLocation| is used to set the location of the sound source in relation to the listener
// and |aLoop| specifies if the sound should be continuously looped or not.
- (NSUInteger)playSoundWithKey:(NSString*)aSoundKey gain:(float)aGain pitch:(float)aPitch
location:(CGPoint)aLocation shouldLoop:(BOOL)aLoop sourceID:(NSUInteger)aSourceID;
// Stops all sounds playing with the supplied sound key
- (void)stopSoundWithKey:(NSString*)aSoundKey;
// Loads a sound with the supplied key, filename, file extension and frequency. Frequency
// could be worked out from the file but this implementation takes it as an argument. If no
// sound is found with a matching key then nothing happens.
- (void)loadSoundWithKey:(NSString*)aSoundKey musicFile:(NSString*)aMusicFile;
// Removes the sound with the supplied key from the sound library. This deletes the buffer that was created
// to hold the sound
- (void)removeSoundWithKey:(NSString*)aSoundKey;
// Plays the music with the supplied key. If no music is found then nothing happens.
// |aRepeatCount| specifies the number of times the music should loop.
- (void)playMusicWithKey:(NSString*)aMusicKey timesToRepeat:(NSUInteger)aRepeatCount;
// Plays the next track in the play list if there is one
- (void)playNextTrack;
// Loads the path of a music files into a dictionary with the a key of |aMusicKey|
- (void)loadBackgroundMusicWithKey:(NSString*)aMusicKey musicFile:(NSString*)aMusicFile;
// Removes the path to the music file with the matching key from the music library array.
- (void)removeBackgroundMusicWithKey:(NSString*)aMusicKey;
// Adds a track to the named play list
- (void)addToPlaylistNamed:(NSString*)aPlaylistName track:(NSString*)aTrackName;
// Plays the contents of the named play list
- (void)startPlaylistNamed:(NSString*)aPlaylistName;
// Removes the named track from the named playlist
- (void)removeFromPlaylistNamed:(NSString*)aPlaylistName track:(NSString*)aTrackName;
// Removes the named playlist
- (void)removePlaylistNamed:(NSString*)aPlaylistName;
// Clears the named playlist
- (void)clearPlaylistNamed:(NSString*)aPlaylistName;
// Stops any currently playing music.
- (void)stopMusic;
// Pauses any currently playing music.
- (void)pauseMusic;
// Resumes music that has been paused
- (void)resumeMusic;
// Fades the music volume down to the specified value over the specified period of time. This method
// does not change the musicVolume for the sound manager which allows you to always get the music volume
// allowing you to fade music down and then back up to the defined value set inside the settings screen for
// example
- (void)fadeMusicVolumeFrom:(float)aFromVolume toVolume:(float)aToVolume duration:(float)aSeconds stop:(BOOL)aStop;
// Cross fades between the currently playing track and the track specified over the duration
// specified
- (void)crossFadeTo:(NSString*)aTrack duration:(float)aDuration;
// Shutsdown the SoundManager class and deallocates resources which have been assigned.
- (void)shutdownSoundManager;
#pragma mark -
#pragma mark SoundManager settings
// Set the volume for music which is played.
- (void)setMusicVolume:(float)aVolume;
// Sets the location of the OpenAL listener.
- (void)setListenerPosition:(CGPoint)aPosition;
// Sets the orientation of the listener. This is used to make sure that sound
// is played correctly based on the direction the player is moving in
- (void)setOrientation:(CGPoint)aPosition;
// Sets the volume for all sounds which are played. This acts as a global FX volume for
// all sounds.
- (void)setFxVolume:(float)aVolume;
@end

View File

@@ -0,0 +1,861 @@
//
// SoundManager.m
// SLQTSOR
//
// Created by Michael Daley on 22/05/2009.
// Copyright 2009 Michael Daley. All rights reserved.
//
#import "SoundManager.h"
#import "SynthesizeSingleton.h"
#import "MyOpenALSupport.h"
#pragma mark -
#pragma mark Private interface
@interface SoundManager (Private)
// This method is used to initialize OpenAL. It gets the default device, creates a new context
// to be used and then preloads the define # sources. This preloading means we wil be able to play up to
// (max 32) different sounds at the same time
- (BOOL)initOpenAL;
// Used to get the next available OpenAL source. The returned source is then bound to a sound
// buffer so that the sound can be played. This method checks each of the available OpenAL
// soucres which have been generated and returns the first source which is not currently being
// used. If no sources are found to be free then the first looping source is returned. If there
// are no looping sources then the first source created is returned
- (NSUInteger)nextAvailableSource;
// Used to set the current state of OpenAL. When the game is interrupted the OpenAL state is
// stopped and then restarted when the game becomes active again.
- (void)setActivated:(BOOL)aState;
// If audio is currently playing this method returns YES
- (BOOL)isAudioPlaying;
// Checks to see if an OpenAL error has been logged. If so it renders the error to the screen
- (void)checkForErrors;
@end
#pragma mark -
#pragma mark Public implementation
@implementation SoundManager
// Make this class a singleton class
SYNTHESIZE_SINGLETON_FOR_CLASS(SoundManager);
@synthesize currentMusicVolume;
@synthesize fxVolume;
@synthesize isExternalAudioPlaying;
@synthesize isMusicPlaying;
@synthesize usePlaylist;
@synthesize loopLastPlaylistTrack;
@synthesize musicVolume;
#pragma mark -
#pragma mark Dealloc and Init and Shutdown
- (void)dealloc {
// Loop through the OpenAL sources and delete them
for(NSNumber *sourceIDVal in soundSources) {
NSUInteger sourceID = [sourceIDVal unsignedIntValue];
alDeleteSources(1, &sourceID);
[self checkForErrors];
}
// Loop through the OpenAL buffers and delete
NSEnumerator *enumerator = [soundLibrary keyEnumerator];
id key;
while ((key = [enumerator nextObject])) {
NSNumber *bufferIDVal = [soundLibrary objectForKey:key];
NSUInteger bufferID = [bufferIDVal unsignedIntValue];
alDeleteBuffers(1, &bufferID);
[self checkForErrors];
}
// Release the arrays and dictionaries we have been using
[soundLibrary release];
[soundSources release];
[musicLibrary release];
[musicPlaylists release];
if (currentPlaylistTracks) {
[currentPlaylistTracks release];
}
// If background music has been played then release the AVAudioPlayer
if(musicPlayer)
[musicPlayer release];
// Disable and then destroy the context
alcMakeContextCurrent(NULL);
[self checkForErrors];
alcDestroyContext(context);
[self checkForErrors];
// Close the device
alcCloseDevice(device);
[self checkForErrors];
[super dealloc];
}
- (id)init {
self = [super init];
if(self != nil) {
// Initialize the array and dictionaries we are going to use
soundSources = [[NSMutableArray alloc] init];
soundLibrary = [[NSMutableDictionary alloc] init];
musicLibrary = [[NSMutableDictionary alloc] init];
musicPlaylists = [[NSMutableDictionary alloc] init];
// Grab a reference to the AVAudioSession singleton
audioSession = [AVAudioSession sharedInstance];
// Reset the error ivar
audioSessionError = nil;
// Check to see if music is already playing. If that is the case then you can leave the sound category as AmbientSound.
// If music is not playing we can set the sound category to SoloAmbientSound so that decoding is done using the hardware.
isExternalAudioPlaying = [self isAudioPlaying];
if (!isExternalAudioPlaying) {
NSLog(@"INFO - SoundManager: No external audio playing so using the SoloAmbient audio session category");
soundCategory = AVAudioSessionCategorySoloAmbient;
} else {
NSLog(@"INFO - SoundManager: External sound detected so using the Ambient audio session category");
soundCategory = AVAudioSessionCategoryAmbient;
}
// Having decided on the category we then set it
[audioSession setCategory:soundCategory error:&audioSessionError];
if (audioSessionError) {
NSLog(@"WARNING - SoundManager: Unable to set the sound category to ambient");
}
// Set up the OpenAL. If an error occurs then nil will be returned.
BOOL success = [self initOpenAL];
if(!success) {
NSLog(@"ERROR - SoundManager: Error initializing OpenAL");
return nil;
}
// Set up the listener position
float listener_pos[] = {0, 0, 0};
float listener_ori[] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
float listener_vel[] = {0, 0, 0};
alListenerfv(AL_POSITION, listener_pos);
[self checkForErrors];
alListenerfv(AL_ORIENTATION, listener_ori);
[self checkForErrors];
alListenerfv(AL_VELOCITY, listener_vel);
[self checkForErrors];
// Set the default volume for music and fx along the fading flag
currentMusicVolume = 0.5f;
musicVolume = 0.5f;
fxVolume = 0.5f;
playlistIndex = 0;
// Set up initial flag values
isFading = NO;
isMusicPlaying = NO;
stopMusicAfterFade = YES;
usePlaylist = NO;
loopLastPlaylistTrack = NO;
}
return self;
}
- (void)shutdownSoundManager {
@synchronized(self) {
if(sharedSoundManager != nil) {
[self dealloc];
}
}
}
#pragma mark -
#pragma mark Sound management
- (void)loadSoundWithKey:(NSString*)aSoundKey musicFile:(NSString*)aMusicFile {
// Check to make sure that a sound with the same key does not already exist
NSNumber *numVal = [soundLibrary objectForKey:aSoundKey];
// If the key is not found log it and finish
if(numVal != nil) {
NSLog(@"WARNING - SoundManager: Sound key '%@' already exists.", aSoundKey);
return;
}
NSUInteger bufferID;
// Generate a buffer within OpenAL for this sound
alGenBuffers(1, &bufferID);
[self checkForErrors];
// Set up the variables which are going to be used to hold the format
// size and frequency of the sound file we are loading
ALenum format;
ALsizei size;
ALsizei freq;
ALvoid *data;
alError = AL_NO_ERROR;
NSBundle *bundle = [NSBundle mainBundle];
// Get the audio data from the file which has been passed in
NSString *fileName = [[aMusicFile lastPathComponent] stringByDeletingPathExtension];
NSString *fileType = [aMusicFile pathExtension];
NSString *filePath = [bundle pathForResource:fileName ofType:fileType];
if ( filePath == nil )
filePath = aMusicFile;
CFURLRef fileURL = (CFURLRef)[[NSURL fileURLWithPath: filePath] retain];
if (fileURL)
{
data = MyGetOpenALAudioData(fileURL, &size, &format, &freq);
CFRelease(fileURL);
if((alError = alGetError()) != AL_NO_ERROR) {
NSLog(@"ERROR - SoundManager: Error loading sound: %@ with error %x\n", fileName, alError);
}
// Use the static buffer data API
alBufferData(bufferID, format, data, size, freq);
[self checkForErrors];
if((alError = alGetError()) != AL_NO_ERROR) {
NSLog(@"ERROR - SoundManager: Error attaching audio to buffer: %x\n", alError);
}
// Free the memory we used when getting the audio data
if (data)
free(data);
}
else
{
NSLog(@"ERROR - SoundManager: Could not find file '%@.%@'", fileName, fileType);
if (data)
free(data);
data = NULL;
}
// Place the buffer ID into the sound library against |aSoundKey|
[soundLibrary setObject:[NSNumber numberWithUnsignedInt:bufferID] forKey:aSoundKey];
NSLog(@"INFO - SoundManager: Loaded sound with key '%@' into buffer '%d'", aSoundKey, bufferID);
}
- (void)removeSoundWithKey:(NSString*)aSoundKey {
// Reset errors in OpenAL
alError = alGetError();
alError = AL_NO_ERROR;
// Find the buffer which has been linked to the sound key provided
NSNumber *numVal = [soundLibrary objectForKey:aSoundKey];
// If the key is not found log it and finish
if(numVal == nil) {
NSLog(@"WARNING - SoundManager: No sound with key '%@' was found so cannot be removed", aSoundKey);
return;
}
// Get the buffer number from
NSUInteger bufferID = [numVal unsignedIntValue];
NSInteger bufferForSource;
NSInteger sourceState;
for(NSNumber *sourceID in soundSources) {
NSUInteger currentSourceID = [sourceID unsignedIntValue];
// Grab the current state of the source and also the buffer attached to it
alGetSourcei(currentSourceID, AL_SOURCE_STATE, &sourceState);
[self checkForErrors];
alGetSourcei(currentSourceID, AL_BUFFER, &bufferForSource);
[self checkForErrors];
// If this source is not playing then unbind it. If it is playing and the buffer it
// is playing is the one we are removing, then also unbind that source from this buffer
if(sourceState != AL_PLAYING || (sourceState == AL_PLAYING && bufferForSource == bufferID)) {
alSourceStop(currentSourceID);
[self checkForErrors];
alSourcei(currentSourceID, AL_BUFFER, 0);
[self checkForErrors];
}
}
// Delete the buffer
alDeleteBuffers(1, &bufferID);
// Check for any errors
if((alError = alGetError()) != AL_NO_ERROR) {
NSLog(@"ERROR - SoundManager: Could not delete buffer %d with error %x", bufferID, alError);
exit(1);
}
// Remove the soundkey from the soundLibrary
[soundLibrary removeObjectForKey:aSoundKey];
NSLog(@"INFO - SoundManager: Removed sound with key '%@'", aSoundKey);
}
- (void)loadBackgroundMusicWithKey:(NSString*)aMusicKey musicFile:(NSString*)aMusicFile {
// Get the filename and type from the music file name passed in
NSString *fileName = [[aMusicFile lastPathComponent] stringByDeletingPathExtension];
NSString *fileType = [aMusicFile pathExtension];
// Check to make sure that a sound with the same key does not already exist
NSString *path = [musicLibrary objectForKey:aMusicKey];
// If the key is found log it and finish
if(path != nil) {
NSLog(@"WARNING - SoundManager: Music with the key '%@' already exists.", aMusicKey);
return;
}
path = [[NSBundle mainBundle] pathForResource:fileName ofType:fileType];
if (!path) {
if ( ![[NSFileManager defaultManager] fileExistsAtPath: aMusicFile] )
{
NSLog(@"WARNING - SoundManager: Cannot find file '%@.%@'", fileName, fileType);
return;
}
else
path = aMusicFile;
}
[musicLibrary setObject:path forKey:aMusicKey];
NSLog(@"INFO - SoundManager: Loaded background music with key '%@'", aMusicKey);
}
- (void)removeBackgroundMusicWithKey:(NSString*)aMusicKey {
NSString *path = [musicLibrary objectForKey:aMusicKey];
if(path == NULL) {
NSLog(@"WARNING - SoundManager: No music found with key '%@' was found so cannot be removed", aMusicKey);
return;
}
[musicLibrary removeObjectForKey:aMusicKey];
NSLog(@"INFO - SoundManager: Removed music with key '%@'", aMusicKey);
}
- (void)addToPlaylistNamed:(NSString*)aPlaylistName track:(NSString*)aTrackName {
NSString *path = [musicLibrary objectForKey:aTrackName];
if (!path) {
NSLog(@"WARNING - SoundManager: Track '%@' does not exist in the music library and cannot be added to the play list.");
return;
}
// See if the playlist already exists
NSMutableArray *playlistTracks = [musicPlaylists objectForKey:aPlaylistName];
if (!playlistTracks) {
playlistTracks = [[NSMutableArray alloc] init];
}
[playlistTracks addObject:aTrackName];
// Add the track key to the play list
[musicPlaylists setObject:playlistTracks forKey:aPlaylistName];
}
- (void)startPlaylistNamed:(NSString*)aPlaylistName {
NSMutableArray *playlistTracks = [musicPlaylists objectForKey:aPlaylistName];
if (!playlistTracks) {
NSLog(@"WARNING - SoundManager: No play list exists with the name '%@'", aPlaylistName);
return;
}
currentPlaylistName = aPlaylistName;
currentPlaylistTracks = playlistTracks;
usePlaylist = YES;
[self playMusicWithKey:[playlistTracks objectAtIndex:0] timesToRepeat:0];
}
- (void)removeFromPlaylistNamed:(NSString*)aPlaylistName track:(NSString*)aTrackName {
NSMutableArray *playlistTracks = [musicPlaylists objectForKey:aPlaylistName];
if (playlistTracks) {
int indexToRemove;
for (int index=0; index < [currentPlaylistTracks count]; index++) {
if ([[currentPlaylistTracks objectAtIndex:index] isEqualToString:aTrackName]) {
indexToRemove = index;
break;
}
}
[currentPlaylistTracks removeObjectAtIndex:indexToRemove];
}
}
- (void)removePlaylistNamed:(NSString*)aPlaylistName {
[musicPlaylists removeObjectForKey:aPlaylistName];
}
- (void)clearPlaylistNamed:(NSString*)aPlaylistName {
NSMutableArray *playlistTracks = [musicPlaylists objectForKey:aPlaylistName];
if (playlistTracks) {
[playlistTracks removeAllObjects];
}
}
#pragma mark -
#pragma mark Sound control
- (NSUInteger)playSoundWithKey:(NSString*)aSoundKey gain:(float)aGain pitch:(float)aPitch location:(CGPoint)aLocation shouldLoop:(BOOL)aLoop sourceID:(NSUInteger)aSourceID {
// Find the buffer linked to the key which has been passed in
NSNumber *numVal = [soundLibrary objectForKey:aSoundKey];
if(numVal == nil) return 0;
NSUInteger bufferID = [numVal unsignedIntValue];
// Find an available source if -1 has been passed in as the sourceID. If the sourceID is
// not -1 i.e. a source ID has been passed in then check to make sure that source is not playing
// and if not play the identified buffer ID within the provided source
NSUInteger sourceID;
if(aSourceID == -1) {
sourceID = [self nextAvailableSource];
} else {
NSInteger sourceState;
alGetSourcei(aSourceID, AL_SOURCE_STATE, &sourceState);
if(sourceState == AL_PLAYING)
return 0;
sourceID = aSourceID;
}
// Make sure that the source is clean by resetting the buffer assigned to the source
// to 0
alSourcei(sourceID, AL_BUFFER, 0);
// Attach the buffer we have looked up to the source we have just found
alSourcei(sourceID, AL_BUFFER, bufferID);
// Set the pitch and gain of the source
alSourcef(sourceID, AL_PITCH, aPitch);
alSourcef(sourceID, AL_GAIN, aGain * fxVolume);
// Set the looping value
if(aLoop) {
alSourcei(sourceID, AL_LOOPING, AL_TRUE);
} else {
alSourcei(sourceID, AL_LOOPING, AL_FALSE);
}
// Set the source location
alSource3f(sourceID, AL_POSITION, aLocation.x, aLocation.y, 0.0f);
// Now play the sound
alSourcePlay(sourceID);
alError = alGetError();
// Check to see if there were any errors
[self checkForErrors];
// Return the source ID so that loops can be stopped etc
return sourceID;
}
- (void)stopSoundWithKey:(NSString*)aSoundKey {
// Reset errors in OpenAL
alError = alGetError();
alError = AL_NO_ERROR;
// Find the buffer which has been linked to the sound key provided
NSNumber *numVal = [soundLibrary objectForKey:aSoundKey];
// If the key is not found log it and finish
if(numVal == nil) {
NSLog(@"WARNING - SoundManager: No sound with key '%@' was found so cannot be stopped", aSoundKey);
return;
}
// Get the buffer number from
NSUInteger bufferID = [numVal unsignedIntValue];
NSInteger bufferForSource;
NSInteger sourceState;
for(NSNumber *sourceID in soundSources) {
NSUInteger currentSourceID = [sourceID unsignedIntValue];
// Grab the current state of the source and also the buffer attached to it
alGetSourcei(currentSourceID, AL_SOURCE_STATE, &sourceState);
alGetSourcei(currentSourceID, AL_BUFFER, &bufferForSource);
// If this source is not playing then unbind it. If it is playing and the buffer it
// is playing is the one we are removing, then also unbind that source from this buffer
if(bufferForSource == bufferID) {
alSourceStop(currentSourceID);
alSourcei(currentSourceID, AL_BUFFER, 0);
}
}
// Check for any errors
[self checkForErrors];
// Remove the soundkey from the soundLibrary
[soundLibrary removeObjectForKey:aSoundKey];
NSLog(@"INFO - SoundManager: Removed sound with key '%@'", aSoundKey);
}
- (void)playMusicWithKey:(NSString*)aMusicKey timesToRepeat:(NSUInteger)aRepeatCount {
NSError *error;
NSString *path = [musicLibrary objectForKey:aMusicKey];
if(!path) {
NSLog(@"ERROR - SoundManager: The music key '%@' could not be found", aMusicKey);
return;
}
if(musicPlayer)
[musicPlayer release];
// Initialize the AVAudioPlayer using the path that we have retrieved from the music library dictionary
musicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
// If the backgroundMusicPlayer object is nil then there was an error
if(!musicPlayer) {
NSLog(@"ERROR - SoundManager: Could not play music for key '%d'", error);
return;
}
// Set the delegate for this music player to be the sound manager
musicPlayer.delegate = self;
// Set the number of times this music should repeat. -1 means never stop until its asked to stop
[musicPlayer setNumberOfLoops:aRepeatCount];
// Set the volume of the music
[musicPlayer setVolume:currentMusicVolume];
// Play the music
[musicPlayer play];
// Set the isMusicPlaying flag
isMusicPlaying = YES;
}
- (void)playNextTrack {
if (playlistIndex + 1 == [currentPlaylistTracks count]-1 && loopLastPlaylistTrack) {
playlistIndex += 1;
[self playMusicWithKey:[currentPlaylistTracks objectAtIndex:playlistIndex] timesToRepeat:-1];
} else if (playlistIndex + 1 < [currentPlaylistTracks count]) {
playlistIndex += 1;
[self playMusicWithKey:[currentPlaylistTracks objectAtIndex:playlistIndex] timesToRepeat:0];
} else if (loopPlaylist) {
playlistIndex = 0;
[self playMusicWithKey:[currentPlaylistTracks objectAtIndex:playlistIndex] timesToRepeat:0];
}
}
- (void)stopMusic {
[musicPlayer stop];
isMusicPlaying = NO;
usePlaylist = NO;
}
- (void)pauseMusic {
if(musicPlayer)
[musicPlayer pause];
isMusicPlaying = NO;
}
- (void)resumeMusic {
if (musicPlayer) {
[musicPlayer play];
isMusicPlaying = YES;
}
}
#pragma mark -
#pragma mark SoundManager settings
- (void)setMusicVolume:(float)aVolume {
// Set the volume iVar
if (aVolume > 1)
aVolume = 1.0f;
currentMusicVolume = aVolume;
musicVolume = aVolume;
// Check to make sure that the audio player exists and if so set its volume
if(musicPlayer) {
[musicPlayer setVolume:currentMusicVolume];
}
}
- (void)setFxVolume:(float)aVolume {
fxVolume = aVolume;
}
- (void)setListenerPosition:(CGPoint)aPosition {
listenerPosition = aPosition;
alListener3f(AL_POSITION, aPosition.x, aPosition.y, 0.0f);
}
- (void)setOrientation:(CGPoint)aPosition {
float orientation[] = {aPosition.x, aPosition.y, 0.0f, 0.0f, 0.0f, 1.0f};
alListenerfv(AL_ORIENTATION, orientation);
}
- (void)fadeMusicVolumeFrom:(float)aFromVolume toVolume:(float)aToVolume duration:(float)aSeconds stop:(BOOL)aStop {
// If there is already a fade timer active, invalidate it so we can start another one
if (timer) {
[timer invalidate];
timer = NULL;
}
// Work out how much to fade the music by based on the current volume, the requested volume
// and the duration
fadeAmount = (aToVolume - aFromVolume) / (aSeconds / kFadeInterval);
currentMusicVolume = aFromVolume;
// Reset the fades duration
fadeDuration = 0;
targetFadeDuration = aSeconds;
isFading = YES;
stopMusicAfterFade = aStop;
// Set up a timer that fires kFadeInterval times per second calling the fadeVolume method
timer = [NSTimer scheduledTimerWithTimeInterval:kFadeInterval target:self selector:@selector(fadeVolume:) userInfo:nil repeats:TRUE];
}
- (void)crossFadeTo:(NSString*)aTrack duration:(float)aDuration {
// TODO: Finish this method
}
#pragma mark -
#pragma mark AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
isMusicPlaying = NO;
// If we are using a play list then handle the next track to be played
if (usePlaylist) {
[self playNextTrack];
}
}
#pragma mark -
#pragma mark AVAudioSessionDelegate
- (void)beginInterruption {
NSLog(@"BEGIN");
[self setActivated:NO];
}
- (void)endInterruption {
[self setActivated:YES];
}
@end
#pragma mark -
#pragma mark Private implementation
@implementation SoundManager (Private)
// Define the number of sources which will be created. iPhone can have a max of 32
#define MAX_OPENAL_SOURCES 16
- (BOOL)initOpenAL {
NSLog(@"INFO - Sound Manager: Initializing sound manager");
// Get the device we are going to use for sound. Using NULL gets the default device
device = alcOpenDevice(NULL);
// If a device has been found we then need to create a context, make it current and then
// preload the OpenAL Sources
if(device) {
// Use the device we have now got to create a context in which to play our sounds
context = alcCreateContext(device, NULL);
[self checkForErrors];
// Make the context we have just created into the active context
alcMakeContextCurrent(context);
[self checkForErrors];
// Set the distance model to be used
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
[self checkForErrors];
// Pre-create sound sources which can be dynamically allocated to buffers (sounds)
NSUInteger sourceID;
for(int index = 0; index < MAX_OPENAL_SOURCES; index++) {
// Generate an OpenAL source
alGenSources(1, &sourceID);
[self checkForErrors];
// Configure the generated source so that sounds fade as the player moves
// away from them
alSourcef(sourceID, AL_REFERENCE_DISTANCE, 25.0f);
alSourcef(sourceID, AL_MAX_DISTANCE, 150.0f);
alSourcef(sourceID, AL_ROLLOFF_FACTOR, 6.0f);
[self checkForErrors];
// Add the generated sourceID to our array of sound sources
[soundSources addObject:[NSNumber numberWithUnsignedInt:sourceID]];
}
NSLog(@"INFO - Sound Manager: Finished initializing the sound manager");
// Return YES as we have successfully initialized OpenAL
return YES;
}
// We were unable to obtain a device for playing sound so tell the user and return NO.
NSLog(@"ERROR - SoundManager: Unable to allocate a device for sound.");
return NO;
}
- (NSUInteger)nextAvailableSource {
// Holder for the current state of the current source
NSInteger sourceState;
// Find a source which is not being used at the moment
for(NSNumber *sourceNumber in soundSources) {
alGetSourcei([sourceNumber unsignedIntValue], AL_SOURCE_STATE, &sourceState);
// If this source is not playing then return it
if(sourceState != AL_PLAYING) return [sourceNumber unsignedIntValue];
}
// If all the sources are being used we look for the first non looping source
// and use the source associated with that
NSInteger looping;
for(NSNumber *sourceNumber in soundSources) {
alGetSourcei([sourceNumber unsignedIntValue], AL_LOOPING, &looping);
if(!looping) {
// We have found a none looping source so return this source and stop checking
NSUInteger sourceID = [sourceNumber unsignedIntValue];
alSourceStop(sourceID);
return sourceID;
}
}
// If there are no looping sources to be found then just use the first source and use that
NSUInteger sourceID = [[soundSources objectAtIndex:0] unsignedIntegerValue];
alSourceStop(sourceID);
// Check for any errors that may have been raised
[self checkForErrors];
// Return the sourceID found
return sourceID;
}
#pragma mark -
#pragma mark Interruption handling
- (void)setActivated:(BOOL)aState {
OSStatus result;
if(aState) {
NSLog(@"INFO - SoundManager: OpenAL Active");
// Set the AudioSession AudioCategory to what has been defined in soundCategory
[audioSession setCategory:soundCategory error:&audioSessionError];
if(audioSessionError) {
NSLog(@"ERROR - SoundManager: Unable to set the audio session category");
return;
}
// Set the audio session state to true and report any errors
[audioSession setActive:YES error:&audioSessionError];
if (audioSessionError) {
NSLog(@"ERROR - SoundManager: Unable to set the audio session state to YES with error %d.", result);
return;
}
if (musicPlayer) {
[musicPlayer play];
}
// As we are finishing the interruption we need to bind back to our context.
alcMakeContextCurrent(context);
[self checkForErrors];
} else {
NSLog(@"INFO - SoundManager: OpenAL Inactive");
// As we are being interrupted we set the current context to NULL. If this sound manager is to be
// compaitble with firmware prior to 3.0 then the context would need to also be destroyed and
// then re-created when the interruption ended.
alcMakeContextCurrent(NULL);
[self checkForErrors];
}
}
- (BOOL)isAudioPlaying {
UInt32 audioPlaying = 0;
UInt32 audioPlayingSize = sizeof(audioPlaying);
AudioSessionGetProperty(kAudioSessionProperty_OtherAudioIsPlaying, &audioPlayingSize, &audioPlaying);
return (BOOL)audioPlaying;
}
- (void)fadeVolume:(NSTimer*)aTimer {
fadeDuration += kFadeInterval;
if (fadeDuration > targetFadeDuration) {
if (timer) {
[timer invalidate];
timer = NULL;
}
isFading = NO;
if (stopMusicAfterFade) {
[musicPlayer stop];
isMusicPlaying = NO;
}
} else {
currentMusicVolume += fadeAmount;
}
// If music is current playing then set its volume
if(isMusicPlaying) {
[musicPlayer setVolume:currentMusicVolume];
}
}
- (void)checkForErrors {
alError = alGetError();
if(alError != AL_NO_ERROR) {
NSLog(@"ERROR - SoundManager: OpenAL reported error '%d'", alError);
}
}
@end

View File

@@ -0,0 +1,68 @@
//
// SynthesizeSingleton.h
// CocoaWithLove
//
// Created by Matt Gallagher on 20/10/08.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
\
static classname *shared##classname = nil; \
\
+ (classname *)shared##classname \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [[self alloc] init]; \
} \
} \
\
return shared##classname; \
} \
\
+ (id)allocWithZone:(NSZone *)zone \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [super allocWithZone:zone]; \
return shared##classname; \
} \
} \
\
return nil; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return self; \
} \
\
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return NSUIntegerMax; \
} \
\
- (void)release \
{ \
} \
\
- (id)autorelease \
{ \
return self; \
}

View File

@@ -56,7 +56,6 @@
12059D9814980B7300DAC43B /* JSpline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232D3128A01F400C34032 /* JSpline.cpp */; };
12059D9914980B7300DAC43B /* JSprite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232D4128A01F400C34032 /* JSprite.cpp */; };
12059D9A14980B7300DAC43B /* JGfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232D8128A01F400C34032 /* JGfx.cpp */; };
12059D9B14980B7300DAC43B /* JSfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232D9128A01F400C34032 /* JSfx.cpp */; };
12059D9C14980B7300DAC43B /* tinystr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232DE128A01F400C34032 /* tinystr.cpp */; };
12059D9D14980B7300DAC43B /* tinyxml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232E0128A01F400C34032 /* tinyxml.cpp */; };
12059D9E14980B7300DAC43B /* tinyxmlerror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232E2128A01F400C34032 /* tinyxmlerror.cpp */; };
@@ -323,11 +322,8 @@
128ED4F9148BC94D00C58E83 /* CJSONSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 128ED496148BC94C00C58E83 /* CJSONSerializer.m */; };
128ED4FA148BC94D00C58E83 /* CSerializedJSONData.m in Sources */ = {isa = PBXBuildFile; fileRef = 128ED498148BC94C00C58E83 /* CSerializedJSONData.m */; };
128ED508148BCB7D00C58E83 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 128ED507148BCB7D00C58E83 /* CoreLocation.framework */; };
128ED50C148BCBBC00C58E83 /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 128ED509148BCBBC00C58E83 /* AddressBook.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
128ED50D148BCBBC00C58E83 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 128ED50A148BCBBC00C58E83 /* AVFoundation.framework */; };
128ED50E148BCBBC00C58E83 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 128ED50B148BCBBC00C58E83 /* MapKit.framework */; };
128ED510148BCC1900C58E83 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 128ED50F148BCC1900C58E83 /* libsqlite3.dylib */; settings = {ATTRIBUTES = (Weak, ); }; };
128ED519148BF0E000C58E83 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 128ED518148BF0E000C58E83 /* MediaPlayer.framework */; };
129654D1148A52740031100B /* iAd.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 129654D0148A52730031100B /* iAd.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
129654D6148AA23A0031100B /* modrules.xml in Resources */ = {isa = PBXBuildFile; fileRef = 129654D5148AA2390031100B /* modrules.xml */; };
12B812341404B9E20092E303 /* !Pak0.cpk in Resources */ = {isa = PBXBuildFile; fileRef = 12B8121F1404B9E10092E303 /* !Pak0.cpk */; };
@@ -345,6 +341,14 @@
12B8124F1404BD0D0092E303 /* ObjectAnalytics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 12B8124C1404BD0D0092E303 /* ObjectAnalytics.cpp */; };
12CCA030144A05D100E343A0 /* AbilityParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 12CCA02F144A05D100E343A0 /* AbilityParser.cpp */; };
12D095E114417D0500F69056 /* libstdc++.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 12D095E014417D0500F69056 /* libstdc++.dylib */; settings = {ATTRIBUTES = (Weak, ); }; };
12DCD02114DB917F0023B966 /* MyOpenALSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 12DCD01C14DB917F0023B966 /* MyOpenALSupport.c */; };
12DCD02214DB917F0023B966 /* MyOpenALSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 12DCD01C14DB917F0023B966 /* MyOpenALSupport.c */; };
12DCD02314DB917F0023B966 /* SoundManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 12DCD01F14DB917F0023B966 /* SoundManager.m */; };
12DCD02414DB917F0023B966 /* SoundManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 12DCD01F14DB917F0023B966 /* SoundManager.m */; };
12DCD02D14DBE1F90023B966 /* JSfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 12DCD02C14DBE1F90023B966 /* JSfx.cpp */; };
12DCD02E14DBE1F90023B966 /* JSfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 12DCD02C14DBE1F90023B966 /* JSfx.cpp */; };
12DCD03414DBECC50023B966 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12DCD03114DBECC50023B966 /* CoreAudio.framework */; };
12DCD03514DBECC50023B966 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12DCD03214DBECC50023B966 /* OpenAL.framework */; };
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
28FD15000DC6FC520079059D /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD14FF0DC6FC520079059D /* OpenGLES.framework */; };
@@ -489,7 +493,6 @@
CEE2331A128A01F400C34032 /* JSpline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232D3128A01F400C34032 /* JSpline.cpp */; };
CEE2331B128A01F400C34032 /* JSprite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232D4128A01F400C34032 /* JSprite.cpp */; };
CEE2331E128A01F400C34032 /* JGfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232D8128A01F400C34032 /* JGfx.cpp */; };
CEE2331F128A01F400C34032 /* JSfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232D9128A01F400C34032 /* JSfx.cpp */; };
CEE23323128A01F400C34032 /* tinystr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232DE128A01F400C34032 /* tinystr.cpp */; };
CEE23324128A01F400C34032 /* tinyxml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232E0128A01F400C34032 /* tinyxml.cpp */; };
CEE23325128A01F400C34032 /* tinyxmlerror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CEE232E2128A01F400C34032 /* tinyxmlerror.cpp */; };
@@ -694,6 +697,15 @@
12CCA02F144A05D100E343A0 /* AbilityParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AbilityParser.cpp; sourceTree = "<group>"; };
12CCA032144A05DF00E343A0 /* AbilityParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbilityParser.h; sourceTree = "<group>"; };
12D095E014417D0500F69056 /* libstdc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libstdc++.dylib"; path = "usr/lib/libstdc++.dylib"; sourceTree = SDKROOT; };
12DCD01C14DB917F0023B966 /* MyOpenALSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MyOpenALSupport.c; sourceTree = "<group>"; };
12DCD01D14DB917F0023B966 /* MyOpenALSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyOpenALSupport.h; sourceTree = "<group>"; };
12DCD01E14DB917F0023B966 /* SoundManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SoundManager.h; sourceTree = "<group>"; };
12DCD01F14DB917F0023B966 /* SoundManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SoundManager.m; sourceTree = "<group>"; };
12DCD02014DB917F0023B966 /* SynthesizeSingleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SynthesizeSingleton.h; sourceTree = "<group>"; };
12DCD02C14DBE1F90023B966 /* JSfx.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = JSfx.cpp; path = ../../JGE/src/iOS/JSfx.cpp; sourceTree = SOURCE_ROOT; };
12DCD03014DBECC40023B966 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
12DCD03114DBECC50023B966 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
12DCD03214DBECC50023B966 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; };
1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
1D6058910D05DD3D006BFB54 /* wagic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = wagic.app; sourceTree = BUILT_PRODUCTS_DIR; };
1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
@@ -952,7 +964,7 @@
CEE2323A128A01DD00C34032 /* JParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JParticleSystem.h; sourceTree = "<group>"; };
CEE2323B128A01DD00C34032 /* JRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRenderer.h; sourceTree = "<group>"; };
CEE2323C128A01DD00C34032 /* JResourceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JResourceManager.h; sourceTree = "<group>"; };
CEE2323E128A01DD00C34032 /* JSoundSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSoundSystem.h; sourceTree = "<group>"; };
CEE2323E128A01DD00C34032 /* JSoundSystem.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = JSoundSystem.h; sourceTree = "<group>"; };
CEE2323F128A01DD00C34032 /* JSpline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSpline.h; sourceTree = "<group>"; };
CEE23240128A01DD00C34032 /* JSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSprite.h; sourceTree = "<group>"; };
CEE23242128A01DD00C34032 /* JTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JTypes.h; sourceTree = "<group>"; };
@@ -1034,6 +1046,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
12DCD03414DBECC50023B966 /* CoreAudio.framework in Frameworks */,
12DCD03514DBECC50023B966 /* OpenAL.framework in Frameworks */,
128ED380148BAEC900C58E83 /* AudioToolbox.framework in Frameworks */,
128ED50D148BCBBC00C58E83 /* AVFoundation.framework in Frameworks */,
12D095E114417D0500F69056 /* libstdc++.dylib in Frameworks */,
12211EBB14934A2C00641703 /* CFNetwork.framework in Frameworks */,
12211EB914934A1900641703 /* MobileCoreServices.framework in Frameworks */,
@@ -1047,14 +1063,9 @@
28FD15080DC6FC5B0079059D /* QuartzCore.framework in Frameworks */,
CED2152C128DFAFF0050149E /* CoreGraphics.framework in Frameworks */,
F2494ADC12A1BD4100D6284A /* libz.dylib in Frameworks */,
128ED380148BAEC900C58E83 /* AudioToolbox.framework in Frameworks */,
128ED381148BAEC900C58E83 /* MessageUI.framework in Frameworks */,
128ED382148BAEC900C58E83 /* SystemConfiguration.framework in Frameworks */,
128ED508148BCB7D00C58E83 /* CoreLocation.framework in Frameworks */,
128ED50C148BCBBC00C58E83 /* AddressBook.framework in Frameworks */,
128ED50D148BCBBC00C58E83 /* AVFoundation.framework in Frameworks */,
128ED50E148BCBBC00C58E83 /* MapKit.framework in Frameworks */,
128ED519148BF0E000C58E83 /* MediaPlayer.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1340,6 +1351,7 @@
129654D2148AA2390031100B /* iOS */ = {
isa = PBXGroup;
children = (
12DCD01A14DB913B0023B966 /* Managers */,
CE9A478312B514BA00C9F38A /* wagicAppDelegate.h */,
CE9A478412B514BA00C9F38A /* wagicAppDelegate.m */,
128FB96A149537A600ED4EE6 /* Images */,
@@ -1408,6 +1420,34 @@
path = Debug;
sourceTree = "<group>";
};
12DCD01A14DB913B0023B966 /* Managers */ = {
isa = PBXGroup;
children = (
12DCD01B14DB917F0023B966 /* SoundManager */,
);
name = Managers;
sourceTree = "<group>";
};
12DCD01B14DB917F0023B966 /* SoundManager */ = {
isa = PBXGroup;
children = (
12DCD01C14DB917F0023B966 /* MyOpenALSupport.c */,
12DCD01D14DB917F0023B966 /* MyOpenALSupport.h */,
12DCD01E14DB917F0023B966 /* SoundManager.h */,
12DCD01F14DB917F0023B966 /* SoundManager.m */,
12DCD02014DB917F0023B966 /* SynthesizeSingleton.h */,
);
path = SoundManager;
sourceTree = "<group>";
};
12DCD02B14DBE1AF0023B966 /* ios */ = {
isa = PBXGroup;
children = (
12DCD02C14DBE1F90023B966 /* JSfx.cpp */,
);
name = ios;
sourceTree = "<group>";
};
19C28FACFE9D520D11CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
@@ -1452,6 +1492,9 @@
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
12DCD03014DBECC40023B966 /* AudioUnit.framework */,
12DCD03114DBECC50023B966 /* CoreAudio.framework */,
12DCD03214DBECC50023B966 /* OpenAL.framework */,
12211EBA14934A2C00641703 /* CFNetwork.framework */,
12211EB814934A1800641703 /* MobileCoreServices.framework */,
1216D632148F7411000F2295 /* libc++abi.dylib */,
@@ -1802,6 +1845,7 @@
CEE232AF128A01F400C34032 /* src */ = {
isa = PBXGroup;
children = (
12DCD02B14DBE1AF0023B966 /* ios */,
12B8121D1404B9E10092E303 /* zipFS */,
CEE232B3128A01F400C34032 /* hge */,
CEE232D7128A01F400C34032 /* pc */,
@@ -2027,7 +2071,6 @@
12059D9814980B7300DAC43B /* JSpline.cpp in Sources */,
12059D9914980B7300DAC43B /* JSprite.cpp in Sources */,
12059D9A14980B7300DAC43B /* JGfx.cpp in Sources */,
12059D9B14980B7300DAC43B /* JSfx.cpp in Sources */,
12059D9C14980B7300DAC43B /* tinystr.cpp in Sources */,
12059D9D14980B7300DAC43B /* tinyxml.cpp in Sources */,
12059D9E14980B7300DAC43B /* tinyxmlerror.cpp in Sources */,
@@ -2203,6 +2246,9 @@
12059E4814980B7300DAC43B /* WagicDownloadProgressViewController.m in Sources */,
12272FC514CD57CF00192DC7 /* SimpleButton.cpp in Sources */,
12272FC914CD6A3900192DC7 /* InteractiveButton.cpp in Sources */,
12DCD02214DB917F0023B966 /* MyOpenALSupport.c in Sources */,
12DCD02414DB917F0023B966 /* SoundManager.m in Sources */,
12DCD02E14DBE1F90023B966 /* JSfx.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2237,7 +2283,6 @@
CEE2331A128A01F400C34032 /* JSpline.cpp in Sources */,
CEE2331B128A01F400C34032 /* JSprite.cpp in Sources */,
CEE2331E128A01F400C34032 /* JGfx.cpp in Sources */,
CEE2331F128A01F400C34032 /* JSfx.cpp in Sources */,
CEE23323128A01F400C34032 /* tinystr.cpp in Sources */,
CEE23324128A01F400C34032 /* tinyxml.cpp in Sources */,
CEE23325128A01F400C34032 /* tinyxmlerror.cpp in Sources */,
@@ -2413,6 +2458,9 @@
12211EC91494360C00641703 /* WagicDownloadProgressViewController.m in Sources */,
12272FC414CD57CF00192DC7 /* SimpleButton.cpp in Sources */,
12272FC814CD6A3900192DC7 /* InteractiveButton.cpp in Sources */,
12DCD02114DB917F0023B966 /* MyOpenALSupport.c in Sources */,
12DCD02314DB917F0023B966 /* SoundManager.m in Sources */,
12DCD02D14DBE1F90023B966 /* JSfx.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};