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

SwiftUI中使用AVAudioEngine播放600Hz蜂鸣声无声音问题求助

解决Swift中AVFoundation无法播放固定频率蜂鸣声的问题

嘿,我帮你排查了代码里的几个关键问题,这应该就是你听不到蜂鸣声的核心原因:

主要问题分析

  • 音频对象生命周期过短:AVAudioEngine和AVAudioPlayerNode是playBeep()方法里的局部变量,方法执行完毕后就会被系统销毁,音频还没来得及播放就终止了。
  • 立即停止播放:你在调用player.play()后马上执行player.stop(),相当于刚启动就暂停,自然听不到声音。
  • 未配置音频会话:在iOS上,播放音频前需要激活AVAudioSession,否则系统可能会阻止音频输出。
  • 强制解包存在风险:createBeepBuffer()里直接用!强制解包AVAudioFormat,如果格式创建失败会导致崩溃,应该做安全的可选处理。

修正后的完整代码

import SwiftUI
import AVFoundation

struct BeepPlayer: View {
    // 将音频对象提升为实例属性,延长生命周期
    private let engine = AVAudioEngine()
    private let player = AVAudioPlayerNode()
    private let mixer: AVAudioMixerNode
    
    init() {
        mixer = engine.mainMixerNode
        engine.attach(player)
        // 提前连接节点,避免重复操作
        engine.connect(player, to: mixer, format: mixer.outputFormat(forBus: 0))
        // 配置音频会话
        setupAudioSession()
    }
    
    var body: some View {
        Button(action: playBeep) {
            Text("Play 600Hz Beep")
        }
    }
    
    private func setupAudioSession() {
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setCategory(.playback, mode: .default)
            try session.activate()
        } catch {
            print("音频会话配置失败: \(error)")
        }
    }
    
    func playBeep() {
        // 先停止之前的播放(如果有的话)
        player.stop()
        guard let buffer = createBeepBuffer() else {
            print("创建音频缓冲区失败")
            return
        }
        
        player.scheduleBuffer(buffer, at: nil, options: [], completionHandler: nil)
        
        do {
            // 如果引擎未启动,先启动
            if !engine.isRunning {
                try engine.start()
            }
            player.play()
            
            // 1秒后自动停止(根据你的duration设置)
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                player.stop()
            }
        } catch {
            print("播放错误: \(error)")
        }
    }
    
    func createBeepBuffer() -> AVAudioPCMBuffer? {
        let sampleRate: Double = 44100
        let frequency: Float = 600
        let duration: Double = 1
        let frames = AVAudioFrameCount(sampleRate * duration)
        
        // 安全创建音频格式
        guard let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: 2),
              let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frames) else {
            return nil
        }
        
        buffer.frameLength = frames
        
        let amplitude: Float = 0.5
        let increment = 2 * Float.pi * frequency / Float(sampleRate)
        var currentPhase: Float = 0
        
        // 安全获取通道数据
        guard let channelData = buffer.floatChannelData else {
            return nil
        }
        
        for i in 0..<Int(frames) {
            let sample = sin(currentPhase) * amplitude
            channelData[0][i] = sample
            channelData[1][i] = sample
            currentPhase += increment
        }
        
        return buffer
    }
}

关键修改说明

  1. 延长对象生命周期:把engine、player和mixer改成视图的实例属性,确保它们在视图存活期间不会被销毁。
  2. 移除立即停止的代码:改用DispatchQueue延迟1秒停止,匹配你设置的1秒蜂鸣时长。
  3. 添加音频会话配置:在初始化时设置并激活音频会话,确保系统允许音频输出。
  4. 安全处理可选类型:把createBeepBuffer()的返回值改成可选类型,用guard let处理可能的创建失败,避免强制解包崩溃。
  5. 提前连接音频节点:在初始化时完成节点连接,避免每次播放都重复操作。

另外,测试时建议用真机,虽然模拟器大部分时候也能播放,但真机的音频行为更贴近实际场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 16:40:52