, SubBlock
, Dummy(..)
+ , DecorrTerms(..)
+ , DecorrWeights(..)
+ , DecorrSamples(..)
+ , RIFFHeader(..)
+ , RIFFTrailer(..)
, Unknown(..)
)
where
+import Codec.Audio.WavPack.Internal
import Control.Monad
import Data.Binary
import Data.Binary.Get
import Data.Binary.Put
import Data.Bits
import qualified Data.ByteString.Lazy as L
+import Data.Int
import Data.Typeable
+import qualified Data.Vector.Unboxed as UV
import Prelude.Unicode
-- | Type class for every metadata sub-blocks.
-- | Get the metadata ID without odd-size bit nor large-block bit
-- (mandatory).
metaID ∷ α → Word8
- -- | Get the size of metadata (optional).
+ -- | Get the size of metadata, excluding the metadata header
+ -- (optional).
metaSize ∷ α → Word32
metaSize = fromIntegral ∘ L.length ∘ runPut ∘ put
-- | Cast a 'SubBlock' to this type of metadata (optional).
instance Binary SubBlock where
put (SubBlock a)
= let size = metaSize a
- oddBit = if odd size then 0x40 else 0
- largeBit = if size > 255 then 0x80 else 0
+ size' = size + 1
+ oddBit = if odd size then 0x40 else 0
+ largeBit = if size > 0x1FE then 0x80 else 0
idWord = metaID a .|. oddBit .|. largeBit
in
do putWord8 idWord
- if size > 255 then
+ if size > 0x1FE then
-- Don't forget about the endianness.
- do putWord8 $ fromIntegral $ (size `shiftR` 1) .&. 0xFF
- putWord8 $ fromIntegral $ (size `shiftR` 9) .&. 0xFF
- putWord8 $ fromIntegral $ (size `shiftR` 17) .&. 0xFF
+ do putWord8 $ fromIntegral $ (size' `shiftR` 1) .&. 0xFF
+ putWord8 $ fromIntegral $ (size' `shiftR` 9) .&. 0xFF
+ putWord8 $ fromIntegral $ (size' `shiftR` 17) .&. 0xFF
else
- putWord8 $ fromIntegral $ (size `shiftR` 1) .&. 0xFF
+ putWord8 $ fromIntegral $ (size' `shiftR` 1) .&. 0xFF
put a
when (odd size) $ putWord8 0
return $ runGet (getSubBlock rawID) subb
where
getSubBlock ∷ Word8 → Get SubBlock
- getSubBlock 0x00 = fmap SubBlock (get ∷ Get Dummy)
+ getSubBlock 0x00 = fmap SubBlock (get ∷ Get Dummy )
+ getSubBlock 0x02 = fmap SubBlock (get ∷ Get DecorrTerms )
+ getSubBlock 0x03 = fmap SubBlock (get ∷ Get DecorrWeights)
+ getSubBlock 0x04 = fmap SubBlock (get ∷ Get DecorrSamples)
+ getSubBlock 0x21 = fmap SubBlock (get ∷ Get RIFFHeader )
+ getSubBlock 0x22 = fmap SubBlock (get ∷ Get RIFFTrailer )
getSubBlock unknownID
= if unknownID .&. 0x20 ≡ 0 then
fail ("Unknown WavPack metadata ID: " ⧺ show unknownID)
put = putLazyByteString ∘ flip L.replicate 0x00 ∘ fromIntegral ∘ dumSize
get = fmap (Dummy ∘ fromIntegral) remaining
+-- | Decorrelation terms and deltas.
+data DecorrTerms
+ = DecorrTerms {
+ -- | @[ (term, delta) ]@
+ dectVec ∷ !(UV.Vector (Int8, Int8))
+ }
+ deriving (Eq, Show, Typeable)
+
+instance Metadata DecorrTerms where
+ metaID _ = 0x02
+ metaSize = fromIntegral ∘ UV.length ∘ dectVec
+
+instance Binary DecorrTerms where
+ put = UV.mapM_ (putWord8 ∘ packDT) ∘ dectVec
+ where
+ packDT ∷ (Int8, Int8) → Word8
+ packDT (term, δ)
+ = fromIntegral ( (term + 5 .&. 0x1F)
+ .|.
+ ((δ `shiftL` 5) .&. 0xE0)
+ )
+ get = do n ← remaining
+ vec ← UV.replicateM (fromIntegral n) $ fmap unpackDT getWord8
+ -- THINKME: unpack.c(read_decorr_terms) reverses the
+ -- order only when they are decoding. I don't know why so
+ -- I leave it unreversed for now.
+ return $ DecorrTerms vec
+ where
+ unpackDT ∷ Word8 → (Int8, Int8)
+ unpackDT w
+ = let term = (fromIntegral $ w .&. 0x1F) - 5
+ δ = fromIntegral $ (w `shiftR` 5) .&. 0x07
+ in
+ (term, δ)
+
+-- | Decorrelation weights.
+data DecorrWeights
+ = DecorrWeights {
+ -- | For mono blocks, this is a weight vector for the single
+ -- channel. For stereo blocks, it's interleaved as A, B, A, B,
+ -- ...
+ decwVec ∷ !(UV.Vector Int16)
+ }
+ deriving (Eq, Show, Typeable)
+
+instance Metadata DecorrWeights where
+ metaID _ = 0x03
+ metaSize = fromIntegral ∘ UV.length ∘ decwVec
+
+instance Binary DecorrWeights where
+ put = UV.mapM_ (putWord8 ∘ packWeight) ∘ decwVec
+ get = do n ← remaining
+ vec ← UV.replicateM (fromIntegral n)
+ $ fmap unpackWeight getWord8
+ -- THINKME: the same caution as DecorrTerms, but never
+ -- try to simply reverse the vector. Think about the
+ -- interleaving.
+ return $ DecorrWeights vec
+
+-- | Decorrelation samples
+data DecorrSamples
+ = DecorrSamples {
+ -- | The decorrelation sample vector stored in the metadata
+ -- as-is. Actual interpretation of the vector depends on the
+ -- number of channels and each corresponding decorrelation
+ -- terms.
+ decsVec ∷ !(UV.Vector Int32)
+ }
+ deriving (Eq, Show, Typeable)
+
+instance Metadata DecorrSamples where
+ metaID _ = 0x04
+ metaSize = fromIntegral ∘ (⋅ 2) ∘ UV.length ∘ decsVec
+
+instance Binary DecorrSamples where
+ put = UV.mapM_ (putWord16le ∘ fromIntegral ∘ log2s) ∘ decsVec
+ get = do n ← remaining
+ vec ← UV.replicateM (fromIntegral $ n `div` 2)
+ $ fmap (exp2s ∘ fromIntegral) getWord16le
+ return $ DecorrSamples vec
+
+-- | RIFF header for .wav files (before audio)
+data RIFFHeader
+ = RIFFHeader {
+ riffHeader ∷ L.ByteString
+ }
+ deriving (Eq, Show, Typeable)
+
+instance Metadata RIFFHeader where
+ metaID _ = 0x21
+
+instance Binary RIFFHeader where
+ put = putLazyByteString ∘ riffHeader
+ get = fmap RIFFHeader getRemainingLazyByteString
+
+-- | RIFF trailer for .wav files (after audio)
+data RIFFTrailer
+ = RIFFTrailer {
+ riffTrailer ∷ L.ByteString
+ }
+ deriving (Eq, Show, Typeable)
+
+instance Metadata RIFFTrailer where
+ metaID _ = 0x22
+
+instance Binary RIFFTrailer where
+ put = putLazyByteString ∘ riffTrailer
+ get = fmap RIFFTrailer getRemainingLazyByteString
+
-- | Unknown but optional metadata found in the WavPack block.
data Unknown
= Unknown {
- -- | The ID of this unknown metadata.
+ -- | The ID of this unknown metadata without odd-size bit nor
+ -- large-block bit.
unkID ∷ Word8
-- | Raw data; must be less than 2^25 bytes long.
, unkData ∷ L.ByteString
instance Metadata Unknown where
metaID = unkID
- metaSize = fromIntegral ∘ L.length ∘ unkData
instance Binary Unknown where
put = putLazyByteString ∘ unkData
- get = (⊥)
+ get = error "unsupported operation"