#!/usr/bin/env php <?php /** * A thin wrapper around ffmpeg and sox to make it easier to perform * common audio manipulations like cutting to a specific number of * beats at a set BPM, volume adjustments, joining files together, * and converting between file formats or between mono and stereo. * * Setup: * * 1. install ffmpeg and sox * 2. save this file as 'au' in your ~/bin folder * 3. chmod the file to be executable: * * chmod u+x ~/bin/au * * You're all set, now run: * * au help * * Updates: * * https://github.com/jbroadway/au */ if (count ($argv) === 1) { $argv[1] = 'help'; } switch ($argv[1]) { case 'pad': if (count ($argv) < 5) { echo "Usage: au pad file.wav start|end 0.5 [file.wav]\n"; break; } elseif (! file_exists ($argv[2])) { echo "File not found: {$argv[2]}\n"; break; } $pos = $argv[3]; $len = $argv[4]; if ($pos != 'start' && $pos != 'end') { echo "Error: Position must be either 'start' or 'end'.\n"; break; } if (! is_numeric ($len)) { echo "Error: Duration must be a numeric value.\n"; break; } echo 'Padding ' . $pos . ' with ' . $len . "s silence.\n"; if (isset ($argv[5])) { $out = $argv[5]; } else { $out = $argv[2]; } system ( sprintf ( 'sox -n -r 44100 -c 2 au.tmp.silence.wav trim 0.0 %s', $len ) ); if ($pos == 'start') { system ( sprintf ( 'sox au.tmp.silence.wav %s %s', escapeshellarg ($argv[2]), escapeshellarg ($out) ) ); } else { system ( sprintf ( 'sox %s au.tmp.silence.wav %s', escapeshellarg ($argv[2]), escapeshellarg ($out) ) ); } unlink ('au.tmp.silence.wav'); break; case 'cut': if (count ($argv) < 5) { echo "Usage: au cut file.wav 120 16 [file.wav]\n"; break; } elseif (! file_exists ($argv[2])) { echo "File not found: {$argv[2]}\n"; break; } $bpm = $argv[3]; $beats = $argv[4]; if (! is_numeric ($bpm)) { echo "Error: BPM must be a numeric value.\n"; break; } if (! is_numeric ($beats)) { echo "Error: Beats must be a numeric value.\n"; break; } $beat = (float) (60 / $bpm); $length = $beat * $beats; echo 'Cutting to length: ' . $length . "\n"; if (isset ($argv[5])) { $out = $argv[5]; } else { $out = $argv[2]; } system ( sprintf ( 'sox %s %s trim 0.00000 %s', escapeshellarg ($argv[2]), escapeshellarg ($out), $length ) ); break; case 'join': if (count ($argv) < 5) { echo "Usage: au join file1.wav file2.wav [...file3.wav] output.wav\n"; break; } elseif (! file_exists ($argv[2])) { echo "File not found: {$argv[2]}\n"; break; } elseif (! file_exists ($argv[3])) { echo "File not found {$argv[3]}\n"; break; } $tmp = preg_replace ('/\.[a-z0-9]+$/i', '.txt', $argv[count ($argv) - 1]); $out = ''; for ($i = 2; $i < count ($argv) - 1; $i++) { $out .= "file '{$argv[$i]}'\n"; } file_put_contents ($tmp, $out); system ( sprintf ( 'ffmpeg -f concat -i %s -c copy %s', escapeshellarg ($tmp), escapeshellarg ($argv[count ($argv) - 1]) ) ); unlink ($tmp); break; case 'vol': if (count ($argv) < 4) { echo "Usage: au vol file.wav 0.5 [file.wav]\n"; break; } elseif (! file_exists ($argv[2])) { echo "File not found: {$argv[2]}\n"; break; } $vol = $argv[3]; if (! is_numeric ($vol)) { echo "Error: Volume must be a numeric value.\n"; break; } if (isset ($argv[4])) { $out = $argv[4]; } else { $out = $argv[2]; } system ( sprintf ( 'ffmpeg -i %s -af volume=%s %s', escapeshellarg ($argv[2]), $vol, escapeshellarg ($out) ) ); break; case 'mp3': if (count ($argv) < 3) { echo "Usage: au mp3 file.wav [file.mp3]\n"; break; } elseif (! file_exists ($argv[2])) { echo "File not found: {$argv[2]}\n"; break; } if (isset ($argv[3])) { $out = $argv[3]; } else { $out = preg_replace ('/\.([a-z0-9]+)$/i', '.mp3', $argv[2]); } system ( sprintf ( 'ffmpeg -i %s -ab 192k %s', escapeshellarg ($argv[2]), escapeshellarg ($out) ) ); break; case 'wav': if (count ($argv) < 3) { echo "Usage: au wav file.aif [file.wav]\n"; break; } elseif (! file_exists ($argv[2])) { echo "File not found: {$argv[2]}\n"; break; } if (isset ($argv[3])) { $out = $argv[3]; } else { $out = preg_replace ('/\.([a-z0-9]+)$/i', '.wav', $argv[2]); } system ( sprintf ( 'ffmpeg -i %s %s', escapeshellarg ($argv[2]), escapeshellarg ($out) ) ); break; case 'mono': if (count ($argv) < 3) { echo "Usage: au mono file.aif [file.aif]\n"; break; } elseif (! file_exists ($argv[2])) { echo "File not found: {$argv[2]}\n"; break; } if (isset ($argv[3])) { $out = $argv[3]; } else { $out = $argv[2]; } system ( sprintf ( 'ffmpeg -i %s -ac 1 %s', escapeshellarg ($argv[2]), escapeshellarg ($out) ) ); break; case 'stereo': if (count ($argv) < 3) { echo "Usage: au stereo file.aif [file.aif]\n"; break; } elseif (! file_exists ($argv[2])) { echo "File not found: {$argv[2]}\n"; break; } if (isset ($argv[3])) { $out = $argv[3]; } else { $out = $argv[2]; } system ( sprintf ( 'ffmpeg -i %s -ac 2 %s', escapeshellarg ($argv[2]), escapeshellarg ($out) ) ); break; default: echo <<<END Usage: au command [<args>] Available subcommands: cut cut file to specified bpm*beats for making loops join join files together to make one audio file pad pad start or end of file with silence vol adjust the volume of a file mp3 convert to an mp3 of the same name wav convert to a wav of the same name mono convert stereo to mono stereo convert mono to stereo Exampes: # cut verses.wav to 4 bars at 132bpm au cut verses.wav 132 16 verses-cut.wav # convert aif to wav au wav verses.aif # halve the volume of a clip au vol verses.aif 0.5 # join some files au join verse.wav chorus.wav verse.wav song.wav # pad a file with 2 seconds of silence at the end au pad song.wav end 2.0 padded.wav END; break; }