iOS音响系统。开始&停止还是刚开始?

时间:2015-04-30 12:08:25

标签: ios audio audiounit recording audiosession

我有一个应用程序,其中录音是主要和最重要的部分。但是,用户可以切换到表视图控制器,其中显示所有记录并且不执行记录。

问题是什么方法更好:“开始和停止音频系统或只是启动它”。看起来很明显,第一个更正确,比如“在需要时分配,在使用时解除分配”。我将展示我对这个问题的看法,我希望能够通过技术人员的争论获得批准或反对。

当我第一次构建AudioController.m时,我实现了打开/关闭音频会话和启动/停止音频单元的方法。我想在录制无效时停止音频系统。我使用了以下代码:

- (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;
}

- (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;
}

我发现这种方法存在问题,原因如下:

  1. 音频系统以延迟启动。这意味着,recording_callback()没有被调用一段时间。我怀疑它是AudioOutputUnitStart,它负责这一点。我试图用这个函数调用注释掉该行并将其移动到初始化。延迟消失了。
  2. 如果用户非常快速地在录制视图和桌面视图之间切换(音频系统的启动和停止也非常快),则会导致媒体服务的死亡(我知道观察AVAudioSessionMediaServicesWereResetNotification可以在这里提供帮助,但它不是点)。
  3. 为了解决这些问题我用其他方法修改了AudioController.m,我设法发现:当应用程序变为活动状态时启动音频系统并且在应用程序终止之前不停止它在这种情况下有还有几个问题:

    1. CPU使用率
    2. 如果音频类别设置为仅录制,则当用户浏览表格视图控制器时,无法播放其他音频。
    3. 第一个令人惊讶的是,如果在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;
      }
      

      通过在真实设备上执行此CPU使用率等于0%,此时不需要记录且不执行任何其他操作。

      第二个问题可以通过将音频类别切换到RecordAndPlay并启用混音或只是忽略问题来解决。例如,在我的情况下,应用程序需要外部设备使用迷你插孔,因此不能并行使用耳机。

      尽管如此,第一种方法更接近我,因为我喜欢在不再需要时关闭/清理每个流/资源。我想确定除了启动音频系统之外别无其他选择。请确保我不是唯一一个来到这个解决方案的人,这是正确的。

1 个答案:

答案 0 :(得分:1)

解决此问题的关键是要注意音频系统实际上在另一个(实时)线程中运行。当你(或应用程序的主要UI线程)"不需要它时,你无法真正停止和解除分配在另一个线程中运行的东西,但必须延迟为了让其他线程意识到它需要做某事然后完成并清理自己。对于音频,这可能需要多达100毫秒的时间。

鉴于此,战略2(刚开始)更安全,更现实。

或者在尝试停止音频之前设置许多秒不使用的延迟,并在尝试重新启动之前可能再延迟一段时间。