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

Pianobar in Termux: /!\ Cannot open audio device #664

Open
tcaddy opened this issue Jul 9, 2018 · 37 comments
Open

Pianobar in Termux: /!\ Cannot open audio device #664

tcaddy opened this issue Jul 9, 2018 · 37 comments
Labels

Comments

@tcaddy
Copy link

tcaddy commented Jul 9, 2018

Cannot open audio device

I was able to get pianobar to compile in the Termux app on a Chromebook. However, when I try to run the app I get console errors that say:

/!\ Cannot open audio device

Pulseaudio is running. There is a Termux package called play-audio. It will play a sample MP3 file from the Termux command line.

Your environment

  • version of pianobar: 2018.06.22-dev
  • your Linux distribution and release version: output of termux-info command:
bash-4.4$ termux-info
Updatable packages:
All packages up to date
System information:
Linux localhost 3.18.0-18024-gc429fbc8b604 #1 SMP PREEMPT Tue Jun 19 04:33:31 PDT 2018 i686 Android
Termux-packages arch:
i686
Android version:
7.1.1
Device manufacturer:
google
Device model:
Acer Chromebook R11 (CB5-132T / C738T)
  • ffmpeg/libav version and the flags it was compiled with (if you compiled yourself): ffmpeg -v 4.0.1 installed via pkg app in Termux
  • your config file: barebones config with user and password

Steps to reproduce

Getting pianobar to compile in the Termux environment was tricky and I didn't document it, so this is my best guess. LMK if I missed anything.

install packages

Use the pkg install command to install:

  • clang
  • ffmpeg-dev
  • json-c-dev
  • libcurl-dev
  • libgcrypt-dev
  • libpulseaudio-dev
  • make
  • pkg-config
  • pulseaudio

install libao

There is no libao package available so I had to build it from source.

  • download the source: curl -o libao-1.2.0.tar.gz https://ftp.osuosl.org/pub/xiph/releases/ao/libao-1.2.0.tar.gz
  • tar -vxzf libao-1.2.0.tar.gz
  • cd libao-1.2.0
  • ./configure --prefix=$PREFIX
  • make
  • make install

edit Makefile

On line 20 of the Make file, change the else block to return this: CC:=gcc -std=c99

make pianobar

(I had to use make b/c gmake isn't listed as a package in Termux)

  • make clean
  • make

start pulseaudio

pulseaudio

run pianobar

pianobar

Expected behaviour

It should play audio after getting station and song.

Actual behaviour

It gives the error message and skips to the next track.

/!\ Cannot open audio device
@tcaddy
Copy link
Author

tcaddy commented Jul 9, 2018

This is the readme for the play-audio application:https://github.com/termux/play-audio/blob/master/README.md

It says it is using OpenSL ES: https://www.khronos.org/opensles/

@tcaddy
Copy link
Author

tcaddy commented Jul 9, 2018

I also followed this troubleshooting for pulseaudio, which worked for playing a .WAV file.

The output of the list-sinks from the pacmd shell might be of interest:

$ pacmd
Welcome to PulseAudio 12.0! Use "help" for usage information.
>>> load-sample test /data/data/com.termux/files/home/test.wav
>>> list-sinks
1 sink(s) available.
  * index: 1
        name: <OpenSL_ES_sink>
        driver: <module-sles-sink.c>
        flags: DECIBEL_VOLUME LATENCY FLAT_VOLUME DYNAMIC_LATENCY
        state: SUSPENDED
        suspend cause: IDLE
        priority: 1000
        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
        volume steps: 65537
        muted: no
        current latency: 0.00 ms
        max request: 344 KiB
        max rewind: 344 KiB
        monitor source: 1
        sample spec: s16le 2ch 44100Hz
        channel map: front-left,front-right
                     Stereo
        used by: 0
        linked by: 0
        configured latency: 0.00 ms; range is 0.50 .. 2000.00 ms
        module: 16
        properties:
                device.description = "OpenSL ES Output"
                device.class = "abstract"
                device.icon_name = "audio-card"
>>> play-sample test OpenSL_ES_sink
Playing on sink input #0
>>> exit

@PromyLOPh
Copy link
Owner

PromyLOPh commented Jul 9, 2018 via email

@tcaddy
Copy link
Author

tcaddy commented Jul 9, 2018

I think the libao was configured for pulseaudio and something that might have been called "null output".

My libao conf file has a different name. I will try to rename it and add the debug option

@tcaddy
Copy link
Author

tcaddy commented Jul 9, 2018

With the debug option, it shows this error on startup:

debug: Loading driver plugins from /data/data/com.termux/files/usr/lib/ao/plugins-4...
ERROR: Failed to load plugin /data/data/com.termux/files/usr/lib/ao/plugins-4/libpulse.so => dlopen() failed

Looks like the problem is with libao.

@edward-p
Copy link
Contributor

edward-p commented Jul 10, 2018

@tcaddy I think we need to port it by using another audio playback library that suitable for Android just like the project "play-audio", rather than using libao and pulse audio.

@PromyLOPh
Copy link
Owner

PromyLOPh commented Jul 10, 2018 via email

@edward-p
Copy link
Contributor

edward-p commented Jul 10, 2018

@PromyLOPh
@tcaddy

It seems that libpulseaudio on termux isn't fully functional now, which can be tracked here Integrating ALSA and Pulseaudio into Termux #821

@tomty89
Copy link

tomty89 commented Jul 10, 2018

It's just dlopen in Android sucks / is broken. Use LD_PRELOAD=$PREFIX/lib/libpulse.so or add -lpulse to LIBAO_LDFLAGS in the Makefile.

$ cat test.c
#include <dlfcn.h>
#include <stdio.h>

int main() {
        void* shared_handle = dlopen("/data/data/com.termux/files/usr/local/lib/ao/plugins-4/libpulse.so", RTLD_GLOBAL | RTLD_NOW);
        if (shared_handle == NULL) {
                printf("dlopen failed: %s\n", dlerror());
                return 1;
        }
}
$ cc test.c
$ ./a.out
dlopen failed: dlopen failed: cannot locate symbol "pa_threaded_mainloop_new" referenced by "/data/data/com.termux/files/usr64/lib/libpulse-simple.so"...
$ LD_PRELOAD=$PREFIX/lib/libpulse.so ./a.out
$ cc test.c -lpulse
$ ./a.out
$

@tcaddy
Copy link
Author

tcaddy commented Jul 11, 2018

So this runs without error, but there is no sound:
LD_PRELOAD=$PREFIX/lib/libpulse.so pianboar

@edward-p
Copy link
Contributor

@tcaddy
I patched configure.ac with LIBAO_LA_LDFLAGS="-lpulse" for building libao , it works fine without LD_PRELOAD=$PREFIX/lib/libpulse.so.
see here Add packages: pianobar, libao #2641

@edward-p
Copy link
Contributor

edward-p commented Jul 11, 2018

@tcaddy
However, it only works when starting pulseaudio manully. No sound if pulseaudio daemon started by libao.

I found that pulseaudio in termux is also started with lots of preloaded libraries.

$ cat $(which pulseaudio)     
#!/data/data/com.termux/files/usr/bin/sh
export LD_PRELOAD=/data/data/com.termux/files/usr/lib/libandroid-glob.so:/data/data/com.termux/files/usr/lib/libpulse.so:/data/data/com.termux/files/usr/lib/libpulsecommon-12.0.so:/data/data/com.termux/files/usr/lib/libpulsecore-12.0.so
LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64:/data/data/com.termux/files/usr/lib exec /data/data/com.termux/files/usr/libexec/pulseaudio $@

So we need a script just like this:

#!/data/data/com.termux/files/usr/bin/sh
export LD_PRELOAD=/data/data/com.termux/files/usr/lib/libandroid-glob.so:/data/data/com.termux/files/usr/lib/libpulse.so:/data/data/com.termux/files/usr/lib/libpulsecommon-12.0.so:/data/data/com.termux/files/usr/lib/libpulsecore-12.0.so
LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64:/data/data/com.termux/files/usr/lib exec /data/data/com.termux/files/usr/libexec/pianobar $@

@tcaddy
Copy link
Author

tcaddy commented Jul 12, 2018

Correct, it works for me when I manually start pulseaudio, too.

This is pretty cool. Thanks for your help!

@edward-p
Copy link
Contributor

edward-p commented Jul 12, 2018

@tcaddy
But there still is an buffer underrun issue with pulseaudio which makes the sound choppy randomly.

@PromyLOPh
Copy link
Owner

PromyLOPh commented Jul 12, 2018 via email

@edward-p
Copy link
Contributor

edward-p commented Jul 16, 2018

@PromyLOPh
I think you're right. I added usleep(20000) after the ao_play function, this issue could be reproduced on my computer. It seems that mobile devices can't do decoding as fast as computer. We need fix it by doing decoding and playing in parallel with a buffer.

@edward-p
Copy link
Contributor

@PromyLOPh I tried putting ao_play function in another thread which created by BarPlayerThread . The BarPlayerThread prepare frames to play and add them into a STAILQ list which defined in <sys/queue.h>. The other thread access the head of the queue each time, play it then remove it from the queue head. Although with some new issues, the sound is fine on my computer. Howerver, I still get the random choppy sounds on termux. This could be pulseaudio-on-termux's fault now.

@PromyLOPh
Copy link
Owner

PromyLOPh commented Jul 20, 2018 via email

@edward-p
Copy link
Contributor

@PromyLOPh here is my changes. It looks not so good, I think. Because I'm not familiar with ffmpeg, I can't rewrite the whole player. I tried cmus on termux which doesn't use libao, it works perfectly.

@PromyLOPh
Copy link
Owner

PromyLOPh commented Jul 27, 2018 via email

@edward-p
Copy link
Contributor

edward-p commented Jul 27, 2018

@PromyLOPh That's an ingenious idea to use external program and pipe. I've learned a lot from you, thanks! Now I think temux users can, somehow, enjoy pianobar on their Android devices. XD 👍
I'll still try my way.

@edward-p
Copy link
Contributor

@PromyLOPh I tried to avoid copying av frames/packets, and adjust the time when AoPlayThread being created. When decoding is done, insert an "EOF" element to the end of the queue and wait for AoPlayThread to quit (when the thread get the "EOF" element from the queue or when shouldQuit(player) becomes true). Now it works quite well on my phone. See https://github.com/edward-p/pianobar
Still, I have no idea how to deal with the CPU time consuming caused by the thread spinning until queue_element!=NULL.
No matter how, the choppy sound issue is already gone :).

@edward-p
Copy link
Contributor

edward-p commented Jul 29, 2018

Now I added force pause feature. Do force pause at start or queue is run out until the queue size get back to 256(may need adjustment) or the decoding is done(EOF element added at the end of the queue) .
See https://github.com/edward-p/pianobar/commit/d21016537191e6b26376e0ea05f16700c7f7d527

This may help with CPU time consumming issue.

@edward-p
Copy link
Contributor

Rebased and sent pull request pull/665.

@edward-p
Copy link
Contributor

Another issue may lead to random choppy sound.

$ termux-audio-info 
{
  "PROPERTY_OUTPUT_SAMPLE_RATE": "48000",
  "PROPERTY_OUTPUT_FRAMES_PER_BUFFER": "192",
  "AUDIOTRACK_SAMPLE_RATE": 48000,
  "AUDIOTRACK_BUFFER_SIZE_IN_FRAMES": 3844,
  "AUDIOTRACK_SAMPLE_RATE_LOW_LATENCY": 48000,
  "AUDIOTRACK_BUFFER_SIZE_IN_FRAMES_LOW_LATENCY": 384,
  "BLUETOOTH_A2DP_IS_ON": false,
  "WIREDHEADSET_IS_CONNECTED": true
}

As you can see AUDIOTRACK_SAMPLE_RATE_LOW_LATENCY=48000, PROPERTY_OUTPUT_SAMPLE_RATE=48000.

But in pulseaudio

$ pulseaudio --dump-conf
...
enable-lfe-remixing = no
lfe-crossover-freq = 0
default-sample-format = s16le
default-sample-rate = 44100
alternate-sample-rate = 48000
default-sample-channels = 2
default-channel-map = front-left,front-right
default-fragments = 4
default-fragment-size-msec = 25
...

The default-sample-rate is set to 44100 by default after pulseaudio installed on Termux.

This cause the audio sample need to be converted to 48000 by the android system's audioserver before output, which waste a lot of CPU resources (I have monitored it by htop)

So it's important to modify the $PREFIX/etc/pulse/daemon.cfg to match the default-sample-rate to your device.

@tomty89
Copy link

tomty89 commented Jul 31, 2018

While setting pulse sink rate to the current optimum is mostly a good thing to do, it shouldn't cause any underrun even if it is on a non-optimal rate (at least not with my recent rewrite of the sink module).

So the sound shouldn't be choppy unless your device is really low end that it literally can't afford Android's (redundant) resampling. You can report back with a pulse log (e.g. pulseaudio -vvv --daemonize=no &> ~/pulse.log &) anyway.

Note that 48kHz is not some universal optimum. The value depends on your (current) audio device and even your Android configuration. So there isn't really an issue in Termux/pulse on the respect.

SLES in Android provides no means for getting the current optimal rate. AAudio does though, while it is only available in Oreo or later. Might port the SLES sink module to an (extra) AAudio one when I have time.

@edward-p
Copy link
Contributor

edward-p commented Jul 31, 2018

I think this could be the problem of LineageOS 15.1 I'm using, which is in development and unofficial build for a sdm845 device. I get the random choppy sound mostly when I'm using my phone(e.g. screen rotated).
Here is the log:
pulse.log
No this issue on my older phone (also LineageOS 15.1).

p.s. pianobar talks to libao instead of talking to pulseaudio directly.

@tomty89
Copy link

tomty89 commented Jul 31, 2018

Actually I think it's a libao issue. Its code for negotiating buffer/latency with pulse doesn't seem to be really proper, while at the same time it requests really little of that (base on a 20ms factor). Normally the configured latency of the sink would prevent that from happening, but with libao's code weird things seem to happen.

There is a workaround, which is to set buffer_time in libao.conf. However libao seems to be broken in parsing its conf as well, in which the option can at max be set to 44, otherwise the value would become enormous somehow.

The reason that the underruns happen more often when a non-optimal rate is used is, Android has a higher buffer/latency requirement with such track. It also gets way higher if you were on Bluetooth audio, where 44 is hardly enough.

Didn't test with pianobar, but could reproduce similar problem with cmus if it outputs to pulse via libao. No problem if it outputs to pulse via libpulse directly.

@edward-p
Copy link
Contributor

edward-p commented Jul 31, 2018

Yeah, I think you're right. Setting buffer_time in libao.conf works. Have you changed the AO_SYSTEM_CONFIG to /data/data/com.termux/files/usr/etc/libao.conf which defined in include/ao/ao_private.h, line 54?

However, libao still may need maintenance for Bluetooth audio, since 44 is hardly enough.

@tomty89
Copy link

tomty89 commented Jul 31, 2018

Nah I was testing with cmus/libao/libpulse in an Arch proot with the help of this trick. Didn't bother to build them under Termux.

This is what happen with buffer_time=44:

I: [pulseaudio] sink-input.c: Created input 0 "libao[cmus] playback stream" on OpenSL_ES_sink with sample spec s16le 2ch 48000Hz and channel map front-left,front-right
I: [pulseaudio] sink-input.c:     media.name = "libao[cmus] playback stream"
I: [pulseaudio] sink-input.c:     application.name = "libao[cmus]"
I: [pulseaudio] sink-input.c:     native-protocol.peer = "UNIX socket client"
I: [pulseaudio] sink-input.c:     native-protocol.version = "32"
I: [pulseaudio] sink-input.c:     application.process.id = "8352"
I: [pulseaudio] sink-input.c:     application.process.user = "tom"
I: [pulseaudio] sink-input.c:     application.process.host = "localhost"
I: [pulseaudio] sink-input.c:     application.process.binary = "cmus"
I: [pulseaudio] sink-input.c:     application.language = "C"
I: [pulseaudio] sink-input.c:     application.process.machine_id = "1e448bfd85d542a4a1290e5ad1baf413"
I: [pulseaudio] sink-input.c:     module-stream-restore.id = "sink-input-by-application-name:libao[cmus]"
I: [pulseaudio] protocol-native.c: Requested tlength=44.00 ms, minreq=11.00 ms
D: [pulseaudio] protocol-native.c: Adjust latency mode enabled, configuring sink latency to half of overall latency.
D: [pulseaudio] protocol-native.c: Requested latency=11.00 ms, Received latency=125.00 ms
D: [pulseaudio] memblockq.c: memblockq requested: maxlength=10560, tlength=28224, base=4, prebuf=26116, minreq=2112 maxrewind=0
D: [pulseaudio] memblockq.c: memblockq sanitized: maxlength=10560, tlength=10560, base=4, prebuf=8452, minreq=2112 maxrewind=0
I: [pulseaudio] protocol-native.c: Final latency 180.00 ms = 33.00 ms + 2*11.00 ms + 125.00 ms

This is what happen with buffer_time=45:

I: [pulseaudio] sink-input.c: Created input 0 "libao[cmus] playback stream" on OpenSL_ES_sink with sample spec s16le 2ch 48000Hz and channel map front-left,front-right
I: [pulseaudio] sink-input.c:     media.name = "libao[cmus] playback stream"
I: [pulseaudio] sink-input.c:     application.name = "libao[cmus]"
I: [pulseaudio] sink-input.c:     native-protocol.peer = "UNIX socket client"
I: [pulseaudio] sink-input.c:     native-protocol.version = "32"
I: [pulseaudio] sink-input.c:     application.process.id = "8393"
I: [pulseaudio] sink-input.c:     application.process.user = "tom"
I: [pulseaudio] sink-input.c:     application.process.host = "localhost"
I: [pulseaudio] sink-input.c:     application.process.binary = "cmus"
I: [pulseaudio] sink-input.c:     application.language = "C"
I: [pulseaudio] sink-input.c:     application.process.machine_id = "1e448bfd85d542a4a1290e5ad1baf413"
I: [pulseaudio] sink-input.c:     module-stream-restore.id = "sink-input-by-application-name:libao[cmus]"
I: [pulseaudio] protocol-native.c: Requested tlength=5592394.23 ms, minreq=5592394.21 ms
D: [pulseaudio] protocol-native.c: Adjust latency mode enabled, configuring sink latency to half of overall latency.
D: [pulseaudio] protocol-native.c: Requested latency=0.00 ms, Received latency=125.00 ms
D: [pulseaudio] memblockq.c: memblockq requested: maxlength=4194304, tlength=2147503376, base=4, prebuf=1073763690, minreq=1073739690 maxrewind=0
D: [pulseaudio] memblockq.c: memblockq sanitized: maxlength=4194304, tlength=4194304, base=4, prebuf=4, minreq=4194304 maxrewind=0
I: [pulseaudio] protocol-native.c: Final latency 21970.33 ms = 22347776.00 ms + 2*21845.33 ms + 125.00 ms

@edward-p
Copy link
Contributor

Only two underruns get each song at the start with buffer_time=44 I think this is acceptable.

I: [pulseaudio] sink-input.c: Created input 0 "libao[pianobar] playback stream" on OpenSL_ES_sink with sample spec s16le 2ch 44100Hz and channel map front-left,front-right
I: [pulseaudio] sink-input.c:     media.name = "libao[pianobar] playback stream"
I: [pulseaudio] sink-input.c:     application.name = "libao[pianobar]"
I: [pulseaudio] sink-input.c:     native-protocol.peer = "UNIX socket client"
I: [pulseaudio] sink-input.c:     native-protocol.version = "32"
I: [pulseaudio] sink-input.c:     application.process.id = "11359"
I: [pulseaudio] sink-input.c:     application.process.user = "u0_a118"
I: [pulseaudio] sink-input.c:     application.process.host = "localhost"
I: [pulseaudio] sink-input.c:     application.process.binary = "pianobar"
I: [pulseaudio] sink-input.c:     application.process.machine_id = "localhost"
I: [pulseaudio] sink-input.c:     module-stream-restore.id = "sink-input-by-application-name:libao[pianobar]"
I: [pulseaudio] protocol-native.c: Requested tlength=43.99 ms, minreq=11.00 ms
D: [pulseaudio] protocol-native.c: Adjust latency mode enabled, configuring sink latency to half of overall latency.
D: [pulseaudio] protocol-native.c: Requested latency=11.00 ms, Received latency=125.00 ms
D: [pulseaudio] memblockq.c: memblockq requested: maxlength=9700, tlength=25932, base=4, prebuf=23996, minreq=1940 maxrewind=0
D: [pulseaudio] memblockq.c: memblockq sanitized: maxlength=9700, tlength=9700, base=4, prebuf=7764, minreq=1940 maxrewind=0
I: [pulseaudio] protocol-native.c: Final latency 179.99 ms = 32.99 ms + 2*11.00 ms + 125.00 ms
D: [sles-sink] protocol-native.c: max_request changed, trying to update from 9700 to 25932.
D: [sles-sink] protocol-native.c: Failed to increase tlength
D: [pulseaudio] sink.c: OpenSL_ES_sink: state: IDLE -> RUNNING
D: [pulseaudio] core-subscribe.c: Dropped redundant event due to change event.
D: [sles-sink] protocol-native.c: Requesting rewind due to end of underrun.
D: [sles-sink] protocol-native.c: Requesting rewind due to end of underrun.

No underrun get during the song is playing. I haven't tested with Bluetooth audio yet.

@tomty89
Copy link

tomty89 commented Aug 1, 2018

@edward-p Okay I found and fixed problem. You can find the patches here: xiph/libao#8

@edward-p
Copy link
Contributor

edward-p commented Aug 1, 2018

@tomty89 That's great, thanks! I will apply the first when building libao for termux.

@PromyLOPh
Copy link
Owner

Can either of you check whether the latest version of patch #665 fixes the underrun issue reported here?

@edward-p
Copy link
Contributor

I've tested it with xiph/libao#8, works well on both of my Nexus 6P and Oneplus 6 in termux. I also tested it in WSL with a pulseaudio windows port sinking on local tcp, works fine too:)

@iamdarkmatter
Copy link

Works pretty good. Sometimes you have to force stop the APP if it doesn't recognize the device and keeps playing. Keeps running during phone calls. Great work guys! Big fan promy. Pianobar has definitely enriched my life. I read the man pages for pianobar but I do not see where I can store my creds for the mobile version. It is amazing what you guys have done I am still learning about packages on mobile.

@edward-p
Copy link
Contributor

@iamlord

Termux has its own HOME variable just like all Unix-Likes.
So you just create a $HOME/.config/pianobar/config like this:

password = your-password
user = yourname@example.com

Then pianobar can parse it and login automatically.

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

5 participants