2014-12-04 3 views
0


OS X에서 CoreAudio를 사용하여 원시 PCM 데이터 (16 비트 부호)를 재생해야합니다. UDP 소켓을 사용하여 네트워크에서 가져옵니다 (송신 측 데이터는 마이크에서 캡처 됨) .
문제는 내가 들었을 때 짧은 깨지는 소리와 조용한 소리 만 들리는 것입니다.
AudioQueue를 사용하여 데이터를 재생하려고합니다. 이 같은 I 설정을 :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 코드를 기반으로합니다.이 코드는 CBR 데이터 작업에 적합한 Mac Developer Library의 AudioFileStreamExample 버전입니다.

또한 this 게시물을보고 거기에 설명 된 AudioStreamBasicDescription을 시도했습니다. 리틀 또는 빅 엔디 언으로 플래그를 변경하려고했습니다. 그것은 작동하지 않았다.
비슷한 문제를 발견하는 동안 여기에 다른 게시물과 다른 리소스를 보았습니다. 예를 들어 내 PCM 데이터의 순서를 확인했습니다. 두 개 이상의 링크를 게시 할 수 없습니다.

내가 뭘 잘못하고 있는지 이해할 수 있도록 도와주세요. 어쩌면 내가 이런 식으로 포기하고 즉시 Audio Units를 사용해야할까요? CoreAudio의 초보자 일 뿐이며 CoreAudio의 중간 레벨이이 문제를 해결하는 데 도움이되기를 바랍니다.

P. 미안해, 내 영어로 할 수있는대로 노력했다.

+0

'MyFindQueueBuffer'는 (는) 어떻게 생겼습니까? 다만 그런 – sbooth

+0

: 'MyFindQueueBuffer INT (MyData로 * MYDATA, AudioQueueBufferRef inBuffer) 용 { (부호 INT I = 0; I audioQueueBuffer [I]) return i; } return -1; } ' – foobar

+0

죄송합니다, linebreaks %로 엉망이되었습니다. – foobar

답변

2

이 문제가 이미 해결 되었기를 바랍니다.하지만이 문제를 겪고있는 다른 사람들의 이익을 위해 답변을 게시 해 드리겠습니다.

일단 오디오 대기열이 시작되면 대기열에 넣기를 중지하더라도 시간이 앞으로 계속 이동하기 때문에 문제가 발생할 가능성이 큽니다. 그러나 버퍼를 대기열에 추가하면 이전 대기열에 있던 버퍼 바로 다음에있는 타임 스탬프로 대기열에 추가됩니다. 즉, 오디오 대기열이 재생되는 위치보다 앞서 있지 않으면 과거의 타임 스탬프로 버퍼를 대기열에 넣음으로써 오디오 대기열이 조용 해지고 isRunning 속성이 계속 true로 유지됩니다.

이 문제를 해결하려면 몇 가지 옵션이 있습니다. 이론상 가장 간단한 방법은 버퍼를 제출하는 것입니다. 하지만 UDP를 사용하고 있기 때문에 항상 제출할 데이터가 있다는 보장은 없습니다.

또 다른 옵션은 연주해야하는 샘플을 추적하고 간격이 필요할 때마다 빈 소리 버퍼를 제출할 수 있다는 것입니다. 이 옵션은 소스 데이터에 필요한 침묵을 계산하는 데 사용할 수있는 타임 스탬프가있는 경우에 유용합니다. 그러나 이상적으로, 당신은 이것을 할 필요가 없을 것입니다.

대신 시스템 시간을 사용하여 버퍼의 타임 스탬프를 계산해야합니다.AudioQueueEnqueueBuffer 대신 AudioQueueEnqueueBufferWithParameters를 대신 사용해야합니다. 타임 스탬프가 대기열이 현재있는 곳보다 앞서 있는지 확인하면됩니다. 또한 대기열을 시작할 때 시스템 시간을 추적해야하므로 제출중인 각 버퍼의 정확한 시간 소인을 계산할 수 있습니다. 소스 데이터에 타임 스탬프 값이 있으면 버퍼 타임 스탬프도 계산할 수 있어야합니다.