sbooth / SFBAudioEngine

A powerhouse of audio functionality for macOS, iOS, and tvOS
https://sbooth.github.io/SFBAudioEngine/
MIT License
569 stars 86 forks source link

Play DSD in DoP mode will heard noise in the sound #203

Closed szgambol closed 2 years ago

szgambol commented 3 years ago

I use SimplePlayer-macOS test dsd audio file ( DSD64 ) in DoP mode, referred Issues#185, set the device sample rate before play, but in my situation , I heard a lot noise in the sound, that is my test code below:

        do {

            var enableDoP = false
            var sampleRate : Double = 48 * 1000

            if let audioFile = try? AudioFile(readingPropertiesAndMetadataFrom: item.url) {
                let bitPerChannel = audioFile.properties.bitsPerChannel
                sampleRate = audioFile.properties.sampleRate ?? sampleRate

                enableDoP = bitPerChannel == 1

                if (enableDoP) {
                    sampleRate = sampleRate / 16
                }

                if let device = AudioObject.make(player.outputDeviceID) as? AudioDevice {
                    try device.setSampleRate(sampleRate)
                }

                if let decoder = try item.decoder(enableDoP: enableDoP) {
                    try player.play(decoder)
                }
            }
        }
        catch let error {
            NSApp.presentError(error)
        }

Hope get some suggestion, thank very much.

BTW:

My DAC is McIntosh C47, use Other APP (like Audirvana Plus) to play in DoP mode is ok.

sbooth commented 3 years ago

Does the DAC show that it is receiving DSD input? If so that means at least the DoP markers are being correctly transmitted.

sbooth commented 3 years ago

As an aside, you might consider waiting for the sample rate change to take effect (using a property listener) before calling player.play, although I don't think that's related to your issue.

szgambol commented 3 years ago

Yes, the DAC can display DSD64 when playing.

I try to set delay ( 2、5、10 seconds ) calling player.play after set sample rate, it nothing change, still noise in the sound.

And I used another DAC OPPO Sonica DAC to test, it ok (both on DSD64 and DSD128 test file), no noise in the sound, DAC display DSD on 2.822 MHz / DSD on 5.644 MHz.

I noticed if set delay ( 2 seconds ) after set sample rate, play DSD128 file, DAC( McIntosh C47 ) also can display DSD128, if not, will display 352.8kHz

Test code below:

                if let device = AudioObject.make(player.outputDeviceID) as? AudioDevice {
                    try device.setSampleRate(sampleRate)
                }

                var bFlag = true
                let date_old = Date()
                let tmDelay = 2

                while bFlag {
                    let elapsed = Date().timeIntervalSince(date_old)
                    let duration = Int(elapsed)

                    bFlag = duration < tmDelay
                    usleep(1000 * 500) // 0.5 secodns
                }

                if let decoder = try item.decoder(enableDoP: enableDoP) {
                    try player.play(decoder)
                }
sbooth commented 3 years ago

Since it's working with the OPPO DAC I assume the problem lies with the non-deterministic amount of time it is taking for the McIntosh to change sample rates. What happens when using a property listener and not calling play until the sample rate change is complete?

szgambol commented 3 years ago

Thank your suggestion.

I am so sorry that I am a newer for swift, actually I don’t know how to write property listener for device sample rate change in this project now.

I think your mean is , I need to check device sample rate, ensure call player.play must be after change is completed, so I write a timer instead property listener ( I'm not sure if it's the right way ), run this code, it seem device always change sample rate correct, because log output like below, but noise did not disappear :

2021-11-01 14:14:35.332241+0800 SimplePlayer[87590:1506374] [AudioObject] Setting device 0x3a sample rate to 176400.00 Hz
-------------------------------------------------
176400.0  -> device.actualSampleRate :  176400.0 device.sampleRate :  176400.0
Sample rate is changed!

That is my test code:

func play(item: PlaylistItem) {
        do {
            var enableDoP = false
            var sampleRate : Double = 48 * 1000

            if let audioFile = try? AudioFile(readingPropertiesAndMetadataFrom: item.url) {
                let bitPerChannel = audioFile.properties.bitsPerChannel
                sampleRate = audioFile.properties.sampleRate ?? sampleRate

                enableDoP = bitPerChannel == 1

                if (enableDoP) {
                    sampleRate = sampleRate / 16
                }

                if let device = AudioObject.make(player.outputDeviceID) as? AudioDevice {
                    try device.setSampleRate(sampleRate)
                }

                let context = [sampleRate, enableDoP, item] as [Any]
                Timer.scheduledTimer(
                    timeInterval : 1.0,
                    target: self,
                    selector: #selector(checkDevice),
                    userInfo: context,
                    repeats: true)
            }
        }
        catch let error {
            NSApp.presentError(error)
        }
}

checkDevice function

    var runCount = 5
    @objc func checkDevice(timer: Timer)
    {

        guard let context = timer.userInfo as? [Any] else { return }
        let setting_sampleRate :Double = context[0] as! Double
        let enableDoP :Bool = context[1] as! Bool
        let item :PlaylistItem = context[2] as! PlaylistItem
        do {
            if let device = AudioObject.make(player.outputDeviceID) as? AudioDevice {
                let actualSampleRate : Double = try device.actualSampleRate()
                let sampleRate : Double = try device.actualSampleRate()
                print("-------------------------------------------------")
                print(setting_sampleRate," -> device.actualSampleRate : ", actualSampleRate, "device.sampleRate : ", sampleRate)

                if fabs(setting_sampleRate - actualSampleRate) < Double.ulpOfOne {
                    print("Sample rate is changed!")
                    timer.invalidate()
                    runCount = 5

                    if let decoder = try item.decoder(enableDoP: enableDoP) {
                        try player.play(decoder)
                    }
                }
            }
        }
        catch let error {
            print(error)
            timer.invalidate()
            runCount = 5
        }

        runCount -= 1
        if runCount == 0 {
            timer.invalidate()
            runCount = 5
        }
    }
szgambol commented 3 years ago

Hi,

I can write property listener now, and when I test nominalSampleRate property is work, but actualSampleRate no work ( listener never be trigger ), when I use nominalSampleRate property for trigger to call player.play still noise.

It seem can't detect actualSampleRate change from my DAC (both OPPO and McIntosh).

do I correct about property listener ?

I edited updateDeviceMenu() func, add code below:

if isActiveDevice {
    devicePopUpButton.select(deviceMenuItem)

    /// make property listener for nominalSampleRate
    /// actualSampleRate, nominalSampleRate
    if let device = AudioObject.make(currentOutputDeviceID) as? AudioDevice {
        /// TODO : need to unregister listener when device change
        /// TODO : when sample rate not change will not to play
        try device.whenSelectorChanges(AudioObjectSelector.nominalSampleRate, perform: {_ in
            DispatchQueue.main.async {
                self.onSampleRateChanged()
            }
        })
    }
}

function onSampleRateChanged:

    func onSampleRateChanged() {
        do {
            let item : PlaylistItem? = self.currentItem
            let enableDoP : Bool = self.currentEnableDop
            if let decoder = try item!.decoder(enableDoP: enableDoP) {
                try player.play(decoder)
            }
            print("nominalSampleRate is changed")
        } catch let error {
            NSApp.presentError(error)
        }

    }

code in the function func play(item: PlaylistItem)

                self.currentItem = item
                self.currentEnableDop = enableDoP

                if let device = AudioObject.make(player.outputDeviceID) as? AudioDevice {
                    try device.setSampleRate(sampleRate)
                }
sbooth commented 3 years ago

I assume the problem happens on more than one file? Are you able to test with both a .dsf and .dsdiff file?

szgambol commented 3 years ago

Yes, I test another DSD file that is .dff format, same thing happended.

sbooth commented 3 years ago

I'm not quite sure yet what the problem could be so I don't have any great suggestions to diagnose the issue. Does ensuring the player is stopped before changing the sample rate have any effect?

Also, does the McIntosh DAC use the CoreAudio drivers?

szgambol commented 3 years ago

I aways click stop button first and then select new DSD file, so I think the player stoped.

McIntosh C-47 only supply ASIO driver for Win, but say support DoP on OS X without driver.

Thank you for your replay. I'll check things out again if there are any updates.

szgambol commented 2 years ago

Hi,

I think maybe I found the problem about the noise.

When I use MIDI tools, my DAC MC-47 has three options under 176.4 kHz, ttt

If I select 2ch 24-bit Integer and 2ch 32-bit Integer, the noise will be gone, and 2ch 16-bit Integer the noise will be back.

And I try to find the way to select correct options with Core Audio API, it seem no support, do you have any suggests ?

sbooth commented 2 years ago

Those formats are properties of the AudioStream objects in the output scope of the AudioDevice. A stream has a virtual and physical format. You can inspect them using the AudioDevice and AudioStream classes.

You can try searching kAudioDevicePropertyStreams, kAudioStreamPropertyVirtualFormat, or kAudioStreamPropertyAvailableVirtualFormats for more information.

szgambol commented 2 years ago

Yes, change physical format with AudioStream can set the device to 2ch 24Bit 176.4kHz in my DAC, thank you for you help !