简体   繁体   中英

Auto Batch Process - Trim, change bit rate and then normalize wav files via sox on Windows

I would like to batch process wav files in a folder:

  1. Firstly trimming the files to 5secs and placing the trimmed files in a folder called "Trim5s", then
  2. Changing the bit rate on the trimmed files (that is in the folder "Trim5s") and saving the new bit rate files derived from the trimmed files in Step 1 above to a folder called "16bit" and then
  3. Normalize the new trimmed + new bitrate files to -1 that is in the "16bit" folder derived in Step 2 above and saving the normalied + trimmed files + new bitrate files to a folder "Norm-1".

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:

  • it's a good idea to work with absolute path rather than "relative path and change the current/working directory" because when things go wrong, one get lost in those working directories; absolute path also allows to chain scripts straightforwardly.
  • It's also a good idea to avoid to chain subfolders, because you're creating a chain of dependencies, which can be hard to follow/test when one change the script or add new ones; this reduces the maintenance overhead/cost. Nevertheless, if you want to do it, feel free to change the target directories in the script.
  • It's a good idea to define functions in a script, this improves the readability and improves the testability, and allows reusability.

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM