简体   繁体   English

iOS音频系统。 开始和停止还是只是开始?

[英]iOS audio system. Start & stop or just start?

I have an app, where audio recording is the main and the most important part. 我有一个应用程序,其中录音是主要和最重要的部分。 However user can switch to table view controller where all records are displayed and no recording is performed. 但是,用户可以切换到显示所有记录且不执行任何记录的表视图控制器。

The question is what approach is better: "start & stop audio system or just start it". 问题是哪种方法更好:“启动或停止音频系统或直接启动”。 It may seem obvious that the first one is more correct, like "allocate when you need it, deallocate when used it". 第一个似乎更正确,例如“在需要时分配,在使用时分配”。 I will show my thoughts on this question and I hope to find approval or disapproval with arguments among skilled people. 我将展示我对这个问题的想法,并希望得到熟练技术人员的支持或反对。

When I constructed AudioController.m the first time I implemented methods to open/close audio session and to start/stop audio unit. 当我第一次构造AudioController.m时,我实现了打开/关闭音频会话以及启动/停止音频单元的方法。 I wanted to stop audio system when recording is not active. 我想在录音未激活时停止音频系统。 I used the following code: 我使用以下代码:

- (BOOL)startAudioSystem {

    // open audio session
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    NSError *err = nil;
    if (![audioSession setActive:YES error:&err] ) {
        NSLog(@"Couldn't activate audio session: %@", err);
    }
    // start audio unit
    OSStatus status;
    status = AudioOutputUnitStart([self audioUnit]);
    BOOL noErrors = err == nil && status == noErr;    
    return noErrors;
}

and

- (BOOL)stopAudioSystem {
    // stop audio unit
    BOOL result;
    result = AudioOutputUnitStop([self audioUnit]) == noErr;
    HANDLE_RESULT(result);
    // close audio session
    NSError *err;
    HANDLE_RESULT([[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&err]);
    HANDLE_ERROR(err);
    BOOL noErrors = err == nil && result;
    return noErrors;
}

I found this approach problematic because of the following reasons: 我发现此方法存在问题,原因如下:

  1. Audio system starts with delay. 音频系统延迟启动。 That means, recording_callback() not called for some time. 这意味着recording_callback()已有一段时间没有被调用。 I suspect it is AudioOutputUnitStart, which is responsible for that. 我怀疑是AudioOutputUnitStart对此负责。 I tried to comment out the line with this function call and move it to initialization. 我试图用此函数调用注释掉该行,然后将其移至初始化。 the delay was gone. 延迟消失了。
  2. If user performs switching between recording view and table view very very fast (audio system's starts and stops are very fast too), it cause the death of media service (I know that observing AVAudioSessionMediaServicesWereResetNotification could help here, but it is not the point). 如果用户非常快地在记录视图和表视图之间进行切换(音频系统的启动和停止也非常快),则会导致媒体服务中断(我知道观察AVAudioSessionMediaServicesWereResetNotification可能会有所帮助,但这不是重点)。

To resolve these issues I modified AudioController.m with other approach which I managed to discover: start audio system when application becomes active and do not stop it before the app is terminated In this case there are also several issues: 为了解决这些问题,我用设法发现的其他方法修改了AudioController.m: 在应用程序处于活动状态时启动音频系统,并且在应用程序终止前不停止音频系统。在这种情况下,还存在以下问题:

  1. CPU usage CPU使用率
  2. If audio category is set to recording only, then no other audio could be played when user explores table view controller. 如果将音频类别设置为仅录制,则当用户浏览表视图控制器时无法播放其他音频。

The first one surprisingly is not a big deal, if cancel any kind of processing in recording_callback() like this: 令人惊讶的是第一个并没有什么大不了的,如果取消了recording_callback()中的任何处理,如下所示:

    static OSStatus recordingCallback(void *inRefCon,
                                  AudioUnitRenderActionFlags *ioActionFlags,
                                  const AudioTimeStamp *inTimeStamp,
                                  UInt32 inBusNumber,
                                  UInt32 inNumberFrames,
                                  AudioBufferList *ioData) {

    AudioController *input = (__bridge AudioController*)inRefCon;

    if(!input->shouldPerformProcessing)
        return noErr;

    // processing
    // ...
    //

    return noErr;
}

By doing this CPU usage equals to 0% on real device, when no recording is needed and no other actions are performed. 通过这样做,在不需要记录且不执行其他任何操作的情况下,实际设备上的CPU使用率等于0%。

And the second issue can be solved by switching audio category to RecordAndPlay and enable mixing or just ignore the problem. 第二个问题可以通过将音频类别切换到RecordAndPlay并启用混音来解决,或者只是忽略该问题即可解决。 For example in my case app requires mini Jack to be used by external device, so no headphones can be used in parallel. 例如,在我的案例中,应用程序要求外接设备使用迷你插孔,因此不能并行使用任何耳机。

Despite all this, the first approach is more close to me since I like to close/clean every stream/resource when it is no longer needed. 尽管如此,第一种方法更接近我,因为我喜欢在不再需要时关闭/清理每个流/资源。 And I want to be sure that there is indeed no other option than just start audio system. 而且我想确保确实除了启动音频系统之外别无选择。 Please make me sure that I'm not the only one who came to this solution and it is the correct one. 请确保我不是唯一提出此解决方案的人,而且它是正确的。

The key to solving this problem is to note that the audio system actually runs in another (real-time) thread. 解决此问题的关键是注意音频系统实际上在另一个(实时)线程中运行。 And you can't really stop and deallocate something running in another thread exactly when you (or the app's main UI thread) "don't need it", but have to delay in order to allow the other thread to realize it needs to do something and then finish and clean up itself. 而且,当您(或应用程序的主UI线程)“不需要”时,您不能真正真正地停止并重新分配在其他线程中运行的某些东西,但必须延迟才能让另一个线程意识到它需要做然后完成并清理自身。 This can potentially take up to many 100's of milliseconds for audio. 这可能会花费多达100毫秒的音频时间。

Given that, strategy 2 (just start) is safer and more realistic. 鉴于此,策略2(刚刚开始)更安全,更现实。

Or perhaps set a delay of many many seconds of non-use before attempting to stop audio, and possibly another short delay after that before attempting any restart. 或者,在尝试停止音频之前将不使用的延迟时间设置为许多秒,然后在尝试重新启动之前将其设置为另一短延迟时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM