👻

[Swift]AVAudioEngineを使用した超音波発信

2021/10/11に公開

はじめに

この記事では、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

ログインするとコメントできます