【问题标题】:Initializing MIDIMetaEvent structure初始化 MIDIMetaEvent 结构
【发布时间】:2014-12-31 17:00:30
【问题描述】:

我正在努力用swift初始化MusicPlayer.h中找到的MIDIMetaEvent结构头文件定义结构如下:

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

在那个“数据”成员之前,这似乎相当简单。这是一个 1 元素元组定义吗?我可以轻松地初始化所有其他结构元素,但徒劳地尝试将“数据”设置为除单个值之外的任何其他值。在我的代码中,我使用了一个名为 myData 的 UInt8 数组,并尝试像这样初始化结构:

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”。如果我只是将 data 设置为单个值但将 dataLength 设置为大于 1 的值,则生成的 MIDIEventData 显示事件中的第一个值是我停留在“data”中的值,然后是根据“dataLength”字节的乱码数据字节.很明显,“数据”被视为某种连续的记忆。

那么如何将“数据”元素设置为数组中的 UInt8 元素?

【问题讨论】:

    标签: ios macos swift coremidi


    【解决方案1】:

    AudioToolbox 框架将MIDIMetaEvent 定义为

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

    其中data[1] 实际上用作“可变长度数组”。 在(Objective-)C 中,可以只分配一个指向内存块的指针 实际需要的尺寸:

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

    Swift 对指针强制转换更加严格,固定大小的数组映射到 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 传递给采用UnsafePointer&lt;MIDIMetaEvent&gt; 的Swift 函数 论据。


    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: ...)
    

    传递给采用UnsafePointer&lt;MIDIMetaEvent&gt; 参数的函数:

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

    【讨论】:

    • 另外,关于如何使用它来设置密钥签名的任何想法?对于包含平面音符的签名,这需要一个负数 (csie.ntu.edu.tw/~r92092/ref/midi/#meta_event),这在放入 UInt8 变量时当然会导致错误。
    • @TheNeil:是的,您可以分配一个原始指针并将其绑定到所需的类型。但这与适当的(取消)分配无关,而且效率既不高也不低。绑定内存(目前)只是一个简单的强制转换,实际上在运行时什么都不做。将来可能会改变,比较forums.swift.org/t/…“一个代码验证工具——就像一个假设的指针清理器——可以让你在这方面有所作为。”
    • @TheNeil:我无法真正帮助您解决其他问题,因为我没有使用过 MIDI,也不知道它如何解释数据。但是mem[offset] = UInt8(bitPattern: -3) 可能就是您要找的。​​span>
    • 嗨@TheNeil,你有没有想过如何更改平音符调号的调号设置。文档非常稀少。遗憾的是,Apple 仍在使用这种过时的 API 来处理这类东西。这些 API 应该已经移植到 AVFoundation 框架 :-(
    • 我实际上确实管理了以下内容,从而创建了正确的数据模式;但是,将它添加到音乐序列的速度轨道的关键元事件中会无缘无故地弄乱所有音符数据。这部分工作的任何例子都非常受欢迎! public var midiEventData: [UInt8] { 让意外 = min(numberOfAccidentals, 7) 让 sharpsFlats = (notationStyle == .sharps) ? UInt8(accidentals) : UInt8(bitPattern: Int8(accidentals * -1)) let minorFlag = (type == .major) ? UInt8(0) : UInt8(1) 返回 [sharpsFlats, minorFlag] }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多