2014-12-04 1 views
0


Мне нужно воспроизвести необработанные данные PCM (16 бит) с использованием CoreAudio на OS X. Я получаю его из сети с использованием сокета UDP (на стороне передаются данные с микрофона) ,
Проблема в том, что все, что я слышу сейчас, это небольшой шум трещины, а затем только молчание.
Я пытаюсь воспроизвести данные с помощью AudioQueue. Настройка я это так:Воспроизведение raw pcm из сети с использованием AudioQueue в CoreAudio

// 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, который является версией AudioFileStreamExample из Mac Developer Library, адаптированной для работы с данными CBR.

Также я просмотрел сообщение this и попробовал AudioStreamBasicОписание, описанное там. И попытался сменить флаги на Маленький или Большой Эндиан. Это не сработало.
Я посмотрел на некоторые другие сообщения здесь и в других ресурсах при поиске аналогичной проблемы, например, я проверил порядок моих данных PCM. Я просто не могу разместить более двух ссылок.

Пожалуйста, помогите мне понять, что я делаю неправильно! Может быть, я должен отказаться от этого способа и сразу использовать аудиоустройства? Я просто очень новичок в CoreAudio и надеюсь, что средний уровень CoreAudio поможет мне решить эту проблему.

P.S. Извините за мой английский, я старался как могу.

+0

Как выглядит 'MyFindQueueBuffer'? – sbooth

+0

просто так: 'INT MyFindQueueBuffer (MyData * MyData, AudioQueueBufferRef InBuffer) { для (неподписанных INT I = 0; я audioQueueBuffer [я]) return i; } return -1; } ' – foobar

+0

извините, перепутал с linebreaks%) – foobar

ответ

2

Я надеюсь, что вы уже решили этот по своему усмотрению, но на благо других людей, которые испытывают эту проблему, я отправлю ответ.

Проблема, скорее всего, из-за того, что, как только запускается звуковая очередь, время продолжает движение вперед, даже если вы остановите блокировку буферов. Но когда вы устанавливаете буфер, он помещается в очередь с отметкой времени, которая находится сразу после ранее установленного буфера. Это означает, что если вы не останетесь впереди того, где играет звуковая очередь, в прошлом вы окажетесь в очереди на буферов с меткой времени, поэтому звуковая очередь отключится, и свойство isRunning по-прежнему будет истинным.

Чтобы обойти это, у вас есть несколько вариантов. Простейшим в теории было бы никогда не отставать при подаче буферов. Но поскольку вы используете UDP, нет никакой гарантии, что у вас всегда будут данные для отправки.

Другой вариант заключается в том, что вы можете отслеживать, какой образец вы должны играть, и отправлять пустой буфер молчания всякий раз, когда вам нужно иметь пробел. Этот параметр работает хорошо, если ваши исходные данные имеют отметки времени, которые вы можете использовать, чтобы рассчитать, сколько тишины вам нужно. Но в идеале вам не нужно это делать.

Вместо этого вы должны рассчитывать временную метку для буфера с использованием системного времени.Вместо AudioQueueEnqueueBuffer вам нужно будет использовать AudioQueueEnqueueBufferWithParameters. Вам просто нужно убедиться, что временная метка впереди, где находится очередь. Вам также нужно будет отслеживать, какое системное время было при запуске очереди, поэтому вы можете рассчитать правильную метку времени для каждого отправляемого буфера. Если у вас есть значения временной отметки для исходных данных, вы также сможете использовать их для вычисления временных меток буфера.