packages feed

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')])