2017-09-12 5 views
0

내 앱이 조회 테이블에서 오디오를 합성합니다. 오디오를 성공적으로 재생하지만 재생을 중지하려고 할 때 충돌이 발생합니다. 오디오 재생은 다시 시작하지 않고 종료해야하므로 중단을 처리하기위한 요구 사항이 기본입니다. Responding to Interruptions 섹션을 포함하여 Apple의 오디오 세션 프로그래밍 가이드를 다시 읽었습니다. 그러나 방법을 handleAudioSessionInterruption 분명히 뭔가를 놓치고있어 그래서 인터럽트를 등록하지 않는 것.왜이 오디오 세션이 중단을 인식하지 못합니까?


편집 내 대답을 참조하십시오. 내가 이것에 관해 작업을 시작했을 때 나는 개선에 대한 어떤 제안도 환영하기 때문에 NSNotificationCenter에 대해서는 아무것도 알지 못했다.


두 가지 방법으로 포어 그라운드에서 재생할 오디오 세션을 설정할 수 있습니다.

- (void)setUpAudio 
{ 
    if (_playQueue == NULL) 
    { 
     if ([self setUpAudioSession] == TRUE) 
     { 
      [self setUpPlayQueue]; 
      [self setUpPlayQueueBuffers]; 
     } 
    } 
} 


- (BOOL)setUpAudioSession 
{ 
    BOOL success      = NO; 
    NSError *audioSessionError   = nil; 

    AVAudioSession *session    = [AVAudioSession sharedInstance]; 

    // Set up notifications 

    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(handleAudioSessionInterruption:) 
               name:AVAudioSessionInterruptionNotification 
               object:session]; 

    // Set category 

    success        = [session setCategory:AVAudioSessionCategoryPlayback 
                 error:&audioSessionError]; 
    if (!success) 
    { 
     NSLog(@"%@ Error setting category: %@", 
       NSStringFromSelector(_cmd), [audioSessionError localizedDescription]); 

     // Exit early 
     return success; 
    } 

    // Set mode 

    success        = [session setMode:AVAudioSessionModeDefault 
                error:&audioSessionError]; 
    if (!success) 
    { 
     NSLog(@"%@ Error setting mode: %@", 
       NSStringFromSelector(_cmd), [audioSessionError localizedDescription]); 

     // Exit early 
     return success; 
    } 

    // Set some preferred values 

    NSTimeInterval bufferDuration  = .005; // I would prefer a 5ms buffer duration 

    success        = [session setPreferredIOBufferDuration:bufferDuration 
                      error:&audioSessionError]; 
    if (audioSessionError) 
    { 
     NSLog(@"Error %ld, %@ %i", (long)audioSessionError.code, audioSessionError.localizedDescription, success); 
    } 

    double sampleRate     = _audioFormat.mSampleRate; // I would prefer a sample rate of 44.1kHz 

    success        = [session setPreferredSampleRate:sampleRate 
                    error:&audioSessionError]; 
    if (audioSessionError) 
    { 
     NSLog(@"Error %ld, %@ %i", (long)audioSessionError.code, audioSessionError.localizedDescription, success); 
    } 

    success        = [session setActive:YES 
                 error:&audioSessionError]; 
    if (!success) 
    { 
     NSLog(@"%@ Error activating %@", 
       NSStringFromSelector(_cmd), [audioSessionError localizedDescription]); 
    } 

    // Get current values 

    sampleRate       = session.sampleRate; 
    bufferDuration      = session.IOBufferDuration; 
    NSLog(@"Sample Rate:%0.0fHz I/O Buffer Duration:%f", sampleRate, bufferDuration); 

    return success; 
} 

그리고 여기에 중단 단추를 눌렀을 때 중단을 처리하는 방법이 있습니다. 그러나 그것은 응답하지 않습니다.


편집 올바른 방법은 block,하지 selector. 내 대답을 참조해야합니다.


- (void)handleAudioSessionInterruption:(NSNotification*)notification 
{ 
    if (_playQueue) 
    { 
     NSNumber *interruptionType  = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey]; 
     NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey]; 

     NSLog(@"in-app Audio playback will be stopped by %@ %lu", notification.name, (unsigned long)interruptionType.unsignedIntegerValue); 

     switch (interruptionType.unsignedIntegerValue) 
     { 
      case AVAudioSessionInterruptionTypeBegan: 
      { 
       if (interruptionOption.unsignedIntegerValue == AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation) 
       { 
        NSLog(@"notify other apps that audio is now available"); 
       } 
      } 
       break; 

      default: 
       break; 
     } 
    } 
} 

답변

0

대답AudioSessionInterruption가 에 제대로 observer 가입하지 않았다 처리하는 내 방법. 이는이 솔루션은 처음 Matthias Hollejmans에 의해 직접 음성 합성 개발 목적으로 오디오 플레이어에 매우 적합 AudioBufferPlayer,AVAudioSession delegate 방법을 사용되지 않는 대체합니다하지 selector.

block,를 사용하여 observer을 추가하여 수정되었습니다. InterruptionListenerCallback을 비롯한 일부 비추천 기능이 나중에 Mario Diana으로 업그레이드되었습니다. 해결책 (아래)은 NSNotification을 사용하여 사용자가 AVAudioSession 버튼을 눌러 정상적으로 종료 할 수 있도록합니다.

다음은 관련 코드입니다.

PlayViewController.m

UIButton 조치는 timer을 무효화 synth의 순서대로 종료를 수행하고 AVAudioSession은 하나의 중단 알림 신청, 포함 초기화 중 AVAudioSession

- (void)fromEscButton:(UIButton*)button 
{ 
    [self stopConcertClock]; 

    ...       // code for Exit PlayViewController not shown 
} 

- (void)stopConcertClock 
{ 
    [_synthLock lock]; 
    [_synth stopAllNotes]; 
    [_synthLock unlock]; 
    [timer invalidate]; 
    timer = nil; 
    [self postAVAudioSessionInterruptionNotification]; 
    NSLog(@"Esc button pressed or sequence ended. Exit PlayViewController "); 
} 


- (void) postAVAudioSessionInterruptionNotification 
{ 
    [[NSNotificationCenter defaultCenter] 
    postNotificationName:@"AVAudioSessionInterruptionNotification" 
    object:self]; 
} 

를 종료합니다 통지를 게시 시작 전 startAudioPlayerAudioBufferPlayer

- (id)init 
{ 
    if (self = [super init]) 
    { 
     NSLog(@"PlayViewController starts MotionListener and AudioSession"); 

     [self startAudioSession]; 
    } 
    return self; 
} 


- (void)startAudioSession 
    { 
    // Synth and the AudioBufferPlayer must use the same sample rate. 

    _synthLock = [[NSLock alloc] init]; 

    float sampleRate = 44100.0f; 

    // Initialise synth to fill the audio buffer with audio samples. 

    _synth = [[Synth alloc] initWithSampleRate:sampleRate]; 

    // Initialise the audio buffer. 

    _player = [[AudioBufferPlayer alloc] initWithSampleRate:sampleRate 
                channels:1 
              bitsPerChannel:16 
              packetsPerBuffer:1024]; 
    _player.gain = 0.9f; 

    __block __weak PlayViewController *weakSelf = self; 


    _player.block = ^(AudioQueueBufferRef buffer, AudioStreamBasicDescription audioFormat) 
{ 
    PlayViewController *blockSelf = weakSelf; 

    if (blockSelf != nil) 
    { 
     // Lock access to the synth. This callback runs on an internal Audio Queue thread and we don't 
     // want another thread to change the Synth's state while we're still filling up the audio buffer. 

    [blockSelf -> _synthLock lock]; 

     // Calculate how many packets fit into this buffer. Remember that a packet equals one frame 
     // because we are dealing with uncompressed audio; a frame is a set of left+right samples 
     // for stereo sound, or a single sample for mono sound. Each sample consists of one or more 
     // bytes. So for 16-bit mono sound, each packet is 2 bytes. For stereo it would be 4 bytes. 

    int packetsPerBuffer = buffer -> mAudioDataBytesCapacity/audioFormat.mBytesPerPacket; 

     // Let the Synth write into the buffer. The Synth just knows how to fill up buffers 
     // in a particular format and does not care where they come from. 

    int packetsWritten    = [blockSelf -> _synth fillBuffer:buffer->mAudioData frames:packetsPerBuffer]; 

     // We have to tell the buffer how many bytes we wrote into it. 

    buffer -> mAudioDataByteSize = packetsWritten * audioFormat.mBytesPerPacket; 

    [blockSelf -> _synthLock unlock]; 
    } 
}; 

// Set up notifications 

    [self subscribeForBlockNotification]; 

    [_player startAudioPlayer]; 
} 


- (void)subscribeForBlockNotification 
{ 
    NSNotificationCenter * __weak center = [NSNotificationCenter defaultCenter]; 

    id __block token = [center addObserverForName:@"AVAudioSessionInterruptionNotification" 

             object:nil 
             queue:[NSOperationQueue mainQueue] 
            usingBlock:^(NSNotification *note) { 
             NSLog(@"Received the notification!"); 
             [_player stopAudioPlayer]; 
             [center removeObserver:token]; 
            }]; 
} 

PlayViewController.시간

이는 관련 인터페이스 설정

@interface PlayViewController : UIViewController <EscButtonDelegate> 

{  
    ... 

    // Initialisation of audio player and synth 

    AudioBufferPlayer* player; 
    Synth*    synth; 
    NSLock*   synthLock; 
} 

    ... 

    - (AudioBufferPlayer*)player; 
    - (Synth*)synth; 

@end 

AudioBufferPlayer.m

- (void)stopAudioPlayer 
{ 
    [self stopPlayQueue]; 
    [self tearDownPlayQueue]; 
    [self tearDownAudioSession]; 
} 


- (void)stopPlayQueue 
{ 
    if (_audioPlaybackQueue != NULL) 
    { 
     AudioQueuePause(_audioPlaybackQueue); 
     AudioQueueReset(_audioPlaybackQueue); 
     _playing = NO; 
    } 
} 


- (void)tearDownPlayQueue 
{ 
    AudioQueueDispose(_audioPlaybackQueue, NO); 
    _audioPlaybackQueue = NULL; 
} 


- (BOOL)tearDownAudioSession 
{ 
    NSError *deactivationError = nil; 
    BOOL success = [[AVAudioSession sharedInstance] setActive:NO 
                withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation 
                 error:nil]; 
    if (!success) 
    { 
     NSLog(@"%s AVAudioSession Error: %@", __FUNCTION__, deactivationError); 
    } 
    return success; 
}