Extract iPod Library raw PCM samples and play with sound effects

后端 未结 4 1960
清歌不尽
清歌不尽 2020-12-04 15:47

I am trying to extract raw PCM samples from an MP3 in the iPod Library so that I can play the song and manipulate the pitch, tempo, and apply sound effects (such as filters)

相关标签:
4条回答
  • 2020-12-04 16:28

    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);
    
    0 讨论(0)
  • 2020-12-04 16:48

    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.

    0 讨论(0)
  • 2020-12-04 16:50

    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];
    
    0 讨论(0)
  • 2020-12-04 16:52

    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.

    0 讨论(0)
提交回复
热议问题