<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=utf-8">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    I'm very new to Haskell and am trying to write a "real" program to
    motivate myself to learn it better (so far I've only gotten through
    Project Euler problems after reading LYAH and most of RWH).  I'm
    using Taglib (<a class="moz-txt-link-freetext" href="https://github.com/taglib/taglib">https://github.com/taglib/taglib</a>) to read the metadata
    from a music file and print it.  I have a struct C-side (with C
    linkage) serving as the bridge between Taglib's C++ and Haskell's
    FFI.  A small demo program (compiled with gcc and linked against the
    C++ object files) gives the correct results, but Haskell is weirdly
    only getting <i>some </i>of it right.  Specifically, the C string
    fields are working but ints are not.<br>
    <br>
    The output from the C demo (what Haskell should be printing):<br>
    <br>
    music_metadata<br>
    title: It's My Life,    artist: Bon Jovi,    album: Bon Jovi
    Greatest Hits - The Ultimate Collection<br>
    comment: ,    genre: Rock,    track: 3,<br>
    length: 224,    bitrate: 256,    channels: 2,<br>
    codec: 768<br>
    <br>
    The output from Haskell:<br>
    <br>
    MusicMetadata {codec = UNKNOWN, length = 1099511628000, bitrate =
    8589934848, channels = 12884901890, track = 8589934848, title =
    "It's My Life", artist = "Bon Jovi", album = "Bon Jovi Greatest Hits
    - The Ultimate Collection", comment = "", genre = "Rock"}<br>
    <br>
    I would have expected it to work or not work at all, but did not
    anticipate getting only some of it right.<br>
    <br>
    I was going to include snippets from my hsc file but given how new I
    am to Haskell I don't trust myself to assume where the problem is,
    so sorry if this is way too long:<br>
    <br>
    {-# LANGUAGE CPP, ForeignFunctionInterface #-}<br>
    <br>
    module MusicReader<br>
    ( Codec,<br>
      MusicMetadata,<br>
      readMusicMetadata<br>
    ) where<br>
    <br>
    import Control.Monad<br>
    import Foreign<br>
    import Foreign.C.Types<br>
    import Foreign.C.String<br>
    import System.IO.Unsafe as Unsafe<br>
    <br>
    #include "CodecDefines.h"<br>
    #include "MusicReader.h"<br>
    <br>
    constantToCodec code<br>
                        | code == mp3 = MP3<br>
                        | code == flac = FLAC<br>
                        | code == ogg = OGG_VORBIS<br>
                        | code == mp4 = MP4<br>
                        | code == mpeg = MPEG<br>
                        | code == none = NONE<br>
                        | code == unknown = UNKNOWN<br>
                        | otherwise = UNKNOWN<br>
                           where mp3 = #const MP3_CODEC<br>
                                 flac = #const FLAC_CODEC<br>
                                 ogg = #const OGG_VORBIS_CODEC <br>
                                 mp4 = #const MP4_CODEC<br>
                                 mpeg = #const MPEG_CODEC<br>
                                 none = #const NO_EXTENSION<br>
                                 unknown = #const UNKNOWN_EXTENSION<br>
    <br>
    data Codec = MP3 | FLAC | OGG_VORBIS | MP4 | MPEG | NONE | UNKNOWN
    deriving (Show)<br>
    <br>
    data MusicMetadata = MusicMetadata { codec :: Codec,<br>
                          length :: Int,<br>
                          bitrate :: Int,<br>
                          channels :: Int,<br>
                          track :: Int,<br>
                          title :: String,<br>
                          artist :: String,<br>
                          album :: String,<br>
                          comment :: String,<br>
                          genre :: String } deriving (Show)<br>
    <br>
    instance Storable MusicMetadata where<br>
        sizeOf _ = (#size struct music_metadata)<br>
        alignment _ = alignment (undefined::CDouble)<br>
        peek a = do<br>
            codec <- liftM constantToCodec $ (((#peek struct
    music_metadata, codec) a) :: IO Int)<br>
            length <- ((#peek struct music_metadata, length) a) :: IO
    Int<br>
            bitrate <- ((#peek struct music_metadata, bitrate) a) ::
    IO Int<br>
            channels <- ((#peek struct music_metadata, channels) a)
    :: IO Int<br>
            track <- ((#peek struct music_metadata, bitrate) a) :: IO
    Int<br>
            title <-  ((#peek struct music_metadata, title) a) :: IO
    CString<br>
            artist <- ((#peek struct music_metadata, artist) a) :: IO
    CString<br>
            album <- ((#peek struct music_metadata, album) a) :: IO
    CString<br>
            comment <- ((#peek struct music_metadata, comment) a) ::
    IO CString<br>
            genre <- ((#peek struct music_metadata, genre) a) :: IO
    CString<br>
            --FIXME: find replacement for temporary names<br>
            marshalledTitle <- peekCString title<br>
            marshalledArtist <- peekCString artist<br>
            marshalledAlbum <- peekCString album<br>
            marshalledComment <- peekCString comment<br>
            marshalledGenre <- peekCString genre<br>
            return (MusicMetadata codec length bitrate channels track
    marshalledTitle marshalledArtist marshalledAlbum marshalledComment
    marshalledGenre)<br>
        poke a = undefined<br>
    <br>
    --This is the "primitive" FFI call--calls the C function and gets a
    pointer<br>
    --in return<br>
    --TODO: write a higher level function this module should export that
    calls<br>
    --primReadMusicMetadata and converts the C Pointer into the Haskell
    data<br>
    --MusicMetadata<br>
    foreign import ccall unsafe "read_metadata" primReadMusicMetadata ::
    CString -> IO (Ptr MusicMetadata)<br>
    <br>
    --convert the Haskell string to a CString, call into the FFI then<br>
    --dereference the resulting pointer<br>
    readMusicMetadata a = join $ withCString a $ \cs -> ((liftM peek)
    $ primReadMusicMetadata cs)<br>
    <br>
    <br>
    <br>
    Here's the struct in MusicReader.h (in an extern C block):<br>
        struct music_metadata<br>
        {<br>
            int codec;<br>
            int length,<br>
                    bitrate,<br>
                    channels;<br>
            int track;<br>
            char *title,<br>
                 *artist,<br>
                 *album,<br>
                 *comment,<br>
                 *genre;<br>
        };<br>
    <br>
    <br>
    with the corresponding call:<br>
        struct music_metadata* read_metadata(char*);<br>
    <br>
    <br>
    I've tried playing around with the alignment but it didn't do
    anything.  I also tried declaring the struct's fields as int32_t
    which also did nothing.<br>
    <br>
    The C demo in question is a very simple:<br>
    <br>
    #include <stdio.h><br>
    #include "MusicReader.h"<br>
    <br>
    #define FILENAME "Its_My_Life.m4a"<br>
    <br>
    int main()<br>
    {<br>
        struct music_metadata* metadata = read_metadata(FILENAME);   <br>
        printf("music_metadata\ntitle: %s,\tartist: %s,\talbum: %s\n",<br>
                metadata->title, metadata->artist,
    metadata->album);<br>
        printf("comment: %s,\tgenre: %s,\ttrack: %d,\n", <br>
                metadata->comment, metadata->genre,
    metadata->track);<br>
        printf("length: %d,\tbitrate: %d,\tchannels: %d,\n",<br>
                metadata->length, metadata->bitrate,
    metadata->channels);<br>
        printf("codec: %d\n");<br>
    <br>
    }<br>
    <br>
    It just reads the metadata into the struct and prints the fields.<br>
    <br>
    I've gotten the impression of the Haskell FFI being very
    beginner-unfriendly, which isn't surprising but still disappointing
    because it would be a great opportunity to replace some projects
    with Haskell.<br>
    <br>
    Any help is appreciated, including general feedback on my code!<br>
  </body>
</html>