Roon should be using playback queue feature of HQPlayer and not issue stop commands or other playlist operations between tracks.
You only get it if you have latency, for example from convolution file with high sample rate and high number of taps. But you could try sinc-M and up-sample to say 192khz and it will give some latency.
Iâve got 5 sec latency because of convolution and filter, but with core and HQPlayer on the same machine the problem is gone.
Roon donât seem to interested in fixing things like this nowadays, its more about interface tweaks. And where is @brian?
Latency should only be there when you first hit play in Roon (initialisation).
Shouldnât be there between each track, like if listening to a gapless live album / dj mix.
You donât get the cut-off between tracks on same album, only between albums or between song with different sample rate or bit depth.
I upsample all PCM and DSD content to DSD256 in HQPlayerâŠ
So do I. I got the cut end syndrome when I had core and HQPlayer on different machines. Running both on the same machine resolved the problem.
This is incompatible with our architecture. How can we find out that the stream has really ended (from the perspective of the ears of the user)?
HQPlayer reports state has changed to âstoppedâ. If you ask HQPlayer to stop, it will stop which is expensive operation.
If you want to proceed to next URI without interrupting current one, do âPlaylistAddâ with
queued="1"
attribute set before current one ends. (you have also option for âclearâ attribute)
If you want to immediately proceed (seek or user interactively changing track case), also issue âNextâ command. There is also
<Play last="1"/>
possible.
Or alternatively use the âPlayNextURIâ command.
Extremely long filters mean that head and tail of the filter can add several seconds to beginning and end of playback, compared to formal track length.
P.S. It would be great if Roon would support name based HQPlayer auto-discovery instead of having to fill in IP/hostname (nice option though)⊠Very useful when HQPlayer is running on a machine with DHCP assigned IP.
By âname-basedâ, you mean we should resolve DNS? Thatâs a simple thing for us to do.
We canât really use the queueing stuffâthis is a common friction point when we set Roonâs transport on top of someone elseâs transport, as with HQPlayer, UPnP-like systems, etc. Roon is operating with an abstraction that is more like a stream. The stream may contain several tracks, gapless, crossfaded, etc.
When the format changes, we wait for the stream to play out, then start a new one with the new format. We donât have the information about the new stream until our state machine advances to that point, after the previous one finished, so promising HQPlayer a URL that may/may not exist in the future is a little bit uncomfortable.
I took a quick look in the code, and it looks like we are trying to determine that the stream ended by catching HQPlayerâs transition to âstoppedâ. Is there any chance that HQPlayer is transitioning before the audio is done playing from the userâs perspective?
Should be easy to test by playing local files from HQPlayer, my guess is that HQPlayer stops audio when it gets a stop from Roon, instead of letting the âlatency timeâ of music play first.
Having said that, if you press pause/stop in Roon you want the music to stop as soon as possible. So maybe some transition-stop command is needed?
No, you send multicast discovery packet and see who are the HQPlayerâs around. No DNS needed, each HQPlayer instance responds with their name. More like Avahi/ZeroConf/Bonjour. You can use reverse-DNS on the host IP though.
You can see the client API source code for example implementation (that is used by HQPlayer Client and hqp-control2).
This is not a problem, you need a new stream only once the format changes. You donât need to stop/restart HQPlayer for example when you seek like you now do. If you want faster change, you can use PlayNextURI.
You shouldnât and you donât need to, you just put next item on the list and let playback naturally proceed there.
Yes, it is normal thing to happen. At that point you only know that HQPlayer is done reading the source.
Letâs say filter has 10 second long tail, then after last sample of a track still 10 seconds of silence is pushed through the pipeline to get the entire tail out of the filter. At that point, playback is 10 seconds past length of the track.
Yes, thatâs the case, because HQPlayer cannot know why the stop was asked. It is assumed user wanted to stop the playback.
HQPlayer will stop by itself once it has played out the source. If you want to cut a stream shorter, you just disconnect HQPlayerâs stream connection. There is no need or point in âtransition-stopâ.
But isnât that exactly what HQPlayer doesnât do, at least not if you factor in latency? Roon does a reset (or something similar because of new sample rate) and HQPlayer cuts the music, which leads to cutting off the end of the track.
Roon asks HQPlayer to stop, clears the playlist, adds new item to the playlist and then asks for playback.
This means HQPlayer will stop, tear down the DSP pipeline, stop the DAC and then start everything all over again. Lot of unnecessary heavy lifting.
Instead of Roon just adding next item to the list and letting HQPlayer to proceed there. Or do that even just when HQPlayer tells it has finished (not as reliable).
Yes, thats why I proposed another command, that will continue playing but with a new song with a new sample rate and/or bit depth. Then Roon can use that between songs when needed, and HQPlayer can reuse whatever code you have for similar scenarios when playing from a playlist.
We are not going to do that.
The problem with giving you the next track early, is that it creates a large window where the user could change what the next track is but weâve already told HQPlayer something about it.
This is a tricky condition to handle within any media player and requires a fair amount of careful synchronization. Thereâs no way we could support code attempting to do that in an environment where there is no atomicity or synchronization possible.
Yes, doing a full reset is more heavy-weight in terms of the number of steps, but itâs also safer, and allows for a clean synchronization point where Roon is sure that HQPlayer is doing nothing, and then Roon can atomically (on our end) determine the next track and get the next stream going.
We have tried using proposals like yours in the past with other UPnP-style transport abstractions. It just doesnât end up working well for the reasons I stated above. It results in a lot of flakey bugs around the edges.
This is the root of the problem. This may be normal for HQPlayer, but it is not normal in general
The playhead and transport state in a media player should correspond to the presentation clockâwhat the user is hearing. In computer audio, this is obtained at the point where audio samples are being transmitted to the driver, and comes with side-channel data about additional delays known to the driver that should be handled by the application. Then the application adds knowledge of its own delays.
When HQPlayer says âI am stoppedâ, Roon is (understandably) assuming that it is actually done playing and that doing the reset procedure for the next stream wonât interrupt anything.
If HQPlayer tells Roon that it is stopped while itâs still playingâŠthatâs going to cause problems like this.
Maybe caching in HQPlayer is the only way to solve this, because if HQPlayer delays reporting stop to Roon to avoid cutting the end of the tune, then the latency will still be there but now between the tunes.
Lets take an example: tune A is 3 min long, its followed by tune B in another sample rate and there is a 10 second latency in HQPlayer due to DSP and filters. After 3 minutes, Roon will send a stop to HQPlayer, but only 2:50 of the tune has actually been playing:
- If HQPlayer stops and starts playing tune B at this point, the end of tune A will be cut, and there will still be a 10 second delay before tune B starts
- If HQPlayer delays the stop and makes Roon delay the start of next tune, the end of A will not be cut but the latency between tunes will still be there.
- If HQPlayer implements caching of the data that is in the pipeline, so that it starts caching tune B from 2:50 while playing the last 10 seconds of A, and then at 3:10 (when the user will hear end of A) it does the reset, there will be no latency anywhere nor will the end of A be cut.
3 is obviously the best, followed by 2 and worst is 1.
You can update that at any time anyway if user changed his mind.
No, it is specifically not atomic because you have multiple commands. All commands sent to HQPlayer are executed atomically, but not any series of commands. So you would be much better off using something like PlayNextURI.
Someone could perform any number of operations between the commands you are sending.
HQPlayer is done playing your content, but donât make any assumptions about audio output which should not be of your interest. It is HQPlayerâs business what happens once samples are read from the stream.
This is the problem, going to next stream does not require reset procedure. That reset procedure shouldnât happen, instead you should proceed with the next stream and skip the reset procedure!
This is more like a DAC or any other black-hole sink. When you are using for example I2S and similar outputs, you have stream always going and clocks active, whether there is audio or not. In normal transitions you shouldnât be stopping clocks and powering down hardware into standby mode.
Consider âStopâ command as equivalent of standby-button in hardware devices.