dancasimiro/WAV.jl

`wavappend()` doesn't update header in certain case

Closed this issue · 8 comments

Hi, everyone.

It seems that the function WAV.wavappend doesn't update the header information in the following case:

import WAV 
x1 = rand(16000)
x2 = rand(16000)
WAV.wavwrite(x1, "test.wav", Fs=16000, nbits=16, compression=WAV.WAVE_FORMAT_PCM)
WAV.wavappend(x2, "test.wav")

The code above correctly appends the data; however, when opening the file with, e.g., Audacity, the program reads the first part of the wave file containing x1 only. I think that the header is not updated correctly. It might have something to do with the compression.

By the way: I'm using Julia-0.6.3 (Windows & Linux).

By the way: reading out the field subchunk2size by using

header = read("test.wav", 46)
sum(header[41:44] .* (256.^(0:3)))

yields 32000, which is wrong, since it should be 64000. (Note: subchunk2size equals number_samples * number_channels * bits_per_sample / 8).

I just figured out that the value corresponding to the field chunksize is correct; however, the value corresponding to subchunk2size is wrong. My current workaround is as follows:

# Note: `number_samples` and `path_file` is given. 

# Read header. 
file_stream = open(path_file, "r+")
header = read(file_stream, 46)

# Find characters `data` to determine length (44-bytes or 46-bytes) of header. 
# Hex-string below corresponds to the characters `data`.            
range_matched_sequence = search(header, [0x64, 0x61, 0x74, 0x61])
if !isempty(range_matched_sequence)
    indices_data_size = range_matched_sequence + 4;
    if ==(maximum(indices_data_size), 46)
        warn("File features 46-byte header.")
    end
else
    error("Invalid file header.")
end

# Get value that corresponds to field `blockalign`. 
block_align = sum(header[33:34] .* (256.^(0:1)))
if ~isinteger(block_align)
    error("Corrupted file: non-integer block align.")
end

# Update header. 
header[indices_data_size] = reverse(hex2bytes(hex(number_samples*block_align, 8)))

# Overwrite file's header by updated header. 
seekstart(file_stream)
write(file_stream, header)
close(file_stream)

This way I avoid reading and writing the whole file.

And by the way: Do you assume 44-byte and/or 46-byte header files?

I skimmed through the code and realized that wavappend (or even the whole WAV-package) only supports 44-byte headers. (Please correct me if I'm wrong.) If this is the case, I would suggest that the package should either

  1. throw an error if it appends to a wave file that features a 46-byte header or
  2. converts the 46-byte header into a 44-byte header as follows:
# `header` is an array containing the first 46 bytes of the wave file
if ==(header[17], 0x12)
    warning("Converting 46-byte header into 44-byte header.")
    header[17] = 0x10
    deleteat!(header, [37,38])
end

Hi, everyone. I've found and fixed the bug related to the aforementioned problem. Please go through my pull-request in order to figure out what I've changed.

Thanks @beyondvoid

It’s been a while since I wrote the header code. I’ll take a look and get back to you.

Hi, @dancasimiro. Thanks for taking a look. If you are interested you can find detailed information on the header at http://soundfile.sapp.org/doc/WaveFormat/.

Fixed by pull request #61.