<html>
<head>
<meta content="text/html; charset=windows-1252"
http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
Thanks very much! This helped a lot (and you were right on #2... I
really should have caught that).<br>
<br>
<div class="moz-cite-prefix">On 3/2/15 10:20 AM, Sylvain Henry
wrote:<br>
</div>
<blockquote
cite="mid:CAPmptcWKW0-nyttQnu_-n-WsWdmndE_Z1RcBKwn=NvhCwaVpuw@mail.gmail.com"
type="cite">
<div dir="ltr">Hi,<br>
<br>
1) You have a copy-paste error on the following line
(s/bitrate/track)<br>
track <- ((#peek struct music_metadata, bitrate) a) :: IO
Int<br>
<br>
2) By looking at the values you get in hexadecimal, you are
reading 64bits values when your C struct contains 32 bits
values. If you use: peek ... :: IO Int32, it should be ok.<br>
<br>
3) From a style point of view, you could use Applicative
operators (from Control.Applicative) to avoid temporary names:<br>
peek a = MusicMetaData<br>
<$> liftM constantToCodec $ (((#peek struct
music_metadata, codec) a) :: IO Int32)<br>
<*> fmap fromIntegral (((#peek struct
music_metadata, length) a) :: IO Int32)<br>
<*> fmap fromIntegral (((#peek struct
music_metadata, bitrate) a) :: IO Int32)<br>
<*> fmap fromIntegral (((#peek struct
music_metadata, channels) a) :: IO Int32)<br>
<*> fmap fromIntegral (((#peek struct
music_metadata, track) a) :: IO Int32)<br>
<*> (peekCString =<< (#peek struct
music_metadata, title) a)<br>
...<br>
<br>
4) In your constantToCodec function, you don't need the
temporary names for all the constants.<br>
<br>
Best regards,<br>
Sylvain<br>
<br>
PS : recently I have been working on rewriting the FFI page on
the wiki to make it more beginner-friendly ;-). It is not
totally finished but I'm open to any comment. <a
moz-do-not-send="true"
href="https://www.haskell.org/wiki/Foreign_Function_Interface"
target="_blank">https://www.haskell.org/wiki/Foreign_Function_Interface</a><br>
</div>
<div class="gmail_extra"><br>
<div class="gmail_quote">2015-02-28 14:20 GMT+01:00 Thomas
Jakway <span dir="ltr"><<a moz-do-not-send="true"
href="mailto:tjakway@nyu.edu" target="_blank">tjakway@nyu.edu</a>></span>:<br>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex">
<div 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
moz-do-not-send="true"
href="https://github.com/taglib/taglib" target="_blank">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 = <a moz-do-not-send="true" href="tel:8589934848"
value="+18589934848" target="_blank">8589934848</a>,
channels = 12884901890, track = <a moz-do-not-send="true"
href="tel:8589934848" value="+18589934848"
target="_blank">8589934848</a>, 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>
</div>
<br>
_______________________________________________<br>
Beginners mailing list<br>
<a moz-do-not-send="true"
href="mailto:Beginners@haskell.org">Beginners@haskell.org</a><br>
<a moz-do-not-send="true"
href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners"
target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners</a><br>
<br>
</blockquote>
</div>
<br>
</div>
<br>
<fieldset class="mimeAttachmentHeader"></fieldset>
<br>
<pre wrap="">_______________________________________________
Beginners mailing list
<a class="moz-txt-link-abbreviated" href="mailto:Beginners@haskell.org">Beginners@haskell.org</a>
<a class="moz-txt-link-freetext" href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners">http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners</a>
</pre>
</blockquote>
<br>
</body>
</html>