September 7th 2017

Android: Internet Radio App: Transistor

I'm not the one to promote android apps usually, but here is a nice that does exactly what I desire, and follows some sort of Unix philosophy.

Internet Radio - audio streaming - works nicely on all my linux machines:

  • get a playlist or create a playlist from one or two streaming links
  • save it to some folder
  • open it with my favorite lightweight audio player (MOC here)

But this approach has always been a bit of a problem on app-centric Android, and my particular phone:

  • Standard media players won't open remote streams. Why, is beyond me. Some android/java quirk, who knows what's going on under the hood...
  • Many Radio/streaming apps exist, but they all seem to insist on finding my radio stations for me, and usually default to 128kbps mp3 streams. Why, when i can get 64kbps AAC which gives equal quality with half the bandwidth? Maybe android can't stream AAC?
  • Playing straight from the browser is cumbersome, and usually the browser does not continue playing in the background, i.e. when the phone is dark and in my pocket.

This results in

  • buffering problems
  • phone going to sleep during playback
  • dropouts (probably intiated server-side)

Why is this so difficult even with dedicated radio apps? Maybe because I'm using a mobile broadband flatrate, and no wifi. unfortunately my phone does not have 4G, only 3G, and the throughput can be patchy. i use it while cycling, so moving from cell to cell constantly.
But it seems many apps assume that you don't use mobile broadband, instead constantly surf wifi networks, because everybody has a mobile data plan that is already used up for this month?
...really i just don't know. Android will never have the sort of transparency for me that gnu/linux has.

Enter Transistor:

Transistor is a bare bones app for listening to radio programs over the internet. The app stores stations as files on your device's external storage. It currently understands streams encoded in MP3, AAC and Ogg/Opus(*).
Important note: This is an app of type BYOS ("bring your own station"). It does not feature any kind of built-in search option. You will have to manually add radio stations.
During Playback Transistor acquires a so called partial wake lock. That prevents the Android system to stop playback for power saving reasons.

what? AAC streaming? Why didn't I see this earlier? It's been around for a while. and it sounds like it's born from a unix philosophy: plain text files and folders, not intents and databases!

Long story short, it works! Been using it for a few days with somafm's 64kbps streams, and it's definitely much better. no more going to sleep, almost no dropouts, and very reasonable sound quality.

The UI is simple, usable. Just a list of stations, and once you click on one, a play/stop button. if the station supports it, it also shows currently playing song.

Small caveat: it wants .M3U playlists.

So, here's a gratuitous little conversion script that will convert a PLS playlist to m3u and push the result to an android device of your choice:


# convert (somafm's) pls playlists to m3u playlists.  
# a list of files as arguments.  
# output stored in temp folder in current dir.  

folder="$me$(date +%s)"  
if ! mkdir -p "$folder"; then   
    echo "Could not create output folder"  
    exit 1  
trap "rmdir $folder 2>/dev/null 1>&2" EXIT  

grep="$(which grep 2>/dev/null)"  
[[ "$grep" == "" ]] && echo "grep not found in PATH. Can't continue." && exit 1  

# for pushing .m3us to android device with adb  
# if you don't want to use this, better comment it out completely  
# because adb takes a while to fire up.  
device="XXXXXXXXXXX" # find out your device number with 'adb devices'  
adb="$(which adb 2>/dev/null)"  
if [[ "$adb" == "" ]]; then  
    echo "adb not found in PATH."  
    if ! $adb devices | $grep "$device"; then  
        echo "adb: device $device not found."  

for input in $@; do  
    if [ -r "$input" ] && [[ "${input,,}" = *.pls ]]; then  
        file=( $($grep -E "File.?=" "$input") )  
        title=( $($grep -E "Title.?=" "$input") )  
        length=( $($grep -E "Length.?=" "$input") )  

        echo "Converting $input to $output"  
        echo -e "#EXTM3U" > "$output"  
        for (( i=0 ; i<${#file[@]}; i++ )); do  
            # the file (url) is the most important bit - if that's missing, skip to the next array member  
            if [[ "${file*}" != "" ]]; then  
                title*="${title*#*: }"  
                title*="${title*/ (*)/}"  
                # if title is empty after cutting out some stuff, replace it with the file name (url)  
                [[ "${title*}" == "" ]] && title*="${file*#http*://}"  
                length*="${length*#*=}" # if length is empty, make it -1  
                [[ "${length*}" == "" ]] && length*="-1"  
                echo -e "#EXTINF:${length*},${title*}\n${file*}" >> "$output"  
        [[ "$adb" != "" ]] && $adb push "$output" "$pushpath"  
        echo "File $input does not end in .pls or is not readable - skipping."  

exit 0

save to ~/bin, make executable.
if you want adb push functionality, replace the device="XXXXXXXX" string with your device number (get it with 'adb devices').

Let's say you have all somafm stations in one folder - and you want 64kbps stations only on your phone, like me - then connect your phone, and run the script like this:

pls2m3upush *64.pls

let me know how it goes, i have only one phone for testing.