列出所有可用的音频设备

时间:2018-08-20 16:00:18

标签: swift macos cocoa audio

我想迅速列出所有可用的音频设备,以提供输入和输出的选择。我的应用程序应该在音频通道上侦听,然后“写入”另一个通道。我不希望系统默认!

    let devices = AVCaptureDevice.devices(for: .audio)
    print(devices.count)
    for device in devices {
        print(device.localizedName)
    }

该代码列出了0个设备。但我至少希望内部输出。

一些指向CoreAudio,AudioToolbox和AVFoundation的链接会很好地说明音频源的选择。

4 个答案:

答案 0 :(得分:1)

当我将其粘贴到Xcode Playground中时,您发布的代码对于音频 input 设备非常合适。

但是请注意,AVCaptureDevice API不会列出音频输出设备,因为它们不是捕获设备,而是回放设备。如果设备同时支持输入和输出,则您仍可以在输出上下文中使用设备的uniqueID,例如与AVPlayer的{​​{1}}一起使用。

(还要注意,如果您希望代码也能在iOS上运行,自iOS 11起,audioOutputDeviceUniqueID被标记为已弃用,而应改为AVCaptureDevice.DiscoverySession。)

关于您对Core Audio和AudioToolbox的其他信息的要求,this SO question对此有一些非常全面的答案。该问题要求输入设备,但答案提供了足够的上下文,使您也可以了解输出端的处理方式。甚至还有一些(日期)Swift code的答案。就个人而言,我不得不说从Swift调用Core Audio API通常比获得更多的痛苦。因此,虽然有些不安全,但如果您的项目允许,将这些代码的部分包装到Objective-C或普通C并通过Swift桥接标头公开,可能会更快。

答案 1 :(得分:1)

下面是一些Swift 5代码,将枚举所有音频设备。

您可以将uid与AVAudioPlayer的currentDevice属性一起使用,以输出到特定设备。

import Cocoa
import AVFoundation

class AudioDevice {
    var audioDeviceID:AudioDeviceID

    init(deviceID:AudioDeviceID) {
        self.audioDeviceID = deviceID
    }

    var hasOutput: Bool {
        get {
            var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
                mSelector:AudioObjectPropertySelector(kAudioDevicePropertyStreamConfiguration),
                mScope:AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
                mElement:0)

            var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size);
            var result:OSStatus = AudioObjectGetPropertyDataSize(self.audioDeviceID, &address, 0, nil, &propsize);
            if (result != 0) {
                return false;
            }

            let bufferList = UnsafeMutablePointer<AudioBufferList>.allocate(capacity:Int(propsize))
            result = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, bufferList);
            if (result != 0) {
                return false
            }

            let buffers = UnsafeMutableAudioBufferListPointer(bufferList)
            for bufferNum in 0..<buffers.count {
                if buffers[bufferNum].mNumberChannels > 0 {
                    return true
                }
            }

            return false
        }
    }

    var uid:String? {
        get {
            var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
                mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceUID),
                mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
                mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))

            var name:CFString? = nil
            var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size)
            let result:OSStatus = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, &name)
            if (result != 0) {
                return nil
            }

            return name as String?
        }
    }

    var name:String? {
        get {
            var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
                mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceNameCFString),
                mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
                mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))

            var name:CFString? = nil
            var propsize:UInt32 = UInt32(MemoryLayout<CFString?>.size)
            let result:OSStatus = AudioObjectGetPropertyData(self.audioDeviceID, &address, 0, nil, &propsize, &name)
            if (result != 0) {
                return nil
            }

            return name as String?
        }
    }
}


class AudioDeviceFinder {
    static func findDevices() {
        var propsize:UInt32 = 0

        var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
            mSelector:AudioObjectPropertySelector(kAudioHardwarePropertyDevices),
            mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
            mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))

        var result:OSStatus = AudioObjectGetPropertyDataSize(AudioObjectID(kAudioObjectSystemObject), &address, UInt32(MemoryLayout<AudioObjectPropertyAddress>.size), nil, &propsize)

        if (result != 0) {
            print("Error \(result) from AudioObjectGetPropertyDataSize")
            return
        }

        let numDevices = Int(propsize / UInt32(MemoryLayout<AudioDeviceID>.size))

        var devids = [AudioDeviceID]()
        for _ in 0..<numDevices {
            devids.append(AudioDeviceID())
        }

        result = AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &propsize, &devids);
        if (result != 0) {
            print("Error \(result) from AudioObjectGetPropertyData")
            return
        }

        for i in 0..<numDevices {
            let audioDevice = AudioDevice(deviceID:devids[i])
            if (audioDevice.hasOutput) {
                if let name = audioDevice.name,
                    let uid = audioDevice.uid {
                    print("Found device \"\(name)\", uid=\(uid)")
                }
            }
        }
    }
}

答案 2 :(得分:1)

可以列出输入和输出设备。这是stevex's answer的简化。

对于输出设备:

    if (audioDevice.hasOutput) {
        if let name = audioDevice.name,
            let uid = audioDevice.uid {
            print("Found device \"\(name)\", uid=\(uid)")
        }
    }

对于输入设备:

    if (!audioDevice.hasOutput) {
        if let name = audioDevice.name,
            let uid = audioDevice.uid {
            print("Found device \"\(name)\", uid=\(uid)")
        }
    }

(请注意{{1}之前的!。)

答案 3 :(得分:-1)

在macOS 10.12和更高版本中使用macos-audio-devices。这是一个node.js应用程序。该文档说,macOS 10.13或更早的版本需要下载the Swift runtime support libraries