Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVAudioPlayerNode volume change is not applied immediately

Using AVFoundation and AVAudioPlayerNode to play sound with Xcode Version 9.2 (9C40b) and deploying to iOS 11.2

The problem is that when you change volume, the change is not applied the first time you play the sound, and there are other weird effects.

Start a new project in Xcode, select iOS Game, and give any name. Then replace the code in GameScene.swift with:

import SpriteKit
import GameplayKit
import AVFoundation

class GameScene: SKScene {

    var entities = [GKEntity]()
    var graphs = [String : GKGraph]()

    let audioFilePlayer = AVAudioPlayerNode()
    var audioFile:AVAudioFile! = nil
    var audioFileBuffer:AVAudioPCMBuffer! = nil

    override func sceneDidLoad() {
        do {
            let path = Bundle.main.path(forResource: "sound", ofType: "caf")!
            let url = URL(fileURLWithPath: path)
            audioFile = try AVAudioFile(forReading: url)
            audioFileBuffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: UInt32(audioFile.length))
            try audioFile.read(into: audioFileBuffer!)
            audioEngine.attach(audioFilePlayer)
            audioEngine.connect(audioFilePlayer, to: audioEngine.mainMixerNode, format: audioFileBuffer?.format)
            try audioEngine.start()
        }
        catch {
            print(error)
        }

        let playAction = SKAction.sequence([
            SKAction.wait(forDuration: 3.0),
            SKAction.run { self.play(volume: 1.0) },
            SKAction.wait(forDuration: 1.0),
            SKAction.run { self.play(volume: 1.0) },
            SKAction.wait(forDuration: 1.0),
            SKAction.run { self.play(volume: 0.0) },
            SKAction.wait(forDuration: 1.0),
            SKAction.run { self.play(volume: 0.0) },
            SKAction.wait(forDuration: 1.0),
            SKAction.run { self.play(volume: 1.0) },
            SKAction.wait(forDuration: 1.0),
            SKAction.run { self.play(volume: 1.0) },
            ])
        self.run(playAction)

    }

    func play(volume:Float) {
        print("playing at \(volume)")
        audioFilePlayer.stop()
        audioFilePlayer.volume = volume
        audioFilePlayer.scheduleFile(audioFile, at: nil, completionHandler: nil)
        audioFilePlayer.play()
    }

}

Apologies for poor optionals... Also, add a sound file called sound.caf to the project.

Console output is:

playing at 1.0
playing at 1.0
playing at 0.0
playing at 0.0
playing at 1.0
playing at 1.0

I would expect to hear: loud, loud, nothing, nothing, loud, loud.

I am actually hearing: loud, loud, loud, nothing, soft, loud

(the 'soft' is particularly weird)

I have also tried changing the master volume with:

audioEngine.mainMixerNode.outputVolume = volume

but the sounds are the same.

In older games, used to use OpenAL, but this requires Obj-C and is pretty messy with bridging headers etc. Also, OpenAL is supposedly not supported any more. SKAction based audio cannot handle lots of sounds repeating fast without glitches and scratches (it's a space shooter game...) Problem is the same in simulator and on device. Any help appreciated!

like image 426
Christian Cerri Avatar asked Oct 21 '25 04:10

Christian Cerri


1 Answers

OK I found the answer. AudioEngine needs to be reset after volume changes in order for the changes to be immediate:

audioEngine.reset()

this play() function works:

    func play(volume:Float) {
        print("playing at \(volume)")
        audioFilePlayer.volume = volume
        audioEngine.reset()
        audioFilePlayer.stop()
        audioFilePlayer.scheduleFile(audioFile, at: nil, completionHandler: nil)
        audioFilePlayer.play()
    }
like image 151
Christian Cerri Avatar answered Oct 23 '25 19:10

Christian Cerri



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!