2011-01-25 31 views
8

Tôi đang cố gắng trích xuất các mẫu PCM thô từ MP3 trong Thư viện iPod để tôi có thể phát bài hát và điều chỉnh cường độ, tiến độ và áp dụng hiệu ứng âm thanh (chẳng hạn như bộ lọc). Tôi đã đi xuống các tuyến đường của AVPlayer và AVAudioPlayer mà cả hai không cho phép kiểm soát rất nhiều về phát lại ở tất cả.Trích xuất các mẫu PCM nguyên mẫu của Thư viện iPod và phát với các hiệu ứng âm thanh

Mã dưới đây là như xa như tôi đã nhận được với điều này. Tôi đang ở một thời điểm mà tôi không biết phải làm gì với CMSampleBufferRef trong vòng lặp của tôi vì tôi không biết sử dụng khung nào để phát lại âm thanh và áp dụng các hiệu ứng như vậy.

Bất kỳ ý tưởng nào sẽ là cách tiếp cận tốt nhất để đạt được điều này? Tôi đã xem xét các trường hợp tập tin được chuyển đổi bằng cách sử dụng một AVAssetWriter nhưng điều này sẽ không cắt nó cho tôi bởi vì quá trình này là quá tốn thời gian. Chắc chắn tôi chỉ có thể đọc các mẫu PCM vào bộ nhớ để phát lại mà không cần phải ghi chúng vào đĩa trước?

NB: Tôi biết mã bên dưới tham chiếu một mp3 trong dự án nhưng tôi nhận thức được rằng phương pháp này sẽ làm việc như nhau như thể tôi đang kéo một NSURL từ MPMediaPropertyAssetURL

 

-(IBAction)loadTrack:(id)sender { 

NSString *songPath = [[NSBundle mainBundle] pathForResource:@"Smooth_Sub Focus_192" ofType:@"mp3"]; 
NSURL *assetURL = [[NSURL alloc] initFileURLWithPath:songPath]; 

AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil]; 

NSError *assetError = nil; 
AVAssetReader *assetReader = [[AVAssetReader assetReaderWithAsset:songAsset 
       error:&assetError] retain]; 
if (assetError) { 
    NSLog (@"Error: %@", assetError); 
    return; 
} 

AVAssetReaderOutput *assetReaderOutput = [[AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:songAsset.tracks 
          audioSettings: nil] retain]; 
if (![assetReader canAddOutput:assetReaderOutput]) { 
    NSLog (@"Incompatible Asser Reader Output"); 
    return; 
} 

[assetReader addOutput: assetReaderOutput]; 
[assetReader startReading]; 

CMSampleBufferRef nextBuffer; 
while (nextBuffer = [assetReaderOutput copyNextSampleBuffer]) { 
    /* What Do I Do Here? */ 
} 

[assetReader release]; 
[assetReaderOutput release]; 

} 
 

Trả lời

8

tôi đang làm một cái gì đó tương tự trong mã của riêng tôi. Phương thức sau trả về một số NSData cho 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]; 
} 

Tôi khuyên bạn nên làm điều này trên một chuỗi nền vì nó tốn thời gian.

Một nhược điểm của phương pháp này là toàn bộ bài hát được tải vào bộ nhớ, tất nhiên là hạn chế.

+1

Tuyệt vời. Điều này giúp tôi một nửa ở đó. Tôi đã thực hiện một số nghiên cứu về các khung âm thanh và dường như khung duy nhất phù hợp với yêu cầu của tôi là OpenAL. Với một đường cong học tập dốc, điều này hơi ngắt nhưng tôi có thể sử dụng NSData với thứ gì đó như Finch không? – Dino

+0

Tôi chưa từng sử dụng Finch, vì vậy tôi không chắc chắn liệu nó có chấp nhận trực tiếp NSData hay không. –

+0

FYI: Tôi đã thử nó, và tôi đã nhận EXC_BAD_ACCESS ... Tôi có thể loại bỏ nó bằng cách kiểm tra OSStatus trả về bởi 'CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer' trước khi thực hiện' for'. – Larme

2

Tôi nghĩ rằng bạn muốn điều này ở đó để đảm bảo PCM của nó ...

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

Ngoài ra để trả lời Tom Irving, tôi khuyên bạn nên thay thế

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

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

với

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

mà tránh xử lý gấp đôi các mẫu và giảm chi phí sử dụng bộ nhớ.

cách khác, bạn có thể bọc [NSMutableData dataWithLength: length] trong một bể tự động phát hành như được minh họa in this answer cho một câu hỏi không liên quan nhưng tương tự.

+0

Tuyệt vời - khi tôi đặt nó lại với nhau, tôi chỉ cố gắng làm cho nó hoạt động, chưa bắt đầu tối ưu hóa. Nỗi phiền toái lớn nhất của tôi là tôi dường như không thể tìm ra cách để có được kích thước đầy đủ của bài hát bằng byte trước khi đọc toàn bộ nội dung trong bộ nhớ. –

+0

Rõ ràng tôi có thể đọc nó một lần, mà không cần sao chép bất cứ điều gì, chỉ cần nhận được chiều dài cho mỗi mẫu và tổng hợp chúng, nhưng nó có vẻ quá lộn xộn. –

0

Trong suốt thời gian bài hát, tôi tin rằng bạn chỉ có thể truy vấn các bài hát Asset thusly:

float songTimeInSeconds = CMTimeGetSeconds(songAsset.duration); 
    int songMinutes = (int)(songTimeInSeconds/60.); 
    int songSeconds = (int)(songTimeInSeconds - 60.0*songMinutes); 
+0

Tại sao lại là downvote? –

+0

Có, tại sao? Nó hoạt động trong mã của tôi ... –