iOS:在回调函数

时间:2016-02-08 22:33:42

标签: ios objective-c multithreading audio

以下是上下文:  我一直在开发一个与音频相关的应用程序一段时间了,我有点碰壁,不知道接下来该做什么。

我最近在应用程序中实现了一个自定义类,用于绘制音频输出的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];
                });

我的问题是什么:

  1. 我遇到了各种线程问题,尤其是在Thread 1: EXC_BAD_ACCESS (code=1, address=0xf00000c)这样的主线程上,有时候在调用viewDidLoad时应用程序启动时,以及每当我尝试与任何UI对象进行交互时。
  2. 即使在FFT显示屏上,UI响应速度也变得非常慢。
  3. 我认为问题是:这肯定与您可能知道的线程问题有关,但我对此主题并不熟悉。我想也许在主线程上强制任何UI显示更新,以便再次解决我遇到的问题;我甚至不确定如何正确地做到这一点。

    任何输入/见解都将是一个巨大的帮助。 提前谢谢!

1 个答案:

答案 0 :(得分:2)

如上所述,您的SpectralView*需要完全线程安全。

您的for()循环首先将帧/样本处理推送到高优先级并发队列。由于这是异步的,它将立即返回,此时您的代码将在主威胁上排队请求以更新光谱视图的显示。

这几乎可以保证光谱视图必须与背景处理代码同时更新显示,同时更新光谱视图的状态。

还有第二个问题;您的代码将最终并行化所有通道的处理。通常,未缓和的并发性是缓慢性能的一个因素。此外,无论是否完成该频道的处理,您都将在每个频道的主线程上进行更新。

代码需要重组。你真的应该从视图层拆分模型层。模型层可以写成线程安全的,在处理过程中,您可以获取要显示的数据的快照并在SpectralView上抛出。或者,您的模型图层可以有一个isProcessing标记,SpectralView可以关闭该标记以确定它不应该读取数据。

这是相关的:

https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html