Playing an infinite sine sound

I'm trying to play a sine sound for as long as the user wants it. So my plan is to use two buffers. Fill one buffer, play it and while it plays fill the second buffer and then switch between those two. But I always get audio jumps when switching the buffers, I can't get a continuous sound. Could someone look at my code and tell me what I'm doing wrong? The audio data is correct, when I schedule a bunch of buffers in a row a get a smooth sound.

import Foundation
import AVFoundation

var engine = AVAudioEngine()
var player =  AVAudioPlayerNode()
var mixer = engine.mainMixerNode;

var dq = DispatchQueue(label: "Sine")

let sampleRate = mixer.outputFormat(forBus: 0).sampleRate
let samplesOfAudio = UInt32 (10000)
var buffer1 = AVAudioPCMBuffer(pcmFormat: (player.outputFormat(forBus: 0)), frameCapacity: samplesOfAudio)!
var buffer2 = AVAudioPCMBuffer(pcmFormat: (player.outputFormat(forBus: 0)), frameCapacity: samplesOfAudio)!

var f = 440.0
var s = UInt32(0) //current sample

let s2t = 2.0 * Double.pi / sampleRate //sample number to time conversion factor

func fillBuffer(buffer: AVAudioPCMBuffer) {

   let leftChannel = buffer.floatChannelData![0]
   let rightChannel = buffer.floatChannelData![1]
   for i in 0 ..< samplesOfAudio {
      let v = sin(f * Double(s) * s2t) * 0.2
      leftChannel[Int(i)] = Float(v)
      rightChannel[Int(i)] = Float(v)
      s = s + 1
   }
}

func playSine() {

   engine.attach(player)
   engine.connect(player, to: mixer, format: player.outputFormat(forBus: 0))
   do{
      try engine.start()
   } catch {
   }

   buffer1.frameLength = samplesOfAudio
   buffer2.frameLength = samplesOfAudio
   fillBuffer(buffer: buffer1)
   var nextBuffer = buffer1

   let semaphore = DispatchSemaphore(value: 0)
   dq.async {
      while true {

         player.scheduleBuffer(nextBuffer) {
            semaphore.signal()
         }

         if(nextBuffer == buffer1) {
            nextBuffer = buffer2
         } else {
            nextBuffer = buffer1
         }
         fillBuffer(buffer: nextBuffer)

         semaphore.wait()
      }
   }
   player.play()
}

Problem is that you do not control precisely the sequencing.

Why do you need those buffers ?

Could you try solution described here:

https://stackoverflow.com/questions/27808266/how-do-you-loop-avplayer-in-swift

var playerLooper: AVPlayerLooper! // should be defined in class
var queuePlayer: AVQueuePlayer!

let asset: AVAsset = ... // AVAsset with its 'duration' property value loaded asset.duration = very large value
let playerItem = AVPlayerItem(asset: asset)
self.queuePlayer = AVQueuePlayer(playerItem: playerItem)

// Create a new player looper with the queue player and template item
self.playerLooper = AVPlayerLooper(player: queuePlayer, templateItem: playerItem)

This may be a useful link as well: https://stackoverflow.com/questions/64386833/is-there-any-difference-between-using-avplayer-avplayeritem-and-avplayerasset

Hello,

the problem is that I can't loop it because in the end I want to play more complex sounds than constant sine waves. I also can't use items or assets as well because I want to generate the sounds on the fly

AVAudioSourceNode is probably more appropriate than AVAudioPlayerNode for the use-case you describe. I recommend that you take a look at source node to determine if it would serve your use-case better.

Playing an infinite sine sound
 
 
Q