Звук не воспроизводится после установки режима Hog - PullRequest
0 голосов
/ 05 августа 2020

Я пытаюсь воспроизвести аудиофайл с AVAudioEngine и AVAudioPlayerNode на USB-устройство (DA C). Когда я устанавливаю устройство в режим Hog, звук не воспроизводится, но он отлично работает без режима Hog.

Я также пробую с AVAudioPlayer, и он тоже не работает.

Кто-нибудь знает, почему и как решить проблему?

Если я установил устройство ADI-2 в режим Hog, система установит устройство по умолчанию на другое устройство Meridian и проиграет на нем звук. Я не могу найти способ воспроизвести звук на устройстве в режиме hog ADI-2 (см. Изображения ниже).

no hog mode

hog mode

import Cocoa
import AVFoundation

class ViewController: NSViewController {
    let audioEngine = AVAudioEngine()
    let playerNode = AVAudioPlayerNode()

    override func viewDidLoad() {
        super.viewDidLoad()
        do {
            let outputDevice = AudioDevice.defaultOutputDevice()
            guard let url = Bundle.main.url(forResource: "file", withExtension: "mp3") else { return }
            let audioFile = try AVAudioFile(forReading: url)
            outputDevice.setHogMode() // without this line, audio file is played.
            audioEngine.attach(playerNode)
            audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: audioFile.processingFormat)
            try audioEngine.start()
            playerNode.scheduleFile(audioFile, at: nil, completionHandler: nil)
            playerNode.play()
        } catch let error {
            print(error.localizedDescription)
        }
    }
}
class AudioDevice {
    var id: AudioDeviceID

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

    func setHogMode() -> Bool {
        guard hogModePID() != pid_t(ProcessInfo.processInfo.processIdentifier) else { return false }
        return toggleHogMode()
    }

    func unsetHogMode() -> Bool {
        guard hogModePID() == pid_t(ProcessInfo.processInfo.processIdentifier) else { return false }
        return toggleHogMode()
    }

    private func toggleHogMode() -> Bool {
        let address = AudioObject.address(selector: kAudioDevicePropertyHogMode)
        var newValue: UInt32 = 1
        let status = AudioObject.setPropertyData(id, address: address, andValue: &newValue)
        return noErr == status
    }

    private func hogModePID() -> pid_t? {
        let address = AudioObject.address(selector: kAudioDevicePropertyHogMode)
        var pid = pid_t()
        let status = AudioObject.getPropertyData(id, address: address, andValue: &pid)
        return noErr == status ? pid : nil
    }
}

extension AudioDevice {
    class func defaultOutputDevice() -> AudioDevice {
        let address = AudioObject.address(selector: kAudioHardwarePropertyDefaultSystemOutputDevice)
        var deviceId = AudioDeviceID()
        let status = AudioObject.getPropertyData(AudioObjectID(kAudioObjectSystemObject), address: address, andValue: &deviceId)
        if status != noErr {
            print("OSStatus error \(status) (defaultOutputDevice)")
        }
        return AudioDevice(deviceID: deviceId)
    }
}
class AudioObject {
    class func address(selector: AudioObjectPropertySelector, scope: AudioObjectPropertyScope = kAudioObjectPropertyScopeGlobal, element: AudioObjectPropertyElement = kAudioObjectPropertyElementMaster) -> AudioObjectPropertyAddress {
        return AudioObjectPropertyAddress(mSelector: selector, mScope: scope, mElement: element)
    }

    class func getPropertyData<T>(_ objectID: AudioObjectID, address: AudioObjectPropertyAddress, andValue value: inout T) -> OSStatus {
        var theAddress = address
        var size = UInt32(MemoryLayout<T>.size)
        let status = AudioObjectGetPropertyData(objectID, &theAddress, UInt32(0), nil, &size, &value)
        return status
    }

    class func getPropertyDataSize<Q>(_ objectID: AudioObjectID, address: AudioObjectPropertyAddress, qualifierDataSize: UInt32?, qualifierData: inout [Q], andSize size: inout UInt32) -> (OSStatus) {
        var theAddress = address
        return AudioObjectGetPropertyDataSize(objectID, &theAddress, qualifierDataSize ?? UInt32(0), &qualifierData, &size)
    }

    class func getPropertyDataSize<Q>(_ objectID: AudioObjectID, address: AudioObjectPropertyAddress, qualifierDataSize: UInt32?, qualifierData: inout Q, andSize size: inout UInt32) -> (OSStatus) {
        var theAddress = address
        return AudioObjectGetPropertyDataSize(objectID, &theAddress, qualifierDataSize ?? UInt32(0), &qualifierData, &size)
    }

    class func getPropertyDataSize(_ objectID: AudioObjectID, address: AudioObjectPropertyAddress, andSize size: inout UInt32) -> (OSStatus) {
        var nilValue: ExpressibleByNilLiteral?
        return getPropertyDataSize(objectID, address: address, qualifierDataSize: nil, qualifierData: &nilValue, andSize: &size)
    }

    class func getPropertyDataArray<T, Q>(_ objectID: AudioObjectID, address: AudioObjectPropertyAddress, qualifierDataSize: UInt32?, qualifierData: inout Q, value: inout [T], andDefaultValue defaultValue: T) -> OSStatus {
        var size = UInt32(0)
        let sizeStatus = getPropertyDataSize(objectID, address: address, qualifierDataSize: qualifierDataSize, qualifierData: &qualifierData, andSize: &size)
        if noErr == sizeStatus {
            value = [T](repeating: defaultValue, count: Int(size) / MemoryLayout<T>.size)
        } else {
            return sizeStatus
        }
        var theAddress = address
        let status = AudioObjectGetPropertyData(objectID, &theAddress, qualifierDataSize ?? UInt32(0), &qualifierData, &size, &value)
        return status
    }

    class func getPropertyDataArray<T, Q>(_ objectID: AudioObjectID, address: AudioObjectPropertyAddress, qualifierDataSize: UInt32?, qualifierData: inout [Q], value: inout [T], andDefaultValue defaultValue: T) -> OSStatus {
        var size = UInt32(0)
        let sizeStatus = getPropertyDataSize(objectID, address: address, qualifierDataSize: qualifierDataSize, qualifierData: &qualifierData, andSize: &size)
        if noErr == sizeStatus {
            value = [T](repeating: defaultValue, count: Int(size) / MemoryLayout<T>.size)
        } else {
            return sizeStatus
        }
        var theAddress = address
        let status = AudioObjectGetPropertyData(objectID, &theAddress, qualifierDataSize ?? UInt32(0), &qualifierData, &size, &value)
        return status
    }

    class func getPropertyDataArray<T>(_ objectID: AudioObjectID, address: AudioObjectPropertyAddress, value: inout [T], andDefaultValue defaultValue: T) -> OSStatus {
        var nilValue: ExpressibleByNilLiteral?
        return getPropertyDataArray(objectID, address: address, qualifierDataSize: nil, qualifierData: &nilValue, value: &value, andDefaultValue: defaultValue)
    }

    class func setPropertyData<T>(_ objectID: AudioObjectID, address: AudioObjectPropertyAddress, andValue value: inout T) -> OSStatus {
        var theAddress = address
        let size = UInt32(MemoryLayout<T>.size)
        let status = AudioObjectSetPropertyData(objectID, &theAddress, UInt32(0), nil, size, &value)
        return status
    }
}

1 Ответ

0 голосов
/ 08 августа 2020

Так что я сам никогда не использовал AVAudioEngine, но я предполагаю, что если вы хотите, чтобы он использовал определенное устройство c, вам нужно настроить его для этого (я не вижу этого в code)

Я считаю, что вы видите следующее:

  • По умолчанию AVAudioEngine будет использовать системное устройство по умолчанию
  • Включив режим hog устройство, которое является текущим системным по умолчанию, это устройство больше не может быть по умолчанию по определению - вы косвенно меняете устройство по умолчанию. device.

Похоже, есть аналогичный вопрос, который может помочь (хотя ответы - Objective- C): Установить устройства ввода и вывода AVAudioEngine

Что касается режима hog, он может считаться не очень удобным для пользователя (и может быть предназначен в первую очередь, когда вам нужно работать с несмешиваемым форматом).

...