Minha jornada com SoundAnalysis

Dec 09 2022
Não sou mestre, mas estou disposto a aprender sobre Análise de Som! Neste Macro Desafio, minha equipe decide fazer o Komka, um aplicativo para ajudar Crianças com Síndrome de Down a melhorar sua habilidade de comunicação expressiva. Um dos recursos que o Komka possui é o Sound Practice.

Não sou mestre, mas estou disposto a aprender sobre Análise de Som!

Neste Macro Desafio, minha equipe decide fazer o Komka, um aplicativo para ajudar Crianças com Síndrome de Down a melhorar sua habilidade de comunicação expressiva. Um dos recursos que o Komka possui é o Sound Practice. Esse recurso possui uma barra de progresso circular que aumentará de acordo com a semelhança de como a criança pronuncia a palavra.

Recurso de Boa Prática

Vou tentar explicar como minha equipe construiu esse recurso!

Em primeiro lugar, minha equipe treina o modelo usando createML. Depois de termos o modelo, começamos a fazer a configuração do nosso recurso.

A primeira coisa que faço é criar uma classe para o gerenciador de fluxo de áudio que importa o AVFoundation . AVFoundation é uma estrutura no iOS que permite inspecionar, reproduzir, capturar e processar mídia audiovisual em plataformas Apple.

Essas são variáveis ​​que precisamos fazer.

private var audioEngine: AVAudioEngine?
private var soundAnalyzer: SNAudioStreamAnalyzer?
private var soundClassifierRequest: SNClassifySoundRequest?

Também temos que criar uma função para interromper a sessão de áudio quando não quisermos mais detectar os sons. O uso de uma sessão de áudio é para comunicar ao sistema operacional a natureza geral do áudio do seu aplicativo sem detalhar o comportamento específico ou as interações necessárias com o hardware de áudio.

func stopAudioSession() {
    let audioSession = AVAudioSession.sharedInstance()
    try? audioSession.setActive(false)
}

Também precisamos criar outra função para interromper a gravação de áudio ao vivo que está conectada à finalização de áudio. Portanto, temos que remover o nó do mecanismo de áudio.

func stopLiveRecord(){
    guard let audioEngine = audioEngine else {
        print("ERROR: AudioEngine Unavailable")
        return
    }
    audioEngine.inputNode.removeTap(onBus: 0)
    stopAudioSession()
}

A função abaixo é a função para preparar o soundClassifier usando o modelo que foi feito anteriormente.

private func prepareSoundClassifier(){
    let config = MLModelConfiguration()
    let soundClassifier = try? SoundPracticeModel_Rev(configuration: config)
    
    guard let soundClassifier = soundClassifier else{
        print("ERROR: Model doesn't Exist")
        return
    }
    soundClassifierRequest = try? SNClassifySoundRequest(mlModel: soundClassifier.model)
}

Por fim, para o gerenciador de fluxo de áudio. Faremos a função para iniciar o registro ao vivo

func startLiveRecord(){
    stopLiveRecord()
    do {
        let audioSession = AVAudioSession.sharedInstance()
        try audioSession.setCategory(.record, options: .mixWithOthers)
        try audioSession.setActive(true)
    } catch {
        stopAudioSession()
        print("ERROR: Stop Live record")
    }
    do{
        let newAudioEngine = AVAudioEngine()
        let busIndex = AVAudioNodeBus(0)
        let audioFormat = newAudioEngine.inputNode.inputFormat(forBus: busIndex)
        soundAnalyzer = SNAudioStreamAnalyzer(format: audioFormat)
        
        newAudioEngine.inputNode.removeTap(onBus: busIndex)
        newAudioEngine.inputNode.installTap(onBus: busIndex, bufferSize: 1024, format: audioFormat) {
            (buffer, time) in
            DispatchQueue.main.async {
                self.soundAnalyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime)
            }
        }
        try newAudioEngine.start()
        prepareSoundClassifier()
    }
    catch {
       print("ERROR: \(error)")
    }
}

Para verificar o nível de confiança do seu modelo, você pode criar outra classe que use SNResultObserving .

class SoundAnalyzer: NSObject, ObservableObject, SNResultsObserving {    
    var confidence: Double?
    var confidencePublisher = PublishSubject<Double>()
    
    var currentWord: String = ""
        
    func request(_ request: SNRequest, didProduce result: SNResult) {
        guard let result = result as? SNClassificationResult else{
            return
        }
        
        guard let highestResult = result.classifications.first else{
            return
        }
        
        DispatchQueue.main.async {
            if(self.currentWord == highestResult.identifier){
                self.confidence = highestResult.confidence
                self.confidencePublisher.onNext(self.confidence ?? 0)
            }
        }
    }
}

Foi tudo o que fiz para criar o recurso Sound Practice, que usava o analisador de som! Afinal, este é apenas o passo básico para dominar a jornada do Sound Analyzer

Obrigado pelo seu tempo para ler este artigo, espero que ajude você em sua jornada para dominar o Sound Analyzer!