Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve detection of sound hardware from configuration #2253

Merged
merged 12 commits into from
Oct 1, 2019

Conversation

Be-ing
Copy link
Contributor

@Be-ing Be-ing commented Sep 1, 2019

PortAudio provides no guarantees that the PaDeviceIndex for any device will be stable across restarts of Mixxx. Abusing the PaDeviceIndex as a persistent identifier caused Mixxx to falsely claim that audio interfaces were not available and annoyingly require the user to reconfigure all their sound I/O even when nothing about the available sound hardware actually changed. It is the responsibility of the sound API to provide persistent names for devices to PortAudio.

I have tested this with JACK and ALSA and it works as I expect.

PortAudio provides no guarantees that the PaDeviceIndex for any
device will be stable across restarts of Mixxx. Abusing the
PaDeviceIndex as a persistent identifier caused Mixxx to falsely
claim that audio interfaces were not available and annoyingly
require the user to reconfigure all their sound I/O even when
nothing about the available sound hardware actually changed. It
is the responsibility of the sound API to provide persistent
names for devices to PortAudio.
@Be-ing Be-ing added this to the 2.2.3 milestone Sep 1, 2019
Copy link
Member

@daschuer daschuer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for fixing this annoying bug.

Unfortunately this is not a one liner, because it breaks backwards compatibility for every user.
We must also make sure, that in case of two devices with the same name, Mixxx does not open the same device twice.

Portaudio is missing a Unique ID for the devices but I guess that in case of two devices with the same name, the index will be shifted but the order will not be swapped. Here an old discussion:
https://app.assembla.com/spaces/portaudio/tickets/11-add-callback-api-for-detecting-connection-removal-of-devices/details#

Currently the user can only distinguish it by the place in the combobox. So I think we finally still need to use index during one PA device enumeration. However I am fine to just ommit the index when loading the config and ignore this rare case for now as long maintain backward compatibility.

Relevant Code is here:

int index = deviceComboBox->findData(deviceName);

void DlgPrefSoundItem::refreshDevices(const QList<SoundDevicePointer>& devices) {

int newDevice = deviceComboBox->findData(m_savedDevice);

m_config.getInputs().values(pDevice->getInternalName())) {

m_config.getOutputs().values(pDevice->getInternalName());

@@ -107,8 +107,7 @@ SoundDevicePortAudio::SoundDevicePortAudio(UserSettingsPointer config,
// Setting parent class members:
m_hostAPI = Pa_GetHostApiInfo(deviceInfo->hostApi)->name;
m_dSampleRate = deviceInfo->defaultSampleRate;
m_strInternalName = QString("%1, %2").arg(QString::number(m_devId),
deviceInfo->name);
m_strInternalName = deviceInfo->name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this will one time break the sound hardware hardware config of every user.
I am afraid we need to deal with the index, in case we have two sound cards with the same name.

Please also add a note to the code, that we cannot use QString::fromLocal8Bit(deviceInfo->name); here to not break the backwards compatibility.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also rename m_devId to m_deviceIndex, to clarify that it is not an Identification Number.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even now I often have to reconfigure my settings depending on which devices are found. I don't think that this fix causes any severe usability issues other than that we already have.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a Linux only issue, where devices in use are not enumerated.
After merging this PR, all users have to reconfigure their Hardware preferences.
This is a bad idea since the minority of our users are on Windows.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. You are wrong.
This has been happening on Windows too.
That index changes, sometimes every reboot.
I assume that with "backwards compatibility" you mean when loading on an older Mixxx the configuration created with this PR.
That might be worth considering for development and people trying before upgrading. In fact, keeping compatibility could beneficiate the newer version more than the older one, in this case. So yes, trying to ignore the index on load rather than not saving it could be a better solution, and documenting why.
Finally, there is still that problem that you mention wih the name and "hw:xx". What do you propose?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean: on Linux the issue is worse, because it also happens without reboot, if a device is in use by an other application (pulse).

Finally, there is still that problem that you mention wih the name and "hw:xx". What do you propose?

We need a special case that excludes the hw:x,x and the index if not found. How about looking for the full string fist and if not found use the inner part only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixxx users are accustomed to having to reconfigure their sound hardware frequently, so I am not concerned about making them do it once more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am very certain nobody minds to reconfigure their audio config. That's probably the task which all users do regularly (switching equipment) and it's easy.

@daschuer
Copy link
Member

daschuer commented Sep 1, 2019

It is the responsibility of the sound API to provide persistent names for devices to PortAudio.

That is correct, but unfortunately it is not guaranteed across re-enumerations and after the the hardware config has changed (Hot Plug and Play)

@daschuer
Copy link
Member

daschuer commented Sep 1, 2019

grafik
grafik

@daschuer
Copy link
Member

daschuer commented Sep 1, 2019

You see, that by unplugging the USBMIC, the name of SAA7134 has changed.
A fix for Mixxx will involve omitting PaDeviceIndex and the alsa index (hw:x,x)

@daschuer
Copy link
Member

daschuer commented Sep 1, 2019

It is correct, that the Device name is constant per enumeration as well as the index.

The index does not add any value with ALSA where the the more reliable hw:x,y index is already part of the name. It not unique across reboots though.

On Windows there is no such unique index. Without the own artificial index Mixxx cannot identify two similar devices even during a single enumeration run because Mixxx uses a QMap that looses the original order.

So I propose a solution that keeps the index.

@JosepMaJAZ
Copy link
Contributor

Do you mean that the problem is worse on Windows because of a QMap?

@daschuer
Copy link
Member

daschuer commented Sep 1, 2019

Yes, but only in the rare case of using two devices of the same model. In this case and without PaDeviceIndex in the name, the QMap will only return one of the two devices.

@Be-ing
Copy link
Contributor Author

Be-ing commented Sep 1, 2019

A fix for Mixxx will involve omitting PaDeviceIndex and the alsa index (hw:x,x)

Then we would have nothing to distinguish two identical audio interfaces. With this branch in its current state, Mixxx can reliably distinguish between my two Xone K2 audio interfaces as long as their ALSA indices remain consistent. Unfortunately I don't know of any way to tell ALSA to keep consistent indices for two identical devices. It might be possible with a udev rule distinguishing the devices by serial number.

So I propose a solution that keeps the index.

So do you propose storing both the PortAudio index and ALSA hw index and falling back to just the name if those indices do not match?

@daschuer
Copy link
Member

daschuer commented Sep 1, 2019

Yes.

In the ALSA case we may completely ignore the PaDeviceIndex, since it has its own index and we need to ignore the ALSA index if no matching device is found.

In the windows case we need to use the PaDeviceIndex. For the other APIs we have to decide in the same way. Not a thing we expect from an API wrapper like Portaudio, but it is a known bug without progress unfortunately. (linked above)

This allows for more reliable identification of hardware devices from
the configuration file so users do not need to reconfigure their sound
hardware as often as before.
@Be-ing
Copy link
Contributor Author

Be-ing commented Sep 30, 2019

In the ALSA case we may completely ignore the PaDeviceIndex, since it has its own index and we need to ignore the ALSA index if no matching device is found.
In the windows case we need to use the PaDeviceIndex.

Done. That turned out to be tricky, but it was a good learning experience for using custom types with QVariant.

@Be-ing
Copy link
Contributor Author

Be-ing commented Sep 30, 2019

Testing on Windows and macOS would be appreciated.

@Be-ing
Copy link
Contributor Author

Be-ing commented Sep 30, 2019

I have tested the following and they all work:

  • Configuring one ALSA device, quitting Mixxx, replugging USB audio interfaces in a different order so the ALSA device numbers change (and restarting Linux), restart Mixxx. Mixxx is able to identify the configured device regardless of the ALSA device number.
  • Configuring non-hw devices with ALSA ("default" and "pulse") and restarting Mixxx.
  • Configuring two ALSA devices with identical names and restarting Mixxx. Mixxx is able to identify the configured devices so long as the ALSA device numbers remain the same.
  • Configuring Mixxx to use JACK and restarting Linux so the PortAudio device indices for JACK devices change (verified by looking at ~/.mixxx/soundconfig.xml).
  • Configuring the same input device and same input channels for multiple decks' vinyl control inputs.

@Be-ing Be-ing changed the title don't identify PortAudio devices by PaDeviceIndex in configuration improve detection of sound hardware from configuration Sep 30, 2019
Copy link
Member

@daschuer daschuer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for fixing this finally.
Unfortunately, you have introduced a new storage file format. This is a unessary regression. Because the original format already included all info. You just need to reuse the name parsing function.

Does the alsaHwDevice contain the whole Alsa name or only the suffix? I think we may use the whole name in addition to name. This way it we don't need to special case "default" and it is immune against future issues with the used regex.

public:
QString name;
QString alsaHwDevice;
int portAudioIndex;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please give an example as comment what is stored where.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the name implies, alsaHwDevice only stores the hw:X,Y from the name PortAudio gives Mixxx. For non-hardware ALSA devices, alsaHwDevice remains empty.

src/soundio/soundmanagerutil.h Outdated Show resolved Hide resolved
src/soundio/soundmanagerutil.h Show resolved Hide resolved
@Be-ing
Copy link
Contributor Author

Be-ing commented Sep 30, 2019

Unfortunately, you have introduced a new storage file format. This is a unessary regression. Because the original format already included all info. You just need to reuse the name parsing function.

Ehh, using a regular expression to parse a string from an XML file feels hacky to me. IMO it defeats the point of using XML. I'd rather not do this to avoid a minor one time disruption when upgrading to 2.2.3. As discussed before, users are already accustomed to having to reconfigure their sound hardware often. Making them do it only one more time is no big deal.

@@ -65,6 +66,8 @@ void MixxxApplication::registerMetaTypes() {
qRegisterMetaType<mixxx::ReplayGain>("mixxx::ReplayGain");
qRegisterMetaType<mixxx::Bpm>("mixxx::Bpm");
qRegisterMetaType<mixxx::Duration>("mixxx::Duration");
qRegisterMetaType<SoundDeviceId>("SoundDeviceId");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string argument is not needed if symbols are part of the global namespace

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I thought it was good to include it for consistency with the surrounding code.

src/preferences/dialog/dlgprefsounditem.cpp Outdated Show resolved Hide resolved
src/soundio/sounddeviceportaudio.cpp Outdated Show resolved Hide resolved
src/soundio/soundmanagerconfig.cpp Outdated Show resolved Hide resolved
src/soundio/soundmanagerconfig.h Outdated Show resolved Hide resolved
QString alsaHwDevice;
int portAudioIndex;

QString debugName() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or overload QDebug operator<<(QDebug, const DeviceId&)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, the overload already exists. Those multiple representations are slightly confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried defining only the operator<<, but there are use cases for this debugName in the name for Tracers in SoundDevicePortAudio. Do you have suggestions for another way to handle this?

src/soundio/soundmanagerutil.h Outdated Show resolved Hide resolved
src/soundio/soundmanagerutil.h Outdated Show resolved Hide resolved
@uklotzde
Copy link
Contributor

No concerns from my side. Reselecting the sound device after an upgrade is acceptable if it avoids complicating the code.

@daschuer
Copy link
Member

daschuer commented Sep 30, 2019

It does not really complicate the code, we have the name parser already.

Please remember that only a minority of user suffers this bug. With an incompatible change, all users have to reconfigure the hardware. Reconfigure the hardware preferences is not a no brainer. This is a no-go in a point release for me.

A migration would work for me, but that is IMHO not worth the work just to avoid a reg expression we use anyway in the code.

@Be-ing
Copy link
Contributor Author

Be-ing commented Sep 30, 2019

Please remember that only a minority of user suffers this bug.

AFAIK this affects every user. @JosepMaJAZ confirmed above that it affects Windows.

Reconfigure the hardware preferences is not a no brainer.

Users have been dealing with it frequently for many years.

It is not so much about complicating the code as adding code for a one time minor issue that will likely stay there for many years for no good reason, then one day someone will look at the code again and not be sure why it is still there.

@daschuer
Copy link
Member

daschuer commented Oct 1, 2019

It is effects only a few lines of code.
Changing the file format is not necessary here and we are in a point release.
We can change the file format at any time, there is no need to do it here in a rush.

If you like to change the file format immigrate from the old.

@Be-ing
Copy link
Contributor Author

Be-ing commented Oct 1, 2019

We cannot simply reuse the regex in SoundDevicePortAudio for parsing the string from the old file format. The old file format hacked the PortAudio index together with the device name and ALSA device into a single string. If I implement code to parse that, I may as well revert all the work I did to implement the new SoundDeviceId class and go back to hacking multiple pieces of information into a single QString. There is no need to be pedantic about migrating from an old format when the real world impact is trivial.

@Be-ing Be-ing force-pushed the audio_interface_bug branch 2 times, most recently from 9498938 to 047a088 Compare October 1, 2019 15:11
@daschuer
Copy link
Member

daschuer commented Oct 1, 2019

I don't mind to revert to the original string based solution. Anything that not required to reconfigure the sound hardware will work for me.

@Be-ing
Copy link
Contributor Author

Be-ing commented Oct 1, 2019

I did not mean to propose that as an actual solution. Defining and parsing our own structured string format would be an ugly hack. That is what XML is for.

@daschuer
Copy link
Member

daschuer commented Oct 1, 2019

So we need to migrate from the old format.
Also ok for me.

@ywwg
Copy link
Member

ywwg commented Oct 1, 2019

This is probably out of scope for this PR but I wanted to make sure if we are updating sound card setup that we think about supporting users who may connect different sound cards at different times -- if mixxx can try to choose the user's preferred sound card depending on what's available, that would be really nice! https://bugs.launchpad.net/mixxx/+bug/1516035

@Be-ing
Copy link
Contributor Author

Be-ing commented Oct 1, 2019

@ywwg yes that would be nice, but beyond the scope of this PR.

... which users have been doing for years anyway. Revert this
commit after releasing Mixxx 2.2.3.
@daschuer
Copy link
Member

daschuer commented Oct 1, 2019

Thank you for the migration code. LGTM.

@daschuer
Copy link
Member

daschuer commented Oct 1, 2019

and for picking this annoying and trick bug up ..

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.

6 participants