Extract iPod Library raw PCM samples and play with sound effects

北城以北 提交于 2019-11-27 18:03:25

I'm doing something similar in my own code. The following method returns some NSData for a AVURLAsset:

- (NSData *)extractDataForAsset:(AVURLAsset *)songAsset {

    NSError * error = nil;
    AVAssetReader * reader = [[AVAssetReader alloc] initWithAsset:songAsset error:&error];

    AVAssetTrack * songTrack = [songAsset.tracks objectAtIndex:0];
    AVAssetReaderTrackOutput * output = [[AVAssetReaderTrackOutput alloc] initWithTrack:songTrack outputSettings:nil];
    [reader addOutput:output];
    [output release];

    NSMutableData * fullSongData = [[NSMutableData alloc] init];
    [reader startReading];

    while (reader.status == AVAssetReaderStatusReading){

        AVAssetReaderTrackOutput * trackOutput = (AVAssetReaderTrackOutput *)[reader.outputs objectAtIndex:0];
        CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer];

        if (sampleBufferRef){
            CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(sampleBufferRef);

            size_t length = CMBlockBufferGetDataLength(blockBufferRef);
            UInt8 buffer[length];
            CMBlockBufferCopyDataBytes(blockBufferRef, 0, length, buffer);

            NSData * data = [[NSData alloc] initWithBytes:buffer length:length];
            [fullSongData appendData:data];
            [data release];

            CMSampleBufferInvalidate(sampleBufferRef);
            CFRelease(sampleBufferRef);
        }
    }

    if (reader.status == AVAssetReaderStatusFailed || reader.status == AVAssetReaderStatusUnknown){
        // Something went wrong. Handle it.
    }

    if (reader.status == AVAssetReaderStatusCompleted){
        // You're done. It worked.
    }

    [reader release];

    return [fullSongData autorelease];
}

I would recommend doing this on a background thread because it's time consuming.

A drawback to this method is that the whole song is loaded into memory, which is of course limited.

unsynchronized

Additionally to Tom Irving's Answer, I suggest replacing

       UInt8 buffer[length];
       CMBlockBufferCopyDataBytes(blockBufferRef, 0, length, buffer);

        NSData * data = [[NSData alloc] initWithBytes:buffer length:length];

with

        NSMutableData * data = [[NSMutableData alloc] initWithLength:length];
        CMBlockBufferCopyDataBytes(blockBufferRef, 0, length, data.mutableBytes);

which avoids double handling the samples, and reduces memory usage overhead.

alternatively, you can wrap [NSMutableData dataWithLength:length] in an auto release pool as demonstrated in this answer to an unrelated but similar question.

I think you want this in there to ensure its PCM...

NSDictionary* outputSettingsDict = [[NSDictionary alloc] initWithObjectsAndKeys:

                        [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,
                   //     [NSNumber numberWithInt:44100.0],AVSampleRateKey, /*Not Supported*/
                   //     [NSNumber numberWithInt: 2],AVNumberOfChannelsKey,    /*Not Supported*/

                        [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
                        [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
                        [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
                        [NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved,

                        nil];

AVAssetReaderTrackOutput* output = [[AVAssetReaderTrackOutput alloc] initWithTrack:songTrack outputSettings:outputSettingsDict];

For the song duration, I believe you can simply query the song Asset thusly:

float songTimeInSeconds = CMTimeGetSeconds(songAsset.duration);
    int songMinutes = (int)(songTimeInSeconds/60.);
    int songSeconds = (int)(songTimeInSeconds - 60.0*songMinutes);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!