Search code examples
rstatsoxwav

Batch measurements of .wav files with sox stats


My question is similar to a previous question about "get-mean-amplitude-of-wav-from-sox":

Get Mean amplitude(only) of .wav from sox

I would like to be able to use the stats sox to do batch measurements of 1,000's of .wav files in a directory, and store the results in a data frame or some similar structure I can save as a csv text file.

For one sound file, the code would be:

./sox SampleSound.wav -n stat

Resulting in the following output:

Samples read:          72000000
Length (seconds):   3600.000000
Scaled by:         2147483647.0
Maximum amplitude:     0.778809
Minimum amplitude:    -1.000000
Midline amplitude:    -0.110596
Mean    norm:          0.062671
Mean    amplitude:    -0.008131
RMS     amplitude:     0.172914
Maximum delta:         1.778809
Minimum delta:         0.000000
Mean    delta:         0.014475
RMS     delta:         0.057648
Rough   frequency:         1061
Volume adjustment:        1.000

I would like to: - take batch measurements on 1,000's of sound files in a given directory, - capture the stats output in columns along with the sound file name that was measured, - and export for use as covariates in an analysis in R.

Thanks!

Matthew


Solution

  • First you will need to perform a system call to sox, and capture its output. For example:

    > spam = system("sox worf.wav -n stat 2>&1", intern = TRUE)
    > spam
     [1] "Samples read:             34000" "Length (seconds):      3.083900"
     [3] "Scaled by:         2147483647.0" "Maximum amplitude:     0.999969"
     [5] "Minimum amplitude:    -0.938721" "Midline amplitude:     0.030624"
     [7] "Mean    norm:          0.190602" "Mean    amplitude:    -0.004302"
     [9] "RMS     amplitude:     0.244978" "Maximum delta:         1.340240"
    [11] "Minimum delta:         0.000000" "Mean    delta:         0.051444"
    [13] "RMS     delta:         0.099933" "Rough   frequency:          715"
    [15] "Volume adjustment:        1.000"
    

    Setting intern = TRUE returns the output of the command to a variable. Strangely, sox provides its output to stderr and not stdout, hence the need for 2>&1. The best way forward now is to wrap this in a function which also postprocesses the output of system:

    get_wav_stats = function(wav_file) {
       rough_wav_stats = system(sprintf("sox %s -n stat 2>&1", wav_file), intern = TRUE)
       wav_stats = data.frame(do.call("rbind", strsplit(rough_wav_stats, split = ":")))
       names(wav_stats) = c("variable", "value")
       wav_stats = transform(wav_stats, value = as.numeric(as.character(value)))
       return(wav_stats)
    }
    > spam = get_wav_stats("worf.wav")
    > spam
                variable         value
    1       Samples read  3.400000e+04
    2   Length (seconds)  3.083900e+00
    3          Scaled by  2.147484e+09
    4  Maximum amplitude  9.999690e-01
    5  Minimum amplitude -9.387210e-01
    6  Midline amplitude  3.062400e-02
    7       Mean    norm  1.906020e-01
    8  Mean    amplitude -4.302000e-03
    9  RMS     amplitude  2.449780e-01
    10     Maximum delta  1.340240e+00
    11     Minimum delta  0.000000e+00
    12     Mean    delta  5.144400e-02
    13     RMS     delta  9.993300e-02
    14 Rough   frequency  7.150000e+02
    15 Volume adjustment  1.000000e+00
    

    Next you can wrap this in an apply loop to get all the stats from a given directory:

    # files_dir = list.files("path", full.names = TRUE)
    # For this example I create a mock list:
    files_dir = rep("worf.wav", 10)
    stat_wavs = lapply(files_dir, get_wav_stats)
    > str(stat_wavs)
        List of 10
         $ :'data.frame':   15 obs. of  2 variables:
          ..$ variable: Factor w/ 15 levels "Length (seconds)",..: 13 1 14 2 8 7 6 4 10 3 ...
          ..$ value   : num [1:15] 3.40e+04 3.08 2.15e+09 1.00 -9.39e-01 ...
         $ :'data.frame':   15 obs. of  2 variables:
          ..$ variable: Factor w/ 15 levels "Length (seconds)",..: 13 1 14 2 8 7 6 4 10 3 ...
          ..$ value   : num [1:15] 3.40e+04 3.08 2.15e+09 1.00 -9.39e-01 ...
    << snip >> 
         $ :'data.frame':   15 obs. of  2 variables:
          ..$ variable: Factor w/ 15 levels "Length (seconds)",..: 13 1 14 2 8 7 6 4 10 3 ...
          ..$ value   : num [1:15] 3.40e+04 3.08 2.15e+09 1.00 -9.39e-01 ...
    

    To extract only the value column, which contains the stats you need:

    stats4files = data.frame(do.call("rbind", lapply(stat_wavs, "[[", 2)))
    names(stats4files) = stat_wavs[[1]][[1]]
    rownames(stats4files) = files_dir # this doesn't work actually because I have repeated the same file multiple times :)
    
    > stats4files
       Samples read Length (seconds)  Scaled by Maximum amplitude Minimum amplitude Midline amplitude
    1         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    2         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    3         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    4         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    5         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    6         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    7         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    8         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    9         34000           3.0839 2147483647          0.999969         -0.938721          0.030624
    10        34000           3.0839 2147483647          0.999969         -0.938721          0.030624
       Mean    norm Mean    amplitude RMS     amplitude Maximum delta Minimum delta Mean    delta
    1      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    2      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    3      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    4      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    5      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    6      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    7      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    8      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    9      0.190602         -0.004302          0.244978       1.34024             0      0.051444
    10     0.190602         -0.004302          0.244978       1.34024             0      0.051444
       RMS     delta Rough   frequency Volume adjustment
    1       0.099933               715                 1
    2       0.099933               715                 1
    3       0.099933               715                 1
    4       0.099933               715                 1
    5       0.099933               715                 1
    6       0.099933               715                 1
    7       0.099933               715                 1
    8       0.099933               715                 1
    9       0.099933               715                 1
    10      0.099933               715                 1