Skip to content

Releases: jstrait/wavefile

v1.1.2

30 Dec 21:50
Compare
Choose a tag to compare

This version fixes several edge case bugs related to reading a *.wav file's "fmt " chunk. In particular, reading a "fmt " chunk that has extra trailing bytes; reading a "fmt " chunk in WAVE_FORMAT_EXTENSIBLE format whose chunk extension is missing, incomplete, or has extra trailing bytes; and reading a "fmt " chunk whose chunk extension is too large to fit in the chunk. In short, some valid files that were previously rejected can now be read, and some invalid files are handled more properly.

The full details:

  • Bug Fix: Files that have extra bytes at the end of the "fmt " chunk can now be read.

    If the format code is 1, the "fmt " chunk has extra bytes if the chunk body size is greater than 16 bytes. Otherwise, "extra bytes" means the chunk contains bytes after the chunk extension (not including the required padding byte for an odd-sized chunk).

    Previously, attempting to open certain files like this via Reader.new would result in InvalidFormatError being raised with a misleading "Not a supported wave file. The format chunk extension is shorter than expected." message. This was misleading because if the format code is 1, the "fmt " chunk won't actually have a chunk extension, and for other format codes the chunk extension might actually be the expected size or larger. When reading a file like this, any extra data in the "fmt " chunk beyond what is expected based on the relevant format code will now be ignored.

    • There was a special case where a file like this could be opened correctly. If the format code was 1, and the value of bytes 16 and 17 (0-based), when interpreted as a 16-bit unsigned little-endian integer, happened to be the same as the number of subsequent bytes in the chunk, the file could be opened without issue. For example, if the "fmt " chunk size was 22, the format code was 1, and the value of bytes 16 and 17 was 4 (when interpreted as a 16-bit unsigned little-endian integer), the file could be opened correctly.
    • There was another special case where InvalidFormatError would be incorrectly raised, but the error message would be different (and also misleading). If the format code was 1, and there was exactly 1 extra byte in the "fmt " chunk (i.e. the chunk size was 17 bytes), the error message would be "Not a supported wave file. The format chunk is missing an expected extension." This was misleading because when the format code is 1, the "fmt " chunk doesn't have a chunk extension.
    • Thanks to @CromonMS for reporting this as an issue.
  • Bug Fix: Files in WAVE_FORMAT_EXTENSIBLE format with a missing or incomplete "fmt " chunk extension can no longer be opened using Reader.new.

    Previously, a Reader instance could be constructed for a file like this, but the relevant fields on the object returned by Reader#native_format would contain nil or "" values for these fields, and no sample data could be read from the file. Since files like this are missing required fields that don't necessarily have sensible default values, it seems like it shouldn't be possible to create a Reader instance from them. After this fix, attempting to do so will cause InvalidFormatError to be raised.

  • Bug Fix: Files in WAVE_FORMAT_EXTENSIBLE format that have extra bytes at the end of the "fmt " chunk extension can now be read.

    This is similar but different from the first bug above; that bug refers to extra bytes after the chunk extension, while this bug refers to extra bytes inside the chunk extension. A WAVE_FORMAT_EXTENSIBLE "fmt " chunk extension has extra bytes if it is larger than 22 bytes.

    Previously, a Reader instance could be constructed for a file like this, but Reader#native_format#sub_audio_format_guid would have an incorrect value, and sample data could not be read from the file. After this fix, this field will have the correct value, and if it is one of the supported values then sample data can be read. Any extra data at the end of the chunk extension will be ignored.

    Implicit in this scenario is that the "fmt " chunk has a stated size large enough to fit the oversized chunk extension. For cases where it doesn't, see the next bug fix below.

  • Bug Fix: More accurate message on the InvalidFormatError raised when reading a file whose "fmt " chunk extension is too large to fit in the chunk.

    The message will now correctly state that the chunk extension is too large, rather than "Not a supported wave file. The format chunk extension is shorter than expected.". As an example of what "too large" means, if a "fmt " chunk has a size of 50 bytes, then any chunk extension larger than 32 bytes will be too large and overflow out of the chunk, since a chunk extension's content always starts at byte 18 (0-based).

v1.1.1

29 Dec 18:16
Compare
Choose a tag to compare
  • Removes warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call output when reading a file with a smpl chunk using Ruby 2.7.0. (And presumably, higher Ruby versions as well, but Ruby 2.7.0 is the most recent Ruby version at the time of this release).

v1.1.0

20 Jan 18:09
Compare
Choose a tag to compare
  • Can read smpl chunk data from files that contain this kind of chunk. If a *.wav file contains a smpl chunk, then Reader.sampler_info will return a SamplerInfo instance with the relevant data (or nil otherwise). Thanks to @henrikj242 for suggesting this feature and providing the base implementation.
  • More informative errors raised by Reader.new. When attempting to read an invalid file, the error message now provides more detail about why the file is invalid.
  • Bug Fix: The master RIFF chunk size for files written by the gem will now take into account padding bytes written for child chunks. For example, when writing a file with a data chunk whose body has an odd number of bytes, the master RIFF chunk's size will be 1 byte larger (to take the empty padding byte at the end of the data chunk into account).
  • Bug Fix: If the stated data chunk size is larger than the actual number of bytes in the file, Reader.current_sample_frame will be correct when attempting to read past the end of the chunk. For example, if a data chunk says it has 2000 sample frames, but there are only 1000 sample frames remaining in the file, then after calling Reader.read(1500), Reader.current_sample_frame will have a value of 1000, not 1500. (This bug did not occur for files in which the data chunk listed the correct size).
  • Bug Fix: Fixed off-by-one error in the maximum allowed value for Format#sample rate. The correct maximum sample rate is now 4_294_967_295; previously it allowed a maximum of 4_294_967_296.

v1.0.1

02 Aug 18:38
Compare
Choose a tag to compare

Bug Fix: the file(s) written to an arbitrary IO instance are no longer corrupt if the initial seek position is greater than 0.

v1.0.0

22 Jun 20:50
Compare
Choose a tag to compare
  • Ruby 2.0 or greater is now required - the gem no longer works in Ruby 1.9.3.
  • Backwards incompatible change: Calling Reader.close on a Reader instance that is already closed no longer raises ReaderClosedError. Instead, it does nothing. Similarly, calling Writer.close on a Writer instance that is already closed no longer raises WriterClosedError. Thanks to @kylekyle for raising this as an issue.
  • Better compatibility when writing Wave files. Writer will now write files using a format called WAVEFORMATEXTENSIBLE where appropriate. This is a behind-the-scenes improvement - for most use cases it won't affect how you use the gem, but can result in better compatibility with other programs.
    • A file will automatically be written using WAVEFORMATEXTENSIBLE format if any of the following are true:
      • It has more than 2 channels
      • It uses integer PCM sample format and the bits per sample is not 8 or 16 (in other words, if the sample format is :pcm_24 or :pcm_32).
      • A specific channel->speaker mapping is given (see below).
  • The channel->speaker mapping field can now be read from files that have it defined. For example, if a file indicates that the first sound channel should be mapped to the back right speaker, the second channel to the top center speaker, etc., this can be read using the Reader.format.speaker_mapping field.
    • Example:
      • reader = Reader.new("4_channel_file.wav")
        # [:front_left, :front_right, :front_center, :back_center]
        puts reader.format.speaker_mapping.inspect
        
    • The channel->speaker mapping field isn't present in all Wave files. (Specifically, it's only present if the file uses WAVEFORMATEXTENSIBLE format). For a non-WAVEFORMATEXTENSIBLE file, Reader.native_format.speaker_mapping will be nil, to reflect that the channel->speaker mapping is undefined. Reader.format.speaker_mapping will use a "sensible" default value for the given number of channels.
  • A channel->speaker mapping array can optionally be given when constructing a Format instance. If not given, a default value will be set for the given number of channels.
    • Example:
      • Format.new(4, :pcm_16, 44100, speaker_mapping: [:front_left, :front_right, :front_center, :low_frequency])
  • Errors raised by Format.new are improved to provide more detail.

v0.8.1

22 Jun 20:47
Compare
Choose a tag to compare
  • Fixes an error when frozen string literals are enabled in Ruby 2.3 or higher. (At the time of this release, that means Ruby 2.3 or 2.4). The gem should now work properly when the --enable-frozen-string-literal Ruby option is enabled. Thanks to @samaaron for finding and fixing this!

v0.8.0

22 Jun 20:46
Compare
Choose a tag to compare
  • Wave files using WAVEFORMATEXTENSIBLE format (format code 65534) can now be read.
    • Notes/Limitations
      • The same formats supported in "vanilla" Wave files are supported when reading WAVEFORMATEXTENSIBLE files. That is, PCM (8/16/24/32 bits per sample) or IEEE_FLOAT (32/64 bits per sample).
      • The channel speaker mapping field is not exposed.
      • The number of valid bits per sample must match the sample container size. For example, if a file has a sample container size of 24 bits and each sample is 24 bits, then it can be read. If the container size is 32 bits and each sample is 24 bits, it can't be read.
      • Writing files using WAVEFORMATEXTENSIBLE format is not supported - all files will be written as a "vanilla" file regardless of the number of channels or sample format.
  • Reader and Writer can now be constructed using with an open IO instance, to allow reading/writing using an arbitrary IO-like object (File, StringIO, etc). Previously, they could only be constructed from a file name (given by a String). Thanks to @taf2 for suggesting this feature and providing an example pull request.
  • The buffer size in Reader.each_buffer() is now optional. If not given, a default buffer size will be used.
  • Two Duration objects will now evaluate to equal if they represent the same amount of time, due to an overridden definition of ==. Thanks to @chrylis for suggesting this improvement.
  • A ReaderClosedError is now raised (instead of IOError) when attempting to read from a closed Reader instance. However, ReaderClosedError extends IOError, so this should be a backwards compatible change.
  • A WriterClosedError is now raised (instead of IOError) when attempting to read from a closed Writer instance. However, WriterClosedError extends IOError, so this should be a backwards compatible change.
  • Backwards Incompatible Changes
    • Reader.file_name and Writer.file_name have been removed. When a Reader or Writer instance is constructed from an IO instance, this field wouldn't necessarily have a sensible value. Since I don't know of an obvious use-case for these fields, going ahead and removing them altogether.
    • The long deprecated ability to provide the sample format for a Format instance as an integer (implying PCM format, with the number being the bits per sample) has been removed. For example, this is no longer valid: Format.new(:mono, 16, 44100). Instead, use Format.new(:mono, :pcm_16, 44100).

v0.7.0

22 Jun 20:42
Compare
Choose a tag to compare
  • The minimum supported Ruby version is now 1.9.3 - earlier versions are no longer supported.
  • New method: Reader.native_format. Returns a Format instance with information about the underlaying format of the Wave file being read, which is not necessarily the same format the sample data is being converted to as it's being read.
  • Reader.info() has been removed. Instead, construct a new Reader instance and use Reader.native_format() - this will return a Format instance with the same info that would have been returned by Reader.info().
  • Similarly, the Info class has been removed, due to Reader.info() being removed.
  • Constructing a Reader instance will no longer raise an exception if the file is valid Wave file, but in a format unsupported by this gem. The purpose of this is to allow calling Reader.native_format() on this instance, to get format information for files not supported by this gem.
  • New method: Reader.readable_format? returns true if the file is a valid format that the gem can read, false otherwise.
  • Reader.read() and Reader.each_buffer() will now raise an exception if the file is a valid Wave file, but not a format that the gem can read. Or put differently, if Reader.readable_format? returns false, any subsequent calls to Reader.read() or Reader.each_buffer() will raise an exception.
  • Some constants have been made private since they are intended for internal use.
  • Bug fix: Files will now be read/written correctly on big-endian platforms. Or in other words, sample data is always read as little-endian, regardless of the native endianness of the platform.

v0.6.0

22 Jun 20:39
Compare
Choose a tag to compare
  • Support for reading and writing Wave files containing 24-bit PCM sample data, and the ability to convert buffers containing 24-bit PCM sample data to/from other formats. (Thanks to Rich Orton (https://github.com/richorton) for suggesting this).
  • Reading files with 2 or more channels is now faster.
  • Converting buffers from one format to another is now faster in certain cases.
  • Bug fix: Files containing certain chunks with an odd size are now read properly. According to the Wave file spec, all chunks should be aligned to an even number of bytes. If the chunk has an odd size, a padding byte should be appended to bring the chunk to an even size. The Reader class now properly takes this expected padding byte into account for all chunks when reading files. (Previously it just took this into account for the main 'data' chunk). (Thanks to Andrew Kuklewicz for reporting this).

v0.5.0

22 Jun 20:37
Compare
Choose a tag to compare
  • Added support for reading and writing Wave files containing 32 and 64-bit floating point sample data.
  • Added support for buffers that contain floating point data (i.e., samples between -1.0 and 1.0), including the ability to convert to and from PCM buffers.
  • Added a new Duration object which can be used to calculate the playback time given a sample rate and number of sample frames.
  • New attributes: Reader.current_sample_frame, Reader.total_sample_frames, and Writer.total_sample_frames.
  • Ability to get these attributes as a Duration object as well: Reader.total_duration, Writer.total_duration.
  • The 2nd argument to Format.new now indicates the sample format, not the bits per sample. For example, :pcm_16 or :float_32 instead of 8 or 16. For backwards compatibility, 8, 16, and 32 can still be given and will be interpreted as :pcm_8, :pcm_16, and :pcm_32, but this support might be removed in the future.
  • Bug fix: Wave files are no longer corrupted when an unhandled exception occurs inside a Writer block. (Thanks to James Tunnell (https://github.com/jamestunnell) for reporting and fixing this).
  • Bug fix: Writer.file_name now returns the file name, instead of always returning nil (Thanks to James Tunnell (https://github.com/jamestunnell) for reporting this).
  • Info.duration now returns a Duration object, instead of a hash.
  • Info.sample_count has been renamed sample_frame_count.