You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用SwiftUI和CoreMIDI获取并解码网络MIDI时间码?

网络MIDI Timecode接收问题及代码示例

我已经在macOS应用中成功获取主机列表,代码实现简单直接。目前希望通过网络获取MIDI Timecode并解码为时钟,解码逻辑已完成,但应用连接会话并获取时钟信息的过程非常困难,网上相关资料极少。

以下是我的代码:

MIDIInputPortCreate(client, "MIDI Timecode Receiver Input Port" as CFString, { packetList, _  in
      let packets = packetList.pointee
      let packet = packets.packet
      let statusByte = packet.data.0

      // Check if the MIDI message is a Timecode message
      if statusByte == 0xF1 {
          // Extract the timecode values from the MIDI message
          let frames = Int(packet.data.1 & 0x1F)
          let seconds = Int(packet.data.2 & 0x3F)
          let minutes = Int(packet.data.3 & 0x3F)
          let hours = Int(packet.data.4 & 0x1F)

          // Update the timecode message with the new timecode values
          DispatchQueue.main.async {
              midiTimecode = "\(hours):\(minutes):\(seconds):\(frames)"
          }
      }
  }, nil, &endpoint)

    let networkSession = MIDINetworkSession.default()
   networkSession.addConnection(nil, port: outputPort, to: networkSession.sourceEndpoint())

请问是否有人成功实现过该功能,或是知晓如何从会话中提取时间码?

编辑:已找到苹果官方文档,但该文档未涉及输入时间码的处理,文档主题为《将MIDI 2集成到你的应用中》


问题排查与解决方案建议

  • 修正网络会话连接逻辑:当前networkSession.addConnection调用错误地连接到了本地会话的源端点,你需要连接到远程发送MIDI Timecode的设备端点。正确做法是先发现网络上的可用MIDI端点,再筛选目标设备建立连接:
    // 获取网络上的可用MIDI端点
    let availableEndpoints = MIDINetworkSession.default().availableEndpoints
    // 根据设备名称或属性筛选目标端点(示例:包含Timecode关键词)
    if let targetEndpoint = availableEndpoints.first(where: { $0.name.contains("Timecode") }) {
        // 使用输入端口建立连接,而非输出端口
        networkSession.addConnection(nil, port: inputPort, to: targetEndpoint)
    }
    
  • 修复输入端口创建参数:MIDIInputPortCreate的最后一个参数应为MIDIPortRef类型的输入端口引用,而非端点引用。你需要先声明输入端口变量,再传入创建方法:
    var inputPort: MIDIPortRef = 0
    let createResult = MIDIInputPortCreate(client, "MIDI Timecode Receiver Input Port" as CFString, { packetList, _ in
        // 你的解码逻辑
    }, nil, &inputPort)
    
    // 校验端口创建是否成功
    guard createResult == noErr else {
        print("MIDI输入端口创建失败,错误码:\(createResult)")
        return
    }
    
  • 遍历所有MIDI数据包:当前代码仅处理了数据包列表中的第一个包,实际可能存在多个数据包,需遍历处理:
    let packetList = packetList.pointee
    for packetIndex in 0..<packetList.numPackets {
        let packet = packetList.packet[packetIndex]
        let statusByte = packet.data.0
        
        if statusByte == 0xF1 {
            // 提取并处理Timecode数据
            let frames = Int(packet.data.1 & 0x1F)
            let seconds = Int(packet.data.2 & 0x3F)
            let minutes = Int(packet.data.3 & 0x3F)
            let hours = Int(packet.data.4 & 0x1F)
            
            DispatchQueue.main.async {
                midiTimecode = "\(hours):\(minutes):\(seconds):\(frames)"
            }
        }
    }
    
  • 检查权限与网络配置:确保应用已获取网络权限,设备处于同一局域网,且音频MIDI设置中已启用网络MIDI服务。

内容的提问来源于stack exchange,提问作者Fabian H.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 13:38:12