LoginSignup
1
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-09-02

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)

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

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2