#!/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
