Open tcaddy opened 6 years ago
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/
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
Can you recall which audio backends were enabled by libao’s ./configure?
It might be helpful to switch on libao’s debug output by adding
debug=1
to ~/.libao
. You can force the pulseaudio driver by
adding default_driver=pulse
to the same file.
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
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.
@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.
@tcaddy: I assume libpulse.so does not exist? @edward-p: Writing a plugin for libao would be the better solution imo. It’s really not that difficult and every other projects using libao benefits. Edit: Also I probably won’t accept pull requests adding support for other audio libraries, unless they are universal and replace libao on all supported platforms (i.e. no Android-specific solution).
@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
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
$
So this runs without error, but there is no sound:
LD_PRELOAD=$PREFIX/lib/libpulse.so pianboar
@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
@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 $@
Correct, it works for me when I manually start pulseaudio, too.
This is pretty cool. Thanks for your help!
@tcaddy But there still is an buffer underrun issue with pulseaudio which makes the sound choppy randomly.
Actually, this could be pianobar’s fault. If you look at the player code you will find it decodes chunks of data, then passes them to libao and only after libao returned, decodes the next chunk. It should do this in parallel.
@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.
@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.
Can you share your code? Does the problem exist with other programs that use pulseaudio on termux and don’t use libao (paplay for example)?
@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.
Well, it’s not too bad. Copying av frames/packets is obviously not the most efficient thing to do, but that should’t be the problem. Also the new AoPlayThread spins until queue_element!=NULL, which can consume a lot of CPU time. Still, in theory it should fix the issue.
A different approach would be to offload playback to an external program entirely. I already tried that, see https://github.com/PromyLOPh/pianobar/tree/externalplay and – except for a few unsolved issue – it works quite well. Unfortunately that branch is very outdated and not easily portable to master.
@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 :+1:
I'll still try my way.
@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 :).
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.
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.
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.
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.
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.
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.
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
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.
@edward-p Okay I found and fixed problem. You can find the patches here: https://github.com/xiph/libao/pull/8
@tomty89 That's great, thanks! I will apply the first when building libao for termux.
Can either of you check whether the latest version of patch #665 fixes the underrun issue reported here?
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:)
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.
@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.
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:
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:ffmpeg/libav version and the flags it was compiled with (if you compiled yourself): ffmpeg -v 4.0.1 installed via
pkg
app in Termuxyour 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:install libao
There is no
libao
package available so I had to build it from 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/cgmake
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.