ffi-libfreenect 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+