{-# LANGUAGE RebindableSyntax #-} module Measurement where import qualified LabelChain import qualified Fourier import qualified Class import qualified Named import qualified SpectralDistribution as SD import qualified Arithmetic as Arith import qualified Parameters as Params import qualified Rate import qualified Signal import qualified SignalProcessingMethods as SPMethods import qualified SignalProcessing as SP import SignalProcessingMethods (Triple, ) import Parameters (Freq(Freq), ) import qualified Synthesizer.Generic.Analysis as AnaG import qualified Synthesizer.Basic.Binary as Bin import qualified Data.StorableVector.Lazy as SVL import qualified Data.StorableVector as SV import qualified Control.Applicative.HT as AppHT import qualified Control.Functor.HT as FuncHT import Control.DeepSeq (NFData, rnf, ) import Control.Applicative (Applicative, pure, (<*>), ) import qualified Data.Traversable as Trav import qualified Data.Foldable as Fold import qualified Data.NonEmpty as NonEmpty import Data.Tuple.HT (mapPair, ) import Data.Maybe (mapMaybe, ) import qualified Algebra.Field as Field import NumericPrelude.Numeric import NumericPrelude.Base hiding (id) -- | LabelChain.mergePhases drops a trailing isolated chunk mergeClickPhases :: [[a]] -> [[a]] mergeClickPhases = let go (x0:x1:xs) = (x0++x1) : go xs go _ = [] in go halfLife :: (Ord a, Field.C a) => NonEmpty.T [] a -> Int halfLife xs = let xm = NonEmpty.maximum xs in length $ takeWhile (>= xm / 2) $ dropWhile ( [Float] -> Int countEmphasized params clickAmplitudes = let progression = iterate (one+) zero (c0,c1) = Arith.linearRegression $ zip progression clickAmplitudes in length $ takeWhile (zero<) $ zipWith (-) clickAmplitudes $ map (\k -> Params.emphasisExcess params * (c0+c1*k)) progression type ClassFeatures = Class.Sound (Int, Int, Int) Int Int (Int, Int, Int) type ChunkFeatures = ((Int, Int, Int), Int) chunkFeatures :: Params.T -> SVL.Vector Float -> SVL.Vector Float -> ChunkFeatures chunkFeatures params volume featSig = let clicks = mergeClickPhases $ LabelChain.chopMonotony (mapPair (LabelChain.spanWeakRising, LabelChain.spanWeakFalling) $ Params.weakCounterSlopeSizes params) $ SVL.unpack featSig in ((length clicks, sum $ map halfLife $ mapMaybe NonEmpty.fetch clicks, countEmphasized params $ map NonEmpty.maximum $ mapMaybe (NonEmpty.fetch . SVL.unpack) $ flip SP.chop (map length clicks) $ SVL.zipWith (*) volume featSig), SP.chirpingMainDur featSig) bandFreq0, bandFreq1, bandFreq2 :: Float bandFreq0 = 1000 bandFreq1 = 2500 bandFreq2 = 4000 bandFreqs :: Triple Freq bandFreqs = (Freq bandFreq0, Freq bandFreq1, Freq bandFreq2) data SpectralParameters a = SpectralParameters { spectralFlatness, spectralMaximum :: a, spectralBandParams :: (a,a), spectralDistribution :: (SD.T a) } deriving Show instance (NFData a) => NFData (SpectralParameters a) where rnf (SpectralParameters specFlat specMax bands distr) = rnf (specFlat, specMax, bands, distr) instance Functor SpectralParameters where fmap = Trav.fmapDefault instance Fold.Foldable SpectralParameters where foldMap = Trav.foldMapDefault instance Trav.Traversable SpectralParameters where traverse f (SpectralParameters specFlat specMax bands distr) = pure SpectralParameters <*> f specFlat <*> f specMax <*> AppHT.mapPair (f,f) bands <*> Trav.traverse f distr instance Applicative SpectralParameters where pure x = SpectralParameters x x (x,x) (pure x) SpectralParameters fSpecFlat fSpecMax fbands fdistr <*> SpectralParameters specFlat specMax bands distr = SpectralParameters (fSpecFlat specFlat) (fSpecMax specMax) (mapPair fbands bands) (fdistr <*> distr) spectralParameters :: (Float, Float) -> ((Float, Float), SD.T Float) -> SpectralParameters Float spectralParameters (flat, maxf) (bp, distr) = SpectralParameters flat maxf bp distr spectrogramParameters :: [SV.Vector Float] -> (Float, Float) spectrogramParameters specs = (let blockFlats = map Fourier.spectralFlatness specs in if null blockFlats then 1 else AnaG.average blockFlats, let amax block = fromIntegral (fst (SP.argMaximum block)) / fromIntegral (2 * (SV.length block-1)) blockMaxs = map amax specs in if null blockMaxs then 0 else AnaG.average blockMaxs) classFromChunkFeatures :: ChunkFeatures -> Class.Sound rasping chirping ticking growling -> ClassFeatures classFromChunkFeatures (clickMeasure@(numClicks, _, _), chirpMain) cls = case cls of Class.Rasping _ -> Class.Rasping clickMeasure Class.Chirping _ -> Class.Chirping chirpMain Class.Ticking _ -> Class.Ticking numClicks Class.Growling _ -> Class.Growling clickMeasure Class.Other str -> Class.Other str measureSignal :: SPMethods.T -> Params.T -> Signal.SoxLabelled (Class.Sound rasping chirping ticking growling) -> ([ChunkFeatures], (Signal.LabelChain Rate.Measure (SpectralParameters Float, ClassFeatures), ([Float], Signal.T Rate.Measure [Named.Signal]))) measureSignal sigProc params labelled = let (sig, classified) = FuncHT.unzip labelled intervalSizes = Fold.toList . fmap fst . LabelChain.intervalSizes measRate = Params.measureSampleRate params classifiedHighRate = Signal.body classified classifiedMeasRate = Signal.body $ Signal.labelResample measRate classified dehummed = SPMethods.dehum sigProc sig (volume, relEnv) = SPMethods.envelopeLowRate sigProc measRate dehummed chunkFeats = case intervalSizes classifiedMeasRate of chunkSizes -> zipWith (chunkFeatures params) (SP.chop volume chunkSizes) (SP.chop relEnv chunkSizes) fourierStep = Params.fourierBlockStep params fourierSize = Params.fourierBlockSize params chunkSizesBlockRate = intervalSizes $ LabelChain.mapTime (max 0) $ LabelChain.mapTime (\n -> div (n + div (fourierStep-fourierSize) 2) fourierStep) $ classifiedHighRate spectroParams = map spectrogramParameters $ flip SP.chop chunkSizesBlockRate $ Fourier.slice $ Fourier.absoluteBlockSpectra fourierStep fourierSize $ SP.svlConcat $ SVL.map Bin.toCanonical $ Signal.body sig spectralDists = zipWith spectralParameters spectroParams $ SPMethods.bandParameters sigProc bandFreqs dehummed $ intervalSizes classifiedHighRate in (chunkFeats, (Signal.Cons measRate $ LabelChain.zipWithList (,) spectralDists $ LabelChain.zipWithList classFromChunkFeatures chunkFeats classifiedMeasRate, ([1, 0.4], Signal.Cons measRate [Named.Cons "volume" volume, Named.Cons "envelope" relEnv])))