Sync playing AKSamplerMetronome and AKAppleSequencer - ios

Thanks for AudioKit !
I have next question:
I'm trying to get ideal sync playing of AKSampleMetronome and one midi file wrapped in AKMIDISampler. Here is my code:
let metronome = AKSamplerMetronome()
let mixer = AKMixer()
let midiSampler = AKMIDISampler()
midiSampler.samplerUnit.loadSoundBankInstrument(....)
metronome >>> mixer
midiSampler >>> mixer
AudioKit.output = mixer
AudioKit.start()
let sequencer = AKAppleSequencer(filename: "midifilename")
sequencer.enableLooping()
sequencer.tracks[1].setMIDIOutput(midiSampler.midiIn)
//now play
sequencer.play()
metronome.beatTime = 0
metronome.play()
But when I'm changing tempo like this:
sequencer.setTempo(bpm)
let now = AVAudioTime(hostTime: mach_absolute_time())
metronome.setTempo(Double(bpm), at: now)
After a while the sound of metronome and midi sequencer diverges.
How can I achieve accurate solution for this ?

Lots of ways to do this but I would make one of the tracks in the sequencer a metronome track and send that midi signal to a midiSampler.

Related

How to play MIDI with AudioKit's new AKSequencer

I'm on AudioKit 4.9.1 and can't manage to play a MIDI file with the new AKSequencer (replacing AKAppleSequencer). No sound playing. Assume that MIDI file AND samples are loaded correctly since they previously worked with AKAppleSequencer. Background audio mode capability is also enabled.
Here's the relevant code: (I've also tried both AKSampler and AKAppleSampler but same result)
class MIDIPlayer {
var sampler: AKSampler
var legacySampler: AKAppleSampler
var sequencer: AKSequencer
init(withSfz sfz: String, orSf2 sf2: String, andMidiFile midiFile: String) {
self.sampler = AKSampler()
self.legacySampler = AKAppleSampler()
try? legacySampler.loadSoundFont(sf2, preset: 0, bank: 0)
sampler.loadSFZ(url: Bundle.main.url(forResource: sfz, withExtension: "sfz")!)
AudioKit.output = sampler
try? AudioKit.start()
sequencer = AKSequencer(targetNode: sampler)
// sequencer = AKSequencer(targetNode: legacySampler)
let midi = AKMIDIFile(url: Bundle.main.url(forResource: midiFile, withExtension: "mid")!)
sequencer.load(midiFile: midi)
}
func play() {
sequencer.playFromStart()
}
Is there some difference in how to set up the signal chain that I'm missing?
With the new sequencer, it has to be part of the signal chain. So, do something like
let mixer = AKMixer
sampler >>> mixer
for track in sequencer.tracks { track >>> mixer }
AudioKit.output = mixer
and it should work. Sorry for the delay in seeing this on Github issues.

Continuous Sine Wave From AKMIDISampler when AKMicrophone is Present

I’m having a problem using AKMIDISampler in my project when there’s an initialized AKMicrophone. Along with correctly playing the woodblock sample when “play” is called on the sampler, the first time “play” is called a constant sine wave starts playing - it never stops.
I’ve replicated the problem in the smallest amount of code below. Issue happens when the class is initialized then playTestSample() is called.
Note that if the AKMicrophone related code is all muted the AKMIDISampler plays fine and the sine wave that currently haunts my dreams doesn’t happen.
(I’ve tried switching to use the AKSampler() just to see if the problem would exist there but I haven’t been able to get any sound out of that).
Fyi: I have “App plays audio or streams audio/video using AirPlay” in the “Required background modes” in info.plist - which is know to fix another sine wave issue.
Thank you very much for any assistance.
Btw: AudioKit rocks and has been a massive help on this project! :^)
AK 4.5.4
Xcode 10.1
import Foundation
import AudioKit
class AudioKitTESTManager {
var mixer = AKMixer()
var sampler = AKMIDISampler()
var mic = AKMicrophone()
var micMixer = AKMixer()
var micBooster = AKBooster()
init() {
mixer = AKMixer(sampler, micBooster)
do {
let woodblock = try AKAudioFile(readFileName: RhythmGameConfig.woodblockSoundName)
try sampler.loadAudioFiles([woodblock])
} catch {
print("Error loading audio files into sampler")
}
micMixer = AKMixer(mic)
micBooster = AKBooster(micMixer)
micBooster.gain = 0.0
AudioKit.output = mixer
AKSettings.playbackWhileMuted = true
AKSettings.defaultToSpeaker = true
AKSettings.sampleRate = 44100
do {
print("Attempting to start AudioKit")
try AudioKit.start()
} catch {
AKLog("AudioKit did not start!")
}
}
func playTestSample() {
// You hear the sample and a continuous sine wave starts playing through the samplerMixer
try? sampler.play(noteNumber: 60, velocity: 90, channel: 1)
}
}
Wheeew. I believe I've found a solution. Maybe it will help out someone else?
It seems that loading the files into the sampler AFTER AudioKit.start() fixes the Sine Wave of Terror!
//..
do {
print("Attempting to start AudioKit")
try AudioKit.start()
} catch {
AKLog("AudioKit did not start!")
}
do {
let woodblock = try AKAudioFile(readFileName: RhythmGameConfig.woodblockSoundName)
try sampler.loadAudioFiles([woodblock])
} catch {
print("Error loading audio files into sampler")
}

Out sync sound and picture between two players

I am building a karaoke app with the ability to sing with video so here is my problem:
I am recording the user video (video only from the front camera) along with applying voice filters with audiokit on a separate audio records.
Now in my playback, i want to play the video and the audio in a sync mode but it didn't succeed because a have an out sync of video and audio.
I am using akplayer for audio so i can apply voice mod and vlckit for playing user video.
do {
//MARK: VLC kit part of the video setup
Vlc_VideoPlayer = VLCMediaPlayer()
Vlc_VideoPlayer.media = VLCMedia(url: recordVideoURL)
Vlc_VideoPlayer.addObserver(self, forKeyPath: "time", options: [], context: nil)
Vlc_VideoPlayer.addObserver(self, forKeyPath: "remainingTime", options: [], context: nil)
Vlc_VideoPlayer.drawable = self.CameraView
//MARK: Audiokit with AKPlayer Setup
file = try AKAudioFile(forReading: recordVoiceURL)
player = AKPlayer(audioFile: file)
self.player.preroll()
delay = AKVariableDelay(player)
delay.rampTime = 0.5
delayMixer = AKDryWetMixer(player, delay)
reverb = AKCostelloReverb(delayMixer)
reverbMixer = AKDryWetMixer(delayMixer, reverb)
booster = AKBooster(reverbMixer)
tracker = AKAmplitudeTracker(booster)
AudioKit.output = tracker
try AudioKit.start()
}catch{
print (error)
}
self.startPlayers()
now the startPlayers function :
func startPlayers(){
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if AudioKit.engine.isRunning {
self.Vlc_VideoPlayer.audio.isMuted = true
self.Vlc_VideoPlayer.play()
self.player.isLooping = false
self.player.play()
}else{
self.startPlayers()
}
}
}
I don't know anything about the VLC player, but with the built in AVPlayer there is an option to sync to a clock:
var time: TimeInterval = 1 // 1 second in the future
videoPlayer.masterClock = CMClockGetHostTimeClock()
let hostTime = mach_absolute_time()
let cmHostTime = CMClockMakeHostTimeFromSystemUnits(hostTime)
let cmVTime = CMTimeMakeWithSeconds(time, preferredTimescale: videoPlayer.currentTime().timescale)
let futureTime = CMTimeAdd(cmHostTime, cmVTime)
videoPlayer.setRate(1, time: CMTime.invalid, atHostTime: futureTime)
AKPlayer then supports syncing to the mach_absolute_time() hostTime using its scheduling functions. As you have above, the two will start close together but there is no guarantee of any sync.
Trying to start two players will work out of pure look and unless you have means to synchronize playback after it started, it will not be perfect. Ideally, you should play the audio with VLC as well to make use of its internal synchronization tools.
To iterate on what you have right now, I would suggest to start playback with VLC until it decoded the first frame, pause, start your audio and continue playback with VLC as soon as you decoded the first audio sample. This will still not be perfect, but probably better.

AudioKit AKSequencer does not loop on iOS 10

The following code plays multiple audio files in a loop in iOS 11 but not in iOS 10. Seems like that AKSequencer cannot loop in iOS 10?
let mixer = AKMixer()
var sequencer: AKSequencer = AKSequencer()
sequencer.loadMIDIFile(fromUrl: URL(fileURLWithPath: midiFile))
for index in 0..<sequencer.tracks.count {
let musicTrack: AKMusicTrack = sequencer.tracks[index]
let sampler = AKMIDISampler()
if index < samples.count {
sampler.loadWavFromAbsolutePath(samples[index])
sampler.enableMIDI(midi.client, name: "Sampler_\(index)")
mixer.connect(input: sampler)
}
musicTrack.setMIDIOutput(sampler.midiIn)
}
AudioKit.output = mixer
AudioKit.start()
sequencer.enableLooping()
sequencer.play()
I can only guess what connection this might have to iOS 10, but I've had issues in the past where AKSequencer didn't loop when the its .play() call followed immediately after AudioKit.start(). I found that putting the .play() call in an IBAction or calling it from a timer (delayed 100ms or so) allowed it to loop properly.
I had a similar issue and the problem was related to the scope of the AKSequencer property.
It looks like the sequencer gets de-initialized and cannot process the MIDI signals any further.
Try to to upgrade the var sequencer: AKSequencer = AKSequencer() to a global variable.

Click/pop in repeatedly low pitch audio sample playing with iOS Audio Unit

I'm developing a music instrument in iOS with two audio samples (high and low pitches) that are played with view touches. The first sample is very short (a half second) and the other is a little bigger (two seconds). When I play repeatedly and fast the low pitch sound, there is an audio click/pop. There is no problem playing the high pitch sound.
Both audio samples have fade in and fade out in their init/end and there is no clip problem with them.
I'm using this code to load the audio files (simplified here):
engine = AVAudioEngine()
mixer = engine.mainMixerNode
let player = AVAudioPlayerNode()
do {
let audioFile = try AVAudioFile(forReading: instrumentURL)
let audioFormat = audioFile.processingFormat
let audioFrameCount = UInt32(audioFile.length)
let audioFileBuffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity: audioFrameCount)
try audioFile.readIntoBuffer(audioFileBuffer)
engine.attachNode(player)
engine.connect(player, to: mixer, format: audioFileBuffer.format)
} catch {
print("Init Error!")
}
and this code to play the samples:
player.play()
player.scheduleBuffer(audioFileBuffer, atTime: nil, options: option, completionHandler: nil)
I'm using a similar functionality in Android with the same audio samples without any click/pop problem.
Is this click/pop problem an implementation error?
How can I fix this problem?
Update 1
I just tried another approach, with AVAudioPlayer and I got the same pop/click problem.
Update 2
I think the problem is to start the audio file again before its end. The sound stops abruptly.

Resources