4

私のアプリケーションでは、AVAudioRecorderとAVAudioPlayerを使用して音声を録音し再生するアプリケーションでは、着信時にシナリオが発生しました。録音が進行中で、通話後に録音された録音は、通話後に録音された録音が、通話前に録音された音声の継続であることが必要です。AVAudioRecorderは中断後の音声のみを記録します

私はAVAudioRecorderDelegate方法

  • を使用してオーディオレコーダーにoccuring中断(無効)audioRecorderBeginInterruption追跡:(AVAudioRecorder *)avRecorder: と
  • (無効)audioRecorderEndInterruption avRecorder(AVAudioRecorder *)を。

私のEndInterruptionメソッドでは、私はaudioSessionをアクティブにします。ここで

私は同じ問題を話す別のリンク how to resume recording after interruption occured in iphone?http://www.iphonedevsdk.com/forum/iphone-sdk-development/31268-avaudiorecorderdelegate-interruption.htmlに出くわしたこの問題の解決策を探している間、私は

- (void)startRecordingProcess 
{ 
    AVAudioSession *audioSession = [AVAudioSession sharedInstance]; 
    NSError *err = nil; 
    [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&err]; 
    if(err) 
    { 
     DEBUG_LOG(@"audioSession: %@ %d %@", [err domain], [err code], [[err userInfo] description]); 
     return; 
    } 
    [audioSession setActive:YES error:&err]; 
    err = nil; 
    if(err) 
    { 
     DEBUG_LOG(@"audioSession: %@ %d %@", [err domain], [err code], [[err userInfo] description]); 
     return; 
    } 
    // Record settings for recording the audio 
    recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys: 
        [NSNumber numberWithInt:kAudioFormatMPEG4AAC],AVFormatIDKey, 
        [NSNumber numberWithInt:44100],AVSampleRateKey, 
        [NSNumber numberWithInt: 2],AVNumberOfChannelsKey, 
        [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey, 
        [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey, 
        [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey, 
        nil]; 
    BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:recorderFilePath]; 
    if (fileExists) 
    {   
     BOOL appendingFileExists = 
      [[NSFileManager defaultManager] fileExistsAtPath:appendingFilePath]; 
     if (appendingFileExists) 
     { 
      [[NSFileManager defaultManager]removeItemAtPath:appendingFilePath error:nil]; 
     } 
     if (appendingFilePath) 
     { 
      [appendingFilePath release]; 
      appendingFilePath = nil; 
     } 
     appendingFilePath = [[NSString alloc]initWithFormat:@"%@/AppendedAudio.m4a", DOCUMENTS_FOLDER]; 
     fileUrl = [NSURL fileURLWithPath:appendingFilePath]; 
    } 
    else 
    { 
     isFirstTime = YES; 
     if (recorderFilePath) 
     { 
      DEBUG_LOG(@"Testing 2"); 
      [recorderFilePath release]; 
      recorderFilePath = nil; 
     } 
     DEBUG_LOG(@"Testing 3"); 
     recorderFilePath = [[NSString alloc]initWithFormat:@"%@/RecordedAudio.m4a", DOCUMENTS_FOLDER]; 
     fileUrl = [NSURL fileURLWithPath:recorderFilePath]; 
    } 
    err = nil; 
    recorder = [[recorder initWithURL:fileUrl settings:recordSetting error:&err]retain]; 
    if(!recorder) 
    { 
     DEBUG_LOG(@"recorder: %@ %d %@", [err domain], [err code], [[err userInfo] description]); 
     [[AlertFunctions sharedInstance] showMessageWithTitle:kAppName 
                 message:[err localizedDescription] 
                delegate:nil 
              cancelButtonTitle:@"Ok"]; 
     return; 
    } 
    //prepare to record 
    [recorder setDelegate:self]; 
    [recorder prepareToRecord]; 
    recorder.meteringEnabled = YES; 
    [recorder record]; 

} 

を使用して記録コードがあります。 私はそれらのリンクで与えられた提案を試みましたが、成功しませんでした。 AVAudioRecorder自体で動作させることを望みます。 この問題の解決方法はありますか? 貴重なご意見をいただければ幸いです。

答えて

3

いくつかの調査の後、私はアップルから、現在のAPIの問題であることを通知されました。そこで私は以前のオーディオファイルを中断した直後に保存し、それをレジュームされたオーディオファイルに加えることで、問題の回避策を見つけることができました。同じ問題に直面しているかもしれない誰かを助けることを願っています。

+0

両方のファイルにどのように参加していますか投稿してください。 –

+0

2つのファイルを適切に使用してAVCompositionを作成することができます。次のリンクには、行う方法のサンプルコードがあります。http://stackoverflow.com/questions/7775040/play-avmutablecomposition-with-avplayerその後、AVAssetExportSessionを使用してコンポジションを1つのオーディオファイルに書き出すことができます。このリンクhttp://stackoverflow.com/questions/8019033/avassetexportsession-not-working-in-ios5/9767681#9767681にアクセスしてください。これがあなたの問題を解決することを願っています。 – Siddharth

2

私はまた、AVAudioRecorderが中断後にのみ録音していたのと同様の問題に直面していました。
この問題は、録画の配列を維持し、NSTemporaryDirectoryに保存して最後に最後にマージすることで解決しました。以下は

は重要なステップです:

  1. クラスがAVAudioSessionInterruptionNotificationに耳を傾けてください。中断で
  2. 割り込みエンド(AVAudioSessionInterruptionTypeEnded)で
  3. あなたの記録を保存し、(AVAudioSessionInterruptionTypeBegan)を開始、中断オプションに新しい録音を開始AVAudioSessionInterruptionOptionShouldResume
  4. 保存ボタンを押す上のすべての記録を追加します。上記のステップのための

コードスニペットは、次のとおりです。

ここ
// 1. Make this class listen to the AVAudioSessionInterruptionNotification in viewDidLoad 
- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

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

    // other coding stuff 
} 

// observe the interruption begin/end 
- (void)handleAudioSessionInterruption:(NSNotification*)notification 
{ 
    AVAudioSessionInterruptionType interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; 
    AVAudioSessionInterruptionOptions interruptionOption = [notification.userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue]; 

    switch (interruptionType) { 
     // 2. save recording on interruption begin 
     case AVAudioSessionInterruptionTypeBegan:{ 
      // stop recording 
      // Update the UI accordingly 
      break; 
     } 
     case AVAudioSessionInterruptionTypeEnded:{ 
      if (interruptionOption == AVAudioSessionInterruptionOptionShouldResume) { 
       // create a new recording 
       // Update the UI accordingly 
      } 
      break; 
     } 

     default: 
      break; 
    } 
} 

// 4. append all recordings 
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)avrecorder successfully:(BOOL)flag 
{ 
    // append all recordings one after other 
} 

は実施例である:私が知っている

// 
// XDRecordViewController.m 
// 
// Created by S1LENT WARRIOR 
// 

#import "XDRecordViewController.h" 

@interface XDRecordViewController() 
{ 
    AVAudioRecorder *recorder; 

    __weak IBOutlet UIButton* btnRecord; 
    __weak IBOutlet UIButton* btnSave; 
    __weak IBOutlet UIButton* btnDiscard; 
    __weak IBOutlet UILabel* lblTimer; // a UILabel to display the recording time 

    // some variables to display the timer on a lblTimer 
    NSTimer* timer; 
    NSTimeInterval intervalTimeElapsed; 
    NSDate* pauseStart; 
    NSDate* previousFireDate; 
    NSDate* recordingStartDate; 

    // interruption handling variables 
    BOOL isInterrupted; 
    NSInteger preInterruptionDuration; 

    NSMutableArray* recordings; // an array of recordings to be merged in the end 
} 
@end 

@implementation XDRecordViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // Make this class listen to the AVAudioSessionInterruptionNotification 
    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(handleAudioSessionInterruption:) 
               name:AVAudioSessionInterruptionNotification 
               object:[AVAudioSession sharedInstance]]; 

    [self clearContentsOfDirectory:NSTemporaryDirectory()]; // clear contents of NSTemporaryDirectory() 

    recordings = [NSMutableArray new]; // initialize recordings 

    [self setupAudioSession]; // setup the audio session. you may customize it according to your requirements 
} 

- (void)viewDidAppear:(BOOL)animated 
{ 
    [super viewDidAppear:animated]; 

    [self initRecording]; // start recording as soon as the view appears 
} 

- (void)dealloc 
{ 
    [self clearContentsOfDirectory:NSTemporaryDirectory()]; // remove all files files from NSTemporaryDirectory 

    [[NSNotificationCenter defaultCenter] removeObserver:self]; // remove this class from NSNotificationCenter 
} 

#pragma mark - Event Listeners 

// called when recording button is tapped 
- (IBAction) btnRecordingTapped:(UIButton*)sender 
{ 
    sender.selected = !sender.selected; // toggle the button 

    if (sender.selected) { // resume recording 
     [recorder record]; 
     [self resumeTimer]; 
    } else { // pause recording 
     [recorder pause]; 
     [self pauseTimer]; 
    } 
} 

// called when save button is tapped 
- (IBAction) btnSaveTapped:(UIButton*)sender 
{ 
    [self pauseTimer]; // pause the timer 

    // disable the UI while the recording is saving so that user may not press the save, record or discard button again 
    btnSave.enabled = NO; 
    btnRecord.enabled = NO; 
    btnDiscard.enabled = NO; 

    [recorder stop]; // stop the AVAudioRecorder so that the audioRecorderDidFinishRecording delegate function may get called 

    // Deactivate the AVAudioSession 
    NSError* error; 
    [[AVAudioSession sharedInstance] setActive:NO error:&error]; 
    if (error) { 
     NSLog(@"%@", error); 
    } 
} 

// called when discard button is tapped 
- (IBAction) btnDiscardTapped:(id)sender 
{ 
    [self stopTimer]; // stop the timer 

    recorder.delegate = Nil; // set delegate to Nil so that audioRecorderDidFinishRecording delegate function may not get called 
    [recorder stop]; // stop the recorder 

    // Deactivate the AVAudioSession 
    NSError* error; 
    [[AVAudioSession sharedInstance] setActive:NO error:&error]; 
    if (error) { 
     NSLog(@"%@", error); 
    } 

    [self.navigationController popViewControllerAnimated:YES]; 
} 

#pragma mark - Notification Listeners 
// called when an AVAudioSessionInterruption occurs 
- (void)handleAudioSessionInterruption:(NSNotification*)notification 
{ 
    AVAudioSessionInterruptionType interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; 
    AVAudioSessionInterruptionOptions interruptionOption = [notification.userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue]; 

    switch (interruptionType) { 
     case AVAudioSessionInterruptionTypeBegan:{ 
      // • Recording has stopped, already inactive 
      // • Change state of UI, etc., to reflect non-recording state 
      preInterruptionDuration += recorder.currentTime; // time elapsed 
      if(btnRecord.selected) { // timer is already running 
       [self btnRecordingTapped:btnRecord]; // pause the recording and pause the timer 
      } 

      recorder.delegate = Nil; // Set delegate to nil so that audioRecorderDidFinishRecording may not get called 
      [recorder stop]; // stop recording 
      isInterrupted = YES; 
      break; 
     } 
     case AVAudioSessionInterruptionTypeEnded:{ 
      // • Make session active 
      // • Update user interface 
      // • AVAudioSessionInterruptionOptionShouldResume option 
      if (interruptionOption == AVAudioSessionInterruptionOptionShouldResume) { 
       // Here you should create a new recording 
       [self initRecording]; // create a new recording 
       [self btnRecordingTapped:btnRecord]; 
      } 
      break; 
     } 

     default: 
      break; 
    } 
} 

#pragma mark - AVAudioRecorderDelegate 
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)avrecorder successfully:(BOOL)flag 
{ 
    [self appendAudiosAtURLs:recordings completion:^(BOOL success, NSURL *outputUrl) { 
     // do whatever you want with the new audio file :) 
    }]; 
} 

#pragma mark - Timer 
- (void)timerFired:(NSTimer*)timer 
{ 
    intervalTimeElapsed++; 
    [self updateDisplay]; 
} 

// function to time string 
- (NSString*) timerStringSinceTimeInterval:(NSTimeInterval)timeInterval 
{ 
    NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval]; 
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 
    [dateFormatter setDateFormat:@"mm:ss"]; 
    [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]]; 
    return [dateFormatter stringFromDate:timerDate]; 
} 

// called when recording pauses 
- (void) pauseTimer 
{ 
    pauseStart = [NSDate dateWithTimeIntervalSinceNow:0]; 

    previousFireDate = [timer fireDate]; 

    [timer setFireDate:[NSDate distantFuture]]; 
} 

- (void) resumeTimer 
{ 
    if (!timer) { 
     timer = [NSTimer scheduledTimerWithTimeInterval:1.0 
               target:self 
               selector:@selector(timerFired:) 
               userInfo:Nil 
               repeats:YES]; 
     return; 
    } 

    float pauseTime = - 1 * [pauseStart timeIntervalSinceNow]; 

    [timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]]; 
} 

- (void)stopTimer 
{ 
    [self updateDisplay]; 
    [timer invalidate]; 
    timer = nil; 
} 

- (void)updateDisplay 
{ 
    lblTimer.text = [self timerStringSinceTimeInterval:intervalTimeElapsed]; 
} 

#pragma mark - Helper Functions 
- (void) initRecording 
{ 

    // Set the audio file 
    NSString* name = [NSString stringWithFormat:@"recording_%@.m4a", @(recordings.count)]; // creating a unique name for each audio file 
    NSURL *outputFileURL = [NSURL fileURLWithPathComponents:@[NSTemporaryDirectory(), name]]; 

    [recordings addObject:outputFileURL]; 

    // Define the recorder settings 
    NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init]; 

    [recordSetting setValue:@(kAudioFormatMPEG4AAC) forKey:AVFormatIDKey]; 
    [recordSetting setValue:@(44100.0) forKey:AVSampleRateKey]; 
    [recordSetting setValue:@(1) forKey:AVNumberOfChannelsKey]; 

    NSError* error; 
    // Initiate and prepare the recorder 
    recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:&error]; 
    recorder.delegate = self; 
    recorder.meteringEnabled = YES; 
    [recorder prepareToRecord]; 

    if (![AVAudioSession sharedInstance].inputAvailable) { // can not record audio if mic is unavailable 
     NSLog(@"Error: Audio input device not available!"); 
     return; 
    } 

    intervalTimeElapsed = 0; 
    recordingStartDate = [NSDate date]; 

    if (isInterrupted) { 
     intervalTimeElapsed = preInterruptionDuration; 
     isInterrupted = NO; 
    } 

    // Activate the AVAudioSession 
    [[AVAudioSession sharedInstance] setActive:YES error:&error]; 
    if (error) { 
     NSLog(@"%@", error); 
    } 

    recordingStartDate = [NSDate date]; // Set the recording start date 
    [self btnRecordingTapped:btnRecord]; 
} 

- (void)setupAudioSession 
{ 

    static BOOL audioSessionSetup = NO; 
    if (audioSessionSetup) { 
     return; 
    } 

    AVAudioSession* session = [AVAudioSession sharedInstance]; 

    [session setCategory:AVAudioSessionCategoryPlayAndRecord 
      withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker 
        error:Nil]; 

    [session setMode:AVAudioSessionModeSpokenAudio error:nil]; 

    audioSessionSetup = YES; 
} 

// gets an array of audios and append them to one another 
// the basic logic was derived from here: http://stackoverflow.com/a/16040992/634958 
// i modified this logic to append multiple files 
- (void) appendAudiosAtURLs:(NSMutableArray*)urls completion:(void(^)(BOOL success, NSURL* outputUrl))handler 
{ 
    // Create a new audio track we can append to 
    AVMutableComposition* composition = [AVMutableComposition composition]; 
    AVMutableCompositionTrack* appendedAudioTrack = 
    [composition addMutableTrackWithMediaType:AVMediaTypeAudio 
          preferredTrackID:kCMPersistentTrackID_Invalid]; 

    // Grab the first audio track that need to be appended 
    AVURLAsset* originalAsset = [[AVURLAsset alloc] 
           initWithURL:urls.firstObject options:nil]; 
    [urls removeObjectAtIndex:0]; 

    NSError* error = nil; 

    // Grab the first audio track and insert it into our appendedAudioTrack 
    AVAssetTrack *originalTrack = [[originalAsset tracksWithMediaType:AVMediaTypeAudio] firstObject]; 
    CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, originalAsset.duration); 
    [appendedAudioTrack insertTimeRange:timeRange 
           ofTrack:originalTrack 
           atTime:kCMTimeZero 
            error:&error]; 
    CMTime duration = originalAsset.duration; 

    if (error) { 
     if (handler) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       handler(NO, Nil); 
      }); 
     } 
    } 

    for (NSURL* audioUrl in urls) { 
     AVURLAsset* newAsset = [[AVURLAsset alloc] 
           initWithURL:audioUrl options:nil]; 

     // Grab the rest of the audio tracks and insert them at the end of each other 
     AVAssetTrack *newTrack = [[newAsset tracksWithMediaType:AVMediaTypeAudio] firstObject]; 
     timeRange = CMTimeRangeMake(kCMTimeZero, newAsset.duration); 
     [appendedAudioTrack insertTimeRange:timeRange 
            ofTrack:newTrack 
            atTime:duration 
             error:&error]; 

     duration = appendedAudioTrack.timeRange.duration; 

     if (error) { 
      if (handler) { 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        handler(NO, Nil); 
       }); 
      } 
     } 
    } 

    // Create a new audio file using the appendedAudioTrack 
    AVAssetExportSession* exportSession = [AVAssetExportSession 
              exportSessionWithAsset:composition 
              presetName:AVAssetExportPresetAppleM4A]; 
    if (!exportSession) { 
     if (handler) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       handler(NO, Nil); 
      }); 
     } 
    } 

    NSArray* appendedAudioPath = @[NSTemporaryDirectory(), @"temp.m4a"]; // name of the final audio file 
    exportSession.outputURL = [NSURL fileURLWithPathComponents:appendedAudioPath]; 
    exportSession.outputFileType = AVFileTypeAppleM4A; 
    [exportSession exportAsynchronouslyWithCompletionHandler:^{ 

     BOOL success = NO; 
     // exported successfully? 
     switch (exportSession.status) { 
      case AVAssetExportSessionStatusFailed: 
       break; 
      case AVAssetExportSessionStatusCompleted: { 
       success = YES; 

       break; 
      } 
      case AVAssetExportSessionStatusWaiting: 
       break; 
      default: 
       break; 
     } 

     if (handler) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       handler(success, exportSession.outputURL); 
      }); 
     } 
    }]; 
} 

- (void) clearContentsOfDirectory:(NSString*)directory 
{ 
    NSFileManager *fm = [NSFileManager defaultManager]; 
    NSError *error = nil; 
    for (NSString *file in [fm contentsOfDirectoryAtPath:directory error:&error]) { 
     [fm removeItemAtURL:[NSURL fileURLWithPathComponents:@[directory, file]] error:&error]; 
    } 
} 

@end 

その質問への答えが、この期待に役立ちますするには遅すぎ誰か他!

関連する問題