packages feed

mediabus-fdk-aac-0.3.0.0: src/Data/MediaBus/FdkAac/Conduit/Encoder.hs

{-# LANGUAGE RankNTypes #-}

-- | This module defines a conduit to encode linear audio into AAC.
module Data.MediaBus.FdkAac.Conduit.Encoder
  ( encodeLinearToAacC
  ) where

import Conduit
import Control.Lens
import Control.Monad
import Control.Monad.Logger
import Control.Monad.Trans.State.Strict as State
import Data.MediaBus
import Data.MediaBus.FdkAac.Encoder
import Data.String
import Data.Tagged
import Data.Typeable
import GHC.TypeLits


--  * Conduit Based 'Stream' Encoding
-- | A conduit that receives signed 16 bit, mono or stereo, 'Raw' 'Audio' and
-- yields AAC encoded frames. The timestamps of the input frames will be
-- regarded such that gaps in the timestamps are reflected in the generated
-- frames. The sequence numbering will be
encodeLinearToAacC
  :: forall channels r aot i s t m.
     ( MonadLoggerIO m
     , MonadThrow m
     , MonadResource m
     , KnownChannelLayout channels
     , KnownRate r
     , Typeable r
     , Show (AacAotProxy aot)
     , KnownNat (GetAacAot aot)
     , Show i
     , Show t
     , Typeable t
     , LocalOrd t
     , Show s
     , Typeable s
     , Num t
     , Num s
     , CanBeSample (Pcm channels S16)
     , Show (Pcm channels S16)
     , Integral t
     )
  => AacEncoderConfig r channels aot
  -> Conduit (Stream i s (Ticks r t) () (Audio r channels (Raw S16))) m (Stream i s (Ticks r t) (AacEncoderInfo r channels aot) (Audio r channels (Aac aot)))
encodeLinearToAacC aacCfg = do
  (Tagged enc, info) <- lift $ aacEncoderAllocate aacCfg
  (_res, _st, ()) <-
    prefixLogsC (show enc ++ " - ") $
    runRWSC
      enc
      (MkAacEncSt @r @channels @aot 0)
      (evalStateC
         (initialReframerState :: ReframerSt s (Ticks r t))
         (encodeThenFlush info))
  return ()
  where
    encodeThenFlush info = do
      awaitForever go
      aacs <- lift $ lift flushAacEncoder
      Prelude.mapM_ yieldAacFrame aacs
      where
        go (MkStream (Next f)) = do
          let reframeFrame = f & framePayload %~ getDurationTicks
          $logDebug (fromString ("in: " ++ show f))
          pushRes <- lift $ pushFrame reframeFrame
          case pushRes of
            Nothing -> return ()
            Just er -> do
              $logInfo
                (fromString
                   ("frame timing problem: " ++ show er ++ ", frame: " ++
                    show reframeFrame))
              lift $ State.get >>= $logInfoSH
              aacs <- lift $ lift flushAacEncoder
              Prelude.mapM_ yieldAacFrame aacs
              lift $
                pushStartFrame
                  (reframeFrame ^. frameTimestamp + reframeFrame ^. framePayload)
          aacs <-
            lift $ lift $
            encodeLinearToAac (view framePayload f)
          Prelude.mapM_ yieldAacFrame aacs
        go start@(MkStream (Start (MkFrameCtx fi ft fs _fp))) = do
          lift $ pushStartFrame ft
          lift $ $logDebug (fromString ("in: " ++ show start))
          let outStart = MkFrameCtx fi ft fs info
          lift $ $logDebug (fromString ("out: " ++ show outStart))
          yieldStartFrameCtx outStart
        yieldAacFrame fc =
          let wantDur = getDurationTicks fc
          in when (wantDur > 0) $ do
               frm <- lift $ generateFrame wantDur
               when (frm ^. framePayload < wantDur) $ do
                 $logInfo
                   (fromString
                      ("encoder generated more output than input, input: " ++
                       show (frm ^. framePayload) ++
                       " < output: " ++
                       show wantDur))
                 availDur <- lift nextFrameAvailableDuration
                 $logInfo
                   (fromString
                      ("samples enqueuend in the encoder delay lines: " ++
                       show availDur))
                 rfst <- lift $ State.get
                 $logInfoSH rfst
               let outFrm = frm & framePayload .~ fc
               $logDebug (fromString ("out: " ++ show outFrm))
               yieldNextFrame outFrm