CamillaDSP - Cross-platform IIR and FIR engine for crossovers, room correction etc.

Are you thinking of a config option like "restart_playback_on_xrun" which defaults to false?
Something like that yes. Not sure if it should become part of the standard camilladsp or not, let's see how it turns out first :)


Maybe the RPi devs could be asked to review the messages, whether some of the messages should not be added to vc4_hdmi_audio_prepare.
I think there is one thing to work out first, what is actually getting misaligned?
It seems that most receivers don't have the issue, only this audio extractor. Is it possible that the hdmi output spits out something strange when the underrun happens, that confuses the extractor? AVR:s seem to handle it ok, so I'm wondering if maybe the stream is fine again after recovering from the underrun. What happens if the audio extractor is powered off and then on again when it's in the misaligned state? Still bad?
 
I will try to implement what I described and if it works, you can decide whether to adopt it.

As you mention, if the HDMI output was truly faulty, then this would show up in more use cases.

What are you trying to find out by power-cycling the extractor? IIUC, the whole HDMI device would be detached and the playback device would fail, so it would be a completely new stream when you plug it back in.
 
Looking at the HDMI driver source (actually bcm2835_xxx.c files instead of the upstream vc4_hdmi code) - we can only guess what messages to the VC4 module do https://github.com/raspberrypi/linu...cm2835-audio/vc_vchi_audioserv_defs.h#L19-L30 .

But IIUC, at XRUN the snd_pcm_stop is called https://github.com/raspberrypi/linu..._services/bcm2835-audio/bcm2835-pcm.c#L61-L66 , sending message VC_AUDIO_MSG_TYPE_STOP https://github.com/raspberrypi/linu...vices/bcm2835-audio/bcm2835-vchiq.c#L295-L299

Upon that CDSP calls snd_pcm_prepare, which in https://github.com/raspberrypi/linu...ervices/bcm2835-audio/bcm2835-pcm.c#L212-L214 sends only configuration message https://github.com/raspberrypi/linu...vices/bcm2835-audio/bcm2835-vchiq.c#L269-L286

Upon that write messages followed by samples are sent in https://github.com/raspberrypi/linu...4_services/bcm2835-audio/bcm2835-vchiq.c#L328 , time-controlled by VC_AUDIO_MSG_TYPE_COMPLETE callback from the VC4 in https://github.com/raspberrypi/linu...4_services/bcm2835-audio/bcm2835-vchiq.c#L115 -> https://github.com/raspberrypi/linu..._services/bcm2835-audio/bcm2835-pcm.c#L52-L82 , advancing position of the stream + informing the client via snd_pcm_period_elapsed.

But there is no VC_AUDIO_MSG_TYPE_START being sent in this sequence, unlike when the stream starts in a regular way via pcm.start(). The sequence in CDSP is the recommended one, but maybe the VC4 core needs the VC_AUDIO_MSG_TYPE_START message to restart the stream "correctly". Because at regular start the sequence of messages is VC_AUDIO_MSG_TYPE_OPEN -> VC_AUDIO_MSG_TYPE_CONFIG -> VC_AUDIO_MSG_TYPE_START -> VC_AUDIO_MSG_TYPE_WRITE (maybe config swapped with start, I did not check all the details). But in the xrun -> prepare the VC_AUDIO_MSG_TYPE_START is not called, only VC_AUDIO_MSG_TYPE_STOP -> VC_AUDIO_MSG_TYPE_CONFIG -> VC_AUDIO_MSG_TYPE_WRITE .

I looked at the HDMI protocol. The audio packets and channel control are quite nicely designed, VC4 does all this internally (nothing in the linux driver, it just sends the control/data messages). Basically each packet contains 4 subpackets which carry 2ch samples (i.e. 8ch max). For 2ch the subpackets carry stereo samples sequentially, for 8ch they carry 4 stereo samples for the whole audio frame. Each subpacket has a bit flag in the packet header telling the receiver whether the subpacket is being used and contains valid data. Maybe the HDMI extractor "blindly" keeps copying the valid subpackets to each I2S data line on a round-robin basis. A missing = invalid packet would then break the 8-channels alignment. Maybe the HDMI transmitter outputs packets with some of the subpackets missing when it receives new data until the VC_AUDIO_MSG_TYPE_START (kind of restart) msg is received. I do not know, just blindly guessing.

Nevertheless I wonder what adding 'pcmdevice.start()?;' behind https://github.com/HEnquist/camilla...b68d1bc8c21045f250054a/src/alsadevice.rs#L139 or
'bcm2835_audio_start(alsa_stream)' to the prepare method https://github.com/raspberrypi/linu...c04_services/bcm2835-audio/bcm2835-pcm.c#L217 would do, just for the sake of troubleshooting this issue. If it worked (for AVRs and the extractor), maybe we could ask RPi people to add the VC_AUDIO_MSG_TYPE_START message to the prepare method.

Adding the line to CDSP is much easier but the pcm.start() call does much more than just sending the VC_AUDIO_MSG_TYPE_START message.
 
  • Like
Reactions: 1 user
What are you trying to find out by power-cycling the extractor? IIUC, the whole HDMI device would be detached and the playback device would fail, so it would be a completely new stream when you plug it back in.
The idea was to try to reset the extractor, but if that interrupts the hdmi output device too then it won't work.
Adding the line to CDSP is much easier
We should try this! I'll check what to add, hopefully later tonight.
 
  • Like
Reactions: 1 user
IMO the START message in the driver would be better to test, with a potential to improve the driver
Agreed, but that is quite a bit more work to try. Modifying and recompiling CamillaDSP is quick so I think it makes sense to do that first.

@gordoste Just follow phofmans suggestion and add this in alsadevice.rs:
139: pcmdevice.prepare()?;
140: pcmdevice.start()?;

There is also a stop() command, not sure exactly what it does but it could maybe be interesting to try adding it the same way before either the start() or the prepare() command.
 
Here is what happens

Code:
2023-12-20 12:10:20.972205 WARN  [src/alsadevice.rs:138] PB: Prepare playback after buffer underrun
2023-12-20 12:10:20.972341 DEBUG [src/alsadevice.rs:140] Calling snd_pcm_start()
2023-12-20 12:10:20.972438 ERROR [src/bin.rs:286] Playback error: ALSA function 'snd_pcm_start' failed with error 'EPIPE: Broken pipe'
2023-12-20 12:10:20.972513 DEBUG [src/bin.rs:294] Wait for capture thread to exit..
2023-12-20 12:10:21.014052 DEBUG [src/alsadevice.rs:690] Exit message received, sending EndOfStream
2023-12-20 12:10:21.014839 DEBUG [src/bin.rs:1038] Processing ended with status Ok(Restart)
2023-12-20 12:10:21.014894 DEBUG [src/bin.rs:1052] Restarting with new config
2023-12-20 12:10:21.014913 DEBUG [src/bin.rs:994] Wait for config
2023-12-20 12:10:21.014928 DEBUG [src/bin.rs:1004] Wait mode is disabled, there are no queued commands, and no new config. Exiting.

You can see I added a debug message.

When starting CDSP, the config file location is loaded from the statefile. But it appears that this does not happen again on restart. Is that intentional? (To be clear, this has nothing to do with the channel swapping).
 
Just had a bit more of a think about this behaviour and realised it avoids getting into an infinite loop if a config error means that the playback is failing. So, that makes sense. My workaround is going to need to add to the ExitState enum, which currently has Restart or Exit. I will probably call it Resume because it should restart the thread and resume processing.
 
Agreed, but that is quite a bit more work to try.
That's very true. On the other hand, if a fix/improvement were to arise from this issue, IMO it would be in the driver. The xrun handling in CDSP looks OK and works for other users/drivers. Maybe the RPi HDMI driver does not follow the alsa contract xrun -> stop -> prepare correctly and assumes that clients would always call start before/after prepare.

Cross-compiling RPi kernel on any linux PC and copying to the SD card is well detailed in https://www.raspberrypi.com/documentation/computers/linux_kernel.html#cross-compiling-the-kernel .
There is also a stop() command, not sure exactly what it does but it could maybe be interesting to try adding it the same way before either the start() or the prepare() command.
IIUC the driver already handles the xrun detection with calling stop internally https://github.com/raspberrypi/linu..._services/bcm2835-audio/bcm2835-pcm.c#L61-L66 . Actually it's the effect of this stop call which returns the SNDRV_PCM_STATE_XRUN state to userspace.
 
Last edited:
Just had a bit more of a think about this behaviour and realised it avoids getting into an infinite loop if a config error means that the playback is failing.
Yes this is the idea. Recover if possible, and stop and wait for help if not.
if a fix/improvement were to arise from this issue, IMO it would be in the driver
Agreed!
 
I have implemented the "restart playback on xrun" feature. It is a bit messy because a StatusMessage:: PlaybackError only carries a string, so I had to use a magic string value to indicate that an xrun occurred and that the thread should resume with the existing config. If this is going to be a feature then perhaps a StatusMessage:: PlaybackXrun should be added. And of course, the behaviour needs to be dependent on a config option.

Hopefully this link will allow you to see the code: https://github.com/HEnquist/camilladsp/compare/master...gordoste:camilladsp:master
 
Agreed, but that is quite a bit more work to try. Modifying and recompiling CamillaDSP is quick so I think it makes sense to do that first.

@gordoste Just follow phofmans suggestion and add this in alsadevice.rs:
139: pcmdevice.prepare()?;
140: pcmdevice.start()?;

There is also a stop() command, not sure exactly what it does but it could maybe be interesting to try adding it the same way before either the start() or the prepare() command.

Also, in case you missed it, I previously tried this, the result (broken pipe) is in post #4488.
 
Also, in case you missed it, I previously tried this, the result (broken pipe) is in post #4488.
Would have been really nice if it had worked :) Another thing that could be worth trying, where I think the chance of success is higher, is to add a more controlled stop and start after it has recovered from the xrun. That would then be a call to drain(), followed by a second prepare().


I have implemented the "restart playback on xrun" feature. It is a bit messy because a StatusMessage:: PlaybackError only carries a string, so I had to use a magic string value to indicate that an xrun occurred
I'll take a look!

How do you feel about following phofmans suggestion to try to fix the problem in the driver? These other things are just various ways to try to work around the problem, while that could solve it.
 
Henrik, a low priority suggestion for the pipeline display to use the Filter Step Description as the text in the box for the collapsed display view.
I'm playing a bit with this, and it's not as difficult as I feared to add tooltips to an svg.
This is how it looks at the moment, normal view:
tooltip-normal.png
and collapsed view:
tooltip_collapsed.png
 
  • Like
Reactions: 1 users
Can Camilla use a MacOS laptop with HDMI out into an AVR and show it as a stereo output option
I don't have an AVR so I can't test outputting 8 channels over hdmi from macOS, but I would be quite surprised if it doesn't work.
Assuming that this works (can anyone please confirm?) then it's no problem to do what you want. Install Blackhole, which provides a stereo output device that you use as your default output. Then you set camilladsp up to capture stereo from blackhole, expand that to 8 channels and do the crossover filtering, and finally output to the AVR via the hdmi output device.
 
  • Like
Reactions: 1 users
Install Blackhole, which provides a stereo output device that you use as your default output. Then you set camilladsp up to capture stereo from blackhole, expand that to 8 channels and do the crossover filtering, and finally output to the AVR via the hdmi output device.
Perfect. This is all I needed to get me going in the right direction. Thank you!