diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/Codec/Audio/LAME.hs b/Codec/Audio/LAME.hs
--- a/Codec/Audio/LAME.hs
+++ b/Codec/Audio/LAME.hs
@@ -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
diff --git a/Codec/Audio/LAME/Internal.hs b/Codec/Audio/LAME/Internal.hs
--- a/Codec/Audio/LAME/Internal.hs
+++ b/Codec/Audio/LAME/Internal.hs
@@ -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
diff --git a/LICENSE.md b/LICENSE.md
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,4 +1,4 @@
-Copyright © 2017–2018 Mark Karpov
+Copyright © 2017–present Mark Karpov
 
 All rights reserved.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/cbits/helpers.c b/cbits/helpers.c
--- a/cbits/helpers.c
+++ b/cbits/helpers.c
@@ -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 )
diff --git a/cbits/helpers.h b/cbits/helpers.h
--- a/cbits/helpers.h
+++ b/cbits/helpers.h
@@ -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 */
diff --git a/lame.cabal b/lame.cabal
--- a/lame.cabal
+++ b/lame.cabal
@@ -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
diff --git a/tests/Codec/Audio/LAMESpec.hs b/tests/Codec/Audio/LAMESpec.hs
--- a/tests/Codec/Audio/LAMESpec.hs
+++ b/tests/Codec/Audio/LAMESpec.hs
@@ -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)
