我试图读取.wav文件,也许最终会处理数据,但是我被卡住了。只是读入文件,将其存储在结构中,然后将其写入另一个文件需要很长时间。任何添加的处理将花费更长的时间。

我已经发布了我的代码,这很简单。我必须丢失一些东西并使程序变得比必要的更加复杂或多余。

import qualified Data.Char as DC
import qualified Data.Word as DW
import qualified Data.Int as DI

import qualified Data.Binary.Get as BG
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Internal as BLI

import qualified System.Environment as SE
import qualified System.IO as SIO

main = do
    (fstfilename:sndfilename:_) <- SE.getArgs
    fstfile <- SIO.openFile fstfilename SIO.ReadMode
    input <- BL.hGetContents fstfile

    raw_wav <- return $ BG.runGet parseWav input

    sndfile <- SIO.openFile sndfilename SIO.WriteMode
    SIO.hPutStr sndfile (show (wavData raw_wav))

data Sample = OneChannel {mono :: Integer} |
              TwoChannel {leftChannel :: Integer,
                         rightChannel :: Integer}

instance Show Sample where
    show (OneChannel m) = show m ++ " "
    show (TwoChannel l r) = show l ++ "-" ++ show r ++ " "

data RaWavFile = RaWavFile {numChannels :: Integer,
                        sampleRate :: Integer,
                        bitsPerSample :: Integer,
                        wavData :: [Sample]}
                        deriving (Show)

parseWav :: BG.Get RaWavFile
parseWav = do
        BG.skip 22
        num_channels <- BG.getWord16le
        sample_rate <- BG.getWord32le
        BG.skip 6
        bits_per_sample <- BG.getWord16le

        rem <- BG.getRemainingLazyByteString
        wav_data <- return $ BL.drop 8 (BL.dropWhile
                            ((/=) (fromIntegral (DC.ord 'd') :: DW.Word8)) rem)

        nc <- return $ toInteger num_channels
        sr <- return $ toInteger sample_rate
        bps <- return $ toInteger bits_per_sample
        return $ RaWavFile nc sr bps (orgSamples nc bps wav_data)

--          numChannels bitpersample   wavData
orgSamples :: Integer -> Integer -> BL.ByteString -> [Sample]
orgSamples nc bps BLI.Empty = []
orgSamples nc bps bs
        | nc == 1 = (OneChannel (rle fb)):(orgSamples nc bps rst)
        | nc == 2 = (TwoChannel (rle fb) (rle sb)):(orgSamples nc bps rsst)
        | otherwise = error "Number of channels not 1 or 2"
            where nb = fromIntegral (bps `div` 8) :: DI.Int64
                  (fb, rst) = BL.splitAt nb bs
                  (sb, rsst) = BL.splitAt nb rst
                  rle = toInteger . BG.runGet BG.getWord16le

最佳答案

为什么慢。

  • 您正在使用Integer来存储单个样本。 Integer是一种特殊类型,用于存储任意精度的整数。这样,这些值的每次读/写都会产生大量开销。不惜一切代价避免。我建议使用特定大小的类型,例如Int8 / Int16。您可能还应该对这些类型进行参数化。
  • 您将样本存储为通道类型的标记联合。对于每个样品。那是很多开销。您是否真的希望更改中间文件的通道数量?可能不会。
  • 您正在使用一个列表来存储样本,这在本质上是在谈论连续的字节流时会带来很多开销。

  • 如何使其快速
  • 在样本的位深度上参数化您的类型。我建议直接使用Int8 / Int16,因为8位和16位轨道是最常用的两种格式。您可能想坚持学习他们的项目。
    import Data.Int
    
  • 使用Unboxed Vectors存储您的数据。这避免了(懒惰的)列表和thunk的大量开销,并且将大大减少启动时的内存消耗。
    import Data.Vector.Unboxed as V
    
  • 不存储曲目数。 length $ tracks $ wavFile会在您需要时随时取回。消除代码中所有对Integer的使用(除非您确实确实需要存储大于2 ^ 64的数字)
    data RaWavFile b = RaWavFile {
        sampleRate :: Int,
        tracks     :: [Vector b] }
        deriving (Show)
    
  • 使用类型来指导您。 binary在返回类型上是多态的。只需询问您想要的类型,它就会解析正确的字节数,而无需您干预。
    parseWav :: BL.ByteString -> BG.Get (RaWavFile b)
    wav <- parseWav input :: BG.Get (RaWavFile Int16)
    
  • 您应该只使用BG.runGet一次,以对一个字节串运行解析器。
  • 关于haskell - 加快阅读.wav并在Haskell中进行分析?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24319431/

    10-12 00:20
    查看更多