繁体   English   中英

在附加到remoteio音频单元输入范围的渲染回调中使用循环缓冲区中的音频数据

[英]consuming audio data from circular buffer in a render callback attached to the input scope of a remoteio audio unit

标题几乎总结了我要达到的目标。 我试图在循环缓冲区中充满传入音频数据的同时,在渲染回调中使用Michael Tyson的TPCircularBuffer。 我想将音频从render回调发送到RemoteIO音频单元的输出元素,以便可以通过设备扬声器听到它。

音频是交错的立体声16位,以2048帧的数据包的形式出现。 这是我设置音频会话的方法:

#define kInputBus 1
#define kOutputBus 0
NSError *err = nil;
NSTimeInterval ioBufferDuration = 46;
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&err];
[session setPreferredIOBufferDuration:ioBufferDuration error:&err];
[session setActive:YES error:&err];
AudioComponentDescription defaultOutputDescription;
defaultOutputDescription.componentType = kAudioUnitType_Output;
defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
defaultOutputDescription.componentFlags = 0;
defaultOutputDescription.componentFlagsMask = 0;

AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
NSAssert(defaultOutput, @"Can't find default output.");

AudioComponentInstanceNew(defaultOutput, &remoteIOUnit);
UInt32 flag = 0;

OSStatus status = AudioUnitSetProperty(remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kOutputBus, &flag, sizeof(flag));
size_t bytesPerSample = sizeof(AudioUnitSampleType);
AudioStreamBasicDescription streamFormat = {0};
streamFormat.mSampleRate = 44100.00;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagsCanonical;
streamFormat.mBytesPerPacket = bytesPerSample;
streamFormat.mFramesPerPacket = 1;
streamFormat.mBytesPerFrame = bytesPerSample;
streamFormat.mChannelsPerFrame = 2;
streamFormat.mBitsPerChannel = bytesPerSample * 8;
streamFormat.mReserved = 0;

status = AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &streamFormat, sizeof(streamFormat));

AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = render;
callbackStruct.inputProcRefCon = self;
status = AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct));

这是将音频数据加载到循环缓冲区中并在渲染回调中使用的位置:

#define kBufferLength 2048
-(void)loadBytes:(Byte *)byteArrPtr{
TPCircularBufferProduceBytes(&buffer, byteArrPtr, kBufferLength);
}

OSStatus render(
                void *inRefCon,
                AudioUnitRenderActionFlags *ioActionFlags,
                const AudioTimeStamp *inTimeStamp,
                UInt32 inBusNumber,
                UInt32 inNumberFrames,
                AudioBufferList *ioData)
{
AUDIOIO *audio = (__bridge AUDIOIO *)inRefCon;
AudioSampleType *outSample = (AudioSampleType *)ioData->mBuffers[0].mData;
//Zero outSample
memset(outSample, 0, kBufferLength);
int bytesToCopy = ioData->mBuffers[0].mDataByteSize;
SInt16 *targetBuffer = (SInt16 *)ioData->mBuffers[0].mData;
//Pull audio
int32_t availableBytes;
SInt16 *buffer = TPCircularBufferTail(&audio->buffer, &availableBytes);
memcpy(targetBuffer, buffer, MIN(bytesToCopy, availableBytes));
TPCircularBufferConsume(&audio->buffer, MIN(bytesToCopy, availableBytes));
return noErr;
}

此设置存在问题,因为我没有通过扬声器获得任何音频,但是在设备上进行测试时也没有出现任何错误。 据我所知,TPCircularBuffer正在正确填充和读取。 我已按照Apple文档中的说明设置了音频会话。 我正在考虑接下来尝试设置AUGraph,但我想看看是否有人可以为我在这里尝试的工作提出解决方案。 谢谢!

对于立体声(每帧2个通道),每帧的字节数和每个数据包的字节数必须是样本大小(以字节为单位)的两倍。 与每个通道的位数相同。

补充:如果availableBytes / yourFrameSize几乎不总是比inNumberFrames大或大,那么您将不会得到连续的声音。

乍一看,您似乎已经正确设置了所有内容。 但是,您缺少对AudioOutputUnitStart()的调用:

...
// returns an OSStatus indicating success / fail
AudioOutputUnitStart(remoteIOUnit);

// now your callback should be being called
...

我相信您的问题是使用streamFormat.mBitsPerChannel = bytesPerSample * 8;

您将bytesPerSample分配为sizeof(AudioUnitSampleType) ,它实际上是4个字节。

所以streamFormat.mBytesPerPacket = bytesPerSample; 还可以 但是赋值streamFormat.mBitsPerChannel = bytesPerSample * 8; 就是说您希望每个样本32位,而不是每个样本16位。

我不会基于AudioUnitSampleType创建您的音频格式,因为这与您要使用的个人格式无关。 我将创建定义并执行以下操作:

#define BITS_PER_CHANNEL 16
#define SAMPLE_RATE 44100.0
#define CHANNELS_PER_FRAME 2
#define BYTES_PER_FRAME CHANNELS_PER_FRAME * (BITS_PER_CHANNEL / 8)  //ie 4
#define FRAMES_PER_PACKET 1
#define BYTES_PER_PACKET FRAMES_PER_PACKET * BYTES_PER_FRAME




    streamFormat.mSampleRate = SAMPLE_RATE;  // 44100.0
    streamFormat.mBitsPerChannel = BITS_PER_CHANNEL; //16
    streamFormat.mChannelsPerFrame = CHANNELS_PER_FRAME; // 2
    streamFormat.mFramesPerPacket = FRAMES_PER_PACKET; //1
    streamFormat.mBytesPerFrame = BYTES_PER_FRAME; // 4 total,  2 for left ch,  2 for right ch

    streamFormat.mBytesPerPacket = BYTES_PER_PACKET;

    streamFormat.mReserved = 0;
    streamFormat.mFormatID = kAudioFormatLinearPCM;  // double check this also
    streamFormat.mFormatFlags = kAudioFormatFlagsCanonical;`

您还需要在每次运行后立即查看设置为err和状态的返回值。 您仍然需要在某些调用中添加错误检查,例如

checkMyReturnValueToo = AudioComponentInstanceNew(defaultOutput, &remoteIOUnit);

您的缓冲持续时间也具有极高的价值。 你有46岁,我不确定那是哪里来的。 这意味着在每个音频回调过程中,您需要46秒的音频。 通常,您需要少于一秒钟的时间,具体取决于您的延迟要求。 iOS很可能不会使用任何高电平,但您应尝试将其设置为0.025左右(25毫秒)。 如果需要更快的延迟,可以尝试降低它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM