开发者

Combine and convert MP3 files using QTKit

开发者 https://www.devze.com 2023-01-11 04:39 出处:网络
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?

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];开发者_StackOverflow中文版
    [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...)


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.


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];


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;
0

精彩评论

暂无评论...
验证码 换一张
取 消