I would like to batch process wav files in a folder:
This is my folder structure:
MainFolder
|____file1.wav
|____file2.wav
|____Trim5s
|____file1_trim5s.wav
|____file2_trim5s.wav
|____16bit
|____file1_trim5s_16bit.wav
|____file2_trim5s_16bit.wav
|____Norm-1
|____file1_trim5s_16bit_Norm-1.wav
|____file2_trim5s_16bit_Norm-1.wav
Currently I do the steps manually each time for each step. Here are the Windows cmd
commands:
Step 1: Trim files to secs:
for %i in (*.wav) do sox -S "%i" "Trim5s\%~ni_trim5.wav" trim 0 5
I then manually change the directory to "Trim5s" and run the second step:
Step 2: Change bit depth to 16bits in the "Trim5s" folder:
for %i in (*.wav) do sox -S "%i" -b 16 "16bit\%~ni_16bit.wav"
Then I manually change the directory again to the folder "16bit" and run the third step:
Step 3: Normalize files in the 16bit folder:
for %i in (*.wav) do sox -S --norm=-1 "%i" "Norm-1\%~ni_norm-1.wav"
Is there a way to automate this process where I can automatically do all 3 process? That is, convert the three manual steps given above to a nested loop? Can the nested loop be accomplished or is there a better approach to carry out the 3 tasks mentioned above automatically from the MainFolder?
This is what I tried so far:
for %i in (*.wav) do sox -S "%i" "Trim5s\%~ni_trim-5.wav" trim 0 0.5 & cd Trim5s & for %j in (*.wav) do sox -S "%j" -b 16 "..\16bit\%~nj_16bit.wav" & cd 16bit & for %k in (*wav) do sox -S --norm=-1 "%k" "..\norm-1\%~nk_norm-1.wav"
Any help will be greatly appreciated!
Few comments:
In this script I define first few functions for solving small problems and at the end, it's the actual batch processing:
function ToColor($color) { process { Write-Host $_ -ForegroundColor $color } }
function Trim($source, $target) { "sox -S $source $target trim 0 5" | ToColor "green" }
function To16bits($source, $target) { "sox -S $source -b 16 $target" | ToColor "green" }
function Norm($source, $target) { "sox -S --norm=-1 $source $target" | ToColor "green" }
function AddTag($ori, $tag) { return $ori.name -replace '(\w+)(\.wav)', ('$1_' + $tag + '$2') }
function AddDir($ori, $name) { New-Item -Path $ori -Name $name -ItemType "directory" -Erroraction SilentlyContinue }
function Batch($func, $oriSubDir, $newDir, $tag) {
AddDir $dir $newDir
$oriDir = "$dir$oriSubDir"
$targetDir = "$dir$newDir"
$files = Get-ChildItem *.wav -Path $oriDir
"applying <<$tag>> to $(($files | Measure-Object).Count) files from <$oriDir> to <$targetDir>" | ToColor "yellow"
foreach($file in $files) {
$target = AddTag $file $tag
Invoke-Command $func -ArgumentList "$oriDir/$file", "$targetDir/$target"
New-Item -Path "$targetDir" -ItemType "file" -Name $target # mocks creation of processed file
}
}
function ProcessFiles() {
foreach ($dir in $targetList) {
"using original files from $dir" | ToColor "yellow"
Batch ${function:Trim} "" "trim5s" "trim"
Batch ${function:To16bits} "trim5s" "16bits" "16bits"
Batch ${function:Norm} "16bits" "Norm-1" "norm"
}
}
$targetList = $args | Where-Object {Test-Path $_ -PathType Container}
$count = ($targetList | Measure-Object).Count
if($count -eq 0) {
"usage: Start-BatchSox.ps1 dir1 [dir2 ...]" | ToColor "yellow"
return
}
"Got $count directories to process" | ToColor "yellow"
ProcessFiles
In order to have the actual behavior, you need to comment the New-Item
line and change the process command (as text) to actual command, eg.,
"sox -S $source $target trim 0 5" | ToColor "green"
to:
sox -S $source $target trim 0 5
The script can be put in Start-BatchSox.ps1
which can be put somewhere in your path; then you can call it from anywhere (see the "usage" message).