核心音频:文件播放渲染回调函数

时间:2016-06-12 11:06:22

标签: ios callback core-audio audiounit

我正在使用 RemoteIO Audio Unit 在我的应用中使用kAudioUnitProperty_ScheduledFileIDs进行音频播放。 音频文件采用 PCM 格式。如何为这种情况实现渲染回调函数,所以我可以手动修改缓冲区样本? 这是我的代码:

static AudioComponentInstance audioUnit;

AudioComponentDescription desc;

desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;

AudioComponent comp = AudioComponentFindNext(NULL, &desc);

CheckError(AudioComponentInstanceNew(comp, &audioUnit), "error AudioComponentInstanceNew");


NSURL *playerFile = [[NSBundle mainBundle] URLForResource:@"short" withExtension:@"wav"];

AudioFileID audioFileID;

CheckError(AudioFileOpenURL((__bridge CFURLRef)playerFile, kAudioFileReadPermission, 0, &audioFileID), "error AudioFileOpenURL");

// Determine file properties
UInt64 packetCount;
UInt32 size = sizeof(packetCount);
CheckError(AudioFileGetProperty(audioFileID, kAudioFilePropertyAudioDataPacketCount, &size, &packetCount),
                "AudioFileGetProperty(kAudioFilePropertyAudioDataPacketCount)");

AudioStreamBasicDescription dataFormat;
size = sizeof(dataFormat);
CheckError(AudioFileGetProperty(audioFileID, kAudioFilePropertyDataFormat, &size, &dataFormat),
                "AudioFileGetProperty(kAudioFilePropertyDataFormat)");

// Assign the region to play
ScheduledAudioFileRegion region;
memset (&region.mTimeStamp, 0, sizeof(region.mTimeStamp));
region.mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
region.mTimeStamp.mSampleTime = 0;
region.mCompletionProc = NULL;
region.mCompletionProcUserData = NULL;
region.mAudioFile = audioFileID;
region.mLoopCount = 0;
region.mStartFrame = 0;
region.mFramesToPlay = (UInt32)packetCount * dataFormat.mFramesPerPacket;
CheckError(AudioUnitSetProperty(audioUnit, kAudioUnitProperty_ScheduledFileRegion, kAudioUnitScope_Global, 0, &region, sizeof(region)),
                "AudioUnitSetProperty(kAudioUnitProperty_ScheduledFileRegion)");

// Prime the player by reading some frames from disk
UInt32 defaultNumberOfFrames = 0;
CheckError(AudioUnitSetProperty(audioUnit, kAudioUnitProperty_ScheduledFilePrime, kAudioUnitScope_Global, 0, &defaultNumberOfFrames, sizeof(defaultNumberOfFrames)),
                "AudioUnitSetProperty(kAudioUnitProperty_ScheduledFilePrime)");

AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = MyCallback;
callbackStruct.inputProcRefCon = (__bridge void * _Nullable)(self);

CheckError(AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callbackStruct, sizeof(callbackStruct)), "error AudioUnitSetProperty[kAudioUnitProperty_setRenderCallback]");

CheckError(AudioUnitInitialize(audioUnit), "error AudioUnitInitialize");

回调功能:

static OSStatus MyCallback(void *inRefCon,
                             AudioUnitRenderActionFlags *ioFlags,
                             const AudioTimeStamp *inTimeStamp,
                             UInt32 inBusNumber,
                             UInt32 inNumberFrames,
                             AudioBufferList *ioData){

    printf("my callback");  
    return noErr;
}

音频单元按下按钮开始播放:

- (IBAction)playSound:(id)sender {

 CheckError(AudioOutputUnitStart(audioUnit), "error AudioOutputUnitStart");

}

在使用 kAudioUnitErr_InvalidProperty ( - 10879)错误进行编译时,此代码失败。目标是修改已从 AudioFileID 读取的缓冲区样本,并将结果发送给扬声器。

1 个答案:

答案 0 :(得分:1)

看到你如何熟悉核心音频,我建议你首先让你的remoteIO回调独立于你的文件播放器。只需删除所有与文件播放器相关的代码,然后尝试先使用它。

然后,一旦你有了工作,继续合并你的文件播放器。

据我所知,这是错误的,我认为你将音频文件服务API与音频单元混淆。此API用于将文件读入缓冲区,您可以手动将其输入到remoteIO,如果您想要使用此路由,请使用扩展音频文件服务API,这样会更容易。应该在文件播放器音频单元上调用kAudioUnitProperty_ScheduledFileRegion属性。要获得其中一个,您需要以与remmoteIO相同的方式创建它,但AudioComponentDescription的componentSubType和componentType分别为kAudioUnitSubType_AudioFilePlayerkAudioUnitType_Generator。然后,一旦拥有该单元,您将需要使用kAudioUnitProperty_MakeConnection属性将其连接到remoteIO。

但严重的是,首先让你的remoteIO回调工作,然后尝试制作文件播放器音频单元并连接它(没有回调),然后从那里开始。

独立询问有关这些步骤的非常具体的问题,发布您尝试过的代码不起作用,您将获得大量帮助。