在Swift中使用AudioBufferList

时间:2014-07-19 07:56:32

标签: ios objective-c pointers casting swift

我在Swift中有一个桥接函数,其中一个参数在C中是AudioBufferList *。在Swift中,这会生成UnsafePointer<AudioBufferList>。我设法通过调用audioData[0]来指导指针(有更好的方法吗?)。但是我在接下来的两个层次中挣扎:.mBuffers AudioBuffer数组及其void * / UnsafePointer<()> .mData成员。

在C中它只是

Float32 *audioData = (Float 32*)abl->mBuffers[0]->mData;
output = audioData[sampleNum]...

在Swift中,第一个奇怪的事情是它不允许我访问mBuffers的元素,但是当我作为属性访问它时非常高兴。换句话说,这有效,甚至有正确的数据(我认为mBuffers的第一个成员)......

println(abl[0].mBuffers.mNumberChannels)  // But .mBuffers should be an []!

其次,它让我打印出.mData下标,但值始终为()

println(abl[0].mBuffers.mData[10])  // Prints '()'

我已经尝试了各种投射操作并使用多个索引进行访问,但无济于事......有什么想法吗?

为方便起见,以下是AudioBufferListAudioBuffer的C和Swift定义...

// C
struct AudioBufferList
{
    UInt32      mNumberBuffers;
    AudioBuffer mBuffers[1]; // this is a variable length array of mNumberBuffers elements
    // ...and a bit more for c++
}


struct AudioBuffer
{
    UInt32  mNumberChannels;
    UInt32  mDataByteSize;
    void*   mData;
};

...

// SWIFT

struct AudioBufferList {
    var mNumberBuffers: UInt32
    var mBuffers: (AudioBuffer)
}

struct AudioBuffer {
    var mNumberChannels: UInt32
    var mDataByteSize: UInt32
    var mData: UnsafePointer<()>
}

4 个答案:

答案 0 :(得分:19)

我偶然发现了这个。奇怪的是,当它提出UnsafeMutableAudioBufferListPointer时,前面的类型实际上正在使用Swift。您可以使用UnsafeMutablePointer参数进行初始化。此类型是MutableCollectionType,并提供对包含的音频缓冲区的下标和生成器访问。

例如,您可以使用以下代码将ABL设置为静音

func renderCallback(ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {

    let abl = UnsafeMutableAudioBufferListPointer(ioData)

    for buffer in abl {

        memset(buffer.mData, 0, Int(buffer.mDataByteSize))
    }

    return noErr
}

答案 1 :(得分:11)

编辑:Adam Ritenauer的答案可能是现在最好的答案。要对其进行扩展,您可以在the iOS 8.3 Core Audio changes中查看新的实用程序函数/类型。

UnsafeMutableAudioBufferListPointer可用于读取/访问某些给定数据:

struct UnsafeMutableAudioBufferListPointer {
    init(_ p: UnsafeMutablePointer<AudioBufferList>)
    var count: Int
    subscript (index: Int) -> AudioBuffer { get nonmutating set }
}

你可以在AudioBuffer&amp; amp; AudioBufferList分配你自己的:

extension AudioBufferList {
    static func sizeInBytes(maximumBuffers maximumBuffers: Int) -> Int
    static func allocate(maximumBuffers maximumBuffers: Int) -> UnsafeMutableAudioBufferListPointer
}

extension AudioBuffer {
    init<Element>(_ typedBuffer: UnsafeMutableBufferPointer<Element>, numberOfChannels: Int)
}

旧答案:

这有点棘手,因为AudioBufferList实际上是一个可变大小的结构。这意味着它被声明为具有单个AudioBuffer,但实际上它具有mNumberBuffers成员指定的数量。这个概念并没有很好地转化为Swift,这就是为什么你看到var mBuffers: (AudioBuffer)

因此,访问这些缓冲区及其数据的规范方法是使用UnsafeArray。下面的代码提供了一些想法,但UnsafePointerUnsafeArray没有很好的记录,所以这可能是错误的。

// ***WARNING: UNTESTED CODE AHEAD***

let foo: UnsafePointer<AudioBufferList> // from elsewhere...

// This looks intuitive, but accessing `foo.memory` may be doing a copy.
let bufs = UnsafeArray<AudioBuffer>(start: &foo.memory.mBuffers, length: Int(foo.memory.mNumberBuffers))

// This is another alternative that should work...
let bufsStart = UnsafePointer<AudioBuffer>(UnsafePointer<UInt32>(foo) + 1) // Offset to mBuffers member
let bufs = UnsafeArray<AudioBuffer>(start: bufsStart, length: Int(foo.memory.mNumberBuffers))

// Hopefully this isn't doing a copy, but it shouldn't be too much of a problem anyway.
let buf: AudioBuffer = bufs[0] // or you could use a for loop over bufs, etc.

typealias MySample = Float32
let numSamples = Int(buf.mDataByteSize / UInt32(sizeof(MySample)))
let samples = UnsafeArray<MySample>(start: UnsafePointer<MySample>(buf.mData), length: numSamples)

// Now use the samples array...

这似乎适用于游乐场,但我很难测试真实的音频数据。特别是,我并非100%确定使用start: &foo.memory.mBuffers将按预期工作。 (它返回与原始指针不同的指针,尽管数据似乎在那里。)试一试并报告回来!

编辑:顺便说一下,调试一下,例如:

(lldb) p foo
(UnsafePointer<AudioBufferList>) $R1 = (value = Builtin.RawPointer = 0x0000000100700740)
(lldb) expr -lc -- ((int*)0x0000000100700740)[0]
(int) $2 = 42
(lldb) expr -lc -- ((int*)0x0000000100700740)[1]
(int) $3 = 43
...

答案 2 :(得分:1)

我发现这个工作正常。 abl是通过加载16位AIFF音频文件创建的AudioBufferList。

let mBuffers=abl.memory.mBuffers

let data=UnsafePointer<Int16>(mBuffers.mData)
let dataArray=UnsafeBufferPointer<Int16>(start:data, count: Int(mBuffers.mDataByteSize)/sizeof(Int16))

//checking resulting array
let count=dataArray.count //this matches the expected number of samples in my case
for i in 0..<count
{
    print(dataArray[i]) //values look OK in my case
    print(" ")
}

答案 3 :(得分:1)

这适用于Swift 1.2

        var ddata: NSData
        buf = AudioBuffer(mNumberChannels: 1, mDataByteSize: numberOfFrames * UInt32(sizeof(Float32)), mData: &ddata)
        var audioBuffers = AudioBufferList(mNumberBuffers: 1, mBuffers: buf!)