155 lines
5 KiB
Bash
155 lines
5 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
|
||
|
|
# Training sample collection script for voice command classification
|
||
|
|
# Creates organized recordings for wake word and category phrases
|
||
|
|
|
||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
RECORDINGS_DIR="$SCRIPT_DIR/recordings"
|
||
|
|
SAMPLE_RATE=16000
|
||
|
|
DURATION=4
|
||
|
|
|
||
|
|
# Categories
|
||
|
|
declare -A CATEGORIES=(
|
||
|
|
["wake_word"]="ok, note this"
|
||
|
|
["pressure_below_15"]="eye pressure is less than 15"
|
||
|
|
["pressure_15_to_25"]="eye pressure is 15 to 25"
|
||
|
|
["pressure_25_to_35"]="eye pressure is 25 to 35"
|
||
|
|
["pressure_above_35"]="eye pressure is greater than 35"
|
||
|
|
["drop_l"]="L drop"
|
||
|
|
["drop_p"]="P drop"
|
||
|
|
["drop_d"]="D drop"
|
||
|
|
["both_eyes"]="both eyes"
|
||
|
|
)
|
||
|
|
|
||
|
|
# Get Corsair mic source
|
||
|
|
get_mic_source() {
|
||
|
|
wpctl status | grep -i "corsair" | grep -i "mono" | grep -oE '[0-9]+\.' | head -1 | tr -d '.'
|
||
|
|
}
|
||
|
|
|
||
|
|
# Create directory structure
|
||
|
|
setup_dirs() {
|
||
|
|
for category in "${!CATEGORIES[@]}"; do
|
||
|
|
mkdir -p "$RECORDINGS_DIR/$category"
|
||
|
|
done
|
||
|
|
echo "Created recording directories in: $RECORDINGS_DIR"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Count existing samples for a category
|
||
|
|
count_samples() {
|
||
|
|
local category=$1
|
||
|
|
find "$RECORDINGS_DIR/$category" -name "*.wav" 2>/dev/null | wc -l
|
||
|
|
}
|
||
|
|
|
||
|
|
# Record a single sample
|
||
|
|
record_sample() {
|
||
|
|
local category=$1
|
||
|
|
local voice_name=$2
|
||
|
|
local source_id=$3
|
||
|
|
|
||
|
|
local count=$(count_samples "$category")
|
||
|
|
local filename="${voice_name}_$(printf "%03d" $((count + 1))).wav"
|
||
|
|
local filepath="$RECORDINGS_DIR/$category/$filename"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "Recording: $category"
|
||
|
|
echo "Say: \"${CATEGORIES[$category]}\""
|
||
|
|
echo ""
|
||
|
|
echo "Press ENTER to start recording (${DURATION}s)..."
|
||
|
|
read
|
||
|
|
|
||
|
|
echo ">>> RECORDING NOW - SPEAK! <<<"
|
||
|
|
pw-record --target "$source_id" --rate "$SAMPLE_RATE" --channels 1 --format s16 "$filepath" &
|
||
|
|
local pid=$!
|
||
|
|
sleep "$DURATION"
|
||
|
|
kill "$pid" 2>/dev/null
|
||
|
|
wait "$pid" 2>/dev/null
|
||
|
|
|
||
|
|
# Check audio level
|
||
|
|
local max_vol=$(ffmpeg -i "$filepath" -af "volumedetect" -f null /dev/null 2>&1 | grep "max_volume" | sed 's/.*max_volume: \([-0-9.]*\).*/\1/')
|
||
|
|
|
||
|
|
echo "Saved: $filename (max volume: ${max_vol} dB)"
|
||
|
|
|
||
|
|
if [ "${max_vol%.*}" -lt -50 ] 2>/dev/null; then
|
||
|
|
echo "WARNING: Recording seems quiet. Keep? [Y/n]"
|
||
|
|
read -r keep
|
||
|
|
if [[ "$keep" =~ ^[Nn] ]]; then
|
||
|
|
rm "$filepath"
|
||
|
|
echo "Deleted. Try again."
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
# Interactive recording session
|
||
|
|
record_session() {
|
||
|
|
local source_id=$(get_mic_source)
|
||
|
|
|
||
|
|
if [ -z "$source_id" ]; then
|
||
|
|
echo "ERROR: Corsair mic not found"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "=== Voice Command Sample Collection ==="
|
||
|
|
echo "Mic source: $source_id"
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
echo "Enter your name/voice identifier (e.g., 'john', 'sarah'):"
|
||
|
|
read -r voice_name
|
||
|
|
voice_name=${voice_name:-default}
|
||
|
|
|
||
|
|
while true; do
|
||
|
|
echo ""
|
||
|
|
echo "=== Current sample counts ==="
|
||
|
|
for category in "${!CATEGORIES[@]}"; do
|
||
|
|
local count=$(count_samples "$category")
|
||
|
|
echo " $category: $count samples"
|
||
|
|
done
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "Select category to record:"
|
||
|
|
echo " 1) wake_word - \"ok, note this\""
|
||
|
|
echo " 2) pressure_below_15 - \"eye pressure is less than 15\""
|
||
|
|
echo " 3) pressure_15_to_25 - \"eye pressure is 15 to 25\""
|
||
|
|
echo " 4) pressure_25_to_35 - \"eye pressure is 25 to 35\""
|
||
|
|
echo " 5) pressure_above_35 - \"eye pressure is greater than 35\""
|
||
|
|
echo " 6) drop_l - \"L drop\""
|
||
|
|
echo " 7) drop_p - \"P drop\""
|
||
|
|
echo " 8) drop_d - \"D drop\""
|
||
|
|
echo " 9) both_eyes - \"both eyes\""
|
||
|
|
echo " a) Record ALL categories (one each)"
|
||
|
|
echo " q) Quit"
|
||
|
|
echo ""
|
||
|
|
read -r choice
|
||
|
|
|
||
|
|
case $choice in
|
||
|
|
1) record_sample "wake_word" "$voice_name" "$source_id" ;;
|
||
|
|
2) record_sample "pressure_below_15" "$voice_name" "$source_id" ;;
|
||
|
|
3) record_sample "pressure_15_to_25" "$voice_name" "$source_id" ;;
|
||
|
|
4) record_sample "pressure_25_to_35" "$voice_name" "$source_id" ;;
|
||
|
|
5) record_sample "pressure_above_35" "$voice_name" "$source_id" ;;
|
||
|
|
6) record_sample "drop_l" "$voice_name" "$source_id" ;;
|
||
|
|
7) record_sample "drop_p" "$voice_name" "$source_id" ;;
|
||
|
|
8) record_sample "drop_d" "$voice_name" "$source_id" ;;
|
||
|
|
9) record_sample "both_eyes" "$voice_name" "$source_id" ;;
|
||
|
|
a|A)
|
||
|
|
for category in wake_word pressure_below_15 pressure_15_to_25 pressure_25_to_35 pressure_above_35 drop_l drop_p drop_d both_eyes; do
|
||
|
|
record_sample "$category" "$voice_name" "$source_id"
|
||
|
|
done
|
||
|
|
;;
|
||
|
|
q|Q)
|
||
|
|
echo "Done. Total samples collected:"
|
||
|
|
for category in "${!CATEGORIES[@]}"; do
|
||
|
|
echo " $category: $(count_samples "$category")"
|
||
|
|
done
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
*) echo "Invalid choice" ;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
}
|
||
|
|
|
||
|
|
# Main
|
||
|
|
setup_dirs
|
||
|
|
record_session
|