Releases: jstrait/wavefile
v1.1.2
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 inInvalidFormatError
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 is1
, 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 was22
, the format code was1
, and the value of bytes 16 and 17 was4
(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 was1
, 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 is1
, the"fmt "
chunk doesn't have a chunk extension. - Thanks to @CromonMS for reporting this as an issue.
- There was a special case where a file like this could be opened correctly. If the format code was
-
Bug Fix: Files in WAVE_FORMAT_EXTENSIBLE format with a missing or incomplete
"fmt "
chunk extension can no longer be opened usingReader.new
.Previously, a
Reader
instance could be constructed for a file like this, but the relevant fields on the object returned byReader#native_format
would containnil
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 aReader
instance from them. After this fix, attempting to do so will causeInvalidFormatError
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, butReader#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
- Removes
warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
output when reading a file with asmpl
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
- Can read
smpl
chunk data from files that contain this kind of chunk. If a *.wav file contains asmpl
chunk, thenReader.sampler_info
will return aSamplerInfo
instance with the relevant data (ornil
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 thedata
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 adata
chunk says it has 2000 sample frames, but there are only 1000 sample frames remaining in the file, then after callingReader.read(1500)
,Reader.current_sample_frame
will have a value of1000
, not1500
. (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
v1.0.0
- 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 aReader
instance that is already closed no longer raisesReaderClosedError
. Instead, it does nothing. Similarly, callingWriter.close
on aWriter
instance that is already closed no longer raisesWriterClosedError
. Thanks to @kylekyle for raising this as an issue. - Better compatibility when writing Wave files.
Writer
will now write files using a format calledWAVEFORMATEXTENSIBLE
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).
- A file will automatically be written using
- 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 benil
, 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.
- Example:
- 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])
- Example:
- Errors raised by
Format.new
are improved to provide more detail.
v0.8.1
v0.8.0
- 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.
- The same formats supported in "vanilla" Wave files are supported when reading
- Notes/Limitations
Reader
andWriter
can now be constructed using with an openIO
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 aString
). 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 ofIOError
) when attempting to read from a closedReader
instance. However,ReaderClosedError
extendsIOError
, so this should be a backwards compatible change. - A
WriterClosedError
is now raised (instead ofIOError
) when attempting to read from a closedWriter
instance. However,WriterClosedError
extendsIOError
, so this should be a backwards compatible change. - Backwards Incompatible Changes
Reader.file_name
andWriter.file_name
have been removed. When aReader
orWriter
instance is constructed from anIO
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, useFormat.new(:mono, :pcm_16, 44100)
.
v0.7.0
- The minimum supported Ruby version is now 1.9.3 - earlier versions are no longer supported.
- New method:
Reader.native_format
. Returns aFormat
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 newReader
instance and useReader.native_format()
- this will return aFormat
instance with the same info that would have been returned byReader.info()
.- Similarly, the
Info
class has been removed, due toReader.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 callingReader.native_format()
on this instance, to get format information for files not supported by this gem. - New method:
Reader.readable_format?
returnstrue
if the file is a valid format that the gem can read,false
otherwise. Reader.read()
andReader.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, ifReader.readable_format?
returns false, any subsequent calls toReader.read()
orReader.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
- 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
- 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
, andWriter.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 returningnil
(Thanks to James Tunnell (https://github.com/jamestunnell) for reporting this). Info.duration
now returns aDuration
object, instead of a hash.Info.sample_count
has been renamedsample_frame_count
.