Swift AVAudioEngine でサイン波を鳴らす
AVAudioEngine を使用して、440Hzのサイン波(ラ音)をPCMバッファーに設定し、鳴らす。 AVAudioEngineの基本的な使い方は Swift AVAudioEngine の基本 - タコさんブログ を参照。
環境
- Xcode 7.3.1
- Swift 2.2
準備
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) } }