AVAudioPlayerNode
Links
Swift (Programmiersprache)#Swift - Audio Swift (Programmiersprache)
You can use AVAudioEngine and AVAudioPlayerNode to schedule buffers for timing accurate playback. The AVAudioPlayerNode class has a method called scheduleBuffer(_:at:options:completionHandler:) that allows you to schedule a buffer for playback at a specific time in the future. This can be used to achieve timing accurate playback.
Here is an example of how you can use AVAudioEngine and AVAudioPlayerNode to schedule buffers for timing accurate playback:
let engine = AVAudioEngine()
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: engine.mainMixerNode.outputFormat(forBus: 0))
// Prepare a buffer with your audio data
let buffer = AVAudioPCMBuffer(pcmFormat: player.outputFormat(forBus: 0), frameCapacity: 1024)!
// ... fill buffer with your audio data
// Schedule the buffer for playback
let sampleTime = AVAudioFramePosition(44100) // the sample time you want the buffer to start playing
player.scheduleBuffer(buffer, at: nil, options: .interrupts, completionHandler: nil)
engine.startAndReturnError(nil)
In this example, it first creates an instance of AVAudioEngine and AVAudioPlayerNode, attaches the player to the engine, connects the player to the engine's main mixer node, then create a buffer with the audio data. Finally, it schedules the buffer for playback at a specific time (in this case, 44100 samples) by calling the scheduleBuffer(_:at:options:completionHandler:) method of the player.
The scheduleBuffer(_:at:options:completionHandler:) method allows you to specify the sample time at which the buffer should start playing. If you pass nil to the atTime parameter, the buffer will start playing as soon as possible.
It's worth noting that you should also set the engine.mainMixerNode.outputLatency to 0 or a low value to reduce delay between the scheduled time and the actual playback.
Please let me know if this helps, or if you have any other questions.
How can I use this to play a sound in accurate intervals for example 120 times a minute You can use the AVAudioPlayerNode's scheduleBuffer(_:at:options:completionHandler:) method to schedule multiple buffers for playback in accurate intervals to achieve a metronome like functionality.
Here's an example of how you can use this to play a sound 120 times per minute:
let engine = AVAudioEngine()
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: engine.mainMixerNode.outputFormat(forBus: 0))
// Prepare a buffer with your audio data
let buffer = AVAudioPCMBuffer(pcmFormat: player.outputFormat(forBus: 0), frameCapacity: 1024)!
// ... fill buffer with your audio data
// schedule the buffer for playback
let samplesPerMinute = 44100 * 60
let samplesPerClick = samplesPerMinute / 120
var nextClickSampleTime = engine.outputNode.lastRenderTime.sampleTime + samplesPerClick
for _ in 0..<120 {
player.scheduleBuffer(buffer, at: nil, options: .interrupts, completionHandler: nil)
nextClickSampleTime += samplesPerClick
}
engine.startAndReturnError(nil)
In this example, it first creates an instance of AVAudioEngine and AVAudioPlayerNode, attaches the player to the engine, connects the player to the engine's main mixer node. Then it creates a buffer with the audio data that should be played. After that, it calculates the number of samples per minute and number of samples per click, using the sample rate of the audio format (44100 samples per second) and the desired number of clicks per minute (120). It sets the nextClickSampleTime to the current sample time plus the number of samples per click and then it schedules 120 buffers for playback at the nextClickSampleTime with the scheduleBuffer(_:at:options:completionHandler:) method.
The scheduleBuffer(_:at:options:completionHandler:) method allows you to specify the sample time at which the buffer should start playing, so by scheduling the buffer at the nextClickSampleTime variable, you are scheduling the playback of the buffer at accurate intervals.
It's worth noting that you should also set the engine.mainMixerNode.outputLatency to 0 or a low value to reduce delay between the scheduled time and the actual playback.