OSX AudioUnit SMP
Posted
技术标签:
【中文标题】OSX AudioUnit SMP【英文标题】: 【发布时间】:2014-03-16 20:39:45 【问题描述】:我想知道是否有人有编写 HAL AudioUnit 渲染回调以利用多核处理器和/或对称多处理的经验?
我的情况如下:
子类型 kAudioUnitSubType_HALOutput 的单个音频组件(连同它的渲染回调)负责附加合成 n 个具有独立独立变化和实时更新幅度的正弦部分和相位值。它本身是一种相当简单的暴力嵌套循环方法(每部分、每帧、每通道)。
但是,当部分 "n" 的数量达到某个上限时,处理器会过载并开始产生丢失,而其他三个处理器保持空闲状态。
除了关于加法合成是“处理器昂贵”而不是“波表”的一般讨论之外,我需要知道这是否可以正确解决,这涉及利用多处理器或多处理器上的多处理核心机?将渲染线程分成子线程似乎不是正确的方法,因为渲染回调本身已经是一个时间约束线程,并且最终输出在延迟方面必须是样本精确的。有人在解决此类问题方面有过积极的经验和有效的方法吗?
系统:10.7.x
CPU:四核 i7
提前致谢,
加拿大
【问题讨论】:
【参考方案1】:这很有挑战性,因为 OS X 不是为这样的事情而设计的。有一个单一的音频线程——它是操作系统中最高优先级的线程,并且没有办法在这个优先级上创建用户线程(更不用说得到系统工程师团队的支持了,他们调整它的性能,就像音频渲染一样线)。我并没有声称了解您的算法的细节,但是如果可以将其分解以便可以在更大的样本块上并行执行某些任务(能够吸收偶尔的线程饥饿期),您当然可以产生其他并行处理的高优先级线程。您需要使用某种无锁数据结构在这些线程和音频线程之间交换样本。卷积混响通常这样做是为了允许合理的延迟,同时仍然在巨大的块大小上运行。我会研究这些是如何实现的......
【讨论】:
非常感谢。关于 OSX 中的音频线程优先级策略我很清楚(我发现它是 OSX 更好的概念之一),尽管我从未遇到过任何 Apple 文档说明单个音频线程被预谋硬编码为多 CPU环境不知情。这样做在战略上是不明智的。 我认为音频线程优先级功能的正确实现应该是系统范围的,而不是“lowest-count-cpu-limited”。就我的代码而言,不会从流中读取“更大的样本块”(例如在卷积混响中),每个 Float32 样本都必须在单个 AudioUnit 回调周期内以“inNumberFrames”块的形式显式生成.就数据处理而言,所有其他线程都无关紧要。 我们花了几个月的时间进行实验、学习和调试,才能够回顾并接受您的答案,认为这是最有用的。有许多涵盖学习曲线的后续行动。谢谢!【参考方案2】:您查看过Accelerate.framework 吗?您应该能够通过对向量执行操作而不是使用嵌套的 for 循环来提高效率。
如果您有正弦部分、幅度值和相位值的向量(长度为 n),您可以应用vDSP_vadd
或vDSP_vmul
操作,然后vDSP_sve
.
【讨论】:
感谢您的宝贵回答。在某个开发阶段,我已经认为 vDSP 是值得投入(igat)一些时间的东西,尽管我怀疑它能否显着击败我的优化代码,允许无延迟插值合成多达 1024 个时变正弦部分每个 CPU。还有其他时间问题,例如随时间变化的分音数量。我宁愿避免使用 AUGraphs 和 CARingBuffers。然而,如果 vDSP 可以应用在普通的 AU 渲染回调中,使线程能够感知多 CPU,那将会有所作为。有这方面的经验吗? 在没有看到代码的情况下很难说 vDSP 有多大帮助,但基本上,矢量化比串行执行更快。 vDSP 可以在任何地方使用,包括在 AU 渲染回调内部(使用 vDSP 不会导致您使用 AUGraph 或 CARingBuffer)。AURenderCallback
的 ioData
参数可以被视为一个向量,n 个正弦部分也可以。使用矢量样式函数应用您需要的任何操作:填充、求和、应用标量幅度或幅度向量等。【参考方案3】:
据我所知,AU 线程由主机处理。不久前,我尝试了几种使用各种方法(GCD、openCL 等)对 AU 渲染进行多线程处理的方法,它们要么不可行,要么不可预测。我相信有(或者至少 WAS ......我最近没有检查过)一个内置的 AU,称为“延迟渲染器”,它分别线程化输入和输出,但我似乎记得有延迟,所以可能没有帮助。
另外,如果您在 AULab 中进行测试,我相信它专门设置为仅调用单个线程(我认为仍然如此),因此您可能需要修补另一个测试主机以查看是否分配负载时它仍然会阻塞。
抱歉,我无法提供更多帮助,但我认为这几条信息可能会有所帮助。
【讨论】:
感谢您的回复 AlexKenis, 感谢您的回复 AlexKenis,为了避免误解,我一直在寻找编写 输出渲染回调 的方法,以便系统识别出多个处理器内核可用于完成所需的数字运算。或者以它自己进行这种 家务管理 的方式对其进行硬编码。另外,我相信我之前写过这个,在 AU 下我没有假定插件功能和 AULab 测试,而是 CoreAudio 低级 API。 【参考方案4】:很抱歉回答我自己的问题,否则我不知道如何添加一些相关信息。编辑似乎不起作用,评论太短了。 首先,衷心感谢 jtomschroeder 将我指向 Accelerate.framework。
这对于所谓的基于 IFFT 的重叠/相加再合成非常有效。然而,我还没有找到一个关键来对我正在使用的那种被称为“振荡器-组再合成”的过程进行矢量化,并且因其处理器税而臭名昭著(F.R. Moore:计算机音乐元素)。每个瞬时相位和幅度都必须“即时”内插,并将最后一个值存储到控制结构中以进行进一步内插。时间方向和时间拉伸取决于实时输入。并非所有部分都始终存在,断点的放置是任意的并且可能是不规则的。当然,我主要关心的是以一种最小化数学运算次数的方式组织数据......
如果有人能指出一个积极实践的例子,我将不胜感激。
//这里是简化的代码sn-p:
OSStatus AdditiveRenderProc(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
// local variables' declaration and behaviour-setting conditional statements
// some local variables are here for debugging convenience
// ... ... ...
// Get the time-breakpoint parameters out of the gen struct
AdditiveGenerator *gen = (AdditiveGenerator*)inRefCon;
// compute interpolated values for each partial's each frame
// deltaf[p]... ampf[p][frame]... ...
//here comes the brute-force "processor eater" (single channel only!)
Float32 *buf = (Float32 *)ioData->mBuffers[channel].mData;
for (UInt32 frame = 0; frame < inNumberFrames; frame++)
buf[frame] = 0.;
for(UInt32 p = 0; p < candidates; p++)
if(gen->partialFrequencyf[p] < NYQUISTF)
buf[frame] += sinf(phasef[p]) * ampf[p][frame];
phasef[p] += (gen->previousPartialPhaseIncrementf[p] + deltaf[p]*frame);
if (phasef[p] > TWO_PI) phasef[p] -= TWO_PI;
buf[frame] *= ovampf[frame];
for(UInt32 p = 0; p < candidates; p++)
//store the updated parameters back to the gen struct
//... ... ...
;
返回 noErr;
【讨论】:
以上是关于OSX AudioUnit SMP的主要内容,如果未能解决你的问题,请参考以下文章
混音器 AudioUnit 到 RemoteIO AudioUnit