aboutsummaryrefslogtreecommitdiffstats
path: root/.config/init/funcs/audio-concat
blob: 2d4b9437409c15df216f8a10fa1d45e34c04be27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env zsh
# concatenate multiple audio files into one

source "$HOME/.config/init/helpers" || exit 1

local callstr=$0

usage() {
	echo "Usage: $callstr [OPTIONS...] <infile>..."
	echo "Concatenate multiple audio files into one"
	echo ""
	echo "            \e[1mdescription  opt longform  arg    default\e[0m"
	echo "       destination file  -o  --outfile <file> out.ogg"
	echo "           output codec  -c  --codec    <codec> opus"
	echo "         output bitrate  -b  --bitrate <int>k (opus:35k, vorbis:50k, mp3:65k, flac:N/A)"
	echo " many inputs workaround  -m  --many"
	echo "        print this help  -h  --help"
	exit 0
}

trap_abort() {
	echo ""
	abort "process interrupted"
}

trap 'trap_abort' SIGHUP SIGINT SIGQUIT SIGTERM

local dest='out.opus'

local destpat='^.*\.(opus|mp3|ogg|flac)$'
local bitratepat='^[1-9][0-9]*k$'

local filterstr
local codecstr
local bitratestr
local many

local parseerr=$(2>&1 zparseopts -D -E -M -A args \
	o: -outfile:=o \
	b: -bitrate:=b \
	m  -many=m \
	h  -help=h | cut -d ' ' -f 2-)

[[ -z $parseerr ]] || abort $parseerr

zparseopts -D -E -M -A args \
	o: -outfile:=o \
	b: -bitrate:=b \
	m  -many=m \
	h  -help=h

local optarg
for opt in ${(@k)args}; do
	unset optarg
	[[ -z $args[$opt] ]] || optarg=$args[$opt]
	case $opt in
			-o) [[ $optarg =~ $destpat ]] || \
				abort "destination file must be of type opus, mp3, or ogg"
				dest=$optarg
				[[ ${optarg:e} == 'opus' ]] && codecstr='libopus' \
					&& [[ -z $bitratestr ]] && bitratestr='35k'
				[[ ${optarg:e} == 'mp3' ]] && codecstr='libmp3lame' \
					&& [[ -z $bitratestr ]] && bitratestr='65k'
				[[ ${optarg:e} == 'ogg' ]] && codecstr='libvorbis' \
					&& [[ -z $bitratestr ]] && bitratestr='50k'
				[[ ${optarg:e} == 'flac' ]] && codecstr='flac' \
					&& [[ -z $bitratestr ]] && bitratestr=''
				;;
			-b) [[ $optarg =~ $bitratepat ]] || \
				abort "bitrate must positive integer + k, for kbps"
				bitratestr=$optarg
				;;
			-m) many=true ;;
			-h) usage ;;
	esac
done

[[ -z   $codecstr ]] && codecstr='libopus'
[[ -z $bitratestr ]] && bitratestr='35k'

[[ $codecstr == "flac" ]] && bitratestr=''

[[ ${#@} -eq 0 ]] && abort "no input files specified"

if [[ -e $dest ]]; then
	local delprompt
	printf "destination file \`$dest\` exists\ndelete it? [y/N]: "
	read -q delprompt
	echo ""
	[[ $delprompt = "y" ]] || return 0
	2>/dev/null rm $dest || abort "could not delete file \`$dest\`"
fi

local pid

if [[ -z $many ]]; then
	local inputlist=()
	local i=0
	for f in $@; do
		inputlist+=(-i $f)
		filterstr="${filterstr}[${i}:a:0] "
		i=$((i+1))
	done

	filterstr="${filterstr}concat=n=${i}:v=0:a=1 [outa]"

	(
	ffmpeg -loglevel -8 $inputlist \
		-filter_complex $filterstr -map '[outa]' \
		-c:a $codecstr ${bitratestr/*k/-b:a} $bitratestr \
		-map_metadata -1 $dest
	) &
	pid=$!
else
	(
	for f in $@; do
		ffmpeg -loglevel -8 -i $f -f s16le -ar 44.1k -ac 2 pipe:
	done | ffmpeg -loglevel -8 -f s16le -ar 44.1k -ac 2 -i pipe: \
		-c:a $codecstr ${bitratestr/*k/-b:a} $bitratestr \
		-map_metadata -1 $dest
	) &
	pid=$!
fi

local plural
[[ $#@ -ne 1 ]] && plural='s'

wait-anim $pid "concatenating $#@ file$plural to \`$dest\`"