2011-01-25 18 views
8

iPodライブラリでMP3から生のPCMサンプルを抽出して、曲を再生したり、ピッチ、テンポを操作したり、フィルタ)。私はすでにAVPlayerとAVAudioPlayerのルートを崩していますが、どちらも再生を制御することはあまりありません。iPodライブラリの生のPCMサンプルを抽出してサウンドエフェクトで再生する

以下のコードは、これまで私が得たものです。私は今、オーディオを再生してそのような効果を適用するためにどのフレームワークを使用するのかわからないので、私のwhileループでCMSampleBufferRefをどうすればいいのか分からない時点です。

これを達成するための最良のアプローチは何でしょうか?私はファイルがAVAssetWriterを使って変換されたケースを見てきましたが、プロセスが時間がかかりすぎるため、これは私のためにカットされません。確かに、最初にディスクに書き込むことなく、PCMサンプルをメモリに読み込んで再生することはできますか?

NB:私は以下のコードは、プロジェクト内でmp3を参照している知っているが、私は私が何かをやっている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]; 

} 
 

答えて

8

からNSURLを引いたかのように、このアプローチは、同じように動作することを承知しています私のコードでも同様です。以下の方法がAVURLAssetのためにいくつかのNSDataを返します。それは時間がかかるだから

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

私は、バックグラウンドスレッドでこれを行うことをお勧めします。

この方法の欠点は、曲全体がメモリにロードされることですが、これはもちろん制限されています。

+1

素晴らしいです。これは私の半分の方法をそこに得ます。私はオーディオフレームワークに関するいくつかの研究を行ってきました。私の要件に合った唯一のフレームワークはOpenALです。急な学習曲線でこれは少しオフラインですが、私はFinchのようなものでNSDataを使用できますか? – Dino

+0

私はFinchを自分で使ったことがないので、NSDataを直接受け入れるかどうかはわかりません。 –

+0

FYI:試してみましたが、EXC_BAD_ACCESSを取得しました。「for」を実行する前に、 'CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer'によって返されたOSStatusをチェックすることで、それを取り除くことができました。 – Larme

2

私はトムアーヴィングの回答に加えて

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

...あなたはそのPCMを確保するために、そこにこれをしたいと思います、私は

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

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

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

を交換示唆しますサンプルの二重処理を回避し、メモリー使用量のオーバーヘッドを削減します。

代わりに、[NSMutableData dataWithLength:length]を自動解放プールにラップすることができます(in this answer)。歌の期間については

+0

素晴らしい - 私はそれを一緒に置くとき、私はそれを働かせようとしていた、まだ最適化を開始していなかった。私の最大の迷惑な点は、全部をメモリに読み込む前に、バイトのフルサイズを得る方法を見つけることができないことです。 –

+0

明らかに、私は何もコピーせずに、一度それを読むことができただけで、各サンプルの長さを合計して合計しましたが、あまりにも面倒です。 –

0

、私はあなたが単にthusly曲の資産を問い合わせることができると信じて:

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

なぜdownvote? –

+0

はい、どうしてですか?それは私のコードで動作します... –

関連する問題