87 lines
3.3 KiB
Bash
Executable File
87 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Plays a sound.
|
|
#
|
|
# Options (set on the effect in rules.json):
|
|
# sound message | bell | warning | complete | phone (default: message)
|
|
# duration fit all repeats into this many seconds; 0 uses natural speed
|
|
# intensity volume, 0.0 .. 1.0, default 1 (`volume` works too)
|
|
# repeat how many times to play it, default 1 (`speed` works for old rules)
|
|
set -uo pipefail
|
|
|
|
case ${AR_OPT_SOUND:-message} in
|
|
bell) file=/usr/share/sounds/freedesktop/stereo/bell.oga ;;
|
|
warning) file=/usr/share/sounds/freedesktop/stereo/dialog-warning.oga ;;
|
|
complete) file=/usr/share/sounds/freedesktop/stereo/complete.oga ;;
|
|
phone) file=/usr/share/sounds/freedesktop/stereo/phone-incoming-call.oga ;;
|
|
*) file=/usr/share/sounds/freedesktop/stereo/message-new-instant.oga ;;
|
|
esac
|
|
volume=${AR_OPT_INTENSITY:-${AR_OPT_VOLUME:-1}}
|
|
times=${AR_OPT_REPEAT:-}
|
|
[[ $times =~ ^[0-9]+$ ]] || times=${AR_OPT_SPEED:-1}
|
|
duration=${AR_OPT_DURATION:-0}
|
|
[[ $volume =~ ^[0-9]*\.?[0-9]+$ ]] || volume=1
|
|
[[ $times =~ ^[0-9]+$ ]] || times=1
|
|
(( times > 50 )) && times=50
|
|
file=${file/#\~/$HOME}
|
|
|
|
[[ -r $file ]] || { echo "sound: cannot read $file" >&2; exit 1; }
|
|
|
|
play() {
|
|
if command -v pw-play >/dev/null 2>&1; then
|
|
pw-play --volume="$volume" "$file"
|
|
elif command -v paplay >/dev/null 2>&1; then
|
|
paplay "$file"
|
|
elif command -v mpv >/dev/null 2>&1; then
|
|
mpv --no-video --really-quiet --volume="$(awk -v v="$volume" 'BEGIN { printf "%d", v * 100 }')" "$file"
|
|
else
|
|
echo "sound: no player found (pw-play, paplay or mpv)" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
play_repeatedly() {
|
|
for ((i = 0; i < times; i++)); do
|
|
play || return 1
|
|
done
|
|
}
|
|
|
|
# ffmpeg changes the tempo of each chime so all repeats fit the chosen duration.
|
|
# Each repetition gets its own playback stream, keeping every hit distinct.
|
|
fit_duration() {
|
|
command -v ffprobe >/dev/null 2>&1 || { echo "sound: ffprobe is required when Fit in is set" >&2; return 1; }
|
|
command -v ffmpeg >/dev/null 2>&1 || { echo "sound: ffmpeg is required when Fit in is set" >&2; return 1; }
|
|
command -v pw-play >/dev/null 2>&1 || { echo "sound: pw-play is required when Fit in is set" >&2; return 1; }
|
|
|
|
local clip total tempo filter
|
|
clip=$(ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$file" 2>/dev/null) || return 1
|
|
[[ $clip =~ ^[0-9]*\.?[0-9]+$ ]] || return 1
|
|
total=$(awk -v c="$clip" -v n="$times" 'BEGIN { printf "%.8f", c * n }')
|
|
tempo=$(awk -v t="$total" -v d="$duration" 'BEGIN { printf "%.8f", t / d }')
|
|
[[ $tempo =~ ^[0-9]*\.?[0-9]+$ ]] || return 1
|
|
|
|
# atempo accepts one factor from 0.5 to 2. Chain factors for every value the
|
|
# settings slider permits, preserving pitch while changing playback speed.
|
|
filter=
|
|
while awk -v v="$tempo" 'BEGIN { exit !(v > 2) }'; do
|
|
filter+="atempo=2,"
|
|
tempo=$(awk -v v="$tempo" 'BEGIN { printf "%.8f", v / 2 }')
|
|
done
|
|
while awk -v v="$tempo" 'BEGIN { exit !(v < 0.5) }'; do
|
|
filter+="atempo=0.5,"
|
|
tempo=$(awk -v v="$tempo" 'BEGIN { printf "%.8f", v * 2 }')
|
|
done
|
|
filter+="atempo=$tempo"
|
|
|
|
for ((i = 0; i < times; i++)); do
|
|
ffmpeg -nostdin -v error -i "$file" -filter:a "$filter" -f wav - \
|
|
| pw-play --volume="$volume" - || return 1
|
|
done
|
|
}
|
|
|
|
if [[ $duration =~ ^[0-9]*\.?[0-9]+$ ]] && awk -v d="$duration" 'BEGIN { exit !(d > 0) }'; then
|
|
fit_duration || exit $?
|
|
else
|
|
play_repeatedly
|
|
fi
|