👻
[Swift]AVAudioEngineを使用した超音波発信
はじめに
この記事では、AVAudioEngineを使用して超音波を発信する方法を説明していきます。
環境
- Xcode 12.5
- Swift 5.4
変数の用意
- valume
- 振幅の大きさを表すパラメータです(0.0 ~ 1.0)
- hz
- 周波数を表すパラメータです(おそらくサンプリング周波数の1/2までいけます)
- 今回は20kHzに設定しています
- sampleRate
- サンプリング周波数を表すパラメータです
- 今回は一般的な44.1KHzにしています
- player
- 入力ノードです
- 後に作成した波形データをセットします
let volume: Double = 1.0
let hz: Double = 20000.0
let sampleRate = 44100.0
let audioEngine = AVAudioEngine()
let session = AVAudioSession.sharedInstance()
let player = AVAudioPlayerNode()
波形データを作成
波形データフォーマットを設定します。
今回はサンプリング周波数を44.1kHzに、チャンネル数を1としました。
AVAudioPCMBufferを使用し、bufferを作成します。frameLengthとframeCapacityにサンプリング周波数を指定することで、1秒分の波形データを作成します。
let audioFormat = AVAudioFormat(standardFormatWithSampleRate: 44100.0, channels: 1)
guard let buf = AVAudioPCMBuffer(pcmFormat: audioFormat!, frameCapacity: AVAudioFrameCount(sampleRate)) else{
fatalError("Error initializing AVAudioPCMBuffer")
}
buf.frameLength = buf.frameCapacity
sin波の作成には角周波数を使用します。角周波数は2πfです。これをサンプリング周波数で割る理由は、周波数fがサンプリング周波数のときに1となるよう正規化しているためです。
あとは、bufferのchannel数が(AVAudioFormatより)1なので、bufferのchannel0にsin波の波形を代入していきます。
var theta = 0.0
let plus = 2.0 * .pi * self.hz / self.sampleRate
for i in 0..<Int(buf.frameLength) {
buf.floatChannelData?[0][i] = Float32(sin(theta) * volume)
theta += plus
}
以上のことをまとめてmakeBufferというメソッドを作成しておきます。
func makeBuffer() -> AVAudioPCMBuffer {
let audioFormat = AVAudioFormat(standardFormatWithSampleRate: 44100.0, channels: 1)
guard let buf = AVAudioPCMBuffer(pcmFormat: audioFormat!, frameCapacity: AVAudioFrameCount(sampleRate)) else{
fatalError("Error initializing AVAudioPCMBuffer")
}
buf.frameLength = buf.frameCapacity
var theta = 0.0
let plus = 2.0 * .pi * self.hz / self.sampleRate
for i in 0..<Int(buf.frameLength) {
buf.floatChannelData?[0][i] = Float32(sin(theta) * volume)
theta += plus
}
return buf
}
再生
再生モードにし、アクティブ化します。
// Audio Sessionを再生モードに変更
try! session.setCategory(AVAudioSession.Category.playback)
try! session.setActive(true)
AVAudioEngineは入力から出力までノードをつないで、音声データを流していきます。
作成した波形データをセットするplayerノードを追加し、outputNodeに接続します。
//Nodeを追加
audioEngine.attach(player)
//Format定義
let audioFormat = AVAudioFormat(standardFormatWithSampleRate: 44100.0, channels: 1)
// Nodeを接続(Player -> outputnode)
audioEngine.connect(player, to: audioEngine.outputNode, format: audioFormat)
AVAudioEngine.start()とplayer.play()で再生します。
scheduleBufferのoptionsを.loopsにすることで再生し続けます。
audioEngine.prepare()
try! audioEngine.start()
if !player.isPlaying {
player.play()
//再生音作成
let buffer = makeBuffer()
player.scheduleBuffer(buffer, at: nil, options: .loops, completionHandler: nil)
}
停止
AVAudioEngine.stop()とplayer.stop()し、sessionを非アクティブ化します。
// 再生停止
if player.isPlaying {
player.stop()
}
if audioEngine.isRunning {
audioEngine.stop()
// Audio sessionを停止
try! session.setActive(false)
}
おわり
全体のプログラムはこちらにあるのでぜひ
Discussion