module Freenect
(
initialize
,newContext
,shutdown
,countDevices
,withContext
,processEvents
,processEventsTimeout
,selectSubdevices
,newDevice
,openDevice
,closeDevice
,withDevice
,setLogLevel
,setVideoCallback
,startVideo
,stopVideo
,setDepthCallback
,startDepth
,stopDepth
,setTiltDegrees
,getTiltDegrees
,getAcceleration
,setLed
,setVideoMode
,setDepthMode
,setFlag
,Context
,Device
,FreenectException(..)
,Subdevice(..)
,LogLevel(..)
,Led(..)
,Flag(..)
,Resolution(..)
,VideoFormat(..)
,DepthFormat(..)
,setAudioInCallback
,startAudio
,stopAudio)
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
| StopVideoProblem
| StartDepthProblem
| StopDepthProblem
| UnableToSetTilt
| UnableToSetLed
| UnableToSetFlag
| SetVideoMode
| VideoModeNotSet
| SetDepthMode
| DepthModeNotSet
| StartAudioProblem
| StopAudioProblem
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 ()
processEventsTimeout :: Context -> Int -> IO ()
processEventsTimeout ctx timeout = flip withC ctx $ \cptr -> do
cptr <- peek cptr
result <- process_events_timeout cptr (fromIntegral timeout)
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 | Audio
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 Audio = 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
stopVideo :: Device -> IO ()
stopVideo = withD $ \ptr -> succeed StopVideoProblem (return ()) $ do
ptr <- peek ptr
freenect_stop_video ptr
startDepth :: Device -> IO ()
startDepth = withD $ \ptr -> succeed StartDepthProblem (return ()) $ do
ptr <- peek ptr
freenect_start_depth ptr
stopDepth :: Device -> IO ()
stopDepth = withD $ \ptr -> succeed StopDepthProblem (return ()) $ do
ptr <- peek ptr
freenect_stop_depth ptr
setTiltDegrees :: Double -> Device -> IO ()
setTiltDegrees angle = withD $ \ptr -> succeed UnableToSetTilt (return ()) $ do
ptr <- peek ptr
freenect_set_tilt_degs ptr (realToFrac angle)
getTiltDegrees :: Device -> IO Double
getTiltDegrees= withD $ \ptr -> do
ptr <- peek ptr
_ <- freenect_update_tilt_state ptr
tiltstate <- freenect_get_tilt_state ptr
fmap realToFrac (freenect_get_tilt_degs tiltstate)
getAcceleration :: Device -> IO (Double, Double, Double)
getAcceleration = withD $ \ptr -> do
ptr <- peek ptr
_ <- freenect_update_tilt_state ptr
tiltstate <- freenect_get_tilt_state ptr
allocaArray 3 $ \temp -> do
let step = sizeOf (undefined :: CDouble)
let temp_x = temp :: Ptr CDouble
let temp_y = plusPtr temp step :: Ptr CDouble
let temp_z = plusPtr temp (2 * step) :: Ptr CDouble
freenect_get_mks_accel tiltstate temp_x temp_y temp_z
x <- peek temp_x
y <- peek temp_y
z <- peek temp_z
return (realToFrac x, realToFrac y, realToFrac z)
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))
data Flag
= AutoExposure
| AutoWhiteBalance
| RawColor
| MirrorDepth
| MirrorVideo
deriving(Show,Eq)
setFlag :: Device -> Flag -> Bool -> IO ()
setFlag d flag enabled = flip withD d $ \ptr -> do
ptr <- peek ptr
succeed UnableToSetFlag (return ()) $
freenect_set_flag ptr key value
where
key = toEnumInteger flag
value = fromIntegral (if enabled then 1 else 0)
toEnumInteger AutoExposure = 1 `shift` 14
toEnumInteger AutoWhiteBalance = 1 `shift` 1
toEnumInteger RawColor = 1 `shift` 4
toEnumInteger MirrorDepth = 1 `shift` 16
toEnumInteger MirrorVideo = 1 `shift` 17
startAudio :: Device -> IO ()
startAudio = withD $ \ptr -> succeed StartAudioProblem (return ()) $ do
ptr <- peek ptr
freenect_start_audio ptr
stopAudio :: Device -> IO ()
stopAudio = withD $ \ptr -> succeed StopAudioProblem (return ()) $ do
ptr <- peek ptr
freenect_stop_audio ptr
setAudioInCallback
:: Device
-> (Int -> Vector Word32 -> Vector Word32 -> Vector Word32 -> Vector Word32 -> Vector Word16 -> IO ())
-> IO ()
setAudioInCallback d callback = flip withD d $ \dptr -> do
dptr <- peek dptr
callbackPtr <- wrapAudioInCallback $ \_ num lptr lmptr rmptr rptr nptr _ -> do
let !size = (fromIntegral num)
l_ptr <- newForeignPtr_ lptr
lm_ptr <- newForeignPtr_ lmptr
rm_ptr <- newForeignPtr_ rmptr
r_ptr <- newForeignPtr_ rptr
n_ptr <- newForeignPtr_ nptr
let !l_vector = unsafeFromForeignPtr l_ptr 0 size
let !lm_vector = unsafeFromForeignPtr lm_ptr 0 size
let !rm_vector = unsafeFromForeignPtr rm_ptr 0 size
let !r_vector = unsafeFromForeignPtr r_ptr 0 size
let !n_vector = unsafeFromForeignPtr n_ptr 0 size
callback size l_vector lm_vector rm_vector r_vector n_vector
freenect_set_audio_in_callback dptr callbackPtr