AudioKit AKPlayer 无法环回到以 setPosition 开头

AudioKit AKPlayer can not loopback to beginning with setPosition

在 v4 中,如果您使用 AKPlayer 循环并使用 setPosition API,播放器会在 setPosition 设置的位置和播放器总持续时间之间循环。我需要的是从 setPosition API 设置的位置开始,并在循环发生时循环到开头。我不确定这是否是有意的,但我需要能够在播放器中寻找某个点并循环到开头,现在看来这不是 AudioKit 中的功能。 有没有办法或解决方法来实现这一目标?我需要按照要求完成此操作。 此处重现的是一个完整的 playground 示例:

import AudioKitPlaygrounds
import AudioKit

let file = try AKAudioFile(readFileName: "3.aac")

//: Set up a player to the loop the file's playback
var player = AKPlayer(audioFile: file)
player.isLooping = true
player.buffering = .always

AKManager.output = player
try AKManager.start()


player.setPosition(player.duration - 2)
player.play(at: AVAudioTime.now())
// player loops between player.duration - 2, player.duration  forever. I want it to loop between 0, player.duration

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

预期行为 我希望它能循环回到开头。

详情:

如果您打算开发某种循环应用程序,我认为方法是使用音序器来协调音频文件的播放。在应该开始播放声音的每个位置,向序列添加一个事件。

音序器可以连接到回调工具,回调工具是将事件传递给用户定义函数的节点。在您的回调函数中,您将开始播放该事件指示的音频文件。

这是应该做的事情的概要:

  1. 创建一个包含播放事件的轨道,使用 AKSequencer's addTrack method. Connect this track to an AKCallbackInstrument. Please see 如何将 AKCallbackInstrument 连接到 AKSequencer 轨道。将音序器和轨道的 length 设置为歌曲的总持续时间(注意这些属性以节拍而不是秒表示)。根据需要设置音序器的loopEnabled
  2. 将播放事件添加到轨道,在您希望每个声音播放的时间位置。由于您将自己使用回调函数解释事件,因此使用哪种类型的事件并不重要。您可以简单地使用 Note On.
  3. 在回调函数中,当接收到该事件时开始播放相应的声音。

您需要几个 类 来表示有关每个样本在歌曲中出现位置的一些基本信息:

class Beat {
    var sample: Sample!
    var onsetTime: Double

    var endTime {
        get {
            return onsetTime + sample.duration
        }
    }
}

class Sample {
    var url: URL!
    var duration: Double
}

class ViewController: UIViewController {
    var samples: [Sample] = []
    var beats: [Beat] = []

    var player: AKPlayer!
    var sequencer: AKSequencer!
}

在音符的音高上,您将存储要播放的样本的索引。这就是您的回调函数的样子:

func playCallback(status:UInt8, note:MIDINoteNumber, vel:MIDIVelocity) -> () {
    guard let status = AKMIDIStatus(byte: status),
        let type = status.type,
        type == .noteOn else { return }
    DispatchQueue.main.async {
        player.load(samples[note].url)
        player.play()
    }
}

您可以使用 AKSequencerseek 方法在任意位置开始播放您的歌曲。

有一种特殊情况,音序器在一个节拍的中间开始播放。在这种情况下,您必须手动开始播放该样本。

func play(at time: Double = 0) {
    sequencer.seek(time)
    sequencer.play()
    for beat in beats {
        if beat.onsetTime < time && time < beat.endTime {
            player.load(beat.sample.url)
            player.play(from: time - beat.onsetTime)
        }
    }
}