ffi-libfreenect 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+
2
+ # we may one day have a native extension for bindings... for now only
3
+ # ffi/freenect exists
4
+ require 'ffi/freenect'
5
+ require 'freenect/context'
6
+ require 'freenect/device'
7
+
8
+ module Freenect
9
+ include FFI::Freenect
10
+
11
+ def self.init(*args)
12
+ Context.new(*args)
13
+ end
14
+
15
+ def self.lookup_video_format(fmt)
16
+ return (fmt.is_a?(Numeric) ? fmt : FFI::Freenect::VIDEO_FORMATS[fmt])
17
+ end
18
+
19
+ def self.lookup_video_size(fmt)
20
+ l_fmt = (fmt.is_a?(Numeric) ? FFI::Freenect::VIDEO_FORMATS[fmt] : fmt)
21
+ if l_fmt.nil? or (sz = FFI::Freenect::VIDEO_SIZES[l_fmt]).nil?
22
+ return nil
23
+ else
24
+ return sz
25
+ end
26
+ end
27
+
28
+ def self.lookup_depth_format(fmt)
29
+ return (fmt.is_a?(Numeric) ? fmt : FFI::Freenect::DEPTH_FORMATS[fmt])
30
+ end
31
+
32
+ def self.lookup_depth_size(fmt)
33
+ l_fmt = (fmt.is_a?(Numeric) ? FFI::Freenect::DEPTH_FORMATS[fmt] : fmt)
34
+ if l_fmt.nil? or (sz = FFI::Freenect::DEPTH_SIZES[l_fmt]).nil?
35
+ return nil
36
+ else
37
+ return sz
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,66 @@
1
+ require 'ffi/freenect'
2
+ require 'freenect/device'
3
+
4
+ module Freenect
5
+ class ContextError < StandardError
6
+ end
7
+
8
+ class Context
9
+ def initialize(usb_ctx=nil)
10
+ ctx_p = FFI::MemoryPointer.new(:pointer)
11
+ if ::FFI::Freenect.freenect_init(ctx_p, usb_ctx) != 0
12
+ raise ContextError, "freenect_init() returned nonzero"
13
+ elsif ctx_p.null?
14
+ raise ContextError, "freenect_init() produced a NULL context"
15
+ end
16
+ @ctx = ctx_p.read_pointer
17
+ end
18
+
19
+ def context
20
+ if @ctx_closed
21
+ raise ContextError, "This context has been shut down and can no longer be used"
22
+ else
23
+ return @ctx
24
+ end
25
+ end
26
+
27
+ def num_devices
28
+ ::FFI::Freenect.freenect_num_devices(self.context)
29
+ end
30
+
31
+ def open_device(idx)
32
+ return Device.new(self, idx)
33
+ end
34
+
35
+ alias [] open_device
36
+
37
+ def set_log_level(loglevel)
38
+ ::FFI::Freenect.freenect_set_log_level(self.context, loglevel)
39
+ end
40
+
41
+ alias log_level= set_log_level
42
+
43
+ def set_log_callback(&block)
44
+ ::FFI::Freenect.freenect_set_log_callback(self.context, block)
45
+ end
46
+
47
+ def process_events
48
+ ::FFI::Freenect.freenect_process_events(self.context)
49
+ end
50
+
51
+ def close
52
+ unless closed?
53
+ if ::FFI::Freenect.freenect_shutdown(@ctx) != 0
54
+ raise ContextError, "freenect_shutdown() returned nonzero"
55
+ end
56
+ @ctx_closed = true
57
+ end
58
+ end
59
+
60
+ alias shutdown close
61
+
62
+ def closed?
63
+ @ctx_closed == true
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,210 @@
1
+
2
+ require 'ffi/freenect'
3
+ require 'freenect/context'
4
+
5
+ module Freenect
6
+ RawTiltState = FFI::Freenect::RawTiltState
7
+
8
+ class DeviceError < StandardError
9
+ end
10
+
11
+ class Device
12
+ # Returns a device object tracked by its ruby object reference ID stored
13
+ # in user data.
14
+ #
15
+ # This method is intended for internal use.
16
+ def self.by_reference(devp)
17
+ unless devp.null? or (refp=FFI::Freenect.freenect_get_user(devp)).null?
18
+ obj=ObjectSpace._id2ref(refp.read_long_long)
19
+ return obj if obj.is_a?(Device)
20
+ end
21
+ end
22
+
23
+ def initialize(ctx, idx)
24
+ dev_p = ::FFI::MemoryPointer.new(:pointer)
25
+ @ctx = ctx
26
+
27
+ if ::FFI::Freenect.freenect_open_device(@ctx.context, dev_p, idx) != 0
28
+ raise DeviceError, "unable to open device #{idx} from #{ctx.inspect}"
29
+ end
30
+
31
+ @dev = dev_p.read_pointer
32
+ save_object_id!()
33
+ end
34
+
35
+ def closed?
36
+ @ctx.closed? or (@dev_closed == true)
37
+ end
38
+
39
+ def close
40
+ unless closed?
41
+ if ::FFI::Freenect.freenect_close_device(@dev) == 0
42
+ @dev_closed = true
43
+ end
44
+ end
45
+ end
46
+
47
+ def device
48
+ if closed?
49
+ raise DeviceError, "this device is closed and can no longer be used"
50
+ else
51
+ return @dev
52
+ end
53
+ end
54
+
55
+ def context
56
+ @ctx
57
+ end
58
+
59
+ def get_tilt_state
60
+ unless (p=::FFI::Freenect.freenect_get_tilt_state(self.device)).null?
61
+ return RawTiltState.new(p)
62
+ else
63
+ raise DeviceError, "freenect_get_tilt_state() returned a NULL tilt_state"
64
+ end
65
+ end
66
+
67
+ alias tilt_state get_tilt_state
68
+
69
+ # Returns the current tilt angle
70
+ def get_tilt_degrees
71
+ ::FFI::Freenect.freenect_get_tilt_degs(self.device)
72
+ end
73
+
74
+ alias tilt get_tilt_degrees
75
+
76
+ # Sets the tilt angle.
77
+ # Maximum tilt angle range is between +30 and -30
78
+ def set_tilt_degrees(angle)
79
+ ::FFI::Freenect.freenect_set_tilt_degs(self.device, angle)
80
+ return(update_tilt_state() < 0) # based on libfreenect error cond. as of 12-21-10
81
+ end
82
+
83
+ alias tilt= set_tilt_degrees
84
+
85
+ # Defines a handler for depth events.
86
+ #
87
+ # @yield [device, depth_buf, timestamp]
88
+ # @yieldparam device A pointer to the device that generated the event.
89
+ # @yieldparam depth_buf A pointer to the buffer containing the depth data.
90
+ # @yieldparam timestamp A timestamp for the event?
91
+ def set_depth_callback(&block)
92
+ @depth_callback = block
93
+ ::FFI::Freenect.freenect_set_depth_callback(self.device, @depth_callback)
94
+ end
95
+
96
+ alias on_depth set_depth_callback
97
+
98
+ # Defines a handler for video events.
99
+ #
100
+ # @yield [device, video_buf, timestamp]
101
+ # @yieldparam device A pointer to the device that generated the event.
102
+ # @yieldparam video_buf A pointer to the buffer containing the video data.
103
+ # @yieldparam timestamp A timestamp for the event?
104
+ def set_video_callback(&block)
105
+ @video_callback = block
106
+ ::FFI::Freenect.freenect_set_video_callback(self.device, @video_callback)
107
+ end
108
+
109
+ alias on_video set_video_callback
110
+
111
+ def start_depth
112
+ unless(::FFI::Freenect.freenect_start_depth(self.device) == 0)
113
+ raise DeviceError, "Error in freenect_start_depth()"
114
+ end
115
+ end
116
+
117
+ def stop_depth
118
+ unless(::FFI::Freenect.freenect_stop_depth(self.device) == 0)
119
+ raise DeviceError, "Error in freenect_stop_depth()"
120
+ end
121
+ end
122
+
123
+ def start_video
124
+ unless(::FFI::Freenect.freenect_start_video(self.device) == 0)
125
+ raise DeviceError, "Error in freenect_start_video()"
126
+ end
127
+ end
128
+
129
+ def stop_video
130
+ unless(::FFI::Freenect.freenect_stop_video(self.device) == 0)
131
+ raise DeviceError, "Error in freenect_stop_video()"
132
+ end
133
+ end
134
+
135
+ def set_depth_format(fmt)
136
+ l_fmt = fmt.is_a?(Numeric)? fmt : Freenect::DEPTH_FORMATS[fmt]
137
+ ret = ::FFI::Freenect.freenect_set_depth_format(self.device, l_fmt)
138
+ if (ret== 0)
139
+ @depth_format = fmt
140
+ else
141
+ raise DeviceError, "Error calling freenect_set_depth_format(self, #{fmt})"
142
+ end
143
+ end
144
+
145
+ alias depth_format= set_depth_format
146
+
147
+ # returns the symbolic constant for the current depth format
148
+ def depth_format
149
+ (@depth_format.is_a?(Numeric))? Freenect::DEPTH_FORMATS[@depth_format] : @depth_format
150
+ end
151
+
152
+ # Sets the video format to one of the following accepted values:
153
+ #
154
+ def set_video_format(fmt)
155
+ l_fmt = fmt.is_a?(Numeric)? fmt : Freenect::VIDEO_FORMATS[fmt]
156
+ ret = ::FFI::Freenect.freenect_set_video_format(self.device, l_fmt)
157
+ if (ret== 0)
158
+ @video_format = fmt
159
+ else
160
+ raise DeviceError, "Error calling freenect_set_video_format(self, #{fmt})"
161
+ end
162
+ end
163
+
164
+ alias video_format= set_video_format
165
+
166
+ def video_format
167
+ (@video_format.is_a?(Numeric))? ::Freenect::VIDEO_FORMATS[@video_format] : @video_format
168
+ end
169
+
170
+ # Sets the led to one of the following accepted values:
171
+ # :off, Freenect::LED_OFF
172
+ # :green, Freenect::LED_GREEN
173
+ # :red, Freenect::LED_RED
174
+ # :yellow, Freenect::LED_YELLOW
175
+ # :blink_yellow, Freenect::LED_BLINK_YELLOW
176
+ # :blink_green, Freenect::LED_BLINK_GREEN
177
+ # :blink_red_yellow, Freenect::LED_BLINK_RED_YELLOW
178
+ #
179
+ # Either the symbol or numeric constant can be specified.
180
+ def set_led(mode)
181
+ return(::FFI::Freenect.freenect_set_led(self.device, mode) == 0)
182
+ end
183
+
184
+ alias led= set_led
185
+
186
+ def reference_id
187
+ unless (p=::FFI::Freenect.freenect_get_user(device)).null?
188
+ p.read_long_long
189
+ end
190
+ end
191
+
192
+ private
193
+ def set_depth_buffer(buf)
194
+ end
195
+
196
+ def set_video_buffer(buf)
197
+ end
198
+
199
+ def save_object_id!
200
+ objid_p = FFI::MemoryPointer.new(:long_long)
201
+ objid_p.write_long_long(self.object_id)
202
+ ::FFI::Freenect.freenect_set_user(self.device, objid_p)
203
+ end
204
+
205
+ def update_tilt_state
206
+ ::FFI::Freenect.freenect_update_tilt_state(self.device)
207
+ end
208
+
209
+ end
210
+ end
@@ -0,0 +1,93 @@
1
+
2
+ require 'freenect'
3
+
4
+ module Freenect
5
+ module Sync
6
+ class FormatError < StandardError
7
+ end
8
+
9
+ # Synchronous video function (starts the runloop if it isn't running)
10
+ #
11
+ # @param idx
12
+ # Device index. Default: 0
13
+ #
14
+ # @param fmt
15
+ # Video Format. See FFI::Freenect::VIDEO_FORMATS. Default is :rgb
16
+ #
17
+ # @return [Numeric, String]
18
+ # Returns an array containing a numeric timestamp and the video buffer
19
+ # snapshot with a size based on the requested video format.
20
+ #
21
+ # @raise FormatError
22
+ # An exception is raised if an invalid format is specified.
23
+ #
24
+ # @raise RuntimeError
25
+ # An exception is raised if an unknown error occurs in the
26
+ # freenect_sync_get_video function
27
+ #
28
+ def self.get_video(idx=nil, fmt=nil)
29
+ idx ||= 0
30
+ fmt ||= :rgb
31
+
32
+ if (buf_size = Freenect.lookup_video_size(fmt)).nil?
33
+ raise(FormatError, "Invalid video format: #{fmt.inspect}")
34
+ end
35
+
36
+ video_p = FFI::MemoryPointer.new(buf_size)
37
+ timestamp_p = FFI::MemoryPointer.new(:uint32)
38
+
39
+ ret = ::FFI::Freenect.freenect_sync_get_video(video_p, timestamp_p, idx, Freenect.lookup_video_format(fmt))
40
+
41
+ if ret != 0
42
+ raise("Unknown error in freenect_sync_get_video()") # TODO is errno set or something here?
43
+ else
44
+ return [timestamp_p.read_int, video_p.read_string_length(buf_size)]
45
+ end
46
+ end
47
+
48
+ # Synchronous depth function (starts the runloop if it isn't running)
49
+ #
50
+ # @param idx
51
+ # Device index. Default: 0
52
+ #
53
+ # @param fmt
54
+ # Video Format. See FFI::Freenect::DEPTH_FORMATS. Default is :rgb
55
+ #
56
+ # @return [Numeric, String]
57
+ # Returns an array containing a numeric timestamp and the depth buffer
58
+ # snapshot with a size based on the requested video format.
59
+ #
60
+ # @raise FormatError
61
+ # An exception is raised if an invalid format is specified.
62
+ #
63
+ # @raise RuntimeError
64
+ # An exception is raised if an unknown error occurs in the
65
+ # freenect_sync_get_video function
66
+ #
67
+ def self.get_depth(idx=0, fmt=:depth_11bit)
68
+ idx ||= 0
69
+ fmt ||= :depth_11bit
70
+
71
+ if (buf_size = Freenect.lookup_depth_size(fmt)).nil?
72
+ raise(FormatError, "Invalid depth format: #{fmt.inspect}")
73
+ end
74
+
75
+ depth_p = FFI::MemoryPointer.new(buf_size)
76
+ timestamp_p = FFI::MemoryPointer.new(:uint32)
77
+
78
+ ret = ::FFI::Freenect.freenect_sync_get_depth(depth_p, timestamp_p, idx, Freenect.lookup_depth_format(fmt))
79
+ if ret != 0
80
+ raise("Unknown error in freenect_sync_get_depth()") # TODO is errno set or something here?
81
+ else
82
+ return [timestamp_p.read_int, video_p.read_string_length(buf_size)]
83
+ end
84
+ end
85
+
86
+ # Stops the sync runloop
87
+ def self.stop
88
+ FFI::Freenect.freenect_sync_stop()
89
+ end
90
+
91
+ end
92
+ end
93
+
@@ -0,0 +1,61 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe Freenect::Context do
5
+ before(:all) { @ctx = Freenect::Context.new() }
6
+ after(:all) { @ctx.close if @ctx }
7
+
8
+ it "should initialize a context" do
9
+ @ctx.should be_kind_of(Freenect::Context)
10
+ end
11
+
12
+ it "should indicate the number of devices attached" do
13
+ @ctx.num_devices.should >= 0
14
+ end
15
+
16
+ it "should open a device when one is connected" do
17
+ if @ctx.num_devices > 0
18
+ dev = @ctx[0]
19
+ dev.should be_kind_of(Freenect::Device)
20
+ lambda { dev.close }.should_not raise_error
21
+ end
22
+ end
23
+
24
+ it "should raise an exception when an invalid device index is opened" do
25
+ lambda { @ctx[@ctx.num_devices + 1] }.should raise_error(StandardError)
26
+ lambda { @ctx.open_device(@ctx.num_devices + 1) }.should raise_error(StandardError)
27
+ end
28
+
29
+ it "should allow a libfreenect log level to be set using symbols or constants" do
30
+ (@ctx.log_level = :fatal).should == :fatal
31
+ (@ctx.log_level = Freenect::LOG_FATAL).should == 0
32
+
33
+ (@ctx.log_level = :error).should == :error
34
+ (@ctx.log_level = Freenect::LOG_ERROR).should == 1
35
+
36
+ (@ctx.log_level = :warning).should == :warning
37
+ (@ctx.log_level = Freenect::LOG_WARNING).should == 2
38
+
39
+ (@ctx.log_level = :notice).should == :notice
40
+ (@ctx.log_level = Freenect::LOG_NOTICE).should == 3
41
+
42
+ (@ctx.log_level = :info).should == :info
43
+ (@ctx.log_level = Freenect::LOG_INFO).should == 4
44
+
45
+ (@ctx.log_level = :debug).should == :debug
46
+ (@ctx.log_level = Freenect::LOG_DEBUG).should == 5
47
+
48
+ (@ctx.log_level = :spew).should == :spew
49
+ (@ctx.log_level = Freenect::LOG_SPEW).should == 6
50
+
51
+ (@ctx.log_level = :flood).should == :flood
52
+ (@ctx.log_level = Freenect::LOG_FLOOD).should == 7
53
+ end
54
+
55
+ it "should allow a log callback to be set" do
56
+ @ctx.set_log_callback {|a,b,c| p [a,b,c] }
57
+ end
58
+
59
+
60
+ end
61
+