0

私はiPhone用のVOIPベースのアプリを書いています。ユーザーが画面を押したときに音声に不具合が発生するという奇妙な問題が発生しています。これは、電話自体の音量アップ/ダウンボタンを押したときにも発生します。数日間のデバッグの後、循環バッファと関係があることがわかりました。私はここのものと私のものを交換しました:

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

これはグリッチを引き起こしませんが、レイテンシーは私のもののほぼ4倍長いので、レイテンシーを最小限に抑える必要があり、アプリで何が起こっているのか理解できません。

設定:

http://www.stefanpopp.de/2011/capture-iphone-microphone/基本的なアプリを作成するためにいくらかフォローしましたが、設定や機能などが異なります。このaudioProcessorクラスのプロパティを持つViewControllerがあります。このクラスには、循環バッファー用の変数があります。記録コールバックでデータを送信しますが、これはすべて問題ありません。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]);
}

Audio Unitの再生-バッファからデータをコピーし、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;
}

バッファープロデューサー/コンシューマー:

これは、関数へのポインターを渡してから、このポインターにデータが入力されるように記述されています。データがない場合、ポインターには無音のALAW16進値が入力されます。これにより、オーディオユニットのコードが小さく保たれます。これは、バッファが常にデータを提供することを保証するためです。これはまた、一時的な場所にコピーしてから、上記のリンクが使用するバッファにそれを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;
        }
    }
}
}
4

1 に答える 1

2

Okは、トリックを実行したように見える別のスタイルの循環バッファーを作成しました。レイテンシーは非常に似ており、グリッチはありません。なぜこれが優れているのかまだ完全には理解していません。これに経験のある人は誰でも共有してください。

アップルによって投稿されたものはほとんどないため、以下は私のVOIPセットアップでうまく機能する循環バッファの実装です。自由に使用してください。提案は大歓迎です。そうでない場合は私を追いかけないでください。あなたのために働きます。今回はObjective-cクラスです。

これは、linearPCMではなく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
于 2013-01-10T15:06:09.687 に答える