packages feed

mediabus-fdk-aac (empty) → 0.3.0.0

raw patch · 10 files changed

+1504/−0 lines, 10 filesdep +basedep +bytestringdep +conduitsetup-changed

Dependencies added: base, bytestring, conduit, conduit-combinators, containers, criterion, deepseq, ghc-prim, inline-c, lens, mediabus, mediabus-fdk-aac, monad-logger, random, resourcet, spool, tagged, text, time, transformers, vector

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Sven Heyll and Lindenbaum GmbH (c) 2016, 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/Encoder.hs view
@@ -0,0 +1,86 @@+module Main+  ( main+  ) where++import Control.Monad+import Control.Monad.Logger+import Data.Conduit+import Data.MediaBus+import Data.MediaBus.FdkAac+import Data.Conduit.List+import Data.Time.Clock+import Data.Typeable+import Criterion.Main+import Control.Lens+import Data.Vector.Storable as V+import Data.Word+import GHC.TypeLits++main :: IO ()+main =+  defaultMain+    [bgroupFromTestDuration 1000 16000, bgroupFromTestDuration 1000 48000]++bgroupFromTestDuration :: NominalDiffTime -> Int -> Benchmark+bgroupFromTestDuration testdur samplesPerSecond =+  bgroup+    (show testdur Prelude.++ " ptime=20ms " Prelude.++ show samplesPerSecond Prelude.+++     " Hz") $+  let frameCount = ceiling (testdur / ptime)+      ptime = 20 / 1000+  in [ bgroup+         "AAC HE"+         [ bench "Stereo" $+           nfIO $+           encodeNFrames (aacEncoderConfig aacHe16KhzStereo) $+           testFrames testdur frameCount+         , bench "Mono" $+           nfIO $+           encodeNFrames (aacEncoderConfig aacHe16KhzMono) $+           testFrames testdur frameCount+         ]+     , bgroup+         "AAC LC"+         [ bench "Stereo" $+           nfIO $+           encodeNFrames (aacEncoderConfig aacLc16KhzStereo) $+           testFrames testdur frameCount+         , bench "Mono" $+           nfIO $+           encodeNFrames (aacEncoderConfig aacLc16KhzMono) $+           testFrames testdur frameCount+         ]+     ]++encodeNFrames+  :: forall r channels aot.+     ( CanBeSample (Pcm channels S16)+     , CanBeBlank (Pcm channels S16)+     , Show (Pcm channels S16)+     , KnownRate r+     , KnownChannelLayout channels+     , Show (AacAotProxy aot)+     , KnownNat (GetAacAot aot)+     , Typeable r+     )+  => AacEncoderConfig r channels aot+  -> [Stream SrcId32 SeqNum16 (Ticks r Word64) () (Audio r channels (Raw S16))]+  -> IO [Stream SrcId32 SeqNum16 (Ticks r Word64) (AacEncoderInfo r channels aot) (Audio r channels (Aac aot))]+encodeNFrames cfg pcms =+  runStdoutLoggingT $+  runConduitRes $ sourceList pcms .| encodeLinearToAacC cfg .| consume++testFrames+  :: (KnownRate r, IsPcmValue (Pcm channels S16))+  => NominalDiffTime+  -> Int+  -> [Stream SrcId32 SeqNum16 (Ticks r Word64) () (Audio r channels (Raw S16))]+testFrames frameDuration frames =+  [ MkStream+    (Next+       (MkFrame+          (fromIntegral x * (nominalDiffTime # frameDuration))+          (fromIntegral x)+          (blankFor frameDuration)))+  | x <- [0 .. frames]+  ]
+ examples/Main.hs view
@@ -0,0 +1,30 @@+module Main+  ( main+  ) where++import Control.Monad+import Control.Monad.Logger+import Data.Conduit+import Data.MediaBus+import Data.MediaBus.FdkAac++main :: IO ()+main = void encodeOneSeconfOfSilence++encodeOneSeconfOfSilence+  :: IO [Stream SrcId32+                SeqNum16+                (Ticks64 (Hz 48000))+                (AacEncoderInfo (Hz 48000) Stereo 'HighEfficiency)+                (Audio (Hz 48000) Stereo (Aac 'HighEfficiency))]+encodeOneSeconfOfSilence =+  runStdoutLoggingT $+  runConduitRes $+        yieldNextFrame (MkFrame 0 0 pcmAudioOneSecond)+     .| encodeLinearToAacC encoderConfig+     .| traceShowSink 1 "Example Trace"++  where+    pcmAudioOneSecond :: Audio (Hz 48000) Stereo (Raw S16)+    pcmAudioOneSecond = blankFor 1+    encoderConfig = aacEncoderConfig aacHe48KhzStereo
+ mediabus-fdk-aac.cabal view
@@ -0,0 +1,112 @@+name: mediabus-fdk-aac+version: 0.3.0.0+cabal-version: >=1.10+build-type: Simple+license: BSD3+license-file: LICENSE+copyright: 2016, 2017 Sven Heyll, Lindenbaum GmbH+maintainer: sven.heyll@lindenbaum.eu+homepage: https://github.com/lindenbaum/mediabus-fdk-aac+synopsis: Mediabus plugin for the Frauenhofer ISO-14496-3 AAC FDK+description:+    Please see README.md+category: Sound+author: Sven Heyll++library+    exposed-modules:+        Data.MediaBus.FdkAac.Encoder+        Data.MediaBus.FdkAac.Conduit.Encoder+        Data.MediaBus.FdkAac+    build-depends:+        base >=4.9 && <5,+        bytestring >=0.10.8.1 && <0.11,+        conduit >=1.2.9 && <1.3,+        conduit-combinators >=1.1.0 && <1.2,+        containers >=0.5.7.1 && <0.6,+        deepseq >=1.4.2.0 && <1.5,+        inline-c >=0.5.6.1 && <0.6,+        lens >=4.15.1 && <4.16,+        mediabus >=0.3.2.0 && <0.4,+        monad-logger >=0.3.20.2 && <0.4,+        random ==1.1.*,+        resourcet >=1.1.9 && <1.2,+        spool ==0.1.*,+        tagged >=0.8.5 && <0.9,+        text >=1.2.2.1 && <1.3,+        time >=1.6.0.1 && <1.7,+        transformers >=0.5.2.0 && <0.6,+        vector >=0.11.0.0 && <0.12+    cc-options: -std=c99 -Wall -O2+    pkgconfig-depends: fdk-aac >=0.1.4+    c-sources:+        src/Data/MediaBus/FdkAac/EncoderFdkWrapper.c+    default-language: Haskell2010+    default-extensions: BangPatterns ConstraintKinds CPP DataKinds+                        DefaultSignatures DeriveDataTypeable DeriveFunctor DeriveGeneric+                        DuplicateRecordFields FlexibleInstances FlexibleContexts+                        FunctionalDependencies GADTs GeneralizedNewtypeDeriving+                        KindSignatures MultiParamTypeClasses NamedFieldPuns+                        OverloadedStrings QuasiQuotes RecordWildCards RankNTypes+                        ScopedTypeVariables StandaloneDeriving TemplateHaskell+                        TupleSections TypeApplications TypeFamilies TypeInType+                        TypeOperators TypeSynonymInstances UnicodeSyntax+    extra-libraries:+        m+    hs-source-dirs: src+    other-modules:+        Data.MediaBus.FdkAac.EncoderFdkWrapper+    ghc-options: -O2 -Wall -funbox-strict-fields -fno-warn-unused-do-bind++test-suite examples+    type: exitcode-stdio-1.0+    main-is: Main.hs+    build-depends:+        base >=4.9.1.0 && <4.10,+        deepseq >=1.4.2.0 && <1.5,+        ghc-prim >=0.5.0.0 && <0.6,+        lens >=4.15.1 && <4.16,+        monad-logger >=0.3.20.2 && <0.4,+        mediabus-fdk-aac >=0.3.0.0 && <0.4,+        mediabus >=0.3.2.0 && <0.4,+        conduit >=1.2.9 && <1.3,+        vector >=0.11.0.0 && <0.12+    default-language: Haskell2010+    default-extensions: BangPatterns ConstraintKinds CPP DataKinds+                        DefaultSignatures DeriveDataTypeable DeriveFunctor DeriveGeneric+                        DuplicateRecordFields FlexibleInstances FlexibleContexts+                        FunctionalDependencies GADTs GeneralizedNewtypeDeriving+                        KindSignatures MultiParamTypeClasses NamedFieldPuns+                        OverloadedStrings QuasiQuotes RecordWildCards RankNTypes+                        ScopedTypeVariables StandaloneDeriving TemplateHaskell+                        TupleSections TypeApplications TypeFamilies TypeInType+                        TypeOperators TypeSynonymInstances UnicodeSyntax+    hs-source-dirs: examples+    ghc-options: -O2 -Wall -funbox-strict-fields -fno-warn-unused-do-bind++benchmark encoder-benchmark+    type: exitcode-stdio-1.0+    main-is: Encoder.hs+    build-depends:+        base >=4.9.1.0 && <4.10,+        deepseq >=1.4.2.0 && <1.5,+        ghc-prim >=0.5.0.0 && <0.6,+        lens >=4.15.1 && <4.16,+        monad-logger >=0.3.20.2 && <0.4,+        criterion >=1.1.4.0 && <1.2,+        mediabus-fdk-aac >=0.3.0.0 && <0.4,+        mediabus >=0.3.2.0 && <0.4,+        conduit >=1.2.9 && <1.3,+        vector >=0.11.0.0 && <0.12+    default-language: Haskell2010+    default-extensions: BangPatterns ConstraintKinds CPP DataKinds+                        DefaultSignatures DeriveDataTypeable DeriveFunctor DeriveGeneric+                        DuplicateRecordFields FlexibleInstances FlexibleContexts+                        FunctionalDependencies GADTs GeneralizedNewtypeDeriving+                        KindSignatures MultiParamTypeClasses NamedFieldPuns+                        OverloadedStrings QuasiQuotes RecordWildCards RankNTypes+                        ScopedTypeVariables StandaloneDeriving TemplateHaskell+                        TupleSections TypeApplications TypeFamilies TypeInType+                        TypeOperators TypeSynonymInstances UnicodeSyntax+    hs-source-dirs: bench+    ghc-options: -O2 -Wall -funbox-strict-fields -fno-warn-unused-do-bind
+ src/Data/MediaBus/FdkAac.hs view
@@ -0,0 +1,9 @@+-- | A MediaBus adapter for the Frauenhofer AAC encoder/decoder.+-- This module reexports the high-level encoding and decoding functions.+module Data.MediaBus.FdkAac+  ( module Data.MediaBus.FdkAac.Encoder+  , module Data.MediaBus.FdkAac.Conduit.Encoder+  ) where++import Data.MediaBus.FdkAac.Conduit.Encoder+import Data.MediaBus.FdkAac.Encoder
+ src/Data/MediaBus/FdkAac/Conduit/Encoder.hs view
@@ -0,0 +1,114 @@+{-# 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
+ src/Data/MediaBus/FdkAac/Encoder.hs view
@@ -0,0 +1,410 @@+{-# LANGUAGE RankNTypes #-}++-- | This module defines a type-safe, high-level interface to encode linear+--   audio into AAC. This interface is still relatively low-level.+--   For an easy to use, high-level interface please consult+--   'Data.MediaBus.FdkAac.Conduit.Encoder'.+module Data.MediaBus.FdkAac.Encoder+  ( aacFrameSize+  , aacFrameMediaData+  , Aac+  , aacHe48KhzStereo+  , aacHe16KhzStereo+  , aacHe48KhzMono+  , aacHe16KhzMono+  , aacLc48KhzStereo+  , aacLc16KhzStereo+  , aacLc48KhzMono+  , aacLc16KhzMono+  , aacEncoderConfig+  , AacEncoderInfo(..)+  , aacEncoderInfoId+  , aacEncoderInfoDelay+  , aacEncoderInfoFrameSize+  , aacEncoderInfoAudioSpecificConfig+  , type AacEncoderConfig+  , type AacEncoderContext+  , AacAot(..)+  , AacAotProxy(..)+  , type GetAacAot+  , aacEncoderAllocate+  , type AacEncT+  , AacEncSt(..)+  , numberOfDelayedSamples+  , encodeLinearToAac+  , flushAacEncoder+  ) where++import Conduit+import Control.DeepSeq+import Control.Lens+import Control.Monad.Logger+import Control.Monad.Trans.RWS.Strict+import Control.Monad.Trans.Resource+import Data.Kind+import Data.Maybe+import Data.MediaBus+import Data.MediaBus.FdkAac.EncoderFdkWrapper+import Data.Proxy+import Data.String+import Data.Tagged+import qualified Data.Vector.Storable as V+import Data.Word+import GHC.Generics (Generic)+import GHC.TypeLits+import Text.Printf++-- * MediaBus Aac Encoder+-- | This is the media type for AAC encoded audio data.+data Aac (aot :: AacAot)++data instance  Audio r c (Aac aot) = MkAacFrame{aacFrameMediaData+                                                :: !(V.Vector Word8),+                                                aacFrameSize :: !Word64}+                                   deriving (Generic)++instance (KnownRate r, KnownChannelLayout c, Show (AacAotProxy aot)) =>+         IsMedia (Audio r c (Aac aot))++instance (KnownRate r, KnownChannelLayout c, Show (AacAotProxy aot)) =>+         HasMedia (Audio r c (Aac aot)) (Audio r c (Aac aot)) where+  type MediaFrom (Audio r c (Aac aot)) = Audio r c (Aac aot)+  type MediaTo (Audio r c (Aac aot)) = Audio r c (Aac aot)+  media = iso id id++instance HasMediaBuffer (Audio r c (Aac aot)) (Audio r c (Aac aot)) where+  type MediaBufferFrom (Audio r c (Aac aot)) = MediaBuffer Word8+  type MediaBufferTo (Audio r c (Aac aot)) = MediaBuffer Word8+  mediaBuffer =+    lens+      (MkMediaBuffer . aacFrameMediaData)+      (\aacFrame -> \(MkMediaBuffer v) -> aacFrame {aacFrameMediaData = v})++instance (KnownRate r) =>+         HasDuration (Audio r c (Aac aot)) where+  getDuration MkAacFrame {aacFrameSize} =+    from nominalDiffTime # (MkTicks aacFrameSize :: Ticks r Word64)+  getDurationTicks MkAacFrame {aacFrameSize} =+    convertTicks (MkTicks aacFrameSize :: Ticks r Word64)++instance NFData (Audio r c (Aac aot))++instance (KnownRate r, KnownChannelLayout c, Show (AacAotProxy aot)) =>+         Show (Audio r c (Aac aot)) where+  showsPrec d MkAacFrame {aacFrameMediaData, aacFrameSize} =+    showParen+      (d > 10)+      (showString "aac frame: " .+       showsPrec 11 (MkShowMedia :: MediaDescription (Audio r c (Aac aot))) .+       showString ", samples: " .+       shows aacFrameSize .+       showString ", data: " .+       showsPrec 11 (V.length aacFrameMediaData) . showString " bytes starting with: " .+       showsPrec 11 (V.take 16 aacFrameMediaData))++instance (KnownRate r, KnownChannelLayout c, Show (AacAotProxy aot)) =>+         Show (MediaDescription (Audio r c (Aac aot))) where+  showsPrec d _ =+    showParen+      (d > 10)+      (showsPrec 11 (MkAacAotProxy :: AacAotProxy aot) . showChar ' ' .+       showsPrec 11 (MkRateProxy :: RateProxy r) .+       showChar ' ' .+       showsPrec 11 (MkChannelLayoutProxy :: ChannelLayoutProxy c))++-- ** Aac Configuration+-- | Create the 'Config' value needed to intialize the encoder from type-level+-- audio stream settings.+aacEncoderConfig+  :: forall rate channels aot proxy.+     (KnownRate rate, KnownChannelLayout channels, KnownNat (GetAacAot aot))+  => proxy (Audio rate channels (Aac aot)) -> AacEncoderConfig rate channels aot+aacEncoderConfig _pa =+  let pr :: Proxy rate+      pr = Proxy+      pch :: Proxy channels+      pch = Proxy+      pco :: AacAotProxy aot+      pco = MkAacAotProxy+  in Tagged $+     simpleConfig+       (getAacAot pco)+       (numberOfChannels pch)+       (fromInteger (rateVal pr))++-- *** Pre-defined Aac media Proxies+-- | 48 kHz Stereo 'HighEfficiency'+aacHe48KhzStereo :: Proxy (Audio (Hz 48000) Stereo (Aac 'HighEfficiency))+aacHe48KhzStereo = Proxy++-- | 16 kHz Stereo 'HighEfficiency'+aacHe16KhzStereo :: Proxy (Audio (Hz 16000) Stereo (Aac 'HighEfficiency))+aacHe16KhzStereo = Proxy++-- | 48 kHz Mono 'HighEfficiency'+aacHe48KhzMono :: Proxy (Audio (Hz 48000) Mono (Aac 'HighEfficiency))+aacHe48KhzMono = Proxy++-- | 16 kHz Mono 'HighEfficiency'+aacHe16KhzMono :: Proxy (Audio (Hz 16000) Mono (Aac 'HighEfficiency))+aacHe16KhzMono = Proxy++-- | 48 kHz Stereo 'LowComplexity'+aacLc48KhzStereo :: Proxy (Audio (Hz 48000) Stereo (Aac 'LowComplexity))+aacLc48KhzStereo = Proxy++-- | 16 kHz Stereo 'LowComplexity'+aacLc16KhzStereo :: Proxy (Audio (Hz 16000) Stereo (Aac 'LowComplexity))+aacLc16KhzStereo = Proxy++-- | 48 kHz Mono 'LowComplexity'+aacLc48KhzMono :: Proxy (Audio (Hz 48000) Mono (Aac 'LowComplexity))+aacLc48KhzMono = Proxy++-- | 16 kHz Mono 'LowComplexity'+aacLc16KhzMono :: Proxy (Audio (Hz 16000) Mono (Aac 'LowComplexity))+aacLc16KhzMono = Proxy++-- | The encoding specific information, created __by the encoder__ during+-- initialization.+data AacEncoderInfo (rate :: Rate) (channels :: Type) (aot :: AacAot) = MkAacEncoderInfo+  { _aacEncoderInfoDelay :: !Word64+  -- ^ Number of samples that the output will be delayed through the encoding+  -- mechanism (e.g. aot delay lines)+  , _aacEncoderInfoFrameSize :: !Word64+  -- ^ Number of samples that are encoded into each frame.+  , _aacEncoderInfoAudioSpecificConfig :: !(V.Vector Word8)+  -- ^ The audio specific configuration /box/ as specified in ISO/IEC 14496-3+  -- section 1.6, that the encoder generates.+  , _aacEncoderInfoId :: String+  -- ^ Identification string for one specific encoder, derived from the natice+  -- C pointer in 'encoderHandle'.+  } deriving Generic++instance NFData (AacEncoderInfo r c a)++instance ( KnownRate r+         , KnownChannelLayout c+         , Show (AacAotProxy aot)+         , KnownNat (GetAacAot aot)+         ) =>+         Show (AacEncoderInfo r c aot) where+  showsPrec d MkAacEncoderInfo { _aacEncoderInfoDelay+                               , _aacEncoderInfoFrameSize+                               , _aacEncoderInfoAudioSpecificConfig+                               , _aacEncoderInfoId+                               } =+    showParen+      (d > 10)+      (showString _aacEncoderInfoId . showString " " .+       showsPrec 11 (MkShowMedia :: MediaDescription (Audio r c (Aac aot))) .+       showParen+         True+         (showString "encoding delay: " . showsPrec 11 _aacEncoderInfoDelay .+          showString ", samples per frame: " .+          showsPrec 11 _aacEncoderInfoFrameSize .+          showString ", audio specific config: " .+          showsPrec 11 _aacEncoderInfoAudioSpecificConfig))++-- | Tag a 'Config' with the compile time configuration parameters.+type AacEncoderConfig rate channels aot = Tagged '( rate, channels, aot) Config++-- | Tag an 'Encoder' with the compile time configuration parameters.+type AacEncoderContext rate channels aot = Tagged '( rate, channels, aot) Encoder++-- ** Audio Object Types+-- | Type level symbol to indicate that the AAC audio coding is used for the+-- respective media. This is a phantom type to index the 'Audio' data family.+data AacAot+  = LowComplexity -- ^ corresponds to audio object type @2@+  | HighEfficiency -- ^ corresponds to audio object type @5@+  deriving (Show)++instance Show (AacAotProxy 'LowComplexity) where+  showsPrec _ _ = showString "low-complexity"++instance Show (AacAotProxy 'HighEfficiency) where+  showsPrec _ _ = showString "high-efficiency"++-- | Convert the 'AacAot' to an ISO/IEC 14496-3 Audio Object Type value.+type family GetAacAot (c :: AacAot) :: Nat where+  GetAacAot 'LowComplexity = 2+  GetAacAot 'HighEfficiency = 5++-- | Convert the 'AacAot' to an ISO/IEC 14496-3 Audio Object Type value.+getAacAot+  :: forall aot c proxy.+     (Integral aot, KnownNat (GetAacAot c))+  => proxy c -> aot+getAacAot _ =+  let px :: Proxy (GetAacAot c)+      px = Proxy+  in fromInteger $ natVal px++-- | Proxy for showing an 'AacAot'+data AacAotProxy (aot :: AacAot) =+  MkAacAotProxy++-- * Encoder Initialization+-- | Allocate a new encoder instance in a 'ResourceT' monad such that it will+-- automatically be released and the freed.+aacEncoderAllocate+  :: ( KnownRate rate+     , KnownChannelLayout channels+     , Show (AacAotProxy aot)+     , KnownNat (GetAacAot aot)+     , MonadIO m+     , MonadResource m+     , MonadThrow m+     , MonadLoggerIO m+     )+  => AacEncoderConfig rate channels aot+  -> m (AacEncoderContext rate channels aot, AacEncoderInfo rate channels aot)+aacEncoderAllocate (Tagged cfg) = do+  let destroyAndLog logger enc =+        runLoggingT+          (do $logDebug (fromString (printf "%s destroying" (show enc)))+              res <- liftIO (destroy enc)+              case res of+                AacEncOk ->+                  $logInfo (fromString (printf "%s destroyed" (show enc)))+                other ->+                  $logError+                    (fromString+                       (printf+                          "%s failed to destroy: %s"+                          (show enc)+                          (show other))))+          logger+  logger <- askLoggerIO+  (rkey, eres) <- allocate (create cfg) (Prelude.mapM_ (destroyAndLog logger))+  case eres of+    Left err -> do+      $logError (fromString (printf "encoder allocation failed: %s" (show err)))+      release rkey+      throwM err+    Right enc@MkEncoder {encoderDelay, frameSize, audioSpecificConfig} -> do+      let i =+            MkAacEncoderInfo+            { _aacEncoderInfoDelay = fromIntegral encoderDelay+            , _aacEncoderInfoFrameSize = fromIntegral frameSize+            , _aacEncoderInfoAudioSpecificConfig = audioSpecificConfig+            , _aacEncoderInfoId = show enc+            }+      $logInfo (fromString (printf "allocated: %s" (show i)))+      return (Tagged enc, i)++-- * Stateful Encoding++-- | Type alias for the internal RWS monad of the aac encoder.+type AacEncT rate channels aot m a = RWST Encoder () (AacEncSt rate channels aot) m a++-- | The internal state for 'encodeLinearToAac'+newtype AacEncSt (rate :: Rate) channels (aot :: AacAot) = MkAacEncSt+  { _numberOfDelayedSamples :: Word64+  -- ^ The number of input samples currently buffered in the delay lines of the encoder.+  } deriving (Show)++-- | An internal 'Iso' for the 'AacEncSt'+numberOfDelayedSamples :: Iso' (AacEncSt (rate :: Rate) channels (aot :: AacAot)) Word64+numberOfDelayedSamples = iso _numberOfDelayedSamples MkAacEncSt++-- | Convert linear audio to AAC encoded audio with the given encoder settings.+encodeLinearToAac+  :: ( KnownRate rate+     , KnownChannelLayout channels+     , KnownRate rate+     , Show (AacAotProxy aot)+     , CanBeSample (Pcm channels S16)+     , MonadIO m+     , MonadThrow m+     , MonadLogger m+     )+  => Audio rate channels (Raw S16)+  -> AacEncT rate channels aot m [Audio rate channels (Aac aot)]+encodeLinearToAac !aIn =+  let dIn = V.unsafeCast (aIn ^. mediaBuffer . mediaBufferVector)+  in encodeSampleVector dIn []++-- | Convert linear audio to AAC encoded audio with the given encoder settings,+-- from a raw sample vector.+-- If the sample vector contains more that the encoder can swallow at a time,+-- the encoder is repeatedly called until all input is consumed.+encodeSampleVector+  :: forall m channels rate aot.+     ( MonadLogger m+     , MonadIO m+     , MonadThrow m+     , KnownChannelLayout channels+     , KnownRate rate+     , Show (AacAotProxy aot)+     , V.Storable (Pcm channels S16)+     )+  => V.Vector Word16+  -> [Audio rate channels (Aac aot)]+  -> AacEncT rate channels aot m [Audio rate channels (Aac aot)]+encodeSampleVector dIn acc+  | V.length dIn <= 0 = return []+  | otherwise = do+    e <- ask+    eres <- liftIO $ encode e dIn+    case eres of+      Left err -> do+        $logError (fromString (printf "encoding failed: %s" (show err)))+        throwM err+      Right fr -> returnAccOrEncodeAgain acc fr++-- | Flush any left over input and then also flush delay lines of the AAC encoder.+flushAacEncoder+  :: ( MonadIO m+     , MonadLogger m+     , MonadThrow m+     , KnownChannelLayout channels+     , KnownRate r+     , Show (AacAotProxy aot)+     , V.Storable (Pcm channels S16)+     )+  => AacEncT r channels aot m [Audio r channels (Aac aot)]+flushAacEncoder = do+  d <- use numberOfDelayedSamples+  e <- ask+  $logDebug (fromString (printf "flushing %d samples" d))+  eres <- liftIO $ flush e+  case eres of+    Left err -> do+      $logError (fromString (printf "flushing failed: %s" (show err)))+      throwM err+    Right fr -> do+      $logInfo (fromString (printf "flushed frame: %s" (show fr)))+      returnAccOrEncodeAgain [] fr++-- | Internal function+returnAccOrEncodeAgain+  :: ( MonadLogger m+     , MonadIO m+     , MonadThrow m+     , KnownChannelLayout channels+     , KnownRate rate+     , Show (AacAotProxy aot)+     , V.Storable (Pcm channels S16)+     )+  => [Audio rate channels (Aac aot)]+  -> EncodeResult+  -> AacEncT rate channels aot m [Audio rate channels (Aac aot)]+returnAccOrEncodeAgain acc MkEncodeResult { encodeResultLeftOverInput+                                          , encodeResultSamples+                                          , encodeResultConsumedFrames+                                          } = do+  numberOfDelayedSamples += encodeResultConsumedFrames+  delayed <- use numberOfDelayedSamples+  let acc' = maybe acc (\es -> MkAacFrame es encoded : acc) encodeResultSamples+      encoded =+        if isJust encodeResultSamples+          then delayed+          else 0+  numberOfDelayedSamples -= encoded+  case encodeResultLeftOverInput of+    Nothing -> return (reverse acc')+    Just rest -> encodeSampleVector rest acc'++makeLenses ''AacEncoderInfo
+ src/Data/MediaBus/FdkAac/EncoderFdkWrapper.c view
@@ -0,0 +1,200 @@++#include <stdio.h>++#include <stdint.h>++#include "fdk-aac/aacenc_lib.h"++uintptr_t inline_c_Data_MediaBus_FdkAac_EncoderFdkWrapper_0_e88399b3eaf706673efe095f84ff92194fa12ff2(int * aacEncoderCfgErrorP_inline_c_0, unsigned configChannelMode_inline_c_1, unsigned configModules_inline_c_2, unsigned configChannels_inline_c_3, int * aacEncoderCfgErrorP_inline_c_4, unsigned configAot_inline_c_5, int * aacEncoderCfgErrorP_inline_c_6, unsigned configSbrMode_inline_c_7, int * aacEncoderCfgErrorP_inline_c_8, unsigned configSampleRate_inline_c_9, int * aacEncoderCfgErrorP_inline_c_10, int * aacEncoderCfgErrorP_inline_c_11, unsigned configBitRateMode_inline_c_12, int * aacEncoderCfgErrorP_inline_c_13, unsigned configBitRate_inline_c_14, int * aacEncoderCfgErrorP_inline_c_15, unsigned configBandwidth_inline_c_16, unsigned configBandwidth_inline_c_17, int * aacEncoderCfgErrorP_inline_c_18, int * aacEncoderCfgErrorP_inline_c_19, unsigned configSignallingMode_inline_c_20, int * aacEncoderCfgErrorP_inline_c_21, unsigned configAfterburner_inline_c_22, int * aacEncoderCfgErrorP_inline_c_23, int * aacEncoderCfgErrorP_inline_c_24, int * aacEncoderCfgErrorP_inline_c_25, unsigned confBufMaxLen_inline_c_26, unsigned char * confBufP_inline_c_27, unsigned * confBufSizeP_inline_c_28, unsigned * encDelayP_inline_c_29, unsigned * frameSizeP_inline_c_30, int * errorCodeP_inline_c_31) {++              AACENC_ERROR e = AACENC_OK;+              HANDLE_AACENCODER phAacEncoder;+              AACENC_InfoStruct pInfo;+              CHANNEL_MODE channelMode;+              *(aacEncoderCfgErrorP_inline_c_0) = 13;++              switch (configChannelMode_inline_c_1) {+                  case 1: channelMode = MODE_1;       break;+                  case 2: channelMode = MODE_2;       break;+                  case 3: channelMode = MODE_1_2;     break;+                  case 4: channelMode = MODE_1_2_1;   break;+                  case 5: channelMode = MODE_1_2_2;   break;+                  case 6: channelMode = MODE_1_2_2_1; break;+                 default: channelMode = 0;            break;+              }++              e = aacEncOpen(&phAacEncoder, configModules_inline_c_2, configChannels_inline_c_3);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_4) = 0;+                goto e0;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_AOT, (const UINT) configAot_inline_c_5);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_6) = 1;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_SBR_MODE, (const UINT) configSbrMode_inline_c_7);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_8) = 2;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_SAMPLERATE, (const UINT) configSampleRate_inline_c_9);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_10) = 3;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_CHANNELMODE, channelMode);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_11) = 4;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_BITRATEMODE, (const UINT) configBitRateMode_inline_c_12);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_13) = 5;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_BITRATE, (const UINT) configBitRate_inline_c_14);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_15) = 6;+                goto e1;+              }+              if ((const UINT) configBandwidth_inline_c_16 != 0) {+                e = aacEncoder_SetParam(phAacEncoder, AACENC_BANDWIDTH, (const UINT) configBandwidth_inline_c_17);+                if (e != AACENC_OK) {+                  *(aacEncoderCfgErrorP_inline_c_18) = 7;+                  goto e1;+                }+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_TRANSMUX, TT_MP4_RAW); // TODO extract from TRANSPORT_TYPE in FDK_audio.h+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_19) = 8;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_SIGNALING_MODE, (const UINT) configSignallingMode_inline_c_20);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_21) = 9;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_AFTERBURNER, (const UINT) configAfterburner_inline_c_22);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_23) = 10;+                goto e1;+              }+              e = aacEncEncode(phAacEncoder, NULL, NULL, NULL, NULL);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_24) = 11;+                goto e1;+              }+              e = aacEncInfo(phAacEncoder, &pInfo);+              if (e != AACENC_OK) {+                *(aacEncoderCfgErrorP_inline_c_25) = 12;+                goto e1;+              }++              for (unsigned int i = 0; i < pInfo.confSize && i < confBufMaxLen_inline_c_26; ++i) {+                 *(confBufP_inline_c_27 + i) = pInfo.confBuf[i];+              }+              *(confBufSizeP_inline_c_28) = pInfo.confSize;+              *(encDelayP_inline_c_29)    = pInfo.encoderDelay;+              *(frameSizeP_inline_c_30)   = pInfo.frameLength;+              return ((uintptr_t) phAacEncoder);++              e1:+                 if (AACENC_OK != aacEncClose(&phAacEncoder)) {+                    printf("Failed to free the AAC Encoder.\n");+                 }++              e0:+                 *(errorCodeP_inline_c_31) = e;+                 return (uintptr_t)NULL;+            +}+++int inline_c_Data_MediaBus_FdkAac_EncoderFdkWrapper_1_922d89f2c2b9a2bb7dbc4bbc946440f71a320f2a(uintptr_t encoderHandle_inline_c_0, long vec_inline_c_1, short * vec_inline_c_2, long vec_inline_c_3, long unsafeOutBuffer_inline_c_4, unsigned char * unsafeOutBuffer_inline_c_5, int * numOutBytesP_inline_c_6, int * numInSamplesP_inline_c_7, int * numAncBytesP_inline_c_8) {++            AACENC_ERROR e;+            HANDLE_AACENCODER phAacEncoder = (HANDLE_AACENCODER) encoderHandle_inline_c_0;++            +            AACENC_BufDesc inBuffDesc;+            INT inBuffIds[1]             = {IN_AUDIO_DATA};+            INT inBuffSizes[1]           = {vec_inline_c_1 * 2};+            INT inBuffElSizes[1]         = {2};+            void* inBuffBuffers[1]       = {vec_inline_c_2};+            inBuffDesc.numBufs           = 1;+            inBuffDesc.bufs              = inBuffBuffers;+            inBuffDesc.bufferIdentifiers = inBuffIds;+            inBuffDesc.bufSizes          = inBuffSizes;+            inBuffDesc.bufElSizes        = inBuffElSizes;+            AACENC_InArgs inArgs         =+              { .numInSamples = vec_inline_c_3+              , .numAncBytes = 0 };++            +            AACENC_BufDesc outBuffDesc;+            INT outBuffIds[1]             = {OUT_BITSTREAM_DATA};+            INT outBuffSizes[1]           = {unsafeOutBuffer_inline_c_4};+            INT outBuffElSizes[1]         = {1};+            void* outBuffBuffers[1]       = {unsafeOutBuffer_inline_c_5};+            outBuffDesc.numBufs           = 1;+            outBuffDesc.bufs              = outBuffBuffers;+            outBuffDesc.bufferIdentifiers = outBuffIds;+            outBuffDesc.bufSizes          = outBuffSizes;+            outBuffDesc.bufElSizes        = outBuffElSizes;+            AACENC_OutArgs outArgs;++            e = aacEncEncode (phAacEncoder, &inBuffDesc, &outBuffDesc,+                              &inArgs, &outArgs);+            *(numOutBytesP_inline_c_6)  = outArgs.numOutBytes;+            *(numInSamplesP_inline_c_7) = outArgs.numInSamples;+            *(numAncBytesP_inline_c_8)  = outArgs.numAncBytes;++            return e;+         +}+++int inline_c_Data_MediaBus_FdkAac_EncoderFdkWrapper_2_666791a4914d28907cccec9952c8fb7e36c63ac9(uintptr_t encoderHandle_inline_c_0, long unsafeOutBuffer_inline_c_1, unsigned char * unsafeOutBuffer_inline_c_2, int * numOutBytesP_inline_c_3, int * numInSamplesP_inline_c_4, int * numAncBytesP_inline_c_5) {++            AACENC_ERROR e;+            HANDLE_AACENCODER phAacEncoder = (HANDLE_AACENCODER) encoderHandle_inline_c_0;++            +            AACENC_BufDesc inBuffDesc;+            inBuffDesc.numBufs           = 0;++            AACENC_InArgs inArgs =+              { .numInSamples = -1+              , .numAncBytes = 0 };++            AACENC_BufDesc outBuffDesc;+            INT outBuffIds[1]             = {OUT_BITSTREAM_DATA};+            INT outBuffSizes[1]           = {unsafeOutBuffer_inline_c_1};+            INT outBuffElSizes[1]         = {1};+            void* outBuffBuffers[1]       = {unsafeOutBuffer_inline_c_2};+            outBuffDesc.numBufs           = 1;+            outBuffDesc.bufs              = outBuffBuffers;+            outBuffDesc.bufferIdentifiers = outBuffIds;+            outBuffDesc.bufSizes          = outBuffSizes;+            outBuffDesc.bufElSizes        = outBuffElSizes;+            AACENC_OutArgs outArgs;++            e = aacEncEncode (phAacEncoder, &inBuffDesc, &outBuffDesc,+                              &inArgs, &outArgs);+            *(numOutBytesP_inline_c_3)  = outArgs.numOutBytes;+            *(numInSamplesP_inline_c_4) = outArgs.numInSamples;+            *(numAncBytesP_inline_c_5)  = outArgs.numAncBytes;++            return e;+      +}+++int inline_c_Data_MediaBus_FdkAac_EncoderFdkWrapper_3_8fd9c8ff1f367d8600d391d8958211c9959d567d(uintptr_t encoderHandle_inline_c_0) {++             HANDLE_AACENCODER phAacEncoder = (HANDLE_AACENCODER) encoderHandle_inline_c_0;+             return aacEncClose(&phAacEncoder);+          +}+
+ src/Data/MediaBus/FdkAac/EncoderFdkWrapper.hs view
@@ -0,0 +1,511 @@+-- | An internal module that tightly wraps around the Frauenhofer Development+-- Toolkit for AAC audio.+module Data.MediaBus.FdkAac.EncoderFdkWrapper+  ( AacEncErrorCode(..)+  , toAacEncErrorCode+  , simpleConfig+  , Config(..)+  , CreateFailure(..)+  , CreateFailedAt(..)+  , Encoder(..)+  , create+  , EncodeResult(..)+  , EncodeFailure(..)+  , encode+  , flush+  , destroy+  ) where++import Control.Exception+import Data.Coerce+import Data.Int+import Data.Monoid+import qualified Data.Vector.Storable as V+import qualified Data.Vector.Storable.Mutable as VM+import Data.Word+import Foreign.C.Types+import Foreign.ForeignPtr (mallocForeignPtrBytes, withForeignPtr)+import qualified Language.C.Inline as C+import Text.Printf++C.context (C.baseCtx <> C.vecCtx)++C.include "<stdio.h>"++C.include "<stdint.h>"++C.include "fdk-aac/aacenc_lib.h"++-- | Call into the native code to initialize an encoder context, if everything+-- works out great, an 'Encoder' is returned. Use 'destroy' to release the+-- resources associated with an 'Encoder'.+create :: Config -> IO (Either CreateFailure Encoder)+create config@(MkConfig { configModules+                        , configChannels+                        , configAot+                        , configSampleRate+                        , configBitRate+                        , configBitRateMode+                        , configBandwidth+                        , configSbrMode+                        , configSignallingMode+                        , configChannelMode+                        , configAfterburner+                        }) = do+  let confBufMaxLen = 255 :: CUInt+  confBufC <- mallocForeignPtrBytes (fromIntegral confBufMaxLen)+  ((encDelayC, confBufSizeC, frameSizeC, aacEncoderCfgErrorC, errorCodeC), hPtr) <-+    withForeignPtr confBufC $ \confBufP ->+      C.withPtrs $ \(encDelayP, confBufSizeP, frameSizeP, aacEncoderCfgErrorP, errorCodeP) ->+        [C.block| uintptr_t {+              AACENC_ERROR e = AACENC_OK;+              HANDLE_AACENCODER phAacEncoder;+              AACENC_InfoStruct pInfo;+              CHANNEL_MODE channelMode;+              *($(int* aacEncoderCfgErrorP)) = 13;++              switch ($(unsigned int configChannelMode)) {+                  case 1: channelMode = MODE_1;       break;+                  case 2: channelMode = MODE_2;       break;+                  case 3: channelMode = MODE_1_2;     break;+                  case 4: channelMode = MODE_1_2_1;   break;+                  case 5: channelMode = MODE_1_2_2;   break;+                  case 6: channelMode = MODE_1_2_2_1; break;+                 default: channelMode = 0;            break;+              }++              e = aacEncOpen(&phAacEncoder, $(unsigned int configModules), $(unsigned int configChannels));+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 0;+                goto e0;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_AOT, (const UINT) $(unsigned int configAot));+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 1;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_SBR_MODE, (const UINT) $(unsigned int configSbrMode));+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 2;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_SAMPLERATE, (const UINT) $(unsigned int configSampleRate));+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 3;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_CHANNELMODE, channelMode);+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 4;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_BITRATEMODE, (const UINT) $(unsigned int configBitRateMode));+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 5;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_BITRATE, (const UINT) $(unsigned int configBitRate));+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 6;+                goto e1;+              }+              if ((const UINT) $(unsigned int configBandwidth) != 0) {+                e = aacEncoder_SetParam(phAacEncoder, AACENC_BANDWIDTH, (const UINT) $(unsigned int configBandwidth));+                if (e != AACENC_OK) {+                  *($(int* aacEncoderCfgErrorP)) = 7;+                  goto e1;+                }+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_TRANSMUX, TT_MP4_RAW); // TODO extract from TRANSPORT_TYPE in FDK_audio.h+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 8;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_SIGNALING_MODE, (const UINT) $(unsigned int configSignallingMode));+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 9;+                goto e1;+              }+              e = aacEncoder_SetParam(phAacEncoder, AACENC_AFTERBURNER, (const UINT) $(unsigned int configAfterburner));+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 10;+                goto e1;+              }+              e = aacEncEncode(phAacEncoder, NULL, NULL, NULL, NULL);+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 11;+                goto e1;+              }+              e = aacEncInfo(phAacEncoder, &pInfo);+              if (e != AACENC_OK) {+                *($(int* aacEncoderCfgErrorP)) = 12;+                goto e1;+              }++              for (unsigned int i = 0; i < pInfo.confSize && i < $(unsigned int confBufMaxLen); ++i) {+                 *($(unsigned char* confBufP) + i) = pInfo.confBuf[i];+              }+              *($(unsigned int* confBufSizeP)) = pInfo.confSize;+              *($(unsigned int* encDelayP))    = pInfo.encoderDelay;+              *($(unsigned int* frameSizeP))   = pInfo.frameLength;+              return ((uintptr_t) phAacEncoder);++              e1:+                 if (AACENC_OK != aacEncClose(&phAacEncoder)) {+                    printf("Failed to free the AAC Encoder.\n");+                 }++              e0:+                 *($(int* errorCodeP)) = e;+                 return (uintptr_t)NULL;+            } |]+  if hPtr == 0+    then return+           (Left+              MkCreateFailure+              { createFailureErrorCode = toAacEncErrorCode errorCodeC+              , createFailureAt = toEnum (fromIntegral aacEncoderCfgErrorC)+              , createFailureInputConfig = config+              })+    else do+      let ascVM = VM.unsafeFromForeignPtr0 confBufC (fromIntegral confBufSizeC)+      asc <- V.freeze ascVM+      let outSize = fromIntegral (768 * configChannels)+      outV <- VM.new outSize+      return $+        Right $+        MkEncoder+        { encoderHandle = hPtr+        , channelCount = configChannels+        , encoderDelay = fromIntegral encDelayC+        , frameSize = fromIntegral frameSizeC+        , unsafeOutBuffer = outV+        , audioSpecificConfig = coerce asc+        }++-- | A subset of the possible encoder configuration parameters+data Config = MkConfig+  { configModules :: !CUInt+  , configChannels :: !CUInt+  , configAot :: !CUInt+  , configSampleRate :: !CUInt+  , configBitRate :: !CUInt+  , configBitRateMode :: !CUInt+  , configBandwidth :: !CUInt -- ^ The audio frequency bandwidth to be considered+                             -- when compressing audio, if @0@ the setting is+                             -- not applied.+  , configSbrMode :: !CUInt+  , configSignallingMode :: !CUInt+  , configChannelMode :: !CUInt+  , configAfterburner :: !CUInt+  } deriving (Eq, Show)++-- | Generate a 'Config' from three simple parameters+simpleConfig :: Word8 -> Int -> Word32 -> Config+simpleConfig !aot !channels !sampleRate =+  let !configModules = 0x17+      !configChannels = fromIntegral channels+      !highEfficiency = configAot == 5+      !configAot = fromIntegral aot+      !configSampleRate = fromIntegral sampleRate+      !configBitRate =+        configChannelMode *+        round+          (fromIntegral configSampleRate *+           ((if highEfficiency+               then 0.625+               else 1.5) :: Double --+            ))+      !configBitRateMode = 0+      !configBandwidth = 0+      !configSbrMode = fromIntegral (fromEnum highEfficiency)+      !configSignallingMode =+        if highEfficiency+          then 2+          else 0+      !configChannelMode = fromIntegral channels+      !configAfterburner = 1+  in MkConfig+     { configModules+     , configChannels+     , configAot+     , configSampleRate+     , configBitRate+     , configBitRateMode+     , configBandwidth+     , configSbrMode+     , configSignallingMode+     , configChannelMode+     , configAfterburner+     }++-- | Description of the context in which the 'create' function failed.+data CreateFailure = MkCreateFailure+  { createFailureErrorCode :: AacEncErrorCode+  , createFailureAt :: CreateFailedAt+  , createFailureInputConfig :: Config+  } deriving (Show, Eq)++instance Exception CreateFailure++-- | This sum type narrows down the specific step that failed,+-- in the @inline-c@ wrapper code in the 'create' function.+data CreateFailedAt+  = AacEncOpen+  | AacEncSetAot+  | AacEncSetSbrMode+  | AacEncSetSampleRate+  | AacEncSetChannelMode+  | AacEncSetBitRateMode+  | AacEncSetBitRate+  | AacEncSetBandwidth+  | AacEncSetTransMux+  | AacEncSetSignalingMode+  | AacEncSetAfterburner+  | AacEncApplyConfig+  | AacEncReadInfo+  | AacEncUnknownError+  deriving (Eq, Show, Enum)++-- | Handle for a specific encoder, can be created with 'aacEncoderNew'.+data Encoder = MkEncoder+  { encoderHandle :: !CUIntPtr+  , unsafeOutBuffer :: !(VM.IOVector CUChar)+  , channelCount :: !CUInt+  , encoderDelay :: !Word32+  , frameSize :: !Word32+  , audioSpecificConfig :: !(V.Vector Word8)+  }++-- | This instance only shows the 'encoderHandle' as hex string.+instance Show Encoder where+  showsPrec d MkEncoder {encoderHandle} =+    showParen (d > 10) $+    showString (printf "fdk-aac-enc: %016X" (toInteger encoderHandle))++-- | Encode Samples.+encode :: Encoder -> V.Vector Word16 -> IO (Either EncodeFailure EncodeResult)+encode enc@MkEncoder {encoderHandle, unsafeOutBuffer} !vecW16 = do+  let vec = coerce vecW16+  toEncodeResult vec enc =<<+    (C.withPtrs $ \(numOutBytesP, numInSamplesP, numAncBytesP) ->+       [C.block| int {+            AACENC_ERROR e;+            HANDLE_AACENCODER phAacEncoder = (HANDLE_AACENCODER) $(uintptr_t encoderHandle);++            /* Input buffer */+            AACENC_BufDesc inBuffDesc;+            INT inBuffIds[1]             = {IN_AUDIO_DATA};+            INT inBuffSizes[1]           = {$vec-len:vec * 2};+            INT inBuffElSizes[1]         = {2};+            void* inBuffBuffers[1]       = {$vec-ptr:(short *vec)};+            inBuffDesc.numBufs           = 1;+            inBuffDesc.bufs              = inBuffBuffers;+            inBuffDesc.bufferIdentifiers = inBuffIds;+            inBuffDesc.bufSizes          = inBuffSizes;+            inBuffDesc.bufElSizes        = inBuffElSizes;+            AACENC_InArgs inArgs         =+              { .numInSamples = $vec-len:vec+              , .numAncBytes = 0 };++            /* Ouput buffer */+            AACENC_BufDesc outBuffDesc;+            INT outBuffIds[1]             = {OUT_BITSTREAM_DATA};+            INT outBuffSizes[1]           = {$vec-len:unsafeOutBuffer};+            INT outBuffElSizes[1]         = {1};+            void* outBuffBuffers[1]       = {$vec-ptr:(unsigned char *unsafeOutBuffer)};+            outBuffDesc.numBufs           = 1;+            outBuffDesc.bufs              = outBuffBuffers;+            outBuffDesc.bufferIdentifiers = outBuffIds;+            outBuffDesc.bufSizes          = outBuffSizes;+            outBuffDesc.bufElSizes        = outBuffElSizes;+            AACENC_OutArgs outArgs;++            e = aacEncEncode (phAacEncoder, &inBuffDesc, &outBuffDesc,+                              &inArgs, &outArgs);+            *($(int* numOutBytesP))  = outArgs.numOutBytes;+            *($(int* numInSamplesP)) = outArgs.numInSamples;+            *($(int* numAncBytesP))  = outArgs.numAncBytes;++            return e;+         }|])++-- | Encode the contents of the delay lines of the encoder.+flush :: Encoder -> IO (Either EncodeFailure EncodeResult)+flush enc@(MkEncoder {encoderHandle, unsafeOutBuffer}) = do+  toEncodeResult mempty enc =<<+    (C.withPtrs $ \(numOutBytesP, numInSamplesP, numAncBytesP) ->+       [C.block| int {+            AACENC_ERROR e;+            HANDLE_AACENCODER phAacEncoder = (HANDLE_AACENCODER) $(uintptr_t encoderHandle);++            /* Input buffer */+            AACENC_BufDesc inBuffDesc;+            inBuffDesc.numBufs           = 0;++            AACENC_InArgs inArgs =+              { .numInSamples = -1+              , .numAncBytes = 0 };++            AACENC_BufDesc outBuffDesc;+            INT outBuffIds[1]             = {OUT_BITSTREAM_DATA};+            INT outBuffSizes[1]           = {$vec-len:unsafeOutBuffer};+            INT outBuffElSizes[1]         = {1};+            void* outBuffBuffers[1]       = {$vec-ptr:(unsigned char *unsafeOutBuffer)};+            outBuffDesc.numBufs           = 1;+            outBuffDesc.bufs              = outBuffBuffers;+            outBuffDesc.bufferIdentifiers = outBuffIds;+            outBuffDesc.bufSizes          = outBuffSizes;+            outBuffDesc.bufElSizes        = outBuffElSizes;+            AACENC_OutArgs outArgs;++            e = aacEncEncode (phAacEncoder, &inBuffDesc, &outBuffDesc,+                              &inArgs, &outArgs);+            *($(int* numOutBytesP))  = outArgs.numOutBytes;+            *($(int* numInSamplesP)) = outArgs.numInSamples;+            *($(int* numAncBytesP))  = outArgs.numAncBytes;++            return e;+      } |])++-- | Internal function+toEncodeResult+  :: V.Vector C.CShort+  -> Encoder+  -> ((C.CInt, C.CInt, C.CInt), CInt)+  -> IO (Either EncodeFailure EncodeResult)+toEncodeResult vec MkEncoder {unsafeOutBuffer, channelCount} ((numOutBytes, numInSamples, numAncBytes), retCode) =+  let retCode' = toAacEncErrorCode retCode+  in+    if retCode' /= AacEncOk && retCode' /= AacEncEncodeEof +    then return $+         Left+           MkEncodeFailure+           { encodeFailureCode = toAacEncErrorCode retCode+           , encodeFailureNumOutBytes = fromIntegral numOutBytes+           , encodeFailureNumInSamples = fromIntegral numInSamples+           , encodeFailureNumAncBytes = fromIntegral numAncBytes+           }+    else do+      let !numInSamplesI = fromIntegral numInSamples+          !consumedFrames =+            fromIntegral numInSamplesI `div` fromIntegral channelCount+          !leftOverInput =+            if numInSamplesI >= V.length vec+              then Nothing+              else let !inSliceLen = V.length vec - numInSamplesI+                       !inSlice = V.slice numInSamplesI inSliceLen vec+                   in Just inSlice+          !numOutBytesI = fromIntegral numOutBytes+      !encodedOutput <-+        if numOutBytesI == 0+          then return Nothing+          else do+            !outTooLarge <- V.freeze unsafeOutBuffer+            return $ Just $ V.force $ V.slice 0 numOutBytesI outTooLarge+      return $+        Right+          MkEncodeResult+          { encodeResultConsumedFrames = consumedFrames+          , encodeResultLeftOverInput = coerce leftOverInput+          , encodeResultSamples = coerce encodedOutput+          }++-- | Result of 'encode'+data EncodeResult = MkEncodeResult+  { encodeResultConsumedFrames :: !Word64 -- ^ Number of samples from the input+                                         -- that were processed by the encoder+  , encodeResultLeftOverInput :: !(Maybe (V.Vector Word16)) -- ^ The unprocessed+                                                           -- rest of the input.+                                                           -- Only if the input+                                                           -- is larger than the+                                                           -- encoders frame+                                                           -- length a left over+                                                           -- is returned.+  , encodeResultSamples :: !(Maybe (V.Vector Word8)) -- ^ Encoded output. If less+                                                    -- than the frame length+                                                    -- samples have been+                                                    -- encoded, then the result+                                                    -- will be 'Nothing'+                                                    -- otherwise it is the+                                                    -- encoded AAC content+                                                    -- representing the encded+                                                    -- samples delayed by+                                                    -- 'frameLength'.+  }++instance Show EncodeResult where+  showsPrec d MkEncodeResult { encodeResultConsumedFrames+                             , encodeResultLeftOverInput+                             , encodeResultSamples+                             } =+    showParen (d > 10) $+    showString "encode result: consumed input samples: " .+    shows encodeResultConsumedFrames .+    showString ", left over input values: " .+    (maybe (showString "n/a") (shows . V.length) encodeResultLeftOverInput) .+    showString ", encoded output bytes: " .+    (maybe (showString "n/a") (shows . V.length) encodeResultSamples)++-- | Information about encoder when an 'encode' failure occured.+data EncodeFailure = MkEncodeFailure+  { encodeFailureCode :: !AacEncErrorCode+  , encodeFailureNumOutBytes :: !Word64+  , encodeFailureNumInSamples :: !Int16+  , encodeFailureNumAncBytes :: !Word64+  } deriving (Eq)++instance Exception EncodeFailure++instance Show EncodeFailure where+  show MkEncodeFailure {..} =+    printf+      "(FDK AAC encode error: %s, numOutBytes:  %d, numInSamples: %d, numAncBytes: %d)"+      (show encodeFailureCode)+      encodeFailureNumOutBytes+      encodeFailureNumInSamples+      encodeFailureNumAncBytes++-- | Close an FDK-AAC encoder.+destroy :: Encoder -> IO AacEncErrorCode+destroy MkEncoder {encoderHandle} = do+  toAacEncErrorCode . fromIntegral <$>+    [C.block| int {+             HANDLE_AACENCODER phAacEncoder = (HANDLE_AACENCODER) $(uintptr_t encoderHandle);+             return aacEncClose(&phAacEncoder);+          } |]++-- | Error codes from the C-library, translated to this sum type+data AacEncErrorCode+  = AacEncOk+  | AacEncInvalidHandle+  | AacEncMemoryError+  | AacEncUnsupportedParameter+  | AacEncInvalidConfig+  | AacEncInitError+  | AacEncInitAacError+  | AacEncInitSbrError+  | AacEncInitTpError+  | AacEncInitMetaError+  | AacEncEncoderError+  | AacEncEncodeEof+  | AacEncErrorOther CInt+  deriving (Eq, Show)++-- | Create an 'AacEncErrorCode' from the @int@ returned by the C-code.+toAacEncErrorCode :: CInt -> AacEncErrorCode+toAacEncErrorCode e =+  case e of+    0x00 -> AacEncOk+    0x20 -> AacEncInvalidHandle+    0x21 -> AacEncMemoryError+    0x22 -> AacEncUnsupportedParameter+    0x23 -> AacEncInvalidConfig+    0x40 -> AacEncInitError+    0x41 -> AacEncInitAacError+    0x42 -> AacEncInitSbrError+    0x43 -> AacEncInitTpError+    0x44 -> AacEncInitMetaError+    0x60 -> AacEncEncoderError+    0x80 -> AacEncEncodeEof+    othr -> AacEncErrorOther othr