我正在尝试读取.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
答案 0 :(得分:4)
您正在使用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
一次,以针对字节字符串运行解析器。