iosaudioaudio-recordingavaudiorecorderaiff

iOS: How to trim silence from start and end of .aif audio recording?


My app includes the ability for the user to record a brief message; I'd like to trim off any silence (or, to be more precise, any audio whose volume falls below a given threshold) from the beginning and end of the recording.

I'm recording the audio with an AVAudioRecorder, and saving it to an .aif file. I've seen some mention elsewhere of methods by which I could have it wait to start recording until the audio level reaches a threshold; that'd get me halfway there, but won't help with trimming silence off the end.

If there's a simple way to do this, I'll be eternally grateful!

Thanks.


Solution

  • This project takes audio from the microphone, triggers on loud noise and untriggers when quiet. It also trims and fades in/fades out around the ends.

    https://github.com/fulldecent/FDSoundActivatedRecorder

    Relevant code you are seeking:

    - (NSString *)recordedFilePath
    {
        // Prepare output
        NSString *trimmedAudioFileBaseName = [NSString stringWithFormat:@"recordingConverted%x.caf", arc4random()];
        NSString *trimmedAudioFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:trimmedAudioFileBaseName];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if ([fileManager fileExistsAtPath:trimmedAudioFilePath]) {
            NSError *error;
            if ([fileManager removeItemAtPath:trimmedAudioFilePath error:&error] == NO) {
                NSLog(@"removeItemAtPath %@ error:%@", trimmedAudioFilePath, error);
            }
        }
        NSLog(@"Saving to %@", trimmedAudioFilePath);
    
        AVAsset *avAsset = [AVAsset assetWithURL:self.audioRecorder.url];
        NSArray *tracks = [avAsset tracksWithMediaType:AVMediaTypeAudio];
        AVAssetTrack *track = [tracks objectAtIndex:0];
    
        AVAssetExportSession *exportSession = [AVAssetExportSession
                                               exportSessionWithAsset:avAsset
                                               presetName:AVAssetExportPresetAppleM4A];
    
        // create trim time range
        CMTime startTime = CMTimeMake(self.recordingBeginTime*SAVING_SAMPLES_PER_SECOND, SAVING_SAMPLES_PER_SECOND);
        CMTimeRange exportTimeRange = CMTimeRangeFromTimeToTime(startTime, kCMTimePositiveInfinity);
    
        // create fade in time range
        CMTime startFadeInTime = startTime;
        CMTime endFadeInTime = CMTimeMake(self.recordingBeginTime*SAVING_SAMPLES_PER_SECOND + RISE_TRIGGER_INTERVALS*INTERVAL_SECONDS*SAVING_SAMPLES_PER_SECOND, SAVING_SAMPLES_PER_SECOND);
        CMTimeRange fadeInTimeRange = CMTimeRangeFromTimeToTime(startFadeInTime, endFadeInTime);
    
        // setup audio mix
        AVMutableAudioMix *exportAudioMix = [AVMutableAudioMix audioMix];
        AVMutableAudioMixInputParameters *exportAudioMixInputParameters =
        [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:track];
    
        [exportAudioMixInputParameters setVolumeRampFromStartVolume:0.0 toEndVolume:1.0
                                                          timeRange:fadeInTimeRange];
        exportAudioMix.inputParameters = [NSArray
                                          arrayWithObject:exportAudioMixInputParameters];
    
        // configure export session  output with all our parameters
        exportSession.outputURL = [NSURL fileURLWithPath:trimmedAudioFilePath];
        exportSession.outputFileType = AVFileTypeAppleM4A;
        exportSession.timeRange = exportTimeRange;
        exportSession.audioMix = exportAudioMix;
    
        // MAKE THE EXPORT SYNCHRONOUS
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        [exportSession exportAsynchronouslyWithCompletionHandler:^{
            dispatch_semaphore_signal(semaphore);
        }];
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
        if (AVAssetExportSessionStatusCompleted == exportSession.status) {
            NSLog(@"AVAssetExportSessionStatusCompleted");
            return trimmedAudioFilePath;
        } else if (AVAssetExportSessionStatusFailed == exportSession.status) {
            // a failure may happen because of an event out of your control
            // for example, an interruption like a phone call comming in
            // make sure and handle this case appropriately
            NSLog(@"AVAssetExportSessionStatusFailed %@", exportSession.error.localizedDescription);
        } else {
            NSLog(@"Export Session Status: %d", exportSession.status);
        }
        return nil;
    }