以下是上下文: 我一直在开发一个与音频相关的应用程序一段时间了,我有点碰壁,不知道接下来该做什么。
我最近在应用程序中实现了一个自定义类,用于绘制音频输出的FFT显示。这个类是UIView的子类,这意味着每次我需要绘制一个新的FFT更新时,我需要使用新的样本值在类的实例上调用setNeedDisplay
。
由于我需要为每个帧绘制一个新的FFT(帧〜= 1024个样本),这意味着我的FFT的显示功能被大量调用(1024 / SampleRate~ = 0.02321秒)。至于样本计算,它是44'100 /秒。我在iOS中管理线程方面并不是很有经验,所以我稍微阅读了一下它,我就是这样做的。
如何完成:我有一个NSObject“AudioEngine.h”的子类,负责处理我应用程序中的所有DSP处理,这就是我设置FFT显示的地方。计算所有样本值并将其分配给dispatch_get_global_queue
块内的FFT子类,因为值需要在后台不断更新。样本索引达到最大帧数后调用setneedDisplay
方法,这在dispatch_async(dispatch_get_main_queue)
块内完成
在“AudioEngine.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对象进行交互时。 我认为问题是:这肯定与您可能知道的线程问题有关,但我对此主题并不熟悉。我想也许在主线程上强制任何UI显示更新,以便再次解决我遇到的问题;我甚至不确定如何正确地做到这一点。
任何输入/见解都将是一个巨大的帮助。 提前谢谢!
答案 0 :(得分:2)
如上所述,您的SpectralView*
需要完全线程安全。
您的for()
循环首先将帧/样本处理推送到高优先级并发队列。由于这是异步的,它将立即返回,此时您的代码将在主威胁上排队请求以更新光谱视图的显示。
这几乎可以保证光谱视图必须与背景处理代码同时更新显示,同时更新光谱视图的状态。
还有第二个问题;您的代码将最终并行化所有通道的处理。通常,未缓和的并发性是缓慢性能的一个因素。此外,无论是否完成该频道的处理,您都将在每个频道的主线程上进行更新。
代码需要重组。你真的应该从视图层拆分模型层。模型层可以写成线程安全的或,在处理过程中,您可以获取要显示的数据的快照并在SpectralView上抛出。或者,您的模型图层可以有一个isProcessing
标记,SpectralView可以关闭该标记以确定它不应该读取数据。
这是相关的: