2014-12-04 107 views
0


我需要在OS X上使用CoreAudio播放原始PCM数据(16位带符号)。我使用UDP套接字从网络获取它(在发送方数据从麦克风捕获) 。
问题是,我现在听到的只是一些短暂的噪音,然后只是沉默。
我想使用AudioQueue播放数据。我的设置是这样的:使用CoreAudio中的AudioQueue从网络播放raw pcm

// Set up stream format fields 
AudioStreamBasicDescription streamFormat; 
streamFormat.mSampleRate = 44100; 
streamFormat.mFormatID = kAudioFormatLinearPCM; 
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; 
streamFormat.mBitsPerChannel = 16; 
streamFormat.mChannelsPerFrame = 1; 
streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame; 
streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame; 
streamFormat.mFramesPerPacket = 1; 
streamFormat.mReserved = 0; 

OSStatus err = noErr; 
// create the audio queue 
err = AudioQueueNewOutput(&streamFormat, MyAudioQueueOutputCallback, myData, NULL, NULL, 0, &myData->audioQueue); 
if (err) 
{ PRINTERROR("AudioQueueNewOutput"); myData->failed = true; result = false;} 

// allocate audio queue buffers 
for (unsigned int i = 0; i < kNumAQBufs; ++i) { 
    err = AudioQueueAllocateBuffer(myData->audioQueue, kAQBufSize, &myData->audioQueueBuffer[i]); 
    if (err) 
    { PRINTERROR("AudioQueueAllocateBuffer"); myData->failed = true; break; result = false;} 
} 

// listen for kAudioQueueProperty_IsRunning 
err = AudioQueueAddPropertyListener(myData->audioQueue, kAudioQueueProperty_IsRunning, MyAudioQueueIsRunningCallback, myData); 
if (err) 
{ PRINTERROR("AudioQueueAddPropertyListener"); myData->failed = true; result = false;} 

MyAudioQueueOutputCallback是:

void MyAudioQueueOutputCallback(void* inClientData, 
          AudioQueueRef inAQ, 
          AudioQueueBufferRef inBuffer) 
{ 
    // this is called by the audio queue when it has finished decoding our data. 
    // The buffer is now free to be reused. 
    MyData* myData = (MyData*)inClientData; 

    unsigned int bufIndex = MyFindQueueBuffer(myData, inBuffer); 

    // signal waiting thread that the buffer is free. 
    pthread_mutex_lock(&myData->mutex); 
    myData->inuse[bufIndex] = false; 
    pthread_cond_signal(&myData->cond); 
    pthread_mutex_unlock(&myData->mutex); 
} 

MyAudioQueueIsRunningCallback是:

void MyAudioQueueIsRunningCallback(void* inClientData, 
           AudioQueueRef inAQ, 
           AudioQueuePropertyID inID) 
{ 
    MyData* myData = (MyData*)inClientData; 

    UInt32 running; 
    UInt32 size; 
    OSStatus err = AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &running, &size); 
    if (err) { PRINTERROR("get kAudioQueueProperty_IsRunning"); return; } 
    if (!running) { 
     pthread_mutex_lock(&myData->mutex); 
     pthread_cond_signal(&myData->done); 
     pthread_mutex_unlock(&myData->mutex); 
    } 
} 

和MyData的是:

struct MyData 
{ 
AudioQueueRef audioQueue;               // the audio queue 
AudioQueueBufferRef audioQueueBuffer[kNumAQBufs];   // audio queue buffers 

AudioStreamPacketDescription packetDescs[kAQMaxPacketDescs];  // packet descriptions for enqueuing audio 

unsigned int fillBufferIndex;  // the index of the audioQueueBuffer that is being filled 
size_t bytesFilled;       // how many bytes have been filled 
size_t packetsFilled;      // how many packets have been filled 

bool inuse[kNumAQBufs];      // flags to indicate that a buffer is still in use 
bool started;          // flag to indicate that the queue has been started 
bool failed;          // flag to indicate an error occurred 
bool finished;          // flag to inidicate that termination is requested 

pthread_mutex_t mutex;      // a mutex to protect the inuse flags 
pthread_mutex_t mutex2;   // a mutex to protect the AudioQueue buffer 
pthread_cond_t cond;      // a condition varable for handling the inuse flags 
pthread_cond_t done;      // a condition varable for handling the inuse flags 
}; 

对不起,如果我张贴了太多的代码 - 希望它可以帮助任何人了解我到底做了什么。

大多数情况下,我的代码基于this代码,它是Mac Developer Library中适用于CBR数据的AudioFileStreamExample版本。

另外,我看着this后,并尝试AudioStreamBasicDescription desribed那里。并试图将我的旗帜更改为Little或Big Endian。它没有工作。
我在查看其他资源的同时发现了类似问题的其他资源,例如,我查看了我的PCM数据的顺序。我不能发布两个以上的链接。

请任何人帮助我了解我在做什么错了!也许我应该放弃这种方式并立即使用音频单元?我只是CoreAudio的新手,希望CoreAudio的中级能够帮助我解决这个问题。

P.S.对不起,我的英语,我尽我所能。

+0

“MyFindQueueBuffer”是什么样的? – sbooth 2014-12-05 01:59:56

+0

就这样: 'INT MyFindQueueBuffer(迈德特* myData的,AudioQueueBufferRef inBuffer) { 为(无符号整数,I = 0;我 audioQueueBuffer [I] ) return i; } return -1; }' – foobar 2014-12-05 08:05:18

+0

对不起,与linebreaks搞砸了%) – foobar 2014-12-05 08:25:07

回答

2

我希望你已经解决了这一个你自己的了,但对于谁是有这个问题其他人的利益,我会张贴一个答案。

问题很可能是因为一旦音频队列启动后,即使停止排队缓冲区,时间也会继续前进。但是,当你排队一个缓冲区时,它会在先前排入队列的缓冲区之后加入一个时间戳。这意味着,如果你不留的领先音频队列打在哪里,你最终会与过去的时间戳排队缓冲区,因此音频队列会沉默和,将isRunning属性将仍然是真实的。

要解决此问题,您有几个选项。理论上最简单的办法就是永远不会落后于提交缓冲区。但是由于您使用的是UDP,因此不能保证您总是会提交数据。

另一种选择是,您可以跟踪您应该播放的样本,并在您需要填充空白时提交一个空的缓冲区。如果您的源数据具有可用于计算您需要多少沉默的时间戳,则此选项可以很好地工作。但理想情况下,你不需要这样做。

相反,您应该使用系统时间计算缓冲区的时间戳。而不是AudioQueueEnqueueBuffer,您需要改用AudioQueueEnqueueBufferWithParameters。您只需确保时间戳在队列当前位置之前。您还需要跟踪启动队列时的系统时间,以便为正在提交的每个缓冲区计算正确的时间戳。如果源数据上有时间戳值,则应该可以使用它们来计算缓冲区时间戳。