Sam
Sam

Reputation: 1353

AVMutableCompositionTrack - Using insertEmptyTimeRange to insert silence between two WAV files

The problem I am having is getting a variable amount of silence to be placed in-between two wav files.

My approach thus far is as follows:

Firstly I create an AVMutableComposition and an AVMutableCompositionTrack

AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack* appendedAudioTrack =
[composition addMutableTrackWithMediaType:AVMediaTypeAudio
                         preferredTrackID:kCMPersistentTrackID_Invalid];

Then using AVURLAsset I allocate my conveniently named first.wav file.

AVURLAsset* firstComponent = [[AVURLAsset alloc]
                             initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle]  pathForResource: @"first" ofType: @"wav"]] options:nil];

I then insert this into the mutable composition track I named appendedAudioTrack earlier

NSArray *firstTrack = [firstComponent tracksWithMediaType:AVMediaTypeAudio];
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, firstComponent.duration);
[appendedAudioTrack insertTimeRange:timeRange
                            ofTrack:[firstTrack objectAtIndex:0]
                             atTime:kCMTimeZero
                              error:&error];

The second.wav file is inserted in the exact same way:

AVURLAsset* secondComponent = [[AVURLAsset alloc]
                              initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource: @"second" ofType: @"wav"]] options:nil];
NSArray *secondTrack = [secondComponent tracksWithMediaType:AVMediaTypeAudio];
CMTimeRange timeRange2 = CMTimeRangeMake(kCMTimeZero, secondComponent.duration);
[appendedAudioTrack insertTimeRange:timeRange2
                            ofTrack:[secondTrack objectAtIndex:0]
                             atTime:kCMTimeZero
                              error:&error];

This so far successfully joins the two wav files end to end.

I then try to insert a variable amount of silence using insertEmptyTimeRange like this:

    CMTimeRange timeRange3 = CMTimeRangeFromTimeToTime(firstComponent.duration,CMTimeAdd(firstComponent.duration,CMTimeMake(interval,1)));
[appendedAudioTrack insertEmptyTimeRange:timeRange3];

The silence duration is a float and for testing purposes is currently 0.49. Its variable name is interval and it represents the desired silence in seconds.

An assumption i've made using CMTimeRange is that AVURLAsset's duration property can be considered as the finish CMTime for the first audio track.

When I download the documents directory from organiser in Xcode, and look at the resulting m4a file in audacity the silence is in the file, but its at the start, not in the middle of both .wav files as desired.

Incorrectly it goes: SILENCE, first.wav, second.wav

I would like to know how to properly use insertEmptyTimeRange to produce first.wav, SILENCE, second.wav.

Note: I have seen this question (and others) which presents a very similar problem, however the approach they went for was to use a constant silence file. My silence is variable and determined at run time. And I am also aware that another answer provides what they say is a solution, but it has not worked for me. I have tried all the different methods I found on the internet but it seems I'm misunderstanding something as it never works correctly for me.

Just in case it matters, I export the file like so:

 // Create a new audio file using the appendedAudioTrack
AVAssetExportSession* exportSession = [AVAssetExportSession
                                       exportSessionWithAsset:composition
                                       presetName:AVAssetExportPresetAppleM4A];

if (!exportSession)
{
    // do something
    return nil;
}



//This gives me an output URL to a file name that doesn't yet exist
NSString *path2;
NSArray *paths2 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
path2 = [paths2 objectAtIndex:0];
path2 = [path2 stringByAppendingPathComponent:[string stringByAppendingString: @".m4a"]];
NSString* appendedAudioPath= path2;



exportSession.outputURL = [NSURL fileURLWithPath:appendedAudioPath];
exportSession.outputFileType = AVFileTypeAppleM4A;



[exportSession exportAsynchronouslyWithCompletionHandler:^{


    switch (exportSession.status)
    {
        case AVAssetExportSessionStatusFailed:
            NSLog(@"%@",exportSession.error);
            break;
        case AVAssetExportSessionStatusCompleted:

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

Thanks

Upvotes: 4

Views: 2004

Answers (1)

Sam
Sam

Reputation: 1353

It turns out the interval was not of the correct duration, when checked in audacity it was actually 0.049, not 0.49.

The problem I was having was with this

CMTimeMake(interval,1)

I thought this would yield interval/1 seconds but I was wrong.

Instead I used CMTimeMakeWithSeconds along with NSEC_PER_SEC like so

    CMTimeRange timeRange3 = CMTimeRangeFromTimeToTime(firstComponent.duration,CMTimeAdd(firstComponent.duration,CMTimeMakeWithSeconds(interval ,NSEC_PER_SEC)));
[appendedAudioTrack insertEmptyTimeRange:timeRange3];

This then provided me with the desired silence of interval seconds long, and in the right place.

Upvotes: 2

Related Questions