Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Doesn't seem to work with current ghc on 64 bit linux #2

Closed
erikd opened this Issue May 7, 2011 · 8 comments

Comments

Projects
None yet
2 participants
Contributor

erikd commented May 7, 2011

From : http://stackoverflow.com/questions/5680075/bad-format-using-hsndfile-libsndfile/

this code should just work bug fails due to misalignemnt of fields in the SF_INFO stuct.

import qualified Sound.File.Sndfile as Snd
import Control.Applicative
import Foreign.Marshal.Array
import Data.Word (Word16)
import System.IO (hGetContents, Handle, openFile, IOMode(..))

format :: Snd.Format
format = Snd.Format Snd.HeaderFormatWav Snd.SampleFormatPcm16 Snd.EndianFile

openWavHandle :: [Word16] -> IO Snd.Handle
openWavHandle frames =
    let info = Snd.Info (length frames) 441000 1 format 1 False
    in Snd.openFile "temp.wav" Snd.WriteMode info

writeWav :: [Word16] -> IO Snd.Count
writeWav frames = openWavHandle frames >>= \h ->
              newArray frames >>= \ptr ->
              Snd.hPutBuf h ptr (length frames) >>= \c ->
              return c

makeWavFile :: IO ()
makeWavFile = writeWav [1..256] >>= \c ->
          putStrLn $ "Frames written: " ++ show c


main :: IO ()
main = makeWavFile

@ghost ghost assigned kaoskorobase May 9, 2011

Owner

kaoskorobase commented May 9, 2011

Hi Erik,

Thanks for the bug report. I've just fixed the alignment of the `Info' type (in commit 7d7f9a5c42b7c628dc4454b3d7c991a1fb5e9d79), could you please check if that fixes the problem? It seems to work fine on MacOS X 10.6 with ghc 7.0.1 (64 bit).

<sk>

Contributor

erikd commented May 10, 2011

Sorry, still no good. I've added debug printf statements to libsndfile and sf_open seems to be called with:

frames     : 1894080577536256
samplerate : 1
channels   : 65538
format     : 0x1

Looking at the generated Interface file:

(\ptr val -> do {pokeByteOff ptr 0 (val::CInt)}) ptr     $ fromIntegral $ frames info
(\ptr val -> do {pokeByteOff ptr 4 (val::CInt)}) ptr $ fromIntegral $ samplerate info
(\ptr val -> do {pokeByteOff ptr 8 (val::CInt)}) ptr   $ fromIntegral $ channels info
(\ptr val -> do {pokeByteOff ptr 12 (val::CInt)}) ptr     $ cFormat      $ format info
(\ptr val -> do {pokeByteOff ptr 16 (val::CInt)}) ptr   $ fromIntegral $ sections info
(\ptr val -> do {pokeByteOff ptr 20 (val::CInt)}) ptr   $ fromBool     $ seekable info

Those offsets are wrong because the frames field is a 64 bit integer. If I manually edit the generated Interface.hs file it starts working. The correct answer is:

 frames     : 256
 samplerate : 441000
 channels   : 1
 format     : 0x10002

Why is this working for you on 64 bit OSX? Can you post the definition of sf_count_t and of SF_INFO from sndfile.h on your system?

Owner

kaoskorobase commented May 10, 2011

On OSX the generated offsets seem to be correct (off_t is a 64 bit integer type):

typedef off_t sf_count_t;

struct SF_INFO
{       sf_count_t      frames ;                
        int                     samplerate ;
        int                     channels ;
        int                     format ;
        int                     sections ;
        int                     seekable ;
} ;
poke ptr info =
    do
        (\ptr val -> do {pokeByteOff ptr 0 (val::CLLong)}) ptr     $ fromIntegral $ frames info
        (\ptr val -> do {pokeByteOff ptr 8 (val::CInt)}) ptr $ fromIntegral $ samplerate info
        (\ptr val -> do {pokeByteOff ptr 12 (val::CInt)}) ptr   $ fromIntegral $ channels info
        (\ptr val -> do {pokeByteOff ptr 16 (val::CInt)}) ptr     $ cFormat      $ format info
        (\ptr val -> do {pokeByteOff ptr 20 (val::CInt)}) ptr   $ fromIntegral $ sections info
        (\ptr val -> do {pokeByteOff ptr 24 (val::CInt)}) ptr   $ fromBool     $ seekable info

This might be a problem with c2hs on 64 bit Linux. Which version of c2hs and ghc are you using?

Contributor

erikd commented May 10, 2011

Using c2hs version 0.16.3 (Crystal Seed, 24 Jan 2009).

On Linux, frames is int64_t which should always be 8 bytes.

Ah, I've figured it out. The header file uses int64_t from <stdint.h> without including <stdint.h>. Is there a way to tell c2hs to include that header before parsing the file?

Otherwise I can probably fix this in the header file next version.

Owner

kaoskorobase commented May 10, 2011

Which header needs to be included? Your comment got garbled by the Markdown parser ...

Contributor

erikd commented May 10, 2011

Sorry, stdint.h needs to be included (silly markdown parser).

Owner

kaoskorobase commented May 11, 2011

Thanks for the fix! AFAIK stdint.h is not available on Windows, so the include should probably be made conditional on the platform (there are GHC macros for this)?

EDIT: stdint.h seems to be available in mingw, which is the C compiler used by ghc on windows, so probably it's ok to just include it unconditionally.

Contributor

erikd commented May 11, 2011

Cool. Time for a new release for hackage i think :-).

@erikd erikd closed this May 11, 2011

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment