Reputation: 3655
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
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
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
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