module Freenect
(
initialize
,newContext
,shutdown
,countDevices
,withContext
,processEvents
,selectSubdevices
,newDevice
,openDevice
,closeDevice
,withDevice
,setLogLevel
,setVideoCallback
,startVideo
,setDepthCallback
,startDepth
,setTiltDegrees
,setLed
,setVideoMode
,setDepthMode
,Context
,Device
,FreenectException(..)
,Subdevice(..)
,LogLevel(..)
,Led(..)
,Resolution(..)
,VideoFormat(..)
,DepthFormat(..))
where
import Freenect.FFI
import Control.Exception (bracket,throw,Exception(..))
import Data.Bits
import Data.IORef
import Data.List
import Data.Typeable
import Foreign
import Foreign.C
import Data.Vector.Storable (Vector,unsafeFromForeignPtr)
data Resource a = Initialized a | Uninitialized a
deriving Show
newtype Context = CPtr (IORef (Resource (Ptr (Ptr ContextStruct))))
newtype Device = DPtr (IORef (Resource (Ptr (Ptr DeviceStruct))))
data FreenectException
= InitFail
| ShutdownFail
| CloseDeviceFail
| AlreadyInitializedContext
| AlreadyOpenedDevice
| UseOfUninitializedContext
| UseOfUninitializedDevice
| ProcessEvents CInt
| OpenDeviceFailed Integer
| StartVideoProblem
| StartDepthProblem
| UnableToSetTilt
| UnableToSetLed
| SetVideoMode
| VideoModeNotSet
| SetDepthMode
| DepthModeNotSet
deriving (Show,Typeable)
instance Exception FreenectException
initialize :: Context -> IO ()
initialize (CPtr ptrRef) = do
ptr <- readIORef ptrRef
case ptr of
Initialized{} -> throw AlreadyInitializedContext
Uninitialized ptr -> do
succeed InitFail (writeIORef ptrRef (Initialized ptr)) $
freenect_init ptr 0
newContext :: IO Context
newContext = new_freenect_context >>= fmap CPtr . newIORef . Uninitialized
shutdown :: Context -> IO ()
shutdown cptr@(CPtr ptrRef) = flip withC cptr $ \ptr ->
succeed ShutdownFail
(writeIORef ptrRef (Uninitialized ptr))
(peek ptr >>= freenect_shutdown)
countDevices :: Context -> IO Integer
countDevices =
withC $ \ptr ->
fmap fromIntegral (peek ptr >>= freenect_num_devices)
withContext :: (Context -> IO a) -> IO a
withContext f = bracket newContext shutdown (\c -> do initialize c; f c)
processEvents :: Context -> IO ()
processEvents = withC $ \cptr -> do
cptr <- peek cptr
result <- freenect_process_events cptr
case result of
10 -> return ()
_ | result < 0 -> throw (ProcessEvents result)
| otherwise -> return ()
succeed :: Exception e => e -> IO () -> IO CInt -> IO ()
succeed e ok m = do
result <- m
if result == 0
then ok
else throw e
data Subdevice = Motor | Camera | Auto
deriving (Show,Eq)
selectSubdevices :: Context -> [Subdevice] -> IO ()
selectSubdevices c (nub -> subdevices) = flip withC c $ \ptr -> do
ptr <- peek ptr
freenect_select_subdevices ptr (foldl1 (.|.) (map toDeviceId subdevices))
where toDeviceId Motor = 1
toDeviceId Camera = 2
toDeviceId Auto = 4
newDevice :: IO Device
newDevice = new_freenect_device >>= fmap DPtr . newIORef . Uninitialized
openDevice :: Context -> Device -> Integer -> IO ()
openDevice c (DPtr devptr) index = flip withC c $ \cptr -> do
dptr <- readIORef devptr
case dptr of
Initialized{} -> throw AlreadyOpenedDevice
Uninitialized dptr -> do
succeed (OpenDeviceFailed index) (writeIORef devptr (Initialized dptr)) $ do
cptr <- peek cptr
freenect_open_device cptr dptr (fromIntegral index)
closeDevice :: Device -> IO ()
closeDevice dptr@(DPtr ptrRef) = do
flip withD dptr $ \ptr -> do
succeed CloseDeviceFail
(writeIORef ptrRef (Uninitialized ptr))
(peek ptr >>= freenect_close_device)
withDevice :: Context -> Integer -> (Device -> IO a) -> IO a
withDevice ctx i f = bracket newDevice closeDevice (\d -> do openDevice ctx d i; f d)
withD :: (Ptr (Ptr DeviceStruct) -> IO a) -> Device -> IO a
withD cons (DPtr ptr) = do
ptr <- readIORef ptr
case ptr of
Uninitialized{} -> throw UseOfUninitializedDevice
Initialized ptr -> cons ptr
withC :: (Ptr (Ptr ContextStruct) -> IO a) -> Context -> IO a
withC cons (CPtr ptr) = do
ptr <- readIORef ptr
case ptr of
Uninitialized{} -> throw UseOfUninitializedContext
Initialized ptr -> cons ptr
data LogLevel
= LogFatal
| LogError
| LogWarning
| LogNotice
| LogInfo
| LogDebug
| LogSpew
| LogFlood
deriving (Show,Eq,Enum)
setLogLevel :: LogLevel -> Context -> IO ()
setLogLevel level = withC $ \ptr -> do
ptr <- peek ptr
freenect_set_log_level ptr (fromIntegral (fromEnum level))
setVideoCallback :: Device -> (Vector Word8 -> Word32 -> IO ()) -> IO ()
setVideoCallback d callback = flip withD d $ \dptr -> do
dptr <- peek dptr
resolution <- get_freenect_video_resolution dptr
let !size = resolutionToSize (toEnum (fromIntegral resolution))
callbackPtr <- wrapVideoCallback $ \_ payloadptr timestamp -> do
fptr <- newForeignPtr_ payloadptr
let !vector = unsafeFromForeignPtr fptr 0 (size * 3)
callback vector timestamp
freenect_set_video_callback dptr callbackPtr
setDepthCallback :: Device -> (Vector Word16 -> Word32 -> IO ()) -> IO ()
setDepthCallback d callback = flip withD d $ \dptr -> do
dptr <- peek dptr
resolution <- get_freenect_depth_resolution dptr
let !size = resolutionToSize (toEnum (fromIntegral resolution))
callbackPtr <- wrapDepthCallback $ \_ payloadptr timestamp -> do
fptr <- newForeignPtr_ payloadptr
let !vector = unsafeFromForeignPtr fptr 0 size
callback vector timestamp
freenect_set_depth_callback dptr callbackPtr
resolutionToSize :: Resolution -> Int
resolutionToSize Low = 320 * 240
resolutionToSize Medium = 640 * 480
resolutionToSize High = 1280 * 1024
startVideo :: Device -> IO ()
startVideo = withD $ \ptr -> succeed StartVideoProblem (return ()) $ do
ptr <- peek ptr
freenect_start_video ptr
startDepth :: Device -> IO ()
startDepth = withD $ \ptr -> succeed StartDepthProblem (return ()) $ do
ptr <- peek ptr
freenect_start_depth ptr
setTiltDegrees :: Double -> Device -> IO ()
setTiltDegrees angle = withD $ \ptr -> succeed UnableToSetTilt (return ()) $ do
ptr <- peek ptr
freenect_set_tilt_degs ptr (realToFrac angle)
data Resolution = Low | Medium | High
deriving (Enum,Show,Eq,Ord)
data VideoFormat
= RGB
| Bayer
| EightBitIR
| TenBitIR
| TenBitPackedIR
| YUVRGB
| YUVRaw
deriving (Enum,Show,Eq)
setVideoMode :: Device -> Resolution -> VideoFormat -> IO ()
setVideoMode d res fmt = flip withD d $ \dptr -> do
dptr <- peek dptr
frameMode <- find_video_mode_freenect (fromIntegral (fromEnum res))
(fromIntegral (fromEnum fmt))
succeed SetVideoMode (return ()) $
set_freenect_video_mode dptr frameMode
data DepthFormat
= ElevenBit
| TenBit
| ElevenBitPacked
| TenBitPacked
deriving (Enum,Show,Eq)
setDepthMode :: Device -> Resolution -> DepthFormat -> IO ()
setDepthMode d res fmt = flip withD d $ \dptr -> do
dptr <- peek dptr
frameMode <- find_depth_mode_freenect (fromIntegral (fromEnum res))
(fromIntegral (fromEnum fmt))
succeed SetDepthMode (return ()) $
set_freenect_depth_mode dptr frameMode
data Led
= Off
| Green
| Red
| Yellow
| BlinkGreen
| BlinkRedYellow
deriving (Enum,Show,Eq)
setLed :: Device -> Led -> IO ()
setLed d led = flip withD d $ \ptr -> do
ptr <- peek ptr
succeed UnableToSetLed (return ()) $
freenect_set_led ptr (if ledcode == 5 then 6 else ledcode)
where
ledcode = (fromIntegral (fromEnum led))