Я пытаюсь использовать hsndfile (привязка Haskell для libsndfile) для создания файла .wav, и я достиг еще одной горки, которую не могу преодолеть. Следующий код вызывает ошибку «Неверный формат». (как написано в openWavHandle). Я пробовал все комбинации порядка байтов с 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
format
. - person Oso   schedule 15.04.2011format
утверждает, что это прямой порядок байтов. - person stephen tetley   schedule 15.04.2011Exception {errorString = "Error : major format is 0."}
Я не уверен на 100%, что с этим делать. - person Oso   schedule 16.04.2011*Main> writeWav [1,2,3,4,3,2,1,0]
. Это вернуло 8, и файл temp.wav был создан правильно. Ошибка, связанная с основным форматом, означает, что, возможно, заголовок волны не сгенерирован должным образом. Создан ли какой-либо вывод? - person John L   schedule 16.04.2011makeWavFile
, который вызываетwriteWav
с данными из файла. Файл содержит последовательностьC 4 D 4 E 4 F 4 G 4 A 4 B 4
. - person Oso   schedule 16.04.2011*Main> writeWav [1,2,3,4,3,2,1,0] *** Exception: Exception {errorString = "Error : major format is 0."}
- person Oso   schedule 16.04.2011