Iphone 如何将2.m文件(含2.xibs)中的音乐和SFX开/关按钮与singleton类中的音频连接起来

Iphone 如何将2.m文件(含2.xibs)中的音乐和SFX开/关按钮与singleton类中的音频连接起来,iphone,cocoa-touch,Iphone,Cocoa Touch,我创建了一个singleton类,用于处理我正在创建的益智游戏的背景音乐和SFX。这个班在游戏中表现良好。音乐和SFX一样启动和停止,但我在将它们连接到main menu.xib(main menu.m)上选项菜单中的开/关按钮时遇到问题 我无法从main menu.xib打开和关闭它们,也无法让游戏中的按钮(在game.xib(game.m)上)对主菜单中的选择做出反应。反过来也是一样。无法通过主菜单中的按钮了解游戏中的音乐或SFX是否已关闭 有没有什么办法能把他们制服 或者其他建议 另外,我

我创建了一个singleton类,用于处理我正在创建的益智游戏的背景音乐和SFX。这个班在游戏中表现良好。音乐和SFX一样启动和停止,但我在将它们连接到main menu.xib(main menu.m)上选项菜单中的开/关按钮时遇到问题

我无法从main menu.xib打开和关闭它们,也无法让游戏中的按钮(在game.xib(game.m)上)对主菜单中的选择做出反应。反过来也是一样。无法通过主菜单中的按钮了解游戏中的音乐或SFX是否已关闭

有没有什么办法能把他们制服

或者其他建议

另外,我还从sharedSoundManager部分收到警告消息。警告说“可能没有反应”。我遗漏了什么吗?(我从viewDidLoad调用了sharedSoundManager,如下所示sharedSoundManager=[SingletonSoundManager sharedSoundManager];)

}

}

更新 我的单人音响管理课

#import "SingletonSoundManager.h"

@interface SingletonSoundManager (Private)
- (BOOL)initOpenAL;
- (NSUInteger) nextAvailableSource;
- (AudioFileID) openAudioFile:(NSString*)theFilePath;
- (UInt32) audioFileSize:(AudioFileID)fileDescriptor;
@end


@implementation SingletonSoundManager

// This var will hold our Singleton class instance that will be handed to anyone who asks for it
static SingletonSoundManager *sharedSoundManager = nil;

// Class method which provides access to the sharedSoundManager var.
+ (SingletonSoundManager *)sharedSoundManager {

    // synchronized is used to lock the object and handle multiple threads accessing this method at
    // the same time
    @synchronized(self) {

        // If the sharedSoundManager var is nil then we need to allocate it.
        if(sharedSoundManager == nil) {
            // Allocate and initialize an instance of this class
            [[self alloc] init];
        }
    }

    // Return the sharedSoundManager
    return sharedSoundManager;
}


/* This is called when you alloc an object.  To protect against instances of this class being
 allocated outside of the sharedSoundManager method, this method checks to make sure 
 that the sharedSoundManager is nil before allocating and initializing it.  If it is not
 nil then nil is returned and the instance would need to be obtained through the sharedSoundManager method
 */
+ (id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (sharedSoundManager == nil) {
            sharedSoundManager = [super allocWithZone:zone];
            return sharedSoundManager;  // assignment and return on first allocation
        }
    }
    return nil; //on subsequent allocation attempts return nil
}


- (id)copyWithZone:(NSZone *)zone {
    return self;
}


/* 
 When the init is called from the sharedSoundManager class method, this method will get called.
 This is where we then initialize the arrays and dictionaries which will store the OpenAL buffers
 create as well as the soundLibrary dictionary
 */
- (id)init {
    if(self = [super init]) {
        soundSources = [[NSMutableArray alloc] init];
        soundLibrary = [[NSMutableDictionary alloc] init];
        musicLibrary = [[NSMutableDictionary alloc] init];

        // Set the default volume for music
        backgroundMusicVolume = 1.0f;

        // Set up the OpenAL
        BOOL result = [self initOpenAL];
        if(!result) return nil;
        return self;
    }
    [self release];
    return nil;
}


/*
 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 {
    // 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 "air"
        context = alcCreateContext(device, NULL);
        // Make the context we have just created into the active context
        alcMakeContextCurrent(context);
        // Pre-create 32 sound sources which can be dynamically allocated to buffers (sounds)
        NSUInteger sourceID;
        for(int index = 0; index < kMaxSources; index++) {
            // Generate an OpenAL source
            alGenSources(1, &sourceID);
            // Add the generated sourceID to our array of sound sources
            [soundSources addObject:[NSNumber numberWithUnsignedInt:sourceID]];
        }

        // Return YES as we have successfully initialized OpenAL
        return YES;
    }
    // Something went wrong so return NO
    return NO;
}

- (void) shutdownSoundManager {
    @synchronized(self) {
        if(sharedSoundManager != nil) {
            [self dealloc];
        }
    }
}


- (void) loadSoundWithKey:(NSString*)theSoundKey fileName:(NSString*)theFileName fileExt:(NSString*)theFileExt frequency:(NSUInteger)theFrequency {

    // Get the full path of the audio file
    NSString *filePath = [[NSBundle mainBundle] pathForResource:theFileName ofType:theFileExt];

    // Now we need to open the file
    AudioFileID fileID = [self openAudioFile:filePath];

    // Find out how big the actual audio data is
    UInt32 fileSize = [self audioFileSize:fileID];

    // Create a location for the audio data to live temporarily
    unsigned char *outData = malloc(fileSize);

    // Load the byte data from the file into the data buffer
    OSStatus result = noErr;
    result = AudioFileReadBytes(fileID, FALSE, 0, &fileSize, outData);
    AudioFileClose(fileID);

    if(result != 0) {
        NSLog(@"ERROR SoundEngine: Cannot load sound: %@", theFileName);
        return;
    }

    NSUInteger bufferID;

    // Generate a buffer within OpenAL for this sound
    alGenBuffers(1, &bufferID);

    // Place the audio data into the new buffer
    alBufferData(bufferID, AL_FORMAT_STEREO16, outData, fileSize, theFrequency);

    // Save the buffer to be used later
    [soundLibrary setObject:[NSNumber numberWithUnsignedInt:bufferID] forKey:theSoundKey];

    // Clean the buffer
    if(outData) {
        free(outData);
        outData = NULL;
    }
}


- (void) loadBackgroundMusicWithKey:(NSString*)theMusicKey fileName:(NSString*)theFileName fileExt:(NSString*)theFileExt {

    NSString *path = [[NSBundle mainBundle] pathForResource:theFileName ofType:theFileExt];
    [musicLibrary setObject:path forKey:theMusicKey];
}


/*
 Used to load an audiofile from the file path which is provided.
 */
- (AudioFileID) openAudioFile:(NSString*)theFilePath {

    AudioFileID outAFID;
    // Create an NSURL which will be used to load the file.  This is slightly easier
    // than using a CFURLRef
    NSURL *afUrl = [NSURL fileURLWithPath:theFilePath];

    // Open the audio file provided
    OSStatus result = AudioFileOpenURL((CFURLRef)afUrl, kAudioFileReadPermission, 0, &outAFID);

    // If we get a result that is not 0 then something has gone wrong.  We report it and 
    // return the out audio file id
    if(result != 0) {
        NSLog(@"ERROR SoundEngine: Cannot open file: %@", theFilePath);
        return nil;
    }

    return outAFID;
}


/*
 This helper method returns the file size in bytes for a given AudioFileID
 */
- (UInt32) audioFileSize:(AudioFileID)fileDescriptor {
    UInt64 outDataSize = 0;
    UInt32 thePropSize = sizeof(UInt64);
    OSStatus result = AudioFileGetProperty(fileDescriptor, kAudioFilePropertyAudioDataByteCount, &thePropSize, &outDataSize);
    if(result != 0) NSLog(@"ERROR: cannot file file size");
    return (UInt32)outDataSize;
}


/*
 Plays the sound which matches the key provided.  The Gain, pitch and if the sound should loop can
 also be set from the method signature
 */
- (NSUInteger) playSoundWithKey:(NSString*)theSoundKey gain:(ALfloat)theGain pitch:(ALfloat)thePitch shouldLoop:(BOOL)theShouldLoop {

    ALenum err = alGetError(); // clear the error code

    // Find the buffer linked to the key which has been passed in
    NSNumber *numVal = [soundLibrary objectForKey:theSoundKey];
    if(numVal == nil) return 0;
    NSUInteger bufferID = [numVal unsignedIntValue];

    // Find an available source i.e. it is currently not playing anything
    NSUInteger sourceID = [self nextAvailableSource];

    // 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, thePitch);
    alSourcef(sourceID, AL_GAIN, theGain);

    // Set the looping value
    if(theShouldLoop) {
        alSourcei(sourceID, AL_LOOPING, AL_TRUE);
    } else {
        alSourcei(sourceID, AL_LOOPING, AL_FALSE);
    }

    // Check to see if there were any errors
    err = alGetError();
    if(err != 0) {
        NSLog(@"ERROR SoundManager: %d", err);
        return 0;
    }

    // Now play the sound
    alSourcePlay(sourceID);

    // Return the source ID so that loops can be stopped etc
    return sourceID;
}


- (void) stopSoundWithKey:(NSString*)theSoundKey {

    // Reset errors in OpenAL
    ALenum alError = alGetError();
    alError = AL_NO_ERROR;

    // Find the buffer which has been linked to the sound key provided
    NSNumber *numVal = [soundLibrary objectForKey:theSoundKey];

    // 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", theSoundKey);
        return;
    }

    // Get the buffer number from
    NSUInteger bufferID = [numVal unsignedIntValue];
    NSInteger bufferForSource;
    for(NSNumber *sourceID in soundSources) {

        NSUInteger currentSourceID = [sourceID unsignedIntValue];

        // Grab the buffer currently bound to this source
        alGetSourcei(currentSourceID, AL_BUFFER, &bufferForSource);

        // If the buffer matches the buffer we want to stop then stop the source and unbind it from the buffer
        if(bufferForSource == bufferID) {
            alSourceStop(currentSourceID);
            alSourcei(currentSourceID, AL_BUFFER, 0);
        }
    } 

    // Check for any errors
    if((alError = alGetError()) != AL_NO_ERROR)
        NSLog(@"ERROR - SoundManager: Could not stop sound with key '%@' got error %x", theSoundKey, alError);

}


/*
 Play the background track which matches the key
 */
- (void) playMusicWithKey:(NSString*)theMusicKey timesToRepeat:(NSUInteger)theTimesToRepeat {

    NSError *error;

    NSString *path = [musicLibrary objectForKey:theMusicKey];

    if(!path) {
        NSLog(@"ERROR SoundEngine: The music key '%@' could not be found", theMusicKey);
        return;
    }

    // Initialize the AVAudioPlayer
    backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];

    // If the backgroundMusicPlayer object is nil then there was an error
    if(!backgroundMusicPlayer) {
        NSLog(@"ERROR SoundManager: Could not play music for key '%d'", error);
        return;
    }       

    // Set the number of times this music should repeat.  -1 means never stop until its asked to stop
    [backgroundMusicPlayer setNumberOfLoops:theTimesToRepeat];

    // Set the volume of the music
    [backgroundMusicPlayer setVolume:backgroundMusicVolume];

    // Play the music
    [backgroundMusicPlayer play];


}


/*
 Stop playing the currently playing music
 */
- (void) stopPlayingMusic{
    [backgroundMusicPlayer stop];
}

- (void) pausePlayingMusic{
    [backgroundMusicPlayer pause];
}

- (void) resumePlayingMusic{
    [backgroundMusicPlayer play];
}


/*
 Set the volume of the music which is between 0.0 and 1.0
 */
- (void) setBackgroundMusicVolume:(ALfloat)theVolume {

    // Set the volume iVar
    backgroundMusicVolume = theVolume;

    // Check to make sure that the audio player exists and if so set its volume
    if(backgroundMusicPlayer) {
        [backgroundMusicPlayer setVolume:backgroundMusicVolume];

    }
}


/* 
 Search through the max number of sources to find one which is not planning.  If one cannot
 be found that is not playing then the first one which is looping is stopped and used instead.
 If a source still cannot be found then the first source is stopped and used
 */
- (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 sounrce and use that
    NSUInteger sourceID = [[soundSources objectAtIndex:0] unsignedIntegerValue];
    alSourceStop(sourceID);
    return sourceID;
}


- (id)retain {
    return self;
}


- (unsigned)retainCount {
    return UINT_MAX;  //denotes an object that cannot be released
} 


- (void)release {
    //do nothing
}


- (id)autorelease {
    return self;
}

- (void)dealloc {
    // Loop through the OpenAL sources and delete them
    for(NSNumber *numVal in soundSources) {
        NSUInteger sourceID = [numVal unsignedIntValue];
        alDeleteSources(1, &sourceID);
    }

    // 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);      
    }

    // Release the arrays and dictionaries we have been using
    [soundLibrary release];
    [soundSources release];
    [musicLibrary release];

    // Disable and then destroy the context
    alcMakeContextCurrent(NULL);
    alcDestroyContext(context);

    // Close the device
    alcCloseDevice(device);

    [super dealloc];
}

@end


//My SFX and Music methods from the game.m class



    -(IBAction)SFX{

    BOOL buttonState = [SingletonSoundManager isSFXOn]; //generated "may not respond" warnings. why?
           //BOOL buttonState = [SingletonSoundManager isSFXOn]:
    [SingletonSoundManager setSFXOn:!buttonState]; //generated "may not respond" warnings. why?



    if(sfx2.hidden){
        NSLog(@"SFX Stops");
        sfx1.hidden = buttonState;
        sfx2.hidden = !buttonState;
        [sharedSoundManager stopSoundWithKey:@"movingbricks"]; //generated "may not respond" warnings. why?

    }
    else if(sfx1.hidden){
        NSLog(@"SFX Starts");
        sfx1.hidden = buttonState;
        sfx2.hidden = !buttonState;
        [sharedSoundManager playSoundWithKey:@"movingbricks" gain:0.13f pitch:1.0f shouldLoop:NO]; //generated "may not respond" warnings. why?



    }
}

-(IBAction)MUSIC{
    BOOL buttonState = [SingletonSoundManager isMusicOn]; //generated "may not respond" warnings. why?
           //BOOL buttonState = [SingletonSoundManager resumePlayingMusic];
    [SingletonSoundManager setMusicOn:!buttonState]; //generated "may not respond" warnings. why?
          //[SingletonSoundManager pausePlayingMusic:!buttonState]; 


    if(music2.hidden){
        NSLog(@"Music Stops");
        music1.hidden = buttonState;
        music2.hidden = buttonState;
        [sharedSoundManager pausePlayingMusic]; //generated "may not respond" warnings. why?


    }
    else if(music1.hidden){
        NSLog(@"Music Starts");
        music1.hidden = buttonState;
        music2.hidden = !buttonState;
        [sharedSoundManager resumePlayingMusic]; //generated "may not respond" warnings. why?


    }
}

//My SFX and Music methods inside my mainmenu.m class

    //Turn on and off the SFX
-(IBAction)SFXswap{

    BOOL buttonState = [SingletonSoundManager isSFXOn];  //generated "may not respond" warnings. why?

    [SingletonSoundManager setSFXOn:!buttonState];  //generated "may not respond" warnings. why?



    if(sfx2.hidden){
        NSLog(@"SFX Stops");
        sfx1.hidden = buttonState;
        sfx2.hidden = buttonState;
        [sharedSoundManager stopSoundWithKey:@"movingbricks"]; //generated "may not respond" warnings. why?


    }
    else if(sfx1.hidden){
        NSLog(@"SFX Starts");
        sfx1.hidden = buttonState;
        sfx2.hidden = buttonState;
        [sharedSoundManager playSoundWithKey:@"movingbricks" gain:0.13f pitch:1.0f shouldLoop:NO]; //generated "may not respond" warnings. why?


    }
}

//Turn on and off the backgrund music
-(IBAction)MUSICswap{

    BOOL buttonState = [SingletonSoundManager isMusicOn];  //generated "may not respond" warnings. why?
           //BOOL buttonState = [SingletonSoundManager resumePlayingMusic];
    [SingletonSoundManager setMusicOn:!buttonState];  //generated "may not respond" warnings. why?
           //[SingletonSoundManager pausePlayingMusic:!buttonState]; 



    if(music2.hidden){
        NSLog(@"Music Stops");
        music1.hidden = buttonState;
        music2.hidden = !buttonState;
        [sharedSoundManager pausePlayingMusic]; //generated "may not respond" warnings. why?


    }
    else if(music1.hidden){
        NSLog(@"Music Starts");
        music1.hidden = buttonState;
        music2.hidden = !buttonState;
        [sharedSoundManager resumePlayingMusic]; //generated "may not respond" warnings. why?


    }
}


if this how you meant, Ravin? Please corret me if i´m wrong :)


------------------------------------------------------------------------------------------------------------NEW UPDATE
------------------------------------------------------------------------------------------------------------

    //In singletonSoundManager.h

BOOL isMusicOn; 

- (BOOL) isMusicOn; 
- (void) setMusicOn:(BOOL)boolValue; 


//In singletonSoundManager.m

- (void) playMusicWithKey:(NSString*)theMusicKey timesToRepeat:(NSUInteger)theTimesToRepeat {

    NSError *error;

    NSString *path = [musicLibrary objectForKey:theMusicKey];

    if(!path) {
        NSLog(@"ERROR SoundEngine: The music key '%@' could not be found", theMusicKey);
        return;
    }

    // Initialize the AVAudioPlayer
    backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];

    // If the backgroundMusicPlayer object is nil then there was an error
    if(!backgroundMusicPlayer) {
        NSLog(@"ERROR SoundManager: Could not play music for key '%d'", error);
        return;
    }        

    // Set the number of times this music should repeat.  -1 means never stop until its asked to stop
    [backgroundMusicPlayer setNumberOfLoops:theTimesToRepeat];

    // Set the volume of the music
    [backgroundMusicPlayer setVolume:backgroundMusicVolume];

    // Play the music
    [backgroundMusicPlayer play];

    isMusicOn = YES;
}

-(BOOL)isMusicOn 
{ 
    return isMusicOn; 
} 

-(void)setMusicOn:(BOOL)boolValue 
{ 
    isMusicOn = boolValue; 
} 


/*
 Stop playing the currently playing music
 */
- (void) stopPlayingMusic{
    [backgroundMusicPlayer stop];
}

- (void) pausePlayingMusic{
    [backgroundMusicPlayer pause];
    isMusicOn = NO;
}

- (void) resumePlayingMusic{
    [backgroundMusicPlayer play];
    isMusicOn = YES;
}


//In game.m 


-(IBAction)MUSIC{

    BOOL buttonstate = [SingletonSoundManager isMusicOn]; // "SingletonSoundManager may not respond to isMusicOn" 
                                                                                       //" Initialization makes integer from pointer without a cast"

    [SingletonSoundManager setMusicOn:!buttonstate]; //"SingletonSoundManager may not respond to setMusicOn"

    if(music2.hidden){
        NSLog(@"Music Stops");
        music1.hidden = buttonstate;
        music2.hidden = !buttonstate;

        [sharedSoundManager pausePlayingMusic]; //"SingletonSoundManager may not respond to pausePlayingMusic"

    }
    else if(music1.hidden){
        NSLog(@"Music Starts");
        music1.hidden = !buttonstate;
        music2.hidden = buttonstate;

        [sharedSoundManager resumePlayingMusic]; //"SingletonSoundManager may not respond to resumePlayingMusic"
    }
}


in mainmenu.m
-(IBAction)MUSICswap{

    BOOL buttonstate = [SingletonSoundManager isMusicOn]; // "SingletonSoundManager may not respond to isMusicOn" 
                                                                                       //" Initialization makes integer from pointer without a cast"

    [SingletonSoundManager setMusicOn:!buttonstate]; //"SingletonSoundManager may not respond to setMusicOn"

    if(music2.hidden){
        NSLog(@"Music Stops");
        //music1.hidden = YES;
        //music2.hidden = NO;
        music1.hidden = buttonstate;
        music2.hidden = !buttonstate;
        [sharedSoundManager pausePlayingMusic]; //"SingletonSoundManager may not respond to pausePlayingMusic"
    }
    else if(music1.hidden){
        NSLog(@"Music Starts");
        //music1.hidden = NO;
        //music2.hidden = YES;
        music1.hidden = !buttonstate;
        music2.hidden = buttonstate;
        [sharedSoundManager resumePlayingMusic]; //"SingletonSoundManager may not respond to resumePlayingMusic"
    }
}
#导入“SingletonSoundManager.h”
@接口SingletonSoundManager(专用)
-(BOOL)初始开放;
-(整数)下一个可用源;
-(AudioFileID)openAudioFile:(NSString*)文件路径;
-(UInt32)audioFileSize:(AudioFileID)fileDescriptor;
@结束
@实现SingletonSoundManager
//这个var将保存我们的Singleton类实例,它将被交给任何请求它的人
静态SingletonSoundManager*sharedSoundManager=nil;
//类方法,该方法提供对sharedSoundManager变量的访问。
+(SingletonSoundManager*)共享声音管理器{
//synchronized用于锁定对象,并处理多个线程同时访问此方法
//同时
@同步(自){
//如果sharedSoundManager变量为零,那么我们需要分配它。
if(sharedSoundManager==nil){
//分配并初始化此类的实例
[[self alloc]init];
}
}
//返回sharedSoundManager
返回sharedSoundManager;
}
/*当您分配对象时,将调用此函数。要防止此类实例被删除,请执行以下操作:
分配到sharedSoundManager方法之外,此方法将进行检查以确保
在分配和初始化sharedSoundManager之前,确保它为零。如果不是
nil然后返回nil,需要通过sharedSoundManager方法获取实例
*/
+(id)allocWithZone:(NSZone*)区域{
@同步(自){
if(sharedSoundManager==nil){
sharedSoundManager=[super allocWithZone:zone];
return sharedSoundManager;//分配和第一次分配时返回
}
}
return nil;//在随后的分配尝试中返回nil
}
-(id)copyWithZone:(NSZone*)区{
回归自我;
}
/* 
当从sharedSoundManager类方法调用init时,将调用此方法。
这就是我们初始化数组和字典的地方,这些数组和字典将存储OpenAL缓冲区
创建以及soundLibrary字典
*/
-(id)init{
if(self=[super init]){
声源=[[NSMutableArray alloc]init];
soundLibrary=[[NSMutableDictionary alloc]init];
musicLibrary=[[NSMutableDictionary alloc]init];
//设置音乐的默认音量
背景音乐音量=1.0f;
//设置OpenAL
BOOL result=[self initOpenAL];
如果(!result)返回nil;
回归自我;
}
[自我释放];
返回零;
}
/*
此方法用于初始化OpenAL。它获取默认设备,创建新上下文
要使用,然后预加载定义#源。这种预加载意味着我们将能够玩到
(最多32个)同时发出不同的声音
*/
-(BOOL)初始开放{
//获取我们将用于声音的设备。使用NULL获取默认设备
设备=alcOpenDevice(空);
//如果找到了一个设备,我们需要创建一个上下文,使其成为当前设备,然后
//预加载OpenAL源代码
如果(设备){
//使用我们现在拥有的设备创建上下文“air”
context=alcreatecontext(设备,空);
//将刚刚创建的上下文转换为活动上下文
alcMakeContextCurrent(上下文);
//预创建32个可动态分配到缓冲区(声音)的声源
整数源ID;
for(int index=0;index-(IBAction)MUSIC{

if(music2.hidden){
    NSLog(@"Music Stops");
    music1.hidden = YES;
    music2.hidden = NO;
    [sharedSoundManager pausePlayingMusic];
}
else if(music1.hidden){
    NSLog(@"Music Starts");
    music1.hidden = NO;
    music2.hidden = YES;
    [sharedSoundManager resumePlayingMusic];
}
#import "SingletonSoundManager.h"

@interface SingletonSoundManager (Private)
- (BOOL)initOpenAL;
- (NSUInteger) nextAvailableSource;
- (AudioFileID) openAudioFile:(NSString*)theFilePath;
- (UInt32) audioFileSize:(AudioFileID)fileDescriptor;
@end


@implementation SingletonSoundManager

// This var will hold our Singleton class instance that will be handed to anyone who asks for it
static SingletonSoundManager *sharedSoundManager = nil;

// Class method which provides access to the sharedSoundManager var.
+ (SingletonSoundManager *)sharedSoundManager {

    // synchronized is used to lock the object and handle multiple threads accessing this method at
    // the same time
    @synchronized(self) {

        // If the sharedSoundManager var is nil then we need to allocate it.
        if(sharedSoundManager == nil) {
            // Allocate and initialize an instance of this class
            [[self alloc] init];
        }
    }

    // Return the sharedSoundManager
    return sharedSoundManager;
}


/* This is called when you alloc an object.  To protect against instances of this class being
 allocated outside of the sharedSoundManager method, this method checks to make sure 
 that the sharedSoundManager is nil before allocating and initializing it.  If it is not
 nil then nil is returned and the instance would need to be obtained through the sharedSoundManager method
 */
+ (id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (sharedSoundManager == nil) {
            sharedSoundManager = [super allocWithZone:zone];
            return sharedSoundManager;  // assignment and return on first allocation
        }
    }
    return nil; //on subsequent allocation attempts return nil
}


- (id)copyWithZone:(NSZone *)zone {
    return self;
}


/* 
 When the init is called from the sharedSoundManager class method, this method will get called.
 This is where we then initialize the arrays and dictionaries which will store the OpenAL buffers
 create as well as the soundLibrary dictionary
 */
- (id)init {
    if(self = [super init]) {
        soundSources = [[NSMutableArray alloc] init];
        soundLibrary = [[NSMutableDictionary alloc] init];
        musicLibrary = [[NSMutableDictionary alloc] init];

        // Set the default volume for music
        backgroundMusicVolume = 1.0f;

        // Set up the OpenAL
        BOOL result = [self initOpenAL];
        if(!result) return nil;
        return self;
    }
    [self release];
    return nil;
}


/*
 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 {
    // 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 "air"
        context = alcCreateContext(device, NULL);
        // Make the context we have just created into the active context
        alcMakeContextCurrent(context);
        // Pre-create 32 sound sources which can be dynamically allocated to buffers (sounds)
        NSUInteger sourceID;
        for(int index = 0; index < kMaxSources; index++) {
            // Generate an OpenAL source
            alGenSources(1, &sourceID);
            // Add the generated sourceID to our array of sound sources
            [soundSources addObject:[NSNumber numberWithUnsignedInt:sourceID]];
        }

        // Return YES as we have successfully initialized OpenAL
        return YES;
    }
    // Something went wrong so return NO
    return NO;
}

- (void) shutdownSoundManager {
    @synchronized(self) {
        if(sharedSoundManager != nil) {
            [self dealloc];
        }
    }
}


- (void) loadSoundWithKey:(NSString*)theSoundKey fileName:(NSString*)theFileName fileExt:(NSString*)theFileExt frequency:(NSUInteger)theFrequency {

    // Get the full path of the audio file
    NSString *filePath = [[NSBundle mainBundle] pathForResource:theFileName ofType:theFileExt];

    // Now we need to open the file
    AudioFileID fileID = [self openAudioFile:filePath];

    // Find out how big the actual audio data is
    UInt32 fileSize = [self audioFileSize:fileID];

    // Create a location for the audio data to live temporarily
    unsigned char *outData = malloc(fileSize);

    // Load the byte data from the file into the data buffer
    OSStatus result = noErr;
    result = AudioFileReadBytes(fileID, FALSE, 0, &fileSize, outData);
    AudioFileClose(fileID);

    if(result != 0) {
        NSLog(@"ERROR SoundEngine: Cannot load sound: %@", theFileName);
        return;
    }

    NSUInteger bufferID;

    // Generate a buffer within OpenAL for this sound
    alGenBuffers(1, &bufferID);

    // Place the audio data into the new buffer
    alBufferData(bufferID, AL_FORMAT_STEREO16, outData, fileSize, theFrequency);

    // Save the buffer to be used later
    [soundLibrary setObject:[NSNumber numberWithUnsignedInt:bufferID] forKey:theSoundKey];

    // Clean the buffer
    if(outData) {
        free(outData);
        outData = NULL;
    }
}


- (void) loadBackgroundMusicWithKey:(NSString*)theMusicKey fileName:(NSString*)theFileName fileExt:(NSString*)theFileExt {

    NSString *path = [[NSBundle mainBundle] pathForResource:theFileName ofType:theFileExt];
    [musicLibrary setObject:path forKey:theMusicKey];
}


/*
 Used to load an audiofile from the file path which is provided.
 */
- (AudioFileID) openAudioFile:(NSString*)theFilePath {

    AudioFileID outAFID;
    // Create an NSURL which will be used to load the file.  This is slightly easier
    // than using a CFURLRef
    NSURL *afUrl = [NSURL fileURLWithPath:theFilePath];

    // Open the audio file provided
    OSStatus result = AudioFileOpenURL((CFURLRef)afUrl, kAudioFileReadPermission, 0, &outAFID);

    // If we get a result that is not 0 then something has gone wrong.  We report it and 
    // return the out audio file id
    if(result != 0) {
        NSLog(@"ERROR SoundEngine: Cannot open file: %@", theFilePath);
        return nil;
    }

    return outAFID;
}


/*
 This helper method returns the file size in bytes for a given AudioFileID
 */
- (UInt32) audioFileSize:(AudioFileID)fileDescriptor {
    UInt64 outDataSize = 0;
    UInt32 thePropSize = sizeof(UInt64);
    OSStatus result = AudioFileGetProperty(fileDescriptor, kAudioFilePropertyAudioDataByteCount, &thePropSize, &outDataSize);
    if(result != 0) NSLog(@"ERROR: cannot file file size");
    return (UInt32)outDataSize;
}


/*
 Plays the sound which matches the key provided.  The Gain, pitch and if the sound should loop can
 also be set from the method signature
 */
- (NSUInteger) playSoundWithKey:(NSString*)theSoundKey gain:(ALfloat)theGain pitch:(ALfloat)thePitch shouldLoop:(BOOL)theShouldLoop {

    ALenum err = alGetError(); // clear the error code

    // Find the buffer linked to the key which has been passed in
    NSNumber *numVal = [soundLibrary objectForKey:theSoundKey];
    if(numVal == nil) return 0;
    NSUInteger bufferID = [numVal unsignedIntValue];

    // Find an available source i.e. it is currently not playing anything
    NSUInteger sourceID = [self nextAvailableSource];

    // 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, thePitch);
    alSourcef(sourceID, AL_GAIN, theGain);

    // Set the looping value
    if(theShouldLoop) {
        alSourcei(sourceID, AL_LOOPING, AL_TRUE);
    } else {
        alSourcei(sourceID, AL_LOOPING, AL_FALSE);
    }

    // Check to see if there were any errors
    err = alGetError();
    if(err != 0) {
        NSLog(@"ERROR SoundManager: %d", err);
        return 0;
    }

    // Now play the sound
    alSourcePlay(sourceID);

    // Return the source ID so that loops can be stopped etc
    return sourceID;
}


- (void) stopSoundWithKey:(NSString*)theSoundKey {

    // Reset errors in OpenAL
    ALenum alError = alGetError();
    alError = AL_NO_ERROR;

    // Find the buffer which has been linked to the sound key provided
    NSNumber *numVal = [soundLibrary objectForKey:theSoundKey];

    // 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", theSoundKey);
        return;
    }

    // Get the buffer number from
    NSUInteger bufferID = [numVal unsignedIntValue];
    NSInteger bufferForSource;
    for(NSNumber *sourceID in soundSources) {

        NSUInteger currentSourceID = [sourceID unsignedIntValue];

        // Grab the buffer currently bound to this source
        alGetSourcei(currentSourceID, AL_BUFFER, &bufferForSource);

        // If the buffer matches the buffer we want to stop then stop the source and unbind it from the buffer
        if(bufferForSource == bufferID) {
            alSourceStop(currentSourceID);
            alSourcei(currentSourceID, AL_BUFFER, 0);
        }
    } 

    // Check for any errors
    if((alError = alGetError()) != AL_NO_ERROR)
        NSLog(@"ERROR - SoundManager: Could not stop sound with key '%@' got error %x", theSoundKey, alError);

}


/*
 Play the background track which matches the key
 */
- (void) playMusicWithKey:(NSString*)theMusicKey timesToRepeat:(NSUInteger)theTimesToRepeat {

    NSError *error;

    NSString *path = [musicLibrary objectForKey:theMusicKey];

    if(!path) {
        NSLog(@"ERROR SoundEngine: The music key '%@' could not be found", theMusicKey);
        return;
    }

    // Initialize the AVAudioPlayer
    backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];

    // If the backgroundMusicPlayer object is nil then there was an error
    if(!backgroundMusicPlayer) {
        NSLog(@"ERROR SoundManager: Could not play music for key '%d'", error);
        return;
    }       

    // Set the number of times this music should repeat.  -1 means never stop until its asked to stop
    [backgroundMusicPlayer setNumberOfLoops:theTimesToRepeat];

    // Set the volume of the music
    [backgroundMusicPlayer setVolume:backgroundMusicVolume];

    // Play the music
    [backgroundMusicPlayer play];


}


/*
 Stop playing the currently playing music
 */
- (void) stopPlayingMusic{
    [backgroundMusicPlayer stop];
}

- (void) pausePlayingMusic{
    [backgroundMusicPlayer pause];
}

- (void) resumePlayingMusic{
    [backgroundMusicPlayer play];
}


/*
 Set the volume of the music which is between 0.0 and 1.0
 */
- (void) setBackgroundMusicVolume:(ALfloat)theVolume {

    // Set the volume iVar
    backgroundMusicVolume = theVolume;

    // Check to make sure that the audio player exists and if so set its volume
    if(backgroundMusicPlayer) {
        [backgroundMusicPlayer setVolume:backgroundMusicVolume];

    }
}


/* 
 Search through the max number of sources to find one which is not planning.  If one cannot
 be found that is not playing then the first one which is looping is stopped and used instead.
 If a source still cannot be found then the first source is stopped and used
 */
- (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 sounrce and use that
    NSUInteger sourceID = [[soundSources objectAtIndex:0] unsignedIntegerValue];
    alSourceStop(sourceID);
    return sourceID;
}


- (id)retain {
    return self;
}


- (unsigned)retainCount {
    return UINT_MAX;  //denotes an object that cannot be released
} 


- (void)release {
    //do nothing
}


- (id)autorelease {
    return self;
}

- (void)dealloc {
    // Loop through the OpenAL sources and delete them
    for(NSNumber *numVal in soundSources) {
        NSUInteger sourceID = [numVal unsignedIntValue];
        alDeleteSources(1, &sourceID);
    }

    // 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);      
    }

    // Release the arrays and dictionaries we have been using
    [soundLibrary release];
    [soundSources release];
    [musicLibrary release];

    // Disable and then destroy the context
    alcMakeContextCurrent(NULL);
    alcDestroyContext(context);

    // Close the device
    alcCloseDevice(device);

    [super dealloc];
}

@end


//My SFX and Music methods from the game.m class



    -(IBAction)SFX{

    BOOL buttonState = [SingletonSoundManager isSFXOn]; //generated "may not respond" warnings. why?
           //BOOL buttonState = [SingletonSoundManager isSFXOn]:
    [SingletonSoundManager setSFXOn:!buttonState]; //generated "may not respond" warnings. why?



    if(sfx2.hidden){
        NSLog(@"SFX Stops");
        sfx1.hidden = buttonState;
        sfx2.hidden = !buttonState;
        [sharedSoundManager stopSoundWithKey:@"movingbricks"]; //generated "may not respond" warnings. why?

    }
    else if(sfx1.hidden){
        NSLog(@"SFX Starts");
        sfx1.hidden = buttonState;
        sfx2.hidden = !buttonState;
        [sharedSoundManager playSoundWithKey:@"movingbricks" gain:0.13f pitch:1.0f shouldLoop:NO]; //generated "may not respond" warnings. why?



    }
}

-(IBAction)MUSIC{
    BOOL buttonState = [SingletonSoundManager isMusicOn]; //generated "may not respond" warnings. why?
           //BOOL buttonState = [SingletonSoundManager resumePlayingMusic];
    [SingletonSoundManager setMusicOn:!buttonState]; //generated "may not respond" warnings. why?
          //[SingletonSoundManager pausePlayingMusic:!buttonState]; 


    if(music2.hidden){
        NSLog(@"Music Stops");
        music1.hidden = buttonState;
        music2.hidden = buttonState;
        [sharedSoundManager pausePlayingMusic]; //generated "may not respond" warnings. why?


    }
    else if(music1.hidden){
        NSLog(@"Music Starts");
        music1.hidden = buttonState;
        music2.hidden = !buttonState;
        [sharedSoundManager resumePlayingMusic]; //generated "may not respond" warnings. why?


    }
}

//My SFX and Music methods inside my mainmenu.m class

    //Turn on and off the SFX
-(IBAction)SFXswap{

    BOOL buttonState = [SingletonSoundManager isSFXOn];  //generated "may not respond" warnings. why?

    [SingletonSoundManager setSFXOn:!buttonState];  //generated "may not respond" warnings. why?



    if(sfx2.hidden){
        NSLog(@"SFX Stops");
        sfx1.hidden = buttonState;
        sfx2.hidden = buttonState;
        [sharedSoundManager stopSoundWithKey:@"movingbricks"]; //generated "may not respond" warnings. why?


    }
    else if(sfx1.hidden){
        NSLog(@"SFX Starts");
        sfx1.hidden = buttonState;
        sfx2.hidden = buttonState;
        [sharedSoundManager playSoundWithKey:@"movingbricks" gain:0.13f pitch:1.0f shouldLoop:NO]; //generated "may not respond" warnings. why?


    }
}

//Turn on and off the backgrund music
-(IBAction)MUSICswap{

    BOOL buttonState = [SingletonSoundManager isMusicOn];  //generated "may not respond" warnings. why?
           //BOOL buttonState = [SingletonSoundManager resumePlayingMusic];
    [SingletonSoundManager setMusicOn:!buttonState];  //generated "may not respond" warnings. why?
           //[SingletonSoundManager pausePlayingMusic:!buttonState]; 



    if(music2.hidden){
        NSLog(@"Music Stops");
        music1.hidden = buttonState;
        music2.hidden = !buttonState;
        [sharedSoundManager pausePlayingMusic]; //generated "may not respond" warnings. why?


    }
    else if(music1.hidden){
        NSLog(@"Music Starts");
        music1.hidden = buttonState;
        music2.hidden = !buttonState;
        [sharedSoundManager resumePlayingMusic]; //generated "may not respond" warnings. why?


    }
}


if this how you meant, Ravin? Please corret me if i´m wrong :)


------------------------------------------------------------------------------------------------------------NEW UPDATE
------------------------------------------------------------------------------------------------------------

    //In singletonSoundManager.h

BOOL isMusicOn; 

- (BOOL) isMusicOn; 
- (void) setMusicOn:(BOOL)boolValue; 


//In singletonSoundManager.m

- (void) playMusicWithKey:(NSString*)theMusicKey timesToRepeat:(NSUInteger)theTimesToRepeat {

    NSError *error;

    NSString *path = [musicLibrary objectForKey:theMusicKey];

    if(!path) {
        NSLog(@"ERROR SoundEngine: The music key '%@' could not be found", theMusicKey);
        return;
    }

    // Initialize the AVAudioPlayer
    backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];

    // If the backgroundMusicPlayer object is nil then there was an error
    if(!backgroundMusicPlayer) {
        NSLog(@"ERROR SoundManager: Could not play music for key '%d'", error);
        return;
    }        

    // Set the number of times this music should repeat.  -1 means never stop until its asked to stop
    [backgroundMusicPlayer setNumberOfLoops:theTimesToRepeat];

    // Set the volume of the music
    [backgroundMusicPlayer setVolume:backgroundMusicVolume];

    // Play the music
    [backgroundMusicPlayer play];

    isMusicOn = YES;
}

-(BOOL)isMusicOn 
{ 
    return isMusicOn; 
} 

-(void)setMusicOn:(BOOL)boolValue 
{ 
    isMusicOn = boolValue; 
} 


/*
 Stop playing the currently playing music
 */
- (void) stopPlayingMusic{
    [backgroundMusicPlayer stop];
}

- (void) pausePlayingMusic{
    [backgroundMusicPlayer pause];
    isMusicOn = NO;
}

- (void) resumePlayingMusic{
    [backgroundMusicPlayer play];
    isMusicOn = YES;
}


//In game.m 


-(IBAction)MUSIC{

    BOOL buttonstate = [SingletonSoundManager isMusicOn]; // "SingletonSoundManager may not respond to isMusicOn" 
                                                                                       //" Initialization makes integer from pointer without a cast"

    [SingletonSoundManager setMusicOn:!buttonstate]; //"SingletonSoundManager may not respond to setMusicOn"

    if(music2.hidden){
        NSLog(@"Music Stops");
        music1.hidden = buttonstate;
        music2.hidden = !buttonstate;

        [sharedSoundManager pausePlayingMusic]; //"SingletonSoundManager may not respond to pausePlayingMusic"

    }
    else if(music1.hidden){
        NSLog(@"Music Starts");
        music1.hidden = !buttonstate;
        music2.hidden = buttonstate;

        [sharedSoundManager resumePlayingMusic]; //"SingletonSoundManager may not respond to resumePlayingMusic"
    }
}


in mainmenu.m
-(IBAction)MUSICswap{

    BOOL buttonstate = [SingletonSoundManager isMusicOn]; // "SingletonSoundManager may not respond to isMusicOn" 
                                                                                       //" Initialization makes integer from pointer without a cast"

    [SingletonSoundManager setMusicOn:!buttonstate]; //"SingletonSoundManager may not respond to setMusicOn"

    if(music2.hidden){
        NSLog(@"Music Stops");
        //music1.hidden = YES;
        //music2.hidden = NO;
        music1.hidden = buttonstate;
        music2.hidden = !buttonstate;
        [sharedSoundManager pausePlayingMusic]; //"SingletonSoundManager may not respond to pausePlayingMusic"
    }
    else if(music1.hidden){
        NSLog(@"Music Starts");
        //music1.hidden = NO;
        //music2.hidden = YES;
        music1.hidden = !buttonstate;
        music2.hidden = buttonstate;
        [sharedSoundManager resumePlayingMusic]; //"SingletonSoundManager may not respond to resumePlayingMusic"
    }
}
-(IBAction)SFX{

//ChangeSFX = [[MainMenu alloc] initWithNibName:@"MainMenu" bundle: nil];
//get object of singletonClass for flag access
BOOL buttonState = [singletonClassObject isMusicOn];
[singletonClassObject setMusicOn:!buttonState];
if(sfx2.hidden){
    NSLog(@"SFX Stops");
    sfx1.hidden = buttonState;
    sfx2.hidden = !buttonState;
    [sharedSoundManager stopSoundWithKey:@"movingbricks"];

}
else if(sfx1.hidden){
    NSLog(@"SFX Starts");
    sfx1.hidden = !buttonState;
    sfx2.hidden = buttonState;
    [sharedSoundManager playSoundWithKey:@"movingbricks" gain:0.13f pitch:1.0f shouldLoop:NO];

}
BOOL isMusicOn;
+(BOOL) isMusicOn;
+(void) setMusicOn:(BOOL)boolValue;
+(BOOL)isMusicOn
{
 return isMusicOn;
}

+(void)setMusicOn:(BOOL)boolValue
{
 isMusicOn = boolValue;
}