環境: Xcode10.3、Swift 5.0.1
iPhone のマイクから拾ったオーディオ情報から音程を判定する方法について調べた。
ちゃんとやるには、離散フーリエ変換 (discrete Fourier transform) を使って周波数を算出するらしいのだけれども、今回はAudioKit
というOSSライブラリを使うことで簡単に実現できたのでメモ。
やりたいことがそのまま公式Example(GitHubコードはこちら)として実装されていたのでこの通りに進めた。
画面にはマイクから拾った音の周波数(Frequency)と音階(Note)、また波形がリアルタイムで表示される。
AudioKit インストール
プリコンパイル済みのFrameworkはこちらからダウンロードできる。
プロジェクトにダウンロードしたAudioKit.framework
を追加、
そして TARGET の Build Settings > Linking > Other Linker Flags に -lc++
の追加する。
CocoaPods の場合
Podfile
pod 'AudioKit', '~> 4.0'
インストール
$ pod install
Carthage の場合
Cartfile
github "AudioKit/AudioKit"
ビルド
# iOS $ carthage update --platform iOS --no-use-binaries --cache-builds --new-resolver # Mac $ carthage update --platform Mac --no-use-binaries --cache-builds --new-resolver
さらに TARGET の Build Settings > Linking > Other Linker Flags に -lc++
の追加が必要。
Swift コード
Exampleのコードそのまま。今回は波形表示は不要だったので省いた。
AKFrequencyTracker
クラスを利用することでピッチ検出することができる。
ただし、現状は検出できるのは単音(モノフォニック)のみで、和音のような複数音(ポリフォニック)には対応していないようだ。
import UIKit import AudioKit class ViewController: UIViewController { @IBOutlet weak var frequencyLabel: UILabel! @IBOutlet weak var amplitudeLabel: UILabel! @IBOutlet weak var noteNameWithSharpsLabel: UILabel! @IBOutlet weak var noteNameWithFlatsLabel: UILabel! var mic: AKMicrophone! var tracker: AKFrequencyTracker! var silence: AKBooster! let noteFrequencies = [16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, 29.14, 30.87] let noteNamesWithSharps = ["C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B"] let noteNamesWithFlats = ["C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭", "A", "B♭", "B"] override func viewDidLoad() { super.viewDidLoad() AKSettings.audioInputEnabled = true mic = AKMicrophone() tracker = AKFrequencyTracker(mic) silence = AKBooster(tracker, gain: 0) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) AudioKit.output = silence do { try AudioKit.start() } catch { AKLog("AudioKit did not start!") } Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ViewController.updateUI), userInfo: nil, repeats: true) } @objc func updateUI() { if tracker.amplitude > 0.1 { frequencyLabel.text = String(format: "%0.1f", tracker.frequency) var frequency = Float(tracker.frequency) while frequency > Float(noteFrequencies[noteFrequencies.count - 1]) { frequency /= 2.0 } while frequency < Float(noteFrequencies[0]) { frequency *= 2.0 } var minDistance: Float = 10_000.0 var index = 0 for i in 0..<noteFrequencies.count { let distance = fabsf(Float(noteFrequencies[i]) - frequency) if distance < minDistance { index = i minDistance = distance } } let octave = Int(log2f(Float(tracker.frequency) / frequency)) noteNameWithSharpsLabel.text = "\(noteNamesWithSharps[index])\(octave)" noteNameWithFlatsLabel.text = "\(noteNamesWithFlats[index])\(octave)" } amplitudeLabel.text = String(format: "%0.2f", tracker.amplitude) } }
コード内にあるnoteFrequencies
配列の数値の意味がわからなかったが、調べたところ音高(Pitch)の周波数らしい。
オクターブ 4 のラの音A4 = 440Hz
を基準音とすると以下の表のようになる。
1オクターブ高いと周波数は2倍になる。
Frequency (Hz) | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Octave | Note | |||||||||||
C | C# | D | Eb | E | F | F# | G | G# | A | Bb | B | |
0 | 16.35 | 17.32 | 18.35 | 19.45 | 20.60 | 21.83 | 23.12 | 24.50 | 25.96 | 27.50 | 29.14 | 30.87 |
1 | 32.70 | 34.65 | 36.71 | 38.89 | 41.20 | 43.65 | 46.25 | 49.00 | 51.91 | 55.00 | 58.27 | 61.74 |
2 | 65.41 | 69.30 | 73.42 | 77.78 | 82.41 | 87.31 | 92.50 | 98.00 | 103.8 | 110.0 | 116.5 | 123.5 |
3 | 130.8 | 138.6 | 146.8 | 155.6 | 164.8 | 174.6 | 185.0 | 196.0 | 207.7 | 220.0 | 233.1 | 246.9 |
4 | 261.6 | 277.2 | 293.7 | 311.1 | 329.6 | 349.2 | 370.0 | 392.0 | 415.3 | 440.0 | 466.2 | 493.9 |
5 | 523.3 | 554.4 | 587.3 | 622.3 | 659.3 | 698.5 | 740.0 | 784.0 | 830.6 | 880.0 | 932.3 | 987.8 |
6 | 1047 | 1109 | 1175 | 1245 | 1319 | 1397 | 1480 | 1568 | 1661 | 1760 | 1865 | 1976 |
7 | 2093 | 2217 | 2349 | 2489 | 2637 | 2794 | 2960 | 3136 | 3322 | 3520 | 3729 | 3951 |
8 | 4186 | 4435 | 4699 | 4978 | 5274 | 5588 | 5920 | 6272 | 6645 | 7040 | 7459 | 7902 |
ピアノの鍵盤数は88で7オクターブ、この表での範囲はA0 = 27.50Hz
からC8 = 4186Hz
になる。
ちなみに日本のピアノはA4 = 442Hz
で調律されることが多いとのこと。
水色が真ん中のド (C4)、英語では middle C と言う、黄色がラ (A4)
参考: