{-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE EmptyDataDecls #-} module Audio.TagLib ( module Audio.TagLib , TagLib (..) , FileId () , io ) where import Control.Applicative import Foreign.C.String (CString,withCString,peekCString) import Foreign.C.Types (CInt(..),CChar(..)) import Foreign.Ptr (Ptr,nullPtr) import qualified Control.Exception as E import qualified Data.ByteString as BS import qualified Data.Text.Encoding as T import qualified Data.Text as T import Audio.TagLib.Internal -- Main Interface {{{ -- | Run a @TagLib@ block. Save and free any files -- left open when the block is finished, and free -- all strings produced by taglib. taglib :: TagLib a -> IO a taglib m = do (res,fs) <- evalTagLib initialEnv m' mapM_ cleanupFile fs c_taglib_free_strings return res where m' = do res <- m fs <- openFilePtrs return (res,fs) -- | Open a file and return a corresponding @FileId@. -- Internally, this grabs the Tag and AudioProperties -- pointers to the TagLib_File. openFile :: FilePath -> TagLib FileId openFile fp = do f <- io $ withCString fp $ \c_path -> do c_file <- c_taglib_file_new c_path if (c_file == nullPtr) then E.throw (UnableToOpen fp) else do res <- c_taglib_file_is_valid c_file if (res == 0) then E.throw (InvalidFile fp) else TagLibFile c_file <$> c_taglib_file_tag c_file <*> c_taglib_file_audioproperties c_file fid <- nextId addNewFile fid f return fid -- }}} -- FFI Wrappers {{{ -- | Given a @IO@ action which expects a @Tag@ pointer and @CString@, -- lifts it into an @TagLib@ action, expecting @Text@. packStringTag :: SetStringTag -> FileId -> T.Text -> TagLib () packStringTag k fid txt = do c_tag <- fromFile tagPtr fid io $ BS.useAsCString bs $ k c_tag where bs :: BS.ByteString bs = T.encodeUtf8 txt -- | Given a @IO@ action which expects a @Tag@ pointer and @CInt@, -- lifts it into an @TagLib@ action, expecting a @Int@. packIntTag :: SetIntTag -> FileId -> Int -> TagLib () packIntTag k fid int = do c_tag <- fromFile tagPtr fid io $ k c_tag $ toEnum int -- | Given a @IO@ action which expects a @Tag@ pointer and -- results in a @CString@, lifts it into a @TagLib@ action, -- resulting in @Text@. unpackStringTag :: GetStringTag -> FileId -> TagLib T.Text unpackStringTag k fid = do c_tag <- fromFile tagPtr fid io $ do c_str <- k c_tag T.pack <$> peekCString c_str -- | Given a @IO@ action which expects a @Tag@ pointer and -- results in a @CInt@, lifts it into a @TagLib@ action, -- resulting in @Int@. unpackIntTag :: GetIntTag -> FileId -> TagLib Int unpackIntTag k fid = do c_tag <- fromFile tagPtr fid io $ fromIntegral <$> k c_tag -- | Given a @IO@ action which expects a @AudioProperties@ pointer and -- results in a @CInt@, lifts it into a @TagLib@ action, -- resulting in @Int@. unpackIntAP :: GetIntAP -> FileId -> TagLib Int unpackIntAP k fid = do c_ap <- fromFile audioPropPtr fid io $ fromIntegral <$> k c_ap -- }}} -- FFI Types {{{ -- | FFI Type Synonyms type SetStringTag = Ptr Tag -> CString -> IO () type SetIntTag = Ptr Tag -> CInt -> IO () type GetStringTag = Ptr Tag -> IO (Ptr CChar) type GetIntTag = Ptr Tag -> IO CInt type GetIntAP = Ptr AudioProperties -> IO CInt -- }}} -- Tag Setters {{{ -- | Set the track title. setTitle :: FileId -> T.Text -> TagLib () setTitle = packStringTag c_taglib_tag_set_title -- | Set the artist name. setArtist :: FileId -> T.Text -> TagLib () setArtist = packStringTag c_taglib_tag_set_artist -- | Set the album name. setAlbum :: FileId -> T.Text -> TagLib () setAlbum = packStringTag c_taglib_tag_set_album -- | Set the comment field. setComment :: FileId -> T.Text -> TagLib () setComment = packStringTag c_taglib_tag_set_comment -- | Set the genre field. setGenre :: FileId -> T.Text -> TagLib () setGenre = packStringTag c_taglib_tag_set_genre -- | Set the release year. setYear :: FileId -> Int -> TagLib () setYear = packIntTag c_taglib_tag_set_year -- | Set the track number. setTrack :: FileId -> Int -> TagLib () setTrack = packIntTag c_taglib_tag_set_track foreign import ccall "taglib_tag_set_title" c_taglib_tag_set_title :: SetStringTag foreign import ccall "taglib_tag_set_artist" c_taglib_tag_set_artist :: SetStringTag foreign import ccall "taglib_tag_set_album" c_taglib_tag_set_album :: SetStringTag foreign import ccall "taglib_tag_set_comment" c_taglib_tag_set_comment :: SetStringTag foreign import ccall "taglib_tag_set_genre" c_taglib_tag_set_genre :: SetStringTag foreign import ccall "taglib_tag_set_year" c_taglib_tag_set_year :: SetIntTag foreign import ccall "taglib_tag_set_track" c_taglib_tag_set_track :: SetIntTag -- }}} -- Tag Getters {{{ -- | Get the track title. getTitle :: FileId -> TagLib T.Text getTitle = unpackStringTag c_taglib_tag_title -- | Get the artist name. getArtist :: FileId -> TagLib T.Text getArtist = unpackStringTag c_taglib_tag_artist -- | Get the album name. getAlbum :: FileId -> TagLib T.Text getAlbum = unpackStringTag c_taglib_tag_album -- | Get the contents of the comment field. getComment :: FileId -> TagLib T.Text getComment = unpackStringTag c_taglib_tag_comment -- | Get the contents of the genre field. getGenre :: FileId -> TagLib T.Text getGenre = unpackStringTag c_taglib_tag_genre -- | Get the release year. getYear :: FileId -> TagLib Int getYear = unpackIntTag c_taglib_tag_year -- | Get the track number. getTrack :: FileId -> TagLib Int getTrack = unpackIntTag c_taglib_tag_track foreign import ccall "taglib_tag_title" c_taglib_tag_title :: GetStringTag foreign import ccall "taglib_tag_artist" c_taglib_tag_artist :: GetStringTag foreign import ccall "taglib_tag_album" c_taglib_tag_album :: GetStringTag foreign import ccall "taglib_tag_comment" c_taglib_tag_comment :: GetStringTag foreign import ccall "taglib_tag_genre" c_taglib_tag_genre :: GetStringTag foreign import ccall "taglib_tag_year" c_taglib_tag_year :: GetIntTag foreign import ccall "taglib_tag_track" c_taglib_tag_track :: GetIntTag -- }}} -- AudioProperties Getters {{{ -- | Retrieves the duration of the given file, in seconds. getLength :: FileId -> TagLib Int getLength = unpackIntAP c_taglib_audioproperties_length -- | Retrieves the bitrate of the given file, in kb/s. getBitrate :: FileId -> TagLib Int getBitrate = unpackIntAP c_taglib_audioproperties_bitrate -- | Retrieves the sample rate of the given file, in Hz. getSampleRate :: FileId -> TagLib Int getSampleRate = unpackIntAP c_taglib_audioproperties_samplerate -- | Retrieves the number of channels in the given file. getChannels :: FileId -> TagLib Int getChannels = unpackIntAP c_taglib_audioproperties_channels foreign import ccall "taglib_audioproperties_length" c_taglib_audioproperties_length :: GetIntAP foreign import ccall "taglib_audioproperties_bitrate" c_taglib_audioproperties_bitrate :: GetIntAP foreign import ccall "taglib_audioproperties_samplerate" c_taglib_audioproperties_samplerate :: GetIntAP foreign import ccall "taglib_audioproperties_channels" c_taglib_audioproperties_channels :: GetIntAP -- }}}