#!/bin/sh # RADISH # a new and improved RADIO that can play local files and noise. # XXX: WIP usage() { cat <&2 "Option -$OPTARG requires an argument" usage 1 ;; esac ;; \?) echo >&2 "Unknown option: -$OPTARG" usage 1 ;; esac done shift "$((OPTIND - 1))" radish_play "$@" } cleanup() { rm /tmp/radish.* } ### "Private" functions _radish_player() { command -v "${1:-}" || command -v "${RADISH_PLAYER:-}" || command -v mpv || command -v vlc || { echo >&2 "No suitable player found." echo >&2 "Set \$RADISH_PLAYER." exit 3 } } _radish_stations() { if [ -f "$RADISH_STATION_FILE" ]; then sed -e '/^#/d' "$RADISH_STATION_FILE" else echo noise: fi } _radish_kill_impl() { [ -f "$RADISH_PID_FILE" ] || return 1 x= while xargs -a "$RADISH_PID_FILE" kill 2>/dev/null; do case "$x" in xxx*) printf . x= ;; *) x=x$x ;; esac done echo } _radish_desc() { cut -f2 "${1:--}" 2>/dev/null } _radish_url() { cut -f1 "${1:--}" 2>/dev/null } _radish_tags() { cut -f3 "${1:--}" 2>/dev/null } _radish_not_running() { echo >&2 "Running radish not found." exit 2 } echo() { printf '%s\n' "$*"; } ### Main functionality radish_kill() { if [ -f "$RADISH_PID_FILE" ]; then printf >&2 '%s' "Killing radish..." _radish_kill_impl else _radish_not_running fi cleanup exit } radish_status() { trap 'echo;exit 0' INT if [ -n "${1:-}" ]; then follow=-f else follow= fi tail $follow "$RADISH_STATUS_FILE" 2>/dev/null || _radish_not_running echo exit } radish_list() { _radish_stations | grep -i "${1:-}" | awk -F '\t' '{ desc = $'$_schema_desc' url = $'$_schema_url' tags = $'$_schema_tags' printf "%-23s |", substr(desc,1,20) (length(desc)>20?"...":"") printf "%-23s |", substr(tags,1,20) (length(tags)>20?"...":"") printf "%-23s\n", substr(url,1,20) (length(url)>20?"...":"") }' exit } radish_choose_station() { cands="$(_radish_stations | grep -i "$1")" [ -z "$cands" ] && cands="$(_radish_stations)" if [ "$(echo "$cands" | wc -l)" -gt 1 ]; then echo "$cands" | _radish_desc | cat -n - while true; do printf "Radish> " read station if (echo "$station" | grep -qE '^[0-9]+$'); then station="$(echo "$cands" | sed -n "${station}p;${station}q")" if [ -z "$station" ]; then echo >&2 "No station with that number." continue else break fi fi echo >&2 "Please input a station number." done else station="$(echo "$cands")" fi echo >&2 "Selected: $(echo "$station" | _radish_desc)" station="$(echo "$station" | _radish_url)" } ### Player functions radish_play() { case "${1:-}" in https:* | http:*) _radish_kill_impl || : name="$(grep "$1" "$RADISH_STATION_FILE" | cut -f2)" echo >&2 "Streaming ${name:-$1}..." radish_play_web "$1" ;; noise:*) _radish_kill_impl || : echo >&2 "Playing noise: ${1#noise:}" radish_play_noise "${1:-}" ;; shuf:*) _radish_kill_impl || : echo >&2 "Shuffling from ${1#shuf:}" SHUF=1 radish_play_file "$1" 2>&1 ;; file:*) _radish_kill_impl || : echo >&2 "Playing from ${1#file:}" SHUF=0 radish_play_file "$1" 2>&1 ;; *) if [ -f "$RADISH_STATION_FILE" ]; then radish_choose_station "${1:-}" _radish_kill_impl || : radish_play "$station" else _radish_kill_impl || : radish_play_noise fi ;; esac echo "${1:-}" | _radish_url >"$RADISH_LP_FILE" exit } radish_play_web() { "$(_radish_player)" "$1" >"$RADISH_STATUS_FILE" 2>&1 & echo $! >"$RADISH_PID_FILE" } radish_play_file() { dir="$(echo "$1" | sed 's@^[^:]*:\(//\)\?@@')" find "$dir" -depth -print0 \ -iname '*flac' -o \ -iname '*mp3' -o \ -iname '*ogg' -o \ -iname '*opus' | xargs -0 realpath | case "$SHUF" in 1) shuf ;; 0) sort ;; esac >/tmp/radish.m3u player="$(_radish_player)" case "$player" in *vlc | *mpv) args="--no-video" ;; *) args= ;; esac "$player" $args /tmp/radish.m3u "$1" >"$RADISH_STATUS_FILE" 2>&1 & echo $! >"$RADISH_PID_FILE" } radish_play_noise() { # REQUIRES PLAY (from SOX) -- based on https://gist.github.com/rsvp/1209835 play="$(command -v play)" || { echo >&2 "Noise playback requires sox(1)." exit 3 } # URL format: noise:type/center?time=60;wave=0.033;volume=1 url_format='noise:\([^/]\+\)/\([^?]\+\)?\(.*\)' noise_type="$(echo "${1:-}" | sed -n "s@${url_format}@\1@")" noise_center="$(echo "${1:-}" | sed -n "s@${url_format}@\2@")" noise_params="$(echo "${1:-}" | sed -n "s@${url_format}@\3@")" time=60 wave=0.03333333 volume=1 if [ -z "$noise_type" ] && [ -z "$noise_center" ]; then noise_type=brown noise_center=1786 elif [ -z "$noise_type" ] || [ -z "$noise_center" ]; then echo >&2 "URL format: 'noise:TYPE/CENTER?[PARAMS]" echo >&2 "TYPE is one of 'brown','white','pink','tpdf'." echo >&2 "CENTER is the center of the band-pass filter." echo >&2 "PARAMS are TIME to generate noise;" echo >&2 "WAVE, denoting volume variation; and VOLUME." exit 4 else eval "$noise_params" fi export RADISH_PLAYER=play echo >&2 "$(_radish_player)" \ -c 2 --null synth "$time" "${noise_type}noise" \ band -n "$noise_center" 499 \ tremolo "$wave" 43 reverb 19 \ bass -11 treble -1 \ vol 14dB vol "$volume" \ repeat "$(expr $time - 1)" "$(_radish_player)" \ -c 2 --null synth "$time" "${noise_type}noise" \ band -n "$noise_center" 499 \ tremolo "$wave" 43 reverb 19 \ bass -11 treble -1 \ vol 14dB vol "$volume" \ repeat "$(expr $time - 1)" >"$RADISH_STATUS_FILE" 2>&1 & echo $! >"$RADISH_PID_FILE" } main "$@"