如何使用AudioKit(v5.6.1)测量iOS应用中MIDI音符输入的时长?
AudioKit MIDI时间戳计算音符时长及DispatchTime替代方案
一、AudioKit MIDI时间戳能不能用来计算音符时长?
可以,但需要先理解时间戳的本质并做正确转换。
AudioKit在receivedMIDINoteOn/receivedMIDINoteOff中提供的时间戳,本质是**mach_absolute_time()的原始返回值**——这是CPU的时钟周期计数,并非直接的毫秒/秒单位,需要通过系统API转换为实际时间间隔。
转换步骤:
- 通过
mach_timebase_info获取系统时间基准系数(numer和denom,用于将时钟周期转换为纳秒) - 计算noteOn和noteOff时间戳的差值
- 将差值转换为纳秒,再换算成毫秒
示例代码:
import MachO func machTimestampToMilliseconds(_ timestamp: UInt64) -> Double { var timebaseInfo = mach_timebase_info_data_t() guard mach_timebase_info(&timebaseInfo) == KERN_SUCCESS else { return 0 } // 转换为纳秒,再转毫秒 let nanoseconds = timestamp * UInt64(timebaseInfo.numer) / UInt64(timebaseInfo.denom) return Double(nanoseconds) / 1_000_000.0 } // 计算你的实验数据 let noteOnTs: UInt64 = 115071237234 let noteOffTs: UInt64 = 115315013613 let durationMs = machTimestampToMilliseconds(noteOffTs - noteOnTs) print("实际时长:\(durationMs) ms")
这个方案的优势是:mach_absolute_time()是单调递增的时间源,不受系统时间调整(如手动改时间、NTP同步)影响,计算时间间隔的准确性更高。
二、改用DispatchTime.now()是否合理?
视你的需求场景而定:
适用场景
如果只需要记录回调函数被触发的时间差,且不担心系统时间变动的影响,DispatchTime.now()是更简便的选择:
DispatchTime.now().rawValue直接返回纳秒级时间戳,计算差值后转毫秒非常直观- 无需额外的系统API转换
示例代码:
var noteOnDispatchTime: DispatchTime? func receivedMIDINoteOn(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel, timestamp: MIDITimeStamp) { noteOnDispatchTime = .now() } func receivedMIDINoteOff(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel, timestamp: MIDITimeStamp) { guard let onTime = noteOnDispatchTime else { return } let offTime = DispatchTime.now() let durationNs = offTime.rawValue - onTime.rawValue let durationMs = Double(durationNs) / 1_000_000.0 print("音符时长:\(durationMs) ms") }
局限性
DispatchTime.now()依赖系统墙钟时间,如果用户手动修改系统时间、或系统进行NTP时间同步,会导致时间差计算出现错误,不适合对时间准确性要求高的场景。
总结
- 优先选择AudioKit提供的MIDI时间戳,配合系统API转换,能保证时间间隔计算的准确性和稳定性
- 若追求实现简便且能接受系统时间变动的风险,可使用
DispatchTime.now()
内容的提问来源于stack exchange,提问作者apps2go
相关产品推荐
相关产品推荐

