Section 7/151 menit
7. Sound Analysis — Klasifikasi Audio
7. Sound Analysis — Klasifikasi Audio
Built-in Sound Classifier
swift
import SoundAnalysis
class SoundAnalyzer: NSObject {
private var analyzer: SNAudioStreamAnalyzer?
private let analysisQueue = DispatchQueue(label: "com.company.sound.analysis")
private let audioEngine = AVAudioEngine()
var onSoundDetected: (String, Double) -> Void = { _, _ in }
func startAnalysis() throws {
// Built-in classifier — deteksi 300+ jenis suara
let request = try SNClassifySoundRequest(classifierIdentifier: .version1)
request.windowDuration = CMTimeMakeWithSeconds(1.5, preferredTimescale: 44100)
request.overlapFactor = 0.5 // 50% overlap antar window
let inputFormat = audioEngine.inputNode.inputFormat(forBus: 0)
analyzer = SNAudioStreamAnalyzer(format: inputFormat)
try analyzer?.add(request, withObserver: self)
audioEngine.inputNode.installTap(
onBus: 0,
bufferSize: 8192,
format: inputFormat
) { [weak self] buffer, time in
self?.analysisQueue.async {
self?.analyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime)
}
}
try AVAudioSession.sharedInstance().setCategory(.record)
try AVAudioSession.sharedInstance().setActive(true)
audioEngine.prepare()
try audioEngine.start()
}
func stopAnalysis() {
audioEngine.stop()
audioEngine.inputNode.removeTap(onBus: 0)
analyzer?.removeAllRequests()
try? AVAudioSession.sharedInstance().setActive(false)
}
}
extension SoundAnalyzer: SNResultsObserving {
func request(_ request: SNRequest, didProduce result: SNResult) {
guard let classificationResult = result as? SNClassificationResult else { return }
// Ambil klasifikasi dengan confidence tertinggi
if let topResult = classificationResult.classifications.first,
topResult.confidence > 0.7 {
Task { @MainActor in
self.onSoundDetected(topResult.identifier, topResult.confidence)
}
}
}
func request(_ request: SNRequest, didFailWithError error: Error) {
print("Sound analysis error: \(error)")
}
}
Custom Sound Classifier
swift
// SoundAnalysis: gunakan model custom yang di-train dengan Create ML
class CustomSoundClassifier {
private var analyzer: SNAudioFileAnalyzer?
// Analisis file audio (bukan live stream)
func classify(audioFile: URL) async throws -> [SoundClassification] {
let request = try SNClassifySoundRequest(
mlModel: try MachineSound(configuration: .init()).model
)
let analyzer = try SNAudioFileAnalyzer(url: audioFile)
return try await withCheckedThrowingContinuation { continuation in
var classifications: [SoundClassification] = []
do {
try analyzer.add(request, withObserver: AnyResultsObserver { result, error in
if let error { continuation.resume(throwing: error); return }
guard let result = result as? SNClassificationResult else { return }
for classification in result.classifications where classification.confidence > 0.5 {
classifications.append(SoundClassification(
label: classification.identifier,
confidence: classification.confidence,
timeRange: result.timeRange
))
}
})
analyzer.analyze()
continuation.resume(returning: classifications)
} catch {
continuation.resume(throwing: error)
}
}
}
}
struct SoundClassification {
let label: String
let confidence: Double
let timeRange: CMTimeRange
}