KOSEN{BLOG}について

SoundAnalysisを使って音声分類アプリ開発

実際の動作の様子

hiro

リアルタイムにマイクから入力された音を
10つの音楽のジャンルに分けています。

必要なもの

環境

  • macOS: Catalina
  • Xcode 11.6
  • Swift 5.2

CreateMLを使用できる環境に整備してください。

ダウンロードするもの

  • モデルを作成するための音源

今回は音楽のジャンル分けを行うための音源としてKaggleの音源を使用させてもらいました。

https://www.kaggle.com/andradaolteanu/gtzan-dataset-music-genre-classification?

CreateMLでモデルを作成する

 

Training Dataにダウンロードしたファイルのgenres_originalフォルダをドラック&ドロップし上にあるTrainボタンをクリックすれば学習が始まります。

今回のデータセットの音源は10個クラス分けを行います。

注意

ファイル数が多いのでかなり時間がかかります。 1時間ちょっとかかりました。

試しにやってみたい方はファイル数を少なくして学習を開始してください。

 

モデルの変換の流れ

Sound Analysis Preprocessing → Neural Network → GLM Classifier

一般化線形モデルに変換されていた。

実装方法

今回記事は要点をまとめた記事になっているので、

実際にソースを動かしたい方はGitHubのソースコードを使ってください。

プライバシー設定

Info.plistPrivacy - Microphone Usage Descriptionを追加

初期化設定

今回使用するフレームワークを準備する


import AVKit
import SoundAnalysis

private let audioEngine = AVAudioEngine()
private var soundClassifier = MySoundClassifier()

var inputFormat: AVAudioFormat!
var analyzer: SNAudioStreamAnalyzer!
var resultsObserver = ResultsObserver()
let analysisQueue = DispatchQueue(label: "com.tanakahirokazu.AnalysisQueue")
  • audioEngine ストリーミングオーディオのキャプチャ
    • inputFormat 音声フォーマット
  • soundClassifier 音声分類モデル
  • analyzer キャプチャしたオーディオを解析
  • resultsObserver 結果を処理
  • analysisQueue オーディオ分析を行うための専用のキュー

ストリーミングオーディオのキャプチャ方法

AVAudioEngineを使用してマイクからオーディオデータをキャプチャすることで、分析用のデータを取得する。


private func startAudioEngine() {
        
        inputFormat = audioEngine.inputNode.inputFormat(forBus: 0)
        do{
            try audioEngine.start()
        }catch( _){
            print("error in starting the Audio Engin")
        }
    }

アナライザをセットアップする

アナライザは2種類存在していて、ファイルから処理を行うものとリアルタイム処理を行うものがある。

  • SNAudioFileAnalyzer
  • SNAudioStreamAnalyzer

どちらも使い方は変わらない。


private func setUpAnalyzer() {
        
        //分析するものはマイクの音声(ストリーミング)
        analyzer = SNAudioStreamAnalyzer(format: inputFormat)
        
        do {
            let request = try SNClassifySoundRequest(mlModel: soundClassifier.model)
            try analyzer.add(request, withObserver: resultsObserver)
        } catch {
            print("Unable to prepare request: \\(error.localizedDescription)")
            return
        }

    }

ストリームアナライザーを作成

// Create a new stream analyzer.
analyzer = SNAudioStreamAnalyzer(format: inputFormat)
  1. SNAudioStreamAnalyzerの設定 AVAudioEngineのフォーマットを渡す。
  2. Soundリクエストの作成 MLモデルを渡してリクエストを作成
  3. analyzerに2のリクエストと結果を受け取るObserverを渡す。Observerの説明は後述

解析を開始


private func startAnalyze() {
        
        audioEngine.inputNode.installTap(onBus: 0, bufferSize: 8000, format: inputFormat) { buffer, time in
            self.analysisQueue.async {
                self.analyzer.analyze(buffer, atAudioFramePosition: time.sampleTime)
            }
        }
    }
  1. AudioEngineinputNodeにタップをインストール マイクでキャプチャされたデータのストリームにアクセスできるようになる
  2. それぞれを分析キューにディスパッチし、アナライザーに現在のフレーム位置で分析を開始

分析結果の受け取り

分析プロセスを観察するには、SNResultsObservingクラスを継承するオブジェクトを作成する。


class ResultsObserver: NSObject, SNResultsObserving {
    var delegate: ClassifierDelegate?
    
    func request(_ request: SNRequest, didProduce result: SNResult) {
        guard let result = result as? SNClassificationResult,
            let classification = result.classifications.first else { return }
        
        
        let confidence = classification.confidence*100
        
        if confidence > 60 {
            delegate?.displayPredictionResult(identifier: classification.identifier, confidence: confidence)
        }
    }
    
    func request(_ request: SNRequest, didFailWithError error: Error) {
        print("The the analysis failed: \\(error.localizedDescription)")
    }
    
    func requestDidComplete(_ request: SNRequest) {
        print("The request completed successfully!")
    }
    
}

func request(_ request: SNRequest, didProduce result: SNResult)メソッドで分析結果を受け取る。

したがってここにデータを取得するコードを書く。


guard let result = result as? SNClassificationResult,
            let classification = result.classifications.first else { return }
        
        
        let confidence = classification.confidence*100
        
        if confidence > 60 {
	    print(identifier:\(classification.identifier),confidence:\(confidence)")
            delegate?.displayPredictionResult(identifier: classification.identifier, confidence: confidence)
        }

今回は、一番認識が大きい結果を出力するためにdelegateメソッドを使用している。

詳しくはGitHubのソースコードから

delegateを使用しなくても、ただプリントするだけでも結果は得られるので試してもらいたい。

参考文献

Sound Classification on iOS Using Core ML 3 and Create ML

AppleDocumentation – Analyzing Audio to Classify Sounds

コメントを残す

メールアドレスが公開されることはありません。