Pebble's Diary

プログラマーの作業メモ

詳説 AudioStreamBasicDescription / AVAudioFormat

CoreAudioでよく使われるAudioStreamBasicDescription これはC言語の構造体で使いづらいです。
macOS10.10, iOS8以降ではAVAudioFormatと相互変換できますので、これを経由するとコーディングが簡単になります。

まずは実例を見ていきます。
よく使われる.wavファイルのフォーマットを作ってみます。

let fmt = AVAudioFormat(commonFormat:.pcmFormatInt16,
                        sampleRate:44100,
                        channels:2,
                        interleaved:true)
print("\(fmt)")
// <AVAudioFormat 0x100b06650:  2 ch,  44100 Hz, Int16, inter>
let asbd:AudioStreamBasicDescription = fmt.streamDescription.pointee
// AudioStreamBasicDescription(mSampleRate: 44100.0, mFormatID: 1819304813, mFormatFlags: 12, mBytesPerPacket: 4, mFramesPerPacket: 1, mBytesPerFrame: 4, mChannelsPerFrame: 2, mBitsPerChannel: 16, mReserved: 0)
print("\(asbd)")

1819304813(=0x6c70636d)というのは kAudioFormatLinearPCM = 'lpcm'の10進値で、線形PCM形式であることを示しています。
フォーマットフラグの12は
12=8(=kAudioFormatFlagIsPacked)+4(kAudioFormatFlagIsSignedInteger)
を意味します。

例をもう一つ見て見ましょう。

import AVFoundation
let fmt = AVAudioFormat(standardFormatWithSampleRate:44100, channels:2)
//<AVAudioFormat 0x100a00bb0:  2 ch,  44100 Hz, Float32, non-inter>
let asbd:AudioStreamBasicDescription = fmt.streamDescription.pointee
print("\(asbd)")
//AudioStreamBasicDescription( mSampleRate: 44100.0, mFormatID: 1819304813, mFormatFlags: 41, mBytesPerPacket: 4, 
//mFramesPerPacket: 1, mBytesPerFrame: 4, mChannelsPerFrame: 2, mBitsPerChannel: 32, mReserved: 0)

標準フォーマットとはfloat32のnon-interleavedの事だとわかります。
non-interleaveとはステレオの場合に、
LRの音データをLLL..., RRR...
のように別領域に割り当てることを指します。
interleaveの場合LRLR...のように交互に割り当てます。
フォーマットフラグの41は
41=32(kAudioFormatFlagsIsNonInterleaved)+8(kAudioFormatFlagsIsPacked)+1(kAudioFormatFlagIsFloat)
を意味します。

クラス構造を見てきます。

open class AVAudioFormat : NSObject, NSSecureCoding {
    public init(streamDescription asbd: UnsafePointer<AudioStreamBasicDescription>)
    public init(streamDescription asbd: UnsafePointer<AudioStreamBasicDescription>, channelLayout layout: AVAudioChannelLayout?)
    public init(standardFormatWithSampleRate sampleRate: Double, channels: AVAudioChannelCount)
    public init(standardFormatWithSampleRate sampleRate: Double, channelLayout layout: AVAudioChannelLayout)
    public init(commonFormat format: AVAudioCommonFormat, sampleRate: Double, channels: AVAudioChannelCount, interleaved: Bool)
    public init(commonFormat format: AVAudioCommonFormat, sampleRate: Double, interleaved: Bool, channelLayout layout: AVAudioChannelLayout)
    public init(settings: [String : Any])
    public init(cmAudioFormatDescription formatDescription: CMAudioFormatDescription)
    open func isEqual(_ object: Any) -> Bool
    open var isStandard: Bool { get }
    open var commonFormat: AVAudioCommonFormat { get }
    open var channelCount: AVAudioChannelCount { get }
    open var sampleRate: Double { get }
    open var isInterleaved: Bool { get }
    open var streamDescription: UnsafePointer<AudioStreamBasicDescription> { get }
    open var channelLayout: AVAudioChannelLayout? { get }
    open var magicCookie: Data?
    open var settings: [String : Any] { get }
    open var formatDescription: CMAudioFormatDescription { get }
}

// モノラルなら1,ステレオなら2の値です
public typealias AVAudioChannelCount = UInt32

// よく使うのはpcmFormatFloat32とpcmFormatInt16でしょう
public enum AVAudioCommonFormat : UInt {
    case otherFormat
    case pcmFormatFloat32
    case pcmFormatFloat64
    case pcmFormatInt16
    case pcmFormatInt32
}

formatDescriptionプロパティとsettingsプロパティに何が入っているのかも見てみましょう。

let fmt = AVAudioFormat(commonFormat:.pcmFormatInt16,
                        sampleRate:44100,
                        channels:2,
                        interleaved:true)
print("\(fmt.formatDescription)")
print("\(fmt.settings)")

<CMAudioFormatDescription 0x100a075a0 [0x7fffd7221d80]> {
    mediaType:'soun' 
    mediaSubType:'lpcm' 
    mediaSpecific: {
        ASBD: {
            mSampleRate: 44100.000000 
            mFormatID: 'lpcm' 
            mFormatFlags: 0xc 
            mBytesPerPacket: 4 
            mFramesPerPacket: 1 
            mBytesPerFrame: 4 
            mChannelsPerFrame: 2 
            mBitsPerChannel: 16    } 
        cookie: {(null)} 
        ACL: {(null)}
        FormatList Array: {(null)} 
    } 
    extensions: {(null)}
}
["AVLinearPCMIsFloatKey": 0, "AVLinearPCMIsNonInterleaved": 0, "AVNumberOfChannelsKey": 2, "AVSampleRateKey": 44100, "AVLinearPCMBitDepthKey": 16, "AVLinearPCMIsBigEndianKey": 0, "AVFormatIDKey": 1819304813]

settingsには文字列キーのDictionaryが入っているようです。