タコさんブログ

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

Swift AVAudioEngine の基本

環境

準備

AVFoundation をインポートする。

import AVFoundation

AVAudioEngine

AVAudioEngine は 接続された audio node(AVAudioNode) のグループを定義する。 Audio node を使用してオーディオシグナルを生成、それらの処理、オーディオ入力・出力を実行する。

Audioエンジンを使用する方法は、 Audio node をそれぞれ生成し、audioエンジンへアタッチする。

エンジンの利用手順

  1. エンジンの生成
  2. ノードの生成
  3. エンジンにノードをアタッチ
  4. ノード同士を接続
  5. エンジンをスタート

AVAudioNode

AVAudioNodeはオーディオの生成・処理・I/Oブロックの抽象クラス。 ノードは入力・出力バス(接続ポイントのようなもの)を持っている。ミキサーは複数の入力バスがあり、1つの出力バスがある。 全てのバスにはバスと関連してフォーマットがあり、AVAudioMixerNode と AVAudioOutputNode 以外はノードどうしを接続するときフォーマットが同じである必要がある。

ノードの種類

  • Source (ex. Player, microphone)
  • Process (ex. Mixer, effect)
  • Destination (ex. Speaker)

Source ノードと Destination ノードが接続されている状態をActive Chainと言う。Active Chainでない状態をInactive Chainと言う。

Active Chain 例)

 Source Node (Player) - connect -> Destination Node
 Source Node (Player) - connect -> Processing Node - connect -> Destination Node

出力ノード (Output Node)

エンジンには暗黙に出力ノード(Output node)と呼ばれる Desitination Node があり、出力ノードはオーディオデータを出力ハードウェアに提供する。

ミキサーノード (Mixer Node)

ミキサーノードはN個の入力をミックスし、1つの出力へ出力する。 エンジンには暗黙的にミキサーノードがあり、追加のミキサーノードを生成し、エンジンにアタッチすることができる。ミキサーのインプットには異なるオーディオフォーマットを使用することができる。

 Input 1 -> | ---------------- |
 Input 2 -> |                  |
 Input 3 -> | AVAudioMixerNode |->
   .     -> |                  |
 Input N -> | ---------------- |

以下のようにSub-Mixingをすることも可能。

 Input 1 -> | ------------------ |
   .     -> |  AVAudioMixerNode  | -> | ---------------------------- |
 Input N -> | ------------------ |
                                      | AVAudioMixerNode (Main Mixer)|->
 Input 1 -> | ------------------ |
   .     -> |  AVAudioMixerNode  | -> | ---------------------------- |
 Input M -> | ------------------ |

プレイヤーノード(Player Node)

プレイヤーノード(AVPlayerNode)はファイル(AVAudioFile)またはバッファ(AVAudioBuffer)からデータをスケジュール(特定の時間に)し再生する。

エンジンとノードの生成・ノードのアタッチ

以下のようにして、エンジンとソースノード(Playerノード)を生成し、エンジンにノードをアタッチする。

// エンジンの生成
let engine = AVAudioEngine()
// ソースノードの生成
let player = AVAudioPlayerNode()
// エンジンにノードをアタッチ
engine.attachNode(player)

プレイヤーの設定(ファイル)

ファイルからプレイヤーをスケジュールする例。

do {
  // オーディオファイルの取得
  let audioFile = try AVAudioFile(forReading: url)
  // エンジンからメインミキサを取得
  let mainMixer = engine.mainMixerNode
  // プレイヤーノードとメインミキサーを接続
  engine.connect(player, to: mainMixer, format: audioFile.processingFormat)
  // プレイヤーをすぐに開始するようにスケジュール(atTimeをnil)
  player.scheduleFile(audioFile, atTime:nil, completionHandler:nil)
} catch let error {
  print(error)
}

プレイヤーの設定(バッファ)

バッファからプレイヤーをスケジュールする例。

// バッファを生成または取得
let buffer: AVAudioPCMBuffer = ...
// メインミキサの取得
let mainMixer = engine.mainMixerNode
// プレイヤーノードとメインミキサーを接続
engine.connect(player, to: mainMixer, format: buffer.format)
// プレイヤのスケジュール
player.scheduleBuffer(buffer, atTime:nil, completionHandler:nil)

エンジンの開始とプレイヤーの再生

do {
  // エンジンの開始
  try engine.start()
  // プレイヤーの再生
  player.start
} catch let error {
  print(error)
}

プレイヤーのスケジュールオプション

AVPlayer.scheduleFile(audioFile,atTime,options,completionHandler) のoptionsに設定する。

設定できる options

  • Loops (ループ再生)
  • Interrupts (再生中のオーディオをすぐに停止して、再生)
  • InterruptsAtLoop (ループ再生が終了してから再生)

ループ再生オプションの例。

player.scheduleFile(audioFile,
                    atTime:nil,
                    Loops,
                    completionHandler:nil)

再生の開始時間を設定する

10秒後に再生する例。

let tenSecInTheFuture = AVAudioTime.timeWithSampleTime(10 * buffer.format.sampleRate), atRate: buffer.format.sampleRate)
player.scheduleFile(audioFile, atTime:tenSecInTheFuture, completionHandler:nil)
player.play()

Node Tap

Node Tapを使用すればデータをプルすることができる。

Outputバス(インデックス0)にタップをインストールする例。

let format = mixier.outputFormatForBus(0)
mixier.installTapOnBuss(0,
                        bufferSize:4096,
                        format:format) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
  // Process buffer
}

ノードを非接続にする

入力ノードを非接続にする例。

engine.disconnectNodeOutput(input)

オーディオの再生例

// インスタンス変数
// エンジンの生成
let audioEngine = AVAudioEngine()
// Playerノードの生成
let player = AVAudioPlayerNode()

func play() {
  if let url = NSBundle.mainBundle().URLForResource("sound-file", withExtension: "wav") {
    do {
      // オーディオファイルの取得
      let audioFile = try AVAudioFile(forReading: url)
      // エンジンにノードをアタッチ
      audioEngine.attachNode(player)
      // メインミキサの取得
      let mixer = audioEngine.mainMixerNode
      // Playerノードとメインミキサーを接続
      audioEngine.connect(player,
                          to: mixer,
                          format: audioFile.processingFormat)
      // プレイヤのスケジュール
      player.scheduleFile(audioFile, atTime: nil) {
          print("complete")
      }
      // エンジンの開始
      try audioEngine.start()
      // オーディオの再生
      player.play()
    } catch let error {
      print(error)
    }
  } else {
    print("File not found")
  }
}

参考URL