Created
May 11, 2023 21:01
-
-
Save wchargin/3d461634cad2a264e6a17fde6a8c8bda to your computer and use it in GitHub Desktop.
record sound when the mic
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Extended from a work of Jürgen Hötzel: https://stackoverflow.com/a/2613888 | |
noise_threshold=15 # out of 99 | |
base_dir="${HOME}/recordings" | |
raw_dir="${base_dir}/tmp" | |
chunk_dir="${base_dir}/chunks" | |
seg_dir="${base_dir}/seg" | |
out_dir="${base_dir}/out" | |
sox_raw_options=( -t raw -r 48k -e signed -b 16 -c 1 ) | |
# Chunk size. This affects buffering, but also how much silence you get around | |
# the start and end of a segment (since the final segment is a whole number of | |
# chunks*) and also segmentation (since segment boundaries occur when one chunk | |
# is quiet). With a sample rate of 48 kHz and a bitrate of 16 bits/sample, the | |
# buffer grows at 96 kB/second, so a 1MiB buffer makes for ~10.9-second chunks. | |
# | |
# * except maybe the last one if this program is terminated early | |
split_size=1048576 # 1MiB | |
mkdir -p "${raw_dir}" "${chunk_dir}" "${seg_dir}" "${out_dir}" | |
rawfifo="${raw_dir}/in.raw" | |
test -e "${rawfifo}" || mkfifo "${rawfifo}" | |
# Start recording and splitting in background. | |
rec "${sox_raw_options[@]}" - >"${rawfifo}" 2>/dev/null & | |
printf >&2 'pid[rec]: %s\n' "$!" | |
split -a8 -b "${split_size}" - <"${rawfifo}" "${chunk_dir}/chunk-" & | |
printf >&2 'pid[split]: %s\n' "$!" | |
read_max_level() { | |
sox "${sox_raw_options[@]}" "$1" -n stats -s 99 2>&1 | | |
awk '/^Max level/ { print int($3) }' | |
} | |
poll() { | |
status=1 | |
any_split=-false | |
if [ "$1" = all ]; then | |
any_split=-true | |
fi | |
find "${chunk_dir}" -type f \( -size "${split_size}c" -o "${any_split}" \) -print0 | | |
while IFS= read -r -d '' raw; do | |
status=0 | |
max_level="$(read_max_level "${raw}")" | |
printf >&2 '%s: max_level=%s: ' "$(basename ${raw})" "${max_level}" | |
if [ "${max_level}" -ge "${noise_threshold}" ]; then | |
printf >&2 'appending to segment\n' | |
mv "${raw}" "${seg_dir}" | |
fi | |
if [ "${max_level}" -lt "${noise_threshold}" ] || [ "$1" = all ]; then | |
[ -f "${raw}" ] && rm "${raw}" | |
if ! find "${seg_dir}" -type f | grep -q .; then | |
printf >&2 'nothing to do\n' | |
else | |
segname="$(date +%FT%T)" | |
printf >&2 'finalizing segment %s\n' "${segname}" | |
find "${seg_dir}" -type f -print0 | sort -z | xargs -0 cat | | |
sox "${sox_raw_options[@]}" - "${out_dir}/recording-${segname}.flac" | |
rm "${seg_dir}"/* | |
fi | |
fi | |
done | |
return "${status}" | |
} | |
in_cleanup=0 | |
cleanup() { | |
if [ "${in_cleanup}" = 1 ]; then | |
printf >&2 'cleanup: interrupted: goodbye\n' | |
exit 1 | |
fi | |
in_cleanup=1 | |
printf >&2 'cleanup: polling\n' | |
poll all | |
exit 0 | |
} | |
trap cleanup INT | |
while true; do | |
poll | |
sleep 1 | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment