2013-01-04 41 views
0

我正在爲iPhone編寫基於VOIP的應用程序。我遇到了一個奇怪的問題,當用戶按下屏幕時音頻出現故障,當您按下電話本身的音量增大/減小按鈕時,也會發生這種情況。經過幾天的調試,我發現它與我的循環緩衝區有關。我換礦的一個位置:iOS UI導致​​我的音頻流出現故障

http://atastypixel.com/blog/a-simple-fast-circular-buffer-implementation-for-audio-processing/

這一個不造成干擾,但等待時間幾乎比我的長4倍,我必須有最小的延遲和無法弄清楚怎麼回事與我的應用程序。

設置:

我跟着:http://www.stefanpopp.de/2011/capture-iphone-microphone/稍微使基本應用程序,但我有不同的設置/功能等我有了這個SC7314音頻處理器類的特性的視圖控制器,這個類具有用於可變循環緩衝區。在錄音回調中,我發送數據,這很好。在CFSocket回調中,我將數據從網絡添加到此緩衝區,然後回放回調從此緩衝區中提取數據並將其傳遞到系統。

在回放過程中的某個時刻,如果用戶按下觸發UI事件,它將全部進入地獄,這種奇怪的數據顯示出來。我猜測它是一種線程問題,但我在這方面很少或沒有經驗。我會很感激任何幫助。這裏是相對的代碼:

網絡回調 - 將數據添加到緩衝液:

static void addDataToBuffer(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) 
{ 
    AudioUnitCBufferProduce(&audioProcessor->auCBuffer, (uint8_t*)[(__bridge NSData *)data bytes], [(__bridge NSData *)data length]); 
} 

音頻單元重放 - 複製數據形成緩衝和放置到「targetBuffer」,其指向ioData:

static OSStatus playbackCallback(void *inRefCon, 
          AudioUnitRenderActionFlags *ioActionFlags, 
          const AudioTimeStamp *inTimeStamp, 
          UInt32 inBusNumber, 
          UInt32 inNumberFrames, 
          AudioBufferList *ioData) 
{ 

    uint8_t *targetBuffer = (uint8_t*)ioData->mBuffers[0].mData; 
    AudioUnitCBufferConsume(&audioProcessor->auCBuffer, targetBuffer, inNumberFrames); 

    return noErr; 
} 

緩衝區的init:

void AudioUnitCBufferInit(AudioUnitCBuffer *b) 
{ 
    // create array of bytes of length specified, fill with silence 
    uint8_t buffer[2048]; 

    for(int i = 0; i < 2048; i++) 
    { 
     buffer[i] = 0xd5; 
    } 

    // init buffer elements 
    b->buffer = buffer; 
    b->consumer = buffer; 
    b->producer = buffer; 
    b->length = 2048; 
} 

緩衝區生產者/消費者:

這是這麼寫的,你在一個函數指針傳遞,然後該指針被填充數據,應該有沒有數據的指針將充滿ALAW十六進制值用於靜音。這樣可以使音頻單元代碼保持較小,因爲緩衝區確保它始終爲其提供數據。這比複製到臨時的某個地方更快,然後memcpy將其存儲到上面鏈接使用的緩衝區中,並且速度遠遠低於我的需要。

inline static void AudioUnitCBufferProduce(AudioUnitCBuffer *b, uint8_t *bytes, int32_t len) 
{ 
//printf("\n\ninside producer: len %i \n\n", len); 
while(len--) 
{ 
    // if producer catches up with consumer, skip a byte 
    if (b->producer+1 == b->consumer) 
    { 
     //printf("b->producer+1 == b->consumer == continue \n"); 
     continue; 
    } 
    else 
    { 
     //printf("b->producer+1 != b->consumer == add byte \n"); 
     *b->producer = *bytes++; 
     b->producer++; 

     if(b->producer == &b->buffer[b->length-1]) 
     { 
      //printf("\n\nproducer == end, skipping \n\n"); 
      b->producer = b->buffer; 
     } 
    } 
} 
} 

inline static void AudioUnitCBufferConsume(AudioUnitCBuffer *b, uint8_t *bytes, int32_t len) 
{ 
while(len--) 
{ 
    // if producer catches up with consumer, skip a byte 
    if (b->consumer == b->producer) 
    { 
     *bytes++ = 0xd5; 
    } 
    else 
    { 
     *bytes++ = *b->consumer; 
     b->consumer++; 

     if(b->consumer == &b->buffer[b->length-1]) 
     { 
      b->consumer = b->buffer; 
     } 
    } 
} 
} 
+0

是您的插座回調安排在哪個線程?嘗試後臺線程。 – ZhangChn

+0

使用GCDAsync我讓它在後臺線程上運行,沒有任何變化。使用它的cf主要運行。這點雖然它工作正常,如果我在鏈接中使用循環緩衝區意味着它不是網絡。只有那個緩衝區的延遲差不多是4倍 –

+0

你是如何確定你的循環緩衝區大小的?它是否大於N個傳入數據包(對於某些N),並預先填充以允許到達時間抖動?如何處理接收音頻採樣率與發送音頻採樣率或網絡數據率略有不同?由於音頻採樣率時鐘不同步,速度更快還是更慢? – hotpaw2

回答

2

好吧,寫了一個不同風格的循環緩衝區,似乎已經完成了這個訣竅,非常類似的延遲和沒有毛刺。我仍然不完全明白爲什麼這會更好,任何有經驗的人都請分享一下。

由於蘋果發佈的這些東西很少,下面是我的循環緩衝區實現,可以很好地與我的VOIP設置一起使用,隨時可以使用它,歡迎任何建議,只是不要跟在我後面if它不適合你。這次它是一個Objective-C類。

請注意,這是設計用於與ALAW格式不linearPCM,「0xd5」是ALAW中的一個沉默字節,不確定這將在PCM中,但會期望它是噪聲。

CircularBuffer.h:

// 
// CircularBuffer.h 
// clevercall 
// 
// Created by Simon Mcloughlin on 10/1/2013. 
// 
// 

#import <Foundation/Foundation.h> 

@interface CircularBuffer : NSObject 

-(int) availableBytes; 
-(id) initWithLength:(int)length; 
-(void) produceToBuffer:(const void*)data ofLength:(int)length; 
-(void) consumeBytesTo:(void *)buf OfLength:(int)length; 

@end 

CircularBuffer.m:

// 
// CircularBuffer.m 
// clevercall 
// 
// Created by Simon Mcloughlin on 10/1/2013. 
// 
// 

#import "CircularBuffer.h" 

@implementation CircularBuffer 
{ 
    unsigned int gBufferLength; 
    unsigned int gAvailableBytes; 
    unsigned int gHead; 
    unsigned int gTail; 
    void *gBuffer; 
} 

// Init instance with a certain length and alloc the space 
-(id)initWithLength:(int)length 
{ 
    self = [super init]; 

    if (self != nil) 
    { 
     gBufferLength = length; 
     gBuffer = malloc(length); 
     memset(gBuffer, 0xd5, length); 

     gAvailableBytes = 0; 
     gHead = 0; 
     gTail = 0; 
    } 

    return self; 
} 

// return the number of bytes stored in the buffer 
-(int) availableBytes 
{ 
    return gAvailableBytes; 
} 

-(void) produceToBuffer:(const void*)data ofLength:(int)length 
{ 
    // if the number of bytes to add to the buffer will go past the end. 
    // copy enough to fill to the end 
    // go back to the start 
    // fill the remaining 
    if((gHead + length) > gBufferLength-1) 
    { 
     int remainder = ((gBufferLength-1) - gHead); 
     memcpy(gBuffer + gHead, data, remainder); 
     gHead = 0; 
     memcpy(gBuffer + gHead, data + remainder, (length - remainder)); 
     gHead += (length - remainder); 
     gAvailableBytes += length; 
    } 
    // if there is room in the buffer for these bytes add them 
    else if((gAvailableBytes + length) <= gBufferLength-1) 
    { 
     memcpy(gBuffer + gHead, data, length); 
     gAvailableBytes += length; 
     gHead += length; 
    } 
    else 
    { 
     //NSLog(@"--- Discarded ---"); 
    } 
} 

-(void) consumeBytesTo:(void *)buf OfLength:(int)length 
{ 
    // if the tail is at a point where there is not enough between it and the end to fill the buffer. 
    // copy out whats left 
    // move back to the start 
    // copy out the rest 
    if((gTail + length) > gBufferLength-1 && length <= gAvailableBytes) 
    { 
     int remainder = ((gBufferLength-1) - gTail); 
     memcpy(buf, gBuffer + gTail, remainder); 
     gTail = 0; 
     memcpy(buf + remainder, gBuffer, (length -remainder)); 
     gAvailableBytes-=length; 
     gTail += (length -remainder); 
    } 
    // if there is enough bytes in the buffer 
    else if(length <= gAvailableBytes) 
    { 
     memcpy(buf, gBuffer + gTail, length); 
     gAvailableBytes-=length; 
     gTail+=length; 
    } 
    // else play silence 
    else 
    { 
     memset(buf, 0xd5, length); 
    } 
} 

@end