madx
madx

Reputation: 7203

AudioKit: can't restart AudioKit sampling of frequency after calling 'AudioKit.stop()'

I'm using AudioKit 4.10. I use this simple code to sample frequency and amplitude from a microphone:

import AudioKit

protocol MicAnalyzerDelegate {
    /**
     * The MicAnalyzer calls this delegate function.
     */
    func micAnalyzerInfos(infos: (frequency : Double, amplitude : Double)?)
}

class MicAnalyzer: NSObject {

    // MARK: - Properties
    private static var micAnalyzer: MicAnalyzer = MicAnalyzer()
    var delegate: MicAnalyzerDelegate?

    fileprivate var mic: AKMicrophone!
    fileprivate var tracker: AKFrequencyTracker!
    fileprivate var silence: AKBooster!
    fileprivate var timer: Timer?

    // MARK: - Initializations
    private override init() {
        super.init()
        AKSettings.audioInputEnabled = true
        self.initMicTracker()
    }

    private func initMicTracker(){
        /* Add the built-in microphone. */
        mic = AKMicrophone()
        /* Add a traker */
        tracker = AKFrequencyTracker(mic)
        silence = AKBooster(tracker, gain: 0)
    }

    // MARK: - Accessors
    class func shared() -> MicAnalyzer {
        return micAnalyzer
    }

    func startMonitoring() {
        /* Start the microphone and analyzer. */
        AudioKit.output = silence
        do {
            try AudioKit.start()
        } catch {
            AKLog("AudioKit did not start!")
        }

        /* Initialize and schedule a new run loop timer. */
        timer = Timer.scheduledTimer(timeInterval: 0.1,
                                     target: self,
                                     selector: #selector(MicAnalyzer.tick),
                                     userInfo: nil,
                                     repeats: true)
    }

    // Stopped as described here: https://github.com/AudioKit/AudioKit/issues/1716
    func stopMonitoring() {
        do {
            AudioKit.disconnectAllInputs()
            try AudioKit.stop()
            try AudioKit.shutdown()
        } catch {
            print("AudioKit did not stop")
        }
        AudioKit.output = nil

        // Interrupts polling on tick infos
        timer?.invalidate()
        timer = nil
    }

    var infos: (frequency : Double, amplitude : Double)? {
        if(!tracker.isStopped){
            let frequency = tracker.frequency
            let amplitude = tracker.amplitude

            return (frequency: frequency, amplitude: amplitude)
        } else {
            return nil
        }
    }


    /* Call the delegate. */
    @objc func tick() {
        self.delegate?.micAnalyzerInfos(infos: infos)
    }

}

Calling in order the following code I have that situation:

MicAnalyzer.shared().startMonitoring() // --> Everything works good
MicAnalyzer.shared().stopMonitoring()  // --> I think it is stopped correctly
MicAnalyzer.shared().startMonitoring() // --> From now on the delegate is called but I get always 0 as frequency and amplitude

Why after recalling the AudioKit.start() (inside the MicAnalyzer.shared().startMonitoring()) I can't sample frequency and amplitude anymore? How should I restart the whole thing?


If I reinitiate each time the variables mic, tracker and silence the memory grows as they are not really deallocated. This is a second version of the same code idea:

import AudioKit

protocol MicAnalyzerDelegate : class {
    /**
     * The tuner calls this delegate function 
     */
    func micAnalyzerInfos(infos: (frequency : Double, amplitude : Double)?)
}

// https://audiokit.io/examples/MicrophoneAnalysis/
class MicAnalyzer: NSObject {

    // MARK: - Properties
    private weak var delegate: MicAnalyzerDelegate?

    fileprivate var mic: AKMicrophone!
    fileprivate var tracker: AKFrequencyTracker!
    fileprivate var silence: AKBooster!
    fileprivate var timer: Timer?

    // MARK: - Initializations
    init(delegate: MicAnalyzerDelegate) {
        print("Init called!!!")
        self.delegate = delegate
        super.init()
    }

    deinit {
        print("Deinit called!!!")
    }

    // MARK: - Accessors
    func startMonitoring() {
        AKSettings.audioInputEnabled = true
        /* Add the built-in microphone. */
        mic = AKMicrophone()
        /* Add a traker */
        tracker = AKFrequencyTracker(mic)
        silence = AKBooster(tracker, gain: 0)

        /* Start the microphone and analyzer. */
        AudioKit.output = silence
        do {
            try AudioKit.start()
        } catch {
            AKLog("AudioKit did not start!")
        }

        /* Initialize and schedule a new run loop timer. */
        timer = Timer.scheduledTimer(timeInterval: 1,
                                     target: self,
                                     selector: #selector(MicAnalyzer.tick),
                                     userInfo: nil,
                                     repeats: true)
    }

    func stopMonitoring() {
        do {
            AudioKit.disconnectAllInputs()
            try AudioKit.stop()
            try AudioKit.shutdown()
        } catch {
            print("AudioKit did not stop")
        }
        AudioKit.output = nil

        // Interrupts polling on tick infos
        timer?.invalidate()
        timer = nil

        silence.stop()
        silence = nil
        tracker.stop()
        tracker = nil
        mic.stop()
        mic = nil
    }

    var infos: (frequency : Double, amplitude : Double)? {
        if(!tracker.isStopped){
            let frequency = tracker.frequency
            let amplitude = tracker.amplitude

            return (frequency: frequency, amplitude: amplitude)
        } else {
            return nil
        }
    }

    /* Call the delegate. */
    @objc func tick() {
        print(infos?.frequency ?? "0.0")
        //self.delegate.micAnalyzerInfos(infos: infos)
    }

}

And then I could call that 'second code test' like this in order:

override func viewDidAppear() {
    super.viewDidAppear()
    print("viewDidAppear!!!")
    tuner = Tuner(delegate: self)
    tuner?.startMonitoring()
}

override func viewDidDisappear() {
    super.viewDidDisappear()
    print("viewDidDisappear!!!")
    tuner?.stopMonitoring()
    tuner = nil
}

Upvotes: 0

Views: 159

Answers (1)

punkbit
punkbit

Reputation: 7707

You need to make sure the AKFrequencyTracker is part of the signal chain, as that's an AVAudioEngine engine requirement.

Which means that if you do:

do {
    AudioKit.disconnectAllInputs()
    try AudioKit.stop()
    try AudioKit.shutdown()
} catch {
   // error handler
}

AudioKit.output = nil

You have to call initMicTracker to have the AKFrequencyTracker back in the signal chain. So, to restart you'd do:

  • initMicTracker
  • startMonitoring

Because your stopMonitoring did:

  • AudioKit.disconnectAllInputs(), which means (stop)MicTracker

You know that you already created instances for AKMicrophone, AKFrequencyTracker and AKBooster free it from memory

func stopMonitoring() {
    ...

    self.mic = nil
    self.tracker = nil
    self.silence = nil
}

Upvotes: 1

Related Questions