AudioKit:同步录音机,播放器和节拍器

问题描述 投票:2回答:1

我正在为AudioKit进行概念验证,以评估它是否符合我们的需求。

我们目前正在使用AVAudioRecorderAVPlayerAVMutableComposition作为多轨录音机而且效果很好,但我想尝试用AudioKitAVAudioEngine做同样的事情。

我是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")
        }
    }
}
audiokit audio-player metronome
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

我刚刚发布了一个example,其中环回音频在昨天的回购中进行了比较。

© www.soinside.com 2019 - 2024. All rights reserved.