iOS UI导致​​我的音频流出现故障 [英] iOS UI are causing a glitch in my audio stream

查看:91
本文介绍了iOS UI导致​​我的音频流出现故障的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为iPhone编写一个基于VOIP的应用程序.我遇到了一个奇怪的问题,即当用户按下屏幕时,音频中会有一个小故障,当您按下手机本身上的提高/降低音量按钮时,也会发生这种情况.经过几天的调试,我发现它与循环缓冲区有关.我在这里换了我的:

I'm writing a VOIP based app for the iPhone. I'm having a strange issue where when the user presses the screen there is a glitch in the audio, this happens also when you press the volume up / down buttons on the phone itself. After days of debugging I've found its something to do with my circular buffer. I swapped mine for the one here:

http://atastypixel.com /blog/一个用于音频处理的简单快速循环缓冲区实现/

这不会引起故障,但是延迟是我的延迟的四倍,我必须将延迟降至最低,并且无法弄清我的应用程序正在发生什么.

this one doesn't cause a glitch but the latency is nearly 4 times longer than mine, I have to have minimal latency and can't figure out whats going on with my app.

设置:

我关注了: http://www.stefanpopp.de/2011/capture- iphone-microphone/在某种程度上可以制作基本的应用程序,但是我具有不同的设置/功能等.我有一个具有此audioProcessor类属性的视图控制器,该类具有用于循环缓冲区的变量.在录音回调中,我发送了数据,这一切都很好.在CFSocket回调中,我将网络中的数据添加到此缓冲区中,然后回放回调从该缓冲区中提取数据并将其传递给系统.

I followed: http://www.stefanpopp.de/2011/capture-iphone-microphone/ somewhat to make the basic app but I have different settings / functions etc. I have a view controller that has a property of this audioProcessor class, this class has a variable for the circular buffer. In the recording callback I send the data, this is all fine. In a CFSocket callback I add data from the network to this buffer and then the playback callback pulls data from this buffer and passes it to the system.

在播放过程中的某个时刻,如果用户按下会触发UI事件,这一切都会陷入地狱,并且这些奇怪的数据会显示出来.我猜想它有某种线程问题,但是我在这方面经验很少或没有经验.我将不胜感激.这是相关代码:

At some point during the playback if the user presses triggers a UI event it all goes to hell and this strange data shows up. I'm guessing its some sort of threading issue but I have little or no experience in this area. I'd appreciate any help. Here is the relative code:

网络回调-将数据添加到缓冲区:

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十六进制值以保持静音.这样可以使音频单元代码保持较小,因为缓冲区可确保始终向其提供数据.这比复制到临时位置然后将其内存到上面的链接所使用的缓冲区中要快得多,而对于我的需求而言,它要慢得多.

This is written so that you pass in a pointer to the function and this pointer is then filled with the data, should there be no data the pointer will be filled with the ALAW hex value for silence. This keeps the audio unit code small as the buffer ensures that it will always supply it with data. This is also mush faster than copying to somewhere temporary and then memcpy 'ing it into the buffer which the link above uses and is far to slow for my needs.

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

推荐答案

Ok编写了另一种样式的循环缓冲区,似乎可以解决问题,非常相似的延迟并且没有毛刺.我仍然不完全理解为什么这样做会更好,任何有经验的人都请分享.

Ok wrote a different style of Circular buffer that seems to have done the trick, very similar latency and no glitching. I still don't fully understand why this is better, any one with experience in this please share.

由于这些东西很少被苹果发布,下面是我的循环缓冲区实现,可以很好地与我的VOIP设置配合使用,随时使用它,欢迎提出任何建议,只是不要跟着我来不适用于您.这次是Objective-C课程.

Due to very little of this stuff being posted on by apple, below is my Circular buffer implementation that works well with my VOIP setup, feel free to use it, any suggestions are welcome, just don't come after me if it doesn't work for you. This time its an objective-c class.

请注意,这是设计用于ALAW格式而不是linearPCM的,"0xd5"是ALAW中的一个静音字节,不确定PCM中会是什么,但希望它是噪声.

Please note that this was designed to use with ALAW format not linearPCM, "0xd5" is a byte of silence in ALAW, unsure what this will be in PCM but would expect it to be noise.

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

这篇关于iOS UI导致​​我的音频流出现故障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆