0

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
}
madx
  • 6,723
  • 4
  • 55
  • 59

1 Answers1

1

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
}
punkbit
  • 7,347
  • 10
  • 55
  • 89
  • Yes, I have seen that putting the "initMicTracker" inside the beginning of startMonitoring would start the sampling (I should have write that) but I have 2 big problems. 1) it happens even if I don't "disconnectAllInputs" 2) I have tried to run the program like this (so as you suggested) with XCode "Allocations Profiler" and each time that I call "startMonitoring" and then "stopMonitoring" the memory allocations increase... it seems that there is something left in the memory which is not deallocated correctly... that's why I think that even using this solution is still not the correct way – madx Jun 06 '20 at 13:58
  • 1
    You have to reset the references that you have to reinitialise if you want to create new instances or use what's in memory. I'll add this to my answer but my solution was just meant to answer the original question... – punkbit Jun 06 '20 at 14:49
  • Yes yes, you are right, I had to write it better... Thank you for the patience. I have tried even to stop them and then putting them to null inside the stop Monitoring but nothing the "Allocation Profiler" allocation shown grows... (I have posted a second code above) I have updated my answer with the current code that I'm trying to use. – madx Jun 06 '20 at 15:41
  • Ok, this seems to be something else then; You'll have to update AudioKit to the latest version or at least 4.9.5 and then come back to this thread and leave feedback. – punkbit Jun 06 '20 at 15:47
  • I have now installed the "AudioKit-macOS-4.9.5" framework from https://audiokit.io/downloads/, nothing new, same problem about the memory. – madx Jun 06 '20 at 16:07
  • Ok, so @madx the issue here is not the original question but the memory issue; I'd suggest opening a ticket in the AudioKit github repo, as this is out of scope at the moment I'm afraid. – punkbit Jun 06 '20 at 16:09
  • You are right... I'll accept your answer and close the topic here... – madx Jun 06 '20 at 16:13