Commit | Line | Data |
---|---|---|
75c5aa78 SBS |
1 | #!/bin/bash |
2 | # Desc: Convert IPA string into audio file | |
3 | ||
4 | #==BEGIN Define script parameters== | |
5 | #===BEGIN Initialize variables=== | |
6 | ||
7 | # Script Metadata | |
8 | scriptName="bkipas"; # Define basename of script file. | |
9 | scriptVersion="0.1.0"; # Define version of script. | |
10 | scriptTimeStart="$(date +%Y%m%dT%H%M%S.%N)"; # YYYYmmddTHHMMSS.NNNNNNNNN | |
11 | scriptURL="https://zdv2.bktei.com/gitweb/baltakatei-exdev.git"; # Define website hosting this script. | |
12 | scriptHostname=$(hostname); # Save hostname of system running this script. | |
13 | PATH="$HOME/.local/bin:$PATH"; # Add "$(systemd-path user-binaries)" path in case user apps saved there | |
14 | ||
15 | # Arrays | |
16 | declare -Ag appRollCall # Associative array for storing app status | |
17 | declare -Ag fileRollCall # Associative array for storing file status | |
18 | declare -Ag dirRollCall # Associative array for storing dir status | |
19 | ||
20 | # Variables | |
21 | optionVerbose=""; optionOutputDir=""; optionInputString=""; argStrIn=""; argDirOut=""; pathOut=""; | |
22 | ||
23 | #===END Initialize variables=== | |
24 | #===BEGIN Declare local script functions=== | |
25 | ||
26 | ||
27 | # Initialize variables and functions | |
28 | yell() { echo "$0: $*" >&2; } #o Yell, Die, Try Three-Fingered Claw technique | |
29 | die() { yell "$*"; exit 111; } #o Ref/Attrib: https://stackoverflow.com/a/25515370 | |
30 | try() { "$@" || die "cannot $*"; } #o | |
31 | vbm() { | |
32 | # Description: Prints verbose message ("vbm") to stderr if optionVerbose is set to "true". | |
33 | # Usage: vbm "DEBUG :verbose message here" | |
34 | # Version 0.1.3 | |
35 | # Input: arg1: string | |
36 | # vars: optionVerbose | |
37 | # Output: stderr | |
38 | # Depends: bash 5.0.3, echo 8.30, date 8.30 | |
39 | ||
40 | if [ "$optionVerbose" = "true" ]; then | |
41 | functionTime=$(date --iso-8601=ns); # Save current time in nano seconds. | |
42 | echo "[$functionTime]:$0:""$*" 1>&2; # Display argument text. | |
43 | fi | |
44 | ||
45 | # End function | |
46 | return 0; # Function finished. | |
47 | } # Displays message if optionVerbose true | |
48 | processArguments() { | |
49 | while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0). | |
50 | case "$1" in | |
51 | -v | --verbose) optionVerbose="true"; vbm "DEBUG :Verbose mode enabled.";; # Enable verbose mode. | |
52 | -h | --help) showUsage; exit 1;; # Display usage. | |
53 | --version) showVersion; exit 1;; # Show version. | |
54 | -i | --input-string) optionInputString="true"; if [[ ! -z "$2" ]]; then argStrIn="$2"; vbm "DEBUG :argStrIn:$argStrIn"; shift; fi ;; # Identify input string. | |
55 | -o | --output-dir) optionOutputDir="true"; if [[ -d "$2" ]]; then argDirOut="$2"; vbm "DEBUG :argDirOut:$argDirOut"; shift; fi ;; # Define output directory. | |
56 | *) yell "ERROR: Unrecognized argument: $1"; yell "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options. | |
57 | esac | |
58 | shift | |
59 | done | |
60 | } # Argument Processing | |
61 | checkapp() { | |
62 | # Desc: If arg is a command, save result in assoc array 'appRollCall' | |
63 | # Usage: checkapp arg1 arg2 arg3 ... | |
64 | # Version: 0.1.1 | |
65 | # Input: global assoc. array 'appRollCall' | |
66 | # Output: adds/updates key(value) to global assoc array 'appRollCall' | |
67 | # Depends: bash 5.0.3 | |
68 | local returnState | |
69 | ||
70 | #===Process Args=== | |
71 | for arg in "$@"; do | |
72 | if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command | |
73 | appRollCall[$arg]="true"; | |
74 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi; | |
75 | else | |
76 | appRollCall[$arg]="false"; returnState="false"; | |
77 | fi; | |
78 | done; | |
79 | ||
80 | #===Determine function return code=== | |
81 | if [ "$returnState" = "true" ]; then | |
82 | return 0; | |
83 | else | |
84 | return 1; | |
85 | fi; | |
86 | } # Check that app exists | |
87 | checkfile() { | |
88 | # Desc: If arg is a file path, save result in assoc array 'fileRollCall' | |
89 | # Usage: checkfile arg1 arg2 arg3 ... | |
90 | # Version: 0.1.1 | |
91 | # Input: global assoc. array 'fileRollCall' | |
92 | # Output: adds/updates key(value) to global assoc array 'fileRollCall'; | |
93 | # Output: returns 0 if app found, 1 otherwise | |
94 | # Depends: bash 5.0.3 | |
95 | local returnState | |
96 | ||
97 | #===Process Args=== | |
98 | for arg in "$@"; do | |
99 | if [ -f "$arg" ]; then | |
100 | fileRollCall["$arg"]="true"; | |
101 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi; | |
102 | else | |
103 | fileRollCall["$arg"]="false"; returnState="false"; | |
104 | fi; | |
105 | done; | |
106 | ||
107 | #===Determine function return code=== | |
108 | if [ "$returnState" = "true" ]; then | |
109 | return 0; | |
110 | else | |
111 | return 1; | |
112 | fi; | |
113 | } # Check that file exists | |
114 | checkdir() { | |
115 | # Desc: If arg is a dir path, save result in assoc array 'dirRollCall' | |
116 | # Usage: checkdir arg1 arg2 arg3 ... | |
117 | # Version 0.1.1 | |
118 | # Input: global assoc. array 'dirRollCall' | |
119 | # Output: adds/updates key(value) to global assoc array 'dirRollCall'; | |
120 | # Output: returns 0 if app found, 1 otherwise | |
121 | # Depends: Bash 5.0.3 | |
122 | local returnState | |
123 | ||
124 | #===Process Args=== | |
125 | for arg in "$@"; do | |
126 | if [ -d "$arg" ]; then | |
127 | dirRollCall["$arg"]="true"; | |
128 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi | |
129 | else | |
130 | dirRollCall["$arg"]="false"; returnState="false"; | |
131 | fi | |
132 | done | |
133 | ||
134 | #===Determine function return code=== | |
135 | if [ "$returnState" = "true" ]; then | |
136 | return 0; | |
137 | else | |
138 | return 1; | |
139 | fi | |
140 | } # Check that dir exists | |
141 | displayMissing() { | |
142 | # Desc: Displays missing apps, files, and dirs | |
143 | # Usage: displayMissing | |
144 | # Version 0.1.1 | |
145 | # Input: associative arrays: appRollCall, fileRollCall, dirRollCall | |
146 | # Output: stderr: messages indicating missing apps, file, or dirs | |
147 | # Depends: bash 5, checkAppFileDir() | |
148 | local missingApps value appMissing missingFiles fileMissing | |
149 | local missingDirs dirMissing | |
150 | ||
151 | #==BEGIN Display errors== | |
152 | #===BEGIN Display Missing Apps=== | |
153 | missingApps="Missing apps :"; | |
154 | #for key in "${!appRollCall[@]}"; do echo "DEBUG :$key => ${appRollCall[$key]}"; done | |
155 | for key in "${!appRollCall[@]}"; do | |
156 | value="${appRollCall[$key]}"; | |
157 | if [ "$value" = "false" ]; then | |
158 | #echo "DEBUG :Missing apps: $key => $value"; | |
159 | missingApps="$missingApps""$key "; | |
160 | appMissing="true"; | |
161 | fi; | |
162 | done; | |
163 | if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing. | |
164 | echo "$missingApps" 1>&2; | |
165 | fi; | |
166 | unset value; | |
167 | #===END Display Missing Apps=== | |
168 | ||
169 | #===BEGIN Display Missing Files=== | |
170 | missingFiles="Missing files:"; | |
171 | #for key in "${!fileRollCall[@]}"; do echo "DEBUG :$key => ${fileRollCall[$key]}"; done | |
172 | for key in "${!fileRollCall[@]}"; do | |
173 | value="${fileRollCall[$key]}"; | |
174 | if [ "$value" = "false" ]; then | |
175 | #echo "DEBUG :Missing files: $key => $value"; | |
176 | missingFiles="$missingFiles""$key "; | |
177 | fileMissing="true"; | |
178 | fi; | |
179 | done; | |
180 | if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing. | |
181 | echo "$missingFiles" 1>&2; | |
182 | fi; | |
183 | unset value; | |
184 | #===END Display Missing Files=== | |
185 | ||
186 | #===BEGIN Display Missing Directories=== | |
187 | missingDirs="Missing dirs:"; | |
188 | #for key in "${!dirRollCall[@]}"; do echo "DEBUG :$key => ${dirRollCall[$key]}"; done | |
189 | for key in "${!dirRollCall[@]}"; do | |
190 | value="${dirRollCall[$key]}"; | |
191 | if [ "$value" = "false" ]; then | |
192 | #echo "DEBUG :Missing dirs: $key => $value"; | |
193 | missingDirs="$missingDirs""$key "; | |
194 | dirMissing="true"; | |
195 | fi; | |
196 | done; | |
197 | if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing. | |
198 | echo "$missingDirs" 1>&2; | |
199 | fi; | |
200 | unset value; | |
201 | #===END Display Missing Directories=== | |
202 | ||
203 | #==END Display errors== | |
204 | } # Display missing apps, files, dirs | |
205 | showVersion() { | |
206 | yell "$scriptVersion" | |
207 | cat <<'EOF' | |
208 | Copyright (C) 2020 Steven Baltakatei Sandoval | |
209 | License GPLv3: GNU GPL version 3 | |
210 | This is free software; you are free to change and redistribute it. | |
211 | There is NO WARRANTY, to the extent permitted by law. | |
212 | ||
213 | lexconvert (https://github.com/ssb22/lexconvert commit 64a4837) | |
214 | Copyright (C) 2020 Silas S. Brown | |
215 | License GPLv3: GNU GPL version 3 | |
216 | EOF | |
217 | } # Display script version. | |
218 | showUsage() { | |
219 | cat <<'EOF' | |
220 | USAGE: | |
221 | bkipas [ options ] | |
222 | ||
223 | OPTIONS: | |
224 | -h, --help | |
225 | Display help information. | |
226 | --version | |
227 | Display script version. | |
228 | -v, --verbose | |
229 | Display debugging info. | |
230 | -i, --input-string [ str input ] | |
231 | Specify input IPA string. | |
232 | -o, --output-dir [ path dir ] | |
233 | Specify output directory path. | |
234 | ||
235 | EXAMPLES: | |
236 | bkipas -i "təˈmeɪtoʊ" # same as: echo "[[t@'meItoU]]" | espeak | |
237 | bkipas -i "təˈmeɪtoʊ" -o /tmp/ | |
238 | EOF | |
239 | } # Display information on how to use this script. | |
240 | ||
241 | main() { | |
242 | # Desc: Main function | |
243 | # Usage: main "$@" | |
244 | # Inputs: many | |
245 | # Outputs: file (pathout_tar) | |
246 | # Depends: many | |
247 | ||
248 | # Debug:Get function name | |
249 | fn="${FUNCNAME[0]}"; | |
250 | ||
251 | vbm "STATUS:$fn:Started function main()."; | |
252 | # Process arguments | |
253 | processArguments "$@"; | |
254 | ||
255 | # Specify expected lexconvert.py path | |
256 | pathLexConvert="./bkipas.d/lexconvert.py" && vbm "DEBUG :$fn:pathLexConvert:$pathLexConvert"; | |
257 | ## Note: lexconvert.py converts IPA into eSpeak phoneme mneumonics | |
258 | ## See https://github.com/ssb22/lexconvert | |
259 | ## See https://github.com/espeak-ng/espeak-ng/issues/539#issuecomment-536192362 | |
260 | ||
261 | # Check vital apps, files, dirs | |
262 | if ! checkapp sed python3 espeak oggenc && ! checkfile "$pathLexConvert"; then | |
263 | yell "ERROR:$fn:Critical components missing."; | |
264 | displayMissing; yell "Exiting."; exit 1; fi; | |
265 | ||
266 | # Process input string | |
267 | ## Remove '/' | |
268 | strIn="$(echo "$argStrIn" | sed 's/\///g')"; | |
269 | ## Check for empty string | |
270 | if [[ -z "$strIn" ]]; then yell "ERROR:No IPA string provided."; exit 1; fi; | |
271 | ||
272 | # Determine output file name | |
273 | if [[ "$optionOutputDir" = "true" ]]; then | |
274 | #pathOut="$argDirOut/$scriptTimeStart".ogg && vbm "DEBUG :$fn:pathOut:$pathOut"; | |
275 | pathOut="$argDirOut/$strIn".ogg && vbm "DEBUG :$fn:pathOut:$pathOut"; | |
276 | else | |
277 | #pathOut="$(pwd)/$scriptTimeStart".ogg && vbm "DEBUG :$fn:pathOut:$pathOut"; | |
278 | pathOut="$(pwd)/$strIn".ogg && vbm "DEBUG :$fn:pathOut:$pathOut"; | |
279 | fi; | |
280 | ||
281 | # Generate espeak phoneme mneumonic | |
282 | espeakInput="$(python3 "$pathLexConvert" --phones2phones unicode-ipa espeak "$strIn")" && vbm "DEBUG :$fn:espeakInput:$espeakInput"; | |
283 | ||
284 | # Output pronunciation | |
285 | if [[ "$optionOutputDir" = "true" ]]; then | |
286 | ## Write to file if -o specified | |
287 | echo "$espeakInput" | espeak --stdout | oggenc -o "$pathOut" - ; | |
288 | else | |
289 | ## Speak pronunciation via espeak | |
290 | echo "$espeakInput" | espeak ; | |
291 | fi; | |
292 | ||
293 | } # Main function | |
294 | ||
295 | #===END Declare local script functions=== | |
296 | #==END Define script parameters== | |
297 | ||
298 | #==BEGIN Perform work and exit== | |
299 | main "$@" # Run main function. | |
300 | exit 0; | |
301 | #==END Perform work and exit== | |
302 | ||
303 | # Author: Steven Baltakatei Sandoval; | |
304 | # License: GPLv3 | |
305 |