Home > database >  SNAudioStreamAnalyzer not stopping sound classification request
SNAudioStreamAnalyzer not stopping sound classification request

Time:01-18

I'm a student studying iOS development currently working on a simple AI project that utilizes SNAudioStreamAnalyzer to classify an incoming audio stream from the device's microphone. I can start the stream and analyze audio no problem, but I've noticed I can't seem to get my app to stop analyzing and close the audio input stream when I'm done. At the beginning, I initialize the audio engine and create the classification request like so:

private func startAudioEngine() {
        do {
            // start the stream of audio data
            try audioEngine.start()
            let snoreClassifier = try? SnoringClassifier2_0().model
            let classifySoundRequest = try audioAnalyzer.makeRequest(snoreClassifier)
            try streamAnalyzer.add(classifySoundRequest,
                                   withObserver: self.audioAnalyzer)
        } catch {
            print("Unable to start AVAudioEngine: \(error.localizedDescription)")
        }
    }

After I'm done classifying my audio stream, I attempt to stop the audio engine and close the stream like so:

private func terminateNight() {
        streamAnalyzer.removeAllRequests()
        audioEngine.stop()
        stopAndSaveNight()
        do {
            let session = AVAudioSession.sharedInstance()
            try session.setActive(false)
        } catch {
            print("unable to terminate audio session")
        }
        nightSummary = true
    }

However, after I call the terminateNight() function my app will continue using the microphone and classifying the incoming audio. Here's my SNResultsObserving implementation:

class AudioAnalyzer: NSObject, SNResultsObserving {
    var prediction: String?
    var confidence: Double?
    let snoringEventManager: SnoringEventManager
    
    internal init(prediction: String? = nil, confidence: Double? = nil, snoringEventManager: SnoringEventManager) {
        self.prediction = prediction
        self.confidence = confidence
        self.snoringEventManager = snoringEventManager
    }
    
    func makeRequest(_ customModel: MLModel? = nil) throws -> SNClassifySoundRequest {
        if let model = customModel {
            let customRequest = try SNClassifySoundRequest(mlModel: model)
            return customRequest
        } else {
            throw AudioAnalysisErrors.ModelInterpretationError
        }
    }
    
    func request(_ request: SNRequest, didProduce: SNResult) {
        guard let classificationResult = didProduce as? SNClassificationResult else { return }
        let topClassification = classificationResult.classifications.first
        let timeRange = classificationResult.timeRange
        self.prediction = topClassification?.identifier
        self.confidence = topClassification?.confidence
        if self.prediction! == "snoring" {
            self.snoringEventManager.snoringDetected()
        } else {
            self.snoringEventManager.nonSnoringDetected()
        }
    }
    
    func request(_ request: SNRequest, didFailWithError: Error) {
        print("ended with error \(didFailWithError)")
    }
    
    func requestDidComplete(_ request: SNRequest) {
        print("request finished")
    }
}

It was my understanding that upon calling streamAnalyzer.removeAllRequests() and audioEngine.stop() the app would stop streaming from the microphone and call the requestDidComplete function, but this isn't the behavior I'm getting. Any help is appreciated!

CodePudding user response:

From OP's edition:

So I've realized it was a SwiftUI problem. I was calling the startAudioEngine() function in the initializer of the view it was declared on. I thought this would be fine, but since this view was embedded in a parent view when SwiftUI updated the parent it was re-initializing my view and as such calling startAudioEngine() again. The solution was to call this function in on onAppear block so that it activates the audio engine only when the view appears, and not when SwiftUI initializes it.

CodePudding user response:

I don't believe you should expect to receive requestDidComplete due to removing a request. You'd expect to receive that when you call completeAnalysis.

  • Related