2

我正在做一个概念验证,AudioKit以评估它是否符合我们的需求。

我们目前正在使用AVAudioRecorder,AVPlayerAVMutableComposition用于多轨录音机,效果很好,但我想尝试用AudioKitor做同样的事情AVAudioEngine

我是新手,AudioKit所以我需要一些指导:

  1. 我正在AKClipRecorder用作录音机并调用start(at:)与播放器同步,但我认为有些地方设置不正确,因为录音总是有延迟。为什么会发生这种情况?对于玩家,我正在使用一系列AKPlayers并调用play(at:)来同步它们,从我的测试来看,玩家似乎可以同步。

  2. 我很难找到如何AKMetronome与录音机和播放器同步,没有start(at:)API。我怎样才能做到这一点?是否可以在某个节拍中启动录音机和播放器?

  3. 最后,有没有办法设置的文件格式AKClipRecorder

我在下面添加我的代码。

import Foundation
import AudioKit

class MultiTrackRecorder {

    // MARK: - Constants & Vars

    private var mic: AKMicrophone!
    private var micBooster: AKBooster!
    private var mainMixer: AKMixer!
    private var recorder: AKClipRecorder!
    private var players: [AKPlayer] = []
    private var metronome: AKMetronome!

    // MARK: - Public Methods

    func startEngine() throws {
        AKSettings.bufferLength = .medium
        AKSettings.defaultToSpeaker = true
        try AKSettings.setSession(category: .playAndRecord)

        mic = AKMicrophone()

        recorder = AKClipRecorder(node: mic)

        micBooster = AKBooster(mic)
        micBooster.gain = 0

        metronome = AKMetronome()
        metronome.tempo = 120

        mainMixer = AKMixer()
        mainMixer.connect(input: micBooster)
        mainMixer.connect(input: metronome)

        AudioKit.output = mainMixer
        try AudioKit.start()
    }

    func startRecording() throws {
        try recorder.recordClip(completion: recordingEnded)
        let startTime = AVAudioTime.now() + 0.25
        recorder.start(at: startTime)
        players.forEach { $0.play(at: startTime) }
        //metronome.start()
    }

    func stopRecording() {
        recorder.stopRecording()
        recorder.stop()
        players.forEach { $0.stop() }
        metronome.stop()
    }

    func play() {
        let startTime = AVAudioTime.now() + 0.25
        players.forEach { $0.play(at: startTime) }
    }

    func stop() {
        players.forEach { $0.stop() }
    }

    // MARK: - Private Methods

    private func addPlayer(withFileURL url: URL) {
        guard let player = AKPlayer(url: url) else { return }
        players.append(player)
        mainMixer.connect(input: player)
    }

    private func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentsDirectory = paths[0]
        return documentsDirectory
    }

    private func getDocsPathWithRandomFileName() -> URL {
        let docsDirectory = getDocumentsDirectory()
        let fileName = "\(UUID().uuidString).caf"
        return docsDirectory.appendingPathComponent(fileName, isDirectory: false)
    }

    private func recordingEnded(url: URL?, somethimg: Double, error: Error?) {
        guard let savedFile = url else {
            print("TODO: Handle this error")
            return
        }
        do {
            let moveToDirectory = getDocsPathWithRandomFileName()
            try FileManager.default.moveItem(at: savedFile, to: moveToDirectory)
            addPlayer(withFileURL: moveToDirectory)
            print("New track has been saved in: \(moveToDirectory.absoluteString)")
        } catch {
            print("TODO: Handle this error")
        }
    }
}
4

1 回答 1

3

尝试使用 AKSamplerMetronome 而不是 AKMetronome,它有一个 play(at:) 功能。

至于选择正确的时机,有几件事在起作用。AKMicrophone 不会提供正确的时间戳,请改用 AudioKit.engine.inputNode。如果您真的希望它准确,则需要补偿 io 延迟。

let startTime = AVAudioTime.now() + 0.25
let audioSession = AVAudioSession.sharedInstance()

// start players in advance to compensate for hardware output latency
let playbackStart = startTime - audioSession.outputLatency

//start recorders late to compensate for hardware input latency.
let recordingStart = startTime + audioSession.inputLatency

我刚刚发布了一个示例,在昨天的 repo 中比较了环回音频的准确性。

于 2018-05-06T04:50:48.213 回答