2012-11-21 31 views
1

这是我现在使用的代码,它不工作(当我按下调用此方法的按钮时,没有任何反应)。以前,我有一个audioPlayer的属性,它工作(显然,下面的所有audioPlayer都是self.audioPlayer)。问题是,当我试图播放两次声音时,它会结束第一声播放。AVAudioPlayer - 你必须创建一个属性才能工作吗? (Xcode)

这是没有用的,因为我在制作音板并希望声音能够重叠。我想我可以让audioPlayer变成一个局部变量而不是属性,一切都会好的,但是现在声音根本不起作用,我不知道为什么。在我为AVAudioPlayer找到的所有教程中,都有一个属性,但没有人解释为什么。如果这不起作用,我还有什么替代方法可以使声音重叠?

- (void)loadSound:(NSString *)sound ofType:(NSString *)type withDelegate:(BOOL)delegate { 
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] 
            pathForResource:sound 
            ofType:type]]; 

AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; 
if (delegate) audioPlayer.delegate = self; 
[audioPlayer prepareToPlay]; 

[audioPlayer play]; 
} 

回答

7

你需要一个属性或伊娃的原因是它提供了强有力的参考。当使用ARC时,任何没有强指针的对象都是公平的游戏,而且事实上这就是你所看到的。

你也是正确的,一个AVAudioPlayer强指针将只允许一次引用一个音频播放器。

如果您选择继续使用AVAudioPlayer的解决方案是使用某种集合对象来保存所有播放器实例的强引用。你可以使用一个NSMutableArray如下所示:

编辑我调整了代码稍微使播放的声音方法需要一个NSStringsoundName参数。

@synthesize audioPlayers = _audioPlayers; 
-(NSMutableArray *)audioPlayers{ 
    if (!_audioPlayers){ 
     _audioPlayers = [[NSMutableArray alloc] init]; 
    } 
    return _audioPlayers; 
} 
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ 
    [self.audioPlayers removeObject:player]; 
} 
-(void)playSoundNamed:(NSString *)soundName{ 
    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] 
             pathForResource:soundName 
             ofType:@"wav"]]; 
    AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; 
    if (audioPlayer){ 
     [audioPlayer setDelegate:self]; 
     [audioPlayer prepareToPlay]; 
     [audioPlayer play]; 
     [self.audioPlayers addObject:audioPlayer]; 
    } 
} 

通常的AVAudioPlayer是矫枉过正一个声音效果/音板应用。为了快速声“滴”,你可能会发现音频工具箱框架,outlined in my answer to this question.

From looking at the System Sound class reference, it seems like you can only play one sound at a time.

它只能起到一个SystemSoundID在同一时间。例如,如果你有soundOne和soundTwo。当soundTwo正在播放时,您可以播放soundOne,但一次不能播放一种声音的多个实例。

What's the best way to be able to play sounds that can overlap while still being efficient with the amount of code and memory?

最好是意见。

如果你需要两个相同声音同时播放的实例,那么我会说在这个答案中发布的代码将是代码使用。由于相同声音的每个重叠实例都需要创建新资源,因此使用audioPlayerDidFinishPlaying:这样的代码更易于管理(内存可以很容易地被回收)。

如果相同声音的重叠实例不是交易断路器,那么我认为只需使用AudioServicesCreateSystemSoundID()来创建每个声音的一个实例就更有效率。

我绝对不会试图管理每个按下按钮的创建和处理SystemSoundID s。这会匆匆出错。在这种情况下,AVAudioPlayer仅仅是可维护性的明显赢家。

+0

从查看System Sound类参考,看起来您一次只能播放一个声音。能够播放可重叠的声音的最佳方式是什么,而对代码和内存的数量仍然有效? – robertfiorentino

+0

@fiorgodx我编辑了我的答案以回应您的评论。 – NJones

+0

帮助我解决了当前正在播放的播放器的停止问题,并在启动另一个播放器时释放它(不停止播放按钮)。现在,内存使用并不是天空飞扬,它只能作为唯一一个音频播放器的例子。谢谢 –

4

我假设你正在使用ARC。音频播放器不起作用的原因是因为AVAudioPlayer对象正在释放,然后在loadSound:方法终止后随后销毁。这是由于ARC的对象管理而发生的。在ARC之前,代码:

AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; 
if (delegate) audioPlayer.delegate = self; 
[audioPlayer prepareToPlay]; 
[audioPlayer play]; 

会按预期播放声音。但是,在loadSound:方法终止后,AVAudioPlayer对象仍然存在。这意味着每次播放声音时,都会泄漏内存。

一点关于

属性被引入,以减少代码的开发人员必须编写和维护量的属性。在属性之前,开发人员必须为每个实例变量编写setters和getters。这是很多冗余代码。属性的一个幸运的副作用是它们处理了很多为基于对象的实例变量编写setter/getters所需的内存管理代码。这意味着很多开发人员开始仅使用属性,即使对于不需要公开的变量也是如此。

由于ARC为您处理所有的内存管理细节,因此属性只能用于其原始目的,从而减少冗余代码的数量。传统的iVars默认会被强烈引用,这意味着一个简单的赋值,例如: title = [NSString stringWithFormat:@"hello"]; 与代码基本相同: self.title = [NSString stringWithFormat:@"hello"];

好的,回到你的问题。

如果你打算在loadSound:方法来创建AVAudioPlayer情况下,你需要不断的强引用每个AVAudioPlayer实例或其他ARC会破坏它。我建议将新创建的AVAudioPlayer对象添加到NSMutableArray阵列中。如果采用AVAudioPlayerDelegate协议,则可以实施audioPlayerDidFinishPlaying:successfully:方法。您可以在其中删除阵列中的AVAudioPlayer对象,让ARC知道可以销毁该对象。

相关问题