Swift for Xojo Developers - Part 12 - Sound

Well this doesn’t really replicate any Xojo Syntax, but it is a Sound function that took me a few days to find all the pieces to. I had one that worked (it was much longer and more complicated), but it failed in iOS13 with a hard crash. This one works.

NOTE: this does block the UI while the tone is sounding, which for my current use is fine, but I will probably thread it in a later version

import Foundation
import AVFoundation

fileprivate var audioEngine : AVAudioEngine = AVAudioEngine()
func beep() {
    playTONE(freq:500,dur:0.15)
}

func playTONE(freq:Double,dur:Double) {
    let wait   = dur * 1000000.0
    var player : AVAudioPlayerNode?
    var mixer  : AVAudioMixerNode
    var buffer : AVAudioPCMBuffer


    player     = AVAudioPlayerNode()
    mixer      = audioEngine.mainMixerNode;
    buffer     = AVAudioPCMBuffer(pcmFormat: player!.outputFormat(forBus:0), frameCapacity: 100)!
    buffer.frameLength = 100

    // generate sine wave.
    let sr:Float   = Float(mixer.outputFormat(forBus:0).sampleRate)
    let n_channels = Int(mixer.outputFormat(forBus:0).channelCount)
    var i  : Int   = 0
    let f1 : Float = (Float(freq*2)*Float.pi)/sr
    while i < Int(buffer.frameLength) {
        buffer.floatChannelData?.pointee[i] = sinf(f1*Float(i)) * 0.5
        i+=n_channels
    }
    // setup audio engine
    audioEngine.attach(player!)
    audioEngine.connect(player!, to: mixer, format: player!.outputFormat(forBus: 0))
    audioEngine.prepare()
    try! audioEngine.start()
    // play player and buffer
    player!.scheduleBuffer(buffer, at: nil, options: .loops, completionHandler: nil)
    player!.play()
    usleep(useconds_t(wait))
    player!.stop()
}