Skip to content

Instantly share code, notes, and snippets.

@bfollington
Created February 17, 2025 06:08
Show Gist options
  • Save bfollington/08211733948ad941d7e1c9772eba13b6 to your computer and use it in GitHub Desktop.
Save bfollington/08211733948ad941d7e1c9772eba13b6 to your computer and use it in GitHub Desktop.
Transcribe from microphone
#!/bin/bash
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to install mlx_whisper using uv
install_mlx_whisper() {
if command_exists uv; then
echo "Installing mlx_whisper using uv..." >&2
uv pip install mlx-whisper >&2
else
echo "Error: uv is not installed." >&2
echo "Please visit https://github.com/astral-sh/uv to install uv first" >&2
echo "Then run this script again." >&2
exit 1
fi
}
# Function to check dependencies
check_dependencies() {
local missing_deps=false
if ! command_exists mlx_whisper; then
echo "mlx_whisper is not installed." >&2
read -p "Would you like to install it now? (y/n) " -n 1 -r
echo >&2
if [[ $REPLY =~ ^[Yy]$ ]]; then
install_mlx_whisper
else
missing_deps=true
fi
fi
if ! command_exists ffmpeg; then
echo "ffmpeg is not installed." >&2
echo "Please install ffmpeg using your package manager:" >&2
echo " macOS: brew install ffmpeg" >&2
echo " Ubuntu/Debian: sudo apt install ffmpeg" >&2
echo " Other: https://ffmpeg.org/download.html" >&2
missing_deps=true
fi
if [ "$missing_deps" = true ]; then
exit 1
fi
}
# Function to display usage information
usage() {
echo "Usage: $0 [input_file]" >&2
echo "If no input file is provided, will record audio until Ctrl+C is pressed" >&2
exit 1
}
format="txt" # default format
while getopts "f:" opt; do
case $opt in
f) format="$OPTARG" ;;
*) usage ;;
esac
done
shift $((OPTIND-1))
# Check dependencies first
check_dependencies
# Create temporary directory
temp_dir=$(mktemp -d)
temp_audio="${temp_dir}/recording.wav"
# Function to clean up temporary files
cleanup() {
rm -rf "$temp_dir"
}
trap cleanup EXIT
if [ $# -eq 0 ]; then
# No input file - record audio
echo "Recording audio... Press Ctrl+C to stop" >&2
# Trap Ctrl+C (SIGINT) to gracefully stop recording
trap 'kill $FF_PID; wait $FF_PID' INT
ffmpeg -f avfoundation -i ":2" -acodec pcm_s16le "$temp_audio" >/dev/null 2>&1 &
FF_PID=$!
# Wait for ffmpeg to be killed by Ctrl+C
wait $FF_PID || true
# Reset the trap
trap - INT
# Check if we actually recorded something
if [ -f "$temp_audio" ] && [ -s "$temp_audio" ]; then
echo -e "\nRecording stopped. Transcribing..." >&2
input_file="$temp_audio"
else
echo -e "\nNo recording created. Exiting." >&2
exit 1
fi
else
input_file=$(realpath "$1")
fi
clean_output() {
local format=$1
if [ "$format" = "srt" ]; then
# Remove frontmatter and keep SRT format
awk '/\[.*\]/{p=1}p' | grep '^\[' 2>/dev/null
else
# Convert to plain text by removing timestamps and joining lines
awk '/\[.*\]/{p=1}p' | \
grep '^\[' | \
sed -E 's/\[.*\]//g' | \
awk 'NF' | \
tr -d '\n' | \
sed 's/ / /g' 2>/dev/null
echo 2>/dev/null # Add final newline
fi
}
# Run mlx_whisper and output plain text
mlx_whisper "$input_file" \
--language en \
--output-format ${format:-txt} | clean_output "${format:-txt}" | pbcopy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment