Использование AudioBufferList с 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 '()'

Я пробовал различные операции кастинга и доступ с несколькими индексами, но безрезультатно... любые идеи?

Ниже приведены определения C и Swift для AudioBufferList и AudioBuffer для удобства...

// 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<()>
}

Ответы

Ответ 1

Изменить: Адам Ритенауэр ответ, вероятно, самый лучший. Чтобы расширить его, вы можете посмотреть новые функции/типы функций в изменениях IOS 8.3 Core Audio.

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. В приведенном ниже коде представлены некоторые идеи, но UnsafePointer и UnsafeArray недостаточно хорошо документированы, поэтому это может быть неправильно.

// ***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

Я нашел это случайно. Как ни странно, тип впереди фактически работал с Swift, когда он предлагал UnsafeMutableAudioBufferListPointer. Что вы можете инициализировать с помощью аргумента 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
}

Ответ 3

Я нашел, что это работает нормально. abl - это AudioBufferList, созданный при загрузке 16-битного аудиофайла AIFF.

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!)