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

如何在macOS上捕获音频输出采样?已尝试AVAudioEngine未成功

在macOS上捕获指定输出设备的音频采样

你的代码问题在于使用了audioEngine.inputNode——这是捕获麦克风输入的节点,而非你需要的输出设备的音频流。要捕获输出设备的声音,需要针对目标输出设备的节点配置Tap,而非默认输入节点。

修正方案与完整代码

  1. 定位目标输出设备:先枚举系统可用的音频输出设备,筛选出你需要捕获的设备
  2. 绑定引擎到目标设备:将AVAudioEngine关联到选定的输出设备
  3. 在输出节点安装Tap:针对输出节点设置数据回调,捕获输出的音频采样
  4. 处理权限:确保应用已获得macOS音频捕获权限(在系统隐私设置中开启)

完整实现代码:

import AVFoundation

class AudioOutputCaptureManager {
    private let audioEngine = AVAudioEngine()
    private var captureCallback: (([Float]) -> Void)?
    private var targetDevice: AVAudioDevice?
    
    init(with device: AVAudioDevice?) {
        self.targetDevice = device
        setupEngine()
    }
    
    // 枚举所有可用的输出设备
    static func getAvailableOutputDevices() -> [AVAudioDevice] {
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setCategory(.playAndRecord, mode: .default)
            // 筛选支持输出的设备
            return session.availableInputs?.filter { device in
                device.dataSources?.contains(where: {
                    $0.dataSourceType == .lineOut || $0.dataSourceType == .speaker
                }) ?? false
            } ?? []
        } catch {
            print("获取音频设备失败: \(error)")
            return []
        }
    }
    
    private func setupEngine() {
        guard let device = targetDevice else {
            print("未指定目标输出设备")
            return
        }
        
        do {
            // 将引擎输出节点绑定到目标设备
            try audioEngine.outputNode.setDevice(device)
            let outputFormat = audioEngine.outputNode.outputFormat(forBus: 0)
            
            // 在输出节点安装Tap,捕获音频数据
            audioEngine.outputNode.installTap(onBus: 0, bufferSize: 1024, format: outputFormat) { buffer, _ in
                let frameCount = Int(buffer.frameLength)
                let channels = UnsafeBufferPointer(start: buffer.floatChannelData, count: Int(buffer.format.channelCount))
                
                if let channelData = channels.first {
                    let samples = Array(UnsafeBufferPointer(start: channelData, count: frameCount))
                    self.captureCallback?(samples)
                }
            }
        } catch {
            print("配置音频引擎失败: \(error)")
        }
    }
    
    func startCapture(callback: @escaping ([Float]) -> Void) {
        self.captureCallback = callback
        do {
            try AVAudioSession.sharedInstance().setActive(true)
            try audioEngine.start()
        } catch {
            print("启动捕获失败: \(error)")
        }
    }
    
    func stopCapture() {
        audioEngine.stop()
        audioEngine.reset()
        do {
            try AVAudioSession.sharedInstance().setActive(false)
        } catch {
            print("停用音频会话失败: \(error)")
        }
    }
}

使用示例

// 获取可用输出设备并选择第一个
if let targetDevice = AudioOutputCaptureManager.getAvailableOutputDevices().first {
    let captureManager = AudioOutputCaptureManager(with: targetDevice)
    captureManager.startCapture { samples in
        // 处理捕获到的音频采样
        print("捕获到\(samples.count)个采样点")
    }
    // 停止捕获时调用
    // captureManager.stopCapture()
}

关键注意事项

  • 权限配置:必须在Info.plist中添加NSMicrophoneUsageDescription字段(macOS音频捕获权限统一依赖此配置),否则应用会崩溃或无法访问设备
  • 设备兼容性:部分输出设备不支持音频回环捕获,需确认目标设备具备此功能
  • 格式自定义:如果需要特定采样率/位深的音频数据,可以在installTap时传入自定义的AVAudioFormat参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 21:51:08