-
Notifications
You must be signed in to change notification settings - Fork 71
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
Multi channel device playback causes Pulseaudio assertion #115
Comments
You need to provide a number of channels to |
Hey bastibe, thanks for your answer.
This is exactly what I intend to to achieve. I want to use all 12 channels of the sound device. I experimented a little and found out, that everything works fine up to 6 channels. Using 7 channels and above triggers the map assertion in Pulseaudio:
The output is:
|
I'm sorry, I accidentally read This does indeed look like some bad interaction with pulse. But audio hardware is always fickle, and there always are edge cases that SoundCard might not know about yet. Does your code work with a custom channel map? I.e. playing only to the last six channels or something like that? |
I guess by channel map you refer to the pulseaudio configuration? Can you hint me to the right direction, I have a hard time making sense of the pulseaudio documentation. |
No, sorry, I meant SoundCard's channel map. You can use |
I did some more testing using the binaural virtual surround sink as output device (see https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Modules/#module-virtual-surround-sink). The virtual sink is configured to work as 7.1-channel surround sink (8 channels). The best result, I could get was sending 5 channels of the test file (L, R, C, LB, RB) to the first 5 channels of the virtual sink device:
This way, the channel mapping is correct, however for some weird reason the word "front" before "right" present in signal channel1 is not audible in the output, this is reproducible. When trying to send more than 6 audio channels to the sink, pulseaudio throws the aforementioned assertion error. If audio is send to output channel 6 or 7 using a channel map, the channel mapping goes off. In the following example, output channel 6 is audible on the center speaker and channel 7 from the front left speaker:
|
This certainly sounds like a bug to me. Regrettably, I currently only have two-channel devices available to me, so I can't test this very well. Do you have any intuition whether this is a pulse issue or a soundcard issue? |
I wrote a short how-to for the setup of the virtual surround sink, which works as a virtual 8-channel-device for binaural headphone rendering (https://github.com/szlop/Howto-Pulseaudio-module-virtual-surround-sink-for-Binaural-Surround-Downmix). This is, what I use for testing, when I do not have access to the actual surround setup. I extracted a set of HRIRs from the Hesuvi project, which I can send you, if you are interested.
Sorry, I'm not very familiar with Pulseaudio, I guess you are the expert. |
I wish... Anyway, my second child was just delivered in the last few days, so I probably won't be able to investigate for a while. But please feel free to ping me in a few weeks, or with further information. |
Congratulations! :) For now, I'm happy with your library, as 5.1 channel output works as intended. Also it seems like pulseaudio 14 is going to be released in the forseeable future. I' will check back then. |
Soundcard crashes with more than 6 channels, because it asks Pulseaudio for a default channel map using pa_channel_map_init_auto(). Im Pulseaudio default channels maps are not defined for more than 6 channels, so the function returns an empty map which leads to the crash. I replaced the channel map from pa_channel_map_init_auto by a hard-coded channel map (see commit f509799). So far I didn't have problems with stereo, 5.1 and 7.1. I guess the clean solution would be give the user the possibility to define a channel map. |
Thank you for your analysis. Is there a generic way of generating an arbitrary-number-of-channels channel map that we could use instead of I would be grateful for a pull request that fixes this issue. |
There is a function called pa_channel_map_init_extend() which is supposed to fill the remaining channels (after 5.1) with AUX channels. I tried this function first. I got a valid channel map, but the mapping was completely off. Also I didn't really get the meaning of: SoundCard/soundcard/pulseaudio.py Lines 651 to 653 in 97a9f2b
As I see it, map[idx] holds an index which refers to the enum type pa_channel_position, which does not have a meaningful order. I guess a user defined channel map should make use of the function pa_channel_position_from_string(). I can try to work something out, when I find the time. |
I don't know how deep your understanding of C is, so forgive me if the following is a bit too basic. There are two sort-of-conflicting interpretations of the channelmap in play here. The header file specifies channel map entries by name, i.e. Hence we ignore, for all intents and purposes, the meaning of
fills in a default channel map with values counting up from 1 in case no explicit map was given. A five-channel default map will yield Since SoundCard needs to be compatible between platforms, we should not worry about the channel names. Channel maps should always be numeric. Did this explanation help? |
Sorry, I guess, I didn't explain my point very well. Correct me, if I'm mistaken: If the user sets a map containing the channels 0:4 to address the first 5 channels, the ordering in Linux will end up like this: Again, I might be wrong, I didn't test this in detail. However this would explain, why I got weird channel wrap-arounds when testing custom channel maps. |
I got confused here, because of the double indexing in the Pulseaudio enum type. |
It sounds like there is a misunderstanding either between the two of us, or in your interpetation of pulsaudio's C code. The pulseaudio enum contains typedef enum pa_channel_position {
PA_CHANNEL_POSITION_INVALID = -1,
PA_CHANNEL_POSITION_MONO = 0,
PA_CHANNEL_POSITION_FRONT_LEFT = 1,
PA_CHANNEL_POSITION_FRONT_RIGHT = 2,
PA_CHANNEL_POSITION_FRONT_CENTER = 3,
PA_CHANNEL_POSITION_REAR_CENTER = 4,
PA_CHANNEL_POSITION_REAR_LEFT = 5,
PA_CHANNEL_POSITION_REAR_RIGHT = 6,
PA_CHANNEL_POSITION_LFE = 7,
PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER = 8,
PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER = 9,
PA_CHANNEL_POSITION_SIDE_LEFT = 10,
PA_CHANNEL_POSITION_SIDE_RIGHT = 11,
PA_CHANNEL_POSITION_AUX0 = 12,
PA_CHANNEL_POSITION_AUX1 = 13,
/* etc. */
} Thus every channel is uniquely identified by a number, and channels do not repeat. The intermittend Or am I misunderstanding something? |
Hi bastibe, sorry for ghosting you two years ago, something came in the way. I started working at my old project again and put some work in multichannel support for soundcard. I guess my last post does not explain my problem well, so here is another try. Right now, a channel map can be set by specifying the number of channels, e.g. If a number of channels is set, a channel map is created by the call of https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/blob/master/src/pulse/channelmap.c#L208 pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
pa_assert(m);
pa_assert(pa_channels_valid(channels));
pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
pa_channel_map_init(m);
m->channels = (uint8_t) channels;
switch (def) {
case PA_CHANNEL_MAP_AIFF:
/* This is somewhat compatible with RFC3551 */
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 6:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
case 5:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
case 3:
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_RIGHT;
m->map[2] = PA_CHANNEL_POSITION_CENTER;
return m;
case 4:
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_CENTER;
m->map[2] = PA_CHANNEL_POSITION_RIGHT;
m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
default:
return NULL;
} If the value of the parameter Apart from this, for This would not be so bad, if it was not for the crash, for channels higher than 6 (which also happens for custom channel maps with more than 6 channels.) The crash can easily be fixed by using My problem with the way, Soundcard's channel mapping with the Pulseaudio backend works is, that it is essentially a black box for anything above two channels. In contrast to the comment in the code, the channel index does not address a physical sound device channel, at least not with the Pulseaudio backend. As far as I know, it is not even possible to directly access a physical device channel using Pulseaudio. (It would be possible by using Pipewire's Pro Audio profile.) In order to create a proper channel map for my use case, I have to look up the channel positions for my specific sound device and profile. This can be done by calling The next step is to set up a channel map for Soundcard, which refers to the listed channel positions. For this, it is necessary to look at the Pulseaudio channel position type definition:
A channel map containing the channel positions for my surround profile (
To set this channel map to Soundcard, the indices need to be decremented by 1 because of the increment here. Thus, a proper channel map for my 7.1 device would be In case, the channel positions used in the Soundcard channel map do not match the channel positions of the properties of the Pulseaudio sink device, Pulseaudio tries to map the Soundcard positions to the channel positions of the existing sink device, which will result in channel remixing. If you don't know, what is going on, this behavior can be hard to debug. I encountered this problem several times and it took me some time before realizing, that the messed up rendering was actually what was to be expected and not a bug in my code. The problem as I see it, is that Pulseaudio is middle ware and not really compatible with the other audio backends, in the way that it abstracts from audio hardware. Channel maps cannot easily be ported from the Windows to the Pulseaudio backend. My proposal to make the Pulseaudio backend more suitable for multichannel applications can be found here: The main changes are:
I messed up and mixed my branch with some code changes, which address problems which come up once in a while:
Sorry for this endless post, I hope I made my point clear this time. :) I'd be happy to discuss my code changes and help to advance Soundcard's multichannel capabilities. I'm happy with the module and found it easy to get familiar with the code. 👍 |
This is awesome! Thank you so much! This indeed explains some weird behavior I've seen, and solves it beautifully! Please open the pull request you already drafted, and we'll hash out the details. |
Thanks for the positive feedback! I took the time to untangle my commits and created a pull request for the channel map stuff: #169 |
The following code generates an assertion in pulseaudio, when a multi-channel sound device is selected:
The output is:
The same code works however fine, if a device with 2 output channels is selected. I'm on Arch Linux using pulseaudio 13.99.2+13+g7f4d7fcf5-1. Is this a bug in pulseaudio?
The text was updated successfully, but these errors were encountered: