vocoder-conduit-0.1.0.0: src/Vocoder/Conduit/Frames.hs
{-|
Module : Vocoder.Conduit.Frames
Description : Frame processing
Copyright : (c) Marek Materzok, 2021
License : BSD2
-}
{-# LANGUAGE BangPatterns, FlexibleContexts #-}
module Vocoder.Conduit.Frames (
framesOfE,
genFramesOfE,
sumFramesE
) where
import Control.Arrow
import Data.Conduit
import Data.MonoTraversable
import Data.Maybe(fromMaybe)
import qualified Data.Sequences as Seq
-- | Splits a chunked input stream into overlapping frames of constant size
-- suitable for STFT processing.
framesOfE :: (Monad m, Seq.IsSequence seq) => Seq.Index seq -> Seq.Index seq -> ConduitT seq seq m ()
framesOfE chunkSize hopSize = genFramesOfE chunkSize hopSize (Seq.fromList []) >> return ()
-- | More general version of framesOfE, suitable for processing multiple inputs.
genFramesOfE :: (Monad m, Seq.IsSequence seq) => Seq.Index seq -> Seq.Index seq -> seq -> ConduitT seq seq m seq
genFramesOfE chunkSize hopSize q = do
mnextv <- await
case mnextv of
Nothing -> return q
Just nextv -> do
let newBuf = q `mappend` nextv
let newBufLen = Seq.lengthIndex newBuf
mapM_ yield [Seq.take chunkSize $ Seq.drop k newBuf
| k <- [0, hopSize .. newBufLen - chunkSize]]
let dropcnt = ((newBufLen - chunkSize) `div` hopSize) * hopSize + hopSize
let q' = Seq.drop dropcnt newBuf
genFramesOfE chunkSize hopSize q'
-- | Builds a chunked output stream from a stream of overlapping frames.
sumFramesE :: (Monad m, Seq.IsSequence seq, Num (Element seq)) => Seq.Index seq -> Seq.Index seq -> ConduitT seq seq m ()
sumFramesE chunkSize hopSize = process 0 []
where
ith i (n, c0) = fromMaybe 0 $ Seq.index c0 (i - n)
publish q = yield $ Seq.fromList $ map (\i -> sum $ fmap (ith i) q) [0 .. chunkSize-1]
publishRest q | null q = return ()
| otherwise = publish q >> publishRest (nextq q)
nextq q = fmap ((+ (-chunkSize)) *** id) $ dropWhile (\(n, c) -> Seq.lengthIndex c + n <= chunkSize) q
process2 sofar q
| sofar >= chunkSize = do
publish q
process2 (sofar - chunkSize) $ nextq q
| otherwise = process (sofar + hopSize) q
process sofar q = do
next <- await
case next of
Nothing -> publishRest q
Just next' -> process2 sofar (q ++ [(sofar, next')])