hornetseye-alsa 0.1.0

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.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ hornetseye-alsa
2
+ ======
3
+ This Ruby extension provides an interface for playing audio data using ALSA.
4
+
data/Rakefile ADDED
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env ruby
2
+ require 'date'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/loaders/makefile'
7
+ require 'rbconfig'
8
+
9
+ PKG_NAME = 'hornetseye-alsa'
10
+ PKG_VERSION = '0.1.0'
11
+ CXX = ENV[ 'CXX' ] || 'g++'
12
+ STRIP = ENV[ 'STRIP' ] || 'strip'
13
+ RB_FILES = FileList[ 'lib/**/*.rb' ]
14
+ CC_FILES = FileList[ 'ext/*.cc' ]
15
+ HH_FILES = FileList[ 'ext/*.hh' ] + FileList[ 'ext/*.tcc' ]
16
+ TC_FILES = FileList[ 'test/tc_*.rb' ]
17
+ TS_FILES = FileList[ 'test/ts_*.rb' ]
18
+ SO_FILE = "ext/#{PKG_NAME.tr '\-', '_'}.so"
19
+ PKG_FILES = [ 'Rakefile', 'README.md', 'COPYING', '.document' ] +
20
+ RB_FILES + CC_FILES + HH_FILES + TS_FILES + TC_FILES
21
+ BIN_FILES = [ 'README.md', 'COPYING', '.document', SO_FILE ] +
22
+ RB_FILES + TS_FILES + TC_FILES
23
+ SUMMARY = %q{Play audio data using libalsa}
24
+ DESCRIPTION = %q{This Ruby extension provides an interface for playing audio data using ALSA.}
25
+ AUTHOR = %q{Jan Wedekind}
26
+ EMAIL = %q{jan@wedesoft.de}
27
+ HOMEPAGE = %q{http://wedesoft.github.com/hornetseye-alsa/}
28
+
29
+ OBJ = CC_FILES.ext 'o'
30
+ $CXXFLAGS = ENV[ 'CXXFLAGS' ] || ''
31
+ $CXXFLAGS = "#{$CXXFLAGS} -fPIC -DNDEBUG"
32
+ if RbConfig::CONFIG[ 'rubyhdrdir' ]
33
+ $CXXFLAGS = "#{$CXXFLAGS} -I#{RbConfig::CONFIG[ 'rubyhdrdir' ]} " +
34
+ "-I#{RbConfig::CONFIG[ 'rubyhdrdir' ]}/#{RbConfig::CONFIG[ 'arch' ]}"
35
+ else
36
+ $CXXFLAGS += "#{$CXXFLAGS} -I#{RbConfig::CONFIG[ 'archdir' ]}"
37
+ end
38
+ $LIBRUBYARG = RbConfig::CONFIG[ 'LIBRUBYARG' ]
39
+ $SITELIBDIR = RbConfig::CONFIG[ 'sitelibdir' ]
40
+ $SITEARCHDIR = RbConfig::CONFIG[ 'sitearchdir' ]
41
+
42
+ task :default => :all
43
+
44
+ desc 'Compile Ruby extension (default)'
45
+ task :all => [ SO_FILE ]
46
+
47
+ file SO_FILE => OBJ do |t|
48
+ sh "#{CXX} -shared -o #{t.name} #{OBJ} -lasound #{$LIBRUBYARG}"
49
+ sh "#{STRIP} --strip-all #{t.name}"
50
+ end
51
+
52
+ task :test => [ SO_FILE ]
53
+
54
+ desc 'Install Ruby extension'
55
+ task :install => :all do
56
+ verbose true do
57
+ for f in RB_FILES do
58
+ FileUtils.mkdir_p "#{$SITELIBDIR}/#{File.dirname f.gsub( /^lib\//, '' )}"
59
+ FileUtils.cp_r f, "#{$SITELIBDIR}/#{f.gsub( /^lib\//, '' )}"
60
+ end
61
+ FileUtils.mkdir_p $SITEARCHDIR
62
+ FileUtils.cp SO_FILE, "#{$SITEARCHDIR}/#{File.basename SO_FILE}"
63
+ end
64
+ end
65
+
66
+ desc 'Uninstall Ruby extension'
67
+ task :uninstall do
68
+ verbose true do
69
+ for f in RB_FILES do
70
+ FileUtils.rm_f "#{$SITELIBDIR}/#{f.gsub /^lib\//, ''}"
71
+ end
72
+ FileUtils.rm_f "#{$SITEARCHDIR}/#{File.basename SO_FILE}"
73
+ end
74
+ end
75
+
76
+ Rake::TestTask.new do |t|
77
+ t.libs << 'ext'
78
+ t.test_files = TC_FILES
79
+ end
80
+
81
+ begin
82
+ require 'yard'
83
+ YARD::Rake::YardocTask.new :yard do |y|
84
+ y.options << '--no-private'
85
+ y.files << FileList[ 'lib/**/*.rb' ]
86
+ end
87
+ rescue LoadError
88
+ STDERR.puts 'Please install \'yard\' if you want to generate documentation'
89
+ end
90
+
91
+ Rake::PackageTask.new PKG_NAME, PKG_VERSION do |p|
92
+ p.need_tar = true
93
+ p.package_files = PKG_FILES
94
+ end
95
+
96
+ begin
97
+ require 'rubygems'
98
+ require 'rubygems/builder'
99
+ $SPEC = Gem::Specification.new do |s|
100
+ s.name = PKG_NAME
101
+ s.version = PKG_VERSION
102
+ s.platform = Gem::Platform::RUBY
103
+ s.date = Date.today.to_s
104
+ s.summary = SUMMARY
105
+ s.description = DESCRIPTION
106
+ s.author = AUTHOR
107
+ s.email = EMAIL
108
+ s.homepage = HOMEPAGE
109
+ s.files = PKG_FILES
110
+ s.test_files = TC_FILES
111
+ s.require_paths = [ 'lib', 'ext' ]
112
+ s.rubyforge_project = %q{hornetseye}
113
+ s.extensions = %w{Rakefile}
114
+ s.has_rdoc = 'yard'
115
+ s.extra_rdoc_files = []
116
+ s.rdoc_options = %w{--no-private}
117
+ s.add_dependency %<malloc>, [ '~> 1.2' ]
118
+ s.add_dependency %<multiarray>, [ '~> 0.11' ]
119
+ s.add_dependency %<hornetseye-frame>, [ '~> 0.7' ]
120
+ s.add_development_dependency %q{rake}
121
+ end
122
+ GEM_SOURCE = "#{PKG_NAME}-#{PKG_VERSION}.gem"
123
+ $BINSPEC = Gem::Specification.new do |s|
124
+ s.name = PKG_NAME
125
+ s.version = PKG_VERSION
126
+ s.platform = Gem::Platform::CURRENT
127
+ s.date = Date.today.to_s
128
+ s.summary = SUMMARY
129
+ s.description = DESCRIPTION
130
+ s.author = AUTHOR
131
+ s.email = EMAIL
132
+ s.homepage = HOMEPAGE
133
+ s.files = BIN_FILES
134
+ s.test_files = TC_FILES
135
+ s.require_paths = [ 'lib', 'ext' ]
136
+ s.rubyforge_project = %q{hornetseye}
137
+ s.has_rdoc = 'yard'
138
+ s.extra_rdoc_files = []
139
+ s.rdoc_options = %w{--no-private}
140
+ s.add_dependency %<malloc>, [ '~> 1.2' ]
141
+ s.add_dependency %<multiarray>, [ '~> 0.11' ]
142
+ end
143
+ GEM_BINARY = "#{PKG_NAME}-#{PKG_VERSION}-#{$BINSPEC.platform}.gem"
144
+ desc "Build the gem file #{GEM_SOURCE}"
145
+ task :gem => [ "pkg/#{GEM_SOURCE}" ]
146
+ file "pkg/#{GEM_SOURCE}" => [ 'pkg' ] + $SPEC.files do
147
+ when_writing 'Creating GEM' do
148
+ Gem::Builder.new( $SPEC ).build
149
+ verbose true do
150
+ FileUtils.mv GEM_SOURCE, "pkg/#{GEM_SOURCE}"
151
+ end
152
+ end
153
+ end
154
+ desc "Build the gem file #{GEM_BINARY}"
155
+ task :gem_binary => [ "pkg/#{GEM_BINARY}" ]
156
+ file "pkg/#{GEM_BINARY}" => [ 'pkg' ] + $BINSPEC.files do
157
+ when_writing 'Creating binary GEM' do
158
+ Gem::Builder.new( $BINSPEC ).build
159
+ verbose true do
160
+ FileUtils.mv GEM_BINARY, "pkg/#{GEM_BINARY}"
161
+ end
162
+ end
163
+ end
164
+ rescue LoadError
165
+ STDERR.puts 'Please install \'rubygems\' if you want to create Gem packages'
166
+ end
167
+
168
+ rule '.o' => '.cc' do |t|
169
+ sh "#{CXX} #{$CXXFLAGS} -c -o #{t.name} #{t.source}"
170
+ end
171
+
172
+ file ".depends.mf" do |t|
173
+ sh "g++ -MM #{$CXXFLAGS} #{CC_FILES.join ' '} | " +
174
+ "sed -e :a -e N -e 's/\\n/\\$/g' -e ta | " +
175
+ "sed -e 's/ *\\\\\\$ */ /g' -e 's/\\$/\\n/g' | sed -e 's/^/ext\\//' > #{t.name}"
176
+ end
177
+ CC_FILES.each do |t|
178
+ file t.ext(".o") => t
179
+ end
180
+ import ".depends.mf"
181
+
182
+ CLEAN.include 'ext/*.o'
183
+ CLOBBER.include SO_FILE, 'doc', '.yardoc', '.depends.mf'
184
+
data/ext/alsaoutput.cc ADDED
@@ -0,0 +1,285 @@
1
+ /* HornetsEye - Computer Vision with Ruby
2
+ Copyright (C) 2006, 2007, 2008, 2009, 2010 Jan Wedekind
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+ #include "alsaoutput.hh"
17
+
18
+ using namespace std;
19
+
20
+ VALUE AlsaOutput::cRubyClass = Qnil;
21
+
22
+ AlsaOutput::AlsaOutput( const string &pcmName, unsigned int rate,
23
+ unsigned int channels, int periods,
24
+ int frames ) throw (Error):
25
+ m_pcmHandle(NULL), m_pcmName( pcmName ), m_rate( rate ), m_channels( channels ),
26
+ m_frames( frames )
27
+ {
28
+ try {
29
+ snd_pcm_hw_params_t *hwParams;
30
+ snd_pcm_hw_params_alloca( &hwParams );
31
+ int err = snd_pcm_open( &m_pcmHandle, m_pcmName.c_str(), SND_PCM_STREAM_PLAYBACK,
32
+ SND_PCM_NONBLOCK );
33
+ ERRORMACRO( err >= 0, Error, , "Error opening PCM device \"" << m_pcmName
34
+ << "\": " << snd_strerror( err ) );
35
+ err = snd_pcm_hw_params_any( m_pcmHandle, hwParams );
36
+ ERRORMACRO( err >= 0, Error, , "Unable to configure the PCM device \""
37
+ << m_pcmName << "\": " << snd_strerror( err ) );
38
+ err = snd_pcm_hw_params_set_access( m_pcmHandle, hwParams,
39
+ SND_PCM_ACCESS_RW_INTERLEAVED );
40
+ ERRORMACRO( err >= 0, Error, , "Error setting PCM device \""
41
+ << m_pcmName << "\" to interlaced access: " << snd_strerror( err ) );
42
+ err = snd_pcm_hw_params_set_format( m_pcmHandle, hwParams,
43
+ SND_PCM_FORMAT_S16_LE );
44
+ ERRORMACRO( err >= 0, Error, , "Error setting PCM device \"" << m_pcmName
45
+ << "\" to 16-bit signed integer format: " << snd_strerror( err ) );
46
+ err = snd_pcm_hw_params_set_rate_near( m_pcmHandle, hwParams, &m_rate, 0 );
47
+ ERRORMACRO( err >= 0, Error, , "Error setting sampling rate of PCM device \""
48
+ << m_pcmName << "\" to " << rate << " Hz: " << snd_strerror( err ) );
49
+ err = snd_pcm_hw_params_set_channels( m_pcmHandle, hwParams, channels );
50
+ ERRORMACRO( err >= 0, Error, , "Error setting number of channels of PCM device \""
51
+ << m_pcmName << "\" to " << channels << ": " << snd_strerror( err ) );
52
+ err = snd_pcm_hw_params_set_periods( m_pcmHandle, hwParams, periods, 0 );
53
+ ERRORMACRO( err >= 0, Error, , "Error setting number of periods of PCM device \""
54
+ << m_pcmName << "\" to " << periods << ": " << snd_strerror( err ) );
55
+ err = snd_pcm_hw_params_set_buffer_size_near( m_pcmHandle, hwParams, &m_frames );
56
+ ERRORMACRO( err >= 0, Error, , "Error setting buffer size of PCM device \""
57
+ << m_pcmName << "\" to " << m_frames << " frames: "
58
+ << snd_strerror( err ) );
59
+ err = snd_pcm_hw_params( m_pcmHandle, hwParams );
60
+ ERRORMACRO( err >= 0, Error, , "Error setting parameters of PCM device \""
61
+ << m_pcmName << "\": " << snd_strerror( err ) );
62
+ } catch ( Error &e ) {
63
+ close();
64
+ throw e;
65
+ };
66
+ }
67
+
68
+ AlsaOutput::~AlsaOutput(void)
69
+ {
70
+ close();
71
+ }
72
+
73
+ void AlsaOutput::close(void)
74
+ {
75
+ if ( m_pcmHandle != NULL ) {
76
+ drop();
77
+ snd_pcm_close( m_pcmHandle );
78
+ m_pcmHandle = NULL;
79
+ };
80
+ }
81
+
82
+ void AlsaOutput::write( SequencePtr frame ) throw (Error)
83
+ {
84
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
85
+ << "\" is not open. Did you call \"close\" before?" );
86
+ int err = snd_pcm_writei( m_pcmHandle, (short int *)frame->data(),
87
+ frame->size() / ( 2 * m_channels ) );
88
+ ERRORMACRO( err >= 0, Error, , "Error writing audio frames to PCM device \""
89
+ << m_pcmName << "\": " << snd_strerror( err ) );
90
+ }
91
+
92
+ void AlsaOutput::drop(void) throw (Error)
93
+ {
94
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
95
+ << "\" is not open. Did you call \"close\" before?" );
96
+ snd_pcm_drop( m_pcmHandle );
97
+ }
98
+
99
+ void AlsaOutput::drain(void) throw (Error)
100
+ {
101
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
102
+ << "\" is not open. Did you call \"close\" before?" );
103
+ snd_pcm_drain( m_pcmHandle );
104
+ }
105
+
106
+ unsigned int AlsaOutput::rate(void)
107
+ {
108
+ return m_rate;
109
+ }
110
+
111
+ unsigned int AlsaOutput::channels(void)
112
+ {
113
+ return m_channels;
114
+ }
115
+
116
+ unsigned int AlsaOutput::frames(void)
117
+ {
118
+ return m_frames;
119
+ }
120
+
121
+ int AlsaOutput::avail(void) throw (Error)
122
+ {
123
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
124
+ << "\" is not open. Did you call \"close\" before?" );
125
+ snd_pcm_sframes_t frames = snd_pcm_avail( m_pcmHandle );
126
+ ERRORMACRO( frames >= 0, Error, , "Error querying number of available frames for "
127
+ "update of PCM device \"" << m_pcmName << "\": "
128
+ << snd_strerror( frames ) );
129
+ return frames;
130
+ }
131
+
132
+ int AlsaOutput::delay(void) throw (Error)
133
+ {
134
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
135
+ << "\" is not open. Did you call \"close\" before?" );
136
+ snd_pcm_sframes_t frames;
137
+ int err = snd_pcm_delay( m_pcmHandle, &frames );
138
+ ERRORMACRO( err >= 0, Error, , "Error querying number of available frames for "
139
+ "update of PCM device \"" << m_pcmName << "\": "
140
+ << snd_strerror( err ) );
141
+ return frames;
142
+ }
143
+
144
+ void AlsaOutput::prepare(void) throw (Error)
145
+ {
146
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
147
+ << "\" is not open. Did you call \"close\" before?" );
148
+ int err = snd_pcm_prepare( m_pcmHandle );
149
+ ERRORMACRO( err >= 0, Error, , "Error preparing PCM device \"" << m_pcmName
150
+ << "\": " << snd_strerror( err ) );
151
+ }
152
+
153
+ VALUE AlsaOutput::registerRubyClass( VALUE rbModule )
154
+ {
155
+ cRubyClass = rb_define_class_under( rbModule, "AlsaOutput", rb_cObject );
156
+ rb_define_singleton_method( cRubyClass, "new",
157
+ RUBY_METHOD_FUNC( wrapNew ), 5 );
158
+ rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
159
+ rb_define_method( cRubyClass, "write", RUBY_METHOD_FUNC( wrapWrite ), 1 );
160
+ rb_define_method( cRubyClass, "drop", RUBY_METHOD_FUNC( wrapDrop ), 0 );
161
+ rb_define_method( cRubyClass, "drain", RUBY_METHOD_FUNC( wrapDrain ), 0 );
162
+ rb_define_method( cRubyClass, "rate", RUBY_METHOD_FUNC( wrapRate ), 0 );
163
+ rb_define_method( cRubyClass, "channels", RUBY_METHOD_FUNC( wrapChannels ), 0 );
164
+ rb_define_method( cRubyClass, "frames", RUBY_METHOD_FUNC( wrapFrames ), 0 );
165
+ rb_define_method( cRubyClass, "avail", RUBY_METHOD_FUNC( wrapAvail ), 0 );
166
+ rb_define_method( cRubyClass, "delay", RUBY_METHOD_FUNC( wrapDelay ), 0 );
167
+ rb_define_method( cRubyClass, "prepare", RUBY_METHOD_FUNC( wrapPrepare ), 0 );
168
+ }
169
+
170
+ void AlsaOutput::deleteRubyObject( void *ptr )
171
+ {
172
+ delete (AlsaOutputPtr *)ptr;
173
+ }
174
+
175
+ VALUE AlsaOutput::wrapNew( VALUE rbClass, VALUE rbPCMName, VALUE rbRate,
176
+ VALUE rbChannels, VALUE rbPeriods, VALUE rbFrames )
177
+ {
178
+ VALUE retVal = Qnil;
179
+ try {
180
+ rb_check_type( rbPCMName, T_STRING );
181
+ AlsaOutputPtr ptr( new AlsaOutput( StringValuePtr( rbPCMName ),
182
+ NUM2UINT( rbRate ), NUM2UINT( rbChannels ),
183
+ NUM2INT( rbPeriods ), NUM2INT( rbFrames ) ) );
184
+ retVal = Data_Wrap_Struct( rbClass, 0, deleteRubyObject,
185
+ new AlsaOutputPtr( ptr ) );
186
+ } catch ( exception &e ) {
187
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
188
+ };
189
+ return retVal;
190
+ }
191
+
192
+ VALUE AlsaOutput::wrapClose( VALUE rbSelf )
193
+ {
194
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
195
+ (*self)->close();
196
+ return rbSelf;
197
+ }
198
+
199
+ VALUE AlsaOutput::wrapWrite( VALUE rbSelf, VALUE rbSequence )
200
+ {
201
+ try {
202
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
203
+ SequencePtr sequence( new Sequence( rbSequence ) );
204
+ (*self)->write( sequence );
205
+ } catch ( exception &e ) {
206
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
207
+ };
208
+ return rbSequence;
209
+ }
210
+
211
+ VALUE AlsaOutput::wrapDrop( VALUE rbSelf )
212
+ {
213
+ try {
214
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
215
+ (*self)->drop();
216
+ } catch ( exception &e ) {
217
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
218
+ };
219
+ return rbSelf;
220
+ }
221
+
222
+ VALUE AlsaOutput::wrapDrain( VALUE rbSelf )
223
+ {
224
+ try {
225
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
226
+ (*self)->drain();
227
+ } catch ( exception &e ) {
228
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
229
+ };
230
+ return rbSelf;
231
+ }
232
+
233
+ VALUE AlsaOutput::wrapRate( VALUE rbSelf )
234
+ {
235
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
236
+ return UINT2NUM( (*self)->rate() );
237
+ }
238
+
239
+ VALUE AlsaOutput::wrapChannels( VALUE rbSelf )
240
+ {
241
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
242
+ return UINT2NUM( (*self)->channels() );
243
+ }
244
+
245
+ VALUE AlsaOutput::wrapFrames( VALUE rbSelf )
246
+ {
247
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
248
+ return UINT2NUM( (*self)->frames() );
249
+ }
250
+
251
+ VALUE AlsaOutput::wrapAvail( VALUE rbSelf )
252
+ {
253
+ VALUE rbRetVal = Qnil;
254
+ try {
255
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
256
+ rbRetVal = INT2NUM( (*self)->avail() );
257
+ } catch ( exception &e ) {
258
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
259
+ };
260
+ return rbRetVal;
261
+ }
262
+
263
+ VALUE AlsaOutput::wrapDelay( VALUE rbSelf )
264
+ {
265
+ VALUE rbRetVal = Qnil;
266
+ try {
267
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
268
+ rbRetVal = INT2NUM( (*self)->delay() );
269
+ } catch ( exception &e ) {
270
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
271
+ };
272
+ return rbRetVal;
273
+ }
274
+
275
+ VALUE AlsaOutput::wrapPrepare( VALUE rbSelf )
276
+ {
277
+ try {
278
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
279
+ (*self)->prepare();
280
+ } catch ( exception &e ) {
281
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
282
+ };
283
+ return rbSelf;
284
+ }
285
+