2
$\begingroup$

I am currently working on a project where I would like to read in an audio file, apply a butterworth filter, and then export the filtered audio file while preserving the scale of the amplitude from the original audio file.

I attempted to do this within the seewave and tuneR packages in R using this code:

BD <- readWave(file_name)
short_name <- tools::file_path_sans_ext(file_name)
FBD <- bwfilter(BD, from = 0, to = 250, bandpass = NULL, output = "Wave")
savewav(FBD, f=24000, channel = 1, filename = paste0(short_name,"_filtered250.wav"), rescale=NULL)

I noticed with this code that when viewing the spectrograms and waveforms of the output files compared to the original audio files, that quiet files are getting rescaled significantly (to be much louder than the original file) while loud files are getting rescaled very little. I tested the code without the bwfilter function and found that simply reading the file in and then saving the output with the savewav function from seewave was still causing the same problem. I dug into the rescale argument in savewav and found that NULL normalizes the amplitude to default settings of the package instead of not rescaling at all, and it relies on the normalize function from tuneR.

I then tested with this code to see if I could solve my problem:

  BD <- readWave(file_name)
  short_name <- tools::file_path_sans_ext(file_name)
  normalize(BD, unit = "16", center = TRUE, level = 1, rescale = FALSE, pcm = BD@pcm) 
  savewav(BD, f=24000, channel = 1, filename=paste0(short_name,"_rescaleNULLnormalizeFALSE.wav"), rescale=NULL)

This attempt also scaled up the amplitude (see image below of original .wav file on left and output audio file on right). example of original .wav file compared to output .wav file from this attempt

Next, instead of the savewav function, I used this alternate writeWave function:

BD <- readWave(file_name)
short_name <- tools::file_path_sans_ext(file_name)
writeWave(BD, filename=paste0(short_name,"_writeWave.wav"))

This attempt worked and produced audio files that did not rescale amplitude for quiet, moderate, and loud files. However, when I attempted to add back in the bwfilter function, I received warnings on all of my quiet and moderate files, and an error on the loud file.

 BD <- readWave(file_name)
 short_name <- tools::file_path_sans_ext(file_name)
 FBD <- bwfilter(BD, from = 0, to = 250, bandpass = NULL, output = "Wave")
 writeWave(FBD, filename=paste0(short_name,"_filter0bpNULLwriteWave.wav"))

The warning messages for all the moderate/quiet files were saying in writeWave...: channels' data will be rounded to integers for writing the wave file The output .wav files from this attempt appear to have filtered correctly and the amplitude does not look like it has been rescaled.

The error message for the large file was in writeWave...:for 16-bit Wave files, data range is supposed to be in [-32768, 32767], see ?normalize The loud audio file had a lot of clipping in it from high wind, but I do not understand why the writeWav function worked for the large file before, but not now when I attempt to filter it.

Any advice on how to tackle this problem or alternate code suggestions would be greatly appreciated!

$\endgroup$

1 Answer 1

2
$\begingroup$

I'm not really sure what the question is here, but I'll have a go at addressing some of the behaviours and error messages.

According to the documentation of seewave::savewav savewav will normalize by default. I suspect setting rescale=null, as you have done, results in savewav following the default behaviour of normalising prior to writing, even though such syntax could be easily confused with 'not normalising' which seems to be what you were hoping would happen.

If you prefer using savewav, you can get identical results to writeWave (including the warning message about rounding) by setting:

rescale=c(min(FBD@left),max(FBD@left))

Regarding the warning message:

In writeWave...: channels' data will be rounded to integers for writing the wave file

The bwfilter function is returning a vector of floating point numbers to give a very precise answer. However, writeWave is expecting integer data, so the function appears to be rounding the floating point values to the nearest integer. In my view this warning can be safely ignored and the fractional values aren't likely to be important here.

As for the error message:

In writeWave...:for 16-bit Wave files, data range is supposed to be in [-32768, 32767], see ?normalize

This seems like a potential bug in writeWav in that it will happily read wav files that have values at the limits of the bit-depth, but it won't write them. I think the correct solution here would be to follow up with the developer of tuneR::writeWave. But a potential workaround here would be to check whether any of the data to be written exceeds or equals the data range, and set these to be one less than the lower or upper limits as appropriate (-32767,32766).

I've modified your code to illustrate the various methods:

pathName <- 'w:/LongTermRecording2002_present/Casey2014/'
fileName <- '200_2013-12-25_02-00-00.wav'
short_name <- tools::file_path_sans_ext(fileName)
seewaveName <- paste0(short_name,"seewave_filtered250.wav")
writewaveName <- paste0(short_name,"writewave_filtered250.wav")
x = tuneR::readWave(filename = paste0(pathName,fileName))

FBD <- seewave::bwfilter(x, from = 0, to = 250,
                         bandpass = NULL, output = "Wave")
seewave::savewav(FBD, [email protected], channel = 1, filename = seewaveName,
                 rescale=c(min(FBD@left),max(FBD@left)))
tuneR::writeWave(FBD, filename=writewaveName)
xSeewave <- tuneR::readWave(filename = seewaveName)
xWritewave <- tuneR::readWave(filename = writewaveName)
par(mfrow=c(3,1))
plot(FBD)
plot(xSeewave)
plot(xWritewave)

Plot of results

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.