タコさんブログ

プログラミングメモと小言

Swift AVAudioEngine でサイン波を鳴らす

AVAudioEngine を使用して、440Hzのサイン波(ラ音)をPCMバッファーに設定し、鳴らす。 AVAudioEngineの基本的な使い方は Swift AVAudioEngine の基本 - タコさんブログ を参照。

環境

準備

AVFoundation をインポートする

import AVFoundation

サイン波

周波数fのサイン波は以下の式で与えられる。

 x(n) = sin(2 * PI * f * n/fs)

ここで、
 n = 0, 1, 2, ...
 fs はサンプリング周波数

この式を使用すると、サンプリング周波数 44.1k Hz, 440 Hzのサイン波は以下のようになる。

for n in 0..<some_buffer_length {
  samples[n] = sinf(Float(2.0 * M_PI) * 440.0 * Float(n) / sampleRate)
}

PCMバッファーの生成と設定

2 ch, 44.1k Hz, 非インターリーブドのオーディオフォーマット(audioFormat)に対し、PCMバッファーを以下のように生成する。

let buffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity:UInt32(44100))
buffer.frameLength = UInt32(44100)

上記のバッファー変数(buffer)に対して、サイン波の値を設定する。

// オーディオフォーマットからオーディオのチャンネル数を取得
let channels = Int(audioFormat.channelCount)
for ch in (0..<channels) {
  let samples = buffer.floatChannelData[ch]
  for n in 0..<Int(buffer.frameLength) {
    samples[n] = sinf(Float(2.0 * M_PI) * 440.0 * Float(n) / sampleRate)
  }
}

このバッファーをAVAudioPlayerNode.scheduleBuffer()でスケジュールし、再生すればサイン波が鳴る。

AVAudioEngine, AVAudioPlayerNode をインスタンス変数として宣言する。

// エンジンの生成
let audioEngine = AVAudioEngine()
// ソースノードの生成
let player = AVAudioPlayerNode()

playSineWave関数でオーディオエンジン・バッファーを設定し、サイン波を再生する。

func playSineWave() {
  // プレイヤーノードからオーディオフォーマットを取得
  let audioFormat = player.outputFormatForBus(0)
  // サンプリング周波数: 44.1K Hz
  let sampleRate = Float(audioFormat.sampleRate)
  // 3秒間鳴らすフレームの長さ
  let length = 3.0 * sampleRate
  // PCMバッファーを生成
  let buffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity:UInt32(length))
  // frameLength を設定することで mDataByteSize が更新される
  buffer.frameLength = UInt32(length)
  // オーディオのチャンネル数
  let channels = Int(audioFormat.channelCount)
  for ch in (0..<channels) {
    let samples = buffer.floatChannelData[ch]
    for n in 0..<Int(buffer.frameLength) {
      samples[n] = sinf(Float(2.0 * M_PI) * 440.0 * Float(n) / sampleRate)
    }
  }
      
  // オーディオエンジンにプレイヤーをアタッチ
  audioEngine.attachNode(player)
  let mixer = audioEngine.mainMixerNode
  // プレイヤーノードとミキサーノードを接続
  audioEngine.connect(player, to: mixer, format: audioFormat)
  // 再生の開始を設定
  player.scheduleBuffer(buffer) {
      print("Play completed")
  }
    
  do {
    // エンジンを開始
    try audioEngine.start()
    // 再生
    player.play()
  } catch let error {
    print(error)
  }
}

参考URL