NaN会导致这个核心音频iOS应用程序偶尔崩溃吗? [英] could NaN be causing the occasional crash in this core audio iOS app?

查看:458
本文介绍了NaN会导致这个核心音频iOS应用程序偶尔崩溃吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的第一款应用使用自iOS 6以来弃用的方法合成了来自正弦查找表的音乐音频我刚修改它以解决关于<$ h $ c> AudioSession 的警告,由此博客和AVFoundationFramework上的Apple指南。音频会话警告现已得到解决,应用程序会像之前一样生成音频。它目前在iOS 9下运行。

My first app synthesised music audio from a sine look-up table using methods deprecated since iOS 6. I have just revised it to address warnings about AudioSessionhelped by this blog and the Apple guidelines on AVFoundationFramework. Audio Session warnings have now been addressed and the app produces audio as it did before. It currently runs under iOS 9.

然而,应用程序偶尔崩溃没有明显的原因。我查看了这个SO帖子,但似乎处理访问而不是生成原始音频数据,所以也许它不处理时间问题。我怀疑存在缓冲问题,但在更改或微调代码中的任何内容之前,我需要了解这可能是什么。

However the app occasionally crashes for no apparent reason. I checked out this SO post but it seems to deal with accessing rather than generating raw audio data, so maybe it is not dealing with a timing issue. I suspect there is a buffering problem but I need to understand what this might be before I change or fine tune anything in the code.

我有一个截止日期,可以将修改后的应用程序提供给用户,所以我非常感谢听到处理类似问题的人的消息。

I have a deadline to make the revised app available to users so I'd be most grateful to hear from someone who has dealt a similar issue.

这是问题所在。该应用程序在模拟器报告中进入调试:

Here is the issue. The app goes into debug on the simulator reporting:

com.apple.coreaudio.AQClient (8):EXC_BAD_ACCESS (code=1, address=0xffffffff10626000)

在Debug Navigator中,线程8( com。它报告:

In the Debug Navigator, Thread 8 (com.apple.coreaudio.AQClient (8)), it reports:

    0 -[Synth fillBuffer:frames:]
    1 -[PlayView audioBufferPlayer:fillBuffer:format:]
    2 playCallback

fillBuffer中的这行代码突出显示

This line of code in fillBuffer is highlighted

    float sineValue = (1.0f - b)*sine[a] + b*sine[c];

...所以audioBufferPlayer中的这行代码是

... and so is this line of code in audioBufferPlayer

    int packetsWritten = [synth fillBuffer:buffer->mAudioData frames:packetsPerBuffer];

...和playCallBack

... and playCallBack

    [player.delegate audioBufferPlayer:player fillBuffer:inBuffer format:player.audioFormat];

这是audioBufferPlayer的代码(委托,基本上与演示中的上面)。

Here is the code for audioBufferPlayer (delegate, essentially the same as in the demo referred to above).

    - (void)audioBufferPlayer:(AudioBufferPlayer*)audioBufferPlayer fillBuffer:(AudioQueueBufferRef)buffer format:(AudioStreamBasicDescription)audioFormat            
    {
    [synthLock lock];
    int packetsPerBuffer = buffer->mAudioDataBytesCapacity / audioFormat.mBytesPerPacket;
    int packetsWritten = [synth fillBuffer:buffer->mAudioData frames:packetsPerBuffer];
    buffer->mAudioDataByteSize = packetsWritten * audioFormat.mBytesPerPacket;    
    [synthLock unlock];

    }

...(在myViewController中初始化)

... (initialised in myViewController)

- (id)init
{    
    if ((self = [super init])) {
    // The audio buffer is managed (filled up etc.) within its own thread (Audio Queue thread)
    // Since we are also responding to changes from the GUI, we need a lock so both threads
    // do not attempt to change the same value independently.

        synthLock = [[NSLock alloc] init];

    // Synth and the AudioBufferPlayer must use the same sample rate.

        float sampleRate = 44100.0f;

    // Initialise synth to fill the audio buffer with audio samples.

        synth = [[Synth alloc] initWithSampleRate:sampleRate];

    // Initialise note buttons

        buttons = [[NSMutableArray alloc] init];

    // Initialise the audio buffer.

        player = [[AudioBufferPlayer alloc] initWithSampleRate:sampleRate channels:1 bitsPerChannel:16 packetsPerBuffer:1024];
        player.delegate = self;
        player.gain = 0.9f;
        [[AVAudioSession sharedInstance] setActive:YES error:nil];

    }
    return self;
}   // initialisation

...和playCallback

... and for playCallback

static void playCallback( void* inUserData, AudioQueueRef inAudioQueue, AudioQueueBufferRef inBuffer)
{
    AudioBufferPlayer* player = (AudioBufferPlayer*) inUserData;
    if (player.playing){
        [player.delegate audioBufferPlayer:player fillBuffer:inBuffer format:player.audioFormat];
        AudioQueueEnqueueBuffer(inAudioQueue, inBuffer, 0, NULL);
    }
}

...这里是fillBuffer的代码合成音频

... and here is the code for fillBuffer where audio is synthesised

- (int)fillBuffer:(void*)buffer frames:(int)frames
{
    SInt16* p = (SInt16*)buffer;

    //  Loop through the frames (or "block size"), then consider each sample for each tone.

    for (int f = 0; f < frames; ++f)
    {
        float m = 0.0f;  // the mixed value for this frame

        for (int n = 0; n < MAX_TONE_EVENTS; ++n)
        {
            if (tones[n].state == STATE_INACTIVE)   // only active tones
                continue;

    // recalculate a 30sec envelope and place in a look-up table
    // Longer notes need to interpolate through the envelope

            int a   = (int)tones[n].envStep;        // integer part  (like a floored float)
            float b = tones[n].envStep - a;         // decimal part  (like doing a modulo)

        // c allows us to calculate if we need to wrap around

            int c = a + 1;                          // (like a ceiling of integer part)
            if (c >= envLength) c = a;              // don't wrap around

    /////////////// LOOK UP ENVELOPE TABLE /////////////////

    //  uses table look-up with interpolation for both level and pitch envelopes
    //  'b' is a value interpolated between 2 successive samples 'a' and 'c')            
    //  first, read values for the level envelope

            float envValue = (1.0f - b)*tones[n].levelEnvelope[a] + b*tones[n].levelEnvelope[c];

    //  then the pitch envelope

            float pitchFactorValue = (1.0f - b)*tones[n].pitchEnvelope[a] + b*tones[n].pitchEnvelope[c];

    //  Advance envelope pointer one step

            tones[n].envStep += tones[n].envDelta;

    //  Turn note off at the end of the envelope.
            if (((int)tones[n].envStep) >= envLength){
                tones[n].state = STATE_INACTIVE;
                continue;
            }

        //  Precalculated Sine look-up table            
            a = (int)tones[n].phase;                    // integer part
            b = tones[n].phase - a;                     // decimal part
            c = a + 1;
            if (c >= sineLength) c -= sineLength;       // wrap around

    ///////////////// LOOK UP OF SINE TABLE ///////////////////

            float sineValue = (1.0f - b)*sine[a] + b*sine[c];

    // Wrap round when we get to the end of the sine look-up table.

            tones[n].phase += (tones[n].frequency * pitchFactorValue); // calculate frequency for each point in the pitch envelope
            if (((int)tones[n].phase) >= sineLength)
                tones[n].phase -= sineLength;

    ////////////////// RAMP NOTE OFF IF IT HAS BEEN UNPRESSED

            if (tones[n].state == STATE_UNPRESSED) {
                tones[n].gain -= 0.0001;                
            if ( tones[n].gain <= 0 ) {
                tones[n].state = STATE_INACTIVE;
                }
            }

    //////////////// FINAL SAMPLE VALUE ///////////////////

            float s = sineValue * envValue * gain * tones[n].gain;

    // Clip the signal, if needed.

            if (s > 1.0f) s = 1.0f;
            else if (s < -1.0f) s = -1.0f;

    // Add the sample to the out-going signal   
        m += s;
        }

    // Write the sample mix to the buffer as a 16-bit word. 
    p[f] = (SInt16)(m * 0x7FFF);
    }
return frames;
}

我不确定它是不是红鲱鱼但是我遇到了NaN在几个调试寄存器中。它似乎在计算 fillBuffer 中的正弦查找的相位增量时发生(见上文)。这个计算是以44.1 kHz的采样率对每个样本进行多达十几个部分计算,并在iPhone 4的iOS 4上运行。我在iOS 9的模拟器上运行。我在这篇文章中描述了我所做的唯一更改!

I'm not sure whether it is a red herring but I came across NaN in several debug registers. It appears to happen while calculating phase increment for sine lookup in fillBuffer (see above). That calculation is done for up to a dozen partials every sample at a sampling rate of 44.1 kHz and worked in iOS 4 on an iPhone 4. I'm running on simulator of iOS 9. The only changes I made are described in this post!

推荐答案

我的NaN问题与Core Audio没有直接关系。它是由我的代码的另一个区域的更改引入的边缘条件引起的。真正的问题是在实时计算声音包络的持续时间时尝试除零。

My NaN problem turned out to have nothing directly to do with Core Audio. It was caused by an edge condition introduced by changes in another area of my code. The real problem was a division by zero attempted while calculating the duration of the sound envelope in realtime.

然而,在尝试找出问题的原因时,我有信心我的iOS 7之前的音频会话已被基于AVFoundation的工作设置所取代。感谢我的初始代码 Matthijs Hollemans 的来源以及 Mario Diana 其博客解释了所需的变化。

However, in trying to identify the cause of that problem, I am confident my pre-iOS 7 Audio Session has been replaced by a working setup based on AVFoundation. Thanks goes to the source of my initial code Matthijs Hollemans and also to Mario Diana whose blog explained the changes needed.

首先,我的iPhone上的声音水平明显低于模拟器上的声音水平,这是一个问题这里由代工厂提供。我发现有必要通过替换Mario的

At first, the sound levels on my iPhone were significantly less than the sound levels on the Simulator, a problem addressed here by foundry. I found it necessary to include these improvements by replacing Mario's

    - (BOOL)setUpAudioSession

与代工厂的

    - (void)configureAVAudioSession

希望这可能有助于其他人。

Hopefully this might help someone else.

这篇关于NaN会导致这个核心音频iOS应用程序偶尔崩溃吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆