#!/bin/bash

# audio2videoWaveformNG

# Make head moving over waveform video for any audio file, 
# overlay over image that must be in the same fodler as audio file, named
# like the audio file in question (takes higher order) or generic name like cover.png

# usage example:
#   audio2videoWaveformNG file.flac

# required
command -v ffmpeg >/dev/null 2>&1 || { >&2 echo "I need ffmpeg" ; exit 1; }
command -v ffprobe >/dev/null 2>&1 || { >&2 echo "I need ffprobe" ; exit 1; }
command -v lolcat >/dev/null 2>&1 || { >&2 echo "I need lolcat" ; exit 1; }
command -v convert >/dev/null 2>&1 || { >&2 echo "I need imagemagick convert" ; exit 1; }
command -v composite >/dev/null 2>&1 || { >&2 echo "I need imagemagick composite" ; exit 1; }

# functions
calc () {
     bc <<< "scale=5; $*"
}
round () { 
    awk 'BEGIN{printf "%."'"$1"'"f\n", "'"$2"'"}'
}

# variables
pre=(ffmpeg -y
    -loglevel error
    -stats)         # How the ffmpeg command should start every time
debug="1"           # echo various debug/info messages
offset="0.005"      # seconds < to correct for the ffmpeg cliping the right side of the waveform slightly, pure guesswork.
wave_w="3200"       # px < tmp image width
wave_h="940"       # px < tmp image height
w="1920"            # px < final width, 1920x804 is 21:9 ar.
h="804"             # px < final height
line_size=(2x450)   # moving head graphics size
#line_color="ccffcc" # hex
line_color="bbeebb" # hex
#waveform_color="bbeebb" # hex
waveform_color="3ba67d" # hex
font_color="#bbeebb" # #hex
#font_family="Roboto Condensed"  # convert -list font | grep FontName # < to check
font_family="Agdasima"  # convert -list font | grep FontName # < to check
font_size="$(calc ${w}/70)" && font_size="$(round 0 "${font_size}")" # font size as ratio of final_width
fps="100"            # frames per second
tmpdir_root="/tmp"  # place where script will put tmp files

(( debug )) && echo "font size: $font_size"

# tmp dir
tmp="${tmpdir_root}/waveform_$RANDOM$$"
trap '[ -n "$tmp" ] && rm -fr "$tmp"' EXIT
mkdir -m 700 "$tmp" || { echo '!! unable to create a tmpdir' >&2; tmp=; exit 1; }
(( debug )) && echo "tmp dir is $tmp"

# main

# main loop
while [ $# -gt 0 ]; do
    file="$1"

    # info
    if type roundbox >/dev/null
    then roundbox "$file" | lolcat
    else echo "$file" | lolcat
    fi

    # get the duration into variable
    duration="$(ffprobe -i "$file" -show_entries format=duration -v quiet -of csv="p=0")"
    (( debug )) && echo "duration: $duration (seconds)"

    # image offset: get me how many pixels i need to pad image with $width, round to whole number
    offsetPercents="$(calc "($offset/($duration+$offset))*100")"
    (( debug )) && echo "offsetPercents: $offsetPercents (%)"
    padded_width="$(calc "($offsetPercents/100)*$wave_w+$wave_w")"
    padded_width="$(round "0" "$padded_width")"
    (( debug )) && echo "padded_width: $padded_width (pixels)"

    # image: waveform without padding
    waveformPic=(-i "$file" -filter_complex "aformat=channel_layouts=mono,showwavespic=s=${wave_w}x${wave_h}:colors=${waveform_color}, format=rgba" -frames:v 1 "$tmp/waveform_one.png")
    echo "waveform pic: ${pre[*]} ${waveformPic[*]}" | lolcat
    ("${pre[@]}" "${waveformPic[@]}") || exit # exe
    # image: waveform add padding
    waveformPicPad=(-i "$tmp/waveform_one.png" -vf "pad=${padded_width}:ih:color=black@0" "$tmp/waveform_padded.png")
    echo "waveform pic padded: ${pre[*]} ${waveformPicPad[*]}" | lolcat
    ("${pre[@]}" "${waveformPicPad[@]}") || exit # exe

    # find cover if any, either filebase.png or generic cover.png
    filename="$(readlink -f "$file")"   # absolute
    baseext=$(basename "${filename}")   # file.ext
    base="${baseext%.*}"                # file
    dir="$(dirname "${filename}")"      # dir
    path1="$dir/${base}.png"
    path2="$dir/cover.png"
    if [ -f "$path1" ]; then
        cover="$path1"
    elif [ -f "$path2" ]; then
        cover="$path2"
    else # make some image for cover
        makeblack=(-f lavfi -i color=c=black:s="${w}"x"${h}" -frames:v 1 "$tmp/black.png")
        echo "no cover found, making some black: ${pre[*]} ${video2[*]}" | lolcat 
        ("${pre[@]}" "${makeblack[@]}") || exit # exe
        cover="$tmp/black.png"
    fi

    (( debug )) && echo "cover=$cover"

    # image magick instead of ffmpeg maybe
    set -x
    convert "$cover" -resize "${w}"x"${h}"^ -gravity center -extent "${w}"x"${h}" "$tmp/cover.png" || exit
    set +x 

    # composite waveform over cover
    set -x
    composite -gravity center -compose screen -resize "${w}"x"${h}" "$tmp/waveform_padded.png" "$tmp/cover.png" "$tmp/composite_pre.png" || exit
    set +x

    # add text title
    (( debug )) && echo "base: $base"
    echo "$base" > "$tmp/text.txt"
    set -x
    convert "${tmp}/composite_pre.png" -family "$font_family" -fill "$font_color" -pointsize "$font_size" -compose screen -gravity north -annotate +20+10 @"$tmp/text.txt" "${tmp}/composite.png" || exit
    set +x

    # image: make line to be used as head
    line=(-f lavfi -i color=c="${line_color}":s="${line_size[@]}" -frames:v 1 "$tmp/line.png")
    echo "make line: ${pre[*]} ${line[*]}" | lolcat 
    ("${pre[@]}" "${line[@]}") || exit # exe

    # video2: overlay animated head
    video2=(-r "${fps}" -loop 1 -i "$tmp/composite.png" -i "$tmp/line.png" -i "$file" -filter_complex "[0:v][1:v]overlay=x='if(gte(t,0), -w+(t)*(main_w-overlay_w)/$duration, NAN)':y='(main_h-overlay_h)/2'" -c:v libx264 -preset veryfast -crf 21 -c:a copy -to "$duration" "$tmp/waveform_head.mkv")
    echo "video2: ${pre[*]} ${video2[*]}" | lolcat 
    ("${pre[@]}" "${video2[@]}") || exit # exe

    # find nice filename for output
    baseout="${base}_waveform"
    while [ -f "$dir/${baseout}.mkv" ] ; do
        baseout="${base}_waveform.$RANDOM"
    done
    (( debug )) && echo "filename: ${baseout}.mkv"

    # mv to destination
    echo "${dir}/${baseout}.mkv" | lolcat
    mv "$tmp/waveform_head.mkv" "${dir}/${baseout}.mkv"

shift
done