【问题标题】:Using AudioBufferList with Swift在 Swift 中使用 AudioBufferList
【发布时间】:2024-01-19 21:44:01
【问题描述】:

我在 Swift 中有一个桥接函数,它在 C 中的参数之一是AudioBufferList *。在 Swift 中,这会生成一个UnsafePointer<AudioBufferList>。我已经设法通过调用audioData[0] 来尊重指针(有更好的方法吗?)。但我正在努力处理接下来的 2 层:AudioBuffer.mBuffers 数组及其 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<()>
}

【问题讨论】:

    标签: ios objective-c pointers casting swift


    【解决方案1】:

    我偶然发现了这个。奇怪的是,当它建议 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
    }
    

    【讨论】:

    • 很好,这似乎是相当新的。
    • 问题:我有一个UnsafePointer&lt;AudioBufferList&gt;,但 UnsafeMutableAudioBufferListPointer 需要一个UnsafeMutablePointer&lt;AudioBufferList&gt;。你知道怎么转换吗?
    • 设置为静音是一回事,但产生以编程方式生成的声音似乎完全不同。我正在使用正弦波生成纯音,并且无法找出将浮点幅度转换为 memset 所需的 Int32 的正确方法。我得到的只是可怕的噼啪声。有什么想法吗?
    • @Georg 你想要UnsafeMutablePointer(mutating: myImmutableAudoBufferList)
    【解决方案2】:

    编辑: 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 和 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: &amp;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
    ...
    

    【讨论】:

    • 嗨,我还没有机会在我的项目中尝试这个,只是想快速说一下reinterpretCast 可能也有帮助...
    • 这是一个好点,但我不知道如何在这里使用它。它没有记录,所以我不知道它是否作用于内存中的同一位置,复制周围的东西,或者什么......
    • 谢谢。决定给你荣誉,即使亚当的更好:)
    【解决方案3】:

    我发现这可以正常工作。 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(" ")
    }
    

    【讨论】:

      【解决方案4】:

      这适用于 Swift 1.2

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

      【讨论】:

        最近更新 更多