kstenerud / ObjectAL-for-iPhone

Mac and iOS Audio development, minus the headache. ObjectAL is the easy Objective-C interface to OpenAL, AVAudioPlayer, and audio session management.
http://kstenerud.github.com/ObjectAL-for-iPhone
885 stars 171 forks source link

Fix "bad_access error" when playing short time at high frequency with fade out #96

Open Kta-M opened 8 years ago

Kta-M commented 8 years ago

I fixed a bug regarding the error: "EXC_BAD_ACCESS" which made the app crashed. It occurs when playing sound short time at high frequency with fade out. (at ObjectAL/ObjectAL/Actions/OALActionManager.m#L160)

When starting fade out, a fade action added to OALActionManager targetActions, and it removed when fade out was completed. However, there is a time lag between releasing ALSource and removing fade action from targetActions.

App crashes during the time lag when a new ALSource is generated with the same id as the released old AlSource one and when an action from the ALSource is added to the to the targetActions.

Regarding the OALActionManager step:timer, when a new fade action from the ALSource is attempting to be added, the id of the released ALSource however remains present in the targets. Therefore, the new fade action is added to the array of actions for old ALSource in targetActions. Next, the fade action of released ALSource is removed, but the id of released ALSource is not removed from targets because the fade action of new ALSource remains in the array of actions in targetActions. Hence the next time OALActionManager step:timer is called, the app will crash trying to reference the id of released ALSource in targets.

To fix it, I swapped the order of adding and removing actions in the code, and removed the master timer stop condition and placed it out, at the end of the adding/removing process.

The following code is for verification. It reproduces this bug.

class SampleClass: NSObject {

    let buffer = OpenALManager.sharedInstance().bufferFromFile("sample.wav")
    var sourceArray: Array<ALSoundSource> = []
    var playingSource: ALSoundSource?

    func play() {
        NSTimer.scheduledTimerWithTimeInterval(1.0/60.0, target: self, selector: "update:", userInfo: nil, repeats: true)
    }

    func update(timer: NSTimer) {
        playingSource?.fadeTo(0.0, duration: 0.5, target: self, selector: "onFadeComplete:")

        let source = ALSource.source() as? ALSoundSource
        source!.play(buffer, gain: 1.0, pitch: 1.0, pan: 1.0, loop: false)
        sourceArray.append(source!)
        playingSource = source
    }

    func onFadeComplete(source: ALSoundSource) {
        let index = sourceArray.indexOf{ $0.hash == source.hash }
        sourceArray.removeAtIndex(index!)
    }
}