Last active
February 1, 2022 12:50
-
-
Save mochki/4220cf31d5d5795e84cdc7b5c2d01319 to your computer and use it in GitHub Desktop.
ffmpeg multitrack to stems
This file contains 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
/* | |
it's a pain in the ass to manage and using ffmpeg is kinda easiest. so grab your multitracks and just make a comma/semicolon | |
separated list. | |
vox=Ld Vocals.mp3,Vocal FX.mp3,BGV 1.mp3,Adlibs.mp3,BGV 2.mp3,BGV 3.mp3; | |
drums=Snare.mp3,Percussion.mp3,Claps.mp3,Kick.mp3; | |
bass=808.mp3,Pads.mp3; | |
other=Guitar.mp3,Strings.mp3 | |
this generates a string (multiple ffmpeg commands) that makes the 4 stems that you can just run in your terminal. | |
the stems will be named after the label on the left of the '=' | |
run in the directory the files are in. if you need help getting ffmpeg... | |
make the 'config' into one line: | |
vox=Ld Vocals.mp3,Vocal FX.mp3,BGV 1.mp3,Adlibs.mp3,BGV 2.mp3,BGV 3.mp3;drums=Snare.mp3,Percussion.mp3,Claps.mp3,Kick.mp3;bass=808.mp3,Pads.mp3;other=Guitar.mp3,Strings.mp3 | |
to make stem files, just use ffmpeg's amerge, it's simple and diverse enough. | |
> ffmpeg -i a.mp3 -i b.mp3 -i c.mp3 -filter_complex "[0:a][1:a][2:a]amerge=inputs=3,pan=stereo|c0<c0+c2+c4|c1<c1+c3+c5[a]" -map "[a]" "d.mp3" | |
this is what you run to merge 3 tracks into one stem. figure out the pattern for n tracks | |
i just open up a terminal -> node or chrome -> inspect and run quick scripts. i realize my life is ratchet | |
also realized i wrote this fluid enough to support any config, not just 4 stems like what fits my needs. whoops | |
*/ | |
const stemConfig = 'vox=Ld\ Vocals.mp3,Vocal\ FX.mp3,BGV\ 1.mp3,Adlibs.mp3,BGV\ 2.mp3,BGV\ 3.mp3;drums=Snare.mp3,Percussion.mp3,Claps.mp3,Kick.mp3;bass=808.mp3,Pads.mp3;other=Guitar.mp3,Strings.mp3' | |
const generateCmds = stemConfig => stemConfig.split(';').map(config => [config.split('=')[0], config.split('=')[1].split(',').map(track => `"${track}"`)]).map(([output, tracks]) => `ffmpeg -i ${tracks.join(' -i ')} -filter_complex "${tracks.map((_,i) => `[${i}:a]`).join('')}amerge=inputs=${tracks.length},pan=stereo|c0<${`c${tracks.map((_,i) => i * 2).join('+c')}`}|c1<${`c${tracks.map((_,i) => i * 2 + 1).join('+c')}`}[a]" -map "[a]" "${output}.mp3"`).join(' && ') | |
// one liners makes copy pasta easier and it's a sad world we live in that i live by this. here, it's not magic: | |
stemConfig | |
.split(';') | |
.map((config) => [ | |
config.split('=')[0], | |
config | |
.split('=')[1] | |
.split(',') | |
.map((track) => `"${track}"`), | |
]) // [['vox', [...]], ['drums', [...]], ...] | |
.map( | |
([output, tracks]) => | |
`ffmpeg \ | |
-i ${tracks.join(' -i ')} \ | |
-filter_complex "${tracks.map((_, i) => `[${i}:a]`).join('')}amerge=inputs=${tracks.length},pan=stereo|c0<${`c${tracks.map((_, i) => i * 2).join('+c')}`}|c1<${`c${tracks.map((_, i) => i * 2 + 1).join('+c')}`}[a]" \ | |
-map "[a]" \ | |
"${output}.mp3"` | |
) | |
.join(' && '); // just to run them all at once | |
/* | |
to do | |
don't hard code mp3 you idiot | |
lol knowing ffmpeg i could do this all with one run isntead of &&-ing | |
ehh good enough, im not gonna explain the filter complex. DM for deets hahaha | |
*/ | |
// ugg for peak lazy (chrome) (plus that prefix thing haha) | |
const CMDtoClip = (stemConfig, prefix = '') => copy(stemConfig.split(';').map(config => [config.split('=')[0], config.split('=')[1].split(',').map(track => `"${track}"`)]).map(([output, tracks]) => `ffmpeg -i ${tracks.join(' -i ')} -filter_complex "${tracks.map((_,i) => `[${i}:a]`).join('')}amerge=inputs=${tracks.length},pan=stereo|c0<${`c${tracks.map((_,i) => i * 2).join('+c')}`}|c1<${`c${tracks.map((_,i) => i * 2 + 1).join('+c')}`}[a]" -map "[a]" "${prefix}${output}.mp3"`).join(' && ')) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment