#!/bin/bash ###################################################################### # Copyright (C) 2016 Knut Petersen (address@hidden) # # This 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 3 of the License, or # (at your option) any later version. # # This file 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. ###################################################################### FPS=25 TITLETIME=6.0 AFTERTIME=4.0 DEBUG=0 CLEAN=1 FAIL=0 function weneedprog { for P in $@; do TMP=`which $P 2> /dev/null` if [ "x" == "x$TMP" ]; then echo We need $P but could not find it! FAIL=$((FAIL+1)) fi done } function weneeddata { for P in $@; do TMP=`ls -q $P 2> /dev/null` if [ "x" == "x$TMP" ]; then echo We need $P but could not find it! FAIL=$((FAIL+1)) fi done } echo checking dependencies ... weneedprog ls sort tail uniq grep sed bc gs pdftk fluidsynth sox ffmpeg weneeddata /usr/share/sounds/sf2/FluidR3_GM.sf2 videohelper.notes if [ $FAIL -ne 0 ]; then echo $FAIL missing dependencies, aborting exit 1 else echo dependencies ok fi function checknotes { grep "$1" videohelper.notes &> /dev/null if [ $? -ne 0 ]; then echo Fatal error: $2 exit 2 fi } echo checking videohelper.notes ... checknotes "LILYSOURCE" "LILYSOURCE undefined" checknotes "VIDEOSOURCE" "VIDEOSOURCE undefined" checknotes "MIDISOURCE" "MIDISOURCE undefined" checknotes "tempo" "no tempo definition" checknotes "note" "not a single note event" checknotes "page 1 contains no music" "no title page defined" checknotes "[0-9.]* page" "not a single page" echo videohelper.notes ok eval `grep LILYSOURCE videohelper.notes` eval `grep VIDEOSOURCE videohelper.notes` MIDILIST=`grep MIDISOURCE videohelper.notes | sed -e "s/MIDISOURCE=\([[:print:]]*\).midi/\1/" | sort | uniq | sed ':a;N;$!ba;s/\n/ /g'` LASTMOMENT=$(echo `sort -n videohelper.notes | grep 'note\|rest' | tail -n 1 | \ sed -e 's/\([0-9.]*\)\([[:space:]]*\)\(note\|rest\)\([[:space:]]*\)\([0-9.]*\)/\1+\5/'` | bc -l) MOMENTLIST="`grep "[0-9.]* page" videohelper.notes | sed -e "s/\([0-9.]*\)[[:space:]][[:print:]]*/\1/" | sed ':a;N;$!ba;s/\n/ /g'` $LASTMOMENT" COUNT=1 ARMOM=() for VAL in $MOMENTLIST; do ARMOM[COUNT]=$VAL COUNT=$((COUNT+1)) done if [ $DEBUG -ne 0 ] ; then declare -p ARMOM fi TEMPOMOMENTLIST=`grep tempo videohelper.notes | sed -e "s/\([0-9.]*\)[[:space:]]tempo[[:space:]]\([0-9.]*\)/\1 /" | sed ':a;N;$!ba;s/\n/ /g'` COUNT=1 TMMOM=() for VAL in $TEMPOMOMENTLIST; do TMMOM[COUNT]=$VAL COUNT=$((COUNT+1)) done TMMOM[COUNT]=100000.0 if [ $DEBUG -ne 0 ] ; then declare -p TMMOM fi TEMPOTIMELIST=`grep tempo videohelper.notes | sed -e "s/\([0-9.]*\)[[:space:]]tempo[[:space:]]\([0-9.]*\)/\2 /" | sed ':a;N;$!ba;s/\n/ /g'` COUNT=1 TTMOM=() for VAL in $TEMPOTIMELIST; do TTMOM[COUNT]=$VAL COUNT=$((COUNT+1)) done TTMOM[COUNT]=0.0 if [ $DEBUG -ne 0 ] ; then declare -p TTMOM fi TEMPOS=`grep tempo videohelper.notes | wc | sed -e "s/[[:space:]]*\([[:digit:]]*\)[[:print:]]*/\1/"` if [ $DEBUG -ne 0 ] ; then echo TEMPOS $TEMPOS fi PAGES=`grep page videohelper.notes | wc | sed -e "s/[[:space:]]*\([[:digit:]]*\)[[:print:]]*/\1/"` PAGES=$((PAGES-1)) if [ $DEBUG -ne 0 ] ; then echo PAGES $PAGES fi MOMENTS=$((PAGES+1)) if [ $DEBUG -ne 0 ] ; then echo MOMENTS $MOMENTS fi for M in `seq 1 $MOMENTS`; do ARTIMES[M]=0.0 done for T in `seq 1 $TEMPOS`; do for M in `seq 1 $MOMENTS`; do if (( $(echo "${ARMOM[$M]} > ${TMMOM[$T]}" |bc -l) )); then if (( $(echo "${ARMOM[$M]} <= ${TMMOM[$((T+1))]}" |bc -l) )); then ARTIMES[$M]=`bc -l <<<"${ARTIMES[$M]}+(${ARMOM[$M]}-${TMMOM[$T]})*${TTMOM[$T]}"` else ARTIMES[$M]=`bc -l <<<"${ARTIMES[$M]}+(${TMMOM[$((T+1))]}-${TMMOM[$T]})*${TTMOM[$T]}"` fi fi done done for M in `seq 1 $MOMENTS`; do ARTIMES[$M]=$(echo $(bc <<< "(${ARTIMES[$M]})*$FPS/1")/$FPS | bc -l) done if [ $DEBUG -ne 0 ] ; then echo The first page \(title\) will be visible for "$TITLETIME"s. echo The last page will be visible for "$AFTERTIME"s after the end of the last note/rest. for P in `seq 1 $PAGES`; do echo "page $((P+1)) from moment ${ARMOM[$P]} to ${ARMOM[$((P+1))]} (${ARTIMES[$P]}s to ${ARTIMES[$((P+1))]}s)" done fi ARVT[1]=$TITLETIME for M in `seq 1 $PAGES`; do ARVT[$((M+1))]=`bc <<<${ARTIMES[$((M+1))]}-${ARTIMES[$M]}` done ARVT[$MOMENTS]=`bc <<<${ARVT[$MOMENTS]}+$AFTERTIME` if [ $DEBUG -ne 0 ] ; then declare -p ARVT fi PAGELIST=`grep -o "page [[:digit:]]*" videohelper.notes | sort -n | sed -e "s/\(page\) \([[:digit:]]*\)/\1\2/" | sed ':a;N;$!ba;s/\n/ /g'` echo generating tsilence.wav ... sox -n -r 44100 -c 2 -b16 tsilence.wav trim 0.0 $TITLETIME &>/dev/null & echo generating wav files from midi input ... for M in $MIDILIST; do fluidsynth -ln --fast-render=$M-tmp1.wav /usr/share/sounds/sf2/FluidR3_GM.sf2 $M.midi &>/dev/null & done echo bursting pdf ... pdftk $VIDEOSOURCE burst output page%d.pdf for P in `seq 1 $MOMENTS`; do echo generating page$((P)).h264, length: ${ARVT[$P]}s gs -dBATCH -dNOPAUSE -q -r495.421 -sDEVICE=pnggray -sOutputFile=- page$((P)).pdf | \ ffmpeg -y -framerate 1/100000 -i - \ -vf scale=1024:512 -c:v libx264 -tune stillimage -preset ultrafast \ -pix_fmt yuv420p -r 25 -t ${ARVT[$P]} page$((P)).h264 &> /dev/null & done echo synchronizing ... wait echo normalizing audio data ... for M in $MIDILIST; do sox -v `sox $M-tmp1.wav -n stat -v 2>&1` $M-tmp1.wav $M-tmp2.wav &>/dev/null & done echo synchronizing ... wait echo adding silence to audio data ... for M in $MIDILIST; do sox tsilence.wav $M-tmp2.wav $M.wav &>/dev/null & done echo synchronizing ... wait COUNT=0 for M in $MIDILIST; do echo generating $M.mp4 ... if [ $COUNT -eq 0 ]; then grep -o "page [[:digit:]]*" videohelper.notes | sort -n -k 2 | sed -e "s/\(page\) \([[:digit:]]*\)/file \1\2.h264/" >concat.txt ffmpeg -y -f concat -i concat.txt -i $M.wav -c:v libx264 -tune stillimage \ -preset ultrafast -pix_fmt yuv420p -r 25 -shortest $M.mp4 &>/dev/null COUNT=$((COUNT+1)) FIRSTVIDEO=$M.mp4 else ffmpeg -y -i $FIRSTVIDEO -i $M.wav -c:v copy -map 0:v:0 -map 1:a:0 -shortest $M.mp4 &>/dev/null & COUNT=$((COUNT+1)) fi done echo synchronizing ... wait echo removing temporary files ... if [ $CLEAN -eq 1 ]; then for X in $PAGELIST; do rm $X.pdf $X.h264 done for X in $MIDILIST; do rm $X-tmp1.wav $X-tmp2.wav $X.wav done rm concat.txt doc_data.txt tsilence.wav fi