Skip to content

Conversation

@ghost
Copy link

@ghost ghost commented Mar 30, 2021

This is a very simple addition to librespot which allows users to correct channel balance without any quality loss directly through alsa mixer, as long as their DAC allows such feature.

I am running a FiiO E10K DAC, which outputs its analogue signal into a Xduoo MT-602 tube amp. The amplifier, being a quite cheap unit, has a very slight channel imbalance, which bugs me a lot.

This is a very neat way of correcting such channel imbalance.

@ghost
Copy link
Author

ghost commented Mar 30, 2021

I see the check failed. I do not know how to correct the problem. The code works for me correctly on a latest raspbian on a raspberry pi 4.

@ghost
Copy link
Author

ghost commented Mar 30, 2021

I should probably explain the idea behind using this feature:

Let's say an amplifier outputs left channel a little bit louder, i.e. the sound is shifted to the left. Then I start librespot with:

./librespot --mixer alsa --volume-ctrl linear --decrease-left-channel 100

Then when I change the volume in my spotify client, the final volume in alsamixer is 1 dB less on the left channel than on the right channel. Ultimately, if I set the volume to max (100%), the left channel plays at -1 dB and the right channel plays at 0 dB, solving the amplifier potentiometer problem.

@Johannesd3
Copy link
Contributor

I do not know how to correct the problem.

Just run cargo fmt.

Btw is it possible that your commit mail address does not match your github mail address?

@ghost
Copy link
Author

ghost commented Mar 30, 2021

Just run cargo fmt.

When I run it on my raspberry pi, the command succeeds with no errors.

Btw is it possible that your commit mail address does not match your github mail address?

Maybe, I am sorry for that.

@Johannesd3
Copy link
Contributor

When I run it on my raspberry pi, the command succeeds with no errors.

And hopefully, some files have changed now. If so, just commit them. Otherwise I don't know what happens here.

@ghost
Copy link
Author

ghost commented Mar 30, 2021

Every change I made is in the commit. It compiles and works for me with no problem whatsoever.

pi@raspberrypi:~/librespot $ git status
On branch dev
Your branch is up to date with 'origin/dev'.
pi@raspberrypi:~/librespot $ cargo fmt -v && echo 'last command succeeded'
[lib (2018)] "/home/pi/librespot/audio/src/lib.rs"
[lib (2018)] "/home/pi/librespot/connect/src/lib.rs"
[custom-build (2018)] "/home/pi/librespot/core/build.rs"
[lib (2018)] "/home/pi/librespot/core/src/lib.rs"
[example (2018)] "/home/pi/librespot/examples/get_token.rs"
[example (2018)] "/home/pi/librespot/examples/play.rs"
[example (2018)] "/home/pi/librespot/examples/playlist_tracks.rs"
[lib (2018)] "/home/pi/librespot/metadata/src/lib.rs"
[lib (2018)] "/home/pi/librespot/playback/src/lib.rs"
[custom-build (2018)] "/home/pi/librespot/protocol/build.rs"
[lib (2018)] "/home/pi/librespot/protocol/src/lib.rs"
[lib (2018)] "/home/pi/librespot/src/lib.rs"
[bin (2018)] "/home/pi/librespot/src/main.rs"
rustfmt --edition 2018 /home/pi/librespot/audio/src/lib.rs /home/pi/librespot/connect/src/lib.rs /home/pi/librespot/core/build.rs /home/pi/librespot/core/src/lib.rs /home/pi/librespot/examples/get_token.rs /home/pi/librespot/examples/play.rs /home/pi/librespot/examples/playlist_tracks.rs /home/pi/librespot/metadata/src/lib.rs /home/pi/librespot/playback/src/lib.rs /home/pi/librespot/protocol/build.rs /home/pi/librespot/protocol/src/lib.rs /home/pi/librespot/src/lib.rs /home/pi/librespot/src/main.rs
last command succeeded

Is there anything else I can try to solve the problem?

@Johannesd3
Copy link
Contributor

Johannesd3 commented Mar 30, 2021

I don't doubt it compiles, it's just the formatting that does not match the rules. Usually, cargo fmt would have fixed this issue. If cargo fmt did not change anything, check that you use the correct version. Maybe try cargo fmt --all. Otherwise, I have no idea what might be wrong.

@ghost
Copy link
Author

ghost commented Mar 30, 2021

You were right! Thank you! I didn't know it worked like that. This is my first time with rust. Hopefully it works now :)

@roderickvd
Copy link
Member

With thanks for your contribution, I am doubting several points:

  1. Why not do channel balancing downstream your playback chain, e.g. using the audiopanorama element in the GStreamer backend, or in the PulseAudio settings. In So many features flags... #648 we are discussing how we should cut down to just outputting audio samples, and this moves in the opposite direction.

Now in the scenario that this should be in core:

  1. I know Alsa itself uses an interface like amixer sset Master 80%,20% but personally I'd prefer something like --balance x where x is a float value between -1.0 and 1.0 to pan between left and right.

  2. To keep feature parity, this should really be implemented for softvol too. Samples are interleaved, so you could do volume control on every other sample. The fact that the cubic mapping currently only works for Alsa to me is something that needs to be fixed, not work as a precedent for further exceptions.

@ghost
Copy link
Author

ghost commented Mar 31, 2021

  1. Why not do channel balancing downstream your playback chain, e.g. using the audiopanorama element in the GStreamer backend, or in the PulseAudio settings. In So many features flags... #648 we are discussing how we should cut down to just outputting audio samples, and this moves in the opposite direction.

I thought outputting directly to ALSA's hw:0 was as bit-perfect as was possible to achieve. Is using GStreamer (combined with audiopanorama) a better path?

  1. I know Alsa itself uses an interface like amixer sset Master 80%,20% but personally I'd prefer something like --balance x where x is a float value between -1.0 and 1.0 to pan between left and right.
  2. To keep feature parity, this should really be implemented for softvol too. Samples are interleaved, so you could do volume control on every other sample. The fact that the cubic mapping currently only works for Alsa to me is something that needs to be fixed, not work as a precedent for further exceptions.

Both are definitely valid points, unfortunately outside of my scope of skills I'm afraid.

@roderickvd
Copy link
Member

roderickvd commented Mar 31, 2021

I thought outputting directly to ALSA's hw:0 was as bit-perfect as was possible to achieve. Is using GStreamer (combined with audiopanorama) a better path?

Today you are right that doing digital volume control in Alsa gives the highest transparency. After #660 this will not matter anymore if you set output to 32 or 24 bits.

Other "intermediate" sinks like GStreamer and PulseAudio add some "weight" and probably a tiny bit of latency due to buffering. You probably won't notice on even the lowest denominator of hardware.

Both are definitely valid points, unfortunately outside of my scope of skills I'm afraid.

I'll look into cubic and, while at it, logarithmic mappings in softvol after #660 is merged.
Still on the fence if level balancing should be in librespot.

Edit: pointy hat to me, I had not noticed log volume control to be in spirc.

@ghost
Copy link
Author

ghost commented Mar 31, 2021

I'll look into cubic and, while at it, logarithmic mappings in softvol after #660 is merged.

Thank you.

Still on the fence if level balancing should be in librespot.

Maybe if someone would be so nice and write up an example of how to level balance using ALSA, or some other backend, ideally with highest transparency possible. I couldn't find any other solution. Luckily this little idea I came up with works, but it wouldn't work with a DAC that does not have volume control (for example, my Aune X8 DAC has no volume control). I have no idea how I would level balance with that DAC.

@JasonLG1979
Copy link
Contributor

JasonLG1979 commented Apr 11, 2021

I have no idea how I would level balance with that DAC.

It's not pretty but you could split the left and right channels and then route them though 2 separate sofvol ALSA plugins naming them "Left" and "Right" (or really whatever you want) and them merge them again into another softvol named "PCM" (important most apps expect the main volume to be named "PCM or "Master") if you want balance and volume control for a DAC with no such hardware controls. And as @roderickvd said with 24 or 32 bit you loose nothing as far as fidelity with software volume control in fact it's objectively better than any sort of hardware volume control.

Edit: Scratch that I'm pretty sure the softvol plugin is capable of balance control without crazy routing. I have a DAC without hardware volume control. I'll be back in a bit.

@JasonLG1979
Copy link
Contributor

Here is a script I wrote a while back that allows you to add software volume to a DAC that lacks hardware volume control. As I suspected it works that same as hardware controls as far as left/right balance control so amixer sset <whatever you name the software vol control> 80%,20% works just fine. It also allows you configure the buffer size, sampling rate, bit depth/format, volume resolution and the min/max volume in dB's.

There are also command snippets to find your card, list it's available sampling rates and formats, and the available sample rate converters.

It's pretty self-explanatory just paste it into /etc/asound.conf read it and follow the instructions. You will need to play some audio before the software volume control shows up and reboot after that so they are usable.

https://gist.github.com/JasonLG1979/f29cb0f14218a3b20db5d32c2cfae5ed

If you want to rename the volume control for what ever reason and you want to delete the old control you'll need to do that manually as ALSA doesn't do it automatically.

This page has instructions on how to delete unused software controls:

https://dquinton.github.io/debian-install/helps/18-volume-boost.html

@sashahilton00
Copy link
Member

I'm not sure librespot is the place for this. Given that we're trying to go down the path of less audio processing, this feature feels unnecessary, especially given that it can, and probably should be handled downstream, mainly because the imbalance in this case is itself coming from user equipment as opposed to something that librespot does.

@roderickvd
Copy link
Member

I'm gonna go ahead and close this PR. While I certainly appreciate your work, I think it is beyond the scope of this project to do DSP to work around equipment issues downstream. @JasonLG1979 left instructions how to do this in Alsa, earlier I posted an option on GStreamer.

@roderickvd roderickvd closed this May 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants