2017-01-20 13 views
3

これは、CoreAudioを初めて使用する試みですが、私の目標は、マイクデータをキャプチャし、新しいサンプルレートにリサンプリングし、生の16ビットPCMデータをキャプチャすることです。CoreAudio:マイクのサンプルレートを変更し、コールバックでデータを取得しますか?

私の戦略は、マイク - >サンプルレートコンバータを使ってAUGraphを作成し、次にコンバータの出力からデータを取得するコールバックを持っていることです(私は新しいサンプルレート?)。

私のコールバックはnull AudioBufferList *で起動しますが、明らかに正しくありません。私はこれをどのように設定するべきですか、私は何が間違っていますか?

コードは、以下:

CheckError(NewAUGraph(&audioGraph), @"Creating graph"); 
    CheckError(AUGraphOpen(audioGraph), @"Opening graph"); 

    AUNode micNode, converterNode; 
    AudioUnit micUnit, converterUnit; 

    makeMic(&audioGraph, &micNode, &micUnit); 

    // get the Input/inputBus's stream description 
    UInt32 sizeASBD = sizeof(AudioStreamBasicDescription); 
    AudioStreamBasicDescription hwASBDin; 
    AudioUnitGetProperty(micUnit, 
         kAudioUnitProperty_StreamFormat, 
         kAudioUnitScope_Input, 
         kInputBus, 
         &hwASBDin, 
         &sizeASBD); 
    makeConverter(&audioGraph, &converterNode, &converterUnit, hwASBDin); 

    // connect mic output to converterNode 
    CheckError(AUGraphConnectNodeInput(audioGraph, micNode, 1, converterNode, 0), 
      @"Connecting mic to converter"); 

    // set callback on the output? maybe? 
    AURenderCallbackStruct callbackStruct; 
    callbackStruct.inputProc = audioCallback; 
    callbackStruct.inputProcRefCon = (__bridge void*)self; 
    CheckError(AudioUnitSetProperty(micUnit, 
           kAudioOutputUnitProperty_SetInputCallback, 
           kAudioUnitScope_Global, 
           kInputBus, 
           &callbackStruct, 
           sizeof(callbackStruct)), 
      @"Setting callback"); 

    CheckError(AUGraphInitialize(audioGraph), @"AUGraphInitialize"); 

    // activate audio session 
    NSError *err = nil; 
    AVAudioSession *audioSession = [AVAudioSession sharedInstance]; 
    if (![audioSession setActive:YES error:&err]){ 
    [self error:[NSString stringWithFormat:@"Couldn't activate audio session: %@", err]]; 
    } 
    CheckError(AUGraphStart(audioGraph), @"AUGraphStart"); 

と:

void makeMic(AUGraph *graph, AUNode *micNode, AudioUnit *micUnit) { 
    AudioComponentDescription inputDesc; 
    inputDesc.componentType = kAudioUnitType_Output; 
    inputDesc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; 
    inputDesc.componentFlags = 0; 
    inputDesc.componentFlagsMask = 0; 
    inputDesc.componentManufacturer = kAudioUnitManufacturer_Apple; 

    CheckError(AUGraphAddNode(*graph, &inputDesc, micNode), 
      @"Adding mic node"); 

    CheckError(AUGraphNodeInfo(*graph, *micNode, 0, micUnit), 
      @"Getting mic unit"); 

    // enable microphone for recording 
    UInt32 flagOn = 1; // enable value 
    CheckError(AudioUnitSetProperty(*micUnit, 
            kAudioOutputUnitProperty_EnableIO, 
            kAudioUnitScope_Input, 
            kInputBus, 
            &flagOn, 
            sizeof(flagOn)), 
      @"Enabling microphone"); 
} 

と:

void makeConverter(AUGraph *graph, AUNode *converterNode, AudioUnit *converterUnit, AudioStreamBasicDescription inFormat) { 
    AudioComponentDescription sampleConverterDesc; 
    sampleConverterDesc.componentType = kAudioUnitType_FormatConverter; 
    sampleConverterDesc.componentSubType = kAudioUnitSubType_AUConverter; 
    sampleConverterDesc.componentFlags = 0; 
    sampleConverterDesc.componentFlagsMask = 0; 
    sampleConverterDesc.componentManufacturer = kAudioUnitManufacturer_Apple; 

    CheckError(AUGraphAddNode(*graph, &sampleConverterDesc, converterNode), 
      @"Adding converter node"); 
    CheckError(AUGraphNodeInfo(*graph, *converterNode, 0, converterUnit), 
      @"Getting converter unit"); 

    // describe desired output format 
    AudioStreamBasicDescription convertedFormat; 
    convertedFormat.mSampleRate   = 16000.0; 
    convertedFormat.mFormatID   = kAudioFormatLinearPCM; 
    convertedFormat.mFormatFlags  = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    convertedFormat.mFramesPerPacket = 1; 
    convertedFormat.mChannelsPerFrame = 1; 
    convertedFormat.mBitsPerChannel  = 16; 
    convertedFormat.mBytesPerPacket  = 2; 
    convertedFormat.mBytesPerFrame  = 2; 

    // set format descriptions 
    CheckError(AudioUnitSetProperty(*converterUnit, 
            kAudioUnitProperty_StreamFormat, 
            kAudioUnitScope_Input, 
            0, // should be the only bus # 
            &inFormat, 
            sizeof(inFormat)), 
      @"Setting format of converter input"); 
    CheckError(AudioUnitSetProperty(*converterUnit, 
            kAudioUnitProperty_StreamFormat, 
            kAudioUnitScope_Output, 
            0, // should be the only bus # 
            &convertedFormat, 
            sizeof(convertedFormat)), 
      @"Setting format of converter output"); 
} 
+1

を実証するためのViewControllerクラスを作成したが、この必要性は、ライブ起こることをしていますか?オーディオをファイルに取り込むほうがずっと簡単です。 C APIを使用するように設定されていますか? AVAudioEngineはそうでなければかなりのことができます。 – dave234

+0

ライブを行う必要がありますが、使用するAPIのセットは使用しません。 – user358829

+0

AVAudioEngineをチェックしたところ、何らかの理由でサンプルレートの変換が特定のサンプルレートに制限されているようです。私はc APIが奇数サンプルレートに必要であると思います。 – dave234

答えて

1

レンダリングコールバックは、オーディオユニットのソースを用いています。 remoteIOユニットにkAudioOutputUnitProperty_SetInputCallbackプロパティを設定した場合は、指定したコールバックからAudioUnitRenderを呼び出す必要があります。手動でサンプルレート変換を行う必要があります。これは醜いです。

"簡単な"方法があります。 remoteIOは、入力(マイク)と出力(スピーカー)の2つのユニットとして機能します。 remoteIOを使用してグラフを作成し、希望の形式でマイクをスピーカーに接続します。次に、「タップ」として機能するrenderNotifyコールバックを使用してデータを取得できます。

私は

#import "ViewController.h" 
#import <AudioToolbox/AudioToolbox.h> 
#import <AVFoundation/AVFoundation.h> 

@implementation ViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    //Set your audio session to allow recording 
    AVAudioSession *audioSession = [AVAudioSession sharedInstance]; 
    [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:NULL]; 
    [audioSession setActive:1 error:NULL]; 

    //Create graph and units 
    AUGraph graph = NULL; 
    NewAUGraph(&graph); 

    AUNode ioNode; 
    AudioUnit ioUnit = NULL; 
    AudioComponentDescription ioDescription = {0}; 
    ioDescription.componentManufacturer = kAudioUnitManufacturer_Apple; 
    ioDescription.componentType   = kAudioUnitType_Output; 
    ioDescription.componentSubType  = kAudioUnitSubType_VoiceProcessingIO; 

    AUGraphAddNode(graph, &ioDescription, &ioNode); 
    AUGraphOpen(graph); 
    AUGraphNodeInfo(graph, ioNode, NULL, &ioUnit); 

    UInt32 enable = 1; 
    AudioUnitSetProperty(ioUnit,kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Input,1,&enable,sizeof(enable)); 

    //Set the output of the ioUnit's input bus, and the input of it's output bus to the desired format. 
    //Core audio basically has implicite converters that we're taking advantage of. 
    AudioStreamBasicDescription asbd = {0}; 
    asbd.mSampleRate  = 16000.0; 
    asbd.mFormatID   = kAudioFormatLinearPCM; 
    asbd.mFormatFlags  = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    asbd.mFramesPerPacket = 1; 
    asbd.mChannelsPerFrame = 1; 
    asbd.mBitsPerChannel = 16; 
    asbd.mBytesPerPacket = 2; 
    asbd.mBytesPerFrame  = 2; 

    AudioUnitSetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &asbd, sizeof(asbd)); 
    AudioUnitSetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, sizeof(asbd)); 

    //Connect output of the remoteIO's input bus to the input of it's output bus 
    AUGraphConnectNodeInput(graph, ioNode, 1, ioNode, 0); 

    //Add a render notify with a bridged reference to self (If using ARC) 
    AudioUnitAddRenderNotify(ioUnit, renderNotify, (__bridge void *)self); 

    //Start graph 
    AUGraphInitialize(graph); 
    AUGraphStart(graph); 
    CAShow(graph); 



} 
OSStatus renderNotify(void       *inRefCon, 
         AudioUnitRenderActionFlags *ioActionFlags, 
         const AudioTimeStamp   *inTimeStamp, 
         UInt32      inBusNumber, 
         UInt32      inNumberFrames, 
         AudioBufferList    *ioData){ 

    //Filter anything that isn't a post render call on the input bus 
    if (*ioActionFlags != kAudioUnitRenderAction_PostRender || inBusNumber != 1) { 
     return noErr; 
    } 
    //Get a reference to self 
    ViewController *self = (__bridge ViewController *)inRefCon; 

    //Do stuff with audio 

    //Optionally mute the audio by setting it to zero; 
    for (int i = 0; i < ioData->mNumberBuffers; i++) { 
     memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); 
    } 
    return noErr; 
} 


@end 
+0

大丈夫、ありがとう! 1つのフォローアップの質問:16ビットサンプルをAudioBufferListから戻すにはどうすればよいですか?今私は次のことをやっています:http://pastebin.com/SM11ykf4 私は基本的にNSMutableArrayのNSMutableArrayにこのデータを押し出す必要がありますが、私はすぐに良いデータを取得していないようです。 – user358829

+0

私はあなたがいくつかのCを学ばなければならないと思うと思います。あなたがオーディオを扱っているなら、あなたの時間は価値があります。レンダースレッドで各サンプルのオブジェクトを作成することは、おそらく保持しません。しかし、質問に答えるには、ioData-> mBuffers []。mDataのデータを目的の形式にキャストします。 – dave234

+0

私のCは実際にかなり強力ですが、NSMumberのNSMutableArrayに入るためにはそのデータが必要ですので、React Nativeに渡すことができます。それは効率的ではないことに同意しましたが、私は最適化する前に良いデータを得ていることを確認したいと思います。 mntデータをSInt16 *にキャストして歩くと、悪いデータが表示されます。特に、ここにサンプルがあります:15275,0,15112,0、-17608,0、-17491,0、-17460,0、-17507 、0、-17768,0,15076,0,15178 交互の0は私がここで何か間違っているように感じるようにします... kAudioFormatFlagIsPackedに関する良いドキュメントは見つかりませんでした? – user358829

関連する問題