thanks for reading my question.
I was using Apple CoreAudio to render a simple sine wave at 440Hz in real time. For the first 128 seconds of rendering, I was able to hear a nice 440Hz sine wave. But after 128 seconds, the pitch of the sound suddenly became low. Then, after 256 seconds of playback, the pitch of the sound became higher than 440Hz. Finally, when the playback time reached 512 seconds, the sound was no longer audible. The code below is what I ran. This code is similar to the code written on the website below. (Japanese though)
import SwiftUI
import AVFoundation
var player = Player(engine: &box)
@main
struct MyApp: App {
init() {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord)
let ioBufferDuration = 128.0 / 44100.0
try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(ioBufferDuration)
} catch {
assertionFailure("AVAudioSession setup error: \(error)")
}
}
var body: some Scene {
WindowGroup {
MyView()
}
}
}
import Foundation
import AVFoundation
class Player {
var audioUnit: AudioUnit?
var audioEngine: AVAudioEngine = AVAudioEngine()
var sampleRate: Float = 440.0
var time: Float = 0
var deltaTime: Float = 0
var mainMixer: AVAudioMixerNode?
var outputNode: AVAudioOutputNode?
var format: AVAudioFormat?
var engine: Box?
private lazy var sourceNode = AVAudioSourceNode { (_, _, frameCount, outputData) -> OSStatus in
let ablPointer = UnsafeMutableAudioBufferListPointer(outputData)
for frame in 0..<Int(frameCount) {
let sampleVal = sin(440.0 * 2.0 * Float(Double.pi) * self.time)
self.time += self.deltaTime
for buffer in ablPointer {
let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer)
buf[frame] = sampleVal
}
}
return noErr
}
init(engine: inout Box) {
mainMixer = audioEngine.mainMixerNode
outputNode = audioEngine.outputNode
format = outputNode!.inputFormat(forBus: 0)
sampleRate = Float(format!.sampleRate)
deltaTime = 1 / Float(sampleRate)
self.engine = engine
let inputFormat = AVAudioFormat(commonFormat: format!.commonFormat, sampleRate: Double(sampleRate), channels: 1, interleaved: format!.isInterleaved)
audioEngine.attach(sourceNode)
audioEngine.connect(sourceNode, to: mainMixer!, format: inputFormat!)
audioEngine.connect(mainMixer!, to: outputNode!, format: nil)
mainMixer?.outputVolume = 1
}
func start() {
do {
try audioEngine.start()
} catch {
fatalError("Coud not start engine: \(error.localizedDescription)")
}
}
func stop() {
audioEngine.stop()
}
}
Your Float
time
is running out of precision. Convert it to a Double
to have the same problem occur later, or replace self.time
with an expression using the number of samples processed:
var sampleCount: Int64 = 0 // instance variable
// in sourceNode callback:
let sampleVal = sin(440.0 * 2.0 * Double.pi * Double(sampleCount) / Double(sampleRate) )
sampleCount += 1