2013-01-04 2 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/ 다소 기본적인 응용 프로그램을 만드는하지만 난 등 다른 설정/기능 나는이 audioProcessor 클래스의 속성을 가진 뷰 컨트롤러가있는이 ​​클래스는 대한 변수를 가지고 순환 버퍼. 녹음 콜백에서 데이터를 보내면 괜찮습니다. 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]); 
} 

오디오 유닛 재생 - 복사 데이터 ioData 가리키는 "targetBuffer"로 버퍼 배치를 형성은 :

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; 
} 

버퍼 초기화 :

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 16 진수 값 이것은 버퍼가 항상 데이터를 공급할 수 있도록 오디오 유닛 코드를 작게 유지합니다. 이것은 또한 일시적으로 어딘가에 복사하는 것보다 빠르게 더 빨라지고 위의 링크가 사용하는 버퍼에 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

Ok는 트릭을 완료 한 것으로 보이는 다른 유형의 순환 버퍼를 작성했으며 지연 시간은 매우 짧으며 글리치가 발생하지 않았습니다. 나는 아직도 이것이 왜 더 나은지 완전히 이해하지 못한다.

apple에서 게시하는 내용이 거의 없기 때문에 내 VOIP 설정과 잘 작동하는 Circular 버퍼 구현을 사용해 자유롭게 사용할 수 있으며 모든 제안을 환영합니다. 그것은 당신을 위해 작동하지 않습니다. 이번에는 객관적인 - 클래스.

선형 PCM이 아닌 ALAW 형식으로 설계되었으며 "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 
관련 문제