使用hsndfile(libsndfile)的格式错误

|| 我正在尝试使用hsndfile(libsndfile的Haskell绑定)生成.wav文件,但我又遇到了一个我无法逾越的难题。以下代码引发错误“格式错误”(用openWavHandle编写)。我已经尝试过将Endianness与HeaderFormatWav和SampleFormatPcm16的每种组合(我认为确实存在)都无济于事。有谁知道如何解决这一问题?
import qualified Sound.File.Sndfile as Snd
import qualified Graphics.UI.SDL.Mixer.Channels as SDLC
import qualified Graphics.UI.SDL.Mixer.General as SDLG
import qualified Graphics.UI.SDL.Mixer.Samples as SDLS

import Control.Applicative
import Foreign.Marshal.Array
import Data.List.Split (splitOn)
import Data.Word (Word16)
import System.IO (hGetContents, Handle, openFile, IOMode(..))

a4 :: Double
a4 = 440.0

frameRate :: Int
frameRate = 16000

noteLength :: Double
noteLength = 5.0

volume = maxBound `div` 2 :: Word16

noteToFreq :: (String, Int) -> Double
noteToFreq (note, octave) =
    if octave >= -1 && octave < 10 && n /= 12.0
    then a4 * 2 ** ((o - 4.0) + ((n - 9.0) / 12.0))
    else undefined
    where o = fromIntegral octave :: Double
          n = case note of
                \"B#\" -> 0.0
                \"C\"  -> 0.0
                \"C#\" -> 1.0
                \"Db\" -> 1.0
                \"D\"  -> 2.0
                \"D#\" -> 3.0
                \"Eb\" -> 3.0
                \"E\"  -> 4.0
                \"Fb\" -> 4.0
                \"E#\" -> 5.0
                \"F\"  -> 5.0
                \"F#\" -> 6.0
                \"Gb\" -> 6.0
                \"G\"  -> 7.0
                \"G#\" -> 8.0
                \"Ab\" -> 8.0
                \"A\"  -> 9.0
                \"A#\" -> 10.0
                \"Bb\" -> 10.0
                \"B\"  -> 11.0
                \"Cb\" -> 11.0
                _    -> 12.0

notesToFreqs :: [(String, Int)] -> [Double]
notesToFreqs = map noteToFreq 

noteToSample :: Double -> [Word16]
noteToSample freq =
    take (round $ noteLength * fromIntegral frameRate) $
    map ((round . (* fromIntegral volume)) . sin) 
    [0.0, (freq * 2 * pi / fromIntegral frameRate)..]

notesToSamples :: [Double] -> [Word16]
notesToSamples = concatMap noteToSample 

getFileName :: IO FilePath
getFileName = putStr \"Enter the name of the file: \" >> getLine

openMFile :: FilePath -> IO Handle
openMFile fileName = openFile fileName ReadMode

getNotesAndOctaves :: IO String
getNotesAndOctaves = getFileName >>= openMFile >>= hGetContents 

noteValuePairs :: String -> [(String, Int)]
noteValuePairs = pair . splitOn \" \"
    where pair (x:y:ys) = (x, read y) : pair ys
          pair []       = []

getWavSamples :: IO [Word16]
getWavSamples = (notesToSamples . notesToFreqs . noteValuePairs) <$>
                getNotesAndOctaves 

extendNotes :: [Word16] -> [Word16]
extendNotes = concatMap (replicate 1000)

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

openWavHandle :: [Word16] -> IO Snd.Handle
openWavHandle frames =
    let info = Snd.Info (length frames) frameRate 1 format 1 False
    in if Snd.checkFormat info
       then Snd.openFile \"temp.wav\" Snd.WriteMode info
       else error \"Bad format.\"

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 = getWavSamples >>= \\s ->
              writeWav s >>= \\c ->
              putStrLn $ \"Frames written: \" ++ show c
    
已邀请:
安德鲁, 我是libsndfile的主要作者,也是Haskell黑客的一小部分。我已经看过了,据我所知,下面的最小示例代码应该可以工作。
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
它并不表示hsndfile中存在问题。为了证明这一点,我将C源代码砍到libsndfile上,以打印出SF_INFO结构的值(hsndfile称为Info),然后执行以下操作:
samplerate : 1
channels   : 65538
format     : 0x1
这显然是错误的。 我看过hsndfile的Interface.hsc代码。格式字段的值实际上在channels字段中结束,而channels字段在samplerate字段中结束。 我已经弄乱了这段代码,但是却一无所获。我将对上游hsndfile维护程序执行ping操作。     
感谢Erik,该错误已在Hackage的0.5.1版本中修复。 由于Linux中
sndfile.h
中缺少include,Haskell绑定生成器无法弄清楚a4ѭ的样本数应为64位,因此
Info
结构在转换为C表示时会出现乱码。 请将有关此问题的后续信息定向到hsndfile跟踪器。     

要回复问题请先登录注册