DSD over DoP for Multi-channel playback

Started by soundgals,

soundgals

This Ian in the following code...
if (s & 0xffff0000) == 0x50000 ||
                   (s & 0xffff0000) == 0xfffa0000 ||
                   (s & 0xffff0000) == 0xffaa0000 {
                    dop = s & 0xffff0000 // retain the marker
                } else if s == 0 && dop != 0 { // empty sample with DoP context
                    let newSample = dop | 0x6969 // DoP marker + DSD silence value
                    fbuffer[a] = Float(Int32(bitPattern: newSample)) / 16777216.0 // replace empty sample
                } else if s != 0 && (s & 0xffff0000) != dop {
                    break // not DoP data, stop processing
                }

... The if statement is entered; but the debugger never reaches the line... dop = s & 0xffff0000 // retain the marker...
So I guess there is still something wrong with this code?

Ian @ un4seen

What is the value of the "s" variable at that point?

soundgals

S can have different numeric values such as 698022.

The code sometimes falls through to this line... } else if s != 0 && (s & 0xffff0000) != dop {
                    break // not DoP data, stop processing
                }... and falls out of the for loop. Is it to be expected that some of the data is not recognised as DoP?

Ian @ un4seen

When you're playing a DoP stream (with BASS_ATTRIB_NORAMP=1), the DSP function should always detect it and hit the "retain the marker" line. At other times, it'll hit the "not DoP data, stop processing" line.

Are you sure you should be using "bitPattern" here?

Quote from: soundgals            let s = UInt32(bitPattern: Int32(fbuffer[a] * 16777216.0)) // get sample as UInt32
...
                    fbuffer[a] = Float(Int32(bitPattern: newSample)) / 16777216.0 // replace empty sample

What about if you change it to this?

            let s = Int32(fbuffer[a] * 16777216.0) // get sample as Int32
...
                    fbuffer[a] = Float(newSample) / 16777216.0 // replace empty sample

soundgals

It's the only way I could get prevent xCode errors. If I change those values to signed (Int32s) I get the errors... Integer literal '4294574080' overflows when stored into signed 'Builtin.Int32'

soundgals

My apologies Ian as I just realised I've been missing the obvious. I'm getting an error code 33 when trying to start the devStream encoder. So no wonder it's not working. I don't know how to resolve the error though. Here is the Swift code up to that point...
if (info.freq != channelInfo.freq){ // the rates don't match
                   BASS_Init(device, channelInfo.freq, DWORD(BASS_DEVICE_FREQ | BASS_DEVICE_REINIT), nil, nil)
               }
               var devstream: HSTREAM = 0
               // Define the STREAMPROC function type to match what BASS expects
               typealias STREAMPROC = @convention(c) (HSTREAM, UnsafeMutableRawPointer?, DWORD, UnsafeMutableRawPointer?) -> DWORD
 
               // Create STREAMPROC_DEVICE by casting the special value -2
               let STREAMPROC_DEVICE = unsafeBitCast(-2, to: STREAMPROC?.self)
 
               // Create the device stream
               devstream = BASS_StreamCreate(0, 0, 0, STREAMPROC_DEVICE, nil)
               BASS_ChannelSetDSP(devstream, dsdSilenceDSP, nil, 0)
               if devstream == 0{
                   let error = BASS_ErrorGetCode()
                   print("Can't create stream", error)
               } else {
                   let musicUrl = FileManager.default.urls(for: .musicDirectory, in: .userDomainMask).first!
                   // BASS requires the "file://" prefix to be removed from the url in string format
                   let destinationUrl = musicUrl.appendingPathComponent("output.wav").absoluteString.replacingOccurrences(of: "file://", with: "")
                   BASS_ChannelPlay(stream, 1)
                   let encoder = BASS_Encode_Start(devstream, destinationUrl,  DWORD(BASS_ENCODE_PCM | BASS_ENCODE_FP_24BIT | BASS_ENCODE_AUTOFREE), nil, nil)
                   
                   if encoder == 0{
                       let error = BASS_ErrorGetCode()
                       print("Can't start the encoder", error)
                   }
                   do{
                       sleep(8)
                   }
                   BASS_Encode_Stop(devstream)

Ian @ un4seen

Quote from: soundgalsMy apologies Ian as I just realised I've been missing the obvious. I'm getting an error code 33 when trying to start the devStream encoder. So no wonder it's not working. I don't know how to resolve the error though.

Error code 33 is BASS_ERROR_CREATE, which means BASS_Encode_Start was unable to create the output file. Did you recently change the "destinationUrl" value in that call? It was definitely working at some point because you got the output.wav files from it.

Anyway, I think I now see what the problem is with the DSP function. Where I wrote 16777216, that should be 8388608 (in both cases). Please try correcting that and see if it works then.

soundgals

Thanks Ian. It's looking better now. The trouble is I was trying to test on a different system and I forgot to change the permissions for the folder I was trying to write too. I can also confirm that now the "retain the marker" line is getting hit in the "dsdSilenceDSP" code.

I won't be able to actually hear whether multichannel DoP is working on this system. So please take a look at this new output.wav and check if the channels without content are now being silenced.

Ian @ un4seen

That output.wav file is looking good. Hopefully it'll sound good too!

soundgals

Great thanks. I don't have a multi-channel system where I am now; but I can try to let it down-mix to stereo. When DoP for multi-channel wasn't working the front left and right channels were silent.

My final question on this; is it really necessary to set the volume on all channels to max for DoP multi-channel to work? On my own system I attenuate the centre and lfe channel volumes using the DACs volume control. If this is not possible I will have to apply that attenuation via BASS somehow and provide an interface in my app for that.

Ian @ un4seen

Yes, the DoP data needs to reach the DAC unmodified, as any modification of it will make it no longer DoP data (and so the DAC won't detect DoP/DSD). Any volume adjustment will need to be applied by the DAC (or after that) rather than BASS or the OS. The same goes for downmixing, so you should disable BASS downmixing (with BASS 2.4.18):

BASS_ChannelSetAttribute(stream, BASS_ATTRIB_DOWNMIX, 0); // disable downmixing