我目前正在爲音頻流編寫一個嵌入式應用程序。嵌入式應用程序將接收通過wifi發送的音頻數據包,緩衝數據包,然後將音頻數據發送到解碼器芯片。我寫了一個環形緩衝區實現(在stackoverflow中有一個很好的帖子幫助),但是有時會出現一些奇怪的行爲。在音頻方面,我聽到播放過程中重複播放歌曲的某些部分。我發現這是由於尾指針被設置爲兩次緩衝區的開始。C尾指針環形緩衝區問題(用於音頻流)
- 頭指針復位啓動:
例如,我看到(我在執行中,頭指針,而尾指針標記有效數據的開始,標誌着有效數據的結束)緩衝區的
- 尾指針復位啓動緩衝的
- 尾指針復位啓動緩衝<的 - 這是我聽到的聲音重複
- 頭指針復位啓動緩衝
這裏是環形緩衝區實現:
typedef struct ring_buffer
{
UINT8 *buffer; /* data buffer */
UINT8 *buffer_end; /* end of data buffer */
size_t capacity; /* maximum number of mp3Bytes in the buffer */
size_t count; /* number of mp3Bytes in the buffer */
size_t typesize; /* size of each mp3Byte in the buffer */
UINT8 *head; /* ring buffer head pointer */
UINT8 *tail; /* ring buffer tail pointer */
} ring_buffer;
PUBLIC UINT8
AppAudioStream_RingBufInit(ring_buffer *rb, size_t capacity, size_t typesize)
{
/* alloc buffer of size capacity * typesize */
rb->buffer = malloc(capacity * typesize);
if(rb->buffer == NULL)
{
printf("ring buffer init fail\r\n");
return RING_BUF_INIT_FAIL;
}
/* init rb buffer to 0 */
memset(rb->buffer, 0, capacity * typesize);
/* rb struct element init */
rb->capacity = capacity;
rb->buffer_end = rb->buffer + capacity * typesize;
rb->count = 0;
rb->typesize = typesize;
rb->head = rb->buffer;
rb->tail = rb->buffer;
return RING_BUF_INIT_DONE;
}
PUBLIC VOID
AppAudioStream_RingBufWrite(ring_buffer *rb, UINT8 *mp3Byte)
{
/* default: allow overwriting if ring buffer is full */
memcpy(rb->head, mp3Byte, rb->typesize);
rb->head = rb->head + rb->typesize;
if(rb->head == rb->buffer_end) {
printf("head back to start\r\n");
rb->head = rb->buffer;
}
if(rb->count == rb->capacity) {
printf("buffer full\r\n");
if (rb->head > rb->tail)
rb->tail = rb->tail + rb->typesize;
} else {
rb->count++;
}
}
PUBLIC VOID
AppAudioStream_RingBufRead(ring_buffer *rb, UINT8 *mp3Byte)
{
/* insert 'comfort noise' if the ring buffer is empty */
if(rb->count == 0){
printf("buffer empty\r\n");
*mp3Byte = NOISE_BYTE;
} else {
/* copy data to mp3Byte and increase tail pointer */
memcpy(mp3Byte, rb->tail, rb->typesize);
rb->tail = rb->tail + rb->typesize;
if(rb->tail == rb->buffer_end) {
printf("TAIL back to start\r\n");
printf("Tbuffer count: %i\r\n", rb->count);
rb->tail = rb->buffer;
}
rb->count--;
}
}
這裏是環形緩衝區寫入功能是如何調用:
while (1)
{
AppAudioStream_BufRecv(sd, dataLen, &addr);
}
PUBLIC VOID
AppAudioStream_BufRecv(int sd, INT32 dataLen, struct sockaddr_in *addr)
{
INT32 addrlen = sizeof(struct sockaddr_in);
UINT8 j, i = 0;
UINT8 *audioByte;
/* listen to incoming audio data packets */
dataLen = recvfrom(sd, (char *) appRxBuf, sizeof(appRxBuf), 0,
(struct sockaddr *)&addr, &addrlen);
/* set pointer to first element in recieve buffer */
audioByte = appRxBuf;
/* buffer received packets into FIFO */
while (dataLen > 0)
{
/* write 1 byte into audio FIFO */
AppAudioStream_RingBufWrite(&audioFIFO, audioByte);
/* increase pointer index and update # of bytes left to write */
audioByte++;
dataLen--;
}
/* wait until buffer is 2/3 full to start decoding */
if (audioFIFO.count >= FIFO_TWO_THIRD_FULL
&& audioStreamStatus == GSN_STREAM_BUFFERING) {
audioStreamStatus = GSN_STREAM_START;
//printf("stream start\r\n");
}
}
環形緩衝區讀取函數在每2毫秒發生一次回調中調用(這基本上是一個ISR):
PRIVATE VOID
AppAudioStream_DecoderCb(UINT32* pDummy, UINT32 TimerHandle)
{
UINT8 spiWriteCount = 0;
UINT8 mp3Byte;
int i = 0;
GSN_SPI_NUM_T spiPortNumber = GSN_SPI_NUM_0;
/* read 32 bytes of data from FIFO and write to SPI */
while (spiWriteCount < DATA_WRITE_AMT)
{
/* set stream status to decoding */
audioStreamStatus = GSN_STREAM_DECODING;
/* read 1 byte of audio data from FIFO */
AppAudioStream_RingBufRead(&audioFIFO, &mp3Byte);
/* write 1 byte of audio data out to VS1053 */
AppSpi_SdiByteWrite(spiPortNumber, &mp3Byte);
/* increase byte written count */
spiWriteCount++;
}
}
任何幫助/見解非常感謝。我相信我現在只是忽略了一些非常明顯的東西。
謝謝!
要求陌生人通過檢查發現代碼中的錯誤並不是富有成效的。你應該使用調試器來隔離一個更簡單的[測試用例](http://sscce.org),然後再回來。 –
看起來像一個多線程應用程序,對吧?如果是這樣,你需要保護環形緩衝區。 – ugoren
@Suma不,它不。閱讀[代碼審查常見問題](http://codereview.stackexchange.com/faq)。 – Lundin