forked from woofmeow/straywild
112 lines
3.8 KiB
Bash
Executable file
112 lines
3.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
readonly TARGET_RMS_DBFS="-18.0"
|
|
readonly PEAK_CEILING_DBFS="-3.0"
|
|
readonly OUTPUT_SAMPLE_RATE="48000"
|
|
|
|
usage() {
|
|
printf 'usage: %s SOURCE_DIRECTORY SAMPLE_SET_DIRECTORY\n' "$0" >&2
|
|
printf 'example: %s /path/to/voice_kim kim\n' "$0" >&2
|
|
}
|
|
|
|
if (( $# != 2 )); then
|
|
usage
|
|
exit 2
|
|
fi
|
|
|
|
if ! command -v sox >/dev/null 2>&1; then
|
|
printf 'error: sox is required to import animalese samples\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
readonly SCRIPT_DIRECTORY="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly PROJECT_ROOT="$(cd -- "$SCRIPT_DIRECTORY/.." && pwd)"
|
|
readonly SOURCE_DIRECTORY="$(realpath -e -- "$1")"
|
|
readonly SAMPLE_SET_DIRECTORY="$2"
|
|
|
|
if [[ ! -d "$SOURCE_DIRECTORY" ]]; then
|
|
printf 'error: source is not a directory: %s\n' "$SOURCE_DIRECTORY" >&2
|
|
exit 1
|
|
fi
|
|
if [[ ! "$SAMPLE_SET_DIRECTORY" =~ ^[a-z0-9_-]+$ ]]; then
|
|
printf 'error: sample-set directory must use lowercase letters, numbers, underscores, or hyphens\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
readonly DESTINATION_DIRECTORY="$PROJECT_ROOT/sound/dialogue/animalese/$SAMPLE_SET_DIRECTORY"
|
|
readonly WORK_DIRECTORY="$(mktemp -d -t netfishing-animalese-import.XXXXXX)"
|
|
|
|
cleanup() {
|
|
if [[ "$WORK_DIRECTORY" == /tmp/netfishing-animalese-import.* ]]; then
|
|
find "$WORK_DIRECTORY" -depth -delete
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
mkdir -p -- "$WORK_DIRECTORY/converted" "$WORK_DIRECTORY/normalized"
|
|
mapfile -d '' SOURCE_FILES < <(
|
|
find "$SOURCE_DIRECTORY" -maxdepth 1 -type f -name '*.wav' -print0 \
|
|
| sort -z
|
|
)
|
|
if (( ${#SOURCE_FILES[@]} == 0 )); then
|
|
printf 'error: no .wav samples found in %s\n' "$SOURCE_DIRECTORY" >&2
|
|
exit 1
|
|
fi
|
|
|
|
for source_file in "${SOURCE_FILES[@]}"; do
|
|
filename="$(basename -- "$source_file")"
|
|
if [[ ! "$filename" =~ ^[a-z0-9_]+\.wav$ ]]; then
|
|
printf 'error: unsupported sample filename: %s\n' "$filename" >&2
|
|
exit 1
|
|
fi
|
|
converted_file="$WORK_DIRECTORY/converted/$filename"
|
|
normalized_file="$WORK_DIRECTORY/normalized/$filename"
|
|
|
|
# Measure the exact channel/rate conversion that will be shipped, then use
|
|
# the smaller of the RMS correction and peak-safe gain. Very short voice
|
|
# clips are not reliable inputs for program-loudness algorithms such as
|
|
# EBU R128, so this deterministic RMS/peak policy is intentional.
|
|
# Reserve headroom before high-quality resampling. Several supplied 8-bit
|
|
# clips sit near full scale and their interpolated waveform can otherwise
|
|
# clip before the normalization gain is calculated.
|
|
sox -D "$source_file" \
|
|
-r "$OUTPUT_SAMPLE_RATE" -c 1 -b 32 -e floating-point \
|
|
"$converted_file" gain -6 rate -v "$OUTPUT_SAMPLE_RATE"
|
|
statistics="$(sox "$converted_file" -n stat 2>&1)"
|
|
peak_amplitude="$(awk '/Maximum amplitude/ {print $3}' <<< "$statistics")"
|
|
rms_amplitude="$(awk '/RMS.*amplitude/ {print $3; exit}' <<< "$statistics")"
|
|
if ! awk -v peak="$peak_amplitude" -v rms="$rms_amplitude" \
|
|
'BEGIN {exit !(peak > 0.0 && rms > 0.0)}'; then
|
|
printf 'error: sample is silent or unreadable: %s\n' "$source_file" >&2
|
|
exit 1
|
|
fi
|
|
|
|
gain_db="$(
|
|
awk \
|
|
-v peak="$peak_amplitude" \
|
|
-v rms="$rms_amplitude" \
|
|
-v target_rms="$TARGET_RMS_DBFS" \
|
|
-v peak_ceiling="$PEAK_CEILING_DBFS" \
|
|
'BEGIN {
|
|
peak_db = 20.0 * log(peak) / log(10.0)
|
|
rms_db = 20.0 * log(rms) / log(10.0)
|
|
rms_gain = target_rms - rms_db
|
|
peak_gain = peak_ceiling - peak_db
|
|
gain = rms_gain < peak_gain ? rms_gain : peak_gain
|
|
printf "%.6f", gain
|
|
}'
|
|
)"
|
|
sox -D "$converted_file" \
|
|
-r "$OUTPUT_SAMPLE_RATE" -c 1 -b 16 -e signed-integer \
|
|
"$normalized_file" gain "$gain_db"
|
|
printf '%-16s %8s dB\n' "$filename" "$gain_db"
|
|
done
|
|
|
|
mkdir -p -- "$DESTINATION_DIRECTORY"
|
|
for normalized_file in "$WORK_DIRECTORY"/normalized/*.wav; do
|
|
cp -- "$normalized_file" "$DESTINATION_DIRECTORY/$(basename -- "$normalized_file")"
|
|
done
|
|
|
|
printf 'imported %d normalized samples to %s\n' \
|
|
"${#SOURCE_FILES[@]}" "$DESTINATION_DIRECTORY"
|