AVAudioEngine MIDI 文件播放(当前进度+MIDI 结束回调)Swift
Posted
技术标签:
【中文标题】AVAudioEngine MIDI 文件播放(当前进度+MIDI 结束回调)Swift【英文标题】:AVAudioEngine MIDI file play (Current progress + MIDI end callback) Swift 【发布时间】:2020-08-15 10:09:21 【问题描述】:我正在使用 AVAudioEngine、AVAudiosequencer、AVAudioUnitSampler 播放 MID。 AVAudioUnitSampler 加载 Soundfont 和 AVAudioSequencer 加载 MIDI 文件。 我的初始配置是
engine = AVAudioEngine()
sampler = AVAudioUnitSampler()
speedControl = AVAudioUnitVarispeed()
pitchControl = AVAudioUnitTimePitch()
engine.attach(sampler)
engine.attach(pitchControl)
engine.attach(speedControl)
engine.connect(sampler, to: speedControl, format: nil)
engine.connect(speedControl, to: pitchControl, format: nil)
engine.connect(pitchControl, to: engine.mainMixerNode, format: nil)
这是我的序列如何加载 MIDI 文件
func setupSequencer()
self.sequencer = AVAudioSequencer(audioEngine: self.engine)
let options = AVMusicSequenceLoadOptions()
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let introurl = URL(string: songDetails!.intro!)
let midiFileURL = documentsDirectoryURL.appendingPathComponent(introurl!.lastPathComponent)
do
try sequencer.load(from: midiFileURL, options: options)
print("loaded \(midiFileURL)")
catch
print("something screwed up \(error)")
return
sequencer.prepareToPlay()
if sequencer.isPlaying == false
这是采样器加载 SoundFont 的方式
func loadSF2PresetIntoSampler(_ preset: UInt8,bankURL:URL )
do
try self.sampler.loadSoundBankInstrument(at: bankURL,
program: preset,
bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB),
bankLSB: UInt8(kAUSampler_DefaultBankLSB))
catch
print("error loading sound bank instrument")
而且它玩得很好,这没有问题。我还有 2 个其他要求,但在这些方面遇到了问题
我必须在第一个 MID 结束播放后播放另一个 MIDI 文件,为此我需要从引擎或序列中获取完整/完成 MIDI 文件回调或我如何加载多个 MIDI 文件顺序?我尝试了很多方法,但都没有帮助。
我需要显示 MIDI 文件播放的进度,例如当前时间和总时间。为此,我尝试了一种在堆栈答案中找到的方法,即:
var currentPositionInSeconds: TimeInterval
get
guard let offsetTime = offsetTime else return 0
guard let lastRenderTime = engine.outputNode.lastRenderTime else return 0
let frames = lastRenderTime.sampleTime - offsetTime.sampleTime
return Double(frames) / offsetTime.sampleRate
这里offsetTime是
offsetTime = engine.outputNode.lastRenderTime
而且它总是返回 nil。
【问题讨论】:
【参考方案1】:很高兴看到有人使用我的示例代码。
这是一个缺失的功能。请提交雷达。没有雷达什么都不会发生。 他们确实会注意安排工作时间。
我通过获取序列的长度来伪造它。
if let ft = sequencer.tracks.first
self.lengthInSeconds = ft.lengthInSeconds
else
self.lengthInSeconds = 0
然后在我的播放功能中,
do
try sequencer.start()
Timer.scheduledTimer(withTimeInterval: self.lengthInSeconds, repeats: false)
[weak self] (t: Timer) in
guard let self = self else return
t.invalidate()
self.logger.debug("sequencer finished")
self.sequencer.stop()
...
如果您想要更准确,可以使用 DispatchSourceTimer。
【讨论】:
以上是关于AVAudioEngine MIDI 文件播放(当前进度+MIDI 结束回调)Swift的主要内容,如果未能解决你的问题,请参考以下文章
在 WatchOS 上使用 AVAudioEngine 录制时阻止播放