2014-12-31 4 views
2

Я изо всех сил, чтобы инициализировать структуру MIDIMetaEvent найденную в MusicPlayer.h с быстрым заголовочный файл не определяет структуру следующим образом:Initializing MIDIMetaEvent структура

struct MIDIMetaEvent { 
    var metaEventType: UInt8 
    var unused1: UInt8 
    var unused2: UInt8 
    var unused3: UInt8 
    var dataLength: UInt32 
    var data: (UInt8) 
} 

который кажется довольно простым вплоть до члена, что «данные». Является ли это 1-элементным определением кортежа? Я могу легко инициализировать все остальные элементы структуры, но тщетно пытался установить «данные» на что-либо еще, кроме одного значения. В моем коде я использовал массив Uint8 под названием MyData и попытался инициализировать структуру следующим образом:

var msg = MIDIMetaEvent(
    metaEventType : UInt8(0x7F), 
    unused1  : UInt8(0), 
    unused2  : UInt8(0), 
    unused3  : UInt8(0), 
    dataLength : UInt32(myData.count), 
    data   : UnsafeBufferPointer<UInt8>(start: UnsafePointer<UInt8>(myData), count:myData.count)) 

Но компилятор не доволен этим и жалуется на «UnsafeBufferPointer не конвертируемых в Uint8». Если я просто устанавливаю данные на одно значение, но устанавливаю значение dataLength на значение больше 1, то полученная в результате MIDIEventData показывает, что первое значение в событии - это то, что я застрял в «данных», за которым следуют баб данных данных в соответствии с байтами dataLength , Таким образом, «данные» воспринимаются как некоторая непрерывная память.

Как я могу установить этот элемент данных в элементы UInt8 из массива?

ответ

6

AudioToolbox база определяет, как MIDIMetaEvent

typedef struct MIDIMetaEvent 
{ 
    UInt8  metaEventType; 
    UInt8  unused1; 
    UInt8  unused2; 
    UInt8  unused3; 
    UInt32  dataLength; 
    UInt8  data[1]; 
} MIDIMetaEvent; 

, где фактически используется в качестве «переменной длины массива». В (С) объективно-можно просто выделить указатель на блок памяти фактически нужного размера:

MIDIMetaEvent *mep = malloc(sizeof(MIDIMetaEvent) + data.count); 

Свифт является более строгим с указателем слепками и массивами фиксированного размера отображается в Swift кортежей (который может быть громоздким для обработки).

Следующая утилита класса показывает, как это можно было бы решить:

class MyMetaEvent { 
    private let size: Int 
    private let mem : UnsafeMutablePointer<UInt8> 

    let metaEventPtr : UnsafeMutablePointer<MIDIMetaEvent> 

    init(type: UInt8, data: [UInt8]) { 
     // Allocate memory of the required size: 
     size = sizeof(MIDIMetaEvent) + data.count 
     mem = UnsafeMutablePointer<UInt8>.alloc(size) 
     // Convert pointer: 
     metaEventPtr = UnsafeMutablePointer(mem) 

     // Fill data: 
     metaEventPtr.memory.metaEventType = type 
     metaEventPtr.memory.dataLength = UInt32(data.count) 
     memcpy(mem + 8, data, UInt(data.count)) 
    } 

    deinit { 
     // Release the allocated memory: 
     mem.dealloc(size) 
    } 
} 

Затем вы можете создать экземпляр с

let me = MyMetaEvent(type: 0x7F, data: myData) 

и передать me.metaEventPtr к Swift функций, принимающих UnsafePointer<MIDIMetaEvent> аргумент.


Обновление для Swift 3/4:

Просто преобразование указатель на другой тип больше не возможно, он должен быть "отскока":

class MyMetaEvent { 
    private let size: Int 
    private let mem: UnsafeMutablePointer<UInt8> 

    init(type: UInt8, data: [UInt8]) { 
     // Allocate memory of the required size: 
     size = MemoryLayout<MIDIMetaEvent>.size + data.count 
     mem = UnsafeMutablePointer<UInt8>.allocate(capacity: size) 
     mem.initialize(to: 0, count: size) 

     // Fill data: 
     mem.withMemoryRebound(to: MIDIMetaEvent.self, capacity: 1) { metaEventPtr in 
      metaEventPtr.pointee.metaEventType = type 
      metaEventPtr.pointee.dataLength = UInt32(data.count) 
      memcpy(&metaEventPtr.pointee.data, data, data.count) 
     } 
    } 

    deinit { 
     // Release the allocated memory: 
     mem.deallocate(capacity: size) 
    } 

    func withMIDIMetaEventPtr(body: (UnsafePointer<MIDIMetaEvent>) -> Void) { 
     mem.withMemoryRebound(to: MIDIMetaEvent.self, capacity: 1) { metaEventPtr in 
      body(metaEventPtr) 
     } 
    } 
} 

Создать пример с пользовательскими данными:

let me = MyMetaEvent(type: 0x7F, data: ...) 

Pass к функции, принимая UnsafePointer<MIDIMetaEvent> аргумент:

me.withMIDIMetaEventPtr { metaEventPtr in 
    let status = MusicTrackNewMetaEvent(track, 0, metaEventPtr) 
} 
+0

Ах, не понимал, что мне нужно спуститься так низко. Я не использовал memcpy в возрасте! Вместо использования UnsafeMutablePointer <>, который вы продемонстрировали выше, я просто решил использовать memcpy (& msg.data, data, UInt (data.count)), который, кажется, работает нормально. Благодаря! Кажется, практическая работа с Swift по-прежнему является довольно грязным делом ... – Yohst

+0

@Yohst: Вы правы, withUnsafeMutablePointer был излишне сложным. Я обновил ответ и добавил альтернативу memcpy(). Было бы проще, если бы Swift-сопоставление массивов C было бы Swift Array вместо кортежа. –

+0

Хммм, я победил слишком рано. Приведенный выше код приводит к копированию только первого байта «данных» на выход. Weird. Когда я исследую память сразу после memcpy() с mem.advancedBy (x) .memory с «x» - 8, 9, 10 и т. Д. Я вижу только первый байт в позиции «8», но бред для остальных. Что такое memcpy()? Или на что-то еще наступила память? – Yohst

0

Мой $ 0,02

Мессинг вокруг с указателями только для создания мета-событие является боль, но мы застряли с ним.Похоже, что это не изменится в ближайшее время.

Я пробовал это, и он работает. Я пишу MusicSequence в файл, а затем открываю его в Sibelius 7. Он просматривает текст, подпись времени и ключевые события сигнатуры, которые я добавил. Кстати, вы добавляете событие времени в последовательность темпа последовательности. Да, ptr [1 ... явно не выделено. Пока еще не проблема. Какие-либо предложения? (критика приветствуется)

let text = "Piano" 
    let data = [UInt8](text.utf8) 

    var metaEvent = MIDIMetaEvent() 
    metaEvent.metaEventType = 3 // track or sequence name 
    metaEvent.dataLength = UInt32(data.count) 

    withUnsafeMutablePointer(&metaEvent.data, { 
     ptr in 
     for i in 0 ..< data.count { 
      ptr[i] = data[i] 
     } 
    }) 

    let status = MusicTrackNewMetaEvent(track, 0, &metaEvent) 
+0

Это выглядит опасно, вы пишете вне структуры MIDIMetaEvent. –