2012-02-20 14 views
4

現在、私はコンピュータサイエンスの学士号の一部としてアプリケーションを開発中です。アプリケーションは、iPhoneハードウェア(加速度計、GPS)と再生されている音楽のデータを相関させます。AVAssetReaderを使用してiOSでデコードされたPCMサンプルを正しく読み込む方法 - 現在正しくないデコード

このプロジェクトはまだ初期段階にあり、わずか2ヶ月間作業しています。

私は今、私が助けが必要な瞬間、iTunesライブラリの曲からPCMサンプルを読み込み、オーディオユニットを使って再生しています。 私が働きたいと思っている実装は以下の通りです:iTunesからランダムな曲を選択し、必要に応じてサンプルを読み込み、バッファに保存してsampleBufferを呼び出します。後でコンシューマモデルでは、オーディオユニット(ミキサーとリモートIO出力を持つ)にコールバックがあり、ここで必要な数のサンプルをsampleBufferからコールバックで指定されたバッファにコピーするだけです。私がスピーカーを通して聞いていることは、私が期待しているものではないものです。私はそれが間違ってデコードされているように見えるが、それは歌を演奏していることを認識することができ、それは多くのノイズを持っています!私は最初の1/2秒(24576サンプル、44.1kHz)を示す画像を添付しました。これはノーマルな見た目の出力に似ていません。 リストに入る前に、ファイルが壊れていないことを確認しました。同様に、バッファのテストケースを書きました(バッファがサンプルを変更しないことを知っています)。これは最善の方法ではないかもしれませんそれは(オーディオキューのルートに行くと主張する人もいます)、サンプルのさまざまな操作を行い、終了する前に曲を変更したり、再生されている曲を並べ替えるなどしたいと思います。しかし、サンプルを表示するグラフ(サンプルが正しくデコードされていないことを示す)はバッファから直接取り出されるため、ディスクからの読み込みとデコードが正しく機能しない理由を解決するためだけに探しています。今は単に働きながら遊びたいだけです。 カントポスト像ため、その相続人は、画像へのリンクをStackOverflowのために新しい:http://i.stack.imgur.com/RHjlv.jpg

リスト:

これがどこにあるIセットアップ今AVAssetReaderAudioMixOutput

// Set the read settings 
    audioReadSettings = [[NSMutableDictionary alloc] init]; 
    [audioReadSettings setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] 
         forKey:AVFormatIDKey]; 
    [audioReadSettings setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey]; 
    [audioReadSettings setValue:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey]; 
    [audioReadSettings setValue:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey]; 
    [audioReadSettings setValue:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsNonInterleaved]; 
    [audioReadSettings setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey]; 

のために使用されますaudioReadSettigns次のコードリストは、曲のpersistant_idを含むNSStringを受け取るメソッドです。

-(BOOL)setNextSongID:(NSString*)persistand_id { 

assert(persistand_id != nil); 

MPMediaItem *song = [self getMediaItemForPersistantID:persistand_id]; 
NSURL *assetUrl = [song valueForProperty:MPMediaItemPropertyAssetURL]; 
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetUrl 
              options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] 
                       forKey:AVURLAssetPreferPreciseDurationAndTimingKey]]; 


NSError *assetError = nil; 

assetReader = [[AVAssetReader assetReaderWithAsset:songAsset error:&assetError] retain]; 

if (assetError) { 
    NSLog(@"error: %@", assetError); 
    return NO; 
} 

CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, songAsset.duration); 
[assetReader setTimeRange:timeRange]; 

track = [[songAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; 

assetReaderOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:[NSArray arrayWithObject:track] 
                      audioSettings:audioReadSettings]; 

if (![assetReader canAddOutput:assetReaderOutput]) { 
    NSLog(@"cant add reader output... die!"); 
    return NO; 
} 

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

// just getting some basic information about the track to print 
NSArray *formatDesc = ((AVAssetTrack*)[[assetReaderOutput audioTracks] objectAtIndex:0]).formatDescriptions; 
for (unsigned int i = 0; i < [formatDesc count]; ++i) { 
    CMAudioFormatDescriptionRef item = (CMAudioFormatDescriptionRef)[formatDesc objectAtIndex:i]; 
    const CAStreamBasicDescription *asDesc = (CAStreamBasicDescription*)CMAudioFormatDescriptionGetStreamBasicDescription(item); 
    if (asDesc) { 
     // get data 
     numChannels = asDesc->mChannelsPerFrame; 
     sampleRate = asDesc->mSampleRate; 
     asDesc->Print(); 
    } 
} 
[self copyEnoughSamplesToBufferForLength:24000]; 
return YES; 
} 
今sampleBufferが誤って復号されたサンプルを持っています

-(void)copyEnoughSamplesToBufferForLength:(UInt32)samples_count { 

[w_lock lock]; 
int stillToCopy = 0; 
if (sampleBuffer->numSamples() < samples_count) { 
    stillToCopy = samples_count; 
} 

NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init]; 


CMSampleBufferRef sampleBufferRef; 
SInt16 *dataBuffer = (SInt16*)malloc(8192 * sizeof(SInt16)); 

int a = 0; 

while (stillToCopy > 0) { 

    sampleBufferRef = [assetReaderOutput copyNextSampleBuffer]; 
    if (!sampleBufferRef) { 
     // end of song or no more samples 
     return; 
    } 

    CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBufferRef); 
    CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(sampleBufferRef); 
    AudioBufferList audioBufferList; 

    CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBufferRef, 
                  NULL, 
                  &audioBufferList, 
                  sizeof(audioBufferList), 
                  NULL, 
                  NULL, 
                  0, 
                  &blockBuffer); 

    int data_length = floorf(numSamplesInBuffer * 1.0f); 

    int j = 0; 

    for (int bufferCount=0; bufferCount < audioBufferList.mNumberBuffers; bufferCount++) { 
     SInt16* samples = (SInt16 *)audioBufferList.mBuffers[bufferCount].mData; 
     for (int i=0; i < numSamplesInBuffer; i++) { 
      dataBuffer[j] = samples[i]; 
      j++; 
     } 
    } 

    CFRelease(sampleBufferRef); 
    sampleBuffer->putSamples(dataBuffer, j); 
    stillToCopy = stillToCopy - data_length; 
} 

free(dataBuffer); 
[w_lock unlock]; 
[apool release]; 
} 

:(空)copyEnoughSamplesToBufferForLength -

次の機能を提供します。なぜこれがそうだと誰でも助けてくれますか?これは、私のiTunesライブラリ(mp3、aac、wavなど)のさまざまなファイルで発生します。 さらに、私のコードの他のリストが必要な場合、または出力がどのように聞こえる場合は、リクエストごとに添付します。私は過去1週間、これをデバッグしようとしていましたが、オンラインでヘルプを見つけられませんでした。誰もが自分のやり方でやっているようですが、私はこの問題を抱えているようです。

ありがとうございました!私は、私は、ファイルを再生するためにkAudioUnitSubType_AudioFilePlayerを使用し、ユニットとそのサンプルにアクセスしますどちらかあなただったら

ピーター

答えて

10

現在、私はiTunesライブラリからオーディオサンプルをAudioUnitに抽出するプロジェクトに取り組んでいます。

参照用にaudiounitレンダリングコールバックが含まれています。入力フォーマットはSInt16StereoStreamFormatとして設定されます。

私はMichael Tysonの循環バッファ実装TPCircularBufferをバッファストレージとして使用しました。非常に使いやすく理解しやすい!ありがとうマイケル!

- (void) loadBuffer:(NSURL *)assetURL_ 
{ 
    if (nil != self.iPodAssetReader) { 
     [iTunesOperationQueue cancelAllOperations]; 

     [self cleanUpBuffer]; 
    } 

    NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
            [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, 
            [NSNumber numberWithFloat:44100.0], AVSampleRateKey, 
            [NSNumber numberWithInt:16], AVLinearPCMBitDepthKey, 
            [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved, 
            [NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey, 
            [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey, 
            nil]; 

    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:assetURL_ options:nil]; 
    if (asset == nil) { 
     NSLog(@"asset is not defined!"); 
     return; 
    } 

    NSLog(@"Total Asset Duration: %f", CMTimeGetSeconds(asset.duration)); 

    NSError *assetError = nil; 
    self.iPodAssetReader = [AVAssetReader assetReaderWithAsset:asset error:&assetError]; 
    if (assetError) { 
     NSLog (@"error: %@", assetError); 
     return; 
    } 

    AVAssetReaderOutput *readerOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:asset.tracks audioSettings:outputSettings]; 

    if (! [iPodAssetReader canAddOutput: readerOutput]) { 
     NSLog (@"can't add reader output... die!"); 
     return; 
    } 

    // add output reader to reader 
    [iPodAssetReader addOutput: readerOutput]; 

    if (! [iPodAssetReader startReading]) { 
     NSLog(@"Unable to start reading!"); 
     return; 
    } 

    // Init circular buffer 
    TPCircularBufferInit(&playbackState.circularBuffer, kTotalBufferSize); 

    __block NSBlockOperation * feediPodBufferOperation = [NSBlockOperation blockOperationWithBlock:^{ 
     while (![feediPodBufferOperation isCancelled] && iPodAssetReader.status != AVAssetReaderStatusCompleted) { 
      if (iPodAssetReader.status == AVAssetReaderStatusReading) { 
       // Check if the available buffer space is enough to hold at least one cycle of the sample data 
       if (kTotalBufferSize - playbackState.circularBuffer.fillCount >= 32768) { 
        CMSampleBufferRef nextBuffer = [readerOutput copyNextSampleBuffer]; 

        if (nextBuffer) { 
         AudioBufferList abl; 
         CMBlockBufferRef blockBuffer; 
         CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(nextBuffer, NULL, &abl, sizeof(abl), NULL, NULL, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer); 
         UInt64 size = CMSampleBufferGetTotalSampleSize(nextBuffer); 

         int bytesCopied = TPCircularBufferProduceBytes(&playbackState.circularBuffer, abl.mBuffers[0].mData, size); 

         if (!playbackState.bufferIsReady && bytesCopied > 0) { 
          playbackState.bufferIsReady = YES; 
         } 

         CFRelease(nextBuffer); 
         CFRelease(blockBuffer); 
        } 
        else { 
         break; 
        } 
       } 
      } 
     } 
     NSLog(@"iPod Buffer Reading Finished"); 
    }]; 

    [iTunesOperationQueue addOperation:feediPodBufferOperation]; 
} 

static OSStatus ipodRenderCallback (

            void      *inRefCon,  // A pointer to a struct containing the complete audio data 
            // to play, as well as state information such as the 
            // first sample to play on this invocation of the callback. 
            AudioUnitRenderActionFlags *ioActionFlags, // Unused here. When generating audio, use ioActionFlags to indicate silence 
            // between sounds; for silence, also memset the ioData buffers to 0. 
            const AudioTimeStamp  *inTimeStamp, // Unused here. 
            UInt32      inBusNumber, // The mixer unit input bus that is requesting some new 
            //  frames of audio data to play. 
            UInt32      inNumberFrames, // The number of frames of audio to provide to the buffer(s) 
            //  pointed to by the ioData parameter. 
            AudioBufferList    *ioData   // On output, the audio data to play. The callback's primary 
            //  responsibility is to fill the buffer(s) in the 
            //  AudioBufferList. 
            ) 
{ 
    Audio* audioObject = (Audio*)inRefCon; 

    AudioSampleType *outSample   = (AudioSampleType *)ioData->mBuffers[0].mData; 

    // Zero-out all the output samples first 
    memset(outSample, 0, inNumberFrames * kUnitSize * 2); 

    if (audioObject.playingiPod && audioObject.bufferIsReady) { 
     // Pull audio from circular buffer 
     int32_t availableBytes; 

     AudioSampleType *bufferTail  = TPCircularBufferTail(&audioObject.circularBuffer, &availableBytes); 

     memcpy(outSample, bufferTail, MIN(availableBytes, inNumberFrames * kUnitSize * 2)); 
     TPCircularBufferConsume(&audioObject.circularBuffer, MIN(availableBytes, inNumberFrames * kUnitSize * 2)); 
     audioObject.currentSampleNum += MIN(availableBytes/(kUnitSize * 2), inNumberFrames); 

     if (availableBytes <= inNumberFrames * kUnitSize * 2) { 
      // Buffer is running out or playback is finished 
      audioObject.bufferIsReady = NO; 
      audioObject.playingiPod = NO; 
      audioObject.currentSampleNum = 0; 

      if ([[audioObject delegate] respondsToSelector:@selector(playbackDidFinish)]) { 
       [[audioObject delegate] performSelector:@selector(playbackDidFinish)]; 
      } 
     } 
    } 

    return noErr; 
} 

- (void) setupSInt16StereoStreamFormat { 

    // The AudioUnitSampleType data type is the recommended type for sample data in audio 
    // units. This obtains the byte size of the type for use in filling in the ASBD. 
    size_t bytesPerSample = sizeof (AudioSampleType); 

    // Fill the application audio format struct's fields to define a linear PCM, 
    //  stereo, noninterleaved stream at the hardware sample rate. 
    SInt16StereoStreamFormat.mFormatID   = kAudioFormatLinearPCM; 
    SInt16StereoStreamFormat.mFormatFlags  = kAudioFormatFlagsCanonical; 
    SInt16StereoStreamFormat.mBytesPerPacket = 2 * bytesPerSample; // *** kAudioFormatFlagsCanonical <- implicit interleaved data => (left sample + right sample) per Packet 
    SInt16StereoStreamFormat.mFramesPerPacket = 1; 
    SInt16StereoStreamFormat.mBytesPerFrame  = SInt16StereoStreamFormat.mBytesPerPacket * SInt16StereoStreamFormat.mFramesPerPacket; 
    SInt16StereoStreamFormat.mChannelsPerFrame = 2;     // 2 indicates stereo 
    SInt16StereoStreamFormat.mBitsPerChannel = 8 * bytesPerSample; 
    SInt16StereoStreamFormat.mSampleRate  = graphSampleRate; 


    NSLog (@"The stereo stream format for the \"iPod\" mixer input bus:"); 
    [self printASBD: SInt16StereoStreamFormat]; 
} 
+0

ありがとう!本当に役に立ちました! – Peter

+0

kUnitSizeとは何ですか? kTotalBufferSizeとは何ですか? –

+1

@smartfaceweb:私の場合、私が使用している以下の設定 'の#define kUnitSizeはsizeof(AudioSampleType) の#defineは、あなたがこのコードはで動作するかどうか私たちに知らせてくださいすることができます655360 の#define kTotalBufferSize kBufferUnit * kUnitSize' – infiniteloop

0

は、コールバックをレンダリングします。直バッファにサンプルを抽出する

または

使用ExtAudioFileRef。

+0

AudioFilePlayerを使用すると、再生するファイルを1つだけ指定できます。さらに、iTunesから再生することはできません。 ExtAudioFileRefは、iTunesからのアクセスを許可しないオーディオセッションも使用しています(または、少なくとも私はそれを動作させることはできません)。誰も私に助けになるような何かを実装していますか?してください – Peter

+0

私は恐れているituneライブラリで多くの経験を持っていない。しかし、これは助けになりますか? http://www.subfurther.com/blog/2010/12/13/from-ipod-library-to-pcm-samples-in-far-fewer-steps-than-were-previously-necessary/ – dubbeat

2

私はそれが一種の後期であると思いますが、このライブラリを試みることができる:

https://bitbucket.org/artgillespie/tslibraryimport

ファイルにオーディオを保存するためにこれを使用した後、あなたはレンダリングコールバックを使用してデータを処理することができMixerHostから。

関連する問題