You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AudioKit音频播放器多轨道播放不同步问题求助

解决多音轨同步播放问题

你的问题出在逐个调用start()方法:每个AudioPlayer的启动都需要初始化音频缓冲区、与音频引擎同步,串行执行这些操作会导致音轨之间出现微小的启动延迟,16轨的情况下这个问题会更明显。DAW里的同步是因为所有音轨共享同一个播放时钟,而你的代码没有利用AudioKit的全局时钟来统一调度。

核心修复方案

  1. 统一音频路由:将所有音轨连接到同一个Mixer,并把Mixer作为AudioEngine的输出,确保所有音轨共享同一音频时钟。
  2. 同步启动时机:使用音频引擎的当前时间作为所有音轨的启动时间戳,让它们在同一时刻开始播放。
  3. 批量管理音轨:用数组代替单独的变量,便于扩展到16轨,减少重复代码。

修改后的代码

MixerClass(优化版)

import Foundation
import AudioKit
import AVFoundation

final class MixerClass: ObservableObject {
    let engine = AudioEngine()
    let mixer = Mixer()
    
    // 用数组管理所有音轨,便于扩展到16轨
    @Published var tracks: [AudioPlayer] = []
    // 统一管理播放状态,因为所有音轨同步启停
    @Published var isPlaying: Bool = false
    
    private let trackNames = ["DQ_Kick", "DQ_Snare", "DQ_Tom1"] // 后续可扩展到16个
    
    init() {
        do {
            Settings.bufferLength = .medium
            try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(Settings.bufferLength.duration)
            
            try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: [.defaultToSpeaker, .mixWithOthers, .allowBluetoothA2DP])
            
            try AVAudioSession.sharedInstance().setActive(true)
        } catch let err {
            print(err)
        }

        do {
            // 批量加载音轨
            for name in trackNames {
                if let url = Bundle.main.url(forResource: name, withExtension: "mp3") {
                    let player = AudioPlayer()
                    try player.load(url: url)
                    player.isLooping = true
                    player.volume = 0.5 // 默认音量
                    tracks.append(player)
                    // 将音轨连接到混音器
                    mixer.addInput(player)
                } else {
                    Log("Could not find track: \(name)")
                }
            }
            
            // 将混音器设置为音频引擎的输出
            engine.output = mixer
            // 启动音频引擎(提前启动,避免播放时的延迟)
            try engine.start()
        } catch let err {
            print(err)
        }
    }

    func playStopToggle() {
        guard !tracks.isEmpty else { return }
        
        if isPlaying {
            // 同步停止所有音轨
            tracks.forEach { $0.stop() }
        } else {
            // 获取音频引擎的当前时间,作为所有音轨的启动时间戳
            let startTime = engine.outputNode.lastRenderTime?.sampleTime ?? 0
            let time = AVAudioTime(sampleTime: startTime, atRate: engine.outputNode.outputFormat(forBus: 0).sampleRate)
            
            // 所有音轨在同一时间点启动
            tracks.forEach { $0.play(at: time) }
        }
        
        isPlaying.toggle()
    }
}

FreePlayMain(适配修改)

import SwiftUI

struct FreePlayMain: View {
    @EnvironmentObject var conductor: MixerClass
    
    var body: some View {
        NavigationStack {
            VStack {
                VStack {
                    // 批量生成音量滑块
                    ForEach(Array(conductor.tracks.enumerated()), id: \.offset) { index, track in
                        Slider(value: $conductor.tracks[index].volume, in: 0...1)
                            .padding()
                            .accessibilityLabel("Track \(index + 1) Volume")
                    }
                }
                playStopButton
            }
        }
    }
    
    private var playStopButton: some View {
        Button(action: {
            conductor.playStopToggle()
        }) {
            VStack {
                Text("\(Image(systemName: conductor.isPlaying ? "stop.fill" : "play.fill")) \(conductor.isPlaying ? "Stop All" : "Start All")")
            }
        }
    }
}

关键修改说明

  • 音频路由连接:所有AudioPlayer都添加到Mixer,Mixer作为AudioEngine的输出,确保所有音轨共享同一音频时钟,这是同步的基础。
  • 同步启动:使用engine.outputNode.lastRenderTime获取当前音频引擎的时间戳,调用play(at:)让所有音轨在同一时刻开始播放,彻底消除启动延迟。
  • 批量管理:用数组存储音轨,后续扩展到16轨只需在trackNames数组中添加文件名即可,无需重复编写加载和控制代码。
  • 提前启动引擎:在init中启动AudioEngine,避免首次播放时的引擎启动延迟。

内容的提问来源于stack exchange,提问作者Wpitchy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 15:25:04