Copyright | © 2016–2017 Mark Karpov |
---|---|
License | BSD 3 clause |
Maintainer | Mark Karpov <markkarpov92@gmail.com> |
Stability | experimental |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This module provides a safe interface that allows to manipulate WAVE files in their “classic” form as well as files in the RF64 format https://tech.ebu.ch/docs/tech/tech3306-2009.pdf. RF64 adds the ability to store files larger than 4 Gb.
The main feature of the API is that it does not allow the user to
duplicate information and introduce errors in that way. For example,
block align may be calculated from other parameters of audio stream, thus
we do not store it in the Wave
record and do not allow user to specify
it. We provide, however, a way to calculate it given Wave
record, see
waveBlockAlign
. The same is done for channels. Channel mask is a more
general means of providing information about number of channels and
corresponding speaker positions, thus we only store channel mask in
user-friendly form, and number of channels can be derived from that
information.
Another feature of the library is that it does not dictate how to
read/write audio data. What we give is the information about audio data
and offset in file where it begins. To write data the user may use a
callback that receives a Handle
as an argument. Size of data block is
deduced automatically for you. Exclusion of audio data from consideration
makes the library pretty fast and open to different ways to handle audio
data itself, including using foreign code (such as C).
The library provides control over all parts of WAVE file that may be of
interest. In particular, it even allows to write arbitrary chunks between
fmt
and data
chunks, although it's rarely useful (and may actually
confuse buggy applications that don't know how to skip unknown chunks).
- data Wave = Wave {}
- data WaveFormat
- data SampleFormat
- data SpeakerPosition
- = SpeakerFrontLeft
- | SpeakerFrontRight
- | SpeakerFrontCenter
- | SpeakerLowFrequency
- | SpeakerBackLeft
- | SpeakerBackRight
- | SpeakerFrontLeftOfCenter
- | SpeakerFrontRightOfCenter
- | SpeakerBackCenter
- | SpeakerSideLeft
- | SpeakerSideRight
- | SpeakerTopCenter
- | SpeakerTopFrontLeft
- | SpeakerTopFrontCenter
- | SpeakerTopFrontRight
- | SpeakerTopBackLeft
- | SpeakerTopBackCenter
- | SpeakerTopBackRight
- data WaveException
- waveByteRate :: Wave -> Word32
- waveBitRate :: Wave -> Double
- waveBitsPerSample :: Wave -> Word16
- waveBlockAlign :: Wave -> Word16
- waveChannels :: Wave -> Word16
- waveDuration :: Wave -> Double
- speakerMono :: Set SpeakerPosition
- speakerStereo :: Set SpeakerPosition
- speakerQuad :: Set SpeakerPosition
- speakerSurround :: Set SpeakerPosition
- speaker5_1 :: Set SpeakerPosition
- speaker7_1 :: Set SpeakerPosition
- speaker5_1Surround :: Set SpeakerPosition
- speaker7_1Surround :: Set SpeakerPosition
- readWaveFile :: MonadIO m => FilePath -> m Wave
- writeWaveFile :: MonadIO m => FilePath -> Wave -> (Handle -> IO ()) -> m ()
Types
Representation of “essential” information about a WAVE file. Every
field in this record provides orthogonal piece of information, so no
field can be calculated from other fields. The fields are complemented by
the following functions that calculate some derivative parameters:
waveByteRate
, waveBitRate
, waveBitsPerSample
, waveBlockAlign
, and
waveChannels
.
Wave | |
|
data WaveFormat Source #
WaveFormat
as flavor of WAVE file.
WaveVanilla | Classic WAVE file, 4 Gb size limitation |
WaveRF64 | WAVE file with RF64 extension |
data SampleFormat Source #
Sample formats with associated bit depth (when variable).
SampleFormatPcmInt Word16 | Unsigned/signed integers, the argument is the number of bits per sample (8 bit and less are encoded as unsigned integers). |
SampleFormatIeeeFloat32Bit | Samples are 32 bit floating point numbers. |
SampleFormatIeeeFloat64Bit | Samples are 64 bit floating point numbers. |
data SpeakerPosition Source #
Speaker positions clarifying which exactly channels are packed in the WAVE file.
SpeakerFrontLeft | Front left |
SpeakerFrontRight | Front right |
SpeakerFrontCenter | Front center |
SpeakerLowFrequency | Sub-woofer |
SpeakerBackLeft | Back left |
SpeakerBackRight | Back right |
SpeakerFrontLeftOfCenter | Front left of center |
SpeakerFrontRightOfCenter | Front right of center |
SpeakerBackCenter | Back center |
SpeakerSideLeft | Side left |
SpeakerSideRight | Side right |
SpeakerTopCenter | Top center |
SpeakerTopFrontLeft | Top front left |
SpeakerTopFrontCenter | Top front center |
SpeakerTopFrontRight | Top front right |
SpeakerTopBackLeft | Top back left |
SpeakerTopBackCenter | Top back center |
SpeakerTopBackRight | Top back right |
data WaveException Source #
Exceptions the library can throw.
BadFileFormat String FilePath | Format of given file doesn't look like anything familiar. The first argument is a message explaining what's wrong and the second argument is the file name. |
NonDataChunkIsTooLong ByteString FilePath | The library found a chunk which is not a |
NonPcmFormatButMissingFact FilePath | The specified format is non-PCM, it's vanilla WAVE, but “fact” chunk is missing. |
Derived information
waveByteRate :: Wave -> Word32 Source #
Byte rate of a given Wave
file. Byte rate is the number of bytes it
takes to encode one second of audio.
waveBitRate :: Wave -> Double Source #
Bit rate in kilobits per second.
waveBitsPerSample :: Wave -> Word16 Source #
Number of significant bits in every sample.
waveBlockAlign :: Wave -> Word16 Source #
Block alignment of samples as number of bits per sample (rounded towards next multiplier of 8 if necessary) multiplied by number of channels. This is how many bytes it takes to encode a single multi-channel sample.
waveChannels :: Wave -> Word16 Source #
Total number of channels present in the audio stream.
waveDuration :: Wave -> Double Source #
Duration in seconds.
Common speaker configurations
speakerMono :: Set SpeakerPosition Source #
Front center (C).
speakerStereo :: Set SpeakerPosition Source #
Front left (L), front right (R).
speakerQuad :: Set SpeakerPosition Source #
L, R, back left (Lb), back right (Rb).
speakerSurround :: Set SpeakerPosition Source #
Surround: L, R, front center (C), back center (Cb).
speaker5_1 :: Set SpeakerPosition Source #
L, R, C, Lb, Rb, low frequency (LFE).
speaker7_1 :: Set SpeakerPosition Source #
L, R, C, Lb, Rb, front left-of-center, front right-of-center, LFE.
speaker5_1Surround :: Set SpeakerPosition Source #
L, R, C, side left (Ls), side right (Rs), LFE.
speaker7_1Surround :: Set SpeakerPosition Source #
L, R, C, Lb, Rb, Ls, Rs, LFE.
Reading
Read Wave
record from a WAVE file found at given path. This action
throws WaveException
if the file is malformed and cannot be read.
You can feed vanilla WAVE and RF64 files. The actual format is detected automatically from the contents of the file, not by extension.
PCM with samples in form of integers and floats only are supported, see
SampleFormat
. Addition of other formats will be performed on request,
please feel free to contact me at
https://github.com/mrkkrp/wave/issues.
Finally, if “fmt” chunk is not extensible, we try to guess channel mask from number of channels alone, here is how:
- 1 channel: front center (C)
- 2 channels: front left (L), front right (R)
- 3 channels: L, R, C
- 4 channels: L, R, back left (Lb), back right (Rb)
- 5 channels: L, R, C, Lb, Rb
- 6 channels: L, R, C, LFE, Lb, Rb
- 7 channels: L, R, C, LFE, back center (Cb), side left (Ls), side right (Rs)
- 8 channels: L, R, C, LFE, Lb, Rb, Ls, Rs
- N channels: first N items are taken from
[minBound..maxBound]
ofSpeakerPosition
s
Writing
:: MonadIO m | |
=> FilePath | Where to save the file |
-> Wave | Parameters of the WAVE file |
-> (Handle -> IO ()) | Callback that will be used to write WAVE data |
-> m () |
Write a WAVE file. The waveFileFormat
value specifies in which of the
supported formats the file should be written. The action uses the
provided callback to write WAVE audio data. waveDataOffset
and
waveDataSize
from Wave
are ignored, instead the values are inferred
dynamically after using the callback. Further, the function takes care of
the requirement that WAVE data should end on “even byte boundary”. The
pad byte is written for you if necessary and included in the data size.
The waveSamplesTotal
field will be inferred for PCM (including formats
with samples represented as floats, i.e. always right now), so the
provided value is not used.
If Wave
specifies floating point sample format, the “fact” chunk is
automatically generated and written (the chunk is required for all
non-PCM formats by the spec), but only for vanilla WAVE.