#!/usr/bin/env bash
#
# SPDX-License-Identifier: GPL-3.0-or-later

usage() {
    cat <<- _EOF_
    live cd sound helper script.
    Usage: livecdsound [OPTION]
    OPTIONS
        -u, --unmute      unmute all sound cards
        -p, --pick        select a card for  speetch output
        -h, --help        Show this usage message

_EOF_
}

bugout () {
    printf "/usr/local/bin/livecdsound: programming error"
    stat_fail
}

echo_card_indices()
{
    if [ -f /proc/asound/cards ] ; then
        sed -n -e's/^[[:space:]]*\([0-7]\)[[:space:]].*/\1/p' /proc/asound/cards
    fi
}

# The following functions try to set many controls.
# No card has all the controls and so some of the attempts are bound to fail.
# Because of this, the functions can't return useful status values.

# $1 <card id>
# $2 <control>
# $3 <level>
unmute_and_set_level(){
    { [ "$3" ] &&[ "$2" ] && [ "$1" ] ; } || bugout
    systemd-cat -t "livecdsound" printf "Setting: %s on card: %s to %s\n" "$2" "$1" "$3"
    systemd-cat -t "livecdsound" amixer -c "$1" set "$2" "$3" unmute
    return 0
}

# $1 <card id>
# $2 <control>
mute_and_zero_level()
{
    { [ "$1" ] && [ "$2" ] ; } || bugout
    systemd-cat -t "livecdsound" printf "Muting control: %s on card: %s\n" "$2" "$1"
    systemd-cat -t "livecdsound" amixer -c "$1" set "$2" "0%" mute
    return 0
}

# $1 <card ID>
# $2 <control>
# $3 "on" | "off"
switch_control()
{
    { [ "$3" ] && [ "$1" ] ; } || bugout
    systemd-cat -t "livecdsound" printf "Switching control: %s on card: %s to %s\n" "$2" "$1" "$3"
    systemd-cat -t "livecdsound" amixer -c "$1" set "$2" "$3"
    return 0
}

# $1 <card ID>
sanify_levels_on_card()
{
    unmute_and_set_level "$1" "Front" "80%"
    unmute_and_set_level "$1" "Master" "80%"
    unmute_and_set_level "$1" "Master Mono" "80%"
    unmute_and_set_level "$1" "Master Digital" "80%"   # E.g., cs4237B
    unmute_and_set_level "$1" "Playback" "80%"
    unmute_and_set_level "$1" "Headphone" "100%"
    unmute_and_set_level "$1" "PCM" "80%"
    unmute_and_set_level "$1" "PCM,1" "80%"   # E.g., ess1969
    unmute_and_set_level "$1" "DAC" "80%"     # E.g., envy24, cs46xx
    unmute_and_set_level "$1" "DAC,0" "80%"   # E.g., envy24
    unmute_and_set_level "$1" "DAC,1" "80%"   # E.g., envy24
    unmute_and_set_level "$1" "Synth" "80%"
    unmute_and_set_level "$1" "CD" "80%"
    unmute_and_set_level "$1" "PC Speaker" "100%"

    mute_and_zero_level "$1" "Mic"
    mute_and_zero_level "$1" "IEC958"         # Ubuntu #19648

    # Intel P4P800-MX
    switch_control "$1" "Master Playback Switch" on
    switch_control "$1" "Master Surround" on

    # Trident/YMFPCI/emu10k1:
    unmute_and_set_level "$1" "Wave" "80%"
    unmute_and_set_level "$1" "Music" "80%"
    unmute_and_set_level "$1" "AC97" "80%"

    # DRC:
    unmute_and_set_level "$1" "Dynamic Range Compression" "80%"

    # Required for HDA Intel (hda-intel):
    unmute_and_set_level "$1" "Front" "80%"

    # Required for SB Live 7.1/24-bit (ca0106):
    unmute_and_set_level "$1" "Analog Front" "80%"

    # Required at least for Via 823x hardware on DFI K8M800-MLVF Motherboard
    switch_control "$1" "IEC958 Capture Monitor" off

    # Required for hardware allowing toggles for AC97 through IEC958,
    #  valid values are 0, 1, 2, 3. Needs to be set to 0 for PCM1.
    unmute_and_set_level "$1" "IEC958 Playback AC97-SPSA" "0"

    # Required for newer Via hardware
    unmute_and_set_level "$1" "VIA DXS,0" "80%"
    unmute_and_set_level "$1" "VIA DXS,1" "80%"
    unmute_and_set_level "$1" "VIA DXS,2" "80%"
    unmute_and_set_level "$1" "VIA DXS,3" "80%"

    # Required on some notebooks with ICH4:
    switch_control "$1" "Headphone Jack Sense" off
    switch_control "$1" "Line Jack Sense" off

    # Some machines need one or more of these to be on;
    # others need one or more of these to be off:

    switch_control "$1" "Audigy Analog/Digital Output Jack" on
    switch_control "$1" "SB Live Analog/Digital Output Jack" on

    # D1984 -- Thinkpad T61/X61
    switch_control "$1" "Speaker" on
    switch_control "$1" "Headphone" on

    # HDA-Intel w/ "Digital" capture mixer (See Ubuntu #193823)
    unmute_and_set_level "$1" "Digital" "80%"

    return 0
}

# $1 <card ID> | "all"
sanify_levels()
{
    local ttsdml_returnstatus=0
    local card
    case "$1" in
              all)
        for card in $(echo_card_indices) ; do
            sanify_levels_on_card "$card" || ttsdml_returnstatus=1
        done
        ;;
      *)
        sanify_levels_on_card "$1" || ttsdml_returnstatus=1
        ;;
    esac
    return $ttsdml_returnstatus
}

# List all cards that *should* be usable for PCM audio.  In my experience,
# the console speaker (handled by the pcsp driver) isn't a suitable playback
# device, so we'll exclude it.
list_non_pcsp_cards()
{
    for card in $(echo_card_indices); do
        local cardfile="/proc/asound/card${card}/id"
        if [ -r "$cardfile" ] && [ -f "$cardfile" ] && \
            [ "$(cat "$cardfile")" != pcsp ]; then
            echo "$card"
        fi
    done
}

# Properly initialize the sound card so that we have audio at boot.
unmute_all_cards()
{
    sanify_levels all
}

is_numeric() {
    local str=$1
    [[ "$str" =~ ^[0-9]+$ ]]
}

set_default_card() {
    local card=$1
    sed -e "s/%card%/$card/g" < /usr/local/share/livecd-sound/asound.conf.in \
        > /etc/asound.conf
}

play_on_card() {
    local card=$1 file=$2
    aplay -q "-Dplughw:$card,0" "$file"
}

# If there are multiple usable sound cards, prompt the user to choose one,
# using auditory feedback.
pick_a_card()
{
    set -f
    usable_cards="$(list_non_pcsp_cards)"
    num_usable_cards="$(wc -w <<< "$usable_cards")"

    if [ "$num_usable_cards" -eq 1 ]; then
        systemd-cat -t "livecdsound" printf "Only one sound card is detected\n"
        exit 0
    fi
    systemd-cat -t "livecdsound" printf "multiple sound cards detected\n"
    for card in $usable_cards; do
        if ! is_numeric "$card"; then
            continue
        fi
        play_on_card "$card" /usr/share/livecd-sounds/pick-a-card.wav&
    done
    wait
    sleep 1
    for card in $usable_cards; do
        if ! is_numeric "$card"; then
            continue
    fi
    play_on_card "$card" /usr/share/livecd-sounds/beep.wav
    if read -r -t 10; then
        systemd-cat -t "livecdsound" printf "Selecting %s sound card as default\n" "$card"
        set_default_card "$card"
        break
    fi
done
}

if [[  $# -eq 0 ]]; then
    echo "error: No argument passed."
    exit 1
fi
while [[ "${1}" != "" ]]; do
    case ${1} in
        -h|--help)
            usage
            exit
            ;;
        -u|--unmute)
            systemd-cat -t "livecdsound" printf "Unmuting all cards"
            unmute_all_cards
            ;;
        -p|--pick)
            pick_a_card
            ;;
        *)
            echo "error: Unsupported argument"
            usage
            exit 1
            ;;
    esac
    shift
done
