如何让AppleSequencer与MIDISampler忽略NoteOff?Release模式异常排查
AudioKit鼓机样本被NoteOff截断及Release环境MIDI数据包异常问题
我正在使用AudioKit 5.6.2开发简易鼓机Sequencer,所使用的样本自带混响,时长较长(如1.5-2.0秒)。若将音符时长设为样本长度,会出现第一个样本播放时,第二个样本被第一个的NoteOff事件提前截断的问题。
参考AudioKit CookBook编写的鼓机代码如下:
import SwiftUI import AudioKit import AVFoundation class DrumsConductor: ObservableObject { let engine = AudioEngine() let sequencer = AppleSequencer() private var drums = MIDISampler(name: "Drums") var tempo: Double = 60.0 { didSet { sequencer.setTempo(tempo) } } init() { engine.output = drums } func destroy() { drums.destroyEndpoint() } func playSequence() { sequencer.stop() sequencer.setTempo(tempo) if sequencer.trackCount < 1 { let _ = sequencer.newTrack() } sequencer.setGlobalMIDIOutput(drums.midiIn) for index in 0..<8 { sequencer.tracks[0].add(noteNumber: 48, velocity: 60, position: Duration(beats: Double(index)), duration: Duration(beats: 1.2)) } sequencer.rewind() sequencer.play() } func stopSequence() { guard sequencer.isPlaying else { return } sequencer.stop() sequencer.preroll() } func start() { do { try engine.start() } catch let err { Log(err) } setupSound() } func stop() { engine.stop() } func setupSound() { let path = "Sounds/Sampler Instruments/Drums" do { try drums.loadSoundFont(path, preset: 0, bank: 0) } catch { fatalError(error.localizedDescription) } } }
我尝试让AppleSequencer或MIDISampler忽略NoteOff事件但未找到原生方法,虽可通过计算样本间距或交替使用多轨道规避,但更希望直接避免样本被截断,询问是否有AudioKit原生解决方案。
后续我复制MIDISampler创建自定义DrumMIDISampler类,修改enableMIDI方法过滤NoteOff事件:
public func enableMIDI(_ midiClient: MIDIClientRef = MIDI.sharedInstance.client, name: String? = nil) { let cfName = (name ?? self.name) as CFString guard let midiBlock = avAudioNode.auAudioUnit.scheduleMIDIEventBlock else { fatalError("Expected AU to respond to MIDI.") } CheckError(MIDIDestinationCreateWithBlock(midiClient, cfName, &midiIn) { packetList, _ in for e in packetList.pointee { print("Call: \(e)") for event in e { print("Event: \(event)") event.data.withUnsafeBufferPointer { ptr in guard let ptr = ptr.baseAddress else { return } // Check if the event is a NoteOff event let statusByte = ptr[0] let isNoteOff = (statusByte >= 0x80 && statusByte <= 0x8F) || (ptr[0] == 0x90 && ptr[2] == 0) // NoteOff Event oder NoteOn Event mit einer Velocity von 0 // Only process the event if it's not a NoteOff event if !isNoteOff { midiBlock(AUEventSampleTimeImmediate, 0, event.data.count, ptr) } } } } }) }
该方案在Debug配置下正常工作,但在Release环境中所有MIDIPacket均为空(Debug下能获取正常MIDIPacket,Release下返回时间戳0、长度0的空数据包),询问该现象原因及解决方法。
内容的提问来源于stack exchange,提问作者Herr der Töne
相关产品推荐
相关产品推荐

