#!/bin/bash
#set -n		# read but do not execute commands (check on syntax errors)
#
# File: /usr/local/bin/mediachannel-info
#
# Show statistics about audio channels of a Media file.
#
#    Copyright (c) 2023  Juergen Kaesmann (JK)
#
##############################################################################
# GPL NOTICE
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
##############################################################################
# FIND
#
##############################################################################
# FILTER
#
##############################################################################
# AWK
#
##############################################################################
# SED
#
##############################################################################
# XARGS
#
##############################################################################
# REPLACE STRINGS
#
##############################################################################
# RSYNC
#
##############################################################################
# DIALOG
#
##############################################################################
# EXIT CODES

##############################################################################
# SOURCE FILES
#
. /usr/local/etc/main.conf				# config main
#. $LLIBDIR/libfuncerr-sh				# functions error
. $LLIBDIR/libfuncstd-sh				# functions standard
#. $LLIBDIR/libfuncmath-sh				# functions math
#. $LLIBDIR/libfuncmath2-sh				# functions math2
#. $LLIBDIR/libfuncip-sh				# functions IP
#. $LLIBDIR/libfunchart-sh				# functions chart
#. $LLIBDIR/libfunccddvd-sh				# functions cd-dvd
#. /usr/local/etc/multimedia.conf			# config multimedia

#if [ ! -e ~/.samplerc ]; then				# install config user
#	cp /usr/local/share/ksm-sample/samplerc.templ ~/.samplerc
#	echo "Please configure your ~/.samplerc"
#	exit 1
#fi
#. ~/.samplerc						# config user

# Select config global OR config local. Config local gets priority.
#if [ -e ~/.samplerc ]; then
#	. ~/.samplerc					# config user
#else
#	. /usr/local/etc/sample.conf			# config global
#fi

##############################################################################
# DECLARATION OF STANDARD CONSTANTS AND VARIABLES
#
readonly package=Multimedia				# package name
readonly package_file=ksm-multimedia			# package file name
readonly vinfopath="/usr/local/share/$package_file"	# path to package dir
readonly version=$(cat $vinfopath/VERSION)		# package version
readonly release=$(cat $vinfopath/RELEASE)		# package release
readonly scriptname=$(basename $0)			# name of script
readonly tempfile=$TEMPDIR/$scriptname.$UID.tmp		# temporary file name
#readonly tempfile=$(mktemp -u $TEMPDIR/$scriptname.$UID.XXXXXX) # temporary file name
#readonly lockfile=$TEMPDIR/lock/$scriptname.$UID.lock	# lock file name
readonly debuglog="/usr/bin/logger -p user.debug -t $scriptname -- DEBUG:"	# log dbg msg
readonly errlog="/usr/bin/logger -p user.err -t $scriptname -- ERROR:"		# log err msg
readonly infolog="/usr/bin/logger -p user.info -t $scriptname -- INFO:"		# log inf msg
cmd=''							# sub command
debug=no						# debug=yes/no
verbose=''						# verbose=yes/''/no
err=${_ERR_OK_}						# error code

##############################################################################
# USER FILL IN AT FIRST RUN

##############################################################################
# SYSTEM

##############################################################################
# USER-CONSTANTS

##############################################################################
# USER-VARIABLES
#
# STRINGS
#args=''				# all args from cmdline
arg1=''					# 1. arg from cmdline
#opt_o=''				# option -o yes
					# option --output=yes
#opt_i=''				# option -i <arg>
					# option --infile=<arg>
#opt_o=''				# option -o <arg>
					# option --outfile=<arg>
#opt_p=''				# option -p <arg>
					# option --pages=<arg>

# NUMBERS
#p_first=0				# first page
#p_last=0				# last page

# ARRAYS
# Indexed arrays are always zero based string arrays.
# If index starts with 1 and all members are assigned then
# count of members of array = last index of array
#declare -a arr
#maxarr="${#arr[@]}"			# get number of elements in arr
#unset arr[@]				# remove the entire array

##############################################################################
# SIGNAL HANDLING

##############################################################################
# FUNCTIONS
##############################################################################
#
##############################################################################
# HELP
#
#=============================================================================
function helptext ()
{
# Output helptext
# Uses cat, echo, logger
# Return codes:
# Standard

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG helptext: $# $@" >&2
	$debuglog helptext: $# $@
fi

cat <<EOT
NAME
$scriptname - Show statistics about audio channels of a Media file.

SYNOPSIS
$scriptname [options] [[astr] file]

DESCRIPTION
Show statistics about audio channels of a Media file. Audio stream
selection is done from ffmpeg (ffmpeg selects the audio stream with the
most chananels). Give <astr> to select one audio stream manually.

Explanation of values:

  DC offset - Mean amplitude displacement from zero.
  Min level - Minimal sample level.
  Max level - Maximal sample level.
  Min difference - Minimal difference between two consecutive samples.
  Max difference - Maximal difference between two consecutive samples.
  Mean difference - Mean difference between two consecutive samples.
    The average of each difference between two consecutive samples.
  Peak level dB + RMS level dB - Standard peak and RMS level in dBFS.
  RMS peak dB + RMS trough dB - Peak and trough values for RMS level
    measured over a short window.
  Crest factor - Standard ratio of peak to RMS level (note: not in dB).
  Flat factor - Flatness (i.e. consecutive samples with the same value)
    of the signal at its peak levels (either Min level or Max level).
  Peak count - Number of occasions (not the number of samples) that the
    signal attained either Min level or Max level.
  Bit depth - Overall bit depth of audio. Num. of bits used f. samples.

ATTENTION
In order to process the stats ffmpeg has to recode temporarily the file
to format flac. This will slow down the speed.

Speed comparison:
  AAC 5.1 : AAC Stereo = 1 : 2.5

OPTIONS
Gnu style options:

  -h|--help    = Output help, then exit.
  -V|--version = Output version info, then exit.
  -d|--debug   = Turn on DEBUG mode.
  -q|--quiet   = Be quiet.
  -v|--verbose = Be verbose.
  --           = End of options.

ARGUMENTS
Give if needed:

  astr = audio stream number 'a' from file 0:a (a = 1 digit).
  file = MPEG file.

EXIT STATUS
On program exit:

  Code 0 = OK

  See output of 'showerrmsg'.

SEE ALSO
Related manpages:

  showerrmsg(1), ffmpeg(1), sox(1).

AUTHORS
Juergen Kaesmann <juergen@kaesmann-online.de>
EOT
#  -o|--output  = Output to STDOUT.
#  -i f         = Input from file <f>.
#  --infile=f   = Input from file <f>.
#  -o f         = Output to file <f>.
#  --outfile=f  = Output to file <f>.
#  -p s,e       = Pages s=START e=END.
#  --pages=s,e  = Pages s=START e=END.

#EXAMPLES
#For instance:
#
#  Do it:
#  $scriptname

#FILES
#Used directories and files:
#
#  Config Global = /usr/local/etc/sample.conf
#  Config User   = ~/.samplerc

return
}

#=============================================================================
function mainproc ()
{
# Do the main stuff.
# par1 = 
# Uses echo, logger
# Return codes:
# Standard

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG mainproc: $# $@" >&2
	$debuglog mainproc: $# $@
fi

local ret=0
#local -a arr
#local maxarr="${#arr[@]}"		# get number of elements in arr
#unset arr[@]				# remove the entire array

echo > $tempfile
if [ -n "$arg2" ]; then					# stream given
	echo "Channel layouts of all streams in $arg2:" >> $tempfile
	audiolayout $arg2 | sort | uniq >> $tempfile
	echo "Channel statistics of stream [0:${arg1}]:" >> $tempfile
	ffmpeg -hide_banner -i $arg2 -map 0:$arg1 -vn -c:0:$arg1 flac -f flac - |
		sox -t .flac - -n stats -s 10.0 2>>$tempfile
	ret=$?
else							# auto select stream
	echo "Channel layouts of all streams in $arg1:" >> $tempfile
	audiolayout $arg1 | sort | uniq >> $tempfile
	echo "Channel statistics of autoselected stream:" >> $tempfile
	ffmpeg -hide_banner -i $arg1 -vn -c:a flac -f flac - |
		sox -t .flac - -n stats -s 10.0 2>>$tempfile
	ret=$?
fi

if [ $ret -eq 0 ]; then
	cat $tempfile
else
	Show_Err ${_ERR_GENERAL_} "$ret from ffmpeg/sox"
	ret=${_ERR_GENERAL_}
fi
rm -f $tempfile

return $ret
}

##############################################################################
# GET OPTIONS
##############################################################################
# Give 2 strings and all cmdline_args to func GetOpt
# (POSIX_options Gnu_long_options cmdline_args):
# GetOpt 'hVdqvi:m:' 'help version debug quiet verbose infile= myval=' $@
#
# A ':' after a POSIX option means this option needs an arg.
# A '=' after a Gnu long option means this option needs an arg.
#
# All cmd/options have to be initialised to ''!
#
# Options are divided in 2 classes:
#   a) cmds (mutually exclusive). All cmds are given to var 'cmd'.
#   b) options (as many as you like). Each option is given to it's own var.
# As a general rule: If cmds/options are given multiple then last given wins.
#
# Options can take 1 argument (no whitespace in arg). If you want to give more
# than 1 arg or want to use multiple strings as 1 arg then concatenate them
# together with a delimiter like ',' or ';' or '.'
# Use a delimiter which will not be part of the arg:
# sampleprog -m val1,val2
# sampleprog --myval=val1,val2
# It's at the programmers responsibility to do meaningfull things with the
# content of the options.
#
# While err = 0 then look for a next option.
# When GetOpt finishes then all options are removed from cmdline.
#
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG Parse Commandline: $# $@" >&2
	$debuglog Parse Commandline: $# $@
fi
#
err=${_ERR_OK_}
while [ "$err" -eq 0 ]; do
	GetOpt 'hVdqv' 'help version debug quiet verbose' $@
	err=$?
	#echo "DEBUG GET OPTIONS: err=${err}  _OPTNAME_=${_OPTNAME_}  _OPTVAL_=${_OPTVAL_}  _OPTPOS_=${_OPTPOS_}  _OPTIDX_=${_OPTIDX_}" >&2
	if [ $err -eq 0 ]; then
		case "$_OPTNAME_" in
		    h|help)	cmd="-h";;
		    V|version)	cmd="-V";;
#		    a)		cmd="-${_OPTNAME_}";;
		    d|debug)	debug=yes;;
		    q|quiet)	verbose=no;;
		    v|verbose)	verbose=yes;;
#		    o|output)	opt_o=yes;;
#		    i|infile)	opt_i="${_OPTVAL_}";;
#		    o|outfile)	opt_o="${_OPTVAL_}";;
#		    p|pages)	opt_p="${_OPTVAL_}";;
		    --)		break;;
		    :)	Show_Err ${_ERR_MISSING_ARG_FOR_OPT_} "-${_OPTVAL_}"
			exit ${_ERR_MISSING_ARG_FOR_OPT_};;
		    ?)	Show_Err ${_ERR_UNKNOWN_OPT_} "-${_OPTVAL_}"
			exit ${_ERR_UNKNOWN_OPT_};;
		    *)	Show_Err ${_ERR_UNKNOWN_ERR_} "_OPTNAME_=${_OPTNAME_} _OPTVAL_=${_OPTVAL_}"
			exit ${_ERR_UNKNOWN_ERR_};;
		esac
	fi
done
err=${_ERR_OK_}
shift ${_OPTIDX_}				# rm all opts from cmdline
#
# Uncomment if giving a cmd is mandatory
#if [ -z "$cmd" ]; then					# error no cmd given
#	Show_Err ${_ERR_MISSING_SUB_CMD_} ""
#	exit ${_ERR_MISSING_SUB_CMD_}
#fi

##############################################################################
# CHECK LINK NAMES

##############################################################################
# CHECK OPTIONS

##############################################################################
# CHECK VERSION

##############################################################################
# CHECK ARGUMENTS
#
case "$cmd" in
    '')
	if [ "$#" -eq 1 ]; then				# parameter count ok
		arg1="$1"
		shift
		#args="$@"
		#shift $#
	elif [ "$#" -eq 2 ]; then			# parameter count ok
		arg1="$1"
		arg2="$2"
		shift
	else						# error in param count
		Show_Err ${_ERR_WRONG_PAR_COUNT_} ""
		exit ${_ERR_WRONG_PAR_COUNT_}
	fi
	;;
    *)
	if [ "$#" -ne 0 ]; then				# error in param count
		Show_Err ${_ERR_WRONG_PAR_COUNT_} ""
		exit ${_ERR_WRONG_PAR_COUNT_}
	fi
	;;
esac

##############################################################################
# LOCK

##############################################################################
# MAINPROGRAM
##############################################################################
#
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG Main: $# $@" >&2
	$debuglog Main: $# $@
fi

# Check what to do (sub commands, options and args are removed from cmdline)
case "$cmd" in
    '')
	mainproc
	err=$?
	;;
    -h)
	helptext
	exit 0
	;;
    -V)
	echo "${scriptname}: (${package}) $package_file ${version}-${release}"
	exit 0
	;;
    *)							# error
	Show_Err ${_ERR_SUB_CMD_NOT_FOUND_} ""
	err=${_ERR_SUB_CMD_NOT_FOUND_}
	;;
esac

#rm -f $lockfile

exit $err
