2013-02-02 14 views
11

मैं पोर्टडियो का उपयोग करके कुछ ऑडियो संसाधित कर रहा हूं। जब भी ऑडियो डेटा संसाधित किया जाता है तो हैकेल एफएफआई बाइंडिंग उपयोगकर्ता द्वारा परिभाषित कॉलबैक को कॉल करते हैं। इस कॉलबैक को बहुत जल्दी और आदर्श रूप से नहीं किया जाना चाहिए I/O के साथ। मैं ऑडियो इनपुट को सहेजना चाहता था और जल्दी से वापस लौटना चाहता था क्योंकि मेरे एप्लिकेशन को रीयलटाइम में ऑडियो पर प्रतिक्रिया करने की आवश्यकता नहीं है (अभी मैं केवल ऑडियो डेटा को फाइल में सहेज रहा हूं; बाद में मैं एक साधारण भाषण मान्यता प्रणाली का निर्माण करूंगा) ।हास्केल में पाइप्स और कॉलबैक

मुझे pipes का विचार पसंद है और मैंने सोचा कि मैं उस पुस्तकालय का उपयोग कर सकता हूं। समस्या यह है कि मुझे नहीं पता कि 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" 
+0

क्या आप पाइप के बिना कॉल से डेटा प्राप्त करने का कुछ उदाहरण कोड दे सकते हैं? – Davorak

+1

शायद 'एमवर के अनुक्रमों के बजाय चैनलों का उपयोग करने पर विचार करें। वे इस तरह के निर्माता-उपभोक्ता समस्याओं के लिए बहुत अच्छी तरह से काम करते हैं। – sabauma

उत्तर

13

सरल समाधान कॉलबैक और Producer के बीच संवाद की MVar रों उपयोग करने के लिए है।

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 

आपकी स्ट्रीम को कॉलबैक MVar को Just input लिखेंगे और अपने अंतिम रूप दिए जाने कॉलबैक Nothing लिखने Producer समाप्त करने के लिए होगा: यहाँ कैसे।

>>> 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) 
>>> 

संपादित करें::

यहाँ एक ghci उदाहरण का प्रदर्शन यह कैसे काम करता है The pipes-concurrency library अब इस सुविधा प्रदान करता है, और यह भी एक section in the tutorial विशेष रूप से समझा है कि यह कैसे उपयोग करने के लिए कॉलबैक से बाहर डेटा प्राप्त करने के है।

+5

यदि आप उपभोक्ता को अभी तक नवीनतम मूल्य लेने के लिए नहीं मिला है, तो आप 'PutMVar' पर अवरुद्ध करने के बजाय निष्पादन को अंतःस्थापित करना चाहते हैं, इसके बजाय आप आसानी से' चैन 'का उपयोग कर सकते हैं। लागत यह है कि आप अधिक स्मृति का उपयोग कर सकते हैं। –

+2

यह सही है। यह इस बात पर निर्भर करता है कि क्या आप कॉलबैक को निर्माता के साथ सिंक्रनाइज़ करना चाहते हैं। –

संबंधित मुद्दे