From 87dbe5e3a155329cde9fe54f9bc8f1fd354e335e Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Wed, 20 Jul 2022 14:46:35 -0500 Subject: Initial commit (well, sort of) --- COPYING | 7 ++ Makefile | 41 ++++++++ radish | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ radish.stations | 15 +++ 4 files changed, 361 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100755 radish create mode 100644 radish.stations diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..4d13a5b --- /dev/null +++ b/COPYING @@ -0,0 +1,7 @@ +Copyright (C) 2022 Case Duckworth + +Usage of the works is permitted provided that this instrument is retained with +the works, so that any entity that uses the works is notified of this +instrument. + +DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2b2656d --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +# Radish + +DESTDIR = +PREFIX = /usr/local + +BIN = $(DESTDIR)$(PREFIX)/bin +RADISH_SHARE = $(DESTDIR)$(PREFIX)/share/radish + +RADISH_BIN = $(BIN)/radish +RADISH_STATIONS = $(RADISH_SHARE)/stations + +.PHONY: help +help: + @echo "RADISH : Play online radio" + @echo "(C) 2022 Case Duckworth " + @echo "Licensed under the Fair License; see COPYING for details." + @echo + @echo "TARGETS:" + @echo " install Install radish to $(DESTDIR)$(PREFIX)/bin/radish." + @echo " An example configuration is at $(DESTDIR)$(PREFIX)/share/radish/stations." + @echo " link Install radish using symlinks." + @echo " Probably only useful for development." + @echo " uninstall Uninstall radish-related files." + +$(BIN) $(RADISH_SHARE): + mkdir -p $@ + +.PHONY: install +install: radish radish.stations $(BIN) $(RADISH_SHARE) + install -D radish $(RADISH_BIN) + install -D radish.stations $(RADISH_STATIONS) + +.PHONY: link +link: $(BIN) $(RADISH_SHARE) + ln -sf $(PWD)/radish $(RADISH_BIN) + ln -sf $(PWD)/radish.stations $(RADISH_STATIONS) + +.PHONY: uninstall +uninstall: + rm $(RADISH_BIN) + rm -r $(RADISH_SHARE) diff --git a/radish b/radish new file mode 100755 index 0000000..d8c55b1 --- /dev/null +++ b/radish @@ -0,0 +1,298 @@ +#!/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 +} + +echo() { printf '%s\n' "$*"; } + +### Main functionality + +radish_kill() { + printf >&2 '%s' "Killing radish..." + _radish_kill_impl || { + echo >&2 "I don't think radish is running." + exit 2 + } + cleanup + exit +} + +radish_status() { + trap 'echo;exit 0' INT + if [ -n "${1:-}" ]; then + follow=-f + else + follow= + fi + tail $follow "$RADISH_STATUS_FILE" + 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 || : + echo >&2 "Streaming $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 "$@" diff --git a/radish.stations b/radish.stations new file mode 100644 index 0000000..7ff60ef --- /dev/null +++ b/radish.stations @@ -0,0 +1,15 @@ +URL Description Tags +https://somafm.com/synphaera256.pls Synphaera soma +https://somafm.com/bagel.pls BAGel Radio soma +https://somafm.com/bootliquor320.pls Boot Liquor soma +https://somafm.com/deepspaceone.pls Deep Space One soma +https://somafm.com/fluid.pls Fluid soma +https://somafm.com/u80s256.pls Underground 80s soma +https://azuracast.tilderadio.org/radio/8000/radio.ogg tilderadio friends +https://s2.radio.co/s2b2b68744/listen BadRadio: 24/7 PHONK phonk +http://radio.plaza.one/opus Nightwave Plaza vaporwave +https://www.scenesat.com/listen/normal/mid.m3u SceneSat demoscene +http://nectarine.ers35.net:8000/necta192.mp3 Nectarine demoscene +https://kexp-mp3-128.streamguys1.com/kexp128.mp3 KEXP fm college +https://rainwave.cc/tune_in/5.ogg.m3u Rainwave chiptune videogame +noise: brown noise noise -- cgit 1.4.1-21-gabe81