medusalix / xow

Linux driver for the Xbox One wireless dongle
https://medusalix.github.io/xow
GNU General Public License v2.0
1.42k stars 90 forks source link

Audio #4

Open Morganamilo opened 4 years ago

Morganamilo commented 4 years ago

Does this driver support audio? Or are there plans to?

medusalix commented 4 years ago

I've looked into audio while developing the macOS version of this driver. It doesn't seem too complicated, but I'm not sure how to implement Linux audio devices in user mode.

LiamFredericks000 commented 4 years ago

I can connect my arctis 9x pro to my linux machine via bluetooth, but I'm stuck using the ad2p bluetooth interface, so I can't use the headset's mic. I used XOW to allow the xbox usb adapter to connect to my headset, but my I couldn't find the headset in linux. Is there anything specific you need help with in order to connect a headset using the xbox wireless adapter?

medusalix commented 4 years ago

The first step would be to create input and output audio devices for the headset but I'm not even sure if that's possible from user mode on Linux. If this can only be done from kernel mode then it's impossible for me to implement this in xow.

LiamFredericks000 commented 4 years ago

hhhmmm... When I connect my headset I didn't see the device anywhere. So even if I were able to create the output and input devices, I couldn't link them to anything.

medusalix commented 4 years ago

Yeah, this is way too much work for me right now, considering I don't even have a device/headset to test this with. I may implement this for a 1.0 release of xow.

YangtseSu commented 4 years ago

I think you need a headset with 3.5mm to connect to the gamepad. Not a headset connect to the Linux OS directly.

YangtseSu commented 4 years ago

Use xbox device as Pulseaudio audio sink and source . https://www.freedesktop.org/wiki/Software/PulseAudio/

LiamFredericks000 commented 4 years ago

That makes sense. I don't actually own a xbox controller so this doesn't work. I used to have a arctis 9x, but i've returned it since I wasn't able to use it wirelessly in linux (except for bluetooth).

medusalix commented 4 years ago

I did some packet capturing and reverse engineered the important parts of the protocol:

Those things would have to be implemented in xow (using PulseAudio). The timing of the individual packets (8 ms) is VERY important (that might be the biggest problem).

LorenzoMinutolo commented 4 years ago

Hi All, Thanks for this beautiful library, keep it up! I'm testing now on Ubuntu 18, wireless dongle slim and Turtle Beach 600. The pairing goes well, the device does not show anywhere (expected). Running ./xow I get this: screen_xow

medusalix commented 4 years ago

@LorenzoMinutolo I'm currently focusing on implementing the audio for the controllers first, I might consider supporting third-party headsets for a future release.

medusalix commented 4 years ago

I've implemented experimental headphone support, available in the audio branch (only audio output right now). I'm still working on it, so please don't be surprised if you hear any crackling noises. I only tested it on my 1708 controller.

Important notes: You will have to install the PulseAudio libraries to compile xow. The PulseAudio daemon is usually running as a user-service and refuses any connections from root accounts. That's why I've decided to convert xow's system-service to a user-service. You will have to uninstall the system-service (sudo systemctl) before installing the user-service (systemctl --user) as described in the updated README. Running xow manually is not recommended and will increase the discernable audio noise.

andreashuetter commented 4 years ago

I just tried the audio branch.

First I could not compile it, but after performing sudo apt-get install libpulse-dev I could.

I also did sudo systemctl stop xow.service sudo systemctl disable xow.service sudo make uninstall of the other version before.

But I can not enable or start the audio version:

desktop@sn970:~/xow$ sudo systemctl --user enable xow.service
Failed to get D-Bus connection: Verbindungsaufbau abgelehnt
desktop@sn970:~/xow$ sudo systemctl --user start xow.service
Failed to get D-Bus connection: Verbindungsaufbau abgelehnt

Running the audio branch of xow manually (which is not recommended for the audio branch) still works:

desktop@sn970:~/xow$ ./xow
 INFO  - xow v0.4-17-g6c8aa7c ©Severin v. W.
 INFO  - Dongle plugged in
 INFO  - Wireless address: 62:45:b4:fa:b0:d2
 INFO  - Dongle initialized
 INFO  - Controller '1' connected
 INFO  - Product ID: 02ea
 INFO  - Serial number: 02600021263813

The PulseAudio daemon is running (I don't know if this information is helpful):

desktop@sn970:~/xow$ ps -ef|grep -i pulse
pulse      646     1  0 15:52 ?        00:00:01 /usr/bin/pulseaudio --system --disallow-exit --daemonize=no --high-priority
medusalix commented 4 years ago

@andreashuetter Make sure to run systemctl --user without sudo.

andreashuetter commented 4 years ago

Well, the reason why I tried it with sudo was that without sudo I always got:

desktop@sn970:~/xow$ systemctl --user enable xow.service
Failed to execute operation: No such file or directory

But now I tried systemctl --user enable /lib/systemd/user/xow.service and this at least did not return any error message.

But it doesn't seem to work at all. After systemctl --user enable /lib/systemd/user/xow.service and systemctl --user start xow.service

I rebooted the machine, and it says it's not active:

desktop@sn970:~/xow$ systemctl status xow
● xow.service
   Loaded: not-found (Reason: No such file or directory)
   Active: inactive (dead)
andreashuetter commented 4 years ago

Silly me, of course systemctl status xow is the wrong command in this case.

Of course, systemctl --user status xow is the way to go here:

desktop@sn970:~/xow$ systemctl --user status xow 
● xow.service - Xbox One Wireless Dongle Driver
   Loaded: loaded (/lib/systemd/user/xow.service; enabled)
   Active: active (running) since Mi 2020-04-15 18:24:30 CEST; 2min 44s ago
 Main PID: 1409 (xow)
   CGroup: /user.slice/user-1000.slice/user@1000.service/xow.service
           └─1409 /usr/local/bin/xow

But after some trying I fear that this version of xow is not suited for SteamOS.

The thing is, when SteamOS gets started it always starts in Big Picture Mode and everything is run by the user steam. In Big Picture Mode there is no desktop, no terminal window and so on. Only the steam gui. But there is the possibility to exit from steam and launch a Gnome Desktop instead where you can have a terminal window, a browser and so on (but no steam). When you do this you get logged on as user desktop.

So whenever I had to do something like checking out xow from github, compiling it and installing it, I did this as user desktop. Calling sudo make install and sudo systemctl enable xow made sure that xow gets started by the system on startup. But when I do systemctl --user enable /lib/systemd/user/xow.service as user desktop this won't work on startup, because this user is not even logged on on startup (as I described, this user only can get logged on by closing the steam session and launching the Gnome desktop instead). So I thought that maybe I have to try this as user steam (by performing sudo su - steam and then systemctl --user enable /lib/systemd/user/xow.service), but this is not possible because the user steam has very few priviliges and is not allowed to perform such things.

I switched back to the (system-service) master branch of xow now, because I think currently it's not possible to get the (user-service) audio branch to work properly on SteamOS.

medusalix commented 4 years ago

Hmm, I think you could try using the old xow.service file with the new audio branch. I've noticed that you're running PulseAudio as a system-wide daemon (--system), whereas most modern Linux distribution start individual instances of PulseAudio for each user, so a system-service might even work in your specific case.

TauAkiou commented 4 years ago

I tried on Fedora 31, built and worked up until I plugged in a pair of headphones, then crashed:

Kernel: 5.5.15-200.fc31.x86_64

2020-04-15 20:30:28 DEBUG - Opening device...
2020-04-15 20:30:28 INFO  - Dongle plugged in
2020-04-15 20:30:28 DEBUG - Firmware already loaded, resetting...
2020-04-15 20:30:28 DEBUG - Firmware loaded
2020-04-15 20:30:28 DEBUG - ASIC version: 7632
2020-04-15 20:30:28 DEBUG - MAC version: 7662
2020-04-15 20:30:28 DEBUG - Chip id: 7612
2020-04-15 20:30:28 INFO  - Wireless address: 62:45:b4:ee:94:05
2020-04-15 20:30:28 INFO  - Dongle initialized
2020-04-15 20:30:30 DEBUG - Client associating: 7e:ed:8c:80:45:6f
2020-04-15 20:30:30 INFO  - Controller '1' connected
2020-04-15 20:30:30 INFO  - Product ID: 02ea
2020-04-15 20:30:30 DEBUG - Firmware version: 3.1.1221.0
2020-04-15 20:30:30 DEBUG - Hardware version: 1284.1.1.1
2020-04-15 20:30:30 INFO  - Serial number: 03600187504835
2020-04-15 20:30:32 DEBUG - Battery type: 1, level: 3
2020-04-15 20:30:41 INFO  - Product ID: 02f6
2020-04-15 20:30:41 DEBUG - Firmware version: 3.1.1221.0
2020-04-15 20:30:41 DEBUG - Hardware version: 1284.1.1.1
terminate called after throwing an instance of 'InputException'
  what():  Error adding key code: Invalid argument
Aborted (core dumped)
andreashuetter commented 4 years ago

Hmm, I think you could try using the old xow.service file with the new audio branch. I've noticed that you're running PulseAudio as a system-wide daemon (--system), whereas most modern Linux distribution start individual instances of PulseAudio for each user, so a system-service might even work in your specific case.

I tried what you suggested: Using the old xow.service file with the new audio branch (on SteamOS). The result is that the controllers work as expected, but when I plug in the headset, nothing happens (the controller is still working). I tried to select the headset for audio output, but it didn't appear anywhere. I guess connecting the headset to the controller is supposed to lead to an additional audio sink beeing listed?

desktop@sn970:~$ pactl list sinks
Sink #0
    State: SUSPENDED
    Name: alsa_output.pci-0000_00_1f.3.analog-stereo
    Description: Internes Audio Analog Stereo
    Driver: module-alsa-card.c
    Sample Specification: s16le 2ch 44100Hz
    Channel Map: front-left,front-right
    Owner Module: 1
    Mute: yes
    Volume: front-left: 5621 /   9% / -64,00 dB,   front-right: 5621 /   9% / -64,00 dB
            balance 0,00
    Base Volume: 65536 / 100% / 0,00 dB
    Monitor Source: alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
    Latency: 0 usec, configured 0 usec
    Flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY 
    Properties:
        alsa.resolution_bits = "16"
        device.api = "alsa"
        device.class = "sound"
        alsa.class = "generic"
        alsa.subclass = "generic-mix"
        alsa.name = "ALC892 Analog"
        alsa.id = "ALC892 Analog"
        alsa.subdevice = "0"
        alsa.subdevice_name = "subdevice #0"
        alsa.device = "0"
        alsa.card = "0"
        alsa.card_name = "HDA Intel PCH"
        alsa.long_card_name = "HDA Intel PCH at 0xdf220000 irq 130"
        alsa.driver_name = "snd_hda_intel"
        device.bus_path = "pci-0000:00:1f.3"
        sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
        device.bus = "pci"
        device.vendor.id = "8086"
        device.vendor.name = "Intel Corporation"
        device.product.id = "a170"
        device.form_factor = "internal"
        device.string = "front:0"
        device.buffering.buffer_size = "352800"
        device.buffering.fragment_size = "176400"
        device.access_mode = "mmap+timer"
        device.profile.name = "analog-stereo"
        device.profile.description = "Analog Stereo"
        device.description = "Internes Audio Analog Stereo"
        alsa.mixer_name = "Realtek ALC892"
        alsa.components = "HDA:10ec0892,19dab246,00100302"
        module-udev-detect.discovered = "1"
        device.icon_name = "audio-card-pci"
    Profile:
        analog-output: Analoge Ausgabe (priority: 9900)
        analog-output-lineout: Line Out (priority: 9900, not available)
    Aktive Profile: analog-output
    Formats:
        pcm

Sink #2
    State: SUSPENDED
    Name: alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2
    Description: HDA NVidia Digital Stereo (HDMI)
    Driver: module-alsa-card.c
    Sample Specification: s16le 2ch 44100Hz
    Channel Map: front-left,front-right
    Owner Module: 15
    Mute: no
    Volume: front-left: 65536 / 100% / 0,00 dB,   front-right: 65536 / 100% / 0,00 dB
            balance 0,00
    Base Volume: 65536 / 100% / 0,00 dB
    Monitor Source: alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2.monitor
    Latency: 0 usec, configured 0 usec
    Flags: HARDWARE DECIBEL_VOLUME LATENCY SET_FORMATS 
    Properties:
        alsa.resolution_bits = "16"
        device.api = "alsa"
        device.class = "sound"
        alsa.class = "generic"
        alsa.subclass = "generic-mix"
        alsa.name = "HDMI 2"
        alsa.id = "HDMI 2"
        alsa.subdevice = "0"
        alsa.subdevice_name = "subdevice #0"
        alsa.device = "8"
        alsa.card = "1"
        alsa.card_name = "HDA NVidia"
        alsa.long_card_name = "HDA NVidia at 0xdf080000 irq 17"
        alsa.driver_name = "snd_hda_intel"
        device.bus_path = "pci-0000:01:00.1"
        sysfs.path = "/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1"
        device.bus = "pci"
        device.vendor.id = "10de"
        device.vendor.name = "NVIDIA Corporation"
        device.product.id = "0fbb"
        device.string = "hdmi:1,2"
        device.buffering.buffer_size = "352768"
        device.buffering.fragment_size = "176384"
        device.access_mode = "mmap+timer"
        device.profile.name = "hdmi-stereo-extra2"
        device.profile.description = "Digital Stereo (HDMI)"
        device.description = "HDA NVidia Digital Stereo (HDMI)"
        alsa.mixer_name = "Nvidia GPU 71 HDMI/DP"
        alsa.components = "HDA:10de0071,19dab282,00100100"
        module-udev-detect.discovered = "1"
        device.icon_name = "audio-card-pci"
    Profile:
        hdmi-output-2: HDMI / DisplayPort 3 (priority: 5700, available)
    Aktive Profile: hdmi-output-2
    Formats:
        pcm

When I stop the xow service and start xow manually to get more info then it keeps printing that it has detected the audio device, but never stops printing this (even if I disconnect the headphone it keeps printing these lines) until I press Ctrl+C:

desktop@sn970:~$ sudo systemctl stop xow
desktop@sn970:~$ /usr/local/bin/xow
 INFO  - xow v0.4-17-g6c8aa7c ©Severin v. W.
 ERROR - Error creating lock file: Bad file descriptor
desktop@sn970:~$ sudo /usr/local/bin/xow
 INFO  - xow v0.4-17-g6c8aa7c ©Severin v. W.
 INFO  - Dongle plugged in
 INFO  - Wireless address: 62:45:b4:fa:b0:d2
 INFO  - Dongle initialized
 INFO  - Controller '1' connected
 INFO  - Product ID: 02ea
 INFO  - Serial number: 02600238452701
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
 INFO  - Accessory announced
 INFO  - Assuming it's an audio device
^C INFO  - Dongle power-off
 ERROR - Error in bulk read: LIBUSB_ERROR_NO_DEVICE
 ERROR - Error in bulk read: LIBUSB_ERROR_NO_DEVICE
medusalix commented 4 years ago

@TauAkiou It looks like it identified your headphones as an input device. I fixed this problem in the latest commit, but you might run into the same issue as @andreashuetter.

I guess connecting the headset to the controller is supposed to lead to an additional audio sink being listed?

Yes, it creates an audio sink for the controller's input (currently non-functional) and an audio source for the output. In your case, it seems to get stuck during the audio initialization (it should normally log Audio enabled). Further analysis will definitely require a USB packet capture (on Windows), where you're plugging in the headphones, so I can find out why xow refuses to work.

Aerol commented 4 years ago

Building audio branch and running xow from source directory I get the following output

❯ ./xow 2020-04-18 11:11:02 INFO - xow v0.4-19-gdfed4ea ©Severin v. W. 2020-04-18 11:11:02 DEBUG - Opening device... 2020-04-18 11:11:02 INFO - Dongle plugged in 2020-04-18 11:11:02 DEBUG - Firmware already loaded, resetting... 2020-04-18 11:11:02 DEBUG - Firmware loaded 2020-04-18 11:11:02 DEBUG - ASIC version: 7632 2020-04-18 11:11:02 DEBUG - MAC version: 7662 2020-04-18 11:11:02 DEBUG - Chip id: 7613 2020-04-18 11:11:02 INFO - Wireless address: 62:45:b5:10:74:a9 2020-04-18 11:11:02 INFO - Dongle initialized 2020-04-18 11:11:06 DEBUG - Client associating: 7e:ed:8c:45:0c:f7 2020-04-18 11:11:06 INFO - Controller '1' connected 2020-04-18 11:11:07 INFO - Device announced, id: 02dd 2020-04-18 11:11:07 DEBUG - Firmware version: 2.3.2385.0 2020-04-18 11:11:07 DEBUG - Hardware version: 2.1.1.1 2020-04-18 11:11:07 INFO - Device type: controller 2020-04-18 11:11:08 INFO - Device announced, id: 02e4 2020-04-18 11:11:08 DEBUG - Firmware version: 2.3.2385.0 2020-04-18 11:11:08 DEBUG - Hardware version: 2.1.1.1 2020-04-18 11:11:08 INFO - Device type: controller terminate called after throwing an instance of 'InputException' what(): Error adding key code: Invalid argument [1] 23864 abort ./xow

That's with headset plugged in; without it runs normally

medusalix commented 4 years ago

@Aerol I've just pushed out a commit that should fix this issue.

Aerol commented 4 years ago

@Aerol I've just pushed out a commit that should fix this issue.

It has indeed! Now getting more proper output

2020-04-18 13:35:39 DEBUG - Firmware version: 2.3.2385.0 2020-04-18 13:35:39 DEBUG - Hardware version: 2.1.1.1 2020-04-18 13:35:39 INFO - Device type: headset 2020-04-18 13:35:41 INFO - Device announced, id: 02e4

medusalix commented 4 years ago

@andreashuetter Your issue should most likely be fixed now (didn't need the USB capture).

andreashuetter commented 4 years ago

@andreashuetter Your issue should most likely be fixed now (didn't need the USB capture).

Thank you! So I did some additional tests with your latest audio commit.

This is what I did: First I uninstalled xow completely and made sure there was no xow binary and no xow.service file anymore on my system. Then I checked out the latest master branch, compiled it and made

make BUILD=RELEASE
sudo make install
sudo systemctl enable xow.service

to get the proper service file.

After that I switched to the audio branch, compiled that as well (but did no make install in order to keep the old service file) and copied the new audio binary manually into /usr/local/bin overwriting the master xow binary:

git checkout audio
make clean
make BUILD=RELEASE
sudo cp xow /usr/local/bin/xow
sudo systemctl start xow

Result is that the controllers work as expected, but I still don't get an additional audio device when I plug in the headset into the controller. Stopping the xow service and starting it manually gives the following output:

desktop@sn970:~$ sudo systemctl stop xow
desktop@sn970:~$ cd xow
desktop@sn970:~/xow$ ./xow
 INFO  - xow v0.4-21-g070fbb7 ©Severin v. W.
 ERROR - Error creating lock file: Bad file descriptor
desktop@sn970:~/xow$ sudo ./xow
 INFO  - xow v0.4-21-g070fbb7 ©Severin v. W.
 INFO  - Dongle plugged in
 INFO  - Wireless address: 62:45:b4:fa:b0:d2
 INFO  - Dongle initialized
 INFO  - Controller '1' connected
 INFO  - Device announced, product id: 02ea
 INFO  - Device type: controller
 INFO  - Serial number: 02600238452701
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
 INFO  - Device type: headset
 INFO  - Device announced, product id: 0111
...

Isn't there supposed to appear a new audio output device in my system config?

desktop@sn970:~$ pactl list sources
Source #1
    State: SUSPENDED
    Name: alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
    Description: Monitor of Internes Audio Analog Stereo
    Driver: module-alsa-card.c
    Sample Specification: s16le 2ch 44100Hz
    Channel Map: front-left,front-right
    Owner Module: 2
    Mute: no
    Volume: front-left: 65536 / 100% / 0,00 dB,   front-right: 65536 / 100% / 0,00 dB
            balance 0,00
    Base Volume: 65536 / 100% / 0,00 dB
    Monitor of Sink: alsa_output.pci-0000_00_1f.3.analog-stereo
    Latency: 0 usec, configured 0 usec
    Flags: DECIBEL_VOLUME LATENCY 
    Properties:
        device.description = "Monitor of Internes Audio Analog Stereo"
        device.class = "monitor"
        alsa.card = "0"
        alsa.card_name = "HDA Intel PCH"
        alsa.long_card_name = "HDA Intel PCH at 0xdf220000 irq 130"
        alsa.driver_name = "snd_hda_intel"
        device.bus_path = "pci-0000:00:1f.3"
        sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
        device.bus = "pci"
        device.vendor.id = "8086"
        device.vendor.name = "Intel Corporation"
        device.product.id = "a170"
        device.form_factor = "internal"
        device.string = "0"
        module-udev-detect.discovered = "1"
        device.icon_name = "audio-card-pci"
    Formats:
        pcm

Source #2
    State: RUNNING
    Name: alsa_input.pci-0000_00_1f.3.analog-stereo
    Description: Internes Audio Analog Stereo
    Driver: module-alsa-card.c
    Sample Specification: s16le 2ch 44100Hz
    Channel Map: front-left,front-right
    Owner Module: 2
    Mute: yes
    Volume: front-left: 11215 /  17% / -46,00 dB,   front-right: 11215 /  17% / -46,00 dB
            balance 0,00
    Base Volume: 20724 /  32% / -30,00 dB
    Monitor of Sink: k. A.
    Latency: 423 usec, configured 20000 usec
    Flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY 
    Properties:
        alsa.resolution_bits = "16"
        device.api = "alsa"
        device.class = "sound"
        alsa.class = "generic"
        alsa.subclass = "generic-mix"
        alsa.name = "ALC892 Analog"
        alsa.id = "ALC892 Analog"
        alsa.subdevice = "0"
        alsa.subdevice_name = "subdevice #0"
        alsa.device = "0"
        alsa.card = "0"
        alsa.card_name = "HDA Intel PCH"
        alsa.long_card_name = "HDA Intel PCH at 0xdf220000 irq 130"
        alsa.driver_name = "snd_hda_intel"
        device.bus_path = "pci-0000:00:1f.3"
        sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
        device.bus = "pci"
        device.vendor.id = "8086"
        device.vendor.name = "Intel Corporation"
        device.product.id = "a170"
        device.form_factor = "internal"
        device.string = "front:0"
        device.buffering.buffer_size = "352800"
        device.buffering.fragment_size = "176400"
        device.access_mode = "mmap+timer"
        device.profile.name = "analog-stereo"
        device.profile.description = "Analog Stereo"
        device.description = "Internes Audio Analog Stereo"
        alsa.mixer_name = "Realtek ALC892"
        alsa.components = "HDA:10ec0892,19dab246,00100302"
        module-udev-detect.discovered = "1"
        device.icon_name = "audio-card-pci"
    Profile:
        analog-input: Analoge Eingabe (priority: 10000)
        analog-input-mic: Mikrofon (priority: 8700, not available)
    Aktive Profile: analog-input
    Formats:
        pcm

Source #3
    State: IDLE
    Name: alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2.monitor
    Description: Monitor of HDA NVidia Digital Stereo (HDMI)
    Driver: module-alsa-card.c
    Sample Specification: s16le 2ch 48000Hz
    Channel Map: front-left,front-right
    Owner Module: 1
    Mute: no
    Volume: front-left: 65536 / 100% / 0,00 dB,   front-right: 65536 / 100% / 0,00 dB
            balance 0,00
    Base Volume: 65536 / 100% / 0,00 dB
    Monitor of Sink: alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2
    Latency: 0 usec, configured 1837333 usec
    Flags: DECIBEL_VOLUME LATENCY 
    Properties:
        device.description = "Monitor of HDA NVidia Digital Stereo (HDMI)"
        device.class = "monitor"
        alsa.card = "1"
        alsa.card_name = "HDA NVidia"
        alsa.long_card_name = "HDA NVidia at 0xdf080000 irq 17"
        alsa.driver_name = "snd_hda_intel"
        device.bus_path = "pci-0000:01:00.1"
        sysfs.path = "/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1"
        device.bus = "pci"
        device.vendor.id = "10de"
        device.vendor.name = "NVIDIA Corporation"
        device.product.id = "0fbb"
        device.string = "1"
        module-udev-detect.discovered = "1"
        device.icon_name = "audio-card-pci"
    Formats:
        pcm
Aerol commented 4 years ago

Things seem to be working ok on my end with commit 070fbb7889cf7abe6f24b0fd4cd0e2f0aae1c4cb

I can even hear myself in my headset, but it seems to happen regardless of audio being muted in PA. Possibly something happening internally on the controller? I'm not 100% positive but I believe what needs to be created are a sink and source for audio to be routed to, not audio streams. We need to be able to tell pulseaudio to take audio from this device and send audio to the device etc.

What's currently being created is a playback stream to "generate audio" and send to a sink device (eg my soundcard output) and a recording stream that accepts audio input and does something with it

medusalix commented 4 years ago

Possibly something happening internally on the controller?

Not that I'm aware of (I'm just using a standard smartphone headset). Input from the controller (microphone) is sent to a PulseAudio sink and output to the controller (headphones) is read from a PulseAudio source. The actual routing of the sink and source to your audio hardware is completely up to PulseAudio's configuration and can be changed via pavucontrol.

What's currently being created is a playback stream to "generate audio" and send to a sink device (eg my soundcard output) and a recording stream that accepts audio input and does something with it.

Applications can only create sinks and sources (that's what xow is doing), but that should be sufficiently flexible to allow for different routing configurations.

Aerol commented 4 years ago

Applications can only create sinks and sources (that's what xow is doing), but that should be sufficiently flexible to allow for different routing configurations.

Applications create playback and/or recording streams that can connect to device sinks and/or sources.

Input from the controller (microphone) is sent to a PulseAudio sink and output to the controller (headphones) is read from a PulseAudio source

I can connect the recording stream (PulseAudio source) created by xow to my main output monitor and can hear it in my headset, eg play music and I hear it in my headset. image

If microphone audio is sent to the playback stream (PulseAudio sink) created by xow, all that would happen is microphone audio going out my desktop speakers. (mic audio is not picked up at the moment)

The way things are now, microphone can not be sent to Discord (or whatever;) and headphones can't receive audio from Discord (or whatever.) The xbox controller/xow needs to present itself to pulseaudio as an audio device (like JACK audio currently does on my system using a pulseaudio module), not an application trying to stream audio. Does this make sense?

medusalix commented 4 years ago

Does this make sense?

Yes, makes perfect sense. I think I mixed up quite a lot of things in my last reply, sorry about that. When I did the initial research on how to create audio devices in Linux from user space, I came to the conclusion that the only way that would work would be to develop an ALSA kernel module. People suggested to use PulseAudio, but apparently it makes things even more complicated. The only thing left that's worth trying out would be PulseAudio modules. I tried out module-pipe-source and module-pipe-sink and I guess it would be possible to load them at runtime via PulseAudio's API and redirect the controller's audio input/output to the modules' pipes. Does that sound like an idea? (pun intended)

Aerol commented 4 years ago

Does that sound like an idea? (pun intended)

lol, yes; this does sound like a very usable idea.

nkoester commented 4 years ago

I tried out module-pipe-source and module-pipe-sink and I guess it would be possible to load them at runtime via PulseAudio's API and redirect the controller's audio input/output to the modules' pipes.

Regarding the redirection I want to point you to the module-switch-on-connect module documented here for example. I use this for automatic output switching when connecting bluetooth headphones

skittles9823 commented 4 years ago

So I was testing the audio branch with my controller and it works really well, obviously it can't send audio yet and that's fine for my use case, however there is one issue I noticed, and that's the fact that the controllers audio input doesn't create a new audio channel and it just hooks into the existing default, in my case the speakers, so audio is going through both my speakers and headphones connected to the controller. Any chance for the controller to create a new channel like it does on windows?

medusalix commented 4 years ago

Any chance for the controller to create a new channel like it does on windows?

Yep, that's the plan. I'm currently working on other things, so I put this issue on hold for now.

skittles9823 commented 4 years ago

@medusalix Awesome, glad to hear.

lunaneff commented 4 years ago

I just found xow today and would like to try out the experimental support. I'm unable to find the audio branch though. Why did you remove it?

medusalix commented 4 years ago

@laurinneff The audio branch had the same licensing-related issues as the master branch, so I decided to remove it for now. I also ported some changes from audio to master, so the branches started to drift away from each other. I plan on incorporating the feedback I got for an audio2 branch in the future after fixing some of the issues mentioned in earlier comments. I hope you understand.

lunaneff commented 4 years ago

@medusalix alright, I understand. Hope I can try it out soon

petrkutalek commented 4 years ago

@medusalix may I ask you why did you decide to build audio support via Pulse Audio and not Alsa subsystem?

medusalix commented 4 years ago

ALSA doesn't offer any option for user mode programs to hook into the audio system and create fully-featured audio devices at all. The only reason why we don't have audio support right now is the lack of a suitable API that would allow xow to expose the devices to the operating system (even with PulseAudio). Similar story for the missing upower support by the way.

petrkutalek commented 4 years ago

I don't think so. bluez-alsa is definitely userspace app and it creates sink device.

EDIT: There is a comment in the issue 48 that outlines other possible solutions via Alsa.

medusalix commented 4 years ago

I know about bluez-alsa, I even tried writing an Alsa PCM plugin for xow. The point is that it simply doesn't behave like an audio card would. You can create sinks and sources that aplay and arecord can access. It's not exposed by PulseAudio and most applications can't even output to it. I need to create dedicated sound devices for xow, not a virtual device that you pipe your audio to. Literally the only viable alternative would be PulseAudio modules, but they don't have a stable API.

petrkutalek commented 4 years ago

OK, thank you Severin. I do not agree with you, nevertheless I accept your decision on your (actually nice) project. :+1: I do not use Pulse Audio at all, just Alsa and I do not have any problems with it. I do not know any app that does not with Alsa but Pulse Audio. In fact Pulse Audio could utilize underlying Alsa. Nevermind.

medusalix commented 4 years ago

The problem is that applications like Discord show you a list of PulseAudio sinks and sources (at least if you've got PulseAudio installed). They simply don't give you an option to select which Alsa sink/source you want your output/input to go to. In fact, PulseAudio provides an Alsa device which allows legacy Alsa-only applications to interface with PulseAudio over their plugin. Alsa PCM plugins unfortunately don't appear in PulseAudio's device list (at least to my knowledge) which means you have to use workarounds like aplay to route your default audio output to the Alsa plugin on systems where PulseAudio is installed. It's not a decision I've taken, it's more like a shortcoming of Alsa's (and PulseAudio's) design. I'm definitely open for suggestions, I don't want to limit myself to a specific audio system, I just want to make sure it's (1) as simple as possible to use and (2) it works on every system out there.

VanGrx commented 4 years ago

I examined audio branch from some repo that forked this one before when audio branch existed (sweet times :cry: ).

Basically, I think you made a good approach with simple API from PulseAudio (note that maybe async API will give better results. I managed to make some async API integration but as there were a lot of callbacks and controller needed to receive exact 0x600 bytes of data, that means threads and internal buffers implementation so I gave up for just POW case :smile: )

The only thing that should have been added that could be the key to the remaining problems is to create ALSA virtual device and connect with PulseAudio simple API to it. PulseAudio should automatically detect created ALSA virtual device so Discord user-case or any that needs PulseAudio should work. From there, PulseAudio simple(or async) API can be handled from xow and sent to controller(or to the virtual driver for mic).

VanGrx commented 4 years ago

Hi guys,

After a wild adventure with ALSA drivers, I gave up and found more simple solution:

After calling pactl load-module module-null-sink sink_name=XboxOutput sink_properties=device.description="Xbox_controller_sink" Another device will be added to the pulse audio. In the xow code, the changes were:

sink = pa_simple_new(
    nullptr,
    name.c_str(),
    PA_STREAM_PLAYBACK,
    "XboxOutput1",
    STREAM_NAME_SINK,
    &config,
    nullptr,
    nullptr,
    &error
);

and

source = pa_simple_new(
    nullptr,
    name.c_str(),
    PA_STREAM_RECORD,
    "XboxOutput1.monitor",
    STREAM_NAME_SOURCE,
    &config,
    nullptr,
    nullptr,
    &error
);

Note: difference is that 4th parameter is changed from nullptr to the name of the module we created.

After this, the audio was able to play only on headphones after selecting them from PulseAudio menu.

For the microphone to work, there must be another module created only for microphone capture. If I understood correctly in xow there must be 2 use-cases detected: 1 is when controller is sending special packet with audio samples and is waiting for the dongle to return them (to establish connection from what I tested) 2 is when controller is sending audio from the mic (in this case we should send samples to other module so we don't hear ourselves and so we can select that specific module for input in the apps) So probably some special case should be if controller is connected or it is connecting.

Hope this is good progress for you and if you agree to integrate audio branch back with this changes included, I can continue on checking microphone pipeline that is needed. :smile:

medusalix commented 4 years ago

Like I said in one of my earlier comments, I think if we're going to going to use PulseAudio's API for this then we'd be better of using module-pipe-sink and module-pipe-source directly instead of redirecting the audio through a null sink/source. What bothers me the most is the fact that PulseAudio is by default running in local-user mode and only allows connections from your user account (not from root). That's the reason why I had to switch to a systemd user service for the audio branch. This will cause issues for multi-user systems because we can't have multiple instances of xow running at the same time. I don't think it makes much sense for a device driver to have per-user instances, as opposed to a single system-wide instance.

Now here's a solution that I'd imagine would work pretty well:

Either the suggestion outlined above or a dedicated PulseAudio module would work, I think.

kakra commented 4 years ago

I think that split-design is the best suggestion: Input devices should be created by system services because udev and logind already setup the permissions etc, connecting to pulse audio is a user service, so it needs to be the proxy between the xow system server and pulseaudio user service and it would probably be pretty light-weight. You could use dbus to signal the user service connection to the system daemon. For multiuser setups, there's "just" one thing to solve: You should track which user session currently claims the input device and only signal that particular user daemon. It should not be possible for other user sessions to "listen" to the audio.

VanGrx commented 4 years ago

Like I said in one of my earlier comments, I think if we're going to going to use PulseAudio's API for this then we'd be better of using module-pipe-sink and module-pipe-source directly instead of redirecting the audio through a null sink/source.

The problem with piping is that you must always have a reader on the other side or the reproduction of the sound will block. That can block write() function in xow if I am not mistaken.

This will cause issues for multi-user systems because we can't have multiple instances of xow running at the same time.

This is overthinking. If multi-user case is a big issue, a install script option that runs PulseAudio in system-wide mode can resolve this (if anybody actually cares if xow is system wide or not). No need to add another layer just for case that will probably never happen.

medusalix commented 4 years ago

The problem with piping is that you must always have a reader on the other side or the reproduction of the sound will block. That can block write() function in xow if I am not mistaken.

According to pipe(7), a write with O_NONBLOCK set on the file descriptor should return EAGAIN in case the FIFO buffer is already full (please correct me if I'm wrong). If we load the module with pa_context_load_module the reader should be available right after the module is loaded.

This is overthinking. If multi-user case is a big issue, a install script option that runs PulseAudio in system-wide mode can resolve this.

You'd have to add an option that runs xow in system-wide mode though, not PA.

[...] if anybody actually cares if xow is system wide or not [...]

Would you expect your mouse or keyboard to turn off as soon as you logged out though? I certainly wouldn't. I believe nobody expects their input devices (or any device for that matter) to be bound to the lifetime of a specific user session. Oh, and there people who run multi-user systems (take a look at this comment). I'm also curious about the (possible) issues that would arise by installing such a version of xow via a package manager like apt. Would the command sudo apt install xow be able to start the service for the current user? I don't know.

kakra commented 4 years ago

Would the command sudo apt install xow be able to start the service for the current user? I don't know.

Short answer: No... Long answer: apt runs in root context, it could run systemctl enable --now xow-system.service but that's it. The user would have to start it on its own. There's dbus activation for user services - not sure if it would work to activate a user service from a system service that way, I don't think so. But even then, the user session systemd must be reloaded to pick up the new service files. So: no. You'd need to re-login.

Also, I don't see why blocking in xow would be an issue as the writer could be implemented in a separate thread without blocking the main thread. It actually should be done that way. Also, the xow system service that receives the sound endpoint, doesn't even write directly to pa (except you manage to pass FDs around), it will probably be a proxy and should do reads and writes in separate threads anyways. The xow audio handler and input handler must be separate threads.