iOS用户界面导致我的音频流出现故障
我正在为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类的属性,这个类有一个循环缓冲区的变量。 在录音回调中,我发送数据,这很好。 在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十六进制值填充以表示静默。 这样可以使音频单元代码保持较小,因为缓冲区确保它始终为其提供数据。 这比复制到临时的某个地方更快,然后memcpy将其存储到上面链接使用的缓冲区中,并且速度远远低于我的需要。
inline static void AudioUnitCBufferProduce(AudioUnitCBuffer *b, uint8_t *bytes, int32_t len)
{
//printf("nninside producer: len %i nn", 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("nnproducer == end, skipping nn");
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写了一个不同风格的循环缓冲区,似乎已经完成了这个诀窍,非常类似的延迟并且没有任何问题。 我仍然不完全明白为什么这会更好,任何有经验的人都请分享一下。
由于这些东西很少被苹果发布,下面是我的循环缓冲区实现,它适用于我的VOIP设置,随时使用它,欢迎任何建议,只是不要在我之后来,如果它不为你工作。 这次它是一个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
链接地址: http://www.djcxy.com/p/67023.html