标题几乎总结了我正在努力实现的目标。我正在尝试在渲染回调中使用Michael Tyson的TPCircularBuffer,而循环缓冲区正在填充传入的音频数据。我想将渲染回调中的音频发送到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,但我想知道是否有人可以建议我在这里尝试做什么的解决方案。谢谢!
答案 0 :(得分:1)
对于立体声(每帧2个通道),每帧的字节数和每个数据包的字节数必须是样本大小的两倍(以字节为单位)。与每个通道的位数相同。
补充:如果availableBytes / yourFrameSize几乎不总是大于或者大于inNumberFrames,那么你将无法获得更多的连续声音。
答案 1 :(得分:0)
乍一看,看起来你已经正确设置了一切。你错过了对AudioOutputUnitStart()
的电话:
...
// returns an OSStatus indicating success / fail
AudioOutputUnitStart(remoteIOUnit);
// now your callback should be being called
...
答案 2 :(得分:0)
我相信您的问题在于使用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和status的返回值。您仍然需要在某些调用中添加错误检查,例如
checkMyReturnValueToo = AudioComponentInstanceNew(defaultOutput, &remoteIOUnit);
您的缓冲持续时间也具有极高的值。你有46,我不知道它来自哪里。这意味着您在每次音频回调期间需要46秒的音频。通常,您需要不到一秒的时间,具体取决于您的延迟要求。很可能iOS不会使用任何高,但你应该尝试设置为0.025左右(25毫秒)。如果您需要更快的延迟,可以尝试降低它。