{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE LambdaCase #-}

-- |
-- Module      :  Codec.Audio.FLAC.StreamDecoder.Internal
-- Copyright   :  © 2016–present Mark Karpov
-- License     :  BSD 3 clause
--
-- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
-- Stability   :  experimental
-- Portability :  portable
--
-- Low-level Haskell wrapper around FLAC stream decoder API.
module Codec.Audio.FLAC.StreamDecoder.Internal
  ( withDecoder,
    decoderSetMd5Checking,
    decoderGetState,
    decoderGetBlockSize,
    decoderProcessSingle,
    decoderProcessUntilEndOfMetadata,
    decoderFinish,
  )
where

import Codec.Audio.FLAC.StreamDecoder.Internal.Types
import Codec.Audio.FLAC.Util
import Control.Monad.Catch
import Data.Word
import Foreign.C.Types

-- | Create and use a 'Decoder'. The decoder is guaranteed to be freed even
-- in the case of exception.
--
-- If memory for the encoder cannot be allocated, corresponding
-- 'DecoderException' is raised.
withDecoder :: (Decoder -> IO a) -> IO a
withDecoder :: (Decoder -> IO a) -> IO a
withDecoder f :: Decoder -> IO a
f = IO (Maybe Decoder)
-> (Maybe Decoder -> IO ()) -> (Maybe Decoder -> IO a) -> IO a
forall (m :: * -> *) a c b.
MonadMask m =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracket IO (Maybe Decoder)
decoderNew ((Decoder -> IO ()) -> Maybe Decoder -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Decoder -> IO ()
decoderDelete) ((Maybe Decoder -> IO a) -> IO a)
-> (Maybe Decoder -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \case
  Nothing ->
    DecoderException -> IO a
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM
      (DecoderState -> DecoderException
DecoderFailed DecoderState
DecoderStateMemoryAllocationError)
  Just x :: Decoder
x -> Decoder -> IO a
f Decoder
x

-- | Create a new stream decoder instance with the default settings. In the
-- case of memory allocation problem 'Nothing' is returned.
decoderNew :: IO (Maybe Decoder)
decoderNew :: IO (Maybe Decoder)
decoderNew = Decoder -> Maybe Decoder
forall a p. Coercible a (Ptr p) => a -> Maybe a
maybePtr (Decoder -> Maybe Decoder) -> IO Decoder -> IO (Maybe Decoder)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO Decoder
c_decoder_new

foreign import ccall unsafe "FLAC__stream_decoder_new"
  c_decoder_new :: IO Decoder

-- | Free a decoder instance.
decoderDelete :: Decoder -> IO ()
decoderDelete :: Decoder -> IO ()
decoderDelete = Decoder -> IO ()
c_decoder_delete

foreign import ccall unsafe "FLAC__stream_decoder_delete"
  c_decoder_delete :: Decoder -> IO ()

-- | Set MD5 signature checking. If 'True' the decoder will compute the MD5
-- signature of the unencoded audio data while decoding and compare it to
-- the signature from the stream info block.
decoderSetMd5Checking :: Decoder -> Bool -> IO Bool
decoderSetMd5Checking :: Decoder -> Bool -> IO Bool
decoderSetMd5Checking = Decoder -> Bool -> IO Bool
c_decoder_set_md5_checking

foreign import ccall unsafe "FLAC__stream_decoder_set_md5_checking"
  c_decoder_set_md5_checking :: Decoder -> Bool -> IO Bool

-- | Get the current decoder state.
decoderGetState :: Decoder -> IO DecoderState
decoderGetState :: Decoder -> IO DecoderState
decoderGetState = (CUInt -> DecoderState) -> IO CUInt -> IO DecoderState
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CUInt -> DecoderState
forall a b. (Integral a, Enum b) => a -> b
toEnum' (IO CUInt -> IO DecoderState)
-> (Decoder -> IO CUInt) -> Decoder -> IO DecoderState
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Decoder -> IO CUInt
c_decoder_get_state

foreign import ccall unsafe "FLAC__stream_decoder_get_state"
  c_decoder_get_state :: Decoder -> IO CUInt

-- | Get frame size as a number of inter-channel samples of last decoded
-- frame.
decoderGetBlockSize :: Decoder -> IO Word32
decoderGetBlockSize :: Decoder -> IO Word32
decoderGetBlockSize = (CUInt -> Word32) -> IO CUInt -> IO Word32
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CUInt -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (IO CUInt -> IO Word32)
-> (Decoder -> IO CUInt) -> Decoder -> IO Word32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Decoder -> IO CUInt
c_decoder_get_blocksize

foreign import ccall unsafe "FLAC__stream_decoder_get_blocksize"
  c_decoder_get_blocksize :: Decoder -> IO CUInt

-- | Process one audio frame. Return 'False' on failure.
decoderProcessSingle :: Decoder -> IO Bool
decoderProcessSingle :: Decoder -> IO Bool
decoderProcessSingle = Decoder -> IO Bool
c_decoder_process_single

foreign import ccall unsafe "FLAC__stream_decoder_process_single"
  c_decoder_process_single :: Decoder -> IO Bool

-- | Decode until the end of the metadata. We use this to skip to the audio
-- stream.
decoderProcessUntilEndOfMetadata :: Decoder -> IO Bool
decoderProcessUntilEndOfMetadata :: Decoder -> IO Bool
decoderProcessUntilEndOfMetadata = Decoder -> IO Bool
c_decoder_process_until_end_of_metadata

foreign import ccall unsafe "FLAC__stream_decoder_process_until_end_of_metadata"
  c_decoder_process_until_end_of_metadata :: Decoder -> IO Bool

-- | Finish the decoding process and release resources (also resets decoder
-- and its settings). Return 'False' in the case of trouble.
decoderFinish :: Decoder -> IO Bool
decoderFinish :: Decoder -> IO Bool
decoderFinish = Decoder -> IO Bool
c_decoder_finish

foreign import ccall unsafe "FLAC__stream_decoder_finish"
  c_decoder_finish :: Decoder -> IO Bool