Section 8/201 menit
8. Sound Analysis: Audio Intelligence
8. Sound Analysis: Audio Intelligence
swift
// Sound Analysis: klasifikasi suara real-time
import SoundAnalysis
import AVFoundation
final class SoundClassifier: NSObject {
private var analyzer: SNAudioStreamAnalyzer?
private let analysisQueue = DispatchQueue(label: "com.app.sound-analysis")
var onSoundDetected: ((String, Double) -> Void)?
func startAnalyzing() throws {
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.record, mode: .default)
try audioSession.setActive(true)
// Gunakan built-in sound classifier Apple (iOS 15+)
let request = try SNClassifySoundRequest(classifierIdentifier: .version1)
request.windowDuration = CMTimeMakeWithSeconds(1.5, preferredTimescale: 44100)
request.overlapFactor = 0.5 // 50% overlap antar window
let inputFormat = AVAudioFormat(
standardFormatWithSampleRate: 44100,
channels: 1
)!
analyzer = SNAudioStreamAnalyzer(format: inputFormat)
try analyzer?.add(request, withObserver: self)
// Setup AVAudioEngine untuk capture audio
setupAudioEngine(format: inputFormat)
}
private func setupAudioEngine(format: AVAudioFormat) {
let engine = AVAudioEngine()
let inputNode = engine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 8192, format: recordingFormat) { [weak self] buffer, time in
self?.analysisQueue.async {
self?.analyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime)
}
}
try? engine.start()
}
}
extension SoundClassifier: SNResultsObserving {
func request(_ request: SNRequest, didProduce result: SNResult) {
guard let classificationResult = result as? SNClassificationResult,
let topClassification = classificationResult.classifications.first,
topClassification.confidence > 0.7 else { return }
DispatchQueue.main.async { [weak self] in
self?.onSoundDetected?(topClassification.identifier, topClassification.confidence)
}
}
func request(_ request: SNRequest, didFailWithError error: Error) {
print("Sound analysis failed: \(error)")
}
}