2011-01-25 9 views
8

Próbuję wyodrębnić surowe próbki PCM z pliku MP3 w bibliotece iPoda, dzięki czemu mogę odtwarzać utwór i manipulować tonacją, tempem i zastosować efekty dźwiękowe (takie jak filtry). Przeszedłem już przez AVPlayer i AVAudioPlayer, które nie pozwalają na bardzo dużą kontrolę nad odtwarzaniem.Wyodrębnianie próbek pobranych z biblioteki iPoda z biblioteki PCM i odtwarzanie z efektami dźwiękowymi

Poniższy kod jest tak daleki, jak to zrobiłem. Jestem teraz w punkcie, w którym nie wiem, co zrobić z CMSampleBufferRef w mojej pętli while, ponieważ nie wiem, której struktury użyć, aby odtworzyć dźwięk i zastosować takie efekty.

Każdy pomysł, jakie byłoby najlepsze podejście, aby to osiągnąć? Przyjrzałem się przypadkom, w których plik jest konwertowany za pomocą zestawu AVAssetWriter, ale nie jest to dla mnie zbyt trudne, ponieważ proces ten jest zbyt czasochłonny. Z pewnością mogę po prostu odczytać próbki PCM w pamięci do odtwarzania bez uprzedniego zapisania ich na dysku?

NB: Wiem poniższy kod odwołuje mp3 w ramach projektu, ale zdaję sobie sprawę, że takie podejście będzie działać tak samo jak gdybym ciągnięcie bibliotece NSURL z 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]; 

} 
 

Odpowiedz

8

robię coś podobne w moim własnym kodzie. Poniższa metoda zwraca jakąś NSData do 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]; 
} 

polecam zrobić to na wątku tła, ponieważ jest to czasochłonne.

Wadą tej metody jest to, że cała piosenka jest ładowana do pamięci, co oczywiście jest ograniczone.

+1

Doskonałe. To doprowadza mnie do połowy. Robiłem badania nad frameworkami audio i wydaje mi się, że jedyną strukturą, która odpowiada moim wymaganiom jest OpenAL. Ze stromą krzywą uczenia się jest to nieco nieprzydatne, ale czy mogę użyć NSData z czymś podobnym do Fincha? – Dino

+0

Sam nigdy nie używałem Fincha, więc nie jestem pewien, czy akceptuje on NSData. –

+0

FYI: Próbowałem go i otrzymałem EXC_BAD_ACCESS ... mogłem go usunąć, sprawdzając OSStatus zwrócony przez 'CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer' przed wykonaniem' for'. – Larme

2

Myślę, że chcesz to tam, aby zapewnić jego 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]; 
5

Dodatkowo do Toma Irvinga Odpowiedź, proponuję zastąpienie

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

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

z

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

który unika podwójnego posługiwania się próbkami i zmniejsza obciążenie związane z wykorzystaniem pamięci.

można także zawinąć [NSMutableData dataWithLength: length] w puli automatycznej wersji, co zademonstrowano jako in this answer dla niepowiązanego, ale podobnego pytania.

+0

Fantastyczny - kiedy go połączyłem, próbowałem sprawić, żeby działało, jeszcze nie rozpoczęło optymalizacji. Moją największą irytacją jest to, że nie potrafię znaleźć sposobu na uzyskanie pełnego rozmiaru utworu w bajtach przed przeczytaniem całej rzeczy w pamięci. –

+0

Oczywiście mogłem go przeczytać raz, bez kopiowania czegokolwiek, po prostu pobierając długość dla każdej próbki i sumując je, ale wydaje się zbyt brudny. –

0

Na czas trwania utworu, uważam, że można po prostu zapytać aktywu piosenkę wygląda następująco:

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

Dlaczego upadek? –

+0

Tak, dlaczego? Działa w moim kodzie ... –

Powiązane problemy