3 , ExistentialQuantification
6 -- | WavPack metadata sub-blocks
7 module Codec.Audio.WavPack.Metadata
21 import Data.Binary.Get
22 import Data.Binary.Put
24 import qualified Data.ByteString.Lazy as L
27 import qualified Data.Vector.Unboxed as UV
28 import Prelude.Unicode
30 -- | Type class for every metadata sub-blocks.
31 class (Binary α, Eq α, Show α, Typeable α) ⇒ Metadata α where
32 -- | Get the metadata ID without odd-size bit nor large-block bit
35 -- | Get the size of metadata, excluding the metadata header
38 metaSize = fromIntegral ∘ L.length ∘ runPut ∘ put
39 -- | Cast a 'SubBlock' to this type of metadata (optional).
40 fromSubBlock ∷ SubBlock → Maybe α
41 fromSubBlock (SubBlock a) = cast a
42 -- | Wrap the metadata into 'SubBlock' (optional).
43 toSubBlock ∷ α → SubBlock
46 -- | An opaque sub-block container.
47 data SubBlock = ∀α. Metadata α ⇒ SubBlock α
50 instance Metadata SubBlock where
51 metaID (SubBlock a) = metaID a
52 metaSize (SubBlock a) = metaSize a
56 instance Binary SubBlock where
58 = let size = metaSize a
60 oddBit = if odd size then 0x40 else 0
61 largeBit = if size > 0x1FE then 0x80 else 0
62 idWord = metaID a .|. oddBit .|. largeBit
66 -- Don't forget about the endianness.
67 do putWord8 $ fromIntegral $ (size' `shiftR` 1) .&. 0xFF
68 putWord8 $ fromIntegral $ (size' `shiftR` 9) .&. 0xFF
69 putWord8 $ fromIntegral $ (size' `shiftR` 17) .&. 0xFF
71 putWord8 $ fromIntegral $ (size' `shiftR` 1) .&. 0xFF
73 when (odd size) $ putWord8 0
75 get = do idWord ← getWord8
76 let isOdd = idWord .&. 0x40 ≢ 0
77 isLarge = idWord .&. 0x80 ≢ 0
78 rawID = idWord .&. (complement 0x40) .&. (complement 0x80)
79 adj = if isOdd then -1 else 0
80 size ← if isLarge then
84 return $ ( (fromIntegral sz2 `shiftL` 17) .|.
85 (fromIntegral sz1 `shiftL` 9) .|.
86 (fromIntegral sz0 `shiftL` 1)
89 fmap ((+ adj) ∘ (`shiftL` 1) ∘ fromIntegral) getWord8
90 subb ← getLazyByteString $ fromIntegral (size ∷ Word32)
91 return $ runGet (getSubBlock rawID) subb
93 getSubBlock ∷ Word8 → Get SubBlock
94 getSubBlock 0x00 = fmap SubBlock (get ∷ Get Dummy )
95 getSubBlock 0x02 = fmap SubBlock (get ∷ Get DecorrTerms )
96 getSubBlock 0x03 = fmap SubBlock (get ∷ Get DecorrWeights)
97 getSubBlock 0x21 = fmap SubBlock (get ∷ Get RIFFHeader )
98 getSubBlock 0x22 = fmap SubBlock (get ∷ Get RIFFTrailer )
100 = if unknownID .&. 0x20 ≡ 0 then
101 fail ("Unknown WavPack metadata ID: " ⧺ show unknownID)
103 -- It's unknown but optional. We can safely ignore it.
104 fmap (SubBlock ∘ Unknown unknownID) getRemainingLazyByteString
106 instance Eq SubBlock where
107 (SubBlock a) == (SubBlock b)
110 instance Show SubBlock where
111 show (SubBlock a) = show a
113 -- | Dummy metadata to pad WavPack blocks.
116 -- | Must be less than 2^25 bytes long due to the limitation
117 -- of WavPack specification.
120 deriving (Eq, Show, Typeable)
122 instance Metadata Dummy where
126 instance Binary Dummy where
127 put = putLazyByteString ∘ flip L.replicate 0x00 ∘ fromIntegral ∘ dumSize
128 get = fmap (Dummy ∘ fromIntegral) remaining
130 -- | Decorrelation terms and deltas.
133 -- | @[ (term, delta) ]@
134 dectVec ∷ !(UV.Vector (Int8, Int8))
136 deriving (Eq, Show, Typeable)
138 instance Metadata DecorrTerms where
140 metaSize = fromIntegral ∘ UV.length ∘ dectVec
142 instance Binary DecorrTerms where
143 put = UV.mapM_ (putWord8 ∘ packDT) ∘ dectVec
145 packDT ∷ (Int8, Int8) → Word8
147 = fromIntegral ( (term + 5 .&. 0x1F)
149 ((δ `shiftL` 5) .&. 0xE0)
151 get = do n ← remaining
152 vec ← UV.replicateM (fromIntegral n) $ fmap unpackDT getWord8
153 -- THINKME: unpack.c(read_decorr_terms) reverses the
154 -- order only when they are decoding. I don't know why so
155 -- I leave it unreversed for now.
156 return $ DecorrTerms vec
158 unpackDT ∷ Word8 → (Int8, Int8)
160 = let term = (fromIntegral $ w .&. 0x1F) - 5
161 δ = fromIntegral $ (w `shiftR` 5) .&. 0x07
165 -- | Decorrelation weights.
168 -- | For mono blocks, this is a weight vector for the single
169 -- channel. For stereo blocks, it's interleaved as A, B, A, B,
171 decwVec ∷ !(UV.Vector Int16)
173 deriving (Eq, Show, Typeable)
175 instance Metadata DecorrWeights where
177 metaSize = fromIntegral ∘ UV.length ∘ decwVec
179 instance Binary DecorrWeights where
180 put = UV.mapM_ (putWord8 ∘ packW) ∘ decwVec
182 packW ∷ Int16 → Word8
184 = let w' | w > 1024 = 1024
187 w'' | w' > 0 = w' - ((w' + 64) `shiftR` 7)
189 w''' = (w'' + 4) `shiftR` 3
192 get = do n ← remaining
193 vec ← UV.replicateM (fromIntegral n) $ fmap unpackW getWord8
194 -- THINKME: the same caution as DecorrTerms, but never
195 -- try to reverse the vector simply. Think about the
197 return $ DecorrWeights vec
199 unpackW ∷ Word8 → Int16
204 w'' = (fromIntegral w') `shiftL` 3
205 w''' | w'' > 0 = w'' + ((w'' + 64) `shiftR` 7)
210 -- | RIFF header for .wav files (before audio)
213 riffHeader ∷ L.ByteString
215 deriving (Eq, Show, Typeable)
217 instance Metadata RIFFHeader where
220 instance Binary RIFFHeader where
221 put = putLazyByteString ∘ riffHeader
222 get = fmap RIFFHeader getRemainingLazyByteString
224 -- | RIFF trailer for .wav files (after audio)
227 riffTrailer ∷ L.ByteString
229 deriving (Eq, Show, Typeable)
231 instance Metadata RIFFTrailer where
234 instance Binary RIFFTrailer where
235 put = putLazyByteString ∘ riffTrailer
236 get = fmap RIFFTrailer getRemainingLazyByteString
238 -- | Unknown but optional metadata found in the WavPack block.
241 -- | The ID of this unknown metadata without odd-size bit nor
244 -- | Raw data; must be less than 2^25 bytes long.
245 , unkData ∷ L.ByteString
247 deriving (Eq, Show, Typeable)
249 instance Metadata Unknown where
252 instance Binary Unknown where
253 put = putLazyByteString ∘ unkData
254 get = error "unsupported operation"