这是上下文: 我已经开发一个与音频相关的应用程序一段时间了,我有点碰壁,不知道下一步该怎么做。
我最近在应用程序中实现了一个自定义类,该类绘制音频输出的 FFT 显示。此类是 UIView 的一个子类,这意味着每次我需要绘制新的 FFT 更新时,我都需要使用新的示例值在类的实例上调用setNeedDisplay
。
由于我需要为每一帧(帧 ~= 1024 个样本(绘制一个新的 FFT,这意味着我的 FFT 的显示函数被调用了很多(1024/采样率 ~= 0.02321 秒(。至于样本计算,它是44'100/秒完成的。我在 iOS 中管理线程方面没有真正的经验,所以我读了一些关于它的信息,这是我是如何做到的。
它是如何完成的:我有一个NSObject"AudioEngine.h"的子类,它负责我的应用程序中的所有DSP处理,这是我设置FFT显示的地方。所有样本值都被计算并分配给dispatch_get_global_queue
块内的FFT子类,因为这些值需要在后台不断更新。一旦样本索引达到最大帧数,就会调用 setneedDisplay
方法,这是在 dispatch_async(dispatch_get_main_queue)
块内完成
在"音频引擎.m"中
for (k = 0; k < nchnls; k++) {
buffer = (SInt32 *) ioData->mBuffers[k].mData;
if (cdata->shouldMute == false) {
buffer[frame] = (SInt32) lrintf(spout[nsmps++]*coef) ;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@autoreleasepool {
// FFT display init here as a singleton
SpectralView *specView = [SpectralView sharedInstance];
//Here is created a pointer to the "samples" property of my subclass
Float32 *specSamps = [specView samples];
//set the number of frames the FFT should take
[specView setInNumberFrames:inNumberFrames];
//scaling sample values
specSamps[frame] = (buffer[frame] * (1./coef) * 0.5);
}
});
} else {
// If output is muted
buffer[frame] = 0;
}
}
//once the number of samples has reached ksmps (vector size) we update the FFT
if (nsmps == ksmps*nchnls){
dispatch_async(dispatch_get_main_queue(), ^{
SpectralView *specView = [SpectralView sharedInstance];
[specView prepareToDraw];
[specView setNeedsDisplay];
});
我的问题是什么:
- 我遇到了各种线程问题,尤其是在主线程(例如
Thread 1: EXC_BAD_ACCESS (code=1, address=0xf00000c)
(上,有时在调用 viewDidLoad 时在应用程序启动时,但每当我尝试与任何 UI 对象交互时也是如此。 - 即使在 FFT 显示器上,UI 响应速度也会变得非常慢。
我认为问题是:正如您可能知道的那样,它肯定与线程问题有关,但我对这个主题真的没有经验。我想过也许在主线程上强制任何 UI 显示更新,以解决我遇到的问题,但又一次;我什至不确定如何正确地做到这一点。
任何输入/见解都将是一个巨大的帮助。提前感谢!
如前所述,您的SpectralView*
需要完全线程安全。
您的for()
循环首先将帧/样本处理推送到高优先级并发队列。 由于这是异步的,它将立即返回,此时您的代码将对主要威胁的请求进行排队,以更新光谱视图的显示。
同时更新显示,后台处理代码也会更新光谱视图的状态。
还有第二个问题; 您的代码最终将并行处理所有通道。 通常,未缓解的并发性会导致性能降低。 此外,您将在每个通道的主线程上引起更新,无论该通道的处理是否完成。
代码需要重新构建。 您确实应该将模型层与视图层分开。 可以将模型层写入为线程安全,也可以在处理过程中获取要显示的数据的快照并将其扔到SpectralView上。 或者,您的模型图层可以有一个isProcessing
标志,SpectralView 可以关闭该标志,以了解它不应该读取数据。
这是相关的:
https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html