packages feed

lame 0.2.0 → 0.2.1

raw patch · 9 files changed

+481/−482 lines, 9 filesdep ~basedep ~textdep ~transformers

Dependency ranges changed: base, text, transformers

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+## LAME 0.2.1++* Works with `text-2.0` and older.+* Dropped support for GHC 8.x.+ ## LAME 0.2.0  * Got rid of `data-default-class`. Instead of making `EncoderSettings` an
Codec/Audio/LAME.hs view
@@ -1,29 +1,29 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+ -- | -- Module      :  Codec.Audio.LAME--- Copyright   :  © 2017–2019 Mark Karpov+-- Copyright   :  © 2017–present Mark Karpov -- License     :  BSD 3 clause -- -- Maintainer  :  Mark Karpov <markkarpov92@gmail.com> -- Stability   :  experimental -- Portability :  portable ----- The module provides an interface to LAME MP3 encoder. All you need to do--- to encode a WAVE (or RF64) file is to call 'encodeMp3', which see.--{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards   #-}-+-- The module provides an interface to the LAME encoder. module Codec.Audio.LAME-  ( encodeMp3-  , EncoderSettings (..)-  , defaultEncoderSettings-  , Compression (..)-  , VbrMode (..)-  , MetadataPlacement (..)-  , FilterSetup (..)-  , I.LameException (..) )+  ( encodeMp3,+    EncoderSettings (..),+    defaultEncoderSettings,+    Compression (..),+    VbrMode (..),+    MetadataPlacement (..),+    FilterSetup (..),+    I.LameException (..),+  ) where +import qualified Codec.Audio.LAME.Internal as I import Codec.Audio.Wave import Control.Monad import Control.Monad.Catch@@ -31,26 +31,23 @@ import Data.ByteString (ByteString) import Data.Maybe (fromMaybe) import Data.Text (Text)+import qualified Data.Text as T import Data.Word import System.Directory import System.FilePath import System.IO-import qualified Codec.Audio.LAME.Internal as I-import qualified Data.Text                 as T  -- | LAME encoder settings. -- -- Currently some noise-shaping and psycho-acoustic parameters are omitted, -- but can be added on request.- data EncoderSettings = EncoderSettings-  { encoderCompression :: !Compression-    -- ^ How to determine amount of compression to apply, see 'Compression'.+  { -- | How to determine amount of compression to apply, see 'Compression'.     -- Default value: @'CompressionRatio' 11@ (corresponds to 128 kbps).-  , encoderQuality :: !(Maybe Word)-    -- ^ Select algorithm. This variable will effect quality by selecting-    -- expensive or cheap algorithms. 0 gives the best quality (very slow). 9 is-    -- very fast, but gives worst quality.+    encoderCompression :: !Compression,+    -- | Select the algorithm. This variable will affect the quality by+    -- selecting expensive or cheap algorithms. 0 gives the best quality+    -- (very slow). 9 is very fast, but gives the worst quality.     --     --     * 2—near-best quality, not too slow.     --     * 5—good quality, fast.@@ -58,107 +55,108 @@     --     -- Default value: 'Nothing', meaning that LAME will select the best     -- value itself.-  , encoderNoGap :: !(Maybe (Word32, Word32))-    -- ^ Enable “no gap” encoding. The first value in the tuple specifies+    encoderQuality :: !(Maybe Word),+    -- | Enable “no gap” encoding. The first value in the tuple specifies     -- current index and the second value specifies total number of tracks.     -- Default value: 'Nothing'—disabled.-  , encoderErrorProtection :: !Bool-    -- ^ Whether to enable error protection. Error protection means that 2-    -- bytes from each frame for CRC checksum. Default value: 'False'.-  , encoderStrictISO :: !Bool-    -- ^ Whether to enforce strict ISO compliance. Default value: 'False'.-  , encoderSampleRate :: !(Maybe Word32)-    -- ^ Sample rate of output file. Default value: 'Nothing', which means-    -- use sample rate of input file.-  , encoderScale :: !Float-    -- ^ Scale input stream by multiplying samples by this value. Default+    encoderNoGap :: !(Maybe (Word32, Word32)),+    -- | Whether to enable the error protection. Error protection means that+    -- 2 bytes from each frame are used for CRC checksum. Default value:+    -- 'False'.+    encoderErrorProtection :: !Bool,+    -- | Whether to enforce strict ISO compliance. Default value: 'False'.+    encoderStrictISO :: !Bool,+    -- | Sample rate of the output file. Default value: 'Nothing', which+    -- means that the sample rate of the input file will be used.+    encoderSampleRate :: !(Maybe Word32),+    -- | Scale input stream by multiplying samples by this value. Default     -- value: 1 (no scaling).-  , encoderFreeFormat :: !Bool-    -- ^ Use free format. Default value: 'False'.-  , encoderCopyright :: !Bool-    -- ^ Mark as copyright protected. Default value: 'False'.-  , encoderOriginal :: !Bool-    -- ^ Mark as original. Default value: 'True'.-  , encoderWriteVbrTag :: !Bool-    -- ^ Whether to write Xing VBR header frame. Default value: 'True'.-  , encoderMetadataPlacement :: !MetadataPlacement-    -- ^ Which container to use to write metadata. Default value:+    encoderScale :: !Float,+    -- | Use free format. Default value: 'False'.+    encoderFreeFormat :: !Bool,+    -- | Mark as copyright protected. Default value: 'False'.+    encoderCopyright :: !Bool,+    -- | Mark as original. Default value: 'True'.+    encoderOriginal :: !Bool,+    -- | Whether to write Xing VBR header frame. Default value: 'True'.+    encoderWriteVbrTag :: !Bool,+    -- | Which container to use to write metadata. Default value:     -- 'Id3v2Only'.-  , encoderTagTitle :: !(Maybe Text)-    -- ^ Title tag to write. Default value: 'Nothing'.-  , encoderTagArtist :: !(Maybe Text)-    -- ^ Artist tag to write. Default value: 'Nothing'.-  , encoderTagAlbum :: !(Maybe Text)-    -- ^ Album tag to write. Default value: 'Nothing'.-  , encoderTagYear :: !(Maybe Text)-    -- ^ Year tag to write. Default value: 'Nothing'.-  , encoderTagComment :: !(Maybe Text)-    -- ^ Comment tag to write. Default value: 'Nothing'.-  , encoderTagTrack :: !(Maybe (Word8, Maybe Word8))-    -- ^ Track number (first in tuple) and (optionally) total number of-    -- tracks (second in tuple). Default value: 'Nothing'.-  , encoderTagGenre :: !(Maybe Text)-    -- ^ Genre tag to write. Default value: 'Nothing'.-  , encoderAlbumArt :: !(Maybe ByteString)-    -- ^ Album art (a picture) to write (JPEG\/PNG\/GIF). Default value:+    encoderMetadataPlacement :: !MetadataPlacement,+    -- | Title tag to write. Default value: 'Nothing'.+    encoderTagTitle :: !(Maybe Text),+    -- | Artist tag to write. Default value: 'Nothing'.+    encoderTagArtist :: !(Maybe Text),+    -- | Album tag to write. Default value: 'Nothing'.+    encoderTagAlbum :: !(Maybe Text),+    -- | Year tag to write. Default value: 'Nothing'.+    encoderTagYear :: !(Maybe Text),+    -- | Comment tag to write. Default value: 'Nothing'.+    encoderTagComment :: !(Maybe Text),+    -- | Track number (the first component of the tuple) and (optionally)+    -- the total number of tracks (the second component of the tuple).+    -- Default value: 'Nothing'.+    encoderTagTrack :: !(Maybe (Word8, Maybe Word8)),+    -- | Genre tag to write. Default value: 'Nothing'.+    encoderTagGenre :: !(Maybe Text),+    -- | Album art (a picture) to write (JPEG\/PNG\/GIF). Default value:     -- 'Nothing'.-  , encoderLowpassFilter :: !FilterSetup-    -- ^ Settings for low-pass filter. Default value: 'FilterAuto'.-  , encoderHighpassFilter :: !FilterSetup-    -- ^ Settings for high-pass filter. Default value: 'FilterAuto'.-  } deriving (Show, Read, Eq, Ord)+    encoderAlbumArt :: !(Maybe ByteString),+    -- | Settings for the low-pass filter. Default value: 'FilterAuto'.+    encoderLowpassFilter :: !FilterSetup,+    -- | Settings for the high-pass filter. Default value: 'FilterAuto'.+    encoderHighpassFilter :: !FilterSetup+  }+  deriving (Show, Read, Eq, Ord) --- | Default value of 'EncoderSettings'.+-- | The default value of 'EncoderSettings'. -- -- @since 0.2.0- defaultEncoderSettings :: EncoderSettings-defaultEncoderSettings = EncoderSettings-  { encoderScale           = 1-  , encoderSampleRate      = Nothing-  , encoderFreeFormat      = False-  , encoderNoGap           = Nothing-  , encoderCopyright       = False-  , encoderOriginal        = True-  , encoderErrorProtection = False-  , encoderStrictISO       = False-  , encoderWriteVbrTag     = True-  , encoderQuality         = Nothing-  , encoderCompression     = CompressionRatio 11-  , encoderMetadataPlacement = Id3v2Only-  , encoderTagTitle        = Nothing-  , encoderTagArtist       = Nothing-  , encoderTagAlbum        = Nothing-  , encoderTagYear         = Nothing-  , encoderTagComment      = Nothing-  , encoderTagTrack        = Nothing-  , encoderTagGenre        = Nothing-  , encoderAlbumArt        = Nothing-  , encoderLowpassFilter   = FilterAuto-  , encoderHighpassFilter  = FilterAuto-  }---- | The data type represents supported options for compression. You can--- specify either fixed bitrate, compression ratio, or use the VBR mode.+defaultEncoderSettings =+  EncoderSettings+    { encoderScale = 1,+      encoderSampleRate = Nothing,+      encoderFreeFormat = False,+      encoderNoGap = Nothing,+      encoderCopyright = False,+      encoderOriginal = True,+      encoderErrorProtection = False,+      encoderStrictISO = False,+      encoderWriteVbrTag = True,+      encoderQuality = Nothing,+      encoderCompression = CompressionRatio 11,+      encoderMetadataPlacement = Id3v2Only,+      encoderTagTitle = Nothing,+      encoderTagArtist = Nothing,+      encoderTagAlbum = Nothing,+      encoderTagYear = Nothing,+      encoderTagComment = Nothing,+      encoderTagTrack = Nothing,+      encoderTagGenre = Nothing,+      encoderAlbumArt = Nothing,+      encoderLowpassFilter = FilterAuto,+      encoderHighpassFilter = FilterAuto+    } +-- | The supported options for compression. You can specify either fixed+-- bitrate, compression ratio, or use the VBR mode. data Compression-  = CompressionBitrate Word-    -- ^ Specify compression by setting a fixed bitrate, e.g.+  = -- | Specify the compression by setting a fixed bitrate, e.g.     -- @'CompressionBitrate' 320@.-  | CompressionRatio Float-    -- ^ Specify compression ratio.-  | CompressionVBR VbrMode Word-    -- ^ VBR. Here you can specify which mode to use and the second argument-    -- is VBR quality from 0 (highest), to 9 (lowest). Good default is 4.+    CompressionBitrate Word+  | -- | Specify the compression ratio.+    CompressionRatio Float+  | -- | VBR. Here you can specify which mode to use and the second argument+    -- is VBR quality from 0 (highest), to 9 (lowest). A good default is 4.+    CompressionVBR VbrMode Word   deriving (Show, Read, Eq, Ord) --- | Variable bitrate (VBR) modes with their associated parameters.-+-- | Variable bitrate (VBR) modes and their parameters. data VbrMode-  = VbrRh-    -- ^ VBR RH.-  | VbrAbr (Maybe Word) (Maybe Word) (Maybe Word) Bool-    -- ^ VBR ABR. Parameters of the data constructor in order:+  = -- | VBR RH.+    VbrRh+  | -- | VBR ABR. Parameters of the data constructor in order:     --     --     * Minimal bitrate     --     * Mean bitrate@@ -166,55 +164,62 @@     --     * “Hard minimal bitrate”, if 'True', then the specified minimal     --       bitrate will be enforced in all cases (normally it's violated     --       for silence).-  | VbrMtrh-    -- ^ VBR MTRH+    VbrAbr (Maybe Word) (Maybe Word) (Maybe Word) Bool+  | -- | VBR MTRH+    VbrMtrh   deriving (Show, Read, Eq, Ord)  -- | Which container to use to write tags\/metadata.- data MetadataPlacement-  = Id3v1Only          -- ^ Only write ID3v1 metadata-  | Id3v2Only          -- ^ Only write ID3v2 metadata-  | Id3Both            -- ^ Write ID3v1 and ID3v2+  = -- | Only write ID3v1 metadata+    Id3v1Only+  | -- | Only write ID3v2 metadata+    Id3v2Only+  | -- | Write ID3v1 and ID3v2+    Id3Both   deriving (Show, Read, Eq, Ord)  -- | Settings for a filter.- data FilterSetup-  = FilterAuto         -- ^ Filter settings are chosen automatically-  | FilterDisabled     -- ^ Filter is disabled-  | FilterManual Word (Maybe Word)-    -- ^ The first parameter is the filter's frequency (cut-off frequency)+  = -- | Filter settings are chosen automatically+    FilterAuto+  | -- | Filter is disabled+    FilterDisabled+  | -- | The first parameter is the filter's frequency (cut-off frequency)     -- in Hz and the second parameter is width of transition band in Hz (if     -- 'Nothing', it's chosen automatically).+    FilterManual Word (Maybe Word)   deriving (Show, Read, Eq, Ord) --- | Encode a WAVE file or RF64 file to MP3.+-- | Encode a WAVE file or RF64 file. -- -- If the input file is not a valid WAVE file, 'WaveException' will be--- thrown. 'I.LameException' is thrown when underlying LAME encoder reports--- a problem.+-- thrown. 'I.LameException' is thrown when the LAME encoder reports a+-- problem. ----- Not all sample formats and bit depth are currently supported. Supported--- sample formats include:+-- Not all sample formats and bit depths are currently supported. The+-- supported sample formats include: -- --     * PCM with samples represented as signed integers > 8 bit and <= 16 --       bit per mono-sample. --     * IEEE floating point (32 bit) samples. --     * IEEE floating point (64 bit) samples. ----- If you feed the encoder with something else, expect--- 'I.LameUnsupportedSampleFormat' to be thrown.--encodeMp3 :: MonadIO m-  => EncoderSettings   -- ^ Encoder settings-  -> FilePath          -- ^ WAVE file to encode-  -> FilePath          -- ^ Where to save the resulting MP3 file-  -> m ()+-- If you feed the encoder something else, 'I.LameUnsupportedSampleFormat'+-- will be thrown.+encodeMp3 ::+  MonadIO m =>+  -- | Encoder settings+  EncoderSettings ->+  -- | WAVE file to encode+  FilePath ->+  -- | Where to save the resulting MP3 file+  FilePath ->+  m () encodeMp3 EncoderSettings {..} ipath' opath' = liftIO . I.withLame $ \l -> do   ipath <- makeAbsolute ipath'   opath <- makeAbsolute opath'-  wave@Wave {..}  <- readWaveFile ipath+  wave@Wave {..} <- readWaveFile ipath   case waveSampleFormat of     SampleFormatPcmInt bps ->       when (bps > 16 || bps <= 8) $@@ -229,7 +234,7 @@   I.setFreeFormat l encoderFreeFormat   forM_ encoderNoGap $ \(currentIndex, total) -> do     I.setNoGapCurrentIndex l (fromIntegral currentIndex)-    I.setNoGapTotal        l (fromIntegral total)+    I.setNoGapTotal l (fromIntegral total)   I.setCopyright l encoderCopyright   I.setOriginal l encoderOriginal   I.setErrorProtection l encoderErrorProtection@@ -247,25 +252,25 @@         VbrRh -> I.setVBR l I.VbrRh         VbrAbr minRate meanRate maxRate hardMin -> do           I.setVBR l I.VbrAbr-          forM_ minRate  (I.setVBRMinBitrate  l . fromIntegral)+          forM_ minRate (I.setVBRMinBitrate l . fromIntegral)           forM_ meanRate (I.setVBRMeanBitrate l . fromIntegral)-          forM_ maxRate  (I.setVBRMaxBitrate  l . fromIntegral)+          forM_ maxRate (I.setVBRMaxBitrate l . fromIntegral)           I.setVBRHardMin l hardMin         VbrMtrh -> I.setVBR l I.VbrMtrh   I.id3TagInit l   case encoderMetadataPlacement of     Id3v1Only -> I.id3TagV1Only l     Id3v2Only -> I.id3TagV2Only l-    Id3Both   -> I.id3TagAddV2  l-  forM_ encoderTagTitle   (I.id3TagSetTextInfo l "TIT2")-  forM_ encoderTagArtist  (I.id3TagSetTextInfo l "TPE1")-  forM_ encoderTagAlbum   (I.id3TagSetTextInfo l "TALB")-  forM_ encoderTagYear    (I.id3TagSetTextInfo l "TYER")+    Id3Both -> I.id3TagAddV2 l+  forM_ encoderTagTitle (I.id3TagSetTextInfo l "TIT2")+  forM_ encoderTagArtist (I.id3TagSetTextInfo l "TPE1")+  forM_ encoderTagAlbum (I.id3TagSetTextInfo l "TALB")+  forM_ encoderTagYear (I.id3TagSetTextInfo l "TYER")   forM_ encoderTagComment (I.id3TagSetComment l)-  forM_ encoderTagTrack   (uncurry renderTrackNumber >=> I.id3TagSetTextInfo l "TRCK")-  forM_ encoderTagGenre   (I.id3TagSetTextInfo l "TCON")-  forM_ encoderAlbumArt   (I.id3TagSetAlbumArt l)-  setupFilter I.setLowpassFreq  I.setLowpassWidth  l encoderLowpassFilter+  forM_ encoderTagTrack (uncurry renderTrackNumber >=> I.id3TagSetTextInfo l "TRCK")+  forM_ encoderTagGenre (I.id3TagSetTextInfo l "TCON")+  forM_ encoderAlbumArt (I.id3TagSetAlbumArt l)+  setupFilter I.setLowpassFreq I.setLowpassWidth l encoderLowpassFilter   setupFilter I.setHighpassFreq I.setHighpassWidth l encoderHighpassFilter   I.initParams l   withTempFile' opath $ \otemp -> do@@ -275,14 +280,17 @@ ---------------------------------------------------------------------------- -- Helpers --- | Setup a filter for given 'I.Lame'.--setupFilter-  :: (I.Lame -> Int -> IO ()) -- ^ How to set cut-off frequncy-  -> (I.Lame -> Int -> IO ()) -- ^ How to set width of transition band-  -> I.Lame            -- ^ Settings to edit-  -> FilterSetup       -- ^ Filter setup to apply-  -> IO ()+-- | Setup a filter for a given 'I.Lame'.+setupFilter ::+  -- | How to set cut-off frequncy+  (I.Lame -> Int -> IO ()) ->+  -- | How to set width of transition band+  (I.Lame -> Int -> IO ()) ->+  -- | Settings to edit+  I.Lame ->+  -- | Filter setup to apply+  FilterSetup ->+  IO () setupFilter setFreq _ l FilterAuto = setFreq l 0 setupFilter setFreq _ l FilterDisabled = setFreq l (-1) setupFilter setFreq setWidth l (FilterManual freq mwidth) = do@@ -292,31 +300,28 @@ -- | A custom wrapper for creating temporary files in the same directory as -- given file. 'Handle' is not opened, you only get 'FilePath' in the -- callback.- withTempFile' :: FilePath -> (FilePath -> IO a) -> IO a withTempFile' path = bracketOnError acquire cleanup   where     acquire = do-      (path',h) <- openBinaryTempFile dir file+      (path', h) <- openBinaryTempFile dir file       hClose h       return path'     cleanup = ignoringIOErrors . removeFile -- in case exception strikes-              -- before we create the actual file-    dir     = takeDirectory path-    file    = takeFileName  path---- | Perform specified action ignoring IO exceptions it may throw.+    -- before we create the actual file+    dir = takeDirectory path+    file = takeFileName path +-- | Perform an action ignoring IO exceptions it may throw. ignoringIOErrors :: IO () -> IO () ignoringIOErrors ioe = ioe `catch` handler   where     handler :: IOError -> IO ()     handler = const (return ()) --- | Render track number and optionally total number of tracks as a strict--- 'Text' value.-+-- | Render the track number and optionally the total number of tracks as a+-- strict 'Text' value. renderTrackNumber :: Word8 -> Maybe Word8 -> IO Text-renderTrackNumber 0 t          = throwM (I.LameInvalidTrackNumber 0 t)+renderTrackNumber 0 t = throwM (I.LameInvalidTrackNumber 0 t) renderTrackNumber n t@(Just 0) = throwM (I.LameInvalidTrackNumber n t) renderTrackNumber n t = return . T.pack $ show n ++ maybe "" (("/" ++) . show) t
Codec/Audio/LAME/Internal.hs view
@@ -1,6 +1,10 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE RecordWildCards #-}+ -- | -- Module      :  Codec.Audio.LAME.Internal--- Copyright   :  © 2017–2019 Mark Karpov+-- Copyright   :  © 2017–present Mark Karpov -- License     :  BSD 3 clause -- -- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>@@ -8,104 +12,114 @@ -- Portability :  portable -- -- Low-level non-public helpers.--{-# LANGUAGE FlexibleContexts         #-}-{-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE RecordWildCards          #-}- module Codec.Audio.LAME.Internal   ( -- * Types-    Lame-  , VbrMode (..)-  , LameException (..)+    Lame,+    VbrMode (..),+    LameException (..),+     -- * Low-level API-  , withLame-  , initParams+    withLame,+    initParams,+     -- ** Input stream description-  , setNumSamples-  , setInputSampleRate-  , setNumChannels-  , setScale-  , setOutputSampleRate+    setNumSamples,+    setInputSampleRate,+    setNumChannels,+    setScale,+    setOutputSampleRate,+     -- ** General control parameters-  , setWriteVbrTag-  , setQuality-  , setFreeFormat-  , setFindReplayGain-  , setNoGapTotal-  , setNoGapCurrentIndex-  , setBitrate-  , setCompressionRatio+    setWriteVbrTag,+    setQuality,+    setFreeFormat,+    setFindReplayGain,+    setNoGapTotal,+    setNoGapCurrentIndex,+    setBitrate,+    setCompressionRatio,+     -- ** Frame parameters-  , setCopyright-  , setOriginal-  , setErrorProtection-  , setStrictISO+    setCopyright,+    setOriginal,+    setErrorProtection,+    setStrictISO,+     -- ** VBR control-  , setVBR-  , setVBRQ-  , setVBRMinBitrate-  , setVBRMeanBitrate-  , setVBRMaxBitrate-  , setVBRHardMin+    setVBR,+    setVBRQ,+    setVBRMinBitrate,+    setVBRMeanBitrate,+    setVBRMaxBitrate,+    setVBRHardMin,+     -- ** Filtering control-  , setLowpassFreq-  , setLowpassWidth-  , setHighpassFreq-  , setHighpassWidth+    setLowpassFreq,+    setLowpassWidth,+    setHighpassFreq,+    setHighpassWidth,+     -- * Tags-  , id3TagInit-  , id3TagAddV2-  , id3TagV1Only-  , id3TagV2Only-  , id3TagSetTextInfo-  , id3TagSetComment-  , id3TagSetAlbumArt+    id3TagInit,+    id3TagAddV2,+    id3TagV1Only,+    id3TagV2Only,+    id3TagSetTextInfo,+    id3TagSetComment,+    id3TagSetAlbumArt,+     -- * Encoding-  , encodingHelper )+    encodingHelper,+  ) where  import Codec.Audio.Wave import Control.Monad.Catch import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Unsafe as B import Data.Coerce import Data.Text (Text)+import qualified Data.Text.Encoding as TE import Data.Void import Foreign hiding (void) import Foreign.C.String import Foreign.C.Types (CSize (..))-import qualified Data.ByteString.Unsafe as B-import qualified Data.Text.Foreign      as TF  ---------------------------------------------------------------------------- -- Types --- | An opaque newtype wrapper around @'Ptr' 'Void'@, serves to represent+-- | An opaque newtype wrapper around @'Ptr' 'Void'@ that represents the -- pointer to the structure that does all the bookkeeping in LAME.- newtype Lame = Lame (Ptr Void)  -- | Enumeration of VBR modes.- data VbrMode-  = VbrRh              -- ^ VBR RH-  | VbrAbr             -- ^ VBR ABR-  | VbrMtrh            -- ^ VBR MTRH+  = -- | VBR RH+    VbrRh+  | -- | VBR ABR+    VbrAbr+  | -- | VBR MTRH+    VbrMtrh   deriving (Show, Read, Eq, Ord, Bounded, Enum) --- | Enumeration of problems you can have with LAME.-+-- | Enumeration of problems you may have with LAME. data LameException-  = LameGenericError   -- ^ A “generic error” happened-  | LameNoMemory       -- ^ Memory allocation issue-  | LameBadBitrate     -- ^ Unsupported bitrate-  | LameBadSampleFreq  -- ^ Unsupported sample rate-  | LameInternalError  -- ^ An “Internal error” happened-  | LameInvalidTrackNumber Word8 (Maybe Word8)-    -- ^ Invalid track number (first argument) or total number of tracks+  = -- | A “generic error” happened+    LameGenericError+  | -- | Memory allocation issue+    LameNoMemory+  | -- | Unsupported bitrate+    LameBadBitrate+  | -- | Unsupported sample rate+    LameBadSampleFreq+  | -- | An “Internal error” happened+    LameInternalError+  | -- | Invalid track number (first argument) or total number of tracks     -- (second argument) was supplied-  | LameUnsupportedSampleFormat SampleFormat-    -- ^ This sample format is not supported at this time+    LameInvalidTrackNumber Word8 (Maybe Word8)+  | -- | This sample format is not supported at this time+    LameUnsupportedSampleFormat SampleFormat   deriving (Eq, Show, Read)  instance Exception LameException@@ -116,17 +130,15 @@ -- | Create and use a 'Lame' (pointer structure needed for talking to the -- LAME API). ----- If memory cannot be allocated, corresponding 'LameException' is raised.-+-- If memory cannot be allocated, 'LameNoMemory' is thrown. withLame :: (Lame -> IO a) -> IO a withLame f = bracket lameInit (mapM_ lameClose) $ \mlame ->   case mlame of     Nothing -> throwM LameNoMemory     Just x -> f x --- | Create a new 'Lame'. In the case of memory allocation problem 'Nothing'--- is returned.-+-- | Create a new 'Lame'. In the case of a memory allocation problem+-- 'Nothing' is returned. lameInit :: IO (Maybe Lame) lameInit = maybePtr <$> c_lame_init @@ -134,7 +146,6 @@   c_lame_init :: IO Lame  -- | Free 'Lame' structure and all associated buffers.- lameClose :: Lame -> IO () lameClose = c_lame_close @@ -142,8 +153,7 @@   c_lame_close :: Lame -> IO ()  -- | Set more internal configuration based on previously set parameters.--- Should be called when all the other stuff is set.-+-- Should be called when everything else is set. initParams :: Lame -> IO () initParams = handleErrors . c_lame_init_params @@ -154,23 +164,20 @@ -- Input stream description  -- | Set the total number of samples to encode.- setNumSamples :: Lame -> Word64 -> IO () setNumSamples l x = handleErrors (c_lame_set_num_samples l x)  foreign import ccall unsafe "lame_set_num_samples"   c_lame_set_num_samples :: Lame -> Word64 -> IO Int --- | Set sample rate of the input stream.-+-- | Set the sample rate of the input stream. setInputSampleRate :: Lame -> Int -> IO () setInputSampleRate l x = handleErrors (c_lame_set_in_samplerate l x)  foreign import ccall unsafe "lame_set_in_samplerate"   c_lame_set_in_samplerate :: Lame -> Int -> IO Int --- | Set number of channels in input stream.-+-- | Set the number of channels in the input stream. setNumChannels :: Lame -> Int -> IO () setNumChannels l x = handleErrors (c_lame_set_num_channels l x) @@ -178,16 +185,14 @@   c_lame_set_num_channels :: Lame -> Int -> IO Int  -- | Scale the input by this amount before encoding.- setScale :: Lame -> Float -> IO () setScale l x = handleErrors (c_lame_set_scale l x)  foreign import ccall unsafe "lame_set_scale"   c_lame_set_scale :: Lame -> Float -> IO Int --- | Set output sample rate in Hz. 0 (default) means that LAME will pick+-- | Set the output sample rate in Hz. 0 (default) means that LAME will pick -- this value automatically.- setOutputSampleRate :: Lame -> Int -> IO () setOutputSampleRate l x = handleErrors (c_lame_set_out_samplerate l x) @@ -198,21 +203,19 @@ -- General control parameters  -- | Set whether to write Xing VBR header frame.- setWriteVbrTag :: Lame -> Bool -> IO () setWriteVbrTag l x = handleErrors (c_lame_set_bWriteVbrTag l (fromBool x))  foreign import ccall unsafe "lame_set_bWriteVbrTag"   c_lame_set_bWriteVbrTag :: Lame -> Int -> IO Int --- | Select algorithm. This variable will effect quality by selecting--- expensive or cheap algorithms. 0 gives the best quality (very slow). 9 is--- very fast, but gives worst quality.+-- | Select the algorithm. This variable will affect the quality by+-- selecting expensive or cheap algorithms. 0 gives the best quality (very+-- slow). 9 is very fast, but gives the worst quality. -- --     * 2—near-best quality, not too slow. --     * 5—good quality, fast. --     * 7—OK quality, really fast.- setQuality :: Lame -> Int -> IO () setQuality l x = handleErrors (c_lame_set_quality l x) @@ -220,7 +223,6 @@   c_lame_set_quality :: Lame -> Int -> IO Int  -- | Set whether we should use free format.- setFreeFormat :: Lame -> Bool -> IO () setFreeFormat l x = handleErrors (c_lame_set_free_format l (fromBool x)) @@ -228,39 +230,34 @@   c_lame_set_free_format :: Lame -> Int -> IO Int  -- | Set whether we should do ReplayGain analysis.- setFindReplayGain :: Lame -> Bool -> IO () setFindReplayGain l x = handleErrors (c_lame_set_findReplayGain l (fromBool x))  foreign import ccall unsafe "lame_set_findReplayGain"   c_lame_set_findReplayGain :: Lame -> Int -> IO Int --- | Set total number of tracks to encode in “no gap” mode.-+-- | Set the total number of tracks to encode in “no gap” mode. setNoGapTotal :: Lame -> Int -> IO () setNoGapTotal l x = handleErrors (c_lame_set_nogap_total l x)  foreign import ccall unsafe "lame_set_nogap_total"   c_lame_set_nogap_total :: Lame -> Int -> IO Int --- | Set index of current track to encode in “no gap” mode.-+-- | Set the index of the current track to encode in “no gap” mode. setNoGapCurrentIndex :: Lame -> Int -> IO () setNoGapCurrentIndex l x = handleErrors (c_lame_set_nogap_currentindex l x)  foreign import ccall unsafe "lame_set_nogap_currentindex"   c_lame_set_nogap_currentindex :: Lame -> Int -> IO Int --- | Set bitrate.-+-- | Set the bitrate. setBitrate :: Lame -> Int -> IO () setBitrate l x = handleErrors (c_lame_set_brate l x)  foreign import ccall unsafe "lame_set_brate"   c_lame_set_brate :: Lame -> Int -> IO Int --- | Set compression ratio.-+-- | Set the compression ratio. setCompressionRatio :: Lame -> Float -> IO () setCompressionRatio l x = handleErrors (c_lame_set_compression_ratio l x) @@ -271,7 +268,6 @@ -- Frame parameters  -- | Mark as copyright protected.- setCopyright :: Lame -> Bool -> IO () setCopyright l x = handleErrors (c_lame_set_copyright l (fromBool x)) @@ -279,7 +275,6 @@   c_lame_set_copyright :: Lame -> Int -> IO Int  -- | Mark as original.- setOriginal :: Lame -> Bool -> IO () setOriginal l x = handleErrors (c_lame_set_original l (fromBool x)) @@ -287,7 +282,6 @@   c_lame_set_original :: Lame -> Int -> IO Int  -- | Set whether to use 2 bytes from each frame for CRC checksum.- setErrorProtection :: Lame -> Bool -> IO () setErrorProtection l x =   handleErrors (c_lame_set_error_protection l (fromBool x))@@ -296,7 +290,6 @@   c_lame_set_error_protection :: Lame -> Int -> IO Int  -- | Enforce strict ISO compliance.- setStrictISO :: Lame -> Bool -> IO () setStrictISO l x = handleErrors (c_lame_set_strict_ISO l (fromBool x)) @@ -307,44 +300,39 @@ -- VBR control  -- | Set type of VBR.- setVBR :: Lame -> VbrMode -> IO () setVBR l x' = handleErrors (c_lame_set_VBR l x)   where     x = case x' of-          VbrRh   -> 2-          VbrAbr  -> 3-          VbrMtrh -> 4+      VbrRh -> 2+      VbrAbr -> 3+      VbrMtrh -> 4  foreign import ccall unsafe "lame_set_VBR"   c_lame_set_VBR :: Lame -> Int -> IO Int  -- | Set VBR quality level, 0 is highest, 9 is lowest.- setVBRQ :: Lame -> Int -> IO () setVBRQ l x = handleErrors (c_lame_set_VBR_q l x)  foreign import ccall unsafe "lame_set_VBR_q"   c_lame_set_VBR_q :: Lame -> Int -> IO Int --- | Only for VBR ABR: set min bitrate in kbps.-+-- | Only for VBR ABR: set the min bitrate in kbps. setVBRMinBitrate :: Lame -> Int -> IO () setVBRMinBitrate l x = handleErrors (c_lame_set_VBR_min_bitrate_kbps l x)  foreign import ccall unsafe "lame_set_VBR_min_bitrate_kbps"   c_lame_set_VBR_min_bitrate_kbps :: Lame -> Int -> IO Int --- | Only for VBR ABR: set mean bitrate in kbps.-+-- | Only for VBR ABR: set the mean bitrate in kbps. setVBRMeanBitrate :: Lame -> Int -> IO () setVBRMeanBitrate l x = handleErrors (c_lame_set_VBR_mean_bitrate_kbps l x)  foreign import ccall unsafe "lame_set_VBR_mean_bitrate_kbps"   c_lame_set_VBR_mean_bitrate_kbps :: Lame -> Int -> IO Int --- | Only for VBR ABR: set max bitrate in kbps.-+-- | Only for VBR ABR: set the max bitrate in kbps. setVBRMaxBitrate :: Lame -> Int -> IO () setVBRMaxBitrate l x = handleErrors (c_lame_set_VBR_max_bitrate_kbps l x) @@ -353,7 +341,6 @@  -- | Set whether to strictly enforce VBR min bitrate. Normally it will be -- violated for analog silence.- setVBRHardMin :: Lame -> Bool -> IO () setVBRHardMin l x = handleErrors (c_lame_set_VBR_hard_min l (fromBool x)) @@ -363,9 +350,8 @@ ---------------------------------------------------------------------------- -- Filtering control --- | Set frequency to put low-pass filter on. Default is 0 (LAME chooses),--- -1 will disable the filter altogether.-+-- | Set the frequency to put the low-pass filter on. Default is 0 (LAME+-- chooses), -1 will disable the filter altogether. setLowpassFreq :: Lame -> Int -> IO () setLowpassFreq l x = handleErrors (c_lame_set_lowpassfreq l x) @@ -374,16 +360,14 @@  -- | Set width of low-pass transition band. Default is one polyphase filter -- band.- setLowpassWidth :: Lame -> Int -> IO () setLowpassWidth l x = handleErrors (c_lame_set_lowpasswidth l x)  foreign import ccall unsafe "lame_set_lowpasswidth"   c_lame_set_lowpasswidth :: Lame -> Int -> IO Int --- | Set frequency to put high-pass filter on. Default is 0 (LAME chooses),--- -1 will disable the filter altogether.-+-- | Set the frequency to put the high-pass filter on. Default is 0 (LAME+-- chooses), -1 will disable the filter altogether. setHighpassFreq :: Lame -> Int -> IO () setHighpassFreq l x = handleErrors (c_lame_set_highpassfreq l x) @@ -392,7 +376,6 @@  -- | Set width of high-pass transition band. Default is one polyphase filter -- band.- setHighpassWidth :: Lame -> Int -> IO () setHighpassWidth l x = handleErrors (c_lame_set_highpasswidth l x) @@ -402,17 +385,15 @@ ---------------------------------------------------------------------------- -- Tags --- | Initialize something about tag editing library. The docs are silent--- what this does, but I guess we'll take it into the business.-+-- | Initialize something about the tag editing library. The docs are silent+-- what this does. id3TagInit :: Lame -> IO () id3TagInit = c_id3tag_init  foreign import ccall unsafe "id3tag_init"   c_id3tag_init :: Lame -> IO () --- | Force addition of version 2 tag.-+-- | Force addition of a version 2 tag. id3TagAddV2 :: Lame -> IO () id3TagAddV2 = c_id3tag_add_v2 @@ -420,7 +401,6 @@   c_id3tag_add_v2 :: Lame -> IO ()  -- | Add only a version 1 tag.- id3TagV1Only :: Lame -> IO () id3TagV1Only = c_id3tag_v1_only @@ -428,7 +408,6 @@   c_id3tag_v1_only :: Lame -> IO ()  -- | Add only a version 2 tag.- id3TagV2Only :: Lame -> IO () id3TagV2Only = c_id3tag_v2_only @@ -436,31 +415,32 @@   c_id3tag_v2_only :: Lame -> IO ()  -- | Set a textual tag identifying it by its ID.- id3TagSetTextInfo :: Lame -> String -> Text -> IO ()-id3TagSetTextInfo l id' text = handleErrors $-  withCString id' $ \idPtr ->-    TF.useAsPtr text $ \textPtr len ->-      c_id3tag_set_textinfo_utf16 l idPtr textPtr (fromIntegral len)+id3TagSetTextInfo l id' text =+  handleErrors . withCString id' $ \idPtr ->+    let utf16encodedBs = TE.encodeUtf16LE text+        len = fromIntegral (B.length utf16encodedBs `div` 2)+     in B.unsafeUseAsCString utf16encodedBs $ \textPtr ->+          c_id3tag_set_textinfo_utf16 l idPtr (castPtr textPtr) len -foreign import ccall unsafe "id3tag_set_textinfo_utf16_"+foreign import ccall unsafe "id3tag_set_textinfo_utf16_wrapped"   c_id3tag_set_textinfo_utf16 :: Lame -> CString -> Ptr Word16 -> Int -> IO Int  -- | Set the comment tag.- id3TagSetComment :: Lame -> Text -> IO ()-id3TagSetComment l text = handleErrors $-  TF.useAsPtr text $ \textPtr len ->-    c_id3tag_set_comment_utf16 l textPtr (fromIntegral len)+id3TagSetComment l text =+  let utf16encodedBs = TE.encodeUtf16LE text+      len = fromIntegral (B.length utf16encodedBs `div` 2)+   in handleErrors . B.unsafeUseAsCString utf16encodedBs $ \textPtr ->+        c_id3tag_set_comment_utf16 l (castPtr textPtr) len -foreign import ccall unsafe "id3tag_set_comment_utf16_"+foreign import ccall unsafe "id3tag_set_comment_utf16_wrapped"   c_id3tag_set_comment_utf16 :: Lame -> Ptr Word16 -> Int -> IO Int --- | Set album art.-+-- | Set the album art. id3TagSetAlbumArt :: Lame -> ByteString -> IO ()-id3TagSetAlbumArt l img = handleErrors $-  B.unsafeUseAsCStringLen img $ \(dataPtr, dataLen) ->+id3TagSetAlbumArt l img =+  handleErrors . B.unsafeUseAsCStringLen img $ \(dataPtr, dataLen) ->     c_id3tag_set_albumart l dataPtr (fromIntegral dataLen)  foreign import ccall unsafe "id3tag_set_albumart"@@ -469,62 +449,64 @@ ---------------------------------------------------------------------------- -- Encoding --- | Encode given input file.--encodingHelper-  :: Lame              -- ^ The settings-  -> Wave              -- ^ Information about input WAVE file-  -> FilePath          -- ^ Location of input file (normalized)-  -> FilePath          -- ^ Location of output file (normalized)-  -> IO ()-encodingHelper l wave@Wave {..} ipath opath = handleErrors $-  withCString ipath $ \ipathPtr ->+-- | Encode a given input file.+encodingHelper ::+  -- | The settings+  Lame ->+  -- | Information about input WAVE file+  Wave ->+  -- | Location of input file (normalized)+  FilePath ->+  -- | Location of output file (normalized)+  FilePath ->+  IO ()+encodingHelper l wave@Wave {..} ipath opath =+  handleErrors . withCString ipath $ \ipathPtr ->     withCString opath $ \opathPtr ->       c_lame_encoding_helper-        l              -- lame settings structure-        (fromIntegral waveDataOffset) -- offset of data chunk-        waveDataSize   -- size of data chunk-        (case waveSampleFormat of-           SampleFormatPcmInt _       -> 0-           SampleFormatIeeeFloat32Bit -> 1-           SampleFormatIeeeFloat64Bit -> 2)-        (waveBitsPerSample wave)  -- bits per sample-        ipathPtr       -- path to input file-        opathPtr       -- path to output file+        l -- lame settings structure+        (fromIntegral waveDataOffset) -- offset of the data chunk+        waveDataSize -- the size of the data chunk+        ( case waveSampleFormat of+            SampleFormatPcmInt _ -> 0+            SampleFormatIeeeFloat32Bit -> 1+            SampleFormatIeeeFloat64Bit -> 2+        )+        (waveBitsPerSample wave) -- bits per sample+        ipathPtr -- path to input file+        opathPtr -- path to output file  foreign import ccall unsafe "lame_encoding_helper"-  c_lame_encoding_helper-    :: Lame            -- lame settings structure-    -> Word64          -- offset of data chunk-    -> Word64          -- size of data chunk-    -> Word16          -- code of sample format-    -> Word16          -- bits per sample-    -> CString         -- path to input file-    -> CString         -- path to output file-    -> IO Int+  c_lame_encoding_helper ::+    Lame -> -- lame settings structure+    Word64 -> -- offset of data chunk+    Word64 -> -- size of data chunk+    Word16 -> -- code of sample format+    Word16 -> -- bits per sample+    CString -> -- path to input file+    CString -> -- path to output file+    IO Int  ---------------------------------------------------------------------------- -- Helpers  -- | Coerce to 'Ptr' and check if it's a null pointer, return 'Nothing' if--- it is, otherwise return the given pointer unchanged. Needless to say that--- this thing is unsafe.-+-- it is, otherwise return the given pointer unchanged. Needless to say,+-- this is unsafe. maybePtr :: Coercible a (Ptr p) => a -> Maybe a maybePtr a   | coerce a == nullPtr = Nothing-  | otherwise           = Just a+  | otherwise = Just a --- | Treat the 'Int' value as a error code. Unless it's 0, throw+-- | Treat an 'Int' value as an error code. Unless it's 0, throw a -- corresponding 'LameException', otherwise just return the unit.- handleErrors :: IO Int -> IO () handleErrors m = do   n <- m   case n of-    0   -> return ()+    0 -> return ()     -10 -> throwM LameNoMemory     -11 -> throwM LameBadBitrate     -12 -> throwM LameBadSampleFreq     -13 -> throwM LameInternalError-    _   -> throwM LameGenericError+    _ -> throwM LameGenericError
LICENSE.md view
@@ -1,4 +1,4 @@-Copyright © 2017–2018 Mark Karpov+Copyright © 2017–present Mark Karpov  All rights reserved. 
README.md view
@@ -4,39 +4,36 @@ [![Hackage](https://img.shields.io/hackage/v/lame.svg?style=flat)](https://hackage.haskell.org/package/lame) [![Stackage Nightly](http://stackage.org/package/lame/badge/nightly)](http://stackage.org/nightly/package/lame) [![Stackage LTS](http://stackage.org/package/lame/badge/lts)](http://stackage.org/lts/package/lame)-[![Build Status](https://travis-ci.org/mrkkrp/lame.svg?branch=master)](https://travis-ci.org/mrkkrp/lame)+![CI](https://github.com/mrkkrp/lame/workflows/CI/badge.svg?branch=master) -This is a fairly complete high-level Haskell binding to-[LAME](http://lame.sourceforge.net/).+This is a high-level Haskell binding to the+[LAME](http://lame.sourceforge.net/) encoder.  ## Provided functionality -* Fast MP3 encoder working in various modes (you specify compression ratio,-  desired bit-rate, or parameters for VBR), with quite a few interesting-  options to tweak.+* Fast MP3 encoder working in different modes. * Setting of all common tags, including pictures.  ## Limitations -* No decoding for now. It's actually done via a separate library in LAME.+* No decoding. It is done with a separate library in LAME. * Relatively limited (compared   to [ID3 specs](http://id3.org/id3v2.3.0#Text_information_frames)) number   of tag fields available for setting. * Some sample widths are not supported: less than or equal to 8 bit and-  greater than 16 bits (for integer samples, floats work OK).+  greater than 16 bits (for integer samples, floats work fine). * Some psycho-acoustic and noise-shaping settings are not available for   tweaking. -Open an issue if something of this is a deal breaker for you, some of these-limitations are easily lifted.- ## Contribution -Please kindly direct all issues, bugs, and questions to [the GitHub issue-tracker for this project](https://github.com/mrkkrp/lame/issues).+Please direct all issues, bugs, and questions to [the GitHub issue tracker+for this project](https://github.com/mrkkrp/lame/issues). +Pull requests are also welcome.+ ## License -Copyright © 2017–2019 Mark Karpov+Copyright © 2017–present Mark Karpov  Distributed under BSD 3 clause license.
cbits/helpers.c view
@@ -1,6 +1,6 @@ #include "helpers.h" -int id3tag_set_textinfo_utf16_+int id3tag_set_textinfo_utf16_wrapped   ( lame_global_flags *gfp   , const char *id   , const uint16_t *raw_text@@ -17,7 +17,7 @@   return id3tag_set_textinfo_utf16(gfp, id, text); } -int id3tag_set_comment_utf16_+int id3tag_set_comment_utf16_wrapped   ( lame_global_flags *gfp   , const uint16_t *raw_text   , int len )
cbits/helpers.h view
@@ -9,8 +9,8 @@ #include <stdint.h> #include <stdlib.h> -int id3tag_set_textinfo_utf16_(lame_global_flags *, const char *, const uint16_t *, int);-int id3tag_set_comment_utf16_(lame_global_flags *, const uint16_t *, int);+int id3tag_set_textinfo_utf16_wrapped(lame_global_flags *, const char *, const uint16_t *, int);+int id3tag_set_comment_utf16_wrapped(lame_global_flags *, const uint16_t *, int); int lame_encoding_helper(lame_global_flags *, uint64_t, uint64_t, uint16_t, uint16_t, const char *, const char *);  #endif /* LAME_HASKELL_HELPERS_H */
lame.cabal view
@@ -1,73 +1,79 @@-name:                 lame-version:              0.2.0-cabal-version:        1.18-tested-with:          GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.3-license:              BSD3-license-file:         LICENSE.md-author:               Mark Karpov <markkarpov92@gmail.com>-maintainer:           Mark Karpov <markkarpov92@gmail.com>-homepage:             https://github.com/mrkkrp/lame-bug-reports:          https://github.com/mrkkrp/lame/issues-category:             Codec, Audio-synopsis:             Fairly complete high-level binding to LAME encoder-build-type:           Simple-description:          Fairly complete high-level binding to LAME encoder.-extra-source-files:   cbits/*.h-extra-doc-files:      CHANGELOG.md-                    , README.md-data-files:           audio-samples/*.wav+cabal-version:      2.4+name:               lame+version:            0.2.1+license:            BSD-3-Clause+license-file:       LICENSE.md+maintainer:         Mark Karpov <markkarpov92@gmail.com>+author:             Mark Karpov <markkarpov92@gmail.com>+tested-with:        ghc ==9.0.2 ghc ==9.2.4 ghc ==9.4.1+homepage:           https://github.com/mrkkrp/lame+bug-reports:        https://github.com/mrkkrp/lame/issues+synopsis:           A high-level binding to the LAME encoder+description:        A high-level binding to the LAME encoder.+category:           Codec, Audio+build-type:         Simple+data-files:         audio-samples/*.wav+extra-source-files: cbits/*.h+extra-doc-files:+    CHANGELOG.md+    README.md  source-repository head-  type:               git-  location:           https://github.com/mrkkrp/lame.git+    type:     git+    location: https://github.com/mrkkrp/lame.git  flag dev-  description:        Turn on development settings.-  manual:             True-  default:            False+    description: Turn on development settings.+    default:     False+    manual:      True  library-  build-depends:      base             >= 4.8   && < 5.0-                    , bytestring       >= 0.2   && < 0.11-                    , directory        >= 1.2.2 && < 1.4-                    , exceptions       >= 0.6   && < 0.11-                    , filepath         >= 1.2   && < 1.5-                    , text             >= 0.2   && < 1.3-                    , transformers     >= 0.4   && < 0.6-                    , wave             >= 0.1.2 && < 0.3-  extra-libraries:    mp3lame-  exposed-modules:    Codec.Audio.LAME-  other-modules:      Codec.Audio.LAME.Internal-  c-sources:          cbits/helpers.c-  include-dirs:       cbits-  if flag(dev)-    ghc-options:      -Wall -Werror-  else-    ghc-options:      -O2 -Wall-  if flag(dev) && impl(ghc >= 8.0)-    ghc-options:      -Wcompat-                      -Wincomplete-record-updates-                      -Wincomplete-uni-patterns-                      -Wnoncanonical-monad-instances-                      -Wnoncanonical-monadfail-instances-  default-language:   Haskell2010+    exposed-modules:  Codec.Audio.LAME+    c-sources:        cbits/helpers.c+    other-modules:    Codec.Audio.LAME.Internal+    default-language: Haskell2010+    extra-libraries:  mp3lame+    include-dirs:     cbits+    build-depends:+        base >=4.15 && <5.0,+        bytestring >=0.2 && <0.12,+        directory >=1.2.2 && <1.4,+        exceptions >=0.6 && <0.11,+        filepath >=1.2 && <1.5,+        text >=2.0 && <2.1,+        transformers >=0.4 && <0.6,+        wave >=0.1.2 && <0.3 +    if flag(dev)+        ghc-options: -Wall -Werror++    else+        ghc-options: -O2 -Wall++    if flag(dev)+        ghc-options:+            -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns+            -Wnoncanonical-monad-instances+ test-suite tests-  main-is:            Spec.hs-  hs-source-dirs:     tests-  type:               exitcode-stdio-1.0-  build-depends:      base             >= 4.8   && < 5.0-                    , directory        >= 1.2.2 && < 1.4-                    , filepath         >= 1.2   && < 1.5-                    , hspec            >= 2.0   && < 3.0-                    , htaglib          >= 1.0   && < 1.3-                    , lame-                    , temporary        >= 1.1   && < 1.4-                    , text             >= 0.2   && < 1.3-  build-tools:        hspec-discover   >= 2.0   && < 3.0-  other-modules:      Codec.Audio.LAMESpec-  if flag(dev)-    ghc-options:      -Wall -Werror-  else-    ghc-options:      -O2 -Wall-  default-language:   Haskell2010+    type:               exitcode-stdio-1.0+    main-is:            Spec.hs+    build-tool-depends: hspec-discover:hspec-discover >=2.0 && <3.0+    hs-source-dirs:     tests+    other-modules:      Codec.Audio.LAMESpec+    default-language:   Haskell2010+    build-depends:+        base >=4.15 && <5.0,+        directory >=1.2.2 && <1.4,+        filepath >=1.2 && <1.5,+        hspec >=2.0 && <3.0,+        htaglib >=1.0 && <1.3,+        lame,+        temporary >=1.1 && <1.4,+        text >=2.0 && <2.1++    if flag(dev)+        ghc-options: -Wall -Werror++    else+        ghc-options: -O2 -Wall
tests/Codec/Audio/LAMESpec.hs view
@@ -1,69 +1,72 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE RecordWildCards #-} -module Codec.Audio.LAMESpec-  ( spec )-where+module Codec.Audio.LAMESpec (spec) where  import Codec.Audio.LAME import Control.Monad import Data.Text (Text)+import qualified Data.Text as T import Data.Word (Word8) import Sound.HTagLib import System.Directory import System.FilePath import System.IO.Temp (withSystemTempDirectory) import Test.Hspec-import qualified Data.Text as T  data Info = Info-  { infoTitle       :: Title-  , infoArtist      :: Artist-  , infoAlbum       :: Album-  , infoComment     :: Comment-  , infoGenre       :: Genre-  , infoYear        :: Maybe Year-  , infoTrackNumber :: Maybe TrackNumber-  , infoDuration    :: Duration-  , infoBitRate     :: BitRate-  , infoSampleRate  :: SampleRate-  , infoChannels    :: Channels-  } deriving (Eq, Show)+  { infoTitle :: Title,+    infoArtist :: Artist,+    infoAlbum :: Album,+    infoComment :: Comment,+    infoGenre :: Genre,+    infoYear :: Maybe Year,+    infoTrackNumber :: Maybe TrackNumber,+    infoDuration :: Duration,+    infoBitRate :: BitRate,+    infoSampleRate :: SampleRate,+    infoChannels :: Channels+  }+  deriving (Eq, Show)  spec :: Spec spec =   describe "encodeMp3" $     withVariousWaves $       it "produces correct MP3 file" $ \(ipath, opath) -> do-        encodeMp3 defaultEncoderSettings-          { encoderTagTitle   = pure tagTitle-          , encoderTagArtist  = pure tagArtist-          , encoderTagAlbum   = pure tagAlbum-          , encoderTagYear    = pure tagYear-          , encoderTagComment = pure tagComment-          , encoderTagTrack   = pure tagTrack-          , encoderTagGenre   = pure tagGenre }+        encodeMp3+          defaultEncoderSettings+            { encoderTagTitle = pure tagTitle,+              encoderTagArtist = pure tagArtist,+              encoderTagAlbum = pure tagAlbum,+              encoderTagYear = pure tagYear,+              encoderTagComment = pure tagComment,+              encoderTagTrack = pure tagTrack,+              encoderTagGenre = pure tagGenre+            }           ipath           opath-        Info {..} <- getTags' opath MPEG $ Info-          <$> titleGetter-          <*> artistGetter-          <*> albumGetter-          <*> commentGetter-          <*> genreGetter-          <*> yearGetter-          <*> trackNumberGetter-          <*> durationGetter-          <*> bitRateGetter-          <*> sampleRateGetter-          <*> channelsGetter+        Info {..} <-+          getTags' opath MPEG $+            Info+              <$> titleGetter+              <*> artistGetter+              <*> albumGetter+              <*> commentGetter+              <*> genreGetter+              <*> yearGetter+              <*> trackNumberGetter+              <*> durationGetter+              <*> bitRateGetter+              <*> sampleRateGetter+              <*> channelsGetter         unTitle infoTitle `shouldBe` tagTitle         unArtist infoArtist `shouldBe` tagArtist         unAlbum infoAlbum `shouldBe` tagAlbum-        fmap (T.pack . show . unYear) infoYear  `shouldBe` pure tagYear+        fmap (T.pack . show . unYear) infoYear `shouldBe` pure tagYear         unComment infoComment `shouldBe` tagComment-        fmap unTrackNumber infoTrackNumber `shouldBe`-          (pure . fromIntegral . fst) tagTrack+        fmap unTrackNumber infoTrackNumber+          `shouldBe` (pure . fromIntegral . fst) tagTrack         unGenre infoGenre `shouldBe` tagGenre         unDuration infoDuration `shouldBe` 1         unBitRate infoBitRate `shouldBe` 128@@ -74,7 +77,6 @@ -- Helpers  -- | Run given test with various WAVE files.- withVariousWaves :: SpecWith (FilePath, FilePath) -> SpecWith () withVariousWaves m =   forM_ waveFiles $ \(path, desc) ->@@ -83,7 +85,6 @@ -- | Make a temporary copy of given file and provide the path to the file in -- a sandbox directory. Automatically remove the files when the test -- finishes.- withSandbox :: FilePath -> ActionWith (FilePath, FilePath) -> IO () withSandbox path action =   withSystemTempDirectory "lame-test" $ \dir -> do@@ -94,21 +95,24 @@  waveFiles :: [(FilePath, String)] waveFiles =-  [ ( "audio-samples/16bit-int.wav"-    , "2 channels 44100 Hz 16 bit PCM" )-  , ( "audio-samples/32bit-float.wav"-    , "2 channels 44100 Hz 32 bit float PCM" )-  , ( "audio-samples/64bit-float.wav"-    , "2 channels 44100 Hz 64 bit float PCM" )+  [ ( "audio-samples/16bit-int.wav",+      "2 channels 44100 Hz 16 bit PCM"+    ),+    ( "audio-samples/32bit-float.wav",+      "2 channels 44100 Hz 32 bit float PCM"+    ),+    ( "audio-samples/64bit-float.wav",+      "2 channels 44100 Hz 64 bit float PCM"+    )   ]  tagTitle, tagArtist, tagAlbum, tagYear, tagComment, tagGenre :: Text-tagTitle   = "Название"-tagArtist  = "Исполнитель"-tagAlbum   = "Альбом"-tagYear    = "2017"+tagTitle = "Название"+tagArtist = "Исполнитель"+tagAlbum = "Альбом"+tagYear = "2017" tagComment = "Комментарий тут…"-tagGenre   = "Жанр"+tagGenre = "Жанр"  tagTrack :: (Word8, Maybe Word8) tagTrack = (1, Just 10)