{- | Functions for cutting signals with respect to lazy chunky time measures. This is essential for realtime applications. -} module Synthesizer.ChunkySize.Cut where import qualified Synthesizer.ChunkySize as ChunkySize import qualified Synthesizer.Generic.Cut as Cut import qualified Synthesizer.Generic.Signal as SigG import qualified Synthesizer.State.Signal as SigS import qualified Data.StorableVector.Lazy.Pattern as SigStV import qualified Data.StorableVector.Lazy as Vector import qualified Number.NonNegativeChunky as Chunky import Foreign.Storable (Storable) import qualified Data.List as List import qualified Data.List.Match as Match import Data.Tuple.HT (mapPair, ) import Data.Monoid (Monoid, ) import Prelude () import NumericPrelude.Numeric import NumericPrelude.Base hiding (splitAt, Read, ) class Cut.Read sig => Read sig where length :: sig -> ChunkySize.T class (Read sig, Monoid sig) => Transform sig where take :: ChunkySize.T -> sig -> sig drop :: ChunkySize.T -> sig -> sig splitAt :: ChunkySize.T -> sig -> (sig, sig) -- instance Storable y => Read SigSt.T y where instance Storable y => Read (Vector.Vector y) where {-# INLINE length #-} length = ChunkySize.fromStorableVectorSize . SigStV.length instance Storable y => Transform (Vector.Vector y) where {-# INLINE take #-} take = SigStV.take . ChunkySize.toStorableVectorSize {-# INLINE drop #-} drop = SigStV.drop . ChunkySize.toStorableVectorSize {-# INLINE splitAt #-} splitAt = SigStV.splitAt . ChunkySize.toStorableVectorSize instance Read ([] y) where {-# INLINE length #-} length xs = Chunky.fromChunks $ Match.replicate xs $ SigG.LazySize one instance Transform ([] y) where {-# INLINE take #-} take ns = Match.take (ChunkySize.toNullList ns) {-# INLINE drop #-} drop ns xs = -- 'drop' cannot make much use of laziness, thus 'foldl' is ok List.foldl (\x (SigG.LazySize n) -> List.drop n x) xs (Chunky.toChunks ns) {-# INLINE splitAt #-} splitAt ns = Match.splitAt (ChunkySize.toNullList ns) {- instance Read (SigFL.T y) where {-# INLINE length #-} length = SigFL.length instance Transform (SigFL.T y) where {-# INLINE take #-} take = SigFL.take {-# INLINE drop #-} drop = SigFL.drop {-# INLINE splitAt #-} splitAt = SigFL.splitAt -} instance Read (SigS.T y) where {-# INLINE length #-} length = Chunky.fromChunks . SigS.toList . SigS.map (const (SigG.LazySize one)) instance Transform (SigS.T y) where {-# INLINE take #-} take size0 = SigS.crochetL (\x (n,ns) -> if n>zero then Just (x, (pred n, ns)) else case ns of SigG.LazySize m : ms -> Just (x, (pred m, ms)) [] -> Nothing) (zero, Chunky.toChunks $ Chunky.normalize size0) {-# INLINE drop #-} drop ns xs = List.foldl (\x (SigG.LazySize n) -> SigS.drop n x) xs (Chunky.toChunks ns) {-# INLINE splitAt #-} splitAt n = -- This implementation is slow. Better leave it unimplemented? mapPair (SigS.fromList, SigS.fromList) . splitAt n . SigS.toList {- {- useful for application of non-negative chunky numbers as gate signals -} instance (ToInteger.C a, NonNeg.C a) => Read (Chunky.T a) where {-# INLINE length #-} length = sum . List.map (fromIntegral . toInteger) . Chunky.toChunks intToChunky :: (Ring.C a, NonNeg.C a) => String -> Int -> Chunky.T a intToChunky name = Chunky.fromNumber . -- the non-negative type is not necessarily a wrapper -- NonNegW.fromNumberMsg ("Generic.Cut."++name) . fromIntegral . (\x -> if x Transform (Chunky.T a) where {-# INLINE take #-} take n = P.min (intToChunky "take" n) {-# INLINE drop #-} drop n x = x NonNeg.-| intToChunky "drop" n {-# INLINE dropMarginRem #-} dropMarginRem n m x = let (z,d,b) = Chunky.minMaxDiff (intToChunky "dropMargin/n" n) (x NonNeg.-| intToChunky "dropMargin/m" m) in (if b then 0 else fromIntegral (Chunky.toNumber d), x NonNeg.-| z) {-# INLINE splitAt #-} splitAt n x = let (z,d,b) = Chunky.minMaxDiff (intToChunky "splitAt" n) x in (z, if b then d else mempty) {-# INLINE reverse #-} reverse = Chunky.fromChunks . List.reverse . Chunky.toChunks instance (P.Integral a) => Read (Chunky98.T a) where {-# INLINE null #-} null = List.null . Chunky98.toChunks {-# INLINE length #-} length = sum . List.map (P.fromIntegral . P.toInteger) . Chunky98.toChunks intToChunky98 :: (NonNeg98.C a) => String -> Int -> Chunky98.T a intToChunky98 name = Chunky98.fromNumber . -- NonNegW.fromNumberMsg ("Generic.Cut."++name) . P.fromIntegral . (\x -> if x<0 then error ("Generic.Cut.NonNeg.Chunky98."++name++": negative argument") else x) instance (P.Integral a, NonNeg98.C a) => Transform (Chunky98.T a) where {-# INLINE take #-} take n = P.min (intToChunky98 "take" n) {-# INLINE drop #-} drop n x = x NonNeg98.-| intToChunky98 "drop" n {-# INLINE dropMarginRem #-} dropMarginRem n m x = let (z,d,b) = Chunky98.minMaxDiff (intToChunky98 "dropMargin/n" n) (x NonNeg98.-| intToChunky98 "dropMargin/m" m) in (if b then 0 else P.fromIntegral (Chunky98.toNumber d), x NonNeg98.-| z) {-# INLINE splitAt #-} splitAt n x = let (z,d,b) = Chunky98.minMaxDiff (intToChunky98 "splitAt" n) x in (z, if b then d else Chunky98.zero) {-# INLINE reverse #-} reverse = Chunky98.fromChunks . List.reverse . Chunky98.toChunks {- | Like @lengthAtLeast n xs = length xs >= n@, but is more efficient, because it is more lazy. -} {-# INLINE lengthAtLeast #-} lengthAtLeast :: (Transform sig) => Int -> sig -> Bool lengthAtLeast n xs = n<=0 || not (null (drop (pred n) xs)) {-# INLINE lengthAtMost #-} lengthAtMost :: (Transform sig) => Int -> sig -> Bool lengthAtMost n xs = n>=0 && null (drop n xs) {-# INLINE sliceVertical #-} sliceVertical :: (Transform sig) => Int -> sig -> SigS.T sig sliceVertical n = SigS.map (take n) . SigS.takeWhile (not . null) . SigS.iterate (drop n) -}