AVAudioRecorder Losing Last Seconds

2019-08-20 08:31发布

问题:

I'm using AVAudioRecorder to record an audio file of varying length with the following configuration:

- (AVAudioRecorder*)recorder
{
    if(!_recorder) {
        // Set the audio file
        NSArray *pathComponents = [NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject], @"ExamTranscript.m4a", nil];
        self.soundFileURL = [NSURL fileURLWithPathComponents:pathComponents];

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

        [recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
        [recordSetting setValue:[NSNumber numberWithFloat:16000.0] forKey:AVSampleRateKey];
        [recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];

        // Initiate and prepare the recorder
        _recorder = [[AVAudioRecorder alloc] initWithURL:self.soundFileURL settings:recordSetting error:NULL];
        _recorder.meteringEnabled = YES;
        _recorder.delegate = self;
    }

    return _recorder;
}

When I call [self.recorder stop] the last 2-3 seconds of the recording are never captured (and they are definitely needed.)

For example, if I call self.recorder.currentTime right before I call [self.recorder stop] I will get a NSTimeInterval greater than the actual duration of the generated file by about 2-3 seconds. Anything recorded in that 2-3 seconds is then lost.

After calling [self.recorder stop] I get the appropriate audioRecorderDidFinishRecording:successfully: delegate method called and the flag indicates it is successful.

I'm at a total loss as to how these last seconds are being lost. (The recorder is not being released and there is still a strong reference to it.)

UPDATE

The app prompts the user to speak multiple times, so the AVAudioRecorder is paused and resumed multiple times before being stopped at the end of the final response. It is paused instead of stopped because it all needs to be recorded to a single audio file.

回答1:

By looking at limited code you shared, I can only suspect that you might be using the recorder object improperly.

Key points below and follow up code that might help you.. (it works for me)

1) You should create/init AVAudioRecorder every time you begin a new recording.

2) currentTime property of AVAudioRecorder is only valid when recording is in progress (i.e. isRecording is YES)

// class properties
@property (nonatomic, retain) AVAudioRecorder *audioRecorder;
@property (nonatomic, retain) NSDictionary *recordQualitySettings;
@property (nonatomic, assign) NSInteger maxRecordDurationInSeconds;

// class's designated initializer
- (instancetype)init
{
    self = [super init];

    if (self) {
        self.recordQualitySettings = [NSDictionary
                               dictionaryWithObjectsAndKeys:
                               [NSNumber numberWithInt:kAudioFormatAppleIMA4],
                               AVFormatIDKey,
                               [NSNumber numberWithInt:1],
                               AVNumberOfChannelsKey,
                               [NSNumber numberWithFloat:16000.0],  //??
                               AVSampleRateKey,
                               nil];
...
...
    }

    return self;
}

- (void)recordBegin
{
    NSString *temnpVoiceFile = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject], @"tempVoice.caf", nil]];
    NSURL *soundFileURL = [NSURL fileURLWithPath:temnpVoiceFile];
    NSError *error = nil;

    // Instantiate an audio recorder.
    self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:soundFileURL settings:self.recordQualitySettings error:&error];
    NSAssert1(!error, @"error creating audio file = %@", error);

    self.audioRecorder.delegate = self;
    self.audioRecorder.meteringEnabled = YES;

    [self.audioRecorder recordForDuration:self.maxRecordDurationInSeconds];   // Any of the 'record..' methods that triggers recording.

}

EDIT:

After your call stop recording using [self.audioRecorder stop] or inside audioRecorderDidFinishRecording delegate method, please check audio duration using the below code (url based)? Just to cross verify that the currentTime is not correct.

AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:self.audioRecorder.url options:nil];   // delegate method provide you `recorder` object as parameter too.
CMTime audioDuration = audioAsset.duration;
double duration = CMTimeGetSeconds(audioDuration);