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

Posted

技术标签:

【中文标题】核心音频:文件播放渲染回调函数【英文标题】:Core audio: file playback render callback function 【发布时间】:2016-06-12 11:06:21 【问题描述】:

我正在使用 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 读取的缓冲区样本并将结果发送给扬声器。

【问题讨论】:

MyCallback() 在哪里? @user3078414,添加到UPD:部分 kAudioUnitErr_InvalidElement aka -10877 似乎是初始化错误,不是运行时错误,AFAIK。我不明白你说“其余代码工作正常”是什么意思。你的程序的“结构”是什么?您声明“使用 AudioUnits”。它是单个 AU 程序,还是您使用多个 AU?做什么的?如果是这样,您是显式连接它们还是使用AUGraph API? 你在哪个音频单元上设置渲染回调? 从代码看OP是在名为audioUnit,@Dave的音频单元上设置回调 【参考方案1】:

鉴于您刚刚熟悉核心音频,我建议您首先让您的 remoteIO 回调独立于您的文件播放器工作。只需删除所有与文件播放器相关的代码,然后先尝试使其正常工作。

然后,一旦您完成了这项工作,就可以继续整合您的文件播放器。

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

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

就这些步骤中的每一个单独提出非常具体的问题,发布您尝试过但不起作用的代码,您将获得大量帮助。

【讨论】:

非常感谢您指出正确的方向。你能看看这个问题吗? ***.com/questions/37870051/…。我连接了我的文件播放器和 RemoteIO 单元,我听到了扬声器的声音,但又卡在了回调函数上。提前致谢。

以上是关于核心音频:文件播放渲染回调函数的主要内容,如果未能解决你的问题,请参考以下文章

编写远程 I/O 渲染回调函数时遇到问题

Core Audio的渲染回调不会改变输出音频[重复]

waveOutOpen()API中的回调函数

什么会影响音频单元渲染回调周期(周期)?

从字节数组填充音频缓冲区并使用渲染回调播放

如何正确使用 iOS AudioUnit 渲染回调