Stephen Poletto
Stephen Poletto

Reputation: 3655

Combine and convert MP3 files using QTKit

I'm trying to combine two .mp3s into a single .wav file using QTKit. It seems to be working, but the last few seconds of the second file are getting truncated. Any ideas?

- (IBAction)combineSelectedFilesAndOutputAsWAV {
    QTMovie *movie = [QTMovie movieWithFile:fileOne error:NULL];
    [movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];  
    QTMovie *segmentTwo = [QTMovie movieWithFile:fileTwo error:NULL];
    QTTimeRange range = { .time = QTZeroTime, .duration = [segmentTwo duration] };
    [segmentTwo setSelection:range];
    [movie appendSelectionFromMovie:segmentTwo];
    while([[movie attributeForKey:QTMovieLoadStateAttribute] longValue] != 100000L) {
        //wait until QTMovieLoadStateComplete
    }
    NSDictionary *exportAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:YES], QTMovieExport,
                                     [NSNumber numberWithLong:kQTFileTypeWave], QTMovieExportType, nil];
    NSString *outputFile = [NSString stringWithFormat:@"%@.wav", outputFilename];
    NSString *filepath = [destinationDirectory stringByAppendingPathComponent:outputFile];
    if (![movie writeToFile:filepath withAttributes:exportAttributes]) {
       //ERROR
    } 
}

(Ignore the while loop waiting for QTMovieLoadStateComplete. I'll switch to using notifications in the future. But for now, it shouldn't matter...)

Upvotes: 0

Views: 1311

Answers (3)

Thamster
Thamster

Reputation: 156

You can also simply set the movie/mp3 loader to be synchronous instead of async using the QTMovieOpenAsyncOKAttribute like so:

NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool:NO] , QTMovieOpenAsyncOKAttribute,
    @"path to file", QTMovieFileNameAttribute,
    [NSNumber numberWithBool:YES], QTMovieEditableAttribute,
     nil];
QTMovie *myMovie = [QTMovie movieWithAttributes:movieAttributes error:nil];

that way the duration should be correct when you check it like so:

[[myMovie attributeForKey:QTMovieDurationAttribute] QTTimeValue].timeValue;

Upvotes: 0

Stephen Poletto
Stephen Poletto

Reputation: 3655

It looks like the real issue was that I was making queries to the QTMovie object right after it was created. Before requesting the duration of a QTMovie object, you should make sure it is fully loaded. I've added the following:

- (void)pasteSegmentToConcatenatedFile:(QTMovie *)segment {
    NSLog(@"LoadStateChanged for movie : %@", segment);
    if ([[segment attributeForKey:QTMovieLoadStateAttribute] longValue] >= kMovieLoadStateComplete) {
        NSLog(@"The movie is fully loaded.");
        QTTimeRange range = { .time = QTZeroTime, .duration = [segment duration] };
        [segment setSelection:range];
        [concatenatedFile appendSelectionFromMovie:segment];
        [self convertAndOutputConcatenatedMovie];
    }
}

This method will get invoked whenever the LoadState of the QTMovie object changes:

QTMovie *segmentTwo = [QTMovie movieWithFile:fileTwo error:NULL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pasteSegmentToConcatenatedFile:) name:QTMovieLoadStateDidChangeNotification object:segmentTwo];

Upvotes: 0

VitalyVal
VitalyVal

Reputation: 1350

1) Generally, there is no way to quickly get exact duration of an arbitrary MP3 file. The only reliable way - decode the entire file. This is true even for CBR files (some MP3 encoders do not provide accurate bitrate). So the [segment duration] may not fit for cases, where exact duration is required. For your pupposes you can try to use something like [segment duration]+delta, or even a MaxDuration instead of [segment duration].

2) In some cases (at least if formats of merged files are the same) you can simply merge the files as binary data (without using QTKit). More pricicely: write RIFF-WAV header for target file; appeng sound stream from first file (that is source file exluding heading and tailing tags if present); appeng sound stream from second file; update RIFF-WAV header.

Upvotes: 2

Related Questions