【问题标题】:AVAudioPlayer and Random SlownessAVAudioPlayer 和随机缓慢
【发布时间】:2013-04-27 21:07:09
【问题描述】:

这个问题可能太模糊,无法通过StackOverflow's 标准,但我必须尝试发布它,因为我已经没有选项了...:/

长话短说:我有一个应用程序会经历随机的缓慢时间段。它不会经常发生(可能每月一次),但是当它完全重新启动它运行的 iDevice 时会有所帮助。症状是: 2-3 秒的响应时间和缓慢、断断续续的动画;整个应用程序基本上变得无法使用。

我已经通过所有可能的诊断工具运行了该应用程序,但都没有发现任何问题;没有内存泄漏或异常高的 CPU 使用率。但是,这并不奇怪,因为该应用程序非常简单,是一款纸牌游戏的追踪器应用程序。

所有这一切让我相信,当用户点击 button 时,我用来播放声音的 AVAudioPlayer 可能是问题的原因(它是整个应用程序中唯一的、相对复杂度较高的元素)。但是,我不确定,这是我需要帮助的地方。我在这里包含了一个示例代码,也许有iOS 音频播放经验的人可以看看它,看看是否有我忽略的错误。

我们开始: 首先,我初始化了一个“静音播放器”,它每秒播放一首静音曲目,以保持AVAudioPlayer 活着。这是必要的,因为AVAudioPlayer 在长时间不活动后被调用时会经历相对较长的响应时间。

NSString *silenceFilePath = [[NSBundle mainBundle] pathForResource: @"silence" ofType: @"wav"];
NSURL *silenceFileURL = [[NSURL alloc] initFileURLWithPath: silenceFilePath];
silencePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: silenceFileURL error: nil];
[silencePlayer setDelegate: self];
[silencePlayer prepareToPlay];

silenceTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f
                                                target:self
                                              selector:@selector(repeatSilence)
                                              userInfo:nil
                                               repeats:YES];

NSTimer 调用如下方法:

- (void) repeatSilence
{
    if (isAudioON == YES)
    {
        if (silencePlayer.playing)
        {
            [silencePlayer stop];
            silencePlayer.currentTime = 0;
            [silencePlayer play];
        }
        else
        {
            [silencePlayer play];
        }
    }
}

其余的相当简单。我启动另一个 AVAudioPlayer 来播放特定的按钮声音(其中有两个):

NSString *buttonSoundFilePath = [[NSBundle mainBundle] pathForResource: @"button_pushed" ofType: @"wav"];
NSURL *buttonFileURL = [[NSURL alloc] initFileURLWithPath: buttonSoundFilePath];
buttonPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: buttonFileURL error: nil];
[buttonPlayer setDelegate: self];
[buttonPlayer prepareToPlay];

当按下按钮时,我会播放声音(与静音播放器的播放方式相同):

if (isAudioON == YES)
{
    if (buttonPlayer.playing)
    {
        [buttonPlayer stop];
         buttonPlayer.currentTime = 0;
        [buttonPlayer play];
    }
    else
    {
        [buttonPlayer play];
    }
}

真的是它的全部。然而,我担心,尽管这种方法很简单,但不知何故,这种连续的音频播放会造成 iOS 发疯的罕见实例。但这一切都只是一个理论,这就是为什么我需要有更多经验的人来看看我的代码并分享他的意见。

谢谢!

更新: 我发现另外几行代码也可能与问题相关。有了它们,我将 AVAudioPlayer 设置为与后台播放的音乐同时工作(例如来自另一个应用程序):

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] setDelegate:self];

【问题讨论】:

    标签: iphone ios objective-c audio avaudioplayer


    【解决方案1】:

    根据我从您的问题中收集到的信息,您想要播放的唯一声音似乎是相对简单且简短的音效。如果确实如此,我不会使用 AVAudioPlayer,因为它是一个资源密集型库,用于像以固定音量播放音效这样简单的事情。我会直接使用音频服务。这是我从其他地方提取的声音效果类的示例(不记得在哪里了)。

    SoundEffect.h

    #import <Foundation/Foundation.h>
    #import <AudioToolbox/AudioToolbox.h>
    
    @interface SoundEffect : NSObject
    {
        SystemSoundID soundID;
    }
    
    - (id)initWithSoundNamed:(NSString *)filename;
    - (void)play;
    
    @end
    

    SoundEffect.m

    #import "SoundEffect.h"
    
    @implementation SoundEffect
    
    - (id)initWithSoundNamed:(NSString *)filename
    {
        if ((self = [super init]))
        {
            NSURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
            if (fileURL != nil)
            {
                SystemSoundID theSoundID;
                OSStatus error = AudioServicesCreateSystemSoundID((__bridge CFURLRef)fileURL, &theSoundID);
                if (error == kAudioServicesNoError)
                    soundID = theSoundID;
            }
        }
        return self;
    }
    
    - (void)dealloc
    {
        AudioServicesDisposeSystemSoundID(soundID);
    }
    
    - (void)play
    {
        AudioServicesPlaySystemSound(soundID);
    }
    
    @end
    

    一旦你有了这个类,你就可以使用以下代码轻松播放任何音效:

    SoundEffect *mySoundEffect = [[SoundEffect alloc] initWithSoundNamed:@"mySound.wav"];
    [shareSoundEffect play];
    

    为了优化,我会将你所有的音效加载到某个地方并静态存储它们,以便预加载实际的声音文件,然后在需要时调用它们的“播放”。与使用 AVAudioPlayer 相比,这样做是一种资源消耗更少的音效播放方式。谁知道呢,也许它会解决你的问题。

    预加载声音文件:

    我的意思比听起来简单。我的意思是,在程序首次加载时初始化每个 Sound Effect 对象后,您将为它们创建一个静态引用。有几种方法可以做到这一点。我通常使用 +load 方法。它看起来像这样:

    在SoundEffect类的实现中添加一个静态变量

    static SoundEffect *mySoundEffect;
    

    接下来,在 Sound Effect 类的 +load 方法中初始化该变量。

    +(void)load
    {
        mySoundEffect = [[SoundEffect alloc] initWithSoundNamed:@"mySound.wav"];
    }
    

    现在只需为音效类创建一个静态方法,如下所示:

    +(void)playMySound
    {
        [mySoundEffect play];
    }
    

    之后,您可以随时通过调用播放该声音:

    [SoundEffect playMySound];
    

    关于音量控制的说明:

    我在我的几个 IOS 应用程序中使用了这个代码,我可以确认以下几点:

    1. 设备侧面的标准音量控件可以正常工作,但前提是用户的设置指定可以使用按钮更改铃声和提醒。
    2. 无法在代码中控制音量。

    【讨论】:

    • 我一定会试试这个。但是,我注意到您的代码中有 SystemSoundID 变量。这是否意味着您的方法使用系统声音对象来播放声音?如果是这样的话,我的问题是使用系统声音时缺乏价值控制。我试过了。另外,您能否提供一个如何“预加载”声音文件的示例?我不熟悉这种做法。
    • 没错,在这种情况下,您将无法以编程方式控制音效的音量。如果您需要声音效果处于较低或较高的音量,您将需要在较低和较高音量下拥有不同版本的声音文件。如果您需要对音效的音量进行动态控制,那么不幸的是,此解决方案可能不适合您。 :(
    • 我不需要程序控制,但我确实需要用户能够使用标准音量控制来静音或调高按钮声音。如果您的解决方案对设备侧面的音量按钮没有响应,那么这对我来说确实可能不是一个合适的答案。
    • 太棒了!我很困惑,因为我之前尝试过的系统声音音量方法只能在选项菜单中设置(当您设置警报声音等时)。不用说,这对我不起作用。不管怎样,这个周末我一定会试试你的代码并分享结果。但我可以告诉你,它已经看起来很有希望了!感谢您的帮助。
    • 我终于有机会尝试你的方法,它对音量控制没有反应。音量是 0 还是最大都没有关系,您的方法播放的声音始终是相同的音量。就像系统声音...
    【解决方案2】:

    我从上面的代码中观察到,我建议进行两个更改。

    1) 您使用了两个 AVAudioPlayer 实例,一个用于每秒连续播放无声的声音。 AVAudioPlayer 的第二个实例用于在单击按钮时播放声音。同时运行两个 AVAudioPlayer 实例,因为在设备内存不足且需要同时播放静音和按钮声音的某个时间点,设备会运行缓慢。最好使用一个 AVAudioPlayer 实例来播放两种声音。

    2) 继续播放静音,直到用户单击按钮。当用户单击按钮时,暂停计时器并停止当前声音并使用相同的 AVAudioPlayer 实例播放按钮声音。这样,当需要播放按钮声音时,大多数不推荐的计时器将停止一段时间,当按钮声音播放完毕时,恢复计时器以播放与 AVAudioPlayer 实例相同的静音声音。这将减轻内存和处理的大量负载。

    【讨论】:

    • 很好的见解。这个周末我会试一试并分享结果!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-23
    • 2020-10-08
    • 2016-03-25
    • 2014-09-27
    • 2023-04-01
    • 2010-10-28
    • 2021-10-09
    相关资源
    最近更新 更多