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

Partial fix for #1600: ALSA device can be selected using a combo box #2135

Merged
merged 5 commits into from
Aug 26, 2015

Conversation

michaelgregorius
Copy link
Contributor

Implements the selection of ALSA devices using a combo box as proposed by @unfa in #1600. The ALSA configuration dialog looks as follows in this branch:

issue1600

Please note that the current implementation does not check whether the device works for the parameters that LMMS uses (SND_PCM_ACCESS_RW_INTERLEAVED, SND_PCM_FORMAT_S16_LE, etc.). I tried doing these checks while compiling the list of available devices but this led to strange effects which are likely caused by the fact that the PCM device has to be opened to query its capabilities. After testing the selection repeatedly I got error messages a la "Resource or device busy".

However, having a combo box to select devices from should be a good step forward compared to a simple line edit. 😃

To be able to react to combo box selection changes several changes were necessary. AudioDevice::setupWidget was moved into its own class AudioDeviceSetupWidget which logically should belong to the GUI (unfortunately the include structure does not make this obvious). This was needed so that inheriting classes can use the Q_OBJECT macro, e.g. to connect signals and slots.

For the ALSA driver there is an implementation AudioAlsaSetupWidget which provides a combo box for selection of the device. The available devices are pulled from a static method in the AudioAlsa class.

All other driver widgets have been changed to inherit from AudioDeviceSetupWidget but have not been changed otherwise.

SetupDialog has been adjusted to keep a map of AudioDeviceSetupWidgets now.

Shows a combo box with the available ALSA cards and devices instead of a
line edit. The problem currently is that the widgets are nested classes
of AudioDevice and therefore the macro Q_OBJECT does not work which
means that its not possible to define slots that react to retrieved
signals.
Moved AudioDevice::setupWidget into its own class AudioDeviceSetupWidget
which logically should belong to the GUI (unfortunately the include
structure does not make this obvious).

For the ALSA driver there is an implementation AudioAlsaSetupWidget
which provides a combo box for selection of the card and device.

All other driver widgets have been changed to inherit from
AudioAlsaSetupWidget but have not been changed otherwise.

SetupDialog has been adjusted to keep a map of AudioAlsaSetupWidgets
now.
This version lets the user select the ALSA device to use with a combo
box. It does not check whether the device works for the parameters that
LMMS uses (SND_PCM_ACCESS_RW_INTERLEAVED, SND_PCM_FORMAT_S16_LE, etc.).
Doing these checks while compiling the list of available devices led to
strange effects which are likely caused by the fact that the PCM device
has to be opened to query its capabilities. This in turn led to error
messages a la "Resource or device busy" when testing the new
functionality repeatedly.

However, having a combo box to select devices from should be a good step
forward compared to a simple line edit. :)
void onCurrentIndexChanged(int index);

private:
QComboBox * m_deviceComboBox;
Copy link
Member

Choose a reason for hiding this comment

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

m_deviceComboBox is only accessed within the context in which it was created (AudioAlsaSetupWidget's constructor), so could be made a local variable within the constructor instead of a member variable.

I don't know if this was intentional or not, as some people consider it a stylistic choice. Personally, I find extraneous member variables to be a bad thing. The more confined the scope of a variable, the fewer places you have to search through when debugging.

@Wallacoloo
Copy link
Member

Hey @michaelgregorius, this is a fantastic idea and it fits right into the gui. I checked out your branch, and as you can probably also see with the Travis build logs, it fails:

[  2%] Building CXX object src/CMakeFiles/lmmsobjs.dir/core/Mixer.cpp.o
In file included from /home/colin/proj/lmms/src/core/Mixer.cpp:43:0:
/home/colin/proj/lmms/include/AudioPortAudio.h:85:2: error: expected class-name before ‘{’ token
  {
  ^

The error appears to be this code:

class AudioPortAudio : public AudioDevice
{
    ...
    class setupWidget : public AudioDevice::setupWidget
    {
    ...
}

Namely, you forgot to fix the parent class from AudioDevice::setupWidget to your new AudioDeviceSetupWidget.

Besides that though, I reviewed the code fairly thoroughly, primarily looking for possible memory errors, and it looked OK. It may be worth separating the implementation from the header in your new include/AudioDeviceSetupWidget.h file. That tends to help with organization (it's more clear that the class belongs to the gui since its implementation file resides in the gui directory) and it makes the file a bit easier to deal with down the road - small changes don't require long recompilations.

I also left a few inline comments for you. Great work though! Looking forward to integrating this into master, although I suspect it will have to wait until after 1.2 due to feature freeze :/

Removal of a superfluous include in AudioAlsaSetupWidget.cpp

Removal of the function "bool hasCapabilities(char *device_name)" which
was not used anyway. It implemented a test for ALSA device capabilities
needed by LMMS (SND_PCM_ACCESS_RW_INTERLEAVED, SND_PCM_FORMAT_S16_LE,
etc.).

Corrected header name in AudioAlsaSetupWidget.h.

Created an implementation file for AudioDeviceSetupWidget to make more
clear that it's part of the GUI.

Fix build for builds that use Port Audio. The setup widget of
AudioPortAudio.h still inherited from AudioDevice::setupWidget instead
of the new AudioDeviceSetupWidget.
@michaelgregorius
Copy link
Contributor Author

Hi @Wallacoloo! Thanks for the really thorough code review and the nice words! I have pushed a new commit that fixes everything except the member m_deviceComboBox. I think having the member gives a better overview of what the dialog is composed of.

Hope that the Travis build goes through now as I had to fix it blindly because I do not build with PortAudio.

@Wallacoloo
Copy link
Member

@michaelgregorius Great! I gave it a test, and it seems to work well enough for me ("default", "pulse" and "sysdefault" all produce the same output. "null" produces silence, as expected. The other ones shown produce silence and also cause for LMMS to show the audio device settings dialog upon restart, which I believe indicates a failure to open the device).

I believe this is the expected behavior, until it gets refined by fixing up the hasCapabilities function to where incompatible devices are hidden.

I also tested setting the device to "bogus" in master and then opened your alsa-combobox branch, and it seemed to handle things pretty well by automatically setting the ALSA device to "default". If I gave it something valid like "pulse", on the other hand, it preserved the device setting. So users shouldn't experience any upgrade problems 👍

@Wallacoloo
Copy link
Member

By the way, the LMMS PortAudio backend appears to be capable of narrowing down the compatible ALSA devices before selection. Their code may be of reference for getting hasCapabilities working right.


free(name);
free(description);

Copy link
Contributor

Choose a reason for hiding this comment

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

Move those two 'free' calls inside above 'if' statement. If 'name' or 'description' are NULL, we have a crash.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍 Nice catch!
However I would have tested against NULL or nullptr instead of 0 but this is pure personal taste :)

Copy link
Member

Choose a reason for hiding this comment

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

I remember seeing this earlier & looking into it. Turns out it's perfectly valid to free a null pointer in C/C++: http://www.cplusplus.com/reference/cstdlib/free/

Additionally, moving BOTH calls to free into the if statement would cause a memory leak in the case that only one of them is null. So I vote to leave as-is.

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, that was the intention. That's also the reason why sometimes you can see code like this:

delete m_pointer;
m_pointer = 0;

It's ok to call delete on a null pointer but not ok to delete the same pointer (address) twice. By setting it to 0 after the deletion you make sure that you will only run into the valid case in case the code is called again without allocating something new for m_pointer.

@Wallacoloo
Copy link
Member

Somehow I forgot about this PR, with everything else going on. Sorry for that. I retested this after locally rebasing it against master, and it's still working great. So, merge time!

Wallacoloo added a commit that referenced this pull request Aug 26, 2015
Partial fix for #1600: ALSA device can be selected using a combo box
@Wallacoloo Wallacoloo merged commit c99c6ee into LMMS:master Aug 26, 2015
@tresf
Copy link
Member

tresf commented Aug 26, 2015

👍 Although next time we should consider a rebase first. :)

@michaelgregorius michaelgregorius deleted the alsa-combobox branch August 26, 2015 17:04
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.

5 participants