ffmpeg
Swift3.0
AVAudioEngine
Accelerate

FFmpeg->AVFrame(Audio)->AVAudioEngine再生のやり方。+ Accelerateフレームワークの能力も借ります。

More than 1 year has passed since last update.


2016.09.05


  • オディオのAVFramelinesizenb_samplesについてAVAudioPCMBufferの設定値をもっと精密にやり直しました。


let audioContext: UnsafeMutablePointer<AVCodecContext>

// プレイするサウンドの基本設定
let audioFormat: AVAudioFormat(
commonFormat: .pcmFormatFloat32,
sampleRate: Double(audioContext.pointee.sample_rate),
channels: 2, // サウンドシステムのoutputチャンネルを超えたらクラッシューが発生されます。
interleaved: false)

let audioEngine = AVAudioEngine()
let audioPlayer = AVAudioPlayerNode()

// オディオエンジンにプレイヤーを付けます。
engine.attach(audioPlayer)
// オディオエンジンの基本出力ノードにプレイヤーを連結します。
engine.connect(audioPlayer, to: engine.mainMixerNode, format: audioFormat)

engine.prepare()

try! engine.start()

audioPlayer.play()
...
// ディコードされたAVFrameをAVAudioPCMBufferに格納します。
let frame: AVFrame

// frameCapacityはframeに配置されているfloatデータの個数を計算し渡します。
let pcmBuffer = AVAudioPCMBuffer(
pcmFormat: audioFormat,
frameCapacity: AVAudioFrameCount(frame.linesize.0 / MemoryLayout<Float>.size))
// frameLengthは実際のaudioのfloatデータの個数を渡します。
// バーファーの長さとsamplesの個数は同じ場合も違う場合もあります。
pcmBuffer.frameLength = frame.nb_samples // AVAudioFileを利用する場合は自動に設定されるところですが、その以外は直接やります。

// ファファーに設定したformatでかくチャンネルのデータのメモリが配置されます。
// 普通はステレオで2個になります。
let channels = buffer.floatChannelData!
let leftOutputChannel: UnsafeMutablePointer<UnsafeMutablePointer<Float>> = channels[0]
let rightOutputChannel: UnsafeMutablePointer<UnsafeMutablePointer<Float>> = channels[1]

// frameのデータタイプは常にUInt8なのでFloatに変えます。
let leftAudioBuffer: UnsafeMutablePointer<Float> = frame.data.0!.withMemoryRebound(to: Float.self, capacity: Int(pcmBuffer.frameLength)){$0}
let rightAudioBuffer: UnsafeMutablePointer<Float> = frame.data.1!.withMemoryRebound(to: Float.self, capacity: Int(pcmBuffer.frameLength)){$0}

// leftAudioBufferの内容をAccelerateの関数を利用し複写します。
cblas_scopy(Int32(pcmBuffer.frameLength), leftAudioBuffer, 1, channels[0], 1)
cblas_scopy(Int32(pcmBuffer.frameLength), rightAudioBuffer, 1, channels[1], 1)

// オディオのデータが2チャンネル以上を持つ場合、ここでは5.1chのオディオだと仮定します。
// 出力のチャンネルは二つなので余るデータはAccelerateの関数を使って既存のデータに加えます。
let lbuf1 = frame.data.2!.withMemoryRebound(to: Float.self, capacity: Int(buffer.frameLength)){$0}
let rbuf1 = frame.data.3!.withMemoryRebound(to: Float.self, capacity: Int(buffer.frameLength)){$0}
let lbuf2 = frame.data.4!.withMemoryRebound(to: Float.self, capacity: Int(buffer.frameLength)){$0}
let rbuf2 = frame.data.5!.withMemoryRebound(to: Float.self, capacity: Int(buffer.frameLength)){$0}

vDSP_vadd(channels[0], 1, lbuf1, 1, channels[0], 1, vDSP_Length(buffer.frameLength))
vDSP_vadd(channels[1], 1, rbuf1, 1, channels[1], 1, vDSP_Length(buffer.frameLength))
vDSP_vadd(channels[0], 1, lbuf2, 1, channels[0], 1, vDSP_Length(buffer.frameLength))
vDSP_vadd(channels[1], 1, rbuf2, 1, channels[1], 1, vDSP_Length(buffer.frameLength))

//最後に作ったオディオのbufferをAVAudioPlayerNodeに入れます。
audioPlayer.scheduleBuffer(pcmBuffer, completionHandler: nil)

で、サウンドの再生が可能になります。