2013-10-17 28 views
12

ファイル機能から簡単な再生をしようとしていますが、コールバック関数が呼び出されないようです。すべてのOSStatusが0に戻り、他の数字もすべて正しく表示されるため(AudioFileReadPacketsからの出力パケット読み出しポインタのように)、実際には意味をなさない。CoreAudio AudioQueueコールバック関数が呼び出されず、エラーが報告されない

OSStatus stat; 

stat = AudioFileOpenURL(
    (CFURLRef)urlpath, kAudioFileReadPermission, 0, &aStreamData->aFile 
); 

UInt32 dsze = 0; 
stat = AudioFileGetPropertyInfo(
    aStreamData->aFile, kAudioFilePropertyDataFormat, &dsze, 0 
); 

stat = AudioFileGetProperty(
    aStreamData->aFile, kAudioFilePropertyDataFormat, &dsze, &aStreamData->aDescription 
); 

stat = AudioQueueNewOutput(
    &aStreamData->aDescription, bufferCallback, aStreamData, NULL, NULL, 0, &aStreamData->aQueue 
); 

aStreamData->pOffset = 0; 

for(int i = 0; i < NUM_BUFFERS; i++) { 
    stat = AudioQueueAllocateBuffer(
     aStreamData->aQueue, aStreamData->aDescription.mBytesPerPacket, &aStreamData->aBuffer[i] 
    ); 

    bufferCallback(aStreamData, aStreamData->aQueue, aStreamData->aBuffer[i]); 
} 

stat = AudioQueuePrime(aStreamData->aQueue, 0, NULL); 
stat = AudioQueueStart(aStreamData->aQueue, NULL); 

そして、コールバック関数(私は関数間でstatの値をチェックしてるところは示されていないが、それだけで通常の戻ってくる。):ここで

セットアップです。

aStreamDataは(I、クラスプロパティとして使用できるようにtypedefed)私のユーザ・データ構造体であり、プレイヤが制御するのObjective-C、Cの静的インスタンスである
void bufferCallback(void *uData, AudioQueueRef queue, AudioQueueBufferRef buffer) { 
    UInt32 bread = 0; 
    UInt32 pread = buffer->mAudioDataBytesCapacity/player->aStreamData->aDescription.mBytesPerPacket; 

    OSStatus stat; 

    stat = AudioFileReadPackets(
     player->aStreamData->aFile, false, &bread, NULL, player->aStreamData->pOffset, &pread, buffer->mAudioData 
    ); 

    buffer->mAudioDataByteSize = bread; 

    stat = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); 

    player->aStreamData->pOffset += pread; 
} 

ラス。他のコードが必要な場合は、私に知らせてください。私は私の知恵の端で少しです。ここに含まれる数値のいずれかを出力すると、allocateループで自分自身を呼び出すときにbufferCallbackの関数を含む正しい結果が得られます。その後は決して呼び出されません。起動メソッドが戻り、何も起こりません。

また、私は周辺デバイス(MBox Pro 3)を使用して、CoreAudioが出力しようとしているときだけ起動するサウンドを再生しています。私がiTunesや何かを起動すると、スピーカーがぼんやりとポップアップし、LEDが点滅から点灯に変わります。デバイスが起動するようにデバイスが起動するので、CAは間違いなく何かをやっています。 (もちろん、私はオンボードのMacbookサウンドをデバイスに送って試してみました。)

私は、同じように聞こえるが動作しない問題に対する他の解決方法を読んだ。私が今やっているバッファーを複数使用しているようなもので、何の違いもないようです。

私は基本的に何か明らかに間違っていると思っていますが、どういうことかはっきりしていません。私は、関連するドキュメントを読んで、利用可能なコード例を見て、答えをネットで少し調べました。そして、これは私がする必要があるだけで、ちょうど行くはずです。

少なくとも、私が調査するために何かできることはありますか?

+0

なぜ動作しないのかわかりません。しかし、なぜAudioUnitを使用しないのですか? –

+0

@AliaksandrAndrashuk AudioUnitsはエフェクトだけでなく、ファイルからのストリーミングにそれらを使用することは合理的ですか? – Radiodef

+0

はい。 AUFilePlayer - > RemoteIO(AUGraph)を設定して、ファイルを再生することができます。 –

答えて

2

私の最初の答えは十分ではなかったので、私は2チャンネルの16ビットウェーブファイルを再生する最小限の例をコンパイルしました。

あなたのコードとの主な違いは、再生開始イベントと停止イベントをリッスンするプロパティリスナーを作成したことです。

あなたのコードについては、一見すると正当だと思われます。しかし、私が指摘する2つの事柄: 1. TOO SMALLバッファサイズでバッファを割り当てているようです。私は、バッファが小さすぎるとAudioQueuesが再生されないことに気付きました。あなたの問題に合っているようです。 2.返されたプロパティを確認しましたか?戻る私のコード例に

すべては、ハード、それは正確に良いコーディング習慣ではありませんので、コード化されたが、それはあなたがそれを行うことができます方法を示しています。

AudioStreamTest.h

#import <Foundation/Foundation.h> 
#import <AudioToolbox/AudioToolbox.h> 

uint32_t bufferSizeInSamples; 
AudioFileID file; 
UInt32 currentPacket; 
AudioQueueRef audioQueue; 
AudioQueueBufferRef buffer[3]; 
AudioStreamBasicDescription audioStreamBasicDescription; 

@interface AudioStreamTest : NSObject 

- (void)start; 
- (void)stop; 

@end 

AudioStreamTest。m

#import "AudioStreamTest.h" 

@implementation AudioStreamTest 

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     bufferSizeInSamples = 441; 

     file = NULL; 
     currentPacket = 0; 

     audioStreamBasicDescription.mBitsPerChannel = 16; 
     audioStreamBasicDescription.mBytesPerFrame = 4; 
     audioStreamBasicDescription.mBytesPerPacket = 4; 
     audioStreamBasicDescription.mChannelsPerFrame = 2; 
     audioStreamBasicDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
     audioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; 
     audioStreamBasicDescription.mFramesPerPacket = 1; 
     audioStreamBasicDescription.mReserved = 0; 
     audioStreamBasicDescription.mSampleRate = 44100; 
    } 

    return self; 
} 

- (void)start { 
    AudioQueueNewOutput(&audioStreamBasicDescription, AudioEngineOutputBufferCallback, (__bridge void *)(self), NULL, NULL, 0, &audioQueue); 

    AudioQueueAddPropertyListener(audioQueue, kAudioQueueProperty_IsRunning, AudioEnginePropertyListenerProc, NULL); 

    AudioQueueStart(audioQueue, NULL); 
} 

- (void)stop { 
    AudioQueueStop(audioQueue, YES); 
    AudioQueueRemovePropertyListener(audioQueue, kAudioQueueProperty_IsRunning, AudioEnginePropertyListenerProc, NULL); 
} 

void AudioEngineOutputBufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { 
    if (file == NULL) return; 

    UInt32 bytesRead = bufferSizeInSamples * 4; 
    UInt32 packetsRead = bufferSizeInSamples; 
    AudioFileReadPacketData(file, false, &bytesRead, NULL, currentPacket, &packetsRead, inBuffer->mAudioData); 
    inBuffer->mAudioDataByteSize = bytesRead; 
    currentPacket += packetsRead; 

    if (bytesRead == 0) { 
     AudioQueueStop(inAQ, false); 
    } 
    else { 
     AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL); 
    } 
} 

void AudioEnginePropertyListenerProc (void *inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID) { 
    //We are only interested in the property kAudioQueueProperty_IsRunning 
    if (inID != kAudioQueueProperty_IsRunning) return; 

    //Get the status of the property 
    UInt32 isRunning = false; 
    UInt32 size = sizeof(isRunning); 
    AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &isRunning, &size); 

    if (isRunning) { 
     currentPacket = 0; 

     NSString *fileName = @"/Users/roy/Documents/XCodeProjectsData/FUZZ/03.wav"; 
     NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: fileName]; 
     AudioFileOpenURL((__bridge CFURLRef) fileURL, kAudioFileReadPermission, 0, &file); 

     for (int i = 0; i < 3; i++){ 
      AudioQueueAllocateBuffer(audioQueue, bufferSizeInSamples * 4, &buffer[i]); 

      UInt32 bytesRead = bufferSizeInSamples * 4; 
      UInt32 packetsRead = bufferSizeInSamples; 
      AudioFileReadPacketData(file, false, &bytesRead, NULL, currentPacket, &packetsRead, buffer[i]->mAudioData); 
      buffer[i]->mAudioDataByteSize = bytesRead; 
      currentPacket += packetsRead; 

      AudioQueueEnqueueBuffer(audioQueue, buffer[i], 0, NULL); 
     } 
    } 

    else { 
     if (file != NULL) { 
      AudioFileClose(file); 
      file = NULL; 

      for (int i = 0; i < 3; i++) { 
       AudioQueueFreeBuffer(audioQueue, buffer[i]); 
       buffer[i] = NULL; 
      } 
     } 
    } 
} 

-(void)dealloc { 
    [super dealloc]; 
    AudioQueueDispose(audioQueue, true); 
    audioQueue = NULL; 
} 

@end 

最後に、AudioQueuesの堅牢性をテストするために行ったいくつかの研究を取り上げたいと思います。

小さすぎるAudioQueueバッファを作成すると、まったく再生されません。それは私が少し遊んで、それがなぜ遊んでいないのか見てみました。

私は150サンプルしか保持できないバッファサイズを試しても、全く音が出ません。

175サンプルを保存できるバッファサイズを試してみると、曲全体が再生されますが、歪みが大きくなります。 175は、4ms未満の音声に相当する。

AudioQueueは、バッファを提供し続ける限り、新しいバッファを要求し続けます。これは、実際にあなたのバッファを再生しているかどうかに関係なくAudioQueueに関係します。

サイズ0のバッファを指定すると、バッファが失われ、そのキューのエンキュー要求に対してエラーkAudioQueueErr_BufferEmptyが返されます。 AudioQueueがそのバッファをもう一度埋めるように頼むことはありません。あなたが投稿した最後のキューでこれが発生した場合、AudioQueueはこれ以上バッファをいっぱいにするように要求しなくなります。その場合、そのセッションのオーディオはもう聞こえません。

AudioQueuesがバッファサイズの小さいものを再生しない理由を確認するために、サウンドがない場合でもコールバックが全く呼び出されているかどうかをテストしました。答えは、AudioQueuesが再生され、データを必要とする限り、バッファが常に呼び出されることです。

したがって、バッファをキューに入れ続けると、バッファは失われません。それは起こりません。エラーがなければ、もちろんです。

なぜサウンドが再生されないのですか?

「AudioQueueEnqueueBuffer()」がエラーを返したかどうかをテストしました。それはしませんでした。私の遊びのルーチンの中にも他のエラーはありません。ファイルからの読み込みから返されたデータも良好です。

すべて正常です、バッファは良好です、データは再エンキューされていますが、音はありません。

私の最後のテストは、私が何かを聞くことができるまで、ゆっくりバッファサイズを増やすことでした。私はついに、かすかで散発的な歪みを聞いた。

は、それからそれは...私に来た

問題は、システムがあなたがオーディオをエンキューするので、もし時間と同期して流れを維持しようとするものであるようだ、とあなたがしたかったオーディオのための時間演奏が終わったら、バッファのその部分をスキップします。バッファーのサイズが小さくなり過ぎると、オーディオシステムが再び同期するまで、より多くのデータがドロップまたはスキップされます。バッファサイズが小さすぎる場合は決してありません。 (連続再生をサポートできるほどのバッファサイズを選択した場合は歪みとして聞こえます)

これは、オーディオキューが機能する唯一の方法ですが、それは良い方法ですあなたが私のような無知で本当にどのように動作するかを「発見」するときの実現。

+0

* "バッファサイズがTOO SMALLのバッファを割り当てているようです" * AudioFileGetPropertyに書き込むようにしています。これを示す例、つまりApple独自のSpeakHereなぜそれが問題になるのかわかりません。基本的に私はそれを変更する必要があると思いますか? * "返されたプロパティを確認しましたか?" * ASBDにはファイルに関する正しい情報が返されます。それは、歪んだ音声について私を困惑させるものです。 – Radiodef

+0

私はそれがあなたの問題だとは言えませんが、私はバッファサイズをテストしていましたが、全く音が出ないことから、本当に小さなバッファで様々な程度の歪んだ音まで、 。他のすべては正常と思われます。これはもちろんハードウェアに依存します。私のハードウェアは古く、おそらく新しいMacよりも速く詰まるでしょう。 – RoyGal

2

私はこれをもう一度見て、バッファを大きくすることで解決できると判断しました。私は@ RoyGalの回答を受け入れました。私は他の人が同じ問題を抱えていると思うので、実際のコードを提供したかったのです。私が試した

ことの一つは、パケットサイズを大きく作っていました:

aData->aDescription.mFramesPerPacket = 512; // or some other number 
aData->aDescription.mBytesPerPacket = (
    aData->aDescription.mFramesPerPacket * aData->aDescription.mBytesPerFrame 
); 

これは動作しません:それはAudioConverterNew returned -50メッセージで失敗するAudioQueuePrimeの原因となります。 PCMの場合はmFramesPerPacketを1にしたいと思う。

(私も何もしていないようでしたkAudioQueueProperty_DecodeBufferSizeFramesプロパティを設定しようとした、それはのためだかわからない。。)

ソリューションは唯一の指定されたサイズでバッファ(複数可)を割り当てることのようです。

AudioQueueAllocateBuffer(
    aData->aQueue, 
    aData->aDescription.mBytesPerPacket * N_BUFFER_PACKETS/N_BUFFERS, 
    &aData->aBuffer[i] 
); 

サイズは十分に大きくなければなりません。ここで

mBytesPerPacket * 1024/N_BUFFERS 

(。N_BUFFERSは、バッファの数であると> 1または再生途切れあるべき場所)

は、問題と解決策を実証MCVEです:

#import <Foundation/Foundation.h> 

#import <AudioToolbox/AudioToolbox.h> 
#import <AudioToolbox/AudioQueue.h> 
#import <AudioToolbox/AudioFile.h> 

#define N_BUFFERS 2 
#define N_BUFFER_PACKETS 1024 

typedef struct AStreamData { 
    AudioFileID aFile; 
    AudioQueueRef aQueue; 
    AudioQueueBufferRef aBuffer[N_BUFFERS]; 
    AudioStreamBasicDescription aDescription; 
    SInt64 pOffset; 
    volatile BOOL isRunning; 
} AStreamData; 

void printASBD(AudioStreamBasicDescription* desc) { 
    printf("mSampleRate = %d\n", (int)desc->mSampleRate); 
    printf("mBytesPerPacket = %d\n", desc->mBytesPerPacket); 
    printf("mFramesPerPacket = %d\n", desc->mFramesPerPacket); 
    printf("mBytesPerFrame = %d\n", desc->mBytesPerFrame); 
    printf("mChannelsPerFrame = %d\n", desc->mChannelsPerFrame); 
    printf("mBitsPerChannel = %d\n", desc->mBitsPerChannel); 
} 

void bufferCallback(
    void *vData, AudioQueueRef aQueue, AudioQueueBufferRef aBuffer 
) { 
    AStreamData* aData = (AStreamData*)vData; 

    UInt32 bRead = 0; 
    UInt32 pRead = (
     aBuffer->mAudioDataBytesCapacity/aData->aDescription.mBytesPerPacket 
    ); 

    OSStatus stat; 

    stat = AudioFileReadPackets(
     aData->aFile, false, &bRead, NULL, aData->pOffset, &pRead, aBuffer->mAudioData 
    ); 
    if(stat != 0) { 
     printf("AudioFileReadPackets returned %d\n", stat); 
    } 

    if(pRead == 0) { 
     aData->isRunning = NO; 
     return; 
    } 

    aBuffer->mAudioDataByteSize = bRead; 

    stat = AudioQueueEnqueueBuffer(aQueue, aBuffer, 0, NULL); 
    if(stat != 0) { 
     printf("AudioQueueEnqueueBuffer returned %d\n", stat); 
    } 

    aData->pOffset += pRead; 
} 

AStreamData* beginPlayback(NSURL* path) { 
    static AStreamData* aData; 
    aData = malloc(sizeof(AStreamData)); 

    OSStatus stat; 

    stat = AudioFileOpenURL(
     (CFURLRef)path, kAudioFileReadPermission, 0, &aData->aFile 
    ); 
    printf("AudioFileOpenURL returned %d\n", stat); 

    UInt32 dSize = 0; 

    stat = AudioFileGetPropertyInfo(
     aData->aFile, kAudioFilePropertyDataFormat, &dSize, 0 
    ); 
    printf("AudioFileGetPropertyInfo returned %d\n", stat); 

    stat = AudioFileGetProperty(
     aData->aFile, kAudioFilePropertyDataFormat, &dSize, &aData->aDescription 
    ); 
    printf("AudioFileGetProperty returned %d\n", stat); 

    printASBD(&aData->aDescription); 

    stat = AudioQueueNewOutput(
     &aData->aDescription, bufferCallback, aData, NULL, NULL, 0, &aData->aQueue 
    ); 
    printf("AudioQueueNewOutput returned %d\n", stat); 

    aData->pOffset = 0; 

    for(int i = 0; i < N_BUFFERS; i++) { 
     // change YES to NO for stale playback 
     if(YES) { 
      stat = AudioQueueAllocateBuffer(
       aData->aQueue, 
       aData->aDescription.mBytesPerPacket * N_BUFFER_PACKETS/N_BUFFERS, 
       &aData->aBuffer[i] 
      ); 
     } else { 
      stat = AudioQueueAllocateBuffer(
       aData->aQueue, 
       aData->aDescription.mBytesPerPacket, 
       &aData->aBuffer[i] 
      ); 
     } 

     printf(
      "AudioQueueAllocateBuffer returned %d for aBuffer[%d] with capacity %d\n", 
      stat, i, aData->aBuffer[i]->mAudioDataBytesCapacity 
     ); 

     bufferCallback(aData, aData->aQueue, aData->aBuffer[i]); 
    } 

    UInt32 numFramesPrepared = 0; 
    stat = AudioQueuePrime(aData->aQueue, 0, &numFramesPrepared); 
    printf("AudioQueuePrime returned %d with %d frames prepared\n", stat, numFramesPrepared); 

    stat = AudioQueueStart(aData->aQueue, NULL); 
    printf("AudioQueueStart returned %d\n", stat); 

    UInt32 pSize = sizeof(UInt32); 
    UInt32 isRunning; 
    stat = AudioQueueGetProperty(
     aData->aQueue, kAudioQueueProperty_IsRunning, &isRunning, &pSize 
    ); 
    printf("AudioQueueGetProperty returned %d\n", stat); 

    aData->isRunning = !!isRunning; 
    return aData; 
} 

void endPlayback(AStreamData* aData) { 
    OSStatus stat = AudioQueueStop(aData->aQueue, NO); 
    printf("AudioQueueStop returned %d\n", stat); 
} 

NSString* getPath() { 
    // change NO to YES and enter path to hard code 
    if(NO) { 
     return @""; 
    } 

    char input[512]; 
    printf("Enter file path: "); 
    scanf("%[^\n]", input); 

    return [[NSString alloc] initWithCString:input encoding:NSASCIIStringEncoding]; 
} 

int main(int argc, const char* argv[]) { 
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 

    NSURL* path = [NSURL fileURLWithPath:getPath()]; 
    AStreamData* aData = beginPlayback(path); 

    if(aData->isRunning) { 
     do { 
      printf("Queue is running...\n"); 
      [NSThread sleepForTimeInterval:1.0]; 
     } while(aData->isRunning); 
     endPlayback(aData); 
    } else { 
     printf("Playback did not start\n"); 
    } 

    [pool drain]; 
    return 0; 
} 
私はマジックナンバーが見つかりました