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

Audio support #76

Open
a1ien opened this issue Jun 1, 2016 · 77 comments
Open

Audio support #76

a1ien opened this issue Jun 1, 2016 · 77 comments
Labels

Comments

@a1ien
Copy link

a1ien commented Jun 1, 2016

I have some research to be able playing audio on DS4.
Audio data send by opcode 0x14, 0x15, 0x17, 0x19.
And code by SBC audio codec.
I used this python script to wrap the audio data
play.py
This script takes the SBC audio data and send it to DS4
You can use gstreamer to prepare audio data.
gst-launch-1.0 -q audiotestsrc ! sbcenc ! 'audio/x-sbc,channels=2,rate=32000,channel-mode=dual,blocks=16,subbands=8,bitpool=25' ! queue ! fdsink | ./play.py
But there is a small problem, periodically occur stuttering.
But it may be caused by improper operation with HID device.
I hope this helps to add audio support.

@Ape
Copy link
Collaborator

Ape commented Jun 2, 2016

Wow! This is actually working. Thank you so much for this.

I also get some stuttering. Perhaps the bluetooth signal is not perfect.

@a1ien
Copy link
Author

a1ien commented Jun 5, 2016

And small addition
If add is-live=true to audiotestsrc
gst-launch-1.0 -q audiotestsrc is-live=true ! sbcenc ....
Stuttering becomes much less
And useful pipeline to play all output to DS4
gst-launch-1.0 -q pulsesrc device="alsa_output.pci-0000_00_1b.0.analog-stereo.monitor" ! queue ! audioresample ! 'audio/x-raw,rate=32000' ! audioconvert ! sbcenc ! 'audio/x-sbc,channels=2,rate=32000,channel-mode=dual,blocks=16,subbands=8,bitpool=25' ! queue ! fdsink | ./play.py
To get device name u can use
pacmd list-sources | grep -e device.string -e 'name:'

@Ape
Copy link
Collaborator

Ape commented Jun 5, 2016

Great! Do you know if there is a way to control the volume?

@a1ien
Copy link
Author

a1ien commented Jun 5, 2016

Yes in last script version have variables
volume_l, volume_speaker.

And also I found, only left channel playing by DS

@parkerlreed
Copy link

parkerlreed commented Jun 6, 2016

I have mine plugged in via USB without ds4drv running

dmesg shows the controller as /dev/hidraw5 and the script is set to use it

        name: <alsa_output.pci-0000_00_1b.0.analog-stereo.monitor>
gst-launch-1.0 -q pulsesrc device="alsa_output.pci-0000_00_1b.0.analog-stereo.monitor" ! queue ! audioresample ! 'audio/x-raw,rate=32000' ! audioconvert ! sbcenc ! 'audio/x-sbc,channels=2,rate=32000,channel-mode=dual,blocks=16,subbands=8,bitpool=25' ! queue ! fdsink | ./play.py

Without the play.py, fddsink outputs data to the terminal correctly. With play.py the controller vibrates sporadically and has no sound coming from the speaker.

Arch x86_64

EDIT: Is this supposed to only work via Bluetooth?

@a1ien
Copy link
Author

a1ien commented Jun 6, 2016

Hmm... It's strange.
You can test
gst-launch-1.0 -q audiotestsrc is-live=true ! sbcenc ! 'audio/x-sbc,channels=2,rate=32000,channel-mode=dual,blocks=16,subbands=8,bitpool=25' ! queue ! fdsink | ./play.py
And what Kernel version you have?
uname -a
I test on last ubuntu 16.04

@parkerlreed
Copy link

parkerlreed commented Jun 6, 2016

audiotestsrc is also silent

Linux e73 4.5.4-1-ARCH #1 SMP PREEMPT Wed May 11 22:21:28 CEST 2016 x86_64 GNU/Linux

@a1ien
Copy link
Author

a1ien commented Jun 6, 2016

EDIT: Is this supposed to only work via Bluetooth?

Yes only Bluetooth

@poconbhui
Copy link
Contributor

poconbhui commented Jun 8, 2016

This is working for me. I used pactl load-module module-null-sink sink_name=ds4 to set up a nicer looking pulseaudio sink, which audio outputs can be selectively output to from pavucontrol. The pulseaudio-dlna project uses something like this quite nicely by adding some extra stuff to the pactl options.

The stuttering seems to go away for me if I use an encoding rate of 16000 instead. Play.py seems to crash every once on a while on pf.write though.

@parkerlreed
Copy link

What did you pass to gstreamer to make it use the new sink?

@Ape
Copy link
Collaborator

Ape commented Jun 8, 2016

@parkerlreed You can use this to get the names:

pacmd list-sources | grep -e device.string -e 'name:'

So if your named sink is ds4 then you can probably use this:

gst-launch-1.0 -q pulsesrc device="ds4.monitor"

@parkerlreed
Copy link

Ahh thanks.

@poconbhui
Copy link
Contributor

If you open pavucontrol, under the "playback" tab, you can select the "Null Output" output from a dropdown menu once you have something playing.

@poconbhui
Copy link
Contributor

The 16000 does sound a lot crappier though.

@a1ien
Copy link
Author

a1ien commented Jun 8, 2016

You also can try always send _17_report instead _14_report. For the 32kHz, works better.

@poconbhui
Copy link
Contributor

So report 17 and 14 are the same except for the amount of audio data? I'm getting much better playback having replaced everything with report 17.

The hidraw writing freezing every once in a while is still a problem for me though. Is anyone else having that problem?

I'm sort of getting around it by setting a SIGALRM timeout as described at http://stackoverflow.com/a/22348885/445799 . Not the nicest option though.

@a1ien
Copy link
Author

a1ien commented Jun 8, 2016

So report 17 and 14 are the same except for the amount of audio data?

Yes. Also 19 same as 15 except for the amount of audio data.

And first part of 15 and 19 same as 11

@poconbhui
Copy link
Contributor

Nice. Any info on the timing between reports? I'm finding I have better sound with a slight delay between packets. For me, it's better when I print a bunch of garbage to the terminal than when I don't.

@a1ien
Copy link
Author

a1ien commented Jun 8, 2016

Any info on the timing between reports?

No, but http://eleccelerator.com/files/ds4_uart_hci_cap_playroom_needs_sorting.pcap.gz may be help.

You cant filter packet from PS4 using bthid[0] == 0xa2
img

@poconbhui
Copy link
Contributor

Any recommendations on programs to use to look at that file? I'm not too familiar with hardware stuff, so I've not touched something like this before.

Ok, I'm having better luck setting up a SIGALRM timeout in a multiprocessing.Process. I was using a threading.Thread, but performance wasn't good enough. Setting the timeout to somewhere around 2*448/32000 seconds is working well enough to allow for a dropped packet or two. There must be a better solution though.

Moving controllers in ds4drv out to processes might be a good idea anyway.

I've added the changes to your play.py with the timeout process here

@a1ien
Copy link
Author

a1ien commented Jun 8, 2016

Wireshark can open file. You can see on screenshot. Before open you need ungzip.

@poconbhui
Copy link
Contributor

poconbhui commented Jun 8, 2016

Woops, forgot about the encoding. Testing with dd suggests data is coming in at about 27.9 kB/s when encoding a 32 kB/s stream.

I'm getting some good results putting the timeout around pf.write on a fairly short timeout value. I've updated the gist to match.

@poconbhui
Copy link
Contributor

I think I've found the volume balance. In b'\x02'+ audo_data of the reports, it's that \x02 value.

@a1ien
Copy link
Author

a1ien commented Jun 8, 2016

I think I've found the volume balance

What means "volume balance". For what purpose use \x02 before audio_data?

@poconbhui
Copy link
Contributor

When playing stereo audio, only one channel was coming through and my right earphone was very quiet.

Setting it to different values changes the relative loudness of the two earphones. So it seems there might not be a left volume / right volume, but instead a total volume and relative volume.

@a1ien
Copy link
Author

a1ien commented Jun 8, 2016

When playing stereo audio, only one channel was coming through and my right earphone was very quiet.

I ran into the same problem, but take earphone with microphone on the jack I get right sound good.

It seems that only supports mono.

@poconbhui
Copy link
Contributor

This seems to be working for me:
header = bytes([int('00100011', 2)]), with header in place of \x02.

@poconbhui
Copy link
Contributor

@poconbhui
Copy link
Contributor

Looks like 00100100 should be the right header for report 17 for the sbcenc settings from gstreamer.

@a1ien
Copy link
Author

a1ien commented Jun 8, 2016

No. Gstreamer generate sbc header.
0x02 - it's not SBC header
SBC header is 0x9c, 0x75, 0x19, 0x24
0x9c - sync
0x75 - 0b1110101
0x19 - bitpool
...

@Ape
Copy link
Collaborator

Ape commented Jun 10, 2016

We can make these dependencies optional and only enable audio support on ds4drv is they are present.

@parkerlreed
Copy link

parkerlreed commented Jun 10, 2016

Is audiotestsrc still working for anybody? I just did a fresh Arch install because by Bluetooth crapped out. I got the controller connected again but when I run the gstreamer to play.py pipe the only thing that happens is the blue light on the controller goes from a dull blue to a brighter blue.

EDIT: ds4drv works in hidraw mode so I'm not sure why play.py isn't communicating.

EDIT2: I did do a fresh play.py pull. If that got updated that could be what's causing my issue.

@poconbhui
Copy link
Contributor

audiotestsrc only works for me with is-live=true set.

@poconbhui
Copy link
Contributor

@Ape of course. I'm just being funny. I'm working on a gstreamer pipeline and thinking about the best way to incorporate it in the current event loop. I'll do a little experimenting, but it might be an idea to have two threads per controller, one for input, one for output.

@poconbhui
Copy link
Contributor

I got gstreamer running in python following http://brettviren.github.io/pygst-tutorial-org/pygst-tutorial.pdf, but the basic model is a bit gross. It involves setting up a blocking GTK main loop. This can probably be pulled off to another thread. I'm not a big fan of it, so I'm going to see if there are any other options.

@a1ien
Copy link
Author

a1ien commented Jun 11, 2016

@parkerlreed last play.py output only to headset.
You can replace header = b'\x24' to header = b'\x02' for play from speaker

@poconbhui
Copy link
Contributor

Ok, I have a working gstreamer version at https://gist.github.com/poconbhui/ab3ea6c5b827c969bd0cc8d76575a0a2 .

@poconbhui
Copy link
Contributor

Should probably put the gstreamer buffer filters back in.

Probably don't need the SBC frame header parser in there anymore because we should already know what the SBC parameters are. Gstreamer might change the values we've asked for though, I have no idea.

The stuff I was using for file write timeouts aren't working with this because of the write not happening in the main thread or something like that. I'm looking into moving it to another process.

@poconbhui
Copy link
Contributor

Ok, the timeout stuff was easier than expected. Just moved the write function out to a Pool process. All in the gist now, working nicely for me.

@poconbhui
Copy link
Contributor

I've just added the pulse sink creation and cleanup. Works well enough. I get some stuttering on startup. That can probably be fixed by adding a buffer to the gstreamer pipeline.

@a1ien
Copy link
Author

a1ien commented Jun 11, 2016

You can use queue element for buffer

@poconbhui
Copy link
Contributor

Buffer for which part?

@poconbhui
Copy link
Contributor

Sorry, just got it. Yeah, I'll probably just put together your original pipeline.

@poconbhui
Copy link
Contributor

The queue buffer works to stop the stuttering if I set a minimum threshold, but it adds lag. Dropping a few hundred initial reports does the same job without the lag.

Which is better, a startup delay or immediate startup with stuttering audio?

@poconbhui
Copy link
Contributor

Ok, just adding a sleep between setting up the pulse sink and starting gstreamer works fine.

@parkerlreed
Copy link

@poconbhui Oh my, thanks for the gstreamer version! I was able to switch the output in pavucontrol and I get no pops or crackles on the controller side.

@poconbhui
Copy link
Contributor

Ooh, looks like I don't need the resampler for pulse. It has one built in! That would make using pure pulseaudio and SBC encoder much easier. Gtk is giving me a headache from frowning so much!

@poconbhui
Copy link
Contributor

pactl load-module module-null-sink sink_name=ds4 sink_properties=device.description="DualShock\ 4",rate=32000

gst-launch-1.0 -q pulsesrc device="ds4.monitor" ! sbcenc ! "audio/x-sbc,rate=32000, ...

Works for me.

@Ape Ape changed the title Information about playing audio on DS4 Audio support Jun 15, 2016
@Ape Ape added the feature label Jun 15, 2016
@Ape
Copy link
Collaborator

Ape commented Jun 15, 2016

I made this the general tracking issue for audio support. See #80 for an implementation (work in progress).

@poconbhui
Copy link
Contributor

Have you any idea how to fix the typo in the pull request title?

@Ape
Copy link
Collaborator

Ape commented Jun 15, 2016

@poconbhui There is this "Edit" button.

@poconbhui
Copy link
Contributor

How the hell did I miss that?

@dequis
Copy link

dequis commented Aug 22, 2017

Any idea if this works with the DS4 v2? Linux 4.12.8 and bluez 5.45

I get no audio at all with the gists in this thread, and tweaking the first one to use _11_report() gets me no visible effects either (led or rumble).

I hope I'm missing something stupid because sniffing traffic of this thing looks hard.

@Scrumplex
Copy link

@dequis LED only works in Bluetooth raw or Hidraw USB mode. The rumble generally does not work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants