gpt4 book ai didi

haskell - Haskell 中的管道和回调

转载 作者:行者123 更新时间:2023-12-02 04:32:50 25 4
gpt4 key购买 nike

我正在使用 portaudio 处理一些音频。每当有音频数据需要处理时,haskell FFI 绑定(bind)都会调用用户定义的回调。这个回调应该处理得非常快,并且最好是没有 I/O。我想保存音频输入并快速返回,因为我的应用程序不需要实时对音频使用react(现在我只是将音频数据保存到文件中;稍后我将构建一个简单的语音识别系统) .

我喜欢管道的想法,并且认为我可以使用该库。问题是我不知道如何创建一个返回通过回调传入的数据的 Producer

我如何处理我的用例?

<小时/>

这就是我现在正在处理的内容,以防有帮助(数据 mvar 现在不起作用,但我不喜欢将所有数据存储在 seq 中......我宁愿将其处理为它来了而不是在最后):

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}

module Main where

import Codec.Wav

import Sound.PortAudio
import Sound.PortAudio.Base
import Sound.PortAudio.Buffer

import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.C.Types
import Foreign.Storable

import qualified Data.StorableVector as SV
import qualified Data.StorableVector.Base as SVB

import Control.Exception.Base (evaluate)

import Data.Int
import Data.Sequence as Seq

import Control.Concurrent

instance Buffer SV.Vector a where
fromForeignPtr fp = return . SVB.fromForeignPtr fp
toForeignPtr = return . (\(a, b, c) -> (a, c)) . SVB.toForeignPtr

-- | Wrap a buffer callback into the generic stream callback type.
buffCBtoRawCB' :: (StreamFormat input, StreamFormat output, Buffer a input, Buffer b output) =>
BuffStreamCallback input output a b -> StreamCallback input output
buffCBtoRawCB' func = \a b c d e -> do
fpA <- newForeignPtr_ d -- We will not free, as callback system will do that for us
fpB <- newForeignPtr_ e -- We will not free, as callback system will do that for us
storeInp <- fromForeignPtr fpA (fromIntegral $ 1 * c)
storeOut <- fromForeignPtr fpB (fromIntegral $ 0 * c)
func a b c storeInp storeOut

callback :: MVar (Seq.Seq [Int32]) -> PaStreamCallbackTimeInfo -> [StreamCallbackFlag] -> CULong
-> SV.Vector Int32 -> SV.Vector Int32 -> IO StreamResult
callback seqmvar = \timeinfo flags numsamples input output -> do
putStrLn $ "timeinfo: " ++ show timeinfo ++ "; flags are " ++ show flags ++ " in callback with " ++ show numsamples ++ " samples."
print input
-- write data to output
--mapM_ (uncurry $ pokeElemOff output) $ zip (map fromIntegral [0..(numsamples-1)]) datum
--print "wrote data"

input' <- evaluate $ SV.unpack input
modifyMVar_ seqmvar (\s -> return $ s Seq.|> input')

case flags of
[] -> return $ if unPaTime (outputBufferDacTime timeinfo) > 0.2 then Complete else Continue
_ -> return Complete

done doneMVar = do
putStrLn "total done dood!"
putMVar doneMVar True
return ()

main = do

let samplerate = 16000

Nothing <- initialize

print "initialized"

m <- newEmptyMVar
datum <- newMVar Seq.empty

Right s <- openDefaultStream 1 0 samplerate Nothing (Just $ buffCBtoRawCB' (callback datum)) (Just $ done m)
startStream s

_ <- takeMVar m -- wait until our callbacks decide they are done!
Nothing <- terminate

print "let's see what we've recorded..."

stuff <- takeMVar datum
print stuff

-- write out wav file

-- let datum =
-- audio = Audio { sampleRate = samplerate
-- , channelNumber = 1
-- , sampleData = datum
-- }
-- exportFile "foo.wav" audio

print "main done"

最佳答案

最简单的解决方案是使用MVar s 在回调和 Producer 之间进行通信。方法如下:

import Control.Proxy
import Control.Concurrent.MVar

fromMVar :: (Proxy p) => MVar (Maybe a) -> () -> Producer p a IO ()
fromMVar mvar () = runIdentityP loop where
loop = do
ma <- lift $ takeMVar mvar
case ma of
Nothing -> return ()
Just a -> do
respond a
loop

您的流回调将写入 Just inputMVar并且您的最终回调将写入 Nothing终止 Producer .

这是一个ghci演示其工作原理的示例:

>>> mvar <- newEmptyMVar :: IO (MVar (Maybe Int))
>>> forkIO $ runProxy $ fromMVar mvar >-> printD
>>> putMVar mvar (Just 1)
1
>>> putMVar mvar (Just 2)
2
>>> putMVar mvar Nothing
>>> putMVar mvar (Just 3)
>>>

编辑:The pipes-concurrency library现在提供了这个功能,甚至还有一个section in the tutorial具体解释如何使用它从回调中获取数据。

关于haskell - Haskell 中的管道和回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14658024/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com