packages feed

hommage-ds-0.0.5: Sound/Hommage/DSPlayer/SimplePlayer.hs

{- 2009 Daniel van den Eijkel <dvde@gmx.net> -}

{-# OPTIONS_GHC -fglasgow-exts #-}
module Sound.Hommage.DSPlayer.SimplePlayer
 ( SimplePlayer (..)
 , mkSimplePlayer
 )
 where

import Sound.Hommage.DSPlayer.DSPlayer
import Sound.Hommage.DSPlayer.VoicePlayer
import Sound.Hommage.Misc (for')

import Control.Monad
import Data.Array.Storable as STA
import Data.IORef
import Foreign
import System.IO

---------------------------------------------------------------------------------------------------
data SimplePlayer = SimplePlayer 
 { dsplayerSP :: DSPlayer                                       -- ^ DSPlayer
 , playSP :: (Ptr Double -> Ptr Double -> IO Voice) -> IO ()    -- ^ creates and plays a voice
 , killSP :: IO ()                                              -- ^ removes all active voices
 , fptSP :: Int -> IO ()                                        -- ^ set the number of frames per tick
 , setOnTickSP :: IO () -> IO ()                                -- ^ set the action which is performed on each tick
 }
---------------------------------------------------------------------------------------------------
mkSimplePlayer :: Int -> IO SimplePlayer
mkSimplePlayer latency = do
 voiceplayer       <- mkVoicePlayer
 framecount_ref    <- newIORef 0
 ontick_ref        <- newIORef (return ())
 framespertick_ref <- newIORef 800
 (leftOutputBuffer  :: STA.StorableArray Int Double) <- STA.newArray (0, 511) 0.0
 (rightOutputBuffer :: STA.StorableArray Int Double) <- STA.newArray (0, 511) 0.0
 let --mkVoice :: (Ptr Double -> Ptr Double -> IO Voice) -> IO Voice
     mkVoice f = withStorableArray leftOutputBuffer $ \ptrL ->
                 withStorableArray rightOutputBuffer $ \ptrR -> f ptrL ptrR 
           
     --addVoice :: Voice -> IO ()
     addVoice v = startVoice voiceplayer $! v     

     --stopAll :: IO ()
     stopAll = clearVoicePlayer voiceplayer          

     --setFPT :: Int -> IO ()
     setFPT fpt = writeIORef framespertick_ref $! max 512 fpt           

     --setOnTick :: IO () -> IO ()
     setOnTick f = writeIORef ontick_ref f
           
     fillOutputBuffers =
         STA.withStorableArray leftOutputBuffer $ \ptrL -> 
         STA.withStorableArray rightOutputBuffer $ \ptrR -> do
             framecount <- readIORef framecount_ref
             framecount' <- for' 0 (<512) (+1) framecount $ \i framecount -> do
                              pokeElemOff ptrL i 0
                              pokeElemOff ptrR i 0
                              when (framecount == 0) (join $ readIORef ontick_ref) --tickcount -- --$ getCurrentPE pateng tickcount >>= sequence_    
                              stepVoicePlayer voiceplayer i
                              fpt <- readIORef framespertick_ref
                              let framecount' = framecount + 1
                              if framecount' >= fpt
                                then return 0
                                else return framecount'
             writeIORef framecount_ref framecount'     
                    
     lat = max 1 latency
     sampleRate = 44100
     fillBuffer outputBuffer _ = 
              STA.withStorableArray leftOutputBuffer $ \ptrL -> 
              STA.withStorableArray rightOutputBuffer $ \ptrR -> do
                forM_ [0..lat-1] $ \i -> do 
                --for 0 (<lat) (+1) $ \i -> do 
                    fillOutputBuffers
                    copyStereoOutputBuf512 ptrL ptrR $! (outputBuffer `advancePtr` (1024 * i))       
                return ()      

 return ( SimplePlayer 
              (DSPlayer { sampleRateDS = sampleRate
                       , bufferSizeDS = lat * 512
                       , isStereoDS = True
                       , fillBufferCallbackDS = fillBuffer
                       })
              (\v -> mkVoice v >>= addVoice)
              stopAll
              setFPT
              setOnTick )
 
copyStereoOutputBuf512 :: Ptr Double -> Ptr Double -> Ptr Int16 -> IO ()
copyStereoOutputBuf512 ptrL ptrR ptrO = do
  forM_ [0..511] $ \i -> do
  --for 0 (<512) (+1) $ \i -> do 
    vl <- peekElemOff ptrL i 
    vr <- peekElemOff ptrR i 
    let k = fromIntegral (i+i) :: Int
        al = round (vl*10000) :: Int16
        ar = round (vr*10000) :: Int16 
    pokeElemOff ptrO (k  ) al  -- left channel
    pokeElemOff ptrO (k+1) ar  -- right channel