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

如何使用Swift结合AudioKit同时播放两个音频文件

问题分析与代码修正

针对你的双音频同步播放需求,代码里存在几处需要修正的问题,同时可以优化实现逻辑:

核心问题与优化点

  • 变量名笔误:类中定义的播放器实例是player,但调用时错误使用了player2,会直接导致编译失败,需统一变量名。
  • 音频会话类别错误:仅播放场景无需使用.playAndRecord类别,改用.playback更合理,避免不必要的录音权限申请;同时调整会话选项适配外放需求。
  • 命名规范问题:Swift中结构体名称需遵循大驼峰命名,将segment改为Segment。
  • 时长计算冗余:AVAudioFile自带duration属性,无需手动通过采样率和样本数计算音频时长,直接调用即可简化代码。
  • 错误处理不当:error.localizedDescription.debugDescription是错误的用法,直接输出error或error.localizedDescription即可清晰展示错误信息。
  • 资源生命周期缺失:类销毁时需停止AudioEngine并取消激活音频会话,避免内存泄漏和后台音频占用。

修正后的完整代码

import AudioKit
import AVFoundation
import Combine

class AudioPlayClass: ObservableObject {
    var player = MultiSegmentAudioPlayer()
    let engine = AudioEngine()
    
    deinit {
        // 清理音频资源
        engine.stop()
        do {
            try AVAudioSession.sharedInstance().setActive(false)
        } catch {
            print("Failed to deactivate audio session: \(error)")
        }
    }

    func playFiles() {
        // 连接播放器到引擎输出
        engine.output = player
        
        do {
            // 配置音频会话为仅播放模式
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: .defaultToSpeaker)
            try AVAudioSession.sharedInstance().setActive(true)
            
            // 读取音频文件
            guard let url = Bundle.main.url(forResource: "note1", withExtension: "wav", subdirectory: "instrumentDirectory") else {
                print("note1.wav not found")
                return
            }
            guard let url2 = Bundle.main.url(forResource: "note2", withExtension: "wav", subdirectory: "instrumentDirectory") else {
                print("note2.wav not found")
                return
            }
            
            let audioFile = try AVAudioFile(forReading: url)
            let audioFile2 = try AVAudioFile(forReading: url2)
            
            // 创建同步播放的音频片段
            let segment1 = Segment(
                audioFile: audioFile,
                playbackStartTime: 0.0,
                fileStartTime: 0,
                fileEndTime: audioFile.duration
            )
            let segment2 = Segment(
                audioFile: audioFile2,
                playbackStartTime: 0.0,
                fileStartTime: 0,
                fileEndTime: audioFile2.duration
            )
            
            // 调度片段并播放
            player.scheduleSegments(audioSegments: [segment1, segment2])
            try engine.start()
            player.play()
            
        } catch {
            print("Audio playback error: \(error.localizedDescription)")
        }
    }
}

// 遵循Swift命名规范的音频片段结构体
public struct Segment: StreamableAudioSegment {
    public var audioFile: AVAudioFile
    public var playbackStartTime: TimeInterval
    public var fileStartTime: TimeInterval
    public var fileEndTime: TimeInterval
    public var completionHandler: AVAudioNodeCompletionHandler?
    
    // 简化初始化(可选,提升代码可读性)
    public init(audioFile: AVAudioFile, playbackStartTime: TimeInterval, fileStartTime: TimeInterval, fileEndTime: TimeInterval, completionHandler: AVAudioNodeCompletionHandler? = nil) {
        self.audioFile = audioFile
        self.playbackStartTime = playbackStartTime
        self.fileStartTime = fileStartTime
        self.fileEndTime = fileEndTime
        self.completionHandler = completionHandler
    }
}

关键说明

  1. 同步播放实现:只要两个Segment的playbackStartTime都设置为0.0,MultiSegmentAudioPlayer会自动处理同步播放逻辑,无需额外的时间同步代码。
  2. 音频会话配置:.playback类别专注于播放场景,配合.defaultToSpeaker选项确保音频从扬声器输出,避免使用听筒。
  3. 资源清理:deinit方法中停止引擎并取消激活会话,是iOS音频开发的最佳实践,避免后台资源占用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 18:50:25