--
-- module HybridFilterBank - Frequency to time.
--
-- This code is part of the Experimental Haskell MP3 Decoder, version 0.0.1.
-- Copyright (c) 2008 Bjorn Edstrom
--
-- This software is provided 'as-is', without any express or implied
-- warranty. In no event will the authors be held liable for any damages
-- arising from the use of this software.
--
-- Permission is granted to anyone to use this software for any purpose,
-- including commercial applications, and to alter it and redistribute it
-- freely, subject to the following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software
-- in a product, an acknowledgment in the product documentation would be
-- appreciated but is not required.
--
-- 2. Altered source versions must be plainly marked as such, and must not be
-- misrepresented as being the original software.
--
-- 3. This notice may not be removed or altered from any source
-- distribution.
--
module Codec.Audio.MP3.HybridFilterBank (
mp3HybridFilterBank
,MP3HybridState(..)
,emptyMP3HybridState
) where
import Codec.Audio.MP3.IMDCT
import Codec.Audio.MP3.SynthesisFilterBank
import Codec.Audio.MP3.Tables
import Codec.Audio.MP3.Types
{-
mapBlock :: Int -> ([a] -> [b]) -> [a] -> [b]
mapBlock blocksize func [] = []
mapBlock blocksize func seq =
let (block, rem) = splitAt blocksize seq
in func block ++ mapBlock blocksize func rem
-}
mapBlock :: Int -> ([a] -> b) -> [a] -> [b]
mapBlock blocksize func [] = []
mapBlock blocksize func seq =
let (block, rem) = splitAt blocksize seq
in func block : mapBlock blocksize func rem
-- 'windowWith' windows two signals. For readability, so we can do:
-- signal' = signal `windowWith` win
windowWith :: (Num a) => [a] -> [a] -> [a]
windowWith = zipWith (*)
--
-- mp3IMDCT
--
-- Input: 576 frequency samples and state from the last time the function
-- was called.
-- Output: 576 time domain samples and new state.
--
mp3IMDCT :: BlockFlag -> Int -> [Frequency] -> [Sample] -> ([Sample], [Sample])
mp3IMDCT blockflag blocktype freq overlap =
let (samples, overlap') =
case blockflag of
LongBlocks -> transf (doImdctLong blocktype) freq
ShortBlocks -> transf (doImdctShort) freq
MixedBlocks -> transf (doImdctLong 0) (take 36 freq) <++>
transf (doImdctShort) (drop 36 freq)
samples' = zipWith (+) samples overlap
in (samples', overlap')
where
transf imdctfunc input = unzipConcat $ mapBlock 18 toSO input
where
-- toSO takes 18 input samples b and computes 36 time samples
-- by the IMDCT. These are further divided into two equal
-- parts (S, O) where S are time samples for this frame
-- and O are values to be overlapped in the next frame.
toSO b = splitAt 18 (imdctfunc b)
unzipConcat xs = let (a, b) = unzip xs
in (concat a, concat b)
--
-- doImdctLong, doImdctShort
--
-- IMDCT with windows. This also does the overlapping when short blocks
-- are used.
--
doImdctLong :: Int -> [Frequency] -> [Sample]
doImdctLong blocktype f = imdct 18 f `windowWith` tableImdctWindow blocktype
doImdctShort :: [Frequency] -> [Sample]
doImdctShort f = overlap3 shorta shortb shortc
where
(f1, f2, f3) = splitAt2 6 f
shorta = imdct 6 f1 `windowWith` tableImdctWindow 2
shortb = imdct 6 f2 `windowWith` tableImdctWindow 2
shortc = imdct 6 f3 `windowWith` tableImdctWindow 2
--shorta = imdct 6 [f!!0,f!!3,f!!6,f!!9,f!!12,f!!15] `windowWith` tableImdctWindow 2
--shortb = imdct 6 [f!!1,f!!4,f!!7,f!!10,f!!13,f!!16] `windowWith` tableImdctWindow 2
--shortc = imdct 6 [f!!2,f!!5,f!!8,f!!11,f!!14,f!!17] `windowWith` tableImdctWindow 2
overlap3 a b c =
p1 ++ (zipWith3 add3 (a ++ p2) (p1 ++ b ++ p1) (p2 ++ c)) ++ p1
where
add3 x y z = x+y+z
p1 = [0,0,0, 0,0,0]
p2 = [0,0,0, 0,0,0, 0,0,0, 0,0,0]
splitAt2 :: Int -> [a] -> ([a], [a], [a])
splitAt2 n xs = let (part1, part23) = splitAt n xs
(part2, part3) = splitAt n part23
in (part1, part2, part3)
(<++>) :: ([a], [b]) -> ([a], [b]) -> ([a], [b])
(as, xs) <++> (bs, ys) = (as++bs, xs++ys)
infixr 5 <++>
--
-- mp3AA
--
-- Undo the encoders alias reduction.
--
-- TODO: Rewrite this, clumsy and non-intuitive.
--
mp3AA :: BlockFlag -> Int -> [Frequency] -> [Frequency]
mp3AA blockflag blocktype freq
| blocktype == 2 && blockflag /= MixedBlocks = freq
| blocktype == 2 && blockflag == MixedBlocks =
(take 9 freq) ++ aaHelper (take 18 (drop 9 freq)) ++ (drop 27 freq)
| otherwise =
(take 9 freq) ++ aaHelper (take 558 (drop 9 freq)) ++ (drop 567 freq)
where
aaHelper [] = []
aaHelper chunk = before ++ aaButterfly middle ++ after ++
aaHelper (drop 18 chunk)
where
before = take 1 chunk
middle = take 16 (drop 1 chunk)
after = take 1 (drop 17 chunk)
aaButterfly f = zipWith (-) (take 8 seqcs) (take 8 seqca) ++
zipWith (+) (drop 8 seqcs) (drop 8 seqca)
where
seqcs = zipWith (*) f (reverse cs ++ cs)
seqca = reverse $ zipWith (*) f (reverse ca ++ ca)
cs = [1 / sqrt (1.0 + c**2) | c <- aaCoeff]
ca = [c / sqrt (1.0 + c**2) | c <- aaCoeff]
aaCoeff = [-0.6, -0.535, -0.33, -0.185,
-0.095, -0.041, -0.0142, -0.0037]
{-
mp3AliasCancel :: BlockFlag -> [Frequency] -> [Frequency]
mp3AliasCancel blockflag freq
| blockflag == ShortBlocks = freq
| blockflag == LongBlocks = helper 31
| blockflag == MixedBlocks = helper 1
where
butterflies samples = let samplesa = zipWith (*) samples cs'
samplesb = zipWith (*) (reverse samples) ca'
in zipWith (+) samplesa samplesb
-- Coeffs.
cs' = reverse cs ++ cs
ca' = map (-) (reverse ca) ++ ca
cs = [1 / sqrt (1.0 + c**2) | c <- aaCoeff]
ca = [c / sqrt (1.0 + c**2) | c <- aaCoeff]
aaCoeff = [-0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037]
-}
--
-- mp3FrequencyInvert
--
-- The time samples returned from mp3IMDCT are inverted. This is the same
-- as with bandpass sampling: odd subbands have inverted frequency spectra -
-- invert it by changing signs on odd samples.
--
mp3FrequencyInvert :: [Sample] -> [Sample]
mp3FrequencyInvert = zipWith (*) pattern
where
pattern = cycle $ replicate 18 1 ++ take 18 (cycle [1,-1])
-- Pads a list until it's length is n.
-- padWith 5 0 [1,2,3] == [1,2,3,0,0]
padWith :: Int -> a -> [a] -> [a]
padWith n padding xs = xs ++ replicate (n - length xs) padding
--
-- mp3HybridFilterBank
--
-- Frequency domain to time domain.
--
mp3HybridFilterBank :: BlockFlag -> Int ->
MP3HybridState -> [Frequency] ->
(MP3HybridState, [Sample])
mp3HybridFilterBank bf bt (MP3HybridState simdct ssynthesis) input =
let input' = padWith 576 0.0 input -- ensure length 576
aa = mp3AA bf bt input'
(samp, simdct') = mp3IMDCT bf bt aa simdct
samp' = mp3FrequencyInvert samp
--(ssynthesis', output) = (ssynthesis, take 18 samp) -- (See Driver.hs)
(ssynthesis', output) = mp3SynthesisFilterBank ssynthesis samp'
in (MP3HybridState simdct' ssynthesis', output)
-- [Sample] = IMDCT output from previous granule, used for overlapping.
-- MP3SynthState = State for the synthesis filterbank.
data MP3HybridState = MP3HybridState [Sample] MP3SynthState
emptyMP3HybridState :: MP3HybridState
emptyMP3HybridState = MP3HybridState (replicate 576 0.0)
(MP3SynthState (replicate 1024 0.0))