AudioKit: синхронизатор, проигрыватели и метроном - PullRequest
0 голосов
/ 06 мая 2018

Я работаю над проверкой концепции AudioKit, чтобы оценить, соответствует ли она нашим потребностям.

В настоящее время мы используем AVAudioRecorder, AVPlayer и AVMutableComposition для многодорожечного рекордера, и он прекрасно работает, но я хочу попробовать сделать то же самое с AudioKit или AVAudioEngine.

Я новичок в AudioKit, поэтому мне нужно руководство:

  1. Я использую AKClipRecorder в качестве записывающего устройства и вызываю start(at:) для синхронизации с плеерами, но я думаю, что-то настроено неправильно, потому что запись всегда задерживается. Почему это может происходить? Для игроков я использую массив AKPlayers и вызываю play(at:) для их синхронизации, и из моих тестов игроки, кажется, синхронизируются нормально.

  2. Мне трудно найти способ синхронизации AKMetronome с рекордером и плеерами, API start(at:) отсутствует. Как мне этого добиться? И можно ли запустить рекордер и проигрыватели в определенный ритм?

  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")
        }
    }
}

1 Ответ

0 голосов
/ 06 мая 2018

Попробуйте AKSamplerMetronome вместо AKMetronome, у него есть функция play (at :).

Что касается правильного выбора времени, здесь есть несколько вещей. AKMicrophone не будет предоставлять правильные метки времени, вместо этого используйте AudioKit.engine.inputNode. Если вы действительно хотите, чтобы он был точным, вам нужно будет компенсировать задержку.

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

Я только что опубликовал пример , в котором аудио с обратной связью сравнивается на точность в репо вчера.

...