Make Your Own Music Montage in BASH
I made a music montage last week for my Code Monkey podcast. Some folks have asked me to share the script I used.
You'll need a fairly normal linux system with bash and ffmpeg installed.
Put Copies of Your Slides Into a Temp Directory
These files will be renamed, overwritten, etc! So only use copies of your pictures!
Prepare Them For Conversion
# Change file format to png if you can
# ====================================
GLOBIGNORE="*.png"
for f in *; do
ffmpeg -i "$f" "${f%.*}.png"
done
unset GLOBIGNORE;
# Delete everything not a png
# ===========================
GLOBIGNORE="*.png"
rm *
unset GLOBIGNORE;
Number Them In The Order You Want Them To Appear
# Randomly numerize the files
# ===========================
((ls) | shuf) | grep -v "/$" | cat -n | while read n f; do mv -n "${f}" "$(printf "%04d" $n).png" --; done
Here I just autonumbered a randomized list, ie, ls | shuf
You can also tweak the ls command to sort by date, size, etc
Figure Out How Long The mp3 File Is
# Get the number of seconds in the target mp3 file
# For our sample here, we're using the song 'code monkey', ie ../CodeMonkey.mp3 which is in the parent directory
# ===================================================
timegoal=$(ffmpeg -i "../CodeMonkey.mp3" 2>&1 | grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
Note that "CodeMonkey.mp3" is my target background music file. Find one you like
Hook It All Up
# Attach mp3 to video of slides
# This has an output file of out.mp4 in the parent directory
# You need a target number of frames for each slide
# It's 1/x where x is the number of seconds for each slide
# ie, 1/2 means 1 slide every 2 seconds
# likewise 2 (like our example) means 2 slides per second
ffmpeg -r 2 -t $timegoal -i "%04d.png" -i ../CodeMonkey.mp3 -c:v libx264 -pix_fmt yuv420p -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" ../out.mp4
That's It!
You should be able to copy/pase each of these sections into your linux shell.
None of this is tested for production!
Optional
You may want to include an opening and/or closing section.
That's beyond the scope of this tutorial, but some video platforms will allow you to add this when you upload the video. There's also plenty of examples using bash on the net. Let me know if you'd like me to show this as well
You can also change the frame size/codec/etc of the resulting video. That's also outside the scope of this tutorial
All Together Now
You can also find this on my bash-snippets repo on github
#!/bin/bash
# Takes path to mp3 file, framerate, and name of output
# Initialize variables
MP3FILE="${1:-mymp3.mp3}"
FRAMERATE="${2:-2}"
MONTAGENAME="${3:-mymontage.mp4}"
# Change file format to png if you can
# ====================================
GLOBIGNORE="*.png"
for f in *; do
ffmpeg -i "$f" "${f%.*}.png"
done
unset GLOBIGNORE;
# Delete everything not a png
# ===========================
GLOBIGNORE="*.png"
rm *
unset GLOBIGNORE;
# Randomly numerize the files
# ===========================
((ls) | shuf) | grep -v "/$" | cat -n | while read n f; do mv -n "${f}" "$(printf "%04d" $n).png" --; done
# Get the number of seconds in the target mp3 file
# For our sample here, we're using the song 'code monkey', ie ../CodeMonkey.mp3 which is in the parent directory
# ===================================================
timegoal=$(ffmpeg -i "$MP3FILE" 2>&1 | grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
# Attach mp3 to video of slides
# This has an output file of out.mp4 in the parent directory
# You need a target number of frames for each slide
# It's 1/x where x is the number of seconds for each slide
# ie, 1/2 means 1 slide every 2 seconds
# likewise 2 (like our example) means 2 slides per second
ffmpeg -r "$FRAMERATE" -t $timegoal -i "%04d.png" -i "$MP3FILE" -c:v libx264 -pix_fmt yuv420p -vf "scale=1280:720, pad=ceil(iw/2)*2:ceil(ih/2)*2" -preset slow -crf 18 "$MONTAGENAME"